diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-07-04 17:08:50 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-07-04 17:08:50 +0000 |
| commit | cc6b3234c6ad7f0f6acef83f6ccb3556581e80cb (patch) | |
| tree | 6288fa70d8a871cbcbd727af967f17df66f65143 /web | |
| parent | d8f79c1912eccdc2c65679c4f60603f98f5eece2 (diff) | |
feat(web): simplify nav to tracker board, model dashboard, drops, settings+roles
Remove the old queue/interrupted/ready/running/budget/roles tabs.
Keep: stories (tracker board, now the default tab), stats (model
visibility dashboard), drops (file drop), settings.
Role configuration moves into the Settings tab as a section below the
general settings, rendered by renderSettingsPanel calling renderRolesPanel
with a sub-container. renderRolesPanel now accepts an optional container
parameter and uses safe DOM methods instead of innerHTML for error/empty
states.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DNDHfCtTZDbQueEV5sUBiD
Diffstat (limited to 'web')
| -rw-r--r-- | web/app.js | 83 | ||||
| -rw-r--r-- | web/index.html | 34 |
2 files changed, 40 insertions, 77 deletions
@@ -583,7 +583,7 @@ export function setTaskFilterTab(tab) { } export function getActiveMainTab() { - return localStorage.getItem('activeMainTab') ?? 'queue'; + return localStorage.getItem('activeMainTab') ?? 'stories'; } export function setActiveMainTab(tab) { @@ -1310,7 +1310,7 @@ let lastHistoryFetch = 0; function getActiveTab() { const active = document.querySelector('.tab.active'); - return active ? active.dataset.tab : 'queue'; + return active ? active.dataset.tab : 'stories'; } function getRefreshInterval() { @@ -1327,26 +1327,11 @@ async function fetchHealth() { function renderActiveTab(allTasks) { const activeTab = getActiveTab(); switch (activeTab) { - case 'queue': - renderQueuePanel(allTasks); - break; - case 'interrupted': - renderInterruptedPanel(allTasks); - break; - case 'ready': - renderReadyPanel(allTasks); - break; - case 'running': - renderRunningView(allTasks); - if (Date.now() - lastHistoryFetch > 60_000) { - lastHistoryFetch = Date.now(); - fetchRecentExecutions(BASE_PATH, fetch) - .then(execs => renderRunningHistory(execs)) - .catch(() => { - const histEl = document.querySelector('.running-history'); - if (histEl) histEl.innerHTML = '<p class="task-meta">Could not load execution history.</p>'; - }); - } + case 'stories': + // Guard against yanking the board out from under an in-progress + // HTML5 drag (see draggingStoryId) — the next poll tick after the + // drag ends will pick up any server-side change. + if (!draggingStoryId) renderStoriesPanel(); break; case 'stats': Promise.all([ @@ -1357,21 +1342,9 @@ function renderActiveTab(allTasks) { .then(([execs, agentData, dashStats]) => renderStatsPanel(allTasks, execs, agentData, dashStats)) .catch(() => {}); break; - case 'stories': - // Guard against yanking the board out from under an in-progress - // HTML5 drag (see draggingStoryId) — the next poll tick after the - // drag ends will pick up any server-side change. - if (!draggingStoryId) renderStoriesPanel(); - break; case 'drops': renderDropsPanel(); break; - case 'budget': - renderBudgetPanel(); - break; - case 'roles': - renderRolesPanel(); - break; case 'settings': renderSettingsPanel(); break; @@ -1444,28 +1417,30 @@ if (typeof window !== 'undefined') { -function renderSettingsPanel() { +async function renderSettingsPanel() { const panel = document.querySelector('[data-panel="settings"]'); if (!panel) return; panel.innerHTML = ''; + + // ── General settings ── const section = document.createElement('div'); section.className = 'stats-section'; section.style.padding = '1rem'; const heading = document.createElement('h2'); - heading.textContent = 'User Settings'; + heading.textContent = 'Settings'; section.appendChild(heading); const refreshLabel = document.createElement('label'); refreshLabel.style.display = 'block'; refreshLabel.style.marginBottom = '0.5rem'; refreshLabel.textContent = 'Auto-Refresh Interval'; - + const refreshSelect = document.createElement('select'); refreshSelect.className = 'agent-selector'; refreshSelect.style.width = '100%'; - + const options = [ { label: '5 seconds', value: '5000' }, { label: '10 seconds (default)', value: '10000' }, @@ -1485,12 +1460,22 @@ function renderSettingsPanel() { refreshSelect.addEventListener('change', () => { localStorage.setItem('refreshInterval', refreshSelect.value); - startPolling(); // restart with new interval + startPolling(); }); refreshLabel.appendChild(refreshSelect); section.appendChild(refreshLabel); panel.appendChild(section); + + // ── Role Configuration ── + const rolesSection = document.createElement('div'); + rolesSection.className = 'stats-section'; + rolesSection.style.padding = '1rem'; + const rolesHeading = document.createElement('h2'); + rolesHeading.textContent = 'Role Configuration'; + rolesSection.appendChild(rolesHeading); + panel.appendChild(rolesSection); + await renderRolesPanel(rolesSection); } // ── WebSocket (real-time events) ────────────────────────────────────────────── @@ -4627,7 +4612,7 @@ async function handleActivateRoleVersionClick(roleName, version, btn) { const body = await res.json().catch(() => ({})); throw new Error(body.error || `HTTP ${res.status}`); } - await renderRolesPanel(); + await renderSettingsPanel(); } catch (err) { btn.disabled = false; btn.textContent = original; @@ -4705,15 +4690,17 @@ function renderRoleCard(roleName, versions, doc = document) { return card; } -async function renderRolesPanel() { - const panel = document.querySelector('[data-panel="roles"]'); - if (!panel) return; +async function renderRolesPanel(container = document.querySelector('[data-panel="roles"]')) { + if (!container) return; try { const namesRes = await fetch(`${BASE_PATH}/api/roles`); const names = namesRes.ok ? await namesRes.json() : []; if (!names || names.length === 0) { - panel.innerHTML = '<div class="task-empty">No role configs yet.</div>'; + const empty = document.createElement('div'); + empty.className = 'task-empty'; + empty.textContent = 'No role configs yet.'; + container.appendChild(empty); return; } @@ -4721,12 +4708,14 @@ async function renderRolesPanel() { fetch(`${BASE_PATH}/api/roles/${encodeURIComponent(name)}/versions`).then(r => r.ok ? r.json() : []), )); - panel.innerHTML = ''; names.forEach((name, i) => { - panel.appendChild(renderRoleCard(name, versionsByRole[i] || [])); + container.appendChild(renderRoleCard(name, versionsByRole[i] || [])); }); } catch (err) { - panel.innerHTML = `<div class="panel-fetch-error">Failed to load roles: ${err.message}</div>`; + const errEl = document.createElement('div'); + errEl.className = 'panel-fetch-error'; + errEl.textContent = `Failed to load roles: ${err.message}`; + container.appendChild(errEl); } } diff --git a/web/index.html b/web/index.html index 32c029e..0704cb8 100644 --- a/web/index.html +++ b/web/index.html @@ -40,35 +40,13 @@ <div id="budget-bar" class="budget-bar"></div> </header> <nav class="tab-bar"> - <button class="tab active" data-tab="queue" title="Queue">⏳</button> - <button class="tab" data-tab="interrupted" title="Interrupted">⚠️<span class="tab-count-badge" hidden></span></button> - <button class="tab" data-tab="ready" title="Ready">✅<span class="tab-count-badge" hidden></span></button> - <button class="tab" data-tab="running" title="Running">▶️<span class="tab-count-badge" hidden></span></button> - <button class="tab" data-tab="stories" title="Stories">📋</button> + <button class="tab active" data-tab="stories" title="Tracker">📋</button> + <button class="tab" data-tab="stats" title="Model Dashboard">📊</button> <button class="tab" data-tab="drops" title="Drops">📁</button> - <button class="tab" data-tab="stats" title="Stats">📊</button> - <button class="tab" data-tab="budget" title="Budget & Escalation">💰</button> - <button class="tab" data-tab="roles" title="Roles">🎛️</button> <button class="tab" data-tab="settings" title="Settings">⚙️</button> </nav> <main id="app"> - <div data-panel="queue"> - <div class="panel-task-list"> - <div class="task-empty">Loading…</div> - </div> - </div> - <div data-panel="interrupted" hidden> - <div class="panel-task-list"></div> - </div> - <div data-panel="ready" hidden> - <div class="panel-task-list"></div> - <div class="ready-completed-history"></div> - </div> - <div data-panel="running" hidden> - <div class="running-current"></div> - <div class="running-history"></div> - </div> - <div data-panel="stories" hidden> + <div data-panel="stories"> <div class="stories-toolbar"> <div class="view-toggle" role="tablist" aria-label="Stories view"> <button class="view-toggle-btn active" data-view="board" type="button">Board</button> @@ -83,11 +61,7 @@ <div class="drops-panel"></div> </div> <div data-panel="stats" hidden></div> - <div data-panel="budget" hidden></div> - <div data-panel="roles" hidden></div> - <div data-panel="settings" hidden> - <p class="task-meta" style="padding:1rem">Settings coming soon.</p> - </div> + <div data-panel="settings" hidden></div> </main> <!-- Side panel backdrop --> |
