Skip to content

feat(web): retheme every surface on shared design tokens (AGT-4201) - #558

Merged
unohee merged 2 commits into
mainfrom
feat/ui-surface-refactor
Sep 4, 2026
Merged

feat(web): retheme every surface on shared design tokens (AGT-4201)#558
unohee merged 2 commits into
mainfrom
feat/ui-surface-refactor

Conversation

@unohee

@unohee unohee commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

What

The web surface (cockpit, chat, orchestration, threads, warehouse, and the two server-rendered legacy pages / and /issues) now draws from one token vocabulary (web/static/css/tokens.css), ported from the chat-ui agent guideline's §8.1 vocabulary to this vanilla ESM stack.

  • Tokens + theme: semantic tokens only (--bg-app/--bg-surface/…, --fg-*, accent/danger/warning/success, radius, 4pt spacing, type scale, motion, z-index, code + chart + role colours). Light theme is a value swap under [data-theme="light"], stamped by themeBoot.js before first paint; theme.mjs owns the toggle (persisted, follows the OS until a choice is made, aria-pressed + a label naming the mode it switches to). Every page has the toggle.
  • shell.css: shared primitives — sticky blurred topbar with one nav across all pages, .btn family, inputs, chips, badges, cards, status lines, empty state, scroll-to-latest, in-page confirm card, drop overlay, :focus-visible ring (never outline:none alone), prefers-reduced-motion kill, .sr-only.
  • Chat (§2.1/§2.3): composer.mjs — autogrow textarea (≤40vh), Enter sends / Shift+Enter newline / isComposing+229 guard, Send disabled on empty, in-place "Sending…" with aria-busy, draft persistence, labelled chip-remove buttons, drop overlay. scrollFollow.mjs — 80px slack, "↓ Latest" (with unseen count), one live-region announcement per batch. Loading / empty / unreachable states.
  • Destructive actions (§3.2): threads' Resolve now shows a confirm card naming the thread and version (Tauri has no window.confirm); warehouse overwrite arms on first click naming the file.
  • Colours in JS: ROLE_COLORS, KIND_COLORS, statusColor() are token references; SVG paints go through paint() (attribute + inline style) so the graph re-paints with the theme.
  • Legacy pages: dashboardHtml.ts / issueBoardHtml.ts keep their inline stylesheets (their scripts are asserted by name in tests) but their :root blocks are now aliases into tokens.css; all 34 hex literals removed; brand font applied.
  • Font: Inter (latin variable subset, 48 KB) vendored with its OFL licence; Korean falls through to the system stack.

Gate

tests/web/tokens.test.ts enforces the guideline permanently: every var() resolves; no hex literal outside tokens.css; no inline <style> in a shell; every shell links tokens+shell+themeBoot in order and shares one nav; every icon-only button has aria-label; legacy pages link tokens, boot the theme, offer a toggle, and carry no colour literal.

New unit tests: composer.test.ts, scrollFollow.test.ts, theme.test.ts, plus a two-step resolve case in threadBoard.test.ts.

Verification

  • npm run typecheck ✓ · npm run lint ✓ (one pre-existing warning in vega adapter test) · LC_ALL=C npx vitest run → 5524 passed, 10 skipped (398 files)
  • Existing DOM contracts (chatView.test.ts, orchestrationView.test.ts, threadBoard.test.ts) unchanged and green.
  • Not run: axe / screen-reader pass (no browser harness in CI); breakpoints reviewed by CSS only.

Guideline §12 self-check

  • states defined per component (idle/loading/streaming/success/error/empty/disabled) — chat room + composer; others idle/loading/error/empty
  • no hardcoded colour/spacing/font outside tokens.css (test-enforced)
  • focus-visible ring on every interactive element (shell.css global)
  • icon-only buttons named (test-enforced)
  • Enter/Shift+Enter/IME behaviour (tested)
  • destructive → in-page confirm naming target (tested)
  • reduced-motion honoured (global + graph-specific)
  • live region once per batch, not per token (tested)
  • axe run — not available in CI; manual pass pending on vela

One token vocabulary (tokens.css) now paints all seven pages — cockpit,
chat, orchestration, threads, warehouse, and the two server-rendered
legacy pages — with a light theme as a value swap under
[data-theme="light"] stamped by themeBoot.js before first paint, and a
toggle on every page. shell.css carries the shared primitives: topbar,
buttons, inputs, chips, badges, focus ring, empty/status lines, in-page
confirm card, reduced-motion.

Chat gets the guideline's composer (autogrow textarea, Enter sends /
Shift+Enter newline / IME-safe, empty-disabled, in-place sending state,
draft persistence, labelled attachment chips, drop overlay) and stream
following (80px slack, "↓ Latest" button, one live-region announcement
per batch). Threads' resolve and the warehouse's overwrite ask in the
page, naming the target. Role/kind/status colours in the JS colour maps
are token references, so the SVG graph re-paints with the theme.

tests/web/tokens.test.ts is the gate: every var() resolves, no hex
literal outside tokens.css, no inline <style> in a shell, every shell
links tokens+shell+themeBoot in order and shares one nav, every
icon-only button has a name. The brand font (Inter, latin subset) is
vendored with its OFL licence.
…/typecheck 10 minutes

Two consecutive Lint runs on this branch sat silent for the full five
minutes inside `npm ci` — after the reify warnings, before any output —
and were cancelled by the job timeout, while the same lockfile installs
in ~20 s on main. Nothing in package.json or the lockfile changed. The
one network call `npm ci` makes after install is the audit report, and
it has no timeout of its own, so a slow audit endpoint stalls the whole
job. `--no-audit --no-fund` removes that dependency and
`--prefer-offline` uses the restored cache first; the wider timeouts
keep a genuinely slow runner from failing the gate.
@unohee
unohee merged commit 394bdc7 into main Sep 4, 2026
7 checks passed
@unohee
unohee deleted the feat/ui-surface-refactor branch September 4, 2026 08:35
unohee added a commit that referenced this pull request Sep 4, 2026
… shell (AGT-4206) (#560)

* feat(web): redesign the Supervisor dashboard on the shared tokens and shell (AGT-4206)

The retheme in #558 only swapped the supervisor's palette; the page kept
its 38 px header with fourteen controls in one row, 9–11 px type, boxed
uppercase labels, and full-bleed columns. This rebuilds the surface on
shell.css: a topbar with the shared navigation, SSE badge and theme
toggle; a control bar (daemon state, provider segmented control, Stop /
Restart, Heartbeat / PR review); a stats strip with tabular numbers; and
three columns of card panels with sentence-case titles. The 598-line
inline <style> moves to web/static/css/supervisor.css, every script-
emitted class keeps its name, and nothing on the page is smaller than
11 px.

window.confirm — absent in the Tauri WebView — is gone from the three
destructive actions (stop/restart the daemon, kill or cancel a process,
move stuck issues to Todo): the button itself becomes the question for
six seconds and the second press answers it. The chat box ignores Enter
during an IME composition.

* fix(web): let the provider control shrink and wrap on narrow screens

A flex item keeps its content width as its minimum, so flex-wrap alone never fired and the 420px layout grew past the viewport. min-width: 0 lets the segmented control shrink; its buttons then wrap.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant