Files
stack/agents/dewey/work/wui/mockups/d3-console.html
T
jason.woltjeandClaude Opus 5.5 1f4882d2b4 docs(wui): Dewey's design work: brand board, five mockups, slice 1 S5 views and checks
Brand board revisions, the five dashboard mockups with their verify
script and evidence, the slice 1 S5 design note (SLICE1-VIEWS.md, rev 3
plus the S2c section 10 update), the Console mockup of inbox, tasks,
agents and trail (mockups/slice1/) and checks/slice1-verify.mjs (16
checks; check 16 pinned to bus at d27042fa). Also the CHAT-03 progress
log and the 2026-09-26 dirty-tree manifest. Design and evidence only;
no package code. Row 40 (#1522), committed on its own at Sage's
direction, 2026-10-09.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 17:54:50 -05:00

100 lines
10 KiB
HTML

<!doctype html>
<html lang="en" data-design="console" data-palette="harbor" data-mode="light">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Control board · Mosaic Stack (Console)</title>
<meta name="description" content="Mosaic Stack web interface mockup, design 3: command bar, project tree, dense table, inspector drawer. Prototype only; every action is simulated.">
<link rel="stylesheet" href="shared/app.css">
<link rel="stylesheet" href="d3-console.css">
</head>
<body>
<a class="skip" href="#main">Skip to content</a>
<div id="shell" class="shell"></div>
<div id="toasts"></div>
<div id="announce" class="sr-only" aria-live="polite" aria-atomic="true"></div>
<dialog id="palette" class="cmd" aria-labelledby="cmd-title" data-overlay>
<form method="dialog" class="cmd-form"><h2 id="cmd-title" class="sr-only">Command palette</h2><label for="cmd-input" class="sr-only">Go to or run</label><input id="cmd-input" type="text" placeholder="Go to a page, session, or project" autocomplete="off"><ul id="cmd-list" role="listbox" aria-label="Matches"></ul><p class="small muted">Enter opens the highlighted match. Escape closes. Commands are simulated.</p></form>
</dialog>
<script src="../brand.js"></script>
<script src="shared/data.js"></script>
<script src="shared/views.js"></script>
<script>
window.DesignLayout = {
id: 'console', name: 'Console',
nav: [["/","home","Control board"],["/sessions","conversation","Sessions"],["/work","board","Work"],["/projects","projects","Projects"],["/agents","agents","Agents"],["/runs","check","Runs"],["/messages","people","Messages"],["/library","extensions","Library"],["/settings","sun","Settings"]],
shell(ctx, V) {
const B = window.Brand, D = window.MosaicData;
const tree = D.projects.map(p => `<li><details open><summary><span class="ic" aria-hidden="true">${B.icon('projects')}</span><a href="#/projects/${p.id}" data-nav>${V.esc(p.name)}</a></summary><ul>${p.workspaces.map(w => { const n = D.sessions.filter(s => s.project === p.id && s.workspace === w.id && !['settled','stopped'].includes(s.state)).length; return `<li class="${w.status}"><a href="#/sessions?filter=${p.id}" class="tree-ws">${V.esc(w.name)}${n ? `<span class="count">${n}</span>` : ''}</a></li>`; }).join('')}</ul></details></li>`).join('');
return `<header class="cmdbar"><a href="#/" class="wordmark"><i>M</i>Mosaic<small>prototype</small></a>
<button type="button" class="cmd-open" id="cmd-open" aria-haspopup="dialog" aria-controls="palette"><span class="ic" aria-hidden="true">${B.icon('search')}</span><span>Go to or run</span><kbd>Ctrl K</kbd></button>
<nav class="cmd-nav" aria-label="Primary"><ul>${this.nav.map(([h, i, l]) => `<li><a href="#${h}" data-nav class="cmd-link" title="${l}">${V.icon(i)}<span class="cmd-label">${l}</span></a></li>`).join('')}</ul></nav>
<div class="cmd-right"><span class="cmd-status" title="Sessions waiting on you"><span class="ic" aria-hidden="true">${B.icon('warning')}</span>${D.waiting.length}</span><label class="sr-only" for="demo-console">Demo state</label><select id="demo-console" data-demo class="demo-select">${['normal','loading','empty','error'].map(s => `<option value="${s}" ${ctx.demoState === s ? 'selected' : ''}>${s}</option>`).join('')}</select><a href="#/settings" class="btn" data-nav aria-label="Settings and appearance">${V.icon('sun')}</a></div></header>
<div class="frame"><nav class="tree" id="tree" aria-label="Projects and workspaces"><h2 class="tree-title">Projects</h2><ul class="tree-list">${tree}</ul><a href="#/launch" class="btn primary" data-nav>${V.icon('plus', 'Launch')}</a><p class="prototype-note">Prototype. Actions are simulated. Arrow keys move through the session table; Enter opens the inspector.</p></nav>
<main id="main" tabindex="-1" class="content"></main>
<aside class="inspector" id="inspector" aria-labelledby="insp-title" hidden><div class="inspector-head"><h2 id="insp-title">Inspector</h2><button type="button" class="btn" id="insp-close">Close</button></div><div id="insp-body"></div></aside></div>
<footer class="foot"><span>Mosaic Stack web interface mockup · design 3 Console</span><a href="index.html">All designs</a></footer>`;
},
home(ctx, V) {
const D = window.MosaicData;
return `<div class="page-head"><h1>Control board</h1><p class="muted">Dense view. Select a row to inspect without leaving the list. ${V.feat('proposed')}</p></div>
<section class="console-waiting" aria-labelledby="h-wait"><h2 id="h-wait">Waiting on you <span class="count">${D.waiting.length}</span></h2>${V.waitingList(ctx, 'row')}</section>
<section aria-labelledby="h-all"><h2 id="h-all">All sessions <span class="count">${D.sessions.length}</span></h2><p class="small muted">Use arrow keys and Enter to inspect a row. Click the agent name to open the full session.</p>${V.sessionsTable(ctx, D.sessions)}</section>
<section class="panel console-activity" aria-labelledby="h-act"><h2 id="h-act">Activity</h2>${V.eventStream(ctx, 8)}</section>`;
},
afterRender(ctx, V) {
const rows = [...document.querySelectorAll('table.sessions tbody tr[data-session]')];
rows.forEach((tr, i) => { tr.tabIndex = i === 0 ? 0 : -1; tr.setAttribute('aria-selected', 'false'); });
const insp = document.getElementById('inspector'); if (insp && ctx.view !== 'home') { insp.hidden = true; document.body.classList.remove('has-inspector'); }
},
inspect(id, V) {
const s = V.sessionOf(id); if (!s) return;
const insp = document.getElementById('inspector'), body = document.getElementById('insp-body');
document.querySelectorAll('table.sessions tbody tr').forEach(tr => tr.setAttribute('aria-selected', String(tr.dataset.session === id)));
body.innerHTML = `<p class="scope-line">${V.avatar(s.agent)}<b>${V.esc(V.agentOf(s.agent).name)}</b> in ${V.esc(s.project)}/${V.esc(s.workspace)}</p><p>${V.badge(s.state)}</p><dl class="kv"><dt>Goal</dt><dd>${V.esc(s.goal.text)}<br><span class="muted">${V.esc(s.goal.report)}</span></dd><dt>Controller</dt><dd>${V.esc(s.controller)} <span class="gen">gen ${s.generation}</span></dd><dt>Last</dt><dd>${V.esc(s.last)}</dd><dt>Config</dt><dd class="cfg cfg-${s.config}">${s.config}</dd><dt>Turns</dt><dd>${s.turns}</dd></dl><div class="actions"><a href="#/sessions/${s.id}" class="btn primary">Open session</a>${V.btn('Observe', 'observe:' + s.id)}${V.btn('Stop', 'stop:' + s.id, {disabled: ['settled','stopped'].includes(s.state)})}</div>`;
insp.hidden = false; document.body.classList.add('has-inspector');
window.MosaicApp.announce(`Inspecting ${V.agentOf(s.agent).name} in ${s.project}/${s.workspace}.`);
},
keydown(e, ctx, V) {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); openPalette(); return; }
const tr = e.target.closest?.('table.sessions tbody tr'); if (!tr) return;
const rows = [...tr.parentElement.querySelectorAll('tr[data-session]')]; const i = rows.indexOf(tr);
if (e.key === 'ArrowDown' && rows[i + 1]) { e.preventDefault(); rows[i + 1].focus(); }
if (e.key === 'ArrowUp' && rows[i - 1]) { e.preventDefault(); rows[i - 1].focus(); }
if (e.key === 'Home') { e.preventDefault(); rows[0].focus(); } if (e.key === 'End') { e.preventDefault(); rows.at(-1).focus(); }
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.inspect(tr.dataset.session, V); }
},
};
const CMDS = () => { const D = window.MosaicData, V = window.MosaicViews; return [
...window.DesignLayout.nav.map(([h, , l]) => ({label: 'Go to ' + l, href: '#' + h})),
...D.sessions.map(s => ({label: `Session: ${V.agentOf(s.agent).name} in ${s.project}/${s.workspace} (${s.state})`, href: '#/sessions/' + s.id})),
...D.projects.map(p => ({label: 'Project: ' + p.name, href: '#/projects/' + p.id})),
{label: 'Launch an agent', href: '#/launch'}, {label: 'Mode: light', theme: 'light'}, {label: 'Mode: dim', theme: 'dim'}, {label: 'Mode: dark', theme: 'dark'},
...window.Brand.palettes.map(p => ({label: 'Palette: ' + p.name, palette: p.id})),
]; };
let cmdIndex = 0, cmdMatches = [];
function renderCmd(term) {
const list = document.getElementById('cmd-list'); const t = term.trim().toLowerCase();
cmdMatches = CMDS().filter(c => !t || c.label.toLowerCase().includes(t)).slice(0, 9); cmdIndex = 0;
list.innerHTML = cmdMatches.map((c, i) => `<li role="option" id="cmd-${i}" aria-selected="${i === 0}" data-i="${i}">${window.MosaicViews.esc(c.label)}</li>`).join('') || '<li class="muted" role="option" aria-selected="false">No match. Try a page name, agent, or project.</li>';
document.getElementById('cmd-input').setAttribute('aria-activedescendant', cmdMatches.length ? 'cmd-0' : '');
}
function runCmd(c) { const app = window.MosaicApp; document.getElementById('palette').close(); if (!c) return; if (c.href) location.hash = c.href; if (c.theme) app.setTheme(null, c.theme); if (c.palette) app.setTheme(c.palette, null); if (c.theme || c.palette) app.toast(`Applied ${c.label.toLowerCase()}.`, 'ok'); }
function openPalette() { const d = document.getElementById('palette'); const input = document.getElementById('cmd-input'); input.value = ''; renderCmd(''); d.showModal(); input.focus(); }
document.addEventListener('click', e => {
if (e.target.closest('#cmd-open')) openPalette();
if (e.target.closest('#insp-close')) { document.getElementById('inspector').hidden = true; document.body.classList.remove('has-inspector'); document.querySelector('table.sessions tbody tr[aria-selected=true]')?.focus(); }
const opt = e.target.closest('#cmd-list [data-i]'); if (opt) runCmd(cmdMatches[+opt.dataset.i]);
const td = e.target.closest('table.sessions tbody tr[data-session] td'); if (td && !e.target.closest('a,button')) window.DesignLayout.inspect(td.parentElement.dataset.session, window.MosaicViews);
});
document.addEventListener('input', e => { if (e.target.id === 'cmd-input') renderCmd(e.target.value); });
document.addEventListener('keydown', e => {
if (e.target.id !== 'cmd-input') return;
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); cmdIndex = Math.max(0, Math.min(cmdMatches.length - 1, cmdIndex + (e.key === 'ArrowDown' ? 1 : -1))); document.querySelectorAll('#cmd-list [data-i]').forEach((li, i) => li.setAttribute('aria-selected', String(i === cmdIndex))); e.target.setAttribute('aria-activedescendant', 'cmd-' + cmdIndex); }
if (e.key === 'Enter') { e.preventDefault(); runCmd(cmdMatches[cmdIndex]); }
});
</script>
<script src="shared/app.js"></script>
</body>
</html>