Download static/css/ai_chat.css from ItsBounvy/bot_host: direct link, hf CLI and curl.
- Browser
- Download file 11.3 kB
-
https://huggingface.co/spaces/ItsBounvy/bot_host/resolve/main/static/css/ai_chat.css
- Command line
-
hf download hf://spaces/ItsBounvy/bot_host/static/css/ai_chat.css
-
curl -L -o ai_chat.css https://huggingface.co/spaces/ItsBounvy/bot_host/resolve/main/static/css/ai_chat.css
11.3 kB
| /* ============================================================ | |
| MMaxAI Hosting Panel — AI chat overlay | |
| ------------------------------------------------------------ | |
| Floating overlay that opens from either the dedicated | |
| "Ask AI" button in the topbar or the inline cmd-bar | |
| Enter-press. Layout uses the same dark Copilot tokens | |
| already defined in copilot.css, so it picks up theme | |
| changes automatically. | |
| ============================================================ */ | |
| /* AI trigger button (topbar) — sits between Cmd+K and the | |
| language toggle. Mirrors the .search-trigger shape. */ | |
| .ai-trigger { | |
| display: inline-flex; align-items: center; gap: 10px; | |
| width: auto; | |
| height: 36px; | |
| padding: 0 14px; | |
| border-radius: var(--radius-pill); | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| color: var(--text-faint); | |
| font-size: 13px; | |
| cursor: pointer; | |
| transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease); | |
| text-align: left; | |
| } | |
| .ai-trigger:hover { background: var(--surface-hover); color: var(--text); border-color: var(--border-strong); } | |
| .ai-trigger svg { width: 14px; height: 14px; flex: 0 0 auto; color: var(--accent-2); } | |
| .ai-trigger .label { flex: 1; } | |
| .ai-trigger kbd { | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| background: var(--surface-2); | |
| border: 1px solid var(--border); | |
| color: var(--text-muted); | |
| font-family: var(--font); | |
| font-size: 11px; | |
| font-weight: 600; | |
| } | |
| /* AI trigger — a touch more eye-catching so people actually notice it. | |
| Slightly stronger border, accent-tinted icon, and a soft glow on hover. */ | |
| .ai-trigger-primary { | |
| background: linear-gradient(180deg, rgba(124,108,255,0.10), rgba(124,108,255,0.04)); | |
| border-color: rgba(124,108,255,0.45); | |
| color: var(--text); | |
| } | |
| .ai-trigger-primary:hover { | |
| background: linear-gradient(180deg, rgba(124,108,255,0.18), rgba(124,108,255,0.06)); | |
| border-color: rgba(124,108,255,0.75); | |
| box-shadow: 0 0 0 3px rgba(124,108,255,0.12); | |
| } | |
| .ai-trigger-primary svg { color: var(--accent); } | |
| /* Hide the AI trigger when no API key is set. The JS toggles | |
| ``[data-ai-disabled]`` on <body> based on /api/ai/status. */ | |
| body[data-ai-disabled="true"] .ai-trigger { display: none; } | |
| /* ---- Overlay shell ---- */ | |
| .ai-overlay { | |
| position: fixed; inset: 0; | |
| z-index: 200; | |
| display: none; | |
| align-items: center; | |
| justify-content: center; | |
| pointer-events: none; | |
| } | |
| .ai-overlay[data-open="true"] { | |
| display: flex; | |
| pointer-events: auto; | |
| animation: ai-fade 140ms var(--ease); | |
| } | |
| @keyframes ai-fade { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| .ai-backdrop { | |
| position: absolute; inset: 0; | |
| background: rgba(4, 6, 14, 0.55); | |
| backdrop-filter: blur(8px); | |
| -webkit-backdrop-filter: blur(8px); | |
| } | |
| .ai-panel { | |
| position: relative; | |
| width: min(720px, calc(100% - 32px)); | |
| height: min(720px, calc(100vh - 80px)); | |
| background: var(--bg-elev-1); | |
| border: 1px solid var(--border-strong); | |
| border-radius: var(--radius-lg); | |
| box-shadow: var(--shadow-lg), 0 0 0 1px rgba(255,255,255,0.03); | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| animation: ai-pop 180ms var(--ease); | |
| } | |
| @keyframes ai-pop { | |
| from { transform: translateY(8px) scale(0.98); opacity: 0; } | |
| to { transform: translateY(0) scale(1); opacity: 1; } | |
| } | |
| @media (max-width: 720px) { | |
| .ai-panel { | |
| height: calc(100vh - 24px); | |
| border-radius: var(--radius); | |
| } | |
| } | |
| /* ---- Header ---- */ | |
| .ai-head { | |
| display: flex; align-items: center; | |
| gap: 12px; | |
| padding: 14px 18px; | |
| border-bottom: 1px solid var(--border); | |
| background: rgba(10, 14, 28, 0.4); | |
| } | |
| .ai-head-left { | |
| display: flex; align-items: center; gap: 10px; | |
| flex: 1; min-width: 0; | |
| } | |
| .ai-spark { color: var(--accent-2); flex: 0 0 auto; } | |
| .ai-title { | |
| font-size: 15px; | |
| font-weight: 600; | |
| color: var(--text); | |
| letter-spacing: -0.005em; | |
| } | |
| .ai-sub { | |
| margin-top: 1px; | |
| font-size: 11.5px; | |
| color: var(--text-muted); | |
| } | |
| .ai-head-right { | |
| display: flex; align-items: center; gap: 6px; | |
| flex: 0 0 auto; | |
| } | |
| .ai-model-tag { | |
| font-size: 11px; | |
| padding: 3px 9px; | |
| border-radius: var(--radius-pill); | |
| background: var(--surface-2); | |
| color: var(--text-muted); | |
| border: 1px solid var(--border); | |
| font-family: var(--font-mono); | |
| margin-right: 4px; | |
| } | |
| .ai-icon { | |
| width: 32px; height: 32px; | |
| border-radius: 8px; | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| color: var(--text-muted); | |
| display: grid; place-items: center; | |
| cursor: pointer; | |
| transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease); | |
| } | |
| .ai-icon:hover { background: var(--surface-hover); color: var(--text); } | |
| /* ---- Transcript scroll ---- */ | |
| .ai-transcript { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 18px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| scrollbar-width: thin; | |
| scrollbar-color: rgba(255,255,255,0.18) transparent; | |
| /* Reserve room for the scrollbar so messages don't get a | |
| 1-frame jump when content overflows vs when it doesn't. */ | |
| scrollbar-gutter: stable; | |
| } | |
| /* WebKit (Chrome / Edge / Safari) — slim, themed scrollbar */ | |
| .ai-transcript::-webkit-scrollbar { width: 8px; height: 8px; } | |
| .ai-transcript::-webkit-scrollbar-track { background: transparent; } | |
| .ai-transcript::-webkit-scrollbar-thumb { | |
| background: rgba(255,255,255,0.14); | |
| border-radius: 999px; | |
| border: 2px solid transparent; | |
| background-clip: padding-box; | |
| } | |
| .ai-transcript::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.24); background-clip: padding-box; border: 2px solid transparent; } | |
| /* ---- Message bubbles ---- */ | |
| .ai-msg { | |
| display: flex; | |
| width: 100%; | |
| animation: ai-bubble-in 160ms var(--ease); | |
| } | |
| @keyframes ai-bubble-in { | |
| from { opacity: 0; transform: translateY(4px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .ai-msg-user { justify-content: flex-end; } | |
| .ai-msg-assistant { justify-content: flex-start; } | |
| .ai-bubble { | |
| max-width: 86%; | |
| padding: 10px 14px; | |
| border-radius: 14px; | |
| line-height: 1.5; | |
| font-size: 14px; | |
| white-space: pre-wrap; | |
| word-wrap: break-word; | |
| overflow-wrap: anywhere; | |
| } | |
| .ai-msg-user .ai-bubble { | |
| background: var(--surface-2); | |
| color: var(--text); | |
| border-bottom-right-radius: 4px; | |
| } | |
| .ai-msg-assistant .ai-bubble { | |
| background: var(--surface); | |
| color: var(--text); | |
| border: 1px solid var(--border); | |
| border-bottom-left-radius: 4px; | |
| } | |
| .ai-msg-error .ai-bubble { | |
| background: rgba(248, 113, 113, 0.10); | |
| color: var(--status-error); | |
| border: 1px solid rgba(248, 113, 113, 0.32); | |
| } | |
| /* Markdown-ish styling inside assistant bubbles. Light touch — | |
| the upstream is instructed to keep replies short, so we don't | |
| need a full markdown renderer. */ | |
| .ai-bubble code { | |
| background: var(--bg); | |
| padding: 1px 6px; | |
| border-radius: 5px; | |
| font-family: var(--font-mono); | |
| font-size: 12.5px; | |
| color: var(--accent-2); | |
| } | |
| .ai-bubble pre { | |
| background: var(--bg); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 10px 12px; | |
| overflow-x: auto; | |
| margin: 8px 0; | |
| font-family: var(--font-mono); | |
| font-size: 12.5px; | |
| line-height: 1.55; | |
| } | |
| .ai-bubble pre code { | |
| background: transparent; | |
| padding: 0; | |
| color: var(--text); | |
| } | |
| .ai-bubble strong { font-weight: 600; } | |
| .ai-bubble em { font-style: italic; color: var(--text-muted); } | |
| .ai-msg-meta { | |
| margin-top: 4px; | |
| font-size: 11px; | |
| color: var(--text-faint); | |
| } | |
| /* Quick-reply chips on the empty-state bubble. */ | |
| .ai-hint { | |
| margin-top: 10px; | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 6px; | |
| } | |
| .ai-quick { | |
| padding: 5px 10px; | |
| font-size: 12px; | |
| background: var(--bg); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius-pill); | |
| color: var(--text-muted); | |
| cursor: pointer; | |
| transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease); | |
| } | |
| .ai-quick:hover { | |
| background: var(--surface); | |
| color: var(--text); | |
| border-color: var(--border-strong); | |
| } | |
| /* Typing indicator — three pulsing dots. */ | |
| .ai-typing { | |
| display: inline-flex; | |
| gap: 4px; | |
| align-items: center; | |
| height: 1em; | |
| } | |
| .ai-typing span { | |
| width: 6px; height: 6px; | |
| border-radius: 50%; | |
| background: var(--text-muted); | |
| animation: ai-bounce 1.2s infinite ease-in-out; | |
| } | |
| .ai-typing span:nth-child(2) { animation-delay: 0.15s; } | |
| .ai-typing span:nth-child(3) { animation-delay: 0.3s; } | |
| @keyframes ai-bounce { | |
| 0%, 80%, 100% { opacity: 0.3; transform: translateY(0); } | |
| 40% { opacity: 1; transform: translateY(-3px); } | |
| } | |
| /* ---- Input form ---- */ | |
| .ai-form { | |
| display: flex; | |
| gap: 10px; | |
| align-items: flex-end; | |
| padding: 14px 16px; | |
| border-top: 1px solid var(--border); | |
| background: rgba(10, 14, 28, 0.4); | |
| } | |
| .ai-form textarea { | |
| flex: 1; | |
| resize: none; | |
| min-height: 44px; | |
| max-height: 200px; | |
| line-height: 1.45; | |
| padding: 12px 14px; | |
| border-radius: 12px; | |
| background: var(--bg); | |
| border: 1px solid var(--border); | |
| color: var(--text); | |
| font: inherit; | |
| font-size: 14px; | |
| outline: none; | |
| transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease); | |
| } | |
| .ai-form textarea:focus { | |
| border-color: var(--border-strong); | |
| box-shadow: 0 0 0 3px rgba(6, 182, 212, 0.18); | |
| } | |
| .ai-form textarea::placeholder { color: var(--text-faint); } | |
| .ai-send { | |
| width: 44px; height: 44px; | |
| border-radius: 12px; | |
| background: var(--text); | |
| color: var(--bg); | |
| display: grid; place-items: center; | |
| border: 0; | |
| cursor: pointer; | |
| transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease); | |
| flex: 0 0 auto; | |
| } | |
| .ai-send:hover { opacity: 0.9; } | |
| .ai-send:disabled { opacity: 0.4; cursor: not-allowed; } | |
| .ai-send svg { width: 14px; height: 14px; } | |
| .ai-send.is-streaming { background: var(--status-error); color: #1f0405; } | |
| /* Disabled state — overlay still renders the empty hint but the | |
| input doesn't accept content. Toggled by JS when /api/ai/status | |
| reports `enabled: false`. */ | |
| body[data-ai-disabled="true"] .ai-form textarea { display: none; } | |
| body[data-ai-disabled="true"] .ai-send { display: none; } | |
| body[data-ai-disabled="true"] .ai-form { | |
| padding: 18px; | |
| justify-content: center; | |
| color: var(--text-muted); | |
| font-size: 13px; | |
| } | |
| body[data-ai-disabled="true"] .ai-form::before { | |
| content: "AI assistant is not configured on this panel. Set NVIDIA_API_KEY in env."; | |
| } | |
| /* Empty-state message is hidden once there's any conversation. */ | |
| .ai-transcript[data-has-messages="true"] [data-empty] { display: none; } | |
| /* Mobile stacking — chat bubble spans more of the width on small | |
| screens, signature stays legible. */ | |
| @media (max-width: 540px) { | |
| .ai-bubble { max-width: 95%; } | |
| .ai-form { padding: 10px; } | |
| .ai-form textarea { font-size: 15px; } | |
| } | |
| /* ---- Cmd-bar → AI button trigger (topbar) is hidden on mobile | |
| to leave room for the Cmd+K shortcut. ---- */ | |
| @media (max-width: 720px) { | |
| .ai-trigger .label { display: none; } | |
| } | |