This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Miguel PalhasandClaude Opus 5 6be0dba74e feat(maestro): spawn form back in a modal on web
Draft panes are gone: an empty pane is a placeholder again, and every
spawn affordance opens a dialog around the shared spawn form. Escape,
Cancel, the ✕ and a backdrop click dismiss it; a successful submit puts
the session in a new tab (Spawn similar and the fast-forward fallback
keep placing it beside the pane they acted on).

Removes the per-leaf draft state, its persistence strip, and the form
reuse/teardown machinery the panes needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 14:44:55 +01:00

5.5 KiB
Raw Permalink Blame History

Design

Visual system for the Maestro web UI. Source of truth is web/src/styles.css (:root token block); this document mirrors it. Register: product — design serves the task. See PRODUCT.md for strategy.

Theme

Dark is the default (terminal context); light is a full peer. Everything is driven by CSS custom-property tokens, so a theme is a token swap, never a restyle. Resolution order:

  1. :root[data-theme="light"|"dark"] — explicit user pin (persisted in localStorage as maestro-theme), set by the header toggle.
  2. @media (prefers-color-scheme: light) — followed when the user hasn't pinned a theme.
  3. Otherwise the :root defaults (dark).

The xterm terminal stays dark in both themes by convention; --term-bg is dark in light mode too.

Color (OKLCH)

Strategy: Restrained (product floor) with one committed signal hue — green = "alive", carrying running status, primary action, focus, and selection. Neutrals are a cool slate (hue ~256). Green is the signal, never a costume: text stays neutral high-contrast, surfaces stay slate, so the result reads terminal-native without CRT novelty.

Role Dark Light
--bg oklch(0.178 0.012 256) oklch(0.985 0.003 256)
--surface oklch(0.214 0.014 256) oklch(1 0 0)
--surface-2 oklch(0.252 0.016 256) oklch(0.965 0.004 256)
--surface-3 oklch(0.292 0.018 256) oklch(0.93 0.006 256)
--border oklch(0.318 0.018 256) oklch(0.892 0.008 256)
--border-strong oklch(0.4 0.022 256) oklch(0.82 0.012 256)
--ink (body text) oklch(0.945 0.006 256) oklch(0.26 0.014 256)
--muted (secondary) oklch(0.722 0.014 256) oklch(0.47 0.016 256)
--faint (tertiary/decorative) oklch(0.58 0.014 256) oklch(0.62 0.016 256)
--accent / --live oklch(0.83–0.84 0.16–0.17 152) oklch(0.6 0.15–0.16 151)
--accent-ink (text on accent) oklch(0.2 0.03 256) oklch(0.99 0.01 152)
--danger oklch(0.72 0.18 25) oklch(0.55 0.21 27)
--warn oklch(0.82 0.14 85) oklch(0.66 0.15 75)

Body and secondary text meet WCAG AA (≥4.5:1) against their surface in both themes; --faint is reserved for decorative/large use only. Status never relies on color alone — a running dot pulses and is filled; a dead session is a hollow ring and carries an "exited" badge.

Tints/glow derive from the accent at low alpha: --accent-tint (0.12), --accent-ring (focus, ~0.55), --live-glow (status halo).

Typography

Monospace is the UI typeface (brand identity), system-stack only (offline- safe, zero web-font dependency, embedded clean into the web binary):

  • --font-mono: ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", "Roboto Mono", Menlo, Consolas, monospace — chrome, labels, data, ids, the wordmark.
  • --font-sans: system-ui stack — reserved for the few prose strings (empty-state sentences).

Fixed rem scale (product register; not fluid/clamped): --fs-11 0.6875rem through --fs-20 1.25rem, in ~1.12–1.2 steps. Base body --fs-15 (0.9375rem), line-height 1.45. No display sizes — the largest type is the 1.25rem wordmark.

Spacing, radius, sizing

  • Spacing scale: --sp-1..--sp-8 = 4 / 8 / 12 / 16 / 20 / 24 / 32 px.
  • Radius: --r-1 6 · --r-2 9 · --r-3 14 · --r-pill 999.
  • Touch/row sizing: --row-h 46, --control-h 38, --tap 40 (mobile-first touch targets).

Motion

State and feedback only; 120–240 ms, ease-out (cubic-bezier(0.22,1,0.36,1)), no bounce. Tokens --dur-fast/--dur/--dur-slow.

  • Living signals: brand dot + running-status ripple pulse (the "alive" cue); connecting state pill pulses.
  • Interaction: row hover/focus, button press (1px), input focus ring, chevron rotation, theme-toggle sun/moon swap.
  • Every animation has a prefers-reduced-motion: reduce path (pulses off, transitions near-instant).

Layout & structure

Mobile-first, structural responsiveness (not fluid type). Single ≤520px breakpoint stacks the header (full-width search, wrapped actions). A faint slate grid texture sits on --bg as terminal atmosphere (very low contrast, not decoration).

Components

Three surfaces, all DOM built in TS, styled by class:

  • Catalog — sticky blurred header (pulsing wordmark, search, theme toggle, Spawn); status bar (N sessions · M live tally); session tree (collapsible directory folders with counts, leaf rows with status dot, name, command meta, exited badge, reveal-on-hover kill). Empty states are distinct and teach: no-sessions, no-search-match.
  • Attach — header (back, session <id>, live/closed/error state pill) over a framed dark xterm with a green cursor.
  • Spawn modal — the spawn form in a centered dialog (a bottom sheet below 600px) over a blurred backdrop; mono inputs with green focus ring; grouped worktree fieldset. Escape, Cancel, ✕ and a backdrop click all dismiss it. Submitting gives the new session its own tab; "Spawn similar" seeds the form from a session's dir + command and puts the result beside it.

Interactive elements ship default / hover / focus-visible / active / disabled states; the accent green is used only for action, selection, focus, and live status — never decoration.

Dev review aids (DEV-only, stripped from production)

web/src/mock.ts + query params seed a representative catalog without a running daemon: ?mock (seed sessions), ?mock&spawn (open the spawn modal), ?theme=light|dark (pin a theme). Guarded by import.meta.env.DEV.