Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e2e86bd
feat(frontend): redesign Electron app UI with a clean Lucide-icon system
ashish921998 Jun 8, 2026
6bed183
Refine the Electron workspace and task composer flow
ashish921998 Jun 9, 2026
42301b1
Merge remote-tracking branch 'origin/main' into feat/electron-ui-rede…
ashish921998 Jun 9, 2026
43183ac
fix(frontend): apply pre-landing review findings to the Electron rede…
ashish921998 Jun 9, 2026
b8dd1ba
chore(frontend): drop the Playwright e2e harness
ashish921998 Jun 9, 2026
9a05e64
fix(frontend): read QueryClient from context so optimistic updates apply
ashish921998 Jun 9, 2026
46b793c
chore(frontend): add terminal-WebGL, electron-updater, and Playwright…
ashish921998 Jun 9, 2026
8ae0c7e
feat(frontend): stream per-session PTY over the terminal mux WebSocket
ashish921998 Jun 9, 2026
d826444
feat(frontend): live server-state sync via the CDC SSE event transport
ashish921998 Jun 9, 2026
b753a99
feat(frontend): resizable workbench layout + activate shadcn components
ashish921998 Jun 9, 2026
c4ce2ab
feat(frontend): electron-updater auto-update scaffold + release CI
ashish921998 Jun 9, 2026
41633db
test(frontend): restore Playwright e2e for the React workbench
ashish921998 Jun 9, 2026
1dbff55
fix(frontend): don't crash the terminal when WebGL is unavailable
ashish921998 Jun 9, 2026
37ca55d
fix(frontend): design QA fixes from running the app + task-create err…
ashish921998 Jun 9, 2026
24e3bb4
fix(frontend): don't let a stale daemon exit orphan a newer process
ashish921998 Jun 9, 2026
f28227a
feat(frontend): emdash-matched workbench redesign + DESIGN.md
ashish921998 Jun 9, 2026
4c32d4b
feat(frontend): wire the workbench to live daemon/server state + revi…
ashish921998 Jun 9, 2026
8380da0
chore: add pre-commit hooks (husky + lint-staged + prettier/gofmt)
ashish921998 Jun 9, 2026
1b4167d
style: baseline Prettier format of frontend (non-WIP files)
ashish921998 Jun 9, 2026
6d29a2e
fix: unbreak the packaged app's daemon access (fetch duplex, app:// o…
ashish921998 Jun 9, 2026
c01cf7a
Redesign the Electron UI shell for daemon-backed workspaces
ashish921998 Jun 9, 2026
958c985
Merge origin/main into feat/electron-ui-redesign
ashish921998 Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .claude/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "renderer",
"runtimeExecutable": "npm",
"runtimeArgs": ["--prefix", "frontend", "run", "dev:web", "--", "--host", "127.0.0.1", "--port", "5181"],
"port": 5181
}
]
}
45 changes: 45 additions & 0 deletions .github/workflows/frontend-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Desktop release

# Builds and publishes the Electron desktop app, generating the update feed
# (latest-mac.yml / latest.yml) that electron-updater reads. Triggered by a
# `desktop-v*` tag or manually.
#
# ⚠️ Until macOS code signing + notarization secrets are configured (see
# frontend/docs/desktop-release.md), published builds are UNSIGNED and will NOT
# auto-update on macOS. The workflow still produces installable artifacts.

on:
push:
tags:
- "desktop-v*"
workflow_dispatch:

jobs:
release:
runs-on: macos-latest
permissions:
contents: write # electron-builder publishes a GitHub Release
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: frontend/package-lock.json
- run: npm ci
- name: Build and publish
run: npm run dist -- --publish always
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# macOS signing + notarization — required for auto-update to run on
# macOS. Add these as repository secrets and remove `mac.identity: null`
# from frontend/package.json. Until then the env vars are unset and the
# build is unsigned.
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
node_modules/
.pnpm/
dist/
frontend/dist-electron/
frontend/release/
frontend/test-results/
out/
build/
*.log
Expand Down Expand Up @@ -46,6 +49,7 @@ session-events.jsonl.*
# OS
.DS_Store
Thumbs.db
.gstack/

# Personal local overrides (not for the team)
.envrc.local
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
4 changes: 4 additions & 0 deletions .lintstagedrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"*.{ts,tsx,js,jsx,mjs,cjs,json,css,scss,html,md,yml,yaml}": "prettier --write",
"*.go": "gofmt -w"
}
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# CLAUDE.md

Read and follow [`AGENTS.md`](AGENTS.md) for repository layout, commands, coding conventions, and hard rules.

## Design System

Always read [`DESIGN.md`](DESIGN.md) before making any visual or UI decision.
All fonts, colors, spacing, layout, the orchestrator-led flow, the topbar, and the
spawn modal are defined there. The UI matches **emdash** with one main orchestrator and
a refined-blue accent. Do not deviate without explicit user approval. In QA/review,
flag any renderer code that doesn't match DESIGN.md.
226 changes: 226 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
# 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-consultation` on 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/`). The `ao` CLI 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 by `Kind` (`KindOrchestrator` vs 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](https://github.com/generalaction/emdash) (primary, visual +
structural), [PostHog Code](https://github.com/PostHog/code) (secondary). Tokens
below were extracted from emdash's `src/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 are `font-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_failed` red, review/`changes_requested` amber, plain `pr_open` muted.
- **Otherwise** → a filled dot: `needs_input` amber (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`; buttons `px-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:**
1. **Orchestrator anchor** — pinned, single, visually distinct (blue 2px left bar,
`--bg-2` fill, hub/`waypoints` icon, name "Orchestrator", a `5 agents · 2 need you`
mono summary). This is ReverbCode's one addition over emdash. Default landing view.
2. `PROJECTS` eyebrow label + a `+`.
3. 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 the `PROJECTS` header `+`, which registers a repo).
4. **Footer:** `Search ⌘K`, `Settings ⌘,`. (No Library.)
5. **Account** row pinned at the very bottom.
- **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-2` fill + 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:** `sm` 4px (scrollbar) · `md` 6px (buttons, inputs, toggles) ·
`lg` 8px (rows, cards, panels) · `xl` 12px (modals) · `full` (badges/pills/dots).
- **Icons:** **lucide** only. No emoji.

### Topbar

- **Left (both):** `project / session` breadcrumb + pin; for the orchestrator, a hub icon
- `Orchestrator`.
- **Right — worker session:** a **PR/CI status pill** that is the action
(`PR #156 · mergeable` green / `CI failed` red / `review requested` amber /
`Open PR` when none) → **Changes / Files / Terminal** view toggles → **⋯ session menu**
(rename, restart, kill, claim PR — the `ao 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, `bg` canvas, `ring-1` at 10% fg,
fade + zoom-95 enter.
- **Header:** eyebrow mono-uppercase title `New worker` + `×` close.
- **Body** (`gap` 15–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
control `Branch · Issue · Pull Request` revealing 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`, exit `ease-in`, move `ease-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 Inter `font-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. |
Loading
Loading