- index.html: btnSend hidden by default (display:none), icon-only (upward arrow SVG, no text label), title attribute for accessibility - style.css: new send-btn design — 34px circle, blue fill (#7cb9ff), subtle glow box-shadow, scale() hover/active for tactile feel, .send-btn.visible with @keyframes send-pop-in (scale+opacity spring using cubic-bezier(.34,1.56,.64,1) for a satisfying pop). Mobile override updated to preserve circle dimensions. - ui.js: updateSendBtn() — shows button with pop-in animation when textarea has content OR files are attached and agent is not busy; hides instantly when content is cleared. Hooked into setBusy() and renderTray() so button state tracks all content sources correctly. - boot.js: input event listener calls updateSendBtn() on every keystroke. - messages.js: autoResize() calls updateSendBtn() so button disappears immediately after send clears the textarea. - tests/test_sprint21.py: 33 tests covering HTML structure, CSS design (circle shape, colors, animations, keyframes), JS logic (updateSendBtn, setBusy, renderTray, autoResize integration), and regressions (363 total, all pass).
313 lines
24 KiB
HTML
313 lines
24 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Hermes</title>
|
|
<link rel="stylesheet" href="/static/style.css">
|
|
<!-- Prism.js syntax highlighting (loaded async, non-blocking) -->
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-tomorrow.min.css" integrity="sha384-wFjoQjtV1y5jVHbt0p35Ui8aV8GVpEZkyF99OXWqP/eNJDU93D3Ugxkoyh6Y2I4A" crossorigin="anonymous">
|
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-core.min.js" integrity="sha384-MXybTpajaBV0AkcBaCPT4KIvo0FzoCiWXgcihYsw4FUkEz0Pv3JGV6tk2G8vJtDc" crossorigin="anonymous" defer></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js" integrity="sha384-Uq05+JLko69eOiPr39ta9bh7kld5PKZoU+fF7g0EXTAriEollhZ+DrN8Q/Oi8J2Q" crossorigin="anonymous" defer></script>
|
|
</head>
|
|
<body>
|
|
<div class="layout">
|
|
<aside class="sidebar">
|
|
<div class="sidebar-header"><div class="logo">H</div><div><h1 style="margin:0;font-size:15px;font-weight:700;letter-spacing:-.01em">Hermes</h1><div style="font-size:10px;color:var(--muted);opacity:.8;margin-top:1px">v0.22</div></div></div>
|
|
<div class="sidebar-nav">
|
|
<button class="nav-tab active" data-panel="chat" data-label="Chat" onclick="switchPanel('chat')" title="Chat">💬</button>
|
|
<button class="nav-tab" data-panel="tasks" data-label="Tasks" onclick="switchPanel('tasks')" title="Tasks">📅</button>
|
|
<button class="nav-tab" data-panel="skills" data-label="Skills" onclick="switchPanel('skills')" title="Skills">🧩</button>
|
|
<button class="nav-tab" data-panel="memory" data-label="Memory" onclick="switchPanel('memory')" title="Memory">🧠</button>
|
|
<button class="nav-tab" data-panel="workspaces" data-label="Spaces" onclick="switchPanel('workspaces')" title="Spaces">📁</button>
|
|
<button class="nav-tab" data-panel="todos" data-label="Todos" onclick="switchPanel('todos')" title="Current task list">✅</button>
|
|
</div>
|
|
<!-- Chat panel -->
|
|
<div class="panel-view active" id="panelChat">
|
|
<div class="sidebar-section">
|
|
<button class="new-chat-btn" id="btnNewChat">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
New conversation <span style="font-size:10px;opacity:.5;margin-left:4px">⌘K</span>
|
|
</button>
|
|
</div>
|
|
<div class="session-search"><input id="sessionSearch" placeholder="Filter conversations..." oninput="filterSessions()"></div>
|
|
<div class="session-list" id="sessionList"></div>
|
|
</div>
|
|
<!-- Tasks (cron) panel -->
|
|
<div class="panel-view" id="panelTasks">
|
|
<div class="sidebar-section" style="padding-bottom:4px;display:flex;align-items:center;justify-content:space-between">
|
|
<div style="font-size:11px;color:var(--muted)">Scheduled jobs</div>
|
|
<button class="cron-btn run" style="padding:3px 8px;font-size:10px" onclick="toggleCronForm()">+ New job</button>
|
|
</div>
|
|
<!-- Create job form (hidden by default) -->
|
|
<div id="cronCreateForm" style="display:none;padding:8px 12px;border-bottom:1px solid var(--border);flex-shrink:0">
|
|
<input id="cronFormName" placeholder="Job name (optional)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px">
|
|
<input id="cronFormSchedule" placeholder="Schedule: '0 9 * * *' or 'every 1h'" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px">
|
|
<textarea id="cronFormPrompt" rows="3" placeholder="Prompt (must be self-contained)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;resize:none;font-family:inherit;margin-bottom:6px"></textarea>
|
|
<select id="cronFormDeliver" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:8px">
|
|
<option value="local">Local (save output only)</option>
|
|
<option value="discord">Discord</option>
|
|
<option value="telegram">Telegram</option>
|
|
</select>
|
|
<div style="display:flex;gap:6px">
|
|
<button class="cron-btn run" style="flex:1" onclick="submitCronCreate()">Create job</button>
|
|
<button class="cron-btn" style="flex:1" onclick="toggleCronForm()">Cancel</button>
|
|
</div>
|
|
<div id="cronFormError" style="font-size:11px;color:var(--accent);margin-top:6px;display:none"></div>
|
|
</div>
|
|
<div class="cron-list" id="cronList"><div style="padding:12px;color:var(--muted);font-size:12px">Loading...</div></div>
|
|
</div>
|
|
<!-- Skills panel -->
|
|
<div class="panel-view" id="panelSkills">
|
|
<div class="sidebar-section" style="padding-bottom:4px;display:flex;align-items:center;justify-content:space-between">
|
|
<div class="skills-search" style="flex:1;padding:0"><input id="skillsSearch" placeholder="Search skills..." oninput="filterSkills()"></div>
|
|
<button class="cron-btn run" style="padding:3px 8px;font-size:10px;flex-shrink:0;margin-left:6px" onclick="toggleSkillForm()">+ New skill</button>
|
|
</div>
|
|
<!-- Skill create/edit form (hidden by default) -->
|
|
<div id="skillCreateForm" style="display:none;padding:8px 12px;border-bottom:1px solid var(--border);flex-shrink:0">
|
|
<input id="skillFormName" placeholder="Skill name (e.g. my-skill)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px;box-sizing:border-box">
|
|
<input id="skillFormCategory" placeholder="Category (optional, e.g. devops)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;margin-bottom:6px;box-sizing:border-box">
|
|
<textarea id="skillFormContent" rows="6" placeholder="SKILL.md content (YAML frontmatter + markdown body)" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;resize:vertical;font-family:'SF Mono',ui-monospace,monospace;margin-bottom:6px;box-sizing:border-box"></textarea>
|
|
<div style="display:flex;gap:6px">
|
|
<button class="cron-btn run" style="flex:1" onclick="submitSkillSave()">Save skill</button>
|
|
<button class="cron-btn" style="flex:1" onclick="toggleSkillForm()">Cancel</button>
|
|
</div>
|
|
<div id="skillFormError" style="font-size:11px;color:var(--accent);margin-top:6px;display:none"></div>
|
|
</div>
|
|
<div class="skills-list" id="skillsList"><div style="padding:12px;color:var(--muted);font-size:12px">Loading...</div></div>
|
|
</div>
|
|
<!-- Memory panel -->
|
|
<div class="panel-view" id="panelMemory">
|
|
<div style="padding:8px 12px 4px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
|
|
<span style="font-size:11px;color:var(--muted)">Personal memory</span>
|
|
<button class="cron-btn run" id="memEditBtn" style="padding:3px 8px;font-size:10px" onclick="toggleMemoryEdit()">✎ Edit</button>
|
|
</div>
|
|
<div class="memory-panel" id="memoryPanel"><div style="color:var(--muted);font-size:12px">Loading...</div></div>
|
|
<!-- Memory edit form (hidden by default) -->
|
|
<div id="memoryEditForm" style="display:none;padding:8px 12px;border-top:1px solid var(--border);flex-shrink:0">
|
|
<div style="font-size:11px;color:var(--muted);margin-bottom:4px">Editing: <span id="memEditSection">memory</span></div>
|
|
<textarea id="memEditContent" rows="10" style="width:100%;background:rgba(255,255,255,.05);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:11px;outline:none;resize:vertical;font-family:'SF Mono',ui-monospace,monospace;box-sizing:border-box;margin-bottom:6px;line-height:1.5"></textarea>
|
|
<div style="display:flex;gap:6px">
|
|
<button class="cron-btn run" style="flex:1" onclick="submitMemorySave()">Save</button>
|
|
<button class="cron-btn" style="flex:1" onclick="closeMemoryEdit()">Cancel</button>
|
|
</div>
|
|
<div id="memEditError" style="font-size:11px;color:var(--accent);margin-top:6px;display:none"></div>
|
|
</div>
|
|
</div>
|
|
<!-- Todo panel -->
|
|
<div class="panel-view" id="panelTodos">
|
|
<div style="padding:10px 12px 4px;font-size:11px;color:var(--muted);flex-shrink:0">Current task list</div>
|
|
<div id="todoPanel" style="flex:1;overflow-y:auto;padding:8px 12px"></div>
|
|
</div>
|
|
<!-- Workspaces panel -->
|
|
<div class="panel-view" id="panelWorkspaces">
|
|
<div style="padding:10px 12px 4px;font-size:11px;color:var(--muted)">Add and switch workspaces for your sessions.</div>
|
|
<div style="flex:1;overflow-y:auto;padding:0 12px 12px" id="workspacesPanel"><div style="color:var(--muted);font-size:12px">Loading...</div></div>
|
|
</div>
|
|
<div class="sidebar-bottom">
|
|
<div class="field-label" style="font-size:10px;letter-spacing:.07em;margin-bottom:4px">MODEL</div>
|
|
<select id="modelSelect">
|
|
<optgroup label="OpenAI">
|
|
<option value="openai/gpt-5.4-mini">GPT-5.4 Mini</option>
|
|
<option value="openai/gpt-4o">GPT-4o</option>
|
|
<option value="openai/o3">o3</option>
|
|
<option value="openai/o4-mini">o4-mini</option>
|
|
</optgroup>
|
|
<optgroup label="Anthropic">
|
|
<option value="anthropic/claude-sonnet-4.6">Claude Sonnet 4.6</option>
|
|
<option value="anthropic/claude-sonnet-4-5">Claude Sonnet 4.5</option>
|
|
<option value="anthropic/claude-haiku-3-5">Claude Haiku 3.5</option>
|
|
</optgroup>
|
|
<optgroup label="Other">
|
|
<option value="google/gemini-2.5-pro">Gemini 2.5 Pro</option>
|
|
<option value="deepseek/deepseek-chat-v3-0324">DeepSeek V3</option>
|
|
<option value="meta-llama/llama-4-scout">Llama 4 Scout</option>
|
|
</optgroup>
|
|
</select>
|
|
<div id="sidebarWsDisplay" style="display:flex;align-items:center;gap:7px;padding:0 0 8px;cursor:pointer;border-radius:8px;transition:background .15s" onclick="toggleWsDropdown()" title="Switch workspace">
|
|
<span style="font-size:14px;opacity:.7">📁</span>
|
|
<div style="min-width:0;flex:1">
|
|
<div style="font-size:11px;font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap" id="sidebarWsName">Workspace</div>
|
|
<div style="font-size:10px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:1px" id="sidebarWsPath"></div>
|
|
</div>
|
|
<span style="font-size:10px;color:var(--muted);flex-shrink:0">▾</span>
|
|
</div>
|
|
<div class="sidebar-actions">
|
|
<button class="sm-btn" id="btnDownload" title="Download as Markdown">↓ Transcript</button>
|
|
<button class="sm-btn" id="btnExportJSON" title="Export full session as JSON">❬/❭ JSON</button>
|
|
<button class="sm-btn" id="btnImportJSON" title="Import session from JSON">↑ Import</button>
|
|
<input type="file" id="importFileInput" accept=".json" style="display:none">
|
|
</div>
|
|
</div>
|
|
<div class="resize-handle" id="sidebarResize"></div>
|
|
</aside>
|
|
<main class="main">
|
|
<div class="topbar">
|
|
<div style="flex:1;min-width:0;overflow:hidden"><div class="topbar-title" id="topbarTitle">Hermes</div><div class="topbar-meta" id="topbarMeta">Start a new conversation</div></div>
|
|
<div class="topbar-chips">
|
|
<div class="chip model" id="modelChip">GPT-5.4 Mini</div>
|
|
<div id="wsChipWrap" style="position:relative">
|
|
<div class="chip ws-chip" id="wsChip" onclick="toggleWsDropdown()" title="Switch workspace" style="cursor:pointer">📁 test-workspace ▾</div>
|
|
<div class="ws-dropdown" id="wsDropdown"></div>
|
|
</div>
|
|
<button class="chip clear-btn" id="btnClearConv" onclick="clearConversation()" title="Clear all messages in this conversation" style="display:none">🗑 Clear</button>
|
|
<button class="chip gear-btn" id="btnSettings" onclick="toggleSettings()" title="Settings">⚙</button>
|
|
</div>
|
|
</div>
|
|
<div class="messages" id="messages">
|
|
<div class="empty-state" id="emptyState">
|
|
<div class="empty-logo">🦉</div>
|
|
<h2>What can I help with?</h2>
|
|
<p>Ask anything, run commands, explore files, or manage your scheduled tasks.</p>
|
|
<div class="suggestion-grid">
|
|
<button class="suggestion" data-msg="What files are in this workspace?">📁 What files are in this workspace?</button>
|
|
<button class="suggestion" data-msg="What's on my schedule today?">📋 What's on my schedule today?</button>
|
|
<button class="suggestion" data-msg="Help me plan a small project.">🗺 Help me plan a small project.</button>
|
|
</div>
|
|
</div>
|
|
<div class="messages-inner" id="msgInner"></div>
|
|
<div id="liveToolCards" style="display:none;max-width:800px;margin:0 auto;width:100%;padding:0 24px;"></div>
|
|
</div>
|
|
<div class="reconnect-banner" id="reconnectBanner">
|
|
<span id="reconnectMsg">⚠ A response may have been in progress when you last left. Reload messages?</span>
|
|
<div style="display:flex;gap:8px;flex-shrink:0">
|
|
<button class="reconnect-btn" onclick="dismissReconnect()">Dismiss</button>
|
|
<button class="reconnect-btn" onclick="refreshSession()">↻ Reload</button>
|
|
</div>
|
|
</div>
|
|
<div class="approval-card" id="approvalCard">
|
|
<div class="approval-inner">
|
|
<div class="approval-header">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
|
Dangerous command — approval required
|
|
</div>
|
|
<div class="approval-desc" id="approvalDesc"></div>
|
|
<div class="approval-cmd" id="approvalCmd"></div>
|
|
<div class="approval-btns">
|
|
<button class="approval-btn once" onclick="respondApproval('once')">✓ Allow once</button>
|
|
<button class="approval-btn session" onclick="respondApproval('session')">🔒 Allow this session</button>
|
|
<button class="approval-btn always" onclick="respondApproval('always')">☆ Always allow</button>
|
|
<button class="approval-btn deny" onclick="respondApproval('deny')">✕ Deny</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Activity bar: shows tool progress / status above composer (not inside input) -->
|
|
<div id="activityBar" style="display:none;max-width:800px;margin:0 auto;width:100%;padding:0 24px;">
|
|
<div id="activityBarInner" style="display:flex;align-items:center;gap:8px;padding:6px 12px;border-radius:8px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);font-size:12px;color:var(--muted);animation:fadeIn .15s ease;">
|
|
<span id="activityIcon" style="font-size:13px;opacity:.6">⚙</span>
|
|
<span id="activityText" style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"></span>
|
|
<button id="btnCancel" onclick="cancelStream()" style="display:none;background:rgba(233,69,96,.12);border:1px solid rgba(233,69,96,.35);color:#e94560;font-size:11px;font-weight:600;padding:3px 10px;border-radius:6px;cursor:pointer;flex-shrink:0;transition:background .15s" title="Cancel this task">■ Cancel</button>
|
|
<button id="btnDismissStatus" onclick="setStatus('')" style="display:none;background:none;border:none;color:var(--muted);font-size:14px;line-height:1;cursor:pointer;padding:0 2px;opacity:.5;flex-shrink:0" title="Dismiss">✕</button>
|
|
<span id="activityDots" style="display:flex;gap:3px;align-items:center">
|
|
<span style="width:4px;height:4px;border-radius:50%;background:var(--blue);opacity:.3;animation:pulse 1.4s ease-in-out infinite"></span>
|
|
<span style="width:4px;height:4px;border-radius:50%;background:var(--blue);opacity:.3;animation:pulse 1.4s ease-in-out .22s infinite"></span>
|
|
<span style="width:4px;height:4px;border-radius:50%;background:var(--blue);opacity:.3;animation:pulse 1.4s ease-in-out .44s infinite"></span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="composer-wrap" id="composerWrap">
|
|
<div class="cmd-dropdown" id="cmdDropdown"></div>
|
|
<div class="composer-box" id="composerBox">
|
|
<div class="drop-hint" id="dropHint">
|
|
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
|
Drop files to upload to workspace
|
|
</div>
|
|
<div class="attach-tray" id="attachTray"></div>
|
|
<div class="mic-status" id="micStatus" style="display:none"><span class="mic-dot"></span> Listening…</div>
|
|
<textarea id="msg" rows="1" placeholder="Message Hermes…"></textarea>
|
|
<div class="composer-footer">
|
|
<div class="composer-left">
|
|
<input type="file" id="fileInput" multiple accept="image/*,text/*,application/pdf,application/json,.md,.py,.js,.ts,.yaml,.yml,.toml,.csv,.sh,.txt,.log,.env" style="display:none">
|
|
<button class="icon-btn" id="btnAttach" title="Attach files">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
|
|
</button>
|
|
<button class="icon-btn mic-btn" id="btnMic" title="Voice input" style="display:none">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<rect x="9" y="1" width="6" height="12" rx="3"/>
|
|
<path d="M5 10a7 7 0 0 0 14 0"/>
|
|
<line x1="12" y1="19" x2="12" y2="23"/>
|
|
<line x1="8" y1="23" x2="16" y2="23"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="composer-right">
|
|
<button class="send-btn" id="btnSend" title="Send message" style="display:none">
|
|
<svg width="16" height="16" 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 class="upload-bar-wrap" id="uploadBarWrap"><div class="upload-bar" id="uploadBar"></div></div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<aside class="rightpanel">
|
|
<div class="resize-handle" id="rightpanelResize"></div>
|
|
<div class="panel-header">
|
|
<span>Workspace</span>
|
|
<div class="panel-actions">
|
|
<button class="panel-icon-btn" id="btnUpDir" title="Parent directory" onclick="navigateUp()" style="display:none">↑</button>
|
|
<button class="panel-icon-btn" id="btnNewFile" title="New file" onclick="promptNewFile()">+</button>
|
|
<button class="panel-icon-btn" id="btnNewFolder" title="New folder" onclick="promptNewFolder()">📁</button>
|
|
<button class="panel-icon-btn" id="btnRefreshPanel" title="Refresh" onclick="if(S.session)loadDir(S.currentDir)">↻</button>
|
|
<button class="panel-icon-btn close-preview" id="btnClearPreview" title="Close preview">✕</button>
|
|
</div>
|
|
</div>
|
|
<div class="breadcrumb-bar" id="breadcrumbBar" style="display:none"></div>
|
|
<div class="file-tree" id="fileTree"></div>
|
|
<div class="preview-area" id="previewArea">
|
|
<div class="preview-path" id="previewPath">
|
|
<span id="previewPathText"></span>
|
|
<span class="preview-badge" id="previewBadge"></span>
|
|
<button id="btnDownloadFile" class="panel-icon-btn" style="margin-left:auto;font-size:12px;width:auto;padding:2px 8px" onclick="downloadFile(_previewCurrentPath)" title="Download file to your computer">⇩ Download</button>
|
|
<button id="btnEditFile" class="panel-icon-btn" style="font-size:12px;width:auto;padding:2px 8px;display:none" onclick="toggleEditMode()">✎ Edit</button>
|
|
</div>
|
|
<pre class="preview-code" id="previewCode"></pre>
|
|
<div class="preview-img-wrap" id="previewImgWrap" style="display:none"><img class="preview-img" id="previewImg" src="" alt=""></div>
|
|
<div class="preview-md" id="previewMd" style="display:none"></div>
|
|
<textarea id="previewEditArea" style="display:none;flex:1;width:100%;background:var(--code-bg);color:#e2e8f0;border:1px solid var(--border2);border-radius:8px;padding:12px;font-family:'SF Mono',ui-monospace,monospace;font-size:12px;line-height:1.6;resize:none;outline:none" oninput="_previewDirty=true;updateEditBtn()"></textarea>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
<div class="settings-overlay" id="settingsOverlay" style="display:none">
|
|
<div class="settings-panel">
|
|
<div class="settings-header">
|
|
<h3 style="margin:0;font-size:16px">Settings</h3>
|
|
<button class="panel-icon-btn" onclick="toggleSettings()" title="Close">✕</button>
|
|
</div>
|
|
<div class="settings-body">
|
|
<div class="settings-field">
|
|
<label for="settingsModel">Default Model</label>
|
|
<select id="settingsModel" style="width:100%;padding:8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px"></select>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="settingsWorkspace">Default Workspace</label>
|
|
<select id="settingsWorkspace" style="width:100%;padding:8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px"></select>
|
|
</div>
|
|
<div class="settings-field">
|
|
<label for="settingsSendKey">Send Key</label>
|
|
<select id="settingsSendKey" style="width:100%;padding:8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px">
|
|
<option value="enter">Enter (Shift+Enter for newline)</option>
|
|
<option value="ctrl+enter">Ctrl+Enter (Enter for newline)</option>
|
|
</select>
|
|
</div>
|
|
<div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px">
|
|
<label for="settingsPassword">Access Password</label>
|
|
<div style="font-size:11px;color:var(--muted);margin-bottom:6px">Enter a new password to set or change it. Leave blank to keep current setting.</div>
|
|
<input type="password" id="settingsPassword" placeholder="Enter new password…" style="width:100%;padding:8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:13px">
|
|
</div>
|
|
<button class="sm-btn" onclick="saveSettings()" style="margin-top:12px;width:100%;padding:8px;font-weight:600">Save Settings</button>
|
|
<button class="sm-btn" id="btnDisableAuth" onclick="disableAuth()" style="margin-top:6px;width:100%;padding:8px;font-weight:600;color:#e8a030;border-color:rgba(232,160,48,.3);display:none">Disable Auth</button>
|
|
<button class="sm-btn" id="btnSignOut" onclick="signOut()" style="margin-top:6px;width:100%;padding:8px;font-weight:600;color:var(--accent);border-color:rgba(233,69,96,.3);display:none">Sign Out</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="toast" id="toast"></div>
|
|
<script src="/static/ui.js"></script>
|
|
<script src="/static/workspace.js"></script>
|
|
<script src="/static/sessions.js"></script>
|
|
<script src="/static/commands.js"></script>
|
|
<script src="/static/messages.js"></script>
|
|
<script src="/static/panels.js"></script>
|
|
<script src="/static/boot.js"></script>
|
|
</body>
|
|
</html> |