Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>GLM 5.2</title> | |
| <!-- Google Fonts --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600&family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <!-- Markdown Parser --> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <style> | |
| :root { | |
| /* Clean Minimalist Light Mode (Screenshot Aesthetics) */ | |
| --bg-color: #ffffff; | |
| --sidebar-bg: #ffffff; | |
| --sidebar-border: rgba(0, 0, 0, 0.06); | |
| --sidebar-icon-color: #71717a; | |
| --sidebar-icon-hover: #18181b; | |
| --sidebar-active-bg: #f4f4f5; | |
| --text-color: #09090b; | |
| --text-muted: #71717a; | |
| --text-terminal: #27272a; | |
| --input-bg: #ffffff; | |
| --input-border: rgba(0, 0, 0, 0.08); | |
| --input-shadow: 0 4px 20px rgba(0, 0, 0, 0.03), 0 10px 40px -10px rgba(0, 0, 0, 0.05); | |
| --btn-active-bg: #18181b; | |
| --btn-active-color: #ffffff; | |
| --btn-disabled-bg: #f4f4f5; | |
| --btn-disabled-color: #a1a1aa; | |
| --font-serif: 'Playfair Display', serif; | |
| --font-sans: 'Outfit', 'Inter', sans-serif; | |
| --font-mono: 'Fira Code', monospace; | |
| --msg-user-bg: transparent; | |
| --msg-assistant-bg: transparent; | |
| --code-bg: #18181b; | |
| --code-color: #f4f4f5; | |
| --accent-color: #18181b; | |
| --accent-bg: #f4f4f5; | |
| --watermark-color: rgba(0, 0, 0, 0.02); | |
| --capsule-bg: #f4f4f5; | |
| --capsule-text: #27272a; | |
| --capsule-border: rgba(0, 0, 0, 0.04); | |
| --cursor-color: #18181b; | |
| } | |
| /* Dark Modern Mode */ | |
| .dark-mode { | |
| --bg-color: #09090b; | |
| --sidebar-bg: #09090b; | |
| --sidebar-border: rgba(255, 255, 255, 0.06); | |
| --sidebar-icon-color: #a1a1aa; | |
| --sidebar-icon-hover: #ffffff; | |
| --sidebar-active-bg: #27272a; | |
| --text-color: #f4f4f5; | |
| --text-muted: #a1a1aa; | |
| --text-terminal: #e4e4e7; | |
| --input-bg: #09090b; | |
| --input-border: rgba(255, 255, 255, 0.08); | |
| --input-shadow: 0 4px 25px rgba(0, 0, 0, 0.4); | |
| --btn-active-bg: #ffffff; | |
| --btn-active-color: #09090b; | |
| --btn-disabled-bg: #27272a; | |
| --btn-disabled-color: #71717a; | |
| --code-bg: #27272a; | |
| --code-color: #f4f4f5; | |
| --accent-color: #ffffff; | |
| --accent-bg: #27272a; | |
| --watermark-color: rgba(255, 255, 255, 0.015); | |
| --capsule-bg: #27272a; | |
| --capsule-text: #e4e4e7; | |
| --capsule-border: rgba(255, 255, 255, 0.04); | |
| --cursor-color: #ffffff; | |
| } | |
| /* Classic Retro Hacker Terminal Theme */ | |
| .classic-terminal-mode { | |
| --bg-color: #050806; | |
| --sidebar-bg: #020403; | |
| --sidebar-border: rgba(51, 255, 51, 0.15); | |
| --sidebar-icon-color: rgba(51, 255, 51, 0.6); | |
| --sidebar-icon-hover: #33ff33; | |
| --sidebar-active-bg: rgba(51, 255, 51, 0.1); | |
| --text-color: #33ff33; | |
| --text-muted: rgba(51, 255, 51, 0.6); | |
| --text-terminal: #33ff33; | |
| --input-bg: #020403; | |
| --input-border: rgba(51, 255, 51, 0.3); | |
| --input-shadow: 0 0 15px rgba(51, 255, 51, 0.1); | |
| --btn-active-bg: #33ff33; | |
| --btn-active-color: #020403; | |
| --btn-disabled-bg: rgba(51, 255, 51, 0.05); | |
| --btn-disabled-color: rgba(51, 255, 51, 0.3); | |
| --font-serif: 'Fira Code', monospace; | |
| --font-sans: 'Fira Code', monospace; | |
| --font-mono: 'Fira Code', monospace; | |
| --code-bg: #020403; | |
| --code-color: #33ff33; | |
| --accent-color: #33ff33; | |
| --accent-bg: rgba(51, 255, 51, 0.08); | |
| --watermark-color: rgba(51, 255, 51, 0.02); | |
| --capsule-bg: rgba(51, 255, 51, 0.05); | |
| --capsule-text: #33ff33; | |
| --capsule-border: rgba(51, 255, 51, 0.2); | |
| --cursor-color: #33ff33; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| transition: background-color 0.25s ease, border-color 0.25s ease, color 0.15s ease; | |
| } | |
| body, html { | |
| height: 100%; | |
| width: 100%; | |
| font-family: var(--font-sans); | |
| background-color: var(--bg-color); | |
| color: var(--text-color); | |
| overflow: hidden; | |
| display: flex; | |
| } | |
| /* Sidebar Styling (Matches Screenshot Layout) */ | |
| .sidebar { | |
| width: 72px; | |
| height: 100%; | |
| background-color: var(--sidebar-bg); | |
| border-right: 1px solid var(--sidebar-border); | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| padding: 16px 0; | |
| justify-content: space-between; | |
| z-index: 10; | |
| } | |
| .sidebar-top { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 16px; | |
| width: 100%; | |
| } | |
| .sidebar-bottom { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 16px; | |
| width: 100%; | |
| } | |
| .sidebar-btn { | |
| width: 44px; | |
| height: 44px; | |
| border-radius: 8px; | |
| border: 1px solid transparent; | |
| background: transparent; | |
| color: var(--sidebar-icon-color); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| position: relative; | |
| } | |
| .sidebar-btn:hover { | |
| color: var(--sidebar-icon-hover); | |
| background-color: var(--sidebar-active-bg); | |
| } | |
| .sidebar-btn.active { | |
| background-color: var(--sidebar-active-bg); | |
| color: var(--sidebar-icon-hover); | |
| border-color: var(--sidebar-border); | |
| } | |
| /* Top Brand Button (Boxed Z Logo) */ | |
| .brand-btn { | |
| width: 44px; | |
| height: 44px; | |
| border-radius: 8px; | |
| background-color: var(--bg-color); | |
| border: 1px solid var(--sidebar-border); | |
| color: var(--text-color); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| font-weight: 700; | |
| font-family: var(--font-sans); | |
| font-size: 18px; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.02); | |
| } | |
| .brand-btn:hover { | |
| background-color: var(--sidebar-active-bg); | |
| } | |
| /* Tooltip */ | |
| .sidebar-btn::after { | |
| content: attr(data-tooltip); | |
| position: absolute; | |
| left: 80px; | |
| background: #18181b; | |
| color: #ffffff; | |
| padding: 4px 8px; | |
| border-radius: 4px; | |
| font-size: 11px; | |
| white-space: nowrap; | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: opacity 0.2s ease, transform 0.2s ease; | |
| transform: translateX(-10px); | |
| z-index: 100; | |
| font-family: var(--font-sans); | |
| } | |
| .sidebar-btn:hover::after { | |
| opacity: 1; | |
| transform: translateX(0); | |
| } | |
| /* Main Workspace */ | |
| .workspace { | |
| flex: 1; | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| position: relative; | |
| } | |
| /* Watermark Background Z */ | |
| .watermark-container { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| width: 450px; | |
| height: 450px; | |
| pointer-events: none; | |
| z-index: 1; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .watermark-svg { | |
| width: 100%; | |
| height: 100%; | |
| stroke: var(--watermark-color); | |
| fill: none; | |
| stroke-width: 0.8; | |
| stroke-linecap: round; | |
| stroke-linejoin: round; | |
| } | |
| /* Welcome Center State */ | |
| .welcome-panel { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| text-align: center; | |
| z-index: 2; | |
| padding: 32px; | |
| user-select: none; | |
| } | |
| .welcome-title { | |
| font-family: var(--font-serif); | |
| font-size: 48px; | |
| font-weight: 400; | |
| margin-bottom: 16px; | |
| color: var(--text-color); | |
| letter-spacing: -0.5px; | |
| animation: fadeInUp 0.8s ease; | |
| } | |
| .welcome-subtitle { | |
| font-family: var(--font-sans); | |
| font-size: 16px; | |
| color: var(--text-muted); | |
| font-weight: 400; | |
| letter-spacing: 0.1px; | |
| animation: fadeInUp 0.8s ease 0.1s both; | |
| } | |
| /* Chat Terminal Console Output */ | |
| .chat-container { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 40px 10%; | |
| display: none; | |
| flex-direction: column; | |
| gap: 24px; | |
| z-index: 2; | |
| scroll-behavior: smooth; | |
| } | |
| .chat-container.terminal-layout { | |
| background-size: 20px 20px; | |
| background-image: | |
| linear-gradient(to right, rgba(0, 0, 0, 0.01) 1px, transparent 1px), | |
| linear-gradient(to bottom, rgba(0, 0, 0, 0.01) 1px, transparent 1px); | |
| } | |
| .dark-mode .chat-container.terminal-layout { | |
| background-image: | |
| linear-gradient(to right, rgba(255, 255, 255, 0.005) 1px, transparent 1px), | |
| linear-gradient(to bottom, rgba(255, 255, 255, 0.005) 1px, transparent 1px); | |
| } | |
| .classic-terminal-mode .chat-container.terminal-layout { | |
| background-image: | |
| linear-gradient(to right, rgba(51, 255, 51, 0.015) 1px, transparent 1px), | |
| linear-gradient(to bottom, rgba(51, 255, 51, 0.015) 1px, transparent 1px); | |
| } | |
| .message-row { | |
| display: flex; | |
| flex-direction: column; | |
| width: 100%; | |
| animation: fadeIn 0.3s ease-out; | |
| line-height: 1.6; | |
| } | |
| /* Terminal Prompt Headers */ | |
| .prompt-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-bottom: 4px; | |
| user-select: none; | |
| } | |
| .prompt-prefix-user { | |
| color: #3b82f6; /* Modern Blue */ | |
| font-weight: 600; | |
| } | |
| .classic-terminal-mode .prompt-prefix-user { | |
| color: #33ff33; | |
| } | |
| .prompt-prefix-system { | |
| color: #10b981; /* Modern Green */ | |
| font-weight: 600; | |
| } | |
| .classic-terminal-mode .prompt-prefix-system { | |
| color: #33ff33; | |
| } | |
| .prompt-command { | |
| color: var(--text-muted); | |
| font-size: 12px; | |
| } | |
| .prompt-text { | |
| color: var(--text-color); | |
| white-space: pre-wrap; | |
| word-break: break-word; | |
| padding-left: 4px; | |
| } | |
| .message-content { | |
| padding-left: 16px; | |
| border-left: 2px solid var(--sidebar-border); | |
| color: var(--text-terminal); | |
| margin-top: 4px; | |
| word-break: break-word; | |
| } | |
| .message-content p { | |
| margin-bottom: 12px; | |
| } | |
| .message-content p:last-child { | |
| margin-bottom: 0; | |
| } | |
| /* Code Block Styling */ | |
| .message-content pre { | |
| background-color: var(--code-bg); | |
| color: var(--code-color); | |
| padding: 14px; | |
| border-radius: 8px; | |
| overflow-x: auto; | |
| margin: 12px 0; | |
| border: 1px solid var(--sidebar-border); | |
| position: relative; | |
| } | |
| .message-content code { | |
| font-family: var(--font-mono); | |
| font-size: 13px; | |
| } | |
| /* Floating Message Input Panel (Matches GLM-5.2 Layout) */ | |
| .input-panel { | |
| width: 100%; | |
| max-width: 800px; | |
| margin: 0 auto 32px auto; | |
| padding: 0 24px; | |
| z-index: 5; | |
| } | |
| .input-container { | |
| background-color: var(--input-bg); | |
| border: 1px solid var(--input-border); | |
| border-radius: 16px; | |
| padding: 16px 20px; | |
| box-shadow: var(--input-shadow); | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| .textarea-wrapper { | |
| width: 100%; | |
| } | |
| .chat-input { | |
| width: 100%; | |
| border: none; | |
| outline: none; | |
| background: transparent; | |
| color: var(--text-color); | |
| font-family: var(--font-sans); | |
| font-size: 16px; | |
| resize: none; | |
| max-height: 200px; | |
| height: 24px; | |
| line-height: 1.5; | |
| } | |
| .chat-input::placeholder { | |
| color: var(--text-muted); | |
| opacity: 0.8; | |
| } | |
| /* Input Controls Panel */ | |
| .input-controls { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| width: 100%; | |
| } | |
| .controls-left { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .action-circle-btn { | |
| width: 32px; | |
| height: 32px; | |
| border-radius: 50%; | |
| border: 1px solid var(--input-border); | |
| background: transparent; | |
| color: var(--text-color); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| } | |
| .action-circle-btn:hover { | |
| background-color: var(--sidebar-active-bg); | |
| } | |
| /* Capsule Agent Button (Screenshot style) */ | |
| .agent-capsule { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| background-color: var(--capsule-bg); | |
| border: 1px solid var(--capsule-border); | |
| color: var(--capsule-text); | |
| padding: 6px 14px; | |
| border-radius: 20px; | |
| font-family: var(--font-sans); | |
| font-size: 13px; | |
| font-weight: 500; | |
| cursor: pointer; | |
| user-select: none; | |
| border: 1px solid var(--sidebar-border); | |
| } | |
| .agent-capsule:hover { | |
| background-color: var(--sidebar-active-bg); | |
| filter: brightness(0.95); | |
| } | |
| .agent-capsule svg { | |
| color: var(--text-muted); | |
| } | |
| /* Send Circular Button (Screenshot style) */ | |
| .send-btn { | |
| width: 32px; | |
| height: 32px; | |
| border-radius: 50%; | |
| border: none; | |
| background-color: var(--btn-disabled-bg); | |
| color: var(--btn-disabled-color); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| } | |
| .send-btn.active { | |
| background-color: var(--btn-active-bg); | |
| color: var(--btn-active-color); | |
| } | |
| .send-btn svg { | |
| width: 16px; | |
| height: 16px; | |
| } | |
| /* Blinking Terminal Cursor */ | |
| .terminal-cursor { | |
| display: inline-block; | |
| width: 8px; | |
| height: 15px; | |
| background-color: var(--cursor-color); | |
| margin-left: 4px; | |
| animation: blink 0.8s infinite; | |
| vertical-align: middle; | |
| } | |
| /* Settings Drawer overlay */ | |
| .settings-drawer { | |
| position: absolute; | |
| left: 73px; | |
| top: 0; | |
| bottom: 0; | |
| width: 320px; | |
| background-color: var(--sidebar-bg); | |
| border-right: 1px solid var(--sidebar-border); | |
| z-index: 9; | |
| box-shadow: 10px 0 30px rgba(0,0,0,0.02); | |
| padding: 24px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| transform: translateX(-105%); | |
| transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .settings-drawer.open { | |
| transform: translateX(0); | |
| } | |
| .settings-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 8px; | |
| } | |
| .settings-title { | |
| font-size: 18px; | |
| font-weight: 600; | |
| } | |
| .close-drawer-btn { | |
| background: transparent; | |
| border: none; | |
| color: var(--text-muted); | |
| cursor: pointer; | |
| } | |
| .close-drawer-btn:hover { | |
| color: var(--text-color); | |
| } | |
| .settings-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| .settings-label { | |
| font-size: 13px; | |
| font-weight: 500; | |
| color: var(--text-muted); | |
| } | |
| .settings-input, .settings-select { | |
| width: 100%; | |
| background-color: var(--input-bg); | |
| border: 1px solid var(--sidebar-border); | |
| padding: 10px 12px; | |
| border-radius: 8px; | |
| color: var(--text-color); | |
| font-family: var(--font-sans); | |
| font-size: 14px; | |
| outline: none; | |
| } | |
| .settings-input:focus, .settings-select:focus { | |
| border-color: var(--text-color); | |
| } | |
| /* CLI Info Alert */ | |
| .cli-info-banner { | |
| background-color: var(--accent-bg); | |
| color: var(--text-color); | |
| border: 1px solid var(--sidebar-border); | |
| border-radius: 8px; | |
| padding: 12px; | |
| font-size: 12px; | |
| line-height: 1.5; | |
| font-family: var(--font-mono); | |
| } | |
| /* Matrix Canvas */ | |
| #matrix-canvas { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| z-index: 0; | |
| opacity: 0.12; | |
| display: none; | |
| } | |
| /* Animations */ | |
| @keyframes blink { | |
| 0%, 49% { opacity: 1; } | |
| 50%, 100% { opacity: 0; } | |
| } | |
| @keyframes fadeInUp { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| /* Scrollbar styling */ | |
| ::-webkit-scrollbar { | |
| width: 6px; | |
| height: 6px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--sidebar-border); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: var(--text-muted); | |
| } | |
| /* System notification log */ | |
| .system-log { | |
| color: #eab308; | |
| font-style: italic; | |
| } | |
| .classic-terminal-mode .system-log { | |
| color: #ffb000; | |
| } | |
| /* Welcoming terminal dashboard box */ | |
| .welcome-terminal-box { | |
| background-color: var(--input-bg); | |
| border: 1px solid var(--input-border); | |
| border-radius: 12px; | |
| width: 100%; | |
| max-width: 480px; | |
| box-shadow: var(--input-shadow); | |
| text-align: left; | |
| margin: 20px 0; | |
| overflow: hidden; | |
| animation: fadeInUp 0.8s ease 0.2s both; | |
| } | |
| .welcome-terminal-header { | |
| background-color: var(--sidebar-bg); | |
| border-bottom: 1px solid var(--sidebar-border); | |
| padding: 10px 16px; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .terminal-dot { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| display: inline-block; | |
| } | |
| .terminal-dot.red { background-color: #ef4444; } | |
| .terminal-dot.yellow { background-color: #f59e0b; } | |
| .terminal-dot.green { background-color: #10b981; } | |
| .terminal-title { | |
| font-family: var(--font-mono); | |
| font-size: 11px; | |
| color: var(--text-muted); | |
| margin-left: 8px; | |
| flex-grow: 1; | |
| text-align: center; | |
| margin-right: 30px; | |
| } | |
| .welcome-terminal-content { | |
| padding: 16px 20px; | |
| margin: 0; | |
| overflow: auto; | |
| } | |
| .welcome-terminal-content code { | |
| font-family: var(--font-mono); | |
| font-size: 13px; | |
| color: var(--text-terminal); | |
| line-height: 1.6; | |
| } | |
| /* Chat Status Bar styles */ | |
| .chat-status-bar { | |
| background-color: var(--sidebar-bg); | |
| border-bottom: 1px solid var(--sidebar-border); | |
| padding: 10px 24px; | |
| font-size: 12px; | |
| display: none; | |
| align-items: center; | |
| justify-content: space-between; | |
| color: var(--text-muted); | |
| z-index: 3; | |
| user-select: none; | |
| } | |
| .chat-status-bar .status-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .chat-status-bar .status-dot { | |
| width: 6px; | |
| height: 6px; | |
| border-radius: 50%; | |
| background-color: #10b981; | |
| } | |
| /* Message styling variations based on Mode */ | |
| .message-row.style-standard { | |
| font-family: var(--font-sans); | |
| font-size: 15px; | |
| line-height: 1.6; | |
| } | |
| .message-row.style-standard .prompt-header { | |
| margin-bottom: 6px; | |
| } | |
| .message-row.style-standard .standard-sender-user { | |
| font-weight: 600; | |
| font-size: 13px; | |
| color: var(--text-color); | |
| background-color: var(--sidebar-active-bg); | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| } | |
| .message-row.style-standard .standard-sender-system { | |
| font-weight: 600; | |
| font-size: 13px; | |
| color: var(--text-color); | |
| } | |
| .message-row.style-standard .prompt-text { | |
| padding-left: 0; | |
| color: var(--text-color); | |
| font-size: 15px; | |
| } | |
| .message-row.style-standard .message-content { | |
| padding-left: 0; | |
| border-left: none; | |
| color: var(--text-color); | |
| font-size: 15px; | |
| } | |
| .message-row.style-standard .message-content p { | |
| margin-bottom: 12px; | |
| } | |
| /* Standard Mode code blocks styling */ | |
| .message-row.style-standard .message-content pre { | |
| background-color: var(--capsule-bg); | |
| color: var(--text-color); | |
| border: 1px solid var(--sidebar-border); | |
| } | |
| .message-row.style-standard .message-content code { | |
| color: inherit; | |
| } | |
| .message-row.style-terminal { | |
| font-family: var(--font-mono); | |
| font-size: 14px; | |
| line-height: 1.6; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Matrix Code Rain Canvas --> | |
| <canvas id="matrix-canvas"></canvas> | |
| <!-- Left Sidebar (Screenshots structure) --> | |
| <nav class="sidebar"> | |
| <div class="sidebar-top"> | |
| <!-- Brand GLM Logo (Minimalist Boxed Header) --> | |
| <button class="brand-btn" id="home-brand-btn" onclick="goToHome()" title="GLM 5.2 Workspace" style="font-size: 11px; font-weight: 800; letter-spacing: -0.5px;">GLM</button> | |
| <button class="sidebar-btn" id="new-chat-btn" onclick="clearSession()" data-tooltip="New Workspace Chat"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/> | |
| <path d="M18.5 2.5a2.121 2.121 0 1 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="sidebar-bottom"> | |
| <!-- Theme Toggler (Sun/Moon/Retro CLI Terminal) --> | |
| <button class="sidebar-btn" id="theme-btn" onclick="cycleTheme()" data-tooltip="Cycle Theme Mode"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/> | |
| <circle cx="12" cy="12" r="4"/> | |
| </svg> | |
| </button> | |
| <!-- Configuration / Settings Cog --> | |
| <button class="sidebar-btn" id="settings-btn" onclick="toggleSettings()" data-tooltip="Settings & Credentials"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <circle cx="12" cy="12" r="3"/> | |
| <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/> | |
| </svg> | |
| </button> | |
| </div> | |
| </nav> | |
| <!-- Sliding Settings Drawer --> | |
| <div class="settings-drawer" id="settings-drawer"> | |
| <div class="settings-header"> | |
| <span class="settings-title">Configuration Drawer</span> | |
| <button class="close-drawer-btn" onclick="toggleSettings()"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <line x1="18" y1="6" x2="6" y2="18"/> | |
| <line x1="6" y1="6" x2="18" y2="18"/> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="settings-group"> | |
| <label class="settings-label" for="temperature-input">Temperature (Creativity)</label> | |
| <input type="range" id="temperature-input" min="0.1" max="1.5" step="0.1" value="0.7" oninput="document.getElementById('temp-val').innerText = this.value; saveConfig()"> | |
| <span id="temp-val" style="font-size:12px; margin-top:2px;">0.7</span> | |
| </div> | |
| <div class="settings-group"> | |
| <label class="settings-label" for="system-input">System Instruction / Agent Prompt</label> | |
| <textarea id="system-input" class="settings-input" rows="4" placeholder="You are a helpful assistant..." onchange="saveConfig()"></textarea> | |
| </div> | |
| <div class="cli-info-banner"> | |
| <strong>Terminal Mode Info:</strong><br> | |
| You can type commands directly in the chat bar!<br> | |
| • <code>/clear</code>: Reset chat<br> | |
| • <code>/theme</code>: Cycle styles<br> | |
| • <code>/help</code>: Display CLI manual<br> | |
| • <code>/matrix</code>: Toggle digital rain | |
| </div> | |
| </div> | |
| <!-- Main Pane --> | |
| <div class="workspace"> | |
| <!-- SVG Watermark Background 5.2 (Double Stencil Outline) --> | |
| <div class="watermark-container"> | |
| <svg class="watermark-svg" viewBox="0 0 100 100"> | |
| <path d="M 30,25 H 70 V 48 H 30 V 75 H 70" /> | |
| <path d="M 35,30 H 65 V 43 H 35 V 70 H 65" stroke-dasharray="2, 2" /> | |
| </svg> | |
| </div> | |
| <!-- Welcome Initial Panel --> | |
| <div class="welcome-panel" id="welcome-panel"> | |
| <h1 class="welcome-title">GLM 5.2</h1> | |
| <p class="welcome-subtitle" style="font-family: var(--font-serif); font-size: 24px; margin-bottom: 12px;">Create anything you can imagine</p> | |
| <!-- Sleek terminal status dashboard inside welcome screen --> | |
| <div class="welcome-terminal-box"> | |
| <div class="welcome-terminal-header"> | |
| <span class="terminal-dot red"></span> | |
| <span class="terminal-dot yellow"></span> | |
| <span class="terminal-dot green"></span> | |
| <span class="terminal-title">visitor@glm5.2: ~</span> | |
| </div> | |
| <pre class="welcome-terminal-content"><code>system_boot_sequence: SUCCESS | |
| model_identity : zai-org/GLM-5.2:fireworks-ai | |
| connection_state : ONLINE | |
| stream_completions : ACTIVE | |
| Type a query or /help to interact with GLM 5.2...</code></pre> | |
| </div> | |
| <p class="welcome-subtext" style="font-size: 13px; color: var(--text-muted); margin-top: 8px;">Interact with GLM 5.2 and explore the boundless creative world</p> | |
| </div> | |
| <!-- Chat Status Bar --> | |
| <div class="chat-status-bar" id="chat-status-bar"> | |
| <span class="status-item"><span class="status-dot"></span> GLM 5.2 CLI Session</span> | |
| <span class="status-item font-mono" style="font-family: var(--font-mono)">Model: zai-org/GLM-5.2:fireworks-ai</span> | |
| <span class="status-item" id="status-agent-mode">Mode: Standard</span> | |
| </div> | |
| <!-- Chat Logs Console --> | |
| <div class="chat-container" id="chat-container"></div> | |
| <!-- Input Bar (Matches Floating GLM-5.2 Layout) --> | |
| <div class="input-panel"> | |
| <div class="input-container"> | |
| <div class="textarea-wrapper"> | |
| <textarea | |
| id="chat-input" | |
| class="chat-input" | |
| placeholder="Send a Message" | |
| rows="1" | |
| oninput="adjustInputHeight(this); checkInputLength(this)" | |
| onkeydown="handleKeyPress(event)" | |
| ></textarea> | |
| </div> | |
| <div class="input-controls"> | |
| <div class="controls-left"> | |
| <!-- Agent pill button (Standard / Terminal toggle) --> | |
| <div class="agent-capsule" id="agent-capsule" onclick="toggleAgentMode()"> | |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <polyline points="4 17 10 11 4 5"/> | |
| <line x1="12" y1="19" x2="20" y2="19"/> | |
| </svg> | |
| <span id="agent-pill-label">Standard Mode</span> | |
| </div> | |
| </div> | |
| <!-- Send Up-Arrow Button --> | |
| <button class="send-btn" id="send-btn" onclick="submitChat()" disabled> | |
| <!-- Rounded arrow up --> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> | |
| <line x1="12" y1="19" x2="12" y2="5"/> | |
| <polyline points="5 12 12 5 19 12"/> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Scripting and logic --> | |
| <script> | |
| // State variables | |
| let chatHistory = []; // Local memory of session conversation | |
| let activeAgentMode = 'standard'; // standard, terminal | |
| let currentTheme = 'light'; // light, dark, classic-terminal | |
| let matrixInterval = null; // Digital rain interval reference | |
| // On Page Load: Hydrate settings from local storage | |
| window.addEventListener('DOMContentLoaded', () => { | |
| const savedTemp = localStorage.getItem('temp'); | |
| if (savedTemp) { | |
| document.getElementById('temperature-input').value = savedTemp; | |
| document.getElementById('temp-val').innerText = savedTemp; | |
| } | |
| const savedSystem = localStorage.getItem('system_prompt'); | |
| if (savedSystem) { | |
| document.getElementById('system-input').value = savedSystem; | |
| } | |
| const savedTheme = localStorage.getItem('theme'); | |
| if (savedTheme) { | |
| applyTheme(savedTheme); | |
| } | |
| }); | |
| // Config Drawer toggles | |
| function toggleSettings() { | |
| const drawer = document.getElementById('settings-drawer'); | |
| drawer.classList.toggle('open'); | |
| } | |
| function saveConfig() { | |
| const temp = document.getElementById('temperature-input').value; | |
| const system = document.getElementById('system-input').value.trim(); | |
| localStorage.setItem('temp', temp); | |
| localStorage.setItem('system_prompt', system); | |
| } | |
| // Navigation back home | |
| function goToHome() { | |
| document.getElementById('welcome-panel').style.display = 'flex'; | |
| document.getElementById('chat-container').style.display = 'none'; | |
| document.getElementById('chat-status-bar').style.display = 'none'; | |
| } | |
| // Clear Workspace History | |
| function clearSession() { | |
| chatHistory = []; | |
| const container = document.getElementById('chat-container'); | |
| container.innerHTML = ''; | |
| // Log local terminal reset | |
| appendLocalLog("Session reset. Terminal memory buffer cleared."); | |
| goToHome(); | |
| } | |
| // Append locally printed logs (for CLI actions) | |
| function appendLocalLog(text) { | |
| const container = document.getElementById('chat-container'); | |
| // Check if welcome panel is visible, hide it | |
| if (document.getElementById('welcome-panel').style.display !== 'none') { | |
| document.getElementById('welcome-panel').style.display = 'none'; | |
| container.style.display = 'flex'; | |
| document.getElementById('chat-status-bar').style.display = 'flex'; | |
| } | |
| const logRow = document.createElement('div'); | |
| logRow.className = 'message-row'; | |
| logRow.innerHTML = ` | |
| <div class="prompt-header"> | |
| <span class="prompt-prefix-system">system@glm5.2:~$</span> | |
| <span class="prompt-command">info</span> | |
| </div> | |
| <div class="prompt-text system-log">${text}</div> | |
| `; | |
| container.appendChild(logRow); | |
| container.scrollTop = container.scrollHeight; | |
| } | |
| // Adjusts input textarea height depending on characters typed | |
| function adjustInputHeight(textarea) { | |
| textarea.style.height = 'auto'; | |
| textarea.style.height = (textarea.scrollHeight) + 'px'; | |
| } | |
| // Toggles the send button color and activity state based on input presence | |
| function checkInputLength(textarea) { | |
| const sendBtn = document.getElementById('send-btn'); | |
| if (textarea.value.trim().length > 0) { | |
| sendBtn.classList.add('active'); | |
| sendBtn.disabled = false; | |
| } else { | |
| sendBtn.classList.remove('active'); | |
| sendBtn.disabled = true; | |
| } | |
| } | |
| // Key Press handling: Shift+Enter inserts newline, Enter submits | |
| function handleKeyPress(event) { | |
| if (event.key === 'Enter' && !event.shiftKey) { | |
| event.preventDefault(); | |
| submitChat(); | |
| } | |
| } | |
| // Toggles theme through cycle: Light -> Dark -> Retro CLI Terminal | |
| function cycleTheme() { | |
| if (currentTheme === 'light') { | |
| applyTheme('dark'); | |
| } else if (currentTheme === 'dark') { | |
| applyTheme('classic-terminal'); | |
| } else { | |
| applyTheme('light'); | |
| } | |
| } | |
| function applyTheme(themeName) { | |
| document.body.classList.remove('dark-mode', 'classic-terminal-mode'); | |
| const matrixCanvas = document.getElementById('matrix-canvas'); | |
| // Stop matrix loop unless in classic-terminal-mode | |
| if (matrixInterval) { | |
| clearInterval(matrixInterval); | |
| matrixInterval = null; | |
| } | |
| matrixCanvas.style.display = 'none'; | |
| if (themeName === 'dark') { | |
| document.body.classList.add('dark-mode'); | |
| currentTheme = 'dark'; | |
| } else if (themeName === 'classic-terminal') { | |
| document.body.classList.add('classic-terminal-mode'); | |
| currentTheme = 'classic-terminal'; | |
| initMatrixRain(); | |
| } else { | |
| currentTheme = 'light'; | |
| } | |
| localStorage.setItem('theme', currentTheme); | |
| } | |
| // Toggle Agents (Terminal mode or Standard chat mode) | |
| function toggleAgentMode() { | |
| const pillLabel = document.getElementById('agent-pill-label'); | |
| const systemTextarea = document.getElementById('system-input'); | |
| const chatContainer = document.getElementById('chat-container'); | |
| if (activeAgentMode === 'standard') { | |
| activeAgentMode = 'terminal'; | |
| pillLabel.innerText = "Terminal Mode"; | |
| systemTextarea.value = "You are a senior system administrator and Unix terminal helper. Answer requests using command-line commands, scripts, code configurations, and wrap instructions in monospaced outputs."; | |
| appendLocalLog("Agent profile set to: Unix Terminal CLI assistant."); | |
| chatContainer.classList.add('terminal-layout'); | |
| } else { | |
| activeAgentMode = 'standard'; | |
| pillLabel.innerText = "Standard Mode"; | |
| systemTextarea.value = ""; | |
| appendLocalLog("Agent profile restored to: default general assistant."); | |
| chatContainer.classList.remove('terminal-layout'); | |
| } | |
| const statusAgentLabel = document.getElementById('status-agent-mode'); | |
| if (statusAgentLabel) { | |
| statusAgentLabel.innerText = "Mode: " + (activeAgentMode === 'standard' ? 'Standard' : 'Terminal'); | |
| } | |
| const statusBar = document.getElementById('chat-status-bar'); | |
| if (statusBar) { | |
| statusBar.style.display = (activeAgentMode === 'terminal' && chatContainer.style.display === 'flex') ? 'flex' : 'none'; | |
| } | |
| saveConfig(); | |
| } | |
| // Handle slash command local executes | |
| function handleSlashCommand(commandStr) { | |
| const container = document.getElementById('chat-container'); | |
| const cmd = commandStr.trim(); | |
| // Append user prompt action to screen | |
| const promptRow = document.createElement('div'); | |
| promptRow.className = 'message-row'; | |
| promptRow.innerHTML = ` | |
| <div class="prompt-header"> | |
| <span class="prompt-prefix-user">visitor@glm5.2:~$</span> | |
| <span class="prompt-command">run</span> | |
| </div> | |
| <div class="prompt-text">${cmd}</div> | |
| `; | |
| container.appendChild(promptRow); | |
| if (cmd === '/clear') { | |
| clearSession(); | |
| return; | |
| } | |
| // Create response layout for command output | |
| const respRow = document.createElement('div'); | |
| respRow.className = 'message-row'; | |
| let outputText = ''; | |
| if (cmd.startsWith('/help')) { | |
| outputText = ` | |
| <strong>GLM-5.2 CLI SYSTEM MANUAL</strong> | |
| ---------------------------------------- | |
| Here is a list of available command line functions: | |
| • <code>/help</code> - Prints this helper instruction table. | |
| • <code>/clear</code> - Resets active session logs and moves to welcome home panel. | |
| • <code>/theme</code> - Alternates theme context (Light -> Dark -> Classic green terminal). | |
| • <code>/matrix</code> - Toggles background matrix digital rain canvas. | |
| • <code>/system [prompt]</code> - Updates assistant's instruction context in config dynamically. | |
| ---------------------------------------- | |
| `; | |
| } else if (cmd.startsWith('/theme')) { | |
| cycleTheme(); | |
| outputText = `Theme toggled to: <strong>${currentTheme.toUpperCase()}</strong>`; | |
| } else if (cmd.startsWith('/matrix')) { | |
| const canvas = document.getElementById('matrix-canvas'); | |
| if (canvas.style.display === 'block') { | |
| canvas.style.display = 'none'; | |
| if (matrixInterval) clearInterval(matrixInterval); | |
| matrixInterval = null; | |
| outputText = "Digital matrix rain canvas disabled."; | |
| } else { | |
| canvas.style.display = 'block'; | |
| initMatrixRain(); | |
| outputText = "Digital matrix rain canvas enabled."; | |
| } | |
| } else if (cmd.startsWith('/system ')) { | |
| const sysText = cmd.substring(8); | |
| document.getElementById('system-input').value = sysText; | |
| saveConfig(); | |
| outputText = `System prompt set to: <em>"${sysText}"</em>`; | |
| } else { | |
| outputText = `Unknown system command: <code>${cmd}</code>. Type <code>/help</code> for list of available CLI commands.`; | |
| } | |
| respRow.innerHTML = ` | |
| <div class="prompt-header"> | |
| <span class="prompt-prefix-system">system@glm5.2:~$</span> | |
| <span class="prompt-command">stdout</span> | |
| </div> | |
| <div class="message-content">${outputText}</div> | |
| `; | |
| container.appendChild(respRow); | |
| container.scrollTop = container.scrollHeight; | |
| } | |
| // Submits user message to the API endpoint and streams the output | |
| async function submitChat() { | |
| const inputField = document.getElementById('chat-input'); | |
| const sendBtn = document.getElementById('send-btn'); | |
| const welcomePanel = document.getElementById('welcome-panel'); | |
| const chatContainer = document.getElementById('chat-container'); | |
| const text = inputField.value.trim(); | |
| if (!text) return; | |
| // Reset Input field | |
| inputField.value = ''; | |
| inputField.style.height = 'auto'; | |
| sendBtn.classList.remove('active'); | |
| sendBtn.disabled = true; | |
| // Check if user is typing a local command | |
| if (text.startsWith('/')) { | |
| // Check if welcome page is visible | |
| if (welcomePanel.style.display !== 'none') { | |
| welcomePanel.style.display = 'none'; | |
| chatContainer.style.display = 'flex'; | |
| if (activeAgentMode === 'terminal') { | |
| document.getElementById('chat-status-bar').style.display = 'flex'; | |
| } | |
| } | |
| handleSlashCommand(text); | |
| return; | |
| } | |
| // Clean UI welcome state | |
| if (welcomePanel.style.display !== 'none') { | |
| welcomePanel.style.display = 'none'; | |
| chatContainer.style.display = 'flex'; | |
| if (activeAgentMode === 'terminal') { | |
| document.getElementById('chat-status-bar').style.display = 'flex'; | |
| } | |
| } | |
| // Append User message row | |
| const userRow = document.createElement('div'); | |
| userRow.className = `message-row user-row style-${activeAgentMode}`; | |
| if (activeAgentMode === 'standard') { | |
| userRow.innerHTML = ` | |
| <div class="prompt-header"> | |
| <span class="standard-sender-user">You</span> | |
| </div> | |
| <div class="prompt-text">${escapeHtml(text)}</div> | |
| `; | |
| } else { | |
| userRow.innerHTML = ` | |
| <div class="prompt-header"> | |
| <span class="prompt-prefix-user">visitor@glm5.2:~$</span> | |
| <span class="prompt-command">prompt</span> | |
| </div> | |
| <div class="prompt-text">${escapeHtml(text)}</div> | |
| `; | |
| } | |
| chatContainer.appendChild(userRow); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| // Save user prompt to conversation history list | |
| chatHistory.push({ role: 'user', content: text }); | |
| // Create Assistant loading stream container | |
| const streamId = 'stream-' + Date.now(); | |
| const assistantRow = document.createElement('div'); | |
| assistantRow.className = `message-row assistant-row style-${activeAgentMode}`; | |
| if (activeAgentMode === 'standard') { | |
| assistantRow.innerHTML = ` | |
| <div class="prompt-header"> | |
| <span class="standard-sender-system">GLM 5.2</span> | |
| </div> | |
| <div class="message-content" id="${streamId}"> | |
| <span class="standard-loader">typing...</span> | |
| </div> | |
| `; | |
| } else { | |
| assistantRow.innerHTML = ` | |
| <div class="prompt-header"> | |
| <span class="prompt-prefix-system">system@glm5.2:~$</span> | |
| <span class="prompt-command">./run_glm_5.2_agent</span> | |
| </div> | |
| <div class="message-content" id="${streamId}"> | |
| <span class="terminal-cursor"></span> | |
| </div> | |
| `; | |
| } | |
| chatContainer.appendChild(assistantRow); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| const streamContainer = document.getElementById(streamId); | |
| // Construct full payload messages list incorporating custom system instruction | |
| const apiMessages = []; | |
| const systemPrompt = document.getElementById('system-input').value.trim(); | |
| if (systemPrompt) { | |
| apiMessages.push({ role: 'system', content: systemPrompt }); | |
| } | |
| apiMessages.push(...chatHistory); | |
| const selectedModel = "zai-org/GLM-5.2:fireworks-ai"; | |
| const temperatureVal = parseFloat(document.getElementById('temperature-input').value); | |
| try { | |
| const response = await fetch('/api/chat', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| messages: apiMessages, | |
| model: selectedModel, | |
| temperature: temperatureVal | |
| }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`Connection failure: Status ${response.status}`); | |
| } | |
| const reader = response.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| let streamedOutput = ""; | |
| let buffer = ""; | |
| while (true) { | |
| const { value, done } = await reader.read(); | |
| if (done) break; | |
| buffer += decoder.decode(value, { stream: true }); | |
| const lines = buffer.split('\n'); | |
| // Retain trailing partial line in buffer | |
| buffer = lines.pop(); | |
| for (const line of lines) { | |
| const cleanLine = line.trim(); | |
| if (!cleanLine) continue; | |
| if (cleanLine.startsWith('data: ')) { | |
| const dataStr = cleanLine.substring(6); | |
| if (dataStr === '[DONE]') { | |
| break; | |
| } | |
| try { | |
| const payload = JSON.parse(dataStr); | |
| if (payload.error) { | |
| streamedOutput += `\n\n**API Output Exception:** ${payload.error}`; | |
| } else if (payload.content) { | |
| streamedOutput += payload.content; | |
| } | |
| // Update current rendering block with Markdown parse and flashing cursor indicator | |
| if (activeAgentMode === 'terminal') { | |
| streamContainer.innerHTML = marked.parse(streamedOutput) + '<span class="terminal-cursor"></span>'; | |
| } else { | |
| streamContainer.innerHTML = marked.parse(streamedOutput); | |
| } | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| } catch (err) { | |
| console.error('Failed to unpack payload chunk:', cleanLine, err); | |
| } | |
| } | |
| } | |
| } | |
| // Finalize streaming output remove indicator cursor | |
| streamContainer.innerHTML = marked.parse(streamedOutput); | |
| // Store assistant output to local chat history memory | |
| chatHistory.push({ role: 'assistant', content: streamedOutput }); | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| } catch (err) { | |
| streamContainer.innerHTML = `<span style="color:#ef4444;">Operation failed: ${err.message}</span>`; | |
| console.error(err); | |
| } | |
| } | |
| // Helper to prevent tag insertions in text content | |
| function escapeHtml(text) { | |
| return text | |
| .replace(/&/g, "&") | |
| .replace(/</g, "<") | |
| .replace(/>/g, ">") | |
| .replace(/"/g, """) | |
| .replace(/'/g, "'"); | |
| } | |
| // Digital Code Rain Matrix Animation | |
| function initMatrixRain() { | |
| const canvas = document.getElementById('matrix-canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| canvas.style.display = 'block'; | |
| // Resize canvas to cover viewport | |
| function resizeCanvas() { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| } | |
| resizeCanvas(); | |
| window.addEventListener('resize', resizeCanvas); | |
| // Matrix character list | |
| const matrixChars = "01010101XYZGLM52SYSTEMCLIWORKSPACECONNECT"; | |
| const fontArr = matrixChars.split(""); | |
| const fontSize = 14; | |
| const columns = canvas.width / fontSize; | |
| // Track drop positions vertically | |
| const drops = []; | |
| for (let x = 0; x < columns; x++) { | |
| drops[x] = 1; | |
| } | |
| function drawMatrix() { | |
| // Set light/dark overlay blending depending on theme | |
| ctx.fillStyle = currentTheme === 'classic-terminal' ? 'rgba(5, 8, 6, 0.04)' : 'rgba(9, 9, 11, 0.04)'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Set green text | |
| ctx.fillStyle = currentTheme === 'classic-terminal' ? '#33ff33' : '#a1a1aa'; | |
| ctx.font = fontSize + "px monospace"; | |
| for (let i = 0; i < drops.length; i++) { | |
| const text = fontArr[Math.floor(Math.random() * fontArr.length)]; | |
| ctx.fillText(text, i * fontSize, drops[i] * fontSize); | |
| // Reset drop when hitting screen bottom boundary | |
| if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) { | |
| drops[i] = 0; | |
| } | |
| drops[i]++; | |
| } | |
| } | |
| if (matrixInterval) clearInterval(matrixInterval); | |
| matrixInterval = setInterval(drawMatrix, 35); | |
| } | |
| </script> | |
| </body> | |
| </html> | |