Phase 8: TypeScript migration, i18n rewrite, Activity Tree, Projects API, Heartbeats
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Hermes</title>
|
||||
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
|
||||
<script>(function(){var t=localStorage.getItem('hermes-theme');if(t==='system'){t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light';}if(t&&t!=='dark')document.documentElement.dataset.theme=t;})()</script>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
<!-- KaTeX math rendering CSS (loaded eagerly to prevent layout shift) -->
|
||||
@@ -26,8 +27,7 @@
|
||||
|
||||
|
||||
<button class="nav-tab" data-panel="agents" data-label="Agents" onclick="switchPanel('agents')" title="Rose + Tier-2 Agents" data-i18n-title="tab_agents"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg></button>
|
||||
|
||||
<button class="nav-tab" data-panel="projects" data-label="Projects" onclick="switchPanel('projects')" title="Projects & Tasks" data-i18n-title="tab_projects"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg></button>
|
||||
<button class="nav-tab" data-panel="activity" data-label="Activity" onclick="switchPanel('activity')" title="Agent Activity Tree" id="navActivity"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></svg></button>
|
||||
</div>
|
||||
<!-- Chat panel -->
|
||||
<div class="panel-view active" id="panelChat">
|
||||
@@ -43,46 +43,60 @@
|
||||
<!-- 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)" data-i18n="scheduled_jobs">Scheduled jobs</div>
|
||||
<button class="cron-btn run" style="padding:3px 8px;font-size:10px" onclick="toggleCronForm()">+ <span data-i18n="new_job">New job</span></button>
|
||||
<div style="font-size:11px;color:var(--muted)" data-i18n="scheduled_jobs">Geplante Aufgaben</div>
|
||||
<button class="cron-btn run" style="padding:3px 8px;font-size:10px" onclick="toggleCronForm()">+ <span data-i18n="new_job">Neue Aufgabe</span></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>
|
||||
<input id="cronFormName" placeholder="Aufgabenname (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="Zeitplan (z.B. '0 9 * * *' oder '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 (muss eigenständig funktionieren)" 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:6px">
|
||||
<option value="local">Local (save output only)</option>
|
||||
<option value="local">Lokal (nur speichern)</option>
|
||||
<option value="discord">Discord</option>
|
||||
<option value="telegram">Telegram</option>
|
||||
</select>
|
||||
<div class="skill-picker-wrap" style="margin-bottom:8px">
|
||||
<input id="cronFormSkillSearch" placeholder="Add skills (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" autocomplete="off">
|
||||
<div id="cronFormSkillDropdown" class="skill-picker-dropdown" style="display:none"></div>
|
||||
<input id="cronFormSkillSearch" placeholder="Skills hinzufügen (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" autocomplete="off">
|
||||
<div id="cronFormSkillDropdown" class="skill-picker-dropdown"></div>
|
||||
<div id="cronFormSkillTags" class="skill-picker-tags"></div>
|
||||
</div>
|
||||
<div style="display:flex;gap:6px">
|
||||
<button class="cron-btn run" style="flex:1" onclick="submitCronCreate()" data-i18n="create_job">Create job</button>
|
||||
<button class="cron-btn" style="flex:1" onclick="toggleCronForm()" data-i18n="cancel">Cancel</button>
|
||||
<button class="cron-btn run" style="flex:1" onclick="submitCronCreate()" data-i18n="create_job">Aufgabe erstellen</button>
|
||||
<button class="cron-btn" style="flex:1" onclick="toggleCronForm()" data-i18n="cancel">Abbrechen</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" data-i18n="loading">Loading...</div></div>
|
||||
<div class="cron-list" id="cronList"><div style="padding:12px;color:var(--muted);font-size:12px">Laden...</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..." data-i18n-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()">+ <span data-i18n="new_skill">New skill</span></button>
|
||||
<div class="sidebar-section" style="padding-bottom:4px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:4px">
|
||||
<div style="display:flex;align-items:center;gap:6px;flex:1;min-width:0">
|
||||
<div class="skills-search" style="flex:1;min-width:0;padding:0"><input id="skillsSearch" placeholder="Search skills..." data-i18n-placeholder="search_skills" oninput="filterSkills()"></div>
|
||||
<select id="skillsSort" onchange="setSkillsSort(this.value)" style="background:var(--hover-bg);border:1px solid var(--border2);border-radius:6px;color:var(--muted);font-size:10px;padding:4px 6px;outline:none;cursor:pointer;flex-shrink:0">
|
||||
<option value="az" ${_skillsSort==='az'?'selected':''}>A-Z</option>
|
||||
<option value="za" ${_skillsSort==='za'?'selected':''}>Z-A</option>
|
||||
<option value="uncat" ${_skillsSort==='uncat'?'selected':''}>Uncat.▲</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="cron-btn run" style="padding:4px 10px;font-size:11px;flex-shrink:0" onclick="toggleSkillForm()">+ <span data-i18n="new_skill">New skill</span></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">
|
||||
<div id="skillFormHeader" style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
|
||||
<span id="skillFormTitle" style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em">Neuer Skill</span>
|
||||
<button onclick="toggleSkillForm()" style="background:none;border:none;color:var(--muted);cursor:pointer;padding:2px;line-height:1;font-size:14px" title="Schließen">×</button>
|
||||
</div>
|
||||
<div style="position:relative;margin-bottom:6px">
|
||||
<input id="skillFormCategory" placeholder="Category (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;box-sizing:border-box">
|
||||
<div id="skillCatDropdown" style="display:none;position:absolute;left:0;right:0;top:100%;background:var(--sidebar);border:1px solid var(--border2);border-radius:6px;z-index:100;max-height:160px;overflow-y:auto;box-shadow:0 4px 12px rgba(0,0,0,.4)"></div>
|
||||
</div>
|
||||
<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()" data-i18n="save_skill">Save skill</button>
|
||||
<button class="cron-btn" style="flex:1" onclick="toggleSkillForm()" data-i18n="cancel">Cancel</button>
|
||||
<button class="cron-btn run" style="flex:1" onclick="submitSkillSave()" data-i18n="save_skill">Save skill</button>
|
||||
</div>
|
||||
<div id="skillFormError" style="font-size:11px;color:var(--accent);margin-top:6px;display:none"></div>
|
||||
</div>
|
||||
@@ -128,95 +142,29 @@
|
||||
<div id="agentInbox" style="display:none;position:absolute;top:0;right:0;bottom:0;width:320px;background:var(--surface);border-left:1px solid var(--border);z-index:100;overflow-y:auto;padding:16px;box-shadow:-4px 0 20px rgba(0,0,0,.3)"></div>
|
||||
</div>
|
||||
|
||||
<!-- Projects panel -->
|
||||
<div class="panel-view" id="panelProjects">
|
||||
<!-- Header: Title + Expand Button -->
|
||||
<div class="projects-header">
|
||||
<div class="projects-title">📋 Projects</div>
|
||||
<div class="projects-header-stats" id="projectsHeaderStats"></div>
|
||||
<button class="panel-icon-btn" id="btnExpandProjects" title="Expand" onclick="expandPanel('projects')">
|
||||
<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="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Quick Add Bar -->
|
||||
<div class="projects-quick-add">
|
||||
<span class="quick-add-icon">+</span>
|
||||
<input id="quickAddInput" placeholder="Add a task..." onkeydown="if(event.key==='Enter')quickAddTask()">
|
||||
<input id="quickAddDue" type="date" title="Due date" id="quickAddDue">
|
||||
<select id="quickAddType">
|
||||
<option value="project">📁 Project</option>
|
||||
<option value="daily">📅 Daily</option>
|
||||
<option value="recurring">🔄 Recurring</option>
|
||||
</select>
|
||||
<button class="quick-add-btn" onclick="quickAddTask()">Add</button>
|
||||
</div>
|
||||
|
||||
<!-- Filter Bar -->
|
||||
<div class="projects-filter-bar" id="projectsFilterBar">
|
||||
<div class="filter-group">
|
||||
<button class="filter-btn active" data-filter="all" onclick="filterTasks('all')">All</button>
|
||||
<button class="filter-btn" data-filter="project" onclick="filterTasks('project')">📁 Projects</button>
|
||||
<button class="filter-btn" data-filter="daily" onclick="filterTasks('daily')">📅 Daily</button>
|
||||
<button class="filter-btn" data-filter="recurring" onclick="filterTasks('recurring')">🔄 Recurring</button>
|
||||
</div>
|
||||
<div class="filter-sep"></div>
|
||||
<div class="filter-group">
|
||||
<button class="filter-btn p1" onclick="filterTasks('p1')">🔴 P1</button>
|
||||
<button class="filter-btn p2" onclick="filterTasks('p2')">🟡 P2</button>
|
||||
<button class="filter-btn p3" onclick="filterTasks('p3')">🟢 P3</button>
|
||||
</div>
|
||||
<div class="filter-sep"></div>
|
||||
<div class="filter-group filter-group-right">
|
||||
<span class="filter-streak" id="filterStreak"></span>
|
||||
<span class="filter-overdue" id="filterOverdue"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content: Split View -->
|
||||
<div class="projects-main" id="projectsMain">
|
||||
<!-- Linke Spalte: Projektliste -->
|
||||
<div class="projects-sidebar" id="projectsSidebar">
|
||||
<div class="sidebar-section-header">📁 Projects</div>
|
||||
<div id="projectsList"></div>
|
||||
<div class="sidebar-section-header" style="margin-top:16px">📅 Daily Tasks</div>
|
||||
<div id="dailyTasksList"></div>
|
||||
<div class="sidebar-section-header" style="margin-top:16px">🔄 Recurring</div>
|
||||
<div id="recurringTasksList"></div>
|
||||
</div>
|
||||
|
||||
<!-- Rechte Spalte: Globales Kanban -->
|
||||
<div class="projects-kanban" id="projectsKanban">
|
||||
<div class="kanban-col" ondragover="onKanbanDragOver(event)" ondrop="onKanbanDrop(event, 'todo')">
|
||||
<div class="kanban-col-header">
|
||||
<span class="kanban-col-title">📋 TODO</span>
|
||||
<span class="kanban-col-count" id="kanbanTodoCount"></span>
|
||||
</div>
|
||||
<div class="kanban-col-content" id="kanbanTodoContent"></div>
|
||||
<!-- Agent Activity Tree panel -->
|
||||
<div id="panelActivity" class="panel-view">
|
||||
<div class="mc-header">
|
||||
<div class="mc-header-top">
|
||||
<h3>Agent Activity</h3>
|
||||
<div class="mc-header-actions">
|
||||
<button id="mcExpandAll" class="mc-btn-sm" title="Expand All">▼</button>
|
||||
<button id="mcCollapseAll" class="mc-btn-sm" title="Collapse All">▶</button>
|
||||
<button id="mcMockData" class="mc-btn-sm" title="Load Mock Data">🧪</button>
|
||||
</div>
|
||||
<div class="kanban-col" ondragover="onKanbanDragOver(event)" ondrop="onKanbanDrop(event, 'in_progress')">
|
||||
<div class="kanban-col-header">
|
||||
<span class="kanban-col-title">⚡ IN PROGRESS</span>
|
||||
<span class="kanban-col-count" id="kanbanInProgressCount"></span>
|
||||
</div>
|
||||
<div class="kanban-col-content" id="kanbanInProgressContent"></div>
|
||||
</div>
|
||||
<div class="kanban-col" ondragover="onKanbanDragOver(event)" ondrop="onKanbanDrop(event, 'review')">
|
||||
<div class="kanban-col-header">
|
||||
<span class="kanban-col-title">👀 REVIEW</span>
|
||||
<span class="kanban-col-count" id="kanbanReviewCount"></span>
|
||||
</div>
|
||||
<div class="kanban-col-content" id="kanbanReviewContent"></div>
|
||||
</div>
|
||||
<div class="kanban-col" ondragover="onKanbanDragOver(event)" ondrop="onKanbanDrop(event, 'done')">
|
||||
<div class="kanban-col-header">
|
||||
<span class="kanban-col-title">✅ DONE</span>
|
||||
<span class="kanban-col-count" id="kanbanDoneCount"></span>
|
||||
</div>
|
||||
<div class="kanban-col-content" id="kanbanDoneContent"></div>
|
||||
</div>
|
||||
<div class="mc-controls">
|
||||
<input type="text" id="mcSearch" class="mc-search" placeholder="Search agents or tools..." />
|
||||
<div class="mc-filter-group">
|
||||
<button class="mc-filter-btn active" data-filter="all">All</button>
|
||||
<button class="mc-filter-btn" data-filter="running">Running</button>
|
||||
<button class="mc-filter-btn" data-filter="pending">Pending</button>
|
||||
<button class="mc-filter-btn" data-filter="done">Done</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mcBody" class="mc-body"></div>
|
||||
<div id="mcFooter" class="mc-footer"></div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-bottom">
|
||||
@@ -254,6 +202,14 @@
|
||||
</button>
|
||||
<div style="flex:1;min-width:0;overflow:hidden"><div class="topbar-title" id="topbarTitle">Hermes</div><div class="topbar-meta" id="topbarMeta" data-i18n="new_conversation">Start a new conversation</div></div>
|
||||
<div class="topbar-chips">
|
||||
<div class="agent-selector-wrap" id="agentSelectorWrap" style="position:relative">
|
||||
<button class="chip agent-selector-chip" id="agentSelectorChip" type="button" onclick="toggleAgentSelectorDropdown()" title="Chat with agent">
|
||||
<span id="agentSelectorIcon">🌹</span>
|
||||
<span id="agentSelectorLabel">Rose</span>
|
||||
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</button>
|
||||
<div class="agent-selector-dropdown" id="agentSelectorDropdown" style="display:none;position:absolute;top:100%;right:0;margin-top:4px;background:var(--surface);border:1px solid var(--border);border-radius:10px;min-width:200px;max-height:320px;overflow-y:auto;z-index:200;box-shadow:0 8px 24px rgba(0,0,0,.3)"></div>
|
||||
</div>
|
||||
<div class="ws-selector-wrap" id="wsSelectorWrap" style="position:relative">
|
||||
<button class="chip ws-selector-chip" id="wsSelectorChip" type="button" onclick="toggleWsSelectorDropdown()" title="Switch workspace">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
||||
@@ -294,9 +250,7 @@
|
||||
</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 class="tool-cards-toggle" id="toolCardToggleBtn" style="margin:4px 0 2px 40px;display:flex;gap:8px">
|
||||
<button onclick="toggleShowAllTools()" id="btnShowAllTools" data-i18n="show_all_tools">Show all tools</button>
|
||||
</div>
|
||||
|
||||
<button class="scroll-bottom-fab" id="scrollBottomFab" onclick="scrollToBottom()" title="Scroll to bottom"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg></button>
|
||||
</div>
|
||||
<div class="update-banner" id="updateBanner">
|
||||
@@ -365,15 +319,15 @@
|
||||
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>
|
||||
<div class="mic-status" id="micStatus"><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">
|
||||
<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">
|
||||
<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">
|
||||
<button class="icon-btn mic-btn voice-chip" id="btnMic" title="Voice input">
|
||||
<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"/>
|
||||
@@ -425,8 +379,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="composer-right">
|
||||
<span class="composer-status" id="composerStatus" style="display:none"></span>
|
||||
<div class="ctx-indicator-wrap" id="ctxIndicatorWrap" style="display:none">
|
||||
<span class="composer-status" id="composerStatus"></span>
|
||||
<div class="ctx-indicator-wrap" id="ctxIndicatorWrap">
|
||||
<button class="ctx-indicator" id="ctxIndicator" type="button" aria-label="Context window usage" aria-describedby="ctxTooltip">
|
||||
<span class="ctx-ring">
|
||||
<svg class="ctx-ring-svg" viewBox="0 0 24 24" aria-hidden="true">
|
||||
@@ -441,10 +395,10 @@
|
||||
<div class="ctx-tooltip-line" id="ctxTooltipUsage"></div>
|
||||
<div class="ctx-tooltip-line" id="ctxTooltipTokens"></div>
|
||||
<div class="ctx-tooltip-line" id="ctxTooltipThreshold"></div>
|
||||
<div class="ctx-tooltip-line" id="ctxTooltipCost" style="display:none"></div>
|
||||
<div class="ctx-tooltip-line" id="ctxTooltipCost"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="cancel-btn" id="btnCancel" onclick="cancelStream()" style="display:none" title="Stop generation" aria-label="Stop generation">
|
||||
<button class="cancel-btn" id="btnCancel" onclick="cancelStream()" title="Stop generation" aria-label="Stop generation">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5" y="5" width="14" height="14" rx="2"></rect></svg>
|
||||
</button>
|
||||
<button class="send-btn" id="btnSend" title="Send message" disabled>
|
||||
@@ -464,10 +418,10 @@
|
||||
<div class="resize-handle" id="rightpanelResize"></div>
|
||||
<div class="panel-header">
|
||||
<span>Workspace</span>
|
||||
<span class="git-badge" id="gitBadge" style="display:none"></span>
|
||||
<span class="git-badge" id="gitBadge"></span>
|
||||
<div class="panel-actions">
|
||||
<button class="panel-icon-btn" id="btnCollapseWorkspacePanel" title="Hide workspace panel" onclick="toggleWorkspacePanel(false)"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6"/></svg></button>
|
||||
<button class="panel-icon-btn" id="btnUpDir" title="Parent directory" onclick="navigateUp()" style="display:none"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg></button>
|
||||
<button class="panel-icon-btn" id="btnUpDir" title="Parent directory" onclick="navigateUp()"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg></button>
|
||||
<button class="panel-icon-btn" id="btnNewFile" title="New file" onclick="promptNewFile()"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></button>
|
||||
<button class="panel-icon-btn" id="btnNewFolder" title="New folder" onclick="promptNewFolder()"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg></button>
|
||||
<button class="panel-icon-btn" id="btnRefreshPanel" title="Refresh" onclick="if(S.session)loadDir(S.currentDir)"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg></button>
|
||||
@@ -480,7 +434,7 @@
|
||||
<input type="text" id="wsSearchInput" placeholder="Files suchen..." oninput="filterWsFiles()">
|
||||
<button class="ws-search-clear" id="wsSearchClear" onclick="clearWsSearch()">×</button>
|
||||
</div>
|
||||
<div class="breadcrumb-bar" id="breadcrumbBar" style="display:none"></div>
|
||||
<div class="breadcrumb-bar" id="breadcrumbBar"></div>
|
||||
<div class="file-tree" id="fileTree"></div>
|
||||
<div class="preview-area" id="previewArea">
|
||||
<div class="preview-path" id="previewPath">
|
||||
@@ -490,8 +444,8 @@
|
||||
<button id="btnEditFile" class="panel-icon-btn" style="font-size:12px;width:auto;padding:2px 8px;display:none;align-items:center;gap:4px" onclick="toggleEditMode()"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg> 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>
|
||||
<div class="preview-img-wrap" id="previewImgWrap"><img class="preview-img" id="previewImg" src="" alt=""></div>
|
||||
<div class="preview-md" id="previewMd"></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>
|
||||
@@ -562,7 +516,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="onboarding-overlay" id="onboardingOverlay" style="display:none" role="dialog" aria-modal="true" aria-labelledby="onboardingTitle">
|
||||
<div class="onboarding-overlay" id="onboardingOverlay" role="dialog" aria-modal="true" aria-labelledby="onboardingTitle">
|
||||
<div class="onboarding-card">
|
||||
<div class="onboarding-shell">
|
||||
<div class="onboarding-sidebar">
|
||||
@@ -575,7 +529,7 @@
|
||||
<div class="onboarding-status" id="onboardingNotice"></div>
|
||||
<div class="onboarding-body" id="onboardingBody"></div>
|
||||
<div class="onboarding-actions">
|
||||
<button class="sm-btn" id="onboardingBackBtn" onclick="prevOnboardingStep()" style="display:none" data-i18n="onboarding_back">Back</button>
|
||||
<button class="sm-btn" id="onboardingBackBtn" onclick="prevOnboardingStep()" data-i18n="onboarding_back">Back</button>
|
||||
<button class="sm-btn" id="onboardingSkipBtn" onclick="skipOnboarding()" style="margin-right:auto;opacity:.7" data-i18n="onboarding_skip">Skip setup</button>
|
||||
<button class="sm-btn" id="onboardingNextBtn" onclick="nextOnboardingStep()" style="font-weight:700;color:var(--blue);border-color:rgba(124,185,255,.32)" data-i18n="onboarding_continue">Continue</button>
|
||||
</div>
|
||||
@@ -635,7 +589,7 @@
|
||||
<button class="settings-action-btn" id="btnImportJSON" title="Import session from JSON"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><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> <span data-i18n="import">Import</span></button>
|
||||
<button class="settings-action-btn danger" id="btnClearConvModal" onclick="clearConversation()" title="Clear all messages in this conversation"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 1 2 2 2v2"/></svg> Clear</button>
|
||||
</div>
|
||||
<input type="file" id="importFileInput" accept=".json" style="display:none">
|
||||
<input type="file" id="importFileInput" accept=".json">
|
||||
</div>
|
||||
<div class="settings-pane" id="settingsPanePreferences" role="tabpanel" aria-labelledby="settingsTabPreferences">
|
||||
<div class="settings-section-head">
|
||||
@@ -722,6 +676,15 @@
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:4px" data-i18n="settings_desc_check_updates">Show a banner when newer versions of the WebUI or Agent are available. Runs a background git fetch periodically.</div>
|
||||
</div>
|
||||
<div class="settings-field">
|
||||
<label data-i18n="settings_label_user_profile">Your Profile</label>
|
||||
<div style="font-size:11px;color:var(--muted);margin-bottom:6px" data-i18n="settings_desc_user_profile">This is how you appear in the chat.</div>
|
||||
<div style="display:flex;gap:8px;align-items:center">
|
||||
<div id="userAvatarPreview" style="width:40px;height:40px;border-radius:50%;background:var(--accent);display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0">Y</div>
|
||||
<input type="text" id="settingsUserEmoji" placeholder="🙂" maxlength="8" style="width:50px;padding:6px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:16px;text-align:center" oninput="updateUserAvatarPreview(this.value)">
|
||||
<input type="text" id="settingsUserName" placeholder="You" maxlength="32" style="flex:1;padding:6px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:13px">
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-field" style="margin-top:12px">
|
||||
<label for="settingsBotName" data-i18n="settings_label_bot_name">Assistant Name</label>
|
||||
<div style="font-size:11px;color:var(--muted);margin-bottom:6px" data-i18n="settings_desc_bot_name">Display name for the assistant throughout the UI. Defaults to Hermes.</div>
|
||||
<input type="text" id="settingsBotName" placeholder="Hermes" maxlength="64" style="width:100%;padding:8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:13px">
|
||||
@@ -739,7 +702,9 @@
|
||||
<div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px">
|
||||
<label for="settingsPassword" data-i18n="settings_label_password">Access Password</label>
|
||||
<div style="font-size:11px;color:var(--muted);margin-bottom:6px" data-i18n="settings_desc_password">Enter a new password to set or change it. Leave blank to keep current setting.</div>
|
||||
<form id="settingsPasswordForm" onsubmit="return false">
|
||||
<input type="password" id="settingsPassword" placeholder="Enter new password…" data-i18n-placeholder="password_placeholder" style="width:100%;padding:8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:6px;font-size:13px">
|
||||
</form>
|
||||
</div>
|
||||
<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" data-i18n="disable_auth">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" data-i18n="sign_out">Sign Out</button>
|
||||
@@ -772,7 +737,7 @@
|
||||
<span class="logs-filename" id="logsFileName">Select a log file</span>
|
||||
<div class="logs-toolbar-right">
|
||||
<input type="text" id="logsSearchInput" placeholder="Search logs..." style="display:none;width:160px;padding:3px 8px;background:var(--code-bg);color:var(--text);border:1px solid var(--border2);border-radius:5px;font-size:11px" onkeyup="filterLogContent()">
|
||||
<div class="logs-level-btns" id="logsLevelBtns" style="display:none">
|
||||
<div class="logs-level-btns" id="logsLevelBtns">
|
||||
<button class="log-level-btn active" data-level="all" onclick="setLogLevel('all')">All</button>
|
||||
<button class="log-level-btn" data-level="ERROR" onclick="setLogLevel('ERROR')" style="color:#e85353">ERROR</button>
|
||||
<button class="log-level-btn" data-level="WARN" onclick="setLogLevel('WARN')" style="color:#e8a030">WARN</button>
|
||||
@@ -782,14 +747,14 @@
|
||||
<input type="checkbox" id="logsAutoRefresh" onchange="toggleLogAutoRefresh()" style="width:13px;height:13px;accent-color:var(--accent)">
|
||||
Live
|
||||
</label>
|
||||
<button class="icon-btn" id="logsRefreshBtn" style="display:none" onclick="loadLogsPanel()" title="Refresh logs">
|
||||
<button class="icon-btn" id="logsRefreshBtn" onclick="loadLogsPanel()" title="Refresh logs">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="logs-body" id="logsBody">
|
||||
<div style="color:var(--muted);font-size:12px;padding:20px;text-align:center" id="logsEmptyState">Select a log file from the sidebar to view its contents.</div>
|
||||
<div id="logsContent" style="display:none"></div>
|
||||
<div id="logsContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -810,7 +775,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mobile-overlay" id="mobileOverlay" onclick="closeMobileSidebar()"></div>
|
||||
<div class="app-dialog-overlay" id="appDialogOverlay" style="display:none" aria-hidden="true">
|
||||
<div class="app-dialog-overlay" id="appDialogOverlay" aria-hidden="true">
|
||||
<div class="app-dialog" id="appDialog" role="dialog" aria-modal="true" aria-labelledby="appDialogTitle" aria-describedby="appDialogDesc">
|
||||
<div class="app-dialog-header">
|
||||
<div class="app-dialog-title" id="appDialogTitle">Confirm action</div>
|
||||
@@ -819,7 +784,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="app-dialog-desc" id="appDialogDesc"></div>
|
||||
<input class="app-dialog-input" id="appDialogInput" type="text" style="display:none">
|
||||
<input class="app-dialog-input" id="appDialogInput" type="text">
|
||||
<div class="app-dialog-actions">
|
||||
<button class="app-dialog-btn" id="appDialogCancel" type="button" data-i18n="cancel">Cancel</button>
|
||||
<button class="app-dialog-btn confirm" id="appDialogConfirm" type="button">Confirm</button>
|
||||
@@ -833,10 +798,11 @@
|
||||
<script src="/static/workspace.js"></script>
|
||||
<script src="/static/sessions.js"></script>
|
||||
<script src="/static/commands.js"></script>
|
||||
<script src="/static/boot.js"></script>
|
||||
<script src="/static/activity-tree.js"></script>
|
||||
<script src="/static/messages.js"></script>
|
||||
<script src="/static/panels.js"></script>
|
||||
<script src="/static/onboarding.js"></script>
|
||||
<script src="/static/boot.js"></script>
|
||||
<!-- Task Edit Modal -->
|
||||
<div id="taskEditModal" class="modal-overlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:2000;justify-content:center;align-items:center">
|
||||
<div style="background:var(--surface);border:1px solid var(--border2);border-radius:12px;padding:24px;width:420px;max-width:90vw">
|
||||
|
||||
Reference in New Issue
Block a user