14 KiB
Design System — ReverbCode
Source of truth for the ReverbCode desktop UI (Electron + React 19 + Tailwind v4
- Radix/shadcn + xterm, in
frontend/src/renderer). Read this before any visual
or UI change. Created by/design-consultationon 2026-06-09.
Product Context
- What this is: ReverbCode is an Electron desktop app for supervising many parallel
AI coding-agent sessions, backed by a Go daemon (backend/). TheaoCLI is the
thin client over the same daemon. - Who it's for: professional software engineers running multiple coding agents at
once who need to delegate, watch, intervene, and ship PRs. - Space/peers: agent orchestration / parallel-agent desktop tools. Closest peers:
emdash (the primary design reference), PostHog Code, Conductor. - Project type: dark-mode-primary desktop app; terminal-dense; keyboard-driven;
runs all day. - The one memorable thing: leverage and speed — "I'm more in control here than
babysitting N terminal tabs myself."
Product flow (what the UI must serve)
ReverbCode is orchestrator-led, which is the one thing that differs from emdash
(a flat list of independent sessions). Grounded in the daemon
(backend/internal/session_manager/manager.go, docs/architecture.md):
- A Project is a registered git repo.
- Per project there is one active Orchestrator session plus N Worker sessions.
Both are the same underlying "session" (durable facts:activity_state,
is_terminated, PR facts); they differ only byKind(KindOrchestratorvs the
default worker). A project may run the orchestrator on a different agent than its workers. - The Orchestrator is the human-facing coordinator: you talk to it; it spawns
workers (ao spawn), messages them (ao send), tracks progress, and synthesizes
results. It avoids implementing unless necessary. - A Worker is a normal agent session — nothing special-cased. It runs one focused
task in an isolated git worktree + branch, with the agent CLI in a terminal as the
conversation, producing a diff → commit/push → PR. It escalates to the orchestrator
only for true blockers or cross-session coordination. - The daemon observes runtime + PR/CI/review facts and derives display status
at read time:working,needs_input,ci_failed,changes_requested,
mergeable,approved,review_pending,pr_open,idle,terminated,merged.
Never store display status; keep session facts small.
Aesthetic Direction
- Direction: match emdash exactly — flat, near-black, hairline-bordered,
utilitarian. Industrial control surface, calm chrome, the terminal as the center of gravity. - Decoration level: minimal. Type + 1px hairlines do all the work. No gradients,
glow, blobs, or emoji. - Mood: low-glare, dense, keyboard-native; signal-over-noise.
- Reference: emdash (primary, visual +
structural), PostHog Code (secondary). Tokens
below were extracted from emdash'ssrc/renderer/index.css. - Deliberate tradeoff: to be emdash, we use the system font stack (not a
custom typeface) and emdash's neutral palette. We diverge in exactly one place: the
accent is ReverbCode's refined blue, not emdash's jade green. The terminal keeps
green (it is the agent CLI).
Typography
System fonts only, like emdash — no custom/Google fonts, zero font payload.
- UI / body / display:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Helvetica Neue", sans-serif(San Francisco
on macOS). - Mono / terminal / code / eyebrow labels:
Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace. - Eyebrow labels (section titles, dialog titles, the rail "PROJECTS" header):
mono, uppercase,letter-spacing: .12–.14em,--foreground-passive. - Scale: 14px base UI / sidebar (
text-sm, weight 400) · 12px secondary + labels
(text-xs) · 13px code/mono/terminal · 11px tiny · 10px micro + badges · 9px sidebar
badge label. Buttons arefont-normal(400), not bold.
Color
emdash's flat Radix-neutral near-black ramp carries the whole interface; color is rare
and meaningful. Values are sRGB approximations of emdash's color(display-p3 …) tokens.
Dark (primary)
| Role | Hex |
|---|---|
--bg canvas |
#111111 |
--bg-1 surface |
#191919 |
--bg-2 raised / hover / active row |
#222222 |
--bg-3 |
#2a2a2a |
--fg text |
#eeeeee |
--fg-muted |
#b4b4b4 |
--fg-passive |
#6e6e6e |
--border hairline |
#3a3a3a |
--border-1 |
#484848 |
--accent (blue) |
#5b9dff |
--needs-you / in-progress (amber) |
#ffcc4a |
--success / mergeable (green) |
#6cb16c |
| terminal green | #7bd88f |
--error (red) |
#d4544f |
| text selection | #3f8ef7 @ 35% |
| terminal bg | #161616 |
Light (supported, not primary)
| Role | Hex |
|---|---|
| canvas / surface / raised | #fcfcfc / #ffffff / #ededee |
| text / muted / passive | #1a1a1a / #666666 / #9a9a9a |
| border | #e3e3e5 |
| accent (blue) | #2563eb |
| amber / green / red | #9a6b00 / #1a7f37 / #c0392b |
Accent rules
- Blue = the live edge only: primary buttons, the active/selected session, focus
rings. Never decorative. - Amber = an agent needs you (blocked /
needs_input/review_pending). - Green =
mergeable/success and terminal/agent CLI text. - Red =
ci_failed/ destructive. - These map 1:1 to the daemon's derived statuses.
Status indicator (no text badges)
Session status is a single ~14px glyph in one fixed slot, never a text pill/badge:
- Working / active → an animated spinner (accent).
- Has an open PR → a PR icon, tinted by PR state: mergeable/approved green,
ci_failedred, review/changes_requestedamber, plainpr_openmuted. - Otherwise → a filled dot:
needs_inputamber (pulsing), idle/done muted gray.
Precedence: working spinner > PR icon > dot. Implemented as StatusGlyph in
components/SideRail.tsx; used in the orchestrator's Workers list. (Worker rows in the
left rail stay name-only — no glyph.)
Spacing
- Base unit: 4px (Tailwind scale: 1=4, 1.5=6, 2=8, 3=12, 4=16, 5=20, 6=24).
- Density: compact / desktop-tight.
- Control + row height:
h-8= 32px default;h-7= 28px small;h-6= 24px xs. - Inputs
px-2.5 py-1; buttonspx-2.5, gap 1–1.5.
Layout
- Approach: fixed three-pane app shell, opens into the workbench (no marketing/dashboard home).
- Panes:
[ rail 240px ] [ center 1fr ] [ side rail 316px ]. - Rail (240px), top → bottom:
- Orchestrator anchor — pinned, single, visually distinct (blue 2px left bar,
--bg-2fill, hub/waypointsicon, name "Orchestrator", a5 agents · 2 need you
mono summary). This is ReverbCode's one addition over emdash. Default landing view. PROJECTSeyebrow label + a+.- Project rows (folder icon + name) with nested worker rows beneath. Each project
row has a hover-revealed+that opens the New-worker modal pre-scoped to that
project (distinct from thePROJECTSheader+, which registers a repo). - Footer:
Search ⌘K,Settings ⌘,. (No Library.) - Account row pinned at the very bottom.
- Orchestrator anchor — pinned, single, visually distinct (blue 2px left bar,
- Worker rows are name-only. Just the session name, truncated. Status, branch, diff,
and PR live in the panes and topbar, never in the row. Selection =--bg-2fill + a
2px blue left bar. (emdash itself shows a faint trailing timestamp; we omit it by choice.) - Center = the conversation. Orchestrator → its coordination terminal (delegate here;
composer reads "tell the orchestrator what to build"). Worker → the agent CLI terminal
(tabbed per agent, e.g.claude-code (1)), with a composer (model selector, worktree
path,Accept edits). The terminal is the conversation; no separate chat surface. - Side rail (316px): orchestrator → a quiet Workers list (name + project + derived
status). Worker → the Git review rail:Changed N→ All files / Discard all / Stage
all → file rows (+adds −dels, stage toggle) →Commit message+Description→
Commit & Push (primary blue) → branch +Create PR. - Border radius:
sm4px (scrollbar) ·md6px (buttons, inputs, toggles) ·
lg8px (rows, cards, panels) ·xl12px (modals) ·full(badges/pills/dots). - Icons: lucide only. No emoji.
Topbar
- Left (both):
project / sessionbreadcrumb + pin; for the orchestrator, a hub iconOrchestrator.
- Right — worker session: a PR/CI status pill that is the action
(PR #156 · mergeablegreen /CI failedred /review requestedamber /
Open PRwhen none) → Changes / Files / Terminal view toggles → ⋯ session menu
(rename, restart, kill, claim PR — theao session …commands). - Right — orchestrator: + New worker → Terminal toggle → ⋯ menu. No diff toggles.
Spawn-worker modal (mirrors emdash's Create Task)
You mostly let the orchestrator spawn workers from its conversation; the manual paths
(the topbar + New worker, a project row's hover +, or ao spawn) open a modal that
mirrors emdash exactly. Launching from a project row pre-fills the Project field:
- Centered dialog, 12px radius,
max-w~512px,bgcanvas,ring-1at 10% fg,
fade + zoom-95 enter. - Header: eyebrow mono-uppercase title
New worker+×close. - Body (
gap15–16px): a borderless large name field (18px, auto-focus, slug
rule "letters, numbers, hyphens") → Project selector → Agent selector
(claude-code / codex / opencode / …) → a "Based on" bordered card with a segmented
controlBranch · Issue · Pull Requestrevealing a combobox → a Prompt / Workspace
tab where Prompt is the worker's initial task (textarea). - Footer: right-aligned single primary
Spawn worker(blue) with a⌘↵keycap,
disabled until valid.
Motion
- Approach: minimal-functional. The one expressive exception: a status dot/spinner
pulse on active/working sessions (opacity breathe) so "alive" is glanceable. Never
animate text or layout. - Easing: enter
ease-out, exitease-in, moveease-in-out. - Duration: micro 80ms · short 160ms · medium 240ms · status pulse 1.8s loop ·
modal enter ~150ms fade+zoom-95.
Implementation notes
- The renderer (
frontend/src/renderer/styles.css) currently uses Inter and a
grayscale-blue theme. Migrate to this system: drop the Interfont-family, adopt the
system stack, and replace the token values with the emdash neutral ramp + blue accent above. - Keep tokens as CSS custom properties under
:root(dark) and:root[data-theme="light"]. - A faithful HTML reference of all of the above (both views + topbar + spawn modal,
light/dark) is saved under
~/.gstack/projects/aoagents-agent-orchestrator/designs/design-system-20260609/.
Decisions Log
| Date | Decision | Rationale |
|---|---|---|
| 2026-06-09 | Match emdash's visual language exactly | User direction; emdash is the demonstrated reference for this app's UI. |
| 2026-06-09 | System font, not a custom typeface (e.g. Geist) | emdash uses the system stack; fidelity + native feel + zero font payload chosen over brand type. |
| 2026-06-09 | Refined blue accent, not emdash's jade green | User's explicit pick; blue for primary/active/focus, terminal stays green. |
| 2026-06-09 | Single global Orchestrator anchor, orchestrator-first default view | The one real difference from emdash; orchestrator is the human-facing coordinator you delegate to. |
| 2026-06-09 | Name-only worker rows | User direction; status/branch/diff live in panes + topbar, not the row. |
| 2026-06-09 | Removed Library from the rail footer | User direction; footer is Search + Settings only. |
| 2026-06-09 | Topbar right = PR/CI pill + view toggles + ⋯ menu (worker) | Surfaces the actionable PR/CI state from the daemon; emdash/PostHog Code precedent. |
| 2026-06-09 | Spawn modal mirrors emdash's Create Task | Consistency with the reference; mapped to ao spawn params. |