Download templates/base.html from ItsBounvy/bot_host: direct link, hf CLI and curl.
- Browser
- Download file 17.3 kB
-
https://huggingface.co/spaces/ItsBounvy/bot_host/resolve/main/templates/base.html
- Command line
-
hf download hf://spaces/ItsBounvy/bot_host/templates/base.html
-
curl -L -o base.html https://huggingface.co/spaces/ItsBounvy/bot_host/resolve/main/templates/base.html
17.3 kB
| {# ============================================================ | |
| MMaxAI Hosting Panel — base layout (Copilot style) | |
| ------------------------------------------------------------ | |
| New shell: persistent sidebar + sticky topbar + cmd-bar. | |
| Loads panel.css for legacy components (.card, .btn, .badge, | |
| .form-*, .table) and copilot.css for the new layout tokens. | |
| ============================================================ #} | |
| <html lang="en" data-theme="dark"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <meta name="color-scheme" content="dark"> | |
| <meta name="theme-color" content="#0a0e1c"> | |
| <title>{% block title %}{{ title }}{% endblock %} · {{ panel_title or "MMaxAI Hosting" }}</title> | |
| <link rel="stylesheet" href="/static/css/panel.css?v={{ panel_assets_v() }}"> | |
| <link rel="stylesheet" href="/static/css/copilot.css?v={{ panel_assets_v() }}"> | |
| <link rel="stylesheet" href="/static/css/ai_chat.css?v={{ panel_assets_v() }}"> | |
| <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎛️</text></svg>"> | |
| </head> | |
| <body class="has-copilot"> | |
| {% if user %} | |
| <div class="shell"> | |
| {# ---------------- Sidebar ---------------- #} | |
| <aside class="sidebar" id="sidebar"> | |
| <div class="sidebar-head"> | |
| <a href="/dashboard" class="brand"> | |
| <div class="logo">M</div> | |
| MMaxAI | |
| </a> | |
| </div> | |
| <nav class="sidebar-nav"> | |
| <div class="sidebar-section-label">{{ _("Main") }}</div> | |
| <a href="/dashboard" class="nav-item {% if request.url.path == '/dashboard' %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('dashboard')|safe }}</span> {{ _("Dashboard") }} | |
| </a> | |
| <a href="/bots" class="nav-item {% if request.url.path.startswith('/bots') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('bot')|safe }}</span> {{ _("Bots") }} | |
| </a> | |
| {% if user.role == 'admin' %} | |
| <div class="sidebar-section-label">{{ _("Admin") }}</div> | |
| <a href="/admin/users" class="nav-item {% if request.url.path.startswith('/admin/users') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('users')|safe }}</span> {{ _("Users") }} | |
| </a> | |
| <a href="/admin/invites" class="nav-item {% if request.url.path.startswith('/admin/invites') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('key')|safe }}</span> {{ _("Invite codes") }} | |
| </a> | |
| <a href="/admin/stats" class="nav-item {% if request.url.path.startswith('/admin/stats') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('cpu')|safe }}</span> {{ _("Statistics") }} | |
| </a> | |
| <a href="/admin/metrics" class="nav-item {% if request.url.path.startswith('/admin/metrics') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('chart')|safe }}</span> {{ _("Metrics") }} | |
| </a> | |
| <a href="/admin/alerts" class="nav-item {% if request.url.path.startswith('/admin/alerts') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('bell')|safe }}</span> {{ _("Alerts") }} | |
| </a> | |
| <a href="/admin/secrets" class="nav-item {% if request.url.path.startswith('/admin/secrets') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('shield')|safe }}</span> {{ _("Secrets") }} | |
| </a> | |
| <a href="/admin/banners" class="nav-item {% if request.url.path.startswith('/admin/banners') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('banner')|safe }}</span> {{ _("Banners") }} | |
| </a> | |
| <a href="/admin/audit" class="nav-item {% if request.url.path.startswith('/admin/audit') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('audit')|safe }}</span> {{ _("Audit") }} | |
| </a> | |
| {% endif %} | |
| {% if user.role == 'admin' %} | |
| <a href="/admin/control" class="nav-item {% if request.url.path.startswith('/admin/control') %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('shield')|safe }}</span> {{ _("Admin control") }} | |
| </a> | |
| {% endif %} | |
| <div class="sidebar-section-label">{{ _("Account") }}</div> | |
| <a href="/profile" class="nav-item {% if request.url.path == '/profile' %}active{% endif %}"> | |
| <span class="nav-icon">{{ panel_icon('user')|safe }}</span> {{ _("Profile") }} | |
| </a> | |
| <a href="/logout" class="nav-item" data-logout> | |
| <span class="nav-icon">{{ panel_icon('logout')|safe }}</span> {{ _("Sign out") }} | |
| </a> | |
| </nav> | |
| <div class="sidebar-footer"> | |
| <a href="/profile" class="profile"> | |
| <div class="avatar">{{ user.username[:2]|upper }}</div> | |
| <div class="meta"> | |
| <div class="name">{{ user.username }}</div> | |
| <div class="role">{{ user.role }}</div> | |
| </div> | |
| </a> | |
| <button type="button" class="icon-btn menu-toggle" aria-label="Close menu">{{ panel_icon('x')|safe }}</button> | |
| </div> | |
| </aside> | |
| <div class="scrim"></div> | |
| {# ---------------- Main wrap ---------------- #} | |
| <div class="main-wrap"> | |
| <header class="topbar"> | |
| <button class="menu-toggle icon-btn" aria-label="Open menu" id="menu-open">{{ panel_icon('menu')|safe }}</button> | |
| <div class="crumbs"> | |
| {% block crumbs %} | |
| <span>Workspace</span> | |
| <span class="sep">/</span> | |
| <h1>{{ title }}</h1> | |
| {% endblock %} | |
| </div> | |
| {% block topbar_extra %}{% endblock %} | |
| <div class="spacer"></div> | |
| <button class="search-trigger" id="cmd-trigger"> | |
| {{ panel_icon('search')|safe }} | |
| <span class="label">Search bots, logs…</span> | |
| <kbd>⌘K</kbd> | |
| </button> | |
| <button class="ai-trigger ai-trigger-primary" id="ai-trigger" type="button" title="{{ _('Ask the panel assistant') }}" aria-label="{{ _('Open AI assistant') }}"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" width="14" height="14"> | |
| <path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3z"/> | |
| <path d="M5 3v4"/> | |
| <path d="M19 17v4"/> | |
| <path d="M3 5h4"/> | |
| <path d="M17 19h4"/> | |
| </svg> | |
| <span class="label">{{ _("Ask AI") }}</span> | |
| <kbd>/</kbd> | |
| </button> | |
| <div class="topbar-actions"> | |
| <button type="button" class="icon-btn lang-toggle" aria-label="Switch language" title="Switch language" id="lang-toggle-btn"> | |
| {{ panel_icon('globe')|safe }} | |
| </button> | |
| <button type="button" class="icon-btn" data-theme-toggle aria-label="Toggle theme" aria-pressed="false" title="Switch theme"> | |
| {{ panel_icon('moon')|safe }} | |
| </button> | |
| <a href="/profile" class="icon-btn" aria-label="Profile">{{ panel_icon('user')|safe }}</a> | |
| <a href="/logout" class="icon-btn" data-logout aria-label="Sign out">{{ panel_icon('logout')|safe }}</a> | |
| </div> | |
| </header> | |
| {% if request.state.plan_tier_outdated %} | |
| <div class="card" id="plan-tier-outdated-banner" | |
| style="margin: 16px 24px 0; padding: 14px 18px; border-left: 4px solid var(--accent, #6c8cff); | |
| background: var(--surface-2, #1a1d2b); display:flex; align-items:center; gap:14px; | |
| flex-wrap:wrap;"> | |
| <span style="font-size:20px;line-height:1;">⚠️</span> | |
| <div style="flex:1; min-width:240px;"> | |
| <strong>{{ _("Site updated") }}</strong> | |
| <span class="muted">{{ _("Your account was on an old plan. We have automatically moved you to the free tier.") }}</span> | |
| </div> | |
| <a href="https://t.me/helpforwacodebot" target="_blank" rel="noopener" | |
| class="btn btn-sm" | |
| style="background: var(--accent, #6c8cff); color: #fff; padding: 6px 12px; border-radius: 6px; text-decoration: none; font-weight: 600;"> | |
| {{ _("Need help? Telegram") }} @helpforwacodebot | |
| </a> | |
| </div> | |
| {% endif %} | |
| <div class="content"> | |
| {% block content %}{% endblock %} | |
| </div> | |
| </div> | |
| </div> | |
| {# ---------------- Language switcher dropdown ---------------- #} | |
| <div class="lang-dropdown" id="lang-dropdown" style="display: none; position: absolute; top: 52px; right: 160px; z-index: 1000; background: var(--surface, #1a1d2b); border: 1px solid var(--border, #2a2d3a); border-radius: 10px; box-shadow: var(--shadow-lg, 0 10px 40px rgba(0,0,0,.4)); min-width: 160px; padding: 6px;"> | |
| {% for lang in available_languages() %} | |
| <a href="/lang?lang={{ lang.code }}&next_url={{ request.url.path }}" | |
| class="lang-option {% if request.cookies.get('panel_lang', _detect_lang()) == lang.code %}active{% endif %}" | |
| style="display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: 6px; color: var(--text); text-decoration: none; font-size: 13px; transition: background 120ms ease;"> | |
| <span style="width: 20px; text-align: center;">{{ lang.name }}</span> | |
| </a> | |
| {% endfor %} | |
| </div> | |
| {% else %} | |
| {# ---------------- Logged-out fallback ---------------- #} | |
| {# Public pages (login/register) do NOT extend base.html — they have their | |
| own full document. This branch only fires if a child template was | |
| wrongly rendered for an unauthenticated request. Render an empty | |
| shell so the child template's `{% block content %}` override still works | |
| without nesting. Note: jinja forbids defining the same block twice | |
| in one template, even across mutually-exclusive branches — so the | |
| fallback here uses a distinct ``public_content`` block name. #} | |
| <main class="main full"> | |
| <div class="content"> | |
| {% block public_content %}{% endblock %} | |
| </div> | |
| </main> | |
| {% endif %} | |
| <div class="toast-container" id="toasts"></div> | |
| {# ---------------- Flash messages ---------------- #} | |
| {# Server queues one-shot toasts in the ``panel_flash`` cookie via | |
| ``_flash_set`` in routes.py. ``_flash_middleware`` in app.py pops | |
| the cookie on EVERY request — not just /login and /register — and | |
| stuffs the decoded payload on ``request.state.flashes``. Any | |
| template that extends ``base.html`` therefore gets the toast | |
| regardless of where the POST->302 landed (e.g. POST /login | |
| redirects to /dashboard, which used to silently miss the | |
| "Welcome back!" toast). ``panel_flash.js`` reads the array and | |
| renders toasts top-right with the upgraded animations. #} | |
| <script> | |
| </script> | |
| <script src="/static/js/panel_flash.js?v={{ panel_assets_v() }}" defer></script> | |
| {# ---------------- AI chat overlay ---------------- #} | |
| <div class="ai-overlay" id="ai-overlay" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="ai-overlay-title"> | |
| <div class="ai-backdrop" data-ai-close></div> | |
| <div class="ai-panel"> | |
| <header class="ai-head"> | |
| <div class="ai-head-left"> | |
| <svg class="ai-spark" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" width="18" height="18"> | |
| <path d="m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3z"/> | |
| <path d="M5 3v4"/> | |
| <path d="M19 17v4"/> | |
| <path d="M3 5h4"/> | |
| <path d="M17 19h4"/> | |
| </svg> | |
| <div> | |
| <div id="ai-overlay-title" class="ai-title">{{ _("Panel assistant") }}</div> | |
| <div class="ai-sub" id="ai-overlay-sub">{{ _("Powered by NVIDIA Nemotron") }}</div> | |
| </div> | |
| </div> | |
| <div class="ai-head-right"> | |
| <span class="ai-model-tag" id="ai-model-tag" hidden></span> | |
| <button class="ai-icon" type="button" data-ai-clear title="Clear conversation" aria-label="Clear conversation"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" width="16" height="16"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg> | |
| </button> | |
| <button class="ai-icon" type="button" data-ai-close title="Close" aria-label="Close"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" width="16" height="16"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> | |
| </button> | |
| </div> | |
| </header> | |
| <div class="ai-transcript" id="ai-transcript" aria-live="polite"> | |
| <div class="ai-msg ai-msg-assistant" data-empty> | |
| <div class="ai-bubble"> | |
| {{ _("Ask me about bots, deploys, logs — or just say \u201cwhat does this panel do?\u201d.") | safe }} | |
| <div class="ai-hint">{{ _("Try:") }} <button type="button" class="ai-quick" data-ai-quick="{{ _('how do I deploy a bot?') }}">{{ _("how do I deploy a bot?") }}</button> <button type="button" class="ai-quick" data-ai-quick="{{ _('what to check when a bot is in ERROR?') }}">{{ _("what to check when a bot is in ERROR?") }}</button> <button type="button" class="ai-quick" data-ai-quick="{{ _('explain the quota system') }}">{{ _("explain the quota system") }}</button></div> | |
| </div> | |
| </div> | |
| </div> | |
| <form class="ai-form" id="ai-form" autocomplete="off"> | |
| <textarea id="ai-input" name="message" rows="1" placeholder="{{ _('Ask anything… (Enter — send, Shift+Enter — newline, Esc — close)') }}" maxlength="8000"></textarea> | |
| <button type="submit" class="ai-send" id="ai-send" title="Send" aria-label="Send"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" width="14" height="14"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg> | |
| </button> | |
| </form> | |
| </div> | |
| </div> | |
| <script src="/static/js/panel.js?v={{ panel_assets_v() }}" defer></script> | |
| <script src="/static/js/ai_chat.js?v={{ panel_assets_v() }}" defer></script> | |
| <script> | |
| // Language switcher dropdown | |
| (function () { | |
| var btn = document.getElementById('lang-toggle-btn'); | |
| var dropdown = document.getElementById('lang-dropdown'); | |
| if (!btn || !dropdown) return; | |
| btn.addEventListener('click', function (e) { | |
| e.stopPropagation(); | |
| var visible = dropdown.style.display === 'none'; | |
| dropdown.style.display = visible ? 'block' : 'none'; | |
| }); | |
| document.addEventListener('click', function () { | |
| dropdown.style.display = 'none'; | |
| }); | |
| dropdown.addEventListener('click', function (e) { | |
| e.stopPropagation(); | |
| }); | |
| })(); | |
| </script> | |
| {% block scripts %}{% endblock %} | |
| </body> | |
| </html> | |