From 478a7f18e9ac9f8ee41a535f39feca1416ea4ed4 Mon Sep 17 00:00:00 2001 From: Lukas May Date: Mon, 2 Mar 2026 18:13:17 +0900 Subject: [PATCH] docs: Add v2 wireframes and theme specification 14 files in docs/wireframes/v2/ addressing 13 UX gaps from v1: - Theme spec with indigo brand, status tokens, terminal/diff tokens, dark mode, Geist typography, 6px radius, layered shadows - Wireframes for all pages with loading/error/empty states - Shared component specs (SaveIndicator, EmptyState, ErrorState, CommandPalette, ThemeToggle) --- docs/wireframes/v2/README.md | 51 ++ docs/wireframes/v2/agents.md | 289 +++++++++ docs/wireframes/v2/app-layout.md | 178 ++++++ docs/wireframes/v2/content-tab.md | 234 +++++++ docs/wireframes/v2/dialogs.md | 358 +++++++++++ docs/wireframes/v2/execution-tab.md | 309 ++++++++++ docs/wireframes/v2/inbox.md | 260 ++++++++ docs/wireframes/v2/initiative-detail.md | 209 +++++++ docs/wireframes/v2/initiatives-list.md | 267 ++++++++ docs/wireframes/v2/plan-tab.md | 284 +++++++++ docs/wireframes/v2/review-tab.md | 205 +++++++ docs/wireframes/v2/settings.md | 186 ++++++ docs/wireframes/v2/shared-components.md | 440 ++++++++++++++ docs/wireframes/v2/theme.md | 769 ++++++++++++++++++++++++ 14 files changed, 4039 insertions(+) create mode 100644 docs/wireframes/v2/README.md create mode 100644 docs/wireframes/v2/agents.md create mode 100644 docs/wireframes/v2/app-layout.md create mode 100644 docs/wireframes/v2/content-tab.md create mode 100644 docs/wireframes/v2/dialogs.md create mode 100644 docs/wireframes/v2/execution-tab.md create mode 100644 docs/wireframes/v2/inbox.md create mode 100644 docs/wireframes/v2/initiative-detail.md create mode 100644 docs/wireframes/v2/initiatives-list.md create mode 100644 docs/wireframes/v2/plan-tab.md create mode 100644 docs/wireframes/v2/review-tab.md create mode 100644 docs/wireframes/v2/settings.md create mode 100644 docs/wireframes/v2/shared-components.md create mode 100644 docs/wireframes/v2/theme.md diff --git a/docs/wireframes/v2/README.md b/docs/wireframes/v2/README.md new file mode 100644 index 0000000..264b052 --- /dev/null +++ b/docs/wireframes/v2/README.md @@ -0,0 +1,51 @@ +# v2 Wireframes + +Redesigned wireframes for Codewalk District frontend — addressing 13 UX gaps identified in v1. + +## Changes from v1 + +| # | Issue | Resolution | Wireframe | +|---|-------|------------|-----------| +| 1 | No brand identity — achromatic palette | Indigo brand color (#6366F1), differentiated tokens | [theme.md](theme.md) | +| 2 | No dark mode toggle | 3-state toggle (Sun/Monitor/Moon) in header | [app-layout.md](app-layout.md), [theme.md](theme.md) | +| 3 | Missing loading/error states across pages | Skeleton loaders, AlertCircle + retry patterns | [shared-components.md](shared-components.md) | +| 4 | No save feedback on editors | SaveIndicator component (Saving.../Saved/Failed) | [content-tab.md](content-tab.md), [plan-tab.md](plan-tab.md) | +| 5 | No breadcrumb navigation in content | Breadcrumb row above page title | [content-tab.md](content-tab.md) | +| 6 | Inconsistent empty states | Standardized EmptyState component | [shared-components.md](shared-components.md) | +| 7 | No search/filter on agents page | Provider, mode, and name search filters | [agents.md](agents.md) | +| 8 | Missing agent context (what task/initiative) | Context row on agent cards | [agents.md](agents.md) | +| 9 | No batch dispatch in execution | "Queue All Pending" button + dispatch feedback | [execution-tab.md](execution-tab.md) | +| 10 | Confusing inbox button layout | Simplified 2-button form (Send + Stop) | [inbox.md](inbox.md) | +| 11 | No initiative metadata on cards | Branch badge + project names + timestamp | [initiatives-list.md](initiatives-list.md) | +| 12 | Missing review tab loading/error | Spinner + AlertCircle states | [review-tab.md](review-tab.md) | +| 13 | No command palette | Cmd+K palette for cross-entity search | [shared-components.md](shared-components.md) | + +## Files + +### Theme + +| File | Description | +|------|-------------| +| [theme.md](theme.md) | Complete design system specification | + +### Pages + +| File | Route | Description | +|------|-------|-------------| +| [app-layout.md](app-layout.md) | `*` | Global shell: consolidated header, theme toggle, health dot | +| [initiatives-list.md](initiatives-list.md) | `/initiatives` | Cards with metadata, sort, search | +| [initiative-detail.md](initiative-detail.md) | `/initiatives/$id` | Two-row header, tab badges | +| [content-tab.md](content-tab.md) | `?tab=content` | Breadcrumbs, save indicator, placeholder | +| [plan-tab.md](plan-tab.md) | `?tab=plan` | Split empty states, save indicator | +| [execution-tab.md](execution-tab.md) | `?tab=execution` | Queue All, dispatch feedback, overflow | +| [review-tab.md](review-tab.md) | `?tab=review` | Loading/error states, jump-to-unresolved | +| [agents.md](agents.md) | `/agents` | Multi-filter, search, context row | +| [inbox.md](inbox.md) | `/inbox` | Simplified question form | +| [settings.md](settings.md) | `/settings/*` | Error states on Projects | + +### Components + +| File | Description | +|------|-------------| +| [dialogs.md](dialogs.md) | Updated dialog wireframes | +| [shared-components.md](shared-components.md) | Reusable component specs | diff --git a/docs/wireframes/v2/agents.md b/docs/wireframes/v2/agents.md new file mode 100644 index 0000000..f9b94c4 --- /dev/null +++ b/docs/wireframes/v2/agents.md @@ -0,0 +1,289 @@ +# Agents Page (v2) + +### Route: `/agents` +### Source: `packages/web/src/routes/agents.tsx` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Provider filter | None | Dropdown: All / Claude / Codex / Gemini / etc. | +| Mode filter | None | Dropdown: All / discuss / plan / detail / execute / verify / etc. | +| Search | None | Text input filtering agent name | +| Agent card context | Name + status + provider + mode + time only | Added context row: "Task: ..." or "Initiative: ..." with link | +| Loading state | 5 skeleton cards (basic) | 5 skeleton cards with shimmer animation | +| Error state | AlertCircle + retry (exists) | Same (unchanged) | +| Empty (filtered) | "No agents match this filter" | "No matching agents" + `[Clear filters]` link | +| Empty (zero agents) | None | Centered empty state with description | + +--- + +## Default State (with agents) + +``` ++=============================================================================+ +| [CW] Initiatives [*Agents*] *3 Inbox (2) Settings [cmd-k] [sun] * | ++=============================================================================+ +| | +| Agents (7) [ Refresh ] | +| [search___________] [All providers v] [All modes v] | +| | +| Agent List 320px | Agent Output | +| -------------------------+-------------------------------------------------| +| +----------------------+ | | +| | * blue-fox-7 [...] | | Session 1 | +| | claude . execute | | > Analyzing codebase structure... | +| | Task: PKCE flow | | > Found 12 files matching pattern | +| | 2m ago | | > Creating src/auth/pkce.ts | +| +----------------------+ | > Writing test file... | +| | | +| +----------------------+ | | +| | - red-elk-3 [...] | | | +| | claude . plan | | | +| | Init: Auth Overhaul | | | +| | 1h ago | | | +| +----------------------+ | | +| | | +| +----------------------+ | | +| | ? green-bear-1 [...] | | | +| | codex . detail | | | +| | Task: DB schema | | | +| | 5m ago | | | +| +----------------------+ | | ++=============================================================================+ +``` + +--- + +## Search + Filter Controls + +``` + [search___________] [All providers v] [All modes v] +``` + +### Search input +- Placeholder: "Search agents..." +- Client-side filter on `agent.name` (case-insensitive substring match) +- Debounced 150ms +- Clear button `[x]` appears when non-empty + +### Provider filter dropdown `[All providers v]` + +``` + [All providers v] + +-----------------+ + | All | + | claude | + | codex | + | gemini | + | ... | + +-----------------+ +``` + +Populated dynamically from `listProviderNames` tRPC query. Only shows providers +that have at least one registered account. + +### Mode filter dropdown `[All modes v]` + +``` + [All modes v] + +-----------------+ + | All | + | discuss | + | plan | + | detail | + | execute | + | verify | + | refine | + | research | + | merge | + | review | + +-----------------+ +``` + +Static list of all task category values. + +--- + +## Agent Card Anatomy (v2) + +### Running agent with task context + +``` ++------------------------------+ +| * blue-fox-7 [...] | +| claude . execute | +| Task: PKCE flow | <-- clickable link to task/initiative +| 2m ago | ++------------------------------+ +``` + +### Running agent with initiative context (no specific task) + +``` ++------------------------------+ +| * blue-fox-7 [...] | +| claude . plan | +| Init: Auth Overhaul | <-- clickable link to initiative +| 2m ago | ++------------------------------+ +``` + +### Waiting for input + +``` ++------------------------------+ +| ? green-bear-1 [...] | +| codex . detail | +| Task: DB schema | +| 5m ago | +| Answer questions -> | <-- link to /inbox ++------------------------------+ +``` + +### Exited + +``` ++------------------------------+ +| - red-elk-3 [...] | +| claude . plan | +| Init: Auth Overhaul | +| 1h ago | ++------------------------------+ +``` + +### Context row rules +- If agent has a `taskId`: show "Task: " linking to the initiative detail (Execution tab) +- Else if agent has an `initiativeId`: show "Init: " linking to the initiative detail +- Else: omit the context row entirely +- Context text: `text-xs text-muted-foreground`, clickable portion gets `hover:underline cursor-pointer` + +### Status dot legend +- `*` green/active = `running` +- `?` orange/waiting = `waiting_for_input` +- `-` grey/exited = `stopped`, `crashed`, `idle`, `completed` + +--- + +## Loading State (5 skeleton cards with shimmer) + +``` ++=============================================================================+ +| | +| Agents [ Refresh ] | +| [search___________] [All providers v] [All modes v] | +| | +| Agent List 320px | Agent Output | +| -------------------------+-------------------------------------------------| +| +----------------------+ | | +| | [.] ░░░░░░░░░░░░ | | | +| | ░░░░░ . ░░░░░░ | | | +| | ░░░░░░░░░░░░░ | | | +| +----------------------+ | Select an agent | +| +----------------------+ | to view output | +| | [.] ░░░░░░░░░░░░ | | | +| | ░░░░░ . ░░░░░░ | | | +| | ░░░░░░░░░░░░░ | | | +| +----------------------+ | | +| +----------------------+ | | +| | [.] ░░░░░░░░░░░░ | | | +| | ░░░░░ . ░░░░░░ | | | +| | ░░░░░░░░░░░░░ | | | +| +----------------------+ | | +| +----------------------+ | | +| | [.] ░░░░░░░░░░░░ | | | +| | ░░░░░ . ░░░░░░ | | | +| | ░░░░░░░░░░░░░ | | | +| +----------------------+ | | +| +----------------------+ | | +| | [.] ░░░░░░░░░░░░ | | | +| | ░░░░░ . ░░░░░░ | | | +| | ░░░░░░░░░░░░░ | | | +| +----------------------+ | | ++=============================================================================+ +``` + +Skeleton cards mirror card anatomy: status dot placeholder, name line, provider/mode line, +context line. Shimmer animation sweeps left-to-right on a 1.5s loop. + +--- + +## Error State + +``` ++=============================================================================+ +| | +| Agents [ Refresh ] | +| | +| | +| [AlertCircle] | +| Failed to load agents | +| | +| [ Retry ] | +| | ++=============================================================================+ +``` + +- `[AlertCircle]` icon: `h-8 w-8 text-destructive` +- Error message: `text-sm text-destructive` +- `[ Retry ]` button: `variant="outline" size="sm"`, calls `agentsQuery.refetch()` + +--- + +## Empty State -- No Agents + +``` ++=============================================================================+ +| | +| Agents [ Refresh ] | +| [search___________] [All providers v] [All modes v] | +| | +| +-----------------------------------------------------------------------+ | +| | | | +| | [bot] | | +| | No agents yet | | +| | Agents appear here when dispatched from initiatives. | | +| | | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +- `[bot]` icon: `Bot` from Lucide, `h-8 w-8 text-muted-foreground` +- Shown when the `listAgents` query returns an empty array and no filters are active + +--- + +## Empty State -- Filters Active, No Match + +``` ++=============================================================================+ +| | +| Agents [ Refresh ] | +| [codex_____________] [claude v] [execute v] | +| | +| +-----------------------------------------------------------------------+ | +| | | | +| | No matching agents | | +| | Try a different search or filter. | | +| | | | +| | [Clear filters] | | +| | | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +- "Clear filters" is a text link (`text-primary hover:underline cursor-pointer`) +- Resets search input to empty, provider filter to "All", mode filter to "All" + +--- + +## Source + +- `packages/web/src/routes/agents.tsx` +- `packages/web/src/components/AgentOutputViewer.tsx` +- `packages/web/src/components/AgentActions.tsx` +- `packages/web/src/components/StatusDot.tsx` diff --git a/docs/wireframes/v2/app-layout.md b/docs/wireframes/v2/app-layout.md new file mode 100644 index 0000000..9f47e6e --- /dev/null +++ b/docs/wireframes/v2/app-layout.md @@ -0,0 +1,178 @@ +# App Layout (v2) + +### Route: `*` (all pages) +### Source: `packages/web/src/routes/__root.tsx` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Header height | 56px + nav row | Single 48px row | +| Nav layout | Wordmark on row 1, tabs on row 2 | Logo + tabs + utilities all on one row | +| Badges | None | Live agent count on Agents, unresolved count on Inbox | +| Theme toggle | None | 3-state Sun/Monitor/Moon | +| Command palette | None | Cmd+K trigger in header | +| Health indicator | None | Status dot (green/yellow/red) | + +--- + +## Default State + +``` ++=============================================================================+ +| [CW] Initiatives Agents *3 Inbox (2) Settings [cmd-k] [sun] * | ++=============================================================================+ +| | +| | +| max-w-7xl padded content | +| | +| | +| | +| | +| | +| | +| | ++=============================================================================+ +``` + +## Active Tab Highlighted + +``` ++=============================================================================+ +| [CW] [*Initiatives*] Agents *3 Inbox (2) Settings [cmd-k] [sun] * | ++=============================================================================+ +| | +| | +| | ++=============================================================================+ +``` + +Active tab gets `bg-muted rounded-md` treatment. Text is bold. + +## Zero Badges (no running agents, no pending conversations) + +``` ++=============================================================================+ +| [CW] Initiatives Agents Inbox Settings [cmd-k] [sun] * | ++=============================================================================+ +| | +| | +| | ++=============================================================================+ +``` + +Badges are completely hidden when count is 0 — no empty `()` or `*0`. + +## Health Dot States + +``` + * ← green: WebSocket connected, last heartbeat < 5s ago + ? ← yellow: connected but heartbeat > 5s (slow/degraded) + - ← red: WebSocket disconnected or server unreachable +``` + +Tooltip on hover shows details: +``` + * Connected (42ms) + ? Slow response (2.3s) + - Disconnected — retrying... +``` + +## Collapsed Mobile View (not yet implemented) + +``` ++=============================================================================+ +| [CW] [cmd-k] [sun] * [☰] | ++=============================================================================+ +| | +| | +| | ++=============================================================================+ +``` + +Note: Mobile hamburger menu is not implemented. This is a placeholder for +future work. At `< 768px`, nav tabs collapse into a hamburger `[☰]` dropdown. + +## 404 Page + +``` ++=============================================================================+ +| [CW] Initiatives Agents Inbox Settings [cmd-k] [sun] * | ++=============================================================================+ +| | +| | +| [AlertCircle] | +| Page not found | +| | +| [ Back to Initiatives ] | +| | +| | ++=============================================================================+ +``` + +--- + +## 48px Header Anatomy + +``` ++-----------------------------------------------------------------------------+ +| LEFT CLUSTER SPACER RIGHT CLUSTER | +| [CW] [Nav] [Nav *N] [Nav (N)] [Nav] -------- [cmd-k] [◐] [●] | ++-----------------------------------------------------------------------------+ + ↑ ↑ ↑ ↑ + 16px icon Cmd+K btn Theme Health +``` + +### Left cluster (nav) +- `[CW]` — 24x24 logo icon, links to `/initiatives` +- Nav tabs: `Initiatives`, `Agents`, `Inbox`, `Settings` +- Each tab is an `` with `gap-1.5` between label and optional badge +- Active tab: `bg-muted text-foreground font-medium rounded-md px-3 py-1.5` +- Inactive tab: `text-muted-foreground hover:text-foreground px-3 py-1.5` + +### Badge rendering +- `Agents *N` — small filled circle + count, only when N > 0, green tint +- `Inbox (N)` — parenthetical count, only when N > 0, yellow tint for unresolved + +### Right cluster (utilities) +- `[cmd-k]` — `Cmd+K` on Mac, `Ctrl+K` on Windows. Shows shortcut text as label. + Clicking opens the CommandPalette overlay (see shared-components.md). +- `[◐]` — ThemeToggle, 3-state segmented control (see shared-components.md) +- `[●]` — Health status dot, 8px circle with color + +### Spacing +- Left cluster: `gap-1` between items +- Between clusters: `flex-1` spacer pushes them apart +- Right cluster: `gap-3` between items + +--- + +## Responsive Notes + +| Breakpoint | Behavior | +|------------|----------| +| `>= 1024px` | Full header as shown | +| `768px - 1023px` | Cmd+K label shortens to icon only `[search]` | +| `< 768px` | Nav tabs collapse into hamburger (future) | + +Content area is always `max-w-7xl mx-auto px-4 sm:px-6 lg:px-8`. + +--- + +## Global Components (rendered in root layout) + +- `` (sonner) — bottom-right toast notifications +- `` — wraps the page outlet +- `` — overlay, triggered by Cmd+K or header button + +--- + +## Source + +- `packages/web/src/routes/__root.tsx` +- `packages/web/src/layouts/AppLayout.tsx` +- `packages/web/src/components/ThemeToggle.tsx` (proposed) +- `packages/web/src/components/CommandPalette.tsx` (proposed) +- `packages/web/src/components/HealthDot.tsx` (proposed) diff --git a/docs/wireframes/v2/content-tab.md b/docs/wireframes/v2/content-tab.md new file mode 100644 index 0000000..8ee27b4 --- /dev/null +++ b/docs/wireframes/v2/content-tab.md @@ -0,0 +1,234 @@ +# Content Tab (v2) + +### Route: `/initiatives/$id` (Content tab) +### Source: `packages/web/src/components/editor/ContentTab.tsx`, `packages/web/src/components/editor/` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Breadcrumbs | None | `Root > Parent > Current` row above title, clickable | +| Save indicator | Bare "Saving..." text, top-right, no success/fail states | `` component: spinner, checkmark (fade), error + retry | +| Empty root page | Blank editor, no guidance | Tiptap placeholder: "Start writing... use / for commands" | +| Root page creation | Only via tree context menu hover `[+]` on nodes | Additional `[+]` button in sidebar header for root-level pages | +| Deep breadcrumbs | N/A | Ellipsis collapse: `Root > ... > Parent > Current` | + +--- + +## Default State (with content) + +``` ++=============================================================================+ +| Pages [+] | Root > Architecture > Current Page | +| --------------------------+ [✓] Saved | +| ├── Architecture | ------------------------------------------------| +| │ ├── Overview | | +| │ └── Decisions | # Authentication Architecture | +| ├── Requirements | | +| └── API Design | This document outlines the OAuth 2.0 | +| | implementation strategy for... | +| | | +| 192px | editor area (flex-1) | +| | | ++=============================================================================+ +``` + +- `[+]` in sidebar header creates a new root-level child page +- Breadcrumb segments are clickable links that call `onNavigate(pageId)` +- `[✓] Saved` indicator fades after 2s via CSS opacity transition +- Tree nodes still show hover `[+]` for nested child creation (unchanged from v1) + +--- + +## Empty Root Page (placeholder) + +``` ++=============================================================================+ +| Pages [+] | Root | +| --------------------------+ | +| (empty tree) | ------------------------------------------------| +| | | +| | My Initiative Name | +| | ^^^^^^^^^^^^^^^^^^^^^^ | +| | 3xl bold title input | +| | | +| | Start writing... use / for commands | +| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | +| | muted placeholder text (text-muted-foreground) | +| | | +| | | +| 192px | editor area (flex-1) | +| | | ++=============================================================================+ +``` + +- Placeholder uses `@tiptap/extension-placeholder` with `emptyEditorClass` +- Placeholder text disappears on focus/first keystroke +- Root page title maps to `initiativeName` (edits update initiative, not page) + +--- + +## Save Indicator States + +Three-state component positioned top-right of editor area, inline with breadcrumb row. + +### Saving (spinner) + +``` + Root > Architecture > Current Page [spinner] Saving... +``` + +- `Loader2` icon with `animate-spin`, `text-muted-foreground` +- Shown while `isSaving` or `updateInitiativeMutation.isPending` + +### Saved (checkmark, fading) + +``` + Root > Architecture > Current Page [✓] Saved +``` + +- Green checkmark icon, `text-green-500` +- Fades to `opacity-0` after 2s via `transition-opacity duration-1000` +- Resets to full opacity on next save cycle + +### Failed (error with retry) + +``` + Root > Architecture > Current Page [!] Failed [retry] +``` + +- `AlertCircle` icon, `text-destructive` +- `[retry]` is a ghost button that triggers manual save flush +- Persists until retry succeeds or new edit triggers auto-save + +--- + +## With Refine Panel Open (3-column layout) + +``` ++=============================================================================+ +| Pages [+] | Root > Current Page [✓] Saved | Refine with Agent | +| ---------------+ | | +| ├── Arch | # Auth Architecture | [spinner] Architect | +| │ ├── Ovw | | is refining... | +| │ └── Dec | This document outlines the | | +| ├── Req | OAuth 2.0 implementation... | | +| └── API | | | +| | | | +| 192px | editor (flex-1) | 320px | +| | | | ++=============================================================================+ +``` + +- Refine panel sits above editor in v1 source (not beside it) +- v2 proposal: move to right column when viewport >= 1280px +- Below 1280px, refine panel stays above editor (current v1 behavior) +- Panel width: 320px fixed, editor remains `flex-1` + +--- + +## Breadcrumb with Deep Nesting (ellipsis collapse) + +### Depth <= 3: show all segments + +``` + Root > Architecture > Decisions +``` + +### Depth > 3: collapse middle with ellipsis + +``` + Root > ... > API Endpoints > Rate Limiting +``` + +- `Root` always visible (first segment) +- `...` is a dropdown trigger showing collapsed intermediate segments +- Last two segments always visible +- Clicking `...` opens a popover with the full path as clickable links: + +``` + +---------------------------+ + | System Design | + | Backend Architecture | + | API Layer | + +---------------------------+ +``` + +--- + +## Page Tree Sidebar Detail + +``` ++--------------------------+ +| Pages [+] | +| | +| ├── Architecture [+] | <-- [+] on hover: create child +| │ ├── Overview | <-- active page: bg-accent +| │ └── Decisions | +| ├── Requirements [+] | +| └── API Design [+] | +| | ++--------------------------+ + 192px, border-right +``` + +- Header `[+]` always visible (new in v2) -- creates page under root +- Node `[+]` on hover only (unchanged from v1) +- FileText icon + truncated title per node +- Active page: `bg-accent rounded-md` highlight +- Indentation: 12px per depth level +- Click navigates; keyboard nav not yet implemented + +--- + +## Loading State + +``` ++=============================================================================+ +| +----------+ | +------------------------------------------+ | +| | ░░░░░░ | skeleton | | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | | +| | ░░░░░░ | h-64 w-48 | | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | | +| | ░░░░░░ | | | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | | +| +----------+ | +------------------------------------------+ | +| 192px | flex-1 | ++=============================================================================+ +``` + +- Shown while `rootPageQuery.isLoading` +- Skeleton shimmer components from `@/components/Skeleton` + +--- + +## Error State + +``` ++=============================================================================+ +| | +| [AlertCircle] | +| Failed to load editor: | +| | +| Make sure the backend server is running | +| with the latest code. | +| | +| [ Retry ] | +| | ++=============================================================================+ +``` + +- `text-destructive` for error message +- `text-muted-foreground` for help text +- Retry button calls `rootPageQuery.refetch()` + +--- + +## Source + +- `packages/web/src/components/editor/ContentTab.tsx` +- `packages/web/src/components/editor/PageTree.tsx` +- `packages/web/src/components/editor/TiptapEditor.tsx` +- `packages/web/src/components/editor/RefineAgentPanel.tsx` +- `packages/web/src/components/editor/DeleteSubpageDialog.tsx` +- `packages/web/src/components/editor/PageTitleContext.tsx` +- `packages/web/src/hooks/useAutoSave.ts` diff --git a/docs/wireframes/v2/dialogs.md b/docs/wireframes/v2/dialogs.md new file mode 100644 index 0000000..6bbd62e --- /dev/null +++ b/docs/wireframes/v2/dialogs.md @@ -0,0 +1,358 @@ +# Dialogs (v2) + +All modal/dialog overlays used throughout the application. v2 adds consistent +loading and error states to all mutation-backed dialogs. + +### Source: `packages/web/src/components/CreateInitiativeDialog.tsx`, `packages/web/src/components/RegisterProjectDialog.tsx`, `packages/web/src/components/RefineSpawnDialog.tsx`, `packages/web/src/components/TaskDetailModal.tsx`, `packages/web/src/components/editor/DeleteSubpageDialog.tsx` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Create Initiative: Branch field | Present (optional text input) | REMOVED (auto-generated on first execution dispatch) | +| All dialogs: submit loading | Some had it, inconsistent | All show `[spinner] ing...` on submit button | +| All dialogs: submit error | Some had it, inconsistent | All show red error text below buttons | + +--- + +## Create Initiative Dialog + +**Trigger**: "New Initiative" button on `/initiatives` + +### Default state + +``` ++----------------------------------------------------------+ +| Create Initiative [x] | +| | +| Name * | +| [Auth System Overhaul___________________________] | +| | +| Execution Mode | +| [ Review v ] | +| | +| Projects | +| +----------------------------------------------------+ | +| | [x] backend github.com/org/backend-api | | +| | [ ] frontend github.com/org/frontend-app | | +| | [ ] shared github.com/org/shared-lib | | +| | + Register new project | | +| +----------------------------------------------------+ | +| | +| [ Cancel ] [ Create ] | ++----------------------------------------------------------+ +``` + +Note: Branch field has been removed. Branches are now auto-generated +(`cw/`) on first execution task dispatch. See auto-branch +system in architecture docs. + +### Submitting state + +``` ++----------------------------------------------------------+ +| Create Initiative [x] | +| | +| Name * | +| [Auth System Overhaul___________________________] | +| | +| Execution Mode | +| [ Review v ] | +| | +| Projects | +| +----------------------------------------------------+ | +| | [x] backend github.com/org/backend-api | | +| | [ ] frontend github.com/org/frontend-app | | +| +----------------------------------------------------+ | +| | +| [ Cancel ] [ [spinner] Creating... ] | ++----------------------------------------------------------+ +``` + +- `[ Create ]` button replaced with `[ [spinner] Creating... ]` +- Button is `disabled` during mutation +- `[ Cancel ]` remains enabled but closing the dialog is prevented + +### Error state + +``` ++----------------------------------------------------------+ +| Create Initiative [x] | +| | +| Name * | +| [Auth System Overhaul___________________________] | +| | +| Execution Mode | +| [ Review v ] | +| | +| Projects | +| +----------------------------------------------------+ | +| | [x] backend github.com/org/backend-api | | +| +----------------------------------------------------+ | +| | +| Failed to create initiative. | +| [ Cancel ] [ Create ] | ++----------------------------------------------------------+ +``` + +- Error text: `text-sm text-destructive`, centered above the footer buttons +- Cleared on next submit attempt + +**Fields**: +- Name (required text input, `autoFocus`) +- Execution Mode (select: Review per Phase / YOLO) +- Projects (ProjectPicker: checkbox list + register link) + +**Source**: `packages/web/src/components/CreateInitiativeDialog.tsx` + +--- + +## Register Project Dialog + +**Trigger**: "Register Project" button on `/settings/projects` or "+ Register new project" in ProjectPicker + +### Default state + +``` ++----------------------------------------------------------+ +| Register Project [x] | +| | +| Project Name * | +| [backend-api_______________________________________] | +| | +| Repository URL * | +| [https://github.com/org/backend-api________________] | +| | +| Default Branch | +| [main______________________________________________] | +| | +| [ Cancel ] [ Register ] | ++----------------------------------------------------------+ +``` + +### Submitting state + +``` +| [ Cancel ] [ [spinner] Registering... ] | +``` + +### Error state + +``` +| Failed to register project. | +| [ Cancel ] [ Register ] | +``` + +**Fields**: +- Project Name (required, unique) +- Repository URL (required, unique, git repo URL) +- Default Branch (text input, defaults to "main") + +**Source**: `packages/web/src/components/RegisterProjectDialog.tsx` + +--- + +## Refine Spawn Dialog + +**Trigger**: "Refine with Agent" button in Content tab, or "Retry" button after agent crash + +### Default state + +``` ++----------------------------------------------------------+ +| Refine Content [x] | +| | +| Instructions (optional) | +| +----------------------------------------------------+ | +| | Focus on the authentication flow section. | | +| | Add more detail about the OAuth providers we | | +| | need to support. | | +| | | | +| +----------------------------------------------------+ | +| | +| [ Cancel ] [ Start ] | ++----------------------------------------------------------+ +``` + +### Submitting state + +``` +| [ Cancel ] [ [spinner] Starting... ] | +``` + +### Error state + +``` +| Failed to start agent. | +| [ Cancel ] [ Start ] | +``` + +**Fields**: +- Instructions (optional textarea, free-form guidance for the agent, 3 rows) + +**Source**: `packages/web/src/components/RefineSpawnDialog.tsx` + +--- + +## Task Detail Modal + +**Trigger**: Click any task row in Plan tab or Execution tab + +### Default state + +``` ++----------------------------------------------------------+ +| Implement PKCE Flow [x] | +| | +| +------------------------+-------------------------+ | +| | Status [IN_PROGRESS]| Priority normal | | +| | Phase 2. OAuth Flow| Type execute | | +| | Agent blue-fox-7 | | +| +------------------------+-------------------------+ | +| | +| Description | +| Implement the OAuth 2.0 PKCE extension for public | +| clients. Generate code verifier/challenge pair, | +| include in authorization request, and validate | +| during token exchange. | +| | +| Dependencies | +| * Set up OAuth routes [DONE] | +| | +| Blocks | +| * GitHub provider adapter [BLOCKED] | +| * Microsoft provider adapter [PENDING] | +| | +| [ Queue Task ] [ Stop Task ] | ++----------------------------------------------------------+ +``` + +### Queue in progress + +``` +| [ [spinner] Queuing... ] [ Stop Task ] | +``` + +### Stop in progress + +``` +| [ Queue Task ] [ [spinner] Stopping... ] | +``` + +### Error state + +``` +| Failed to queue task. Try again. | +| [ Queue Task ] [ Stop Task ] | +``` + +**Fields** (read-only): +- 2-column metadata grid: Status (with badge), Priority, Phase, Type, Agent +- Description text +- Dependencies list (with StatusDot per dependency) +- Blocks list (dependents with StatusDot) + +**Actions**: +- `[ Queue Task ]` -- `variant="outline" size="sm"`, disabled if already running/queued or dependencies incomplete +- `[ Stop Task ]` -- `variant="destructive" size="sm"`, disabled if not running + +**Source**: `packages/web/src/components/TaskDetailModal.tsx` + +--- + +## Delete Subpage Dialog + +**Trigger**: Auto-triggered when a page link is deleted in the Tiptap editor + +### Default state + +``` ++----------------------------------------------------------+ +| Delete Subpage? [x] | +| | +| You removed a link to "Token Rotation". Do you | +| want to delete the subpage as well? | +| | +| [ Keep Subpage ] [ Delete ] | ++----------------------------------------------------------+ +``` + +### Delete in progress + +``` +| [ Keep Subpage ] [ [spinner] Deleting... ] | +``` + +### Error state + +``` +| Failed to delete subpage. | +| [ Keep Subpage ] [ Delete ] | +``` + +**Fields**: None (confirmation dialog only) + +**Actions**: +- `[ Keep Subpage ]` -- `variant="outline"`, closes dialog without deleting +- `[ Delete ]` -- `variant="destructive"`, deletes the subpage and its content + +**Source**: `packages/web/src/components/editor/DeleteSubpageDialog.tsx` + +--- + +## Confirmation Dialogs (window.confirm) + +Used for destructive actions throughout the app. All support **Shift+click to bypass**. + +| Action | Trigger Location | Message | +|--------|-----------------|---------| +| Delete initiative | Initiative card `[...]` menu | "Delete this initiative?" | +| Archive initiative | Initiative card `[...]` menu | "Archive this initiative?" | +| Delete phase | Phase detail `[...]` menu | "Delete this phase?" | +| Delete task | Task row `[x]` button | "Delete this task?" | +| Delete project | Project card `[trash]` button | "Delete this project?" | +| Delete agent | Agent actions `[...]` menu | "Delete this agent?" | +| Stop agent (from inbox) | Inbox detail `[ Stop Agent ]` button | "Stop this agent? It will not be able to resume." | + +Note: All `window.confirm()` dialogs are synchronous browser dialogs. They do not +receive the v2 loading/error treatment since the mutation fires only after confirmation. + +--- + +## Shared Patterns Across All Dialogs + +### Submit button loading state + +``` + v1: [ Create ] + v2: [ [spinner] Creating... ] +``` + +- `[spinner]` is `Loader2` from Lucide with `animate-spin h-4 w-4 mr-2` +- Button text changes to present participle: "Create" -> "Creating...", "Register" -> "Registering...", "Start" -> "Starting...", "Delete" -> "Deleting..." +- Button is `disabled` during mutation + +### Error text placement + +``` ++----------------------------------------------------------+ +| ...form fields... | +| | +| | +| [ Cancel ] [ Submit ] | ++----------------------------------------------------------+ +``` + +- Error text: `text-sm text-destructive` +- Positioned between form content and footer buttons +- Cleared automatically when the user initiates a new submit + +--- + +## Source (shared) + +- `packages/web/src/components/ProjectPicker.tsx` +- `packages/web/src/components/ui/dialog.tsx` +- `packages/web/src/components/ui/button.tsx` diff --git a/docs/wireframes/v2/execution-tab.md b/docs/wireframes/v2/execution-tab.md new file mode 100644 index 0000000..5a97e36 --- /dev/null +++ b/docs/wireframes/v2/execution-tab.md @@ -0,0 +1,309 @@ +# Execution Tab (v2) + +### Route: `/initiatives/$id` (Execution tab) +### Source: `packages/web/src/components/pipeline/PipelineTab.tsx`, `packages/web/src/components/pipeline/` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Batch dispatch | None -- individual task/phase queue buttons only | "Queue All Pending" button at top of execution view | +| Dispatch feedback | Instant status change, no transition | 3-frame animation: `[play]` -> `[spinner] Queuing...` -> `[QUEUED]` + toast | +| Task overflow | All tasks rendered regardless of count | First 6 tasks shown, then "Show N more" expandable link | +| Status legend | None | Legend row at bottom: `* Active ● Done ? Pending ! Error ○ Blocked` | +| Empty state | Shared with Plan tab empty state | Dedicated message: "No tasks to execute. Create tasks in the Plan tab." | +| All done state | No special treatment | Success message with green checkmark | + +--- + +## Default State (pipeline with phases) + +``` ++=============================================================================+ +| Execution [ Queue All Pending ] | +| | +| Phase: OAuth Implementation | +| +-------------------+ +-------------------+ +-------------------+ | +| | Level 0 | | Level 1 | | Level 2 | | +| | | | | | | | +| | * Set up routes |---->| * PKCE flow |---->| Token refresh | | +| | [DONE] | | [RUNNING] | | [PENDING] [play]| | +| | | | | | | | +| | * DB schema |---->| Provider adaptr | | | | +| | [DONE] | | [BLOCKED] | | | | +| +-------------------+ +-------------------+ +-------------------+ | +| | +| Phase: Token Management | +| +-------------------+ | +| | Level 0 | | +| | | | +| | Rotation logic | | +| | [PENDING] [play]| | +| | Expiry handling | | +| | [PENDING] [play]| | +| | + Show 4 more | | +| +-------------------+ | +| | +| ● Done * Active ? Pending ! Error ○ Blocked | ++=============================================================================+ +``` + +- `[ Queue All Pending ]` -- dispatches all tasks with `status === 'pending'` that have no unresolved dependencies +- Pipeline columns computed via `groupPhasesByDependencyLevel()` topological sort +- Horizontal scroll when columns exceed viewport width +- `---->` connector arrows between dependency levels +- `[play]` button visible on hover for `pending` tasks only (not `blocked` or `running`) +- Status legend row pinned to bottom of execution view + +--- + +## Queue All Pending Button + +``` + +-------------------------+ + | [play] Queue All Pending| default state + +-------------------------+ + + +-------------------------+ + | [spinner] Queuing... | while mutation is in-flight + +-------------------------+ + + +-------------------------+ + | [check] 5 tasks queued | success (reverts to default after 2s) + +-------------------------+ +``` + +- Outline variant button, positioned top-right of execution header +- Disabled when no eligible pending tasks exist +- Calls `trpc.queueAllPending.useMutation({ initiativeId })` +- Toast notification: "N tasks queued" + +--- + +## Dispatch Feedback (3-frame animation) + +Individual task dispatch transitions through three visual states: + +### Frame 1: Before (idle pending) + +``` + +-------------------------------------------+ + | Token refresh [PENDING] [play] | + +-------------------------------------------+ +``` + +- `[play]` icon visible on hover (`opacity-0 group-hover:opacity-100`) +- `Clock` icon for `[PENDING]` status, `text-muted-foreground` + +### Frame 2: During (queuing) + +``` + +-------------------------------------------+ + | Token refresh [spinner] Queuing... | + +-------------------------------------------+ +``` + +- `[play]` replaced by `Loader2` with `animate-spin` +- "Queuing..." text replaces status badge +- Row background: subtle `bg-primary/5` pulse + +### Frame 3: After (queued) + +``` + +-------------------------------------------+ + | Token refresh [QUEUED] | + +-------------------------------------------+ +``` + +- Status badge transitions to `[QUEUED]` (blue tint) +- Toast: "Task queued" (via sonner) +- `[play]` button no longer rendered (task is no longer `pending`) + +--- + +## Task Overflow in Phase Cards + +### Collapsed (default when > 6 tasks) + +``` + +-----------------------------------------+ + | * Phase: Token Management [PENDING] | + |-----------------------------------------| + | Rotation logic [PENDING] | + | Expiry handling [PENDING] | + | Refresh tokens [PENDING] | + | Revocation logic [PENDING] | + | Audit logging [PENDING] | + | Rate limiting [PENDING] | + | + Show 4 more | + +-----------------------------------------+ +``` + +- First 6 tasks always visible +- `+ Show N more` is a text button at bottom of card, `text-primary text-xs` +- Count dynamically reflects hidden task count + +### Expanded (after clicking "Show N more") + +``` + +-----------------------------------------+ + | * Phase: Token Management [PENDING] | + |-----------------------------------------| + | Rotation logic [PENDING] | + | Expiry handling [PENDING] | + | Refresh tokens [PENDING] | + | Revocation logic [PENDING] | + | Audit logging [PENDING] | + | Rate limiting [PENDING] | + | Key rotation [PENDING] | + | Token introspection [PENDING] | + | Blacklist service [PENDING] | + | Migration script [PENDING] | + | - Show less | + +-----------------------------------------+ +``` + +- All tasks rendered +- `- Show less` collapses back to 6 +- Expansion state is per-phase-card, resets on tab switch + +--- + +## Pipeline Phase Group Detail + +``` ++-----------------------------------------+ +| * Phase 2: OAuth Flow [READY] [play] | StatusDot + name + badge + queue btn +|-----------------------------------------| +| [play] Set up OAuth routes [DONE] | [play] on hover for pending only +| [play] Implement PKCE flow [RUNNING] | running tasks: Loader2 icon, no play +| [ .. ] Google provider [BLOCKED] | blocked tasks: Ban icon, no play +| [play] Microsoft provider [PENDING] | ++-----------------------------------------+ +``` + +- Phase header `[play]` queues all eligible tasks in the phase +- Task `[play]` queues individual task +- Clicking a task row opens `` (via `ExecutionContext`) +- Task sort order: `sortByPriorityAndQueueTime()` from shared package + +--- + +## Connector Arrows + +``` + +--------+ +--------+ + | Phase | ------[>]-----> | Phase | + +--------+ +--------+ + + horizontal line + triangle arrow between columns +``` + +- SVG arrows drawn between dependency levels +- Arrow color: `text-border` (muted, follows theme) +- Multiple phases in same level stack vertically + +--- + +## Empty State (no tasks to execute) + +``` ++=============================================================================+ +| Execution | +| | +| +---------------------------------------------------------------------+ | +| | | | +| | [list-checks icon] | | +| | No tasks to execute | | +| | | | +| | Create tasks in the Plan tab to start execution. | | +| | | | +| | [ Go to Plan Tab ] | | +| | | | +| +---------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +- Centered `EmptyState` card +- `[list-checks icon]` -- Lucide `ListChecks`, 48px, `text-muted-foreground` +- `[ Go to Plan Tab ]` -- outline button, navigates to `?tab=plan` +- Shown when: `phases.length === 0 && !phasesLoading` + +--- + +## All Tasks Done (completion state) + +``` ++=============================================================================+ +| Execution | +| | +| +---------------------------------------------------------------------+ | +| | | | +| | [check-circle icon] | | +| | All tasks completed | | +| | | | +| | Every task across all phases has been executed. | | +| | | | +| | [ View in Review Tab ] | | +| | | | +| +---------------------------------------------------------------------+ | +| | +| ● Done * Active ? Pending ! Error ○ Blocked | ++=============================================================================+ +``` + +- `[check-circle icon]` -- Lucide `CheckCircle2`, 48px, `text-green-500` +- "All tasks completed" -- `text-lg font-medium` +- `[ View in Review Tab ]` -- outline button, navigates to `?tab=review` +- Shown when: all tasks across all phases have `status === 'completed'` +- Legend row still visible at bottom + +--- + +## Status Legend Row + +``` + ● Done * Active ? Pending ! Error ○ Blocked +``` + +- Fixed at bottom of execution view (below pipeline graph) +- Each item: colored dot/symbol + label + - `●` -- `text-green-500` (CheckCircle2) + - `*` -- `text-blue-500` (Loader2, no spin in legend) + - `?` -- `text-muted-foreground` (Clock) + - `!` -- `text-red-500` (AlertTriangle) + - `○` -- `text-red-500/50` (Ban) +- `text-xs text-muted-foreground`, `flex gap-4 justify-center` + +--- + +## Loading State + +``` ++=============================================================================+ +| Execution | +| | +| [spinner] Loading pipeline... | +| | ++=============================================================================+ +``` + +- `Loader2` with `animate-spin` + "Loading pipeline..." +- `text-muted-foreground`, centered +- Shown while `phasesLoading || tasksQuery.isLoading` + +--- + +## Source + +- `packages/web/src/components/pipeline/PipelineTab.tsx` +- `packages/web/src/components/pipeline/PipelineGraph.tsx` +- `packages/web/src/components/pipeline/PipelineStageColumn.tsx` +- `packages/web/src/components/pipeline/PipelinePhaseGroup.tsx` +- `packages/web/src/components/pipeline/PipelineTaskCard.tsx` +- `packages/web/src/components/execution/ExecutionContext.tsx` +- `packages/web/src/components/execution/TaskModal.tsx` +- `packages/web/src/components/execution/PlanSection.tsx` diff --git a/docs/wireframes/v2/inbox.md b/docs/wireframes/v2/inbox.md new file mode 100644 index 0000000..77031c7 --- /dev/null +++ b/docs/wireframes/v2/inbox.md @@ -0,0 +1,260 @@ +# Inbox Page (v2) + +### Route: `/inbox` +### Source: `packages/web/src/routes/inbox.tsx` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| List error state | None | `[AlertCircle]` + "Failed to load conversations" + `[Retry]` | +| Detail error state | None | `[AlertCircle]` + "Failed to load conversation" + `[Retry]` | +| Action buttons | 3 buttons: Cancel, Dismiss, Send Answers | 2 buttons: `[ Stop Agent ]` (destructive) + `[ Send Answers ]` (primary) | +| Submit feedback | None | Button shows `[spinner] Sending...` during mutation | +| Submit error | Toast only | Inline red text below buttons: "Failed to send. Try again." | +| Empty state | Basic text | Centered empty state with description | + +--- + +## Default State (with conversations) + +``` ++=============================================================================+ +| [CW] Initiatives Agents *3 [*Inbox*] (2) Settings [cmd-k] [sun] * | ++=============================================================================+ +| | +| Inbox (2) [ Refresh ] | +| | +| Conversations | Question from blue-fox-7 | +| --------------------------+ 400px | +| +------------------------+| | +| | ? blue-fox-7 2m || "Should I use PKCE or implicit flow | +| | Auth System Overhaul || for the browser client? The spec | +| +------------------------+|| recommends PKCE but the existing | +| | ? green-bear-1 5m || code uses implicit." | +| | API Redesign || | +| +------------------------+| Your Answer | +| | +--------------------------------------------+| +| | | Use PKCE. We're deprecating implicit || +| | | flow per OAuth 2.1 security BCP. || +| | | || +| | +--------------------------------------------+| +| | | +| | [ Stop Agent ] [ Send Answers ] | ++=============================================================================+ +``` + +--- + +## Conversation Card (in left list) + +### Waiting for response + +``` ++-------------------------------+ +| ? blue-fox-7 2m | <-- status dot (orange) + name + time +| Auth System Overhaul | <-- initiative name ++-------------------------------+ +``` + +### Answered / completed + +``` ++-------------------------------+ +| - green-bear-1 1h | <-- status dot (grey) + name + time +| API Redesign | <-- initiative name ++-------------------------------+ +``` + +- Orange `?` dot = `waiting_for_input` +- Grey `-` dot = answered or completed +- Click selects conversation and shows detail panel +- Selected card gets `bg-muted` highlight + +--- + +## Detail Panel (400px right) + +### Header + +``` ++----------------------------------------------+ +| Question from blue-fox-7 | +| Auth System Overhaul . 2 minutes ago | ++----------------------------------------------+ +``` + +### Question + answer form + +``` ++----------------------------------------------+ +| "Should I use PKCE or implicit flow | +| for the browser client? The spec | +| recommends PKCE but the existing | +| code uses implicit." | +| | +| Your Answer | +| +------------------------------------------+| +| | Use PKCE. We're deprecating implicit || +| | flow per OAuth 2.1 security BCP. || +| | || +| +------------------------------------------+| +| | +| [ Stop Agent ] [ Send Answers ] | ++----------------------------------------------+ +``` + +### Button anatomy + +- `[ Stop Agent ]` -- `variant="destructive"`, stops the asking agent via `stopAgent` mutation. + Replaces the v1 Cancel + Dismiss buttons. Confirmation via `window.confirm()`; + Shift+click bypasses confirmation. +- `[ Send Answers ]` -- `variant="default"` (primary), sends the response via `resumeAgent` mutation. + Disabled until answer textarea is non-empty. + +--- + +## Submit In Progress + +``` +| [ Stop Agent ] [ [spinner] Sending... ] | +``` + +- `[ Send Answers ]` button shows a `Loader2` spinner + "Sending..." text +- Button is `disabled` during mutation +- `[ Stop Agent ]` is also disabled during mutation to prevent conflicting actions + +--- + +## Submit Error + +``` +| | +| [ Stop Agent ] [ Send Answers ] | +| | +| Failed to send. Try again. | ++----------------------------------------------+ +``` + +- Error text: `text-sm text-destructive`, right-aligned below the buttons +- Shown when `resumeAgentMutation.isError` is true +- Cleared on next submit attempt + +--- + +## Empty Inbox + +``` ++=============================================================================+ +| | +| Inbox (0) [ Refresh ] | +| | +| +-----------------------------------------------------------------------+ | +| | | | +| | [inbox] | | +| | No pending questions | | +| | Agents will ask questions here when they need input. | | +| | | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +- `[inbox]` icon: `Inbox` from Lucide, `h-8 w-8 text-muted-foreground` +- Title: `text-sm font-medium` +- Description: `text-sm text-muted-foreground` + +--- + +## List Loading State (skeleton rows) + +``` ++=============================================================================+ +| | +| Inbox [ Refresh ] | +| | +| Conversations | | +| --------------------------+ 400px | +| +------------------------+| | +| | [.] ░░░░░░░░░░ ░░░ || | +| | ░░░░░░░░░░░░░░░░░ || Select an agent to view details | +| +------------------------+| | +| +------------------------+| | +| | [.] ░░░░░░░░░░ ░░░ || | +| | ░░░░░░░░░░░░░░░░░ || | +| +------------------------+| | +| +------------------------+| | +| | [.] ░░░░░░░░░░ ░░░ || | +| | ░░░░░░░░░░░░░░░░░ || | +| +------------------------+| | +| +------------------------+| | +| | [.] ░░░░░░░░░░ ░░░ || | +| | ░░░░░░░░░░░░░░░░░ || | +| +------------------------+| | ++=============================================================================+ +``` + +Skeleton cards mirror conversation card anatomy: status dot placeholder, name + time line, +initiative name line. Shimmer animation sweeps left-to-right on a 1.5s loop. + +--- + +## List Error State + +``` ++=============================================================================+ +| | +| Inbox [ Refresh ] | +| | +| Conversations | | +| --------------------------+ 400px | +| | | +| [AlertCircle] | Select an agent to view details | +| Failed to load | | +| conversations | | +| | | +| [ Retry ] | | +| | | ++=============================================================================+ +``` + +- `[AlertCircle]` icon: `h-6 w-6 text-destructive` +- Error message: `text-sm text-destructive` +- `[ Retry ]` button: `variant="outline" size="sm"`, calls `agentsQuery.refetch()` + `messagesQuery.refetch()` + +--- + +## Detail Error State + +``` ++=============================================================================+ +| | +| Inbox (2) [ Refresh ] | +| | +| Conversations | | +| --------------------------+ 400px | +| +------------------------+| | +| | ? blue-fox-7 2m || [AlertCircle] | +| | Auth System Overhaul || Failed to load conversation | +| +------------------------+| | +| | ? green-bear-1 5m || [ Retry ] | +| | API Redesign || | +| +------------------------+| | +| | | ++=============================================================================+ +``` + +- Shown when `questionsQuery.isError` is true for the selected agent +- `[ Retry ]` button calls `questionsQuery.refetch()` + +--- + +## Source + +- `packages/web/src/routes/inbox.tsx` +- `packages/web/src/components/InboxList.tsx` +- `packages/web/src/components/InboxDetailPanel.tsx` +- `packages/web/src/components/MessageCard.tsx` +- `packages/web/src/components/QuestionForm.tsx` diff --git a/docs/wireframes/v2/initiative-detail.md b/docs/wireframes/v2/initiative-detail.md new file mode 100644 index 0000000..2072e20 --- /dev/null +++ b/docs/wireframes/v2/initiative-detail.md @@ -0,0 +1,209 @@ +# Initiative Detail (v2) + +### Route: `/initiatives/$id` +### Source: `packages/web/src/routes/initiatives/$initiativeId.tsx` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Header layout | Single row: back + name + all badges | Two rows: Row 1 = back + name, Row 2 = badges | +| Tab badges | None | Execution shows "3/7", Review shows "(2)" with yellow tint | +| Keyboard hints | None | Tooltip on tab hover: `1` Content, `2` Plan, `3` Execution, `4` Review | +| Branch editing | Inline edit on click | Unchanged (still inline) | +| Project editing | Popover checklist | Unchanged (still popover) | + +--- + +## Default State + +``` ++=============================================================================+ +| [CW] [*Initiatives*] Agents *3 Inbox (2) Settings [cmd-k] [sun] * | ++=============================================================================+ +| | +| [<] Auth System Overhaul | +| [ACTIVE] [REVIEW] [git] cw/auth-overhaul [P] backend, frontend | +| | +| Content Plan Execution 3/7 Review (2) | +| ----------------------------------------------------------------------- | +| | +| | +| < tab content area > | +| | +| | +| | ++=============================================================================+ +``` + +--- + +## Two-Row Header Anatomy + +``` + Row 1: [<] Initiative Name + Row 2: [STATUS] [MODE] [git] branch [P] project1, project2 +``` + +### Row 1 +- `[<]` — Back chevron, navigates to `/initiatives` +- Initiative name — `text-2xl font-bold` +- Row 1 has no badges — clean title line + +### Row 2 +- Indented to align under the initiative name (not under the back chevron) +- `[STATUS]` — StatusBadge: `[ACTIVE]` green, `[DONE]` grey, `[ARCHIVED]` dim +- `[MODE]` — ExecutionMode: `[YOLO]` amber (clickable toggle), `[REVIEW]` blue (clickable toggle) +- `[git] cw/` — GitBranch icon + branch name. Hidden if no branch yet. +- `[P] project1, project2` — Folder icon + project names. Hidden if none linked. + +### No Branch Yet + +``` + [<] Mobile App Redesign + [ACTIVE] [REVIEW] [P] frontend +``` + +Branch badge simply absent. No placeholder or "add branch" button — +branch is auto-generated on first execution task dispatch. + +### No Projects Yet + +``` + [<] Scratch Initiative + [ACTIVE] [YOLO] [git] cw/scratch [+ Add projects] +``` + +`[+ Add projects]` link opens project picker popover. + +### Branch Editing (click branch badge) + +``` + [<] Auth System Overhaul + [ACTIVE] [REVIEW] [git] [cw/auth-overhaul______] [v] [x] [P] backend +``` + +Inline text input replaces branch badge. `[v]` saves, `[x]` cancels. + +### Project Editing (click pencil icon on project badges) + +``` + [<] Auth System Overhaul + [ACTIVE] [REVIEW] [git] cw/auth-overhaul + +----------------------------------+ + | [x] backend github.com/... | + | [ ] frontend github.com/... | + | [ ] shared github.com/... | + | + Register new project | + +----------------------------------+ + [ Save ] [ Cancel ] +``` + +--- + +## Tab Bar + +``` + Content Plan Execution 3/7 Review (2) + ----------------------------------------------------------------------- + ^^^^^^^^^^^^^^^^^^ + active tab (border-b-2 border-primary) +``` + +### Tab badge rendering + +| Tab | Badge | When | +|-----|-------|------| +| Content | None | Always | +| Plan | None | Always | +| Execution | `3/7` | Fraction of completed/total tasks. Hidden if 0 total tasks. | +| Review | `(2)` | Pending proposal count. Yellow tint. Hidden if 0 pending. | + +### Active tab indicator +- `border-b-2 border-primary` underline on active tab +- `font-medium text-foreground` for active tab text +- `text-muted-foreground` for inactive tab text + +### Tab with zero badges + +``` + Content Plan Execution Review + ----------------------------------------------------------------------- +``` + +When Execution has 0 tasks and Review has 0 pending proposals, badges are +completely hidden. No "0/0" or "(0)". + +### Hover state with keyboard hint (tooltip) + +``` + Content Plan Execution 3/7 Review (2) + ----------------------------------------------------------------------- + ^ + +-------------+ + | 3 Execution | + +-------------+ +``` + +Tooltip shows the keyboard shortcut number + tab name. +Pressing `1`/`2`/`3`/`4` switches tabs when no input is focused. + +Keyboard shortcuts: +- `1` — Content +- `2` — Plan +- `3` — Execution +- `4` — Review + +--- + +## Loading State + +``` ++=============================================================================+ +| | +| [<] ░░░░░░░░░░░░░░░░░░░░░ | +| ░░░░░░░ ░░░░░░░ ░░░░░░░░░░░░░░░ ░░░░░░░░░░░░ | +| | +| ░░░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░░░ | +| ----------------------------------------------------------------------- | +| +-------------------------------------------------------------------+ | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | | +| +-------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +Skeleton matches two-row header + tab bar + content area. + +--- + +## Error State + +``` ++=============================================================================+ +| | +| [<] | +| | +| [AlertCircle] | +| Error loading initiative | +| Could not fetch initiative data. | +| | +| [ Back to Initiatives ] | +| | ++=============================================================================+ +``` + +Back chevron still functional. Error body uses `` component +(see shared-components.md) but with navigation button instead of retry. + +--- + +## Source + +- `packages/web/src/routes/initiatives/$initiativeId.tsx` +- `packages/web/src/components/InitiativeHeader.tsx` +- `packages/web/src/components/InitiativeTabBar.tsx` (proposed) diff --git a/docs/wireframes/v2/initiatives-list.md b/docs/wireframes/v2/initiatives-list.md new file mode 100644 index 0000000..72701f5 --- /dev/null +++ b/docs/wireframes/v2/initiatives-list.md @@ -0,0 +1,267 @@ +# Initiatives List (v2) + +### Route: `/initiatives` +### Source: `packages/web/src/routes/initiatives.tsx` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Card metadata | Name + status + progress only | Added branch, projects, relative timestamp | +| Sort | None | Dropdown: Newest / Oldest / Name A-Z / Most Progress | +| Search | None | Client-side text filter on initiative name | +| Create dialog | Had Branch field | Branch removed (auto-generated on first execution dispatch) | +| Loading state | 3 skeleton cards | 5 skeleton cards with shimmer animation | +| Error state | None | AlertCircle + message + Retry button | +| Empty (filtered) | None | "No matching initiatives" + Clear filters link | +| Empty (zero) | Basic text + CTA | Centered EmptyState component | +| Card actions | View + Spawn dropdown + `...` menu | Same (unchanged) | + +--- + +## Default State (with cards) + +``` ++=============================================================================+ +| [CW] [*Initiatives*] Agents *3 Inbox (2) Settings [cmd-k] [sun] * | ++=============================================================================+ +| | +| Initiatives [ + New Initiative ] | +| | +| [search____________________] [All v] [Newest v] | +| | +| +-----------------------------------------------------------------------+ | +| | Auth System Overhaul [ACTIVE] [REVIEW] | | +| | [git] cw/auth-overhaul [P] backend, frontend . 2h ago | | +| | [=========> ] 3/7 tasks [...] | | +| +-----------------------------------------------------------------------+ | +| | +| +-----------------------------------------------------------------------+ | +| | API Redesign [DONE] [YOLO] | | +| | [git] cw/api-redesign [P] backend . 1d ago | | +| | [==========================] 12/12 tasks [...] | | +| +-----------------------------------------------------------------------+ | +| | +| +-----------------------------------------------------------------------+ | +| | Mobile App Redesign [ACTIVE] [REVIEW] | | +| | [P] frontend . 5m ago | | +| | [==> ] 1/5 tasks [...] | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +Note: Third card has no branch yet (auto-generated on first execution dispatch), +so `[git]` badge is absent. Only `[P]` project badges and timestamp shown. + +## Card Anatomy + +``` ++-------------------------------------------------------------------------+ +| Initiative Name [STATUS] [MODE] | +| [git] cw/ [P] project1, project2 . | +| [==========> ] N/M tasks [...] | ++-------------------------------------------------------------------------+ +``` + +### Metadata row elements +- `[git] cw/` — GitBranch icon + branch name. Hidden if no branch yet. +- `[P] project1, project2` — Folder icon + comma-separated project names. + Hidden if no projects linked. +- `. ` — Dot separator + relative timestamp (e.g., "2h ago", "1d ago"). + Based on `updatedAt`. + +### Status badge colors +- `[ACTIVE]` — green +- `[DONE]` — grey +- `[ARCHIVED]` — dim grey + +### Mode badge colors +- `[YOLO]` — yellow/amber +- `[REVIEW]` — blue + +### Progress bar +- Blue fill when in progress +- Green fill when 100% (all tasks done) +- Grey track for remaining + +### More menu `[...]` + +``` + [...] + +----------------+ + | Discuss | + | Plan | + | ----------- | + | Archive | + | Delete | ← window.confirm; Shift+click bypasses + +----------------+ +``` + +--- + +## Search + Filter + Sort Controls + +``` + [search____________________] [All v] [Newest v] +``` + +### Search input +- Placeholder: "Search initiatives..." +- Client-side filter on `initiative.name` (case-insensitive substring match) +- Debounced 150ms +- Clear button `[x]` appears when non-empty + +### Status filter dropdown `[All v]` + +``` + [ All v] + +---------------+ + | All | + | Active | + | Completed | + | Archived | + +---------------+ +``` + +### Sort dropdown `[Newest v]` + +``` + [ Newest v] + +---------------+ + | Newest | ← updatedAt DESC (default) + | Oldest | ← updatedAt ASC + | Name A-Z | ← name ASC + | Most Progress| ← completion % DESC + +---------------+ +``` + +--- + +## Loading State (5 skeleton cards with shimmer) + +``` ++=============================================================================+ +| | +| Initiatives [ + New Initiative ] | +| | +| [search____________________] [All v] [Newest v] | +| | +| +-----------------------------------------------------------------------+ | +| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | | +| | ░░░░░░░░░░░░░░ | | +| +-----------------------------------------------------------------------+ | +| +-----------------------------------------------------------------------+ | +| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | | +| | ░░░░░░░░░░░░░░ | | +| +-----------------------------------------------------------------------+ | +| +-----------------------------------------------------------------------+ | +| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | | +| | ░░░░░░░░░░░░░░ | | +| +-----------------------------------------------------------------------+ | +| +-----------------------------------------------------------------------+ | +| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | | +| | ░░░░░░░░░░░░░░ | | +| +-----------------------------------------------------------------------+ | +| +-----------------------------------------------------------------------+ | +| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | | +| | ░░░░░░░░░░░░░░ | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +Skeleton cards mirror card anatomy: title line, metadata line, progress line. +Shimmer animation sweeps left-to-right on a 1.5s loop. + +--- + +## Error State + +``` ++=============================================================================+ +| | +| Initiatives [ + New Initiative ] | +| | +| +-----------------------------------------------------------------------+ | +| | | | +| | [AlertCircle] | | +| | Failed to load initiatives | | +| | | | +| | [ Retry ] | | +| | | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +Uses `` component (see shared-components.md). +Retry calls `refetch()` on the tRPC query. + +--- + +## Empty State — No Initiatives + +``` ++=============================================================================+ +| | +| Initiatives [ + New Initiative ] | +| | +| +-----------------------------------------------------------------------+ | +| | | | +| | [inbox] | | +| | No initiatives yet | | +| | Create an initiative to get started. | | +| | | | +| | [ + New Initiative ] | | +| | | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +Uses `` component (see shared-components.md). +CTA button opens the CreateInitiativeDialog. + +--- + +## Empty State — No Filter Match + +``` ++=============================================================================+ +| | +| Initiatives [ + New Initiative ] | +| | +| [auth_____________________] [Active v] [Newest v] | +| | +| +-----------------------------------------------------------------------+ | +| | | | +| | No matching initiatives | | +| | Try a different search or filter. | | +| | | | +| | [Clear filters] | | +| | | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +"Clear filters" is a text link (not a button). Resets search input to empty, +status filter to "All", sort to "Newest". + +--- + +## Source + +- `packages/web/src/routes/initiatives/index.tsx` +- `packages/web/src/components/InitiativeList.tsx` +- `packages/web/src/components/InitiativeCard.tsx` +- `packages/web/src/components/EmptyState.tsx` (proposed) +- `packages/web/src/components/ErrorState.tsx` (proposed) diff --git a/docs/wireframes/v2/plan-tab.md b/docs/wireframes/v2/plan-tab.md new file mode 100644 index 0000000..ca5a83c --- /dev/null +++ b/docs/wireframes/v2/plan-tab.md @@ -0,0 +1,284 @@ +# Plan Tab (v2) + +### Route: `/initiatives/$id` (Plan tab) +### Source: `packages/web/src/components/ExecutionTab.tsx`, `packages/web/src/components/execution/` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Save indicator | None on phase description editor | `` component (same as Content tab) | +| Empty state (no agent) | Single "No phases yet" with both spinner and buttons shown contextually | Split: dedicated "No phases yet" empty state with action buttons | +| Empty state (agent running) | Spinner + "Planning phases..." inline with buttons | Dedicated "Planning in progress..." state, NO action buttons | +| Pending review | Banner only on individual phase detail | Additional top-level banner when proposals exist initiative-wide | + +--- + +## Default State (phases exist, one selected) + +``` ++=============================================================================+ +| Phases [+] | Phase: OAuth Implementation | +| --------------------------+ [✓] Saved | +| +------------------------+| ------------------------------------------------| +| | 1. Auth Setup || Description: | +| | [DONE] 3/3 || Implement OAuth 2.0 authorization code flow | +| +------------------------+| with PKCE extension for public clients. | +| +------------------------+| | +| |*2. OAuth Flow *|| Support Google, GitHub, and Microsoft as | +| | [READY] 0 tasks || identity providers with configurable scopes. | +| | dep: 1 || | +| +------------------------+| Dependencies | +| +------------------------+| [+ Add dependency v] | +| | 3. UI Components || * Phase 1: Auth Setup [DONE] [x] | +| | [PENDING] 5 tasks || | +| | dep: 1, 2 || Tasks (0/5) [+ Add Task] | +| +------------------------+| ├── Set up OAuth routes * [DONE] | +| | ├── Implement PKCE flow * [RUNNING] | +| | │ agent: blue-fox-7 | +| 260px | ├── Google provider [PENDING] [x] | +| | ├── GitHub provider [BLOCKED] [x] | +| | │ blocked by: Google provider | +| | └── Microsoft provider [PENDING] [x] | +| | | ++=============================================================================+ +``` + +- Phase sidebar: 260px, `border-right` +- Selected phase: left blue border + `bg-accent` background +- `[+]` in header triggers `handleStartAdd` (inline input at bottom of list) +- `[✓] Saved` shows after phase description auto-save completes +- Task `[x]` delete buttons appear on hover; Shift+click bypasses confirm dialog + +--- + +## Phase Sidebar Cards + +``` ++------------------------+ +| 1. Auth Setup | Phase number + name (truncated) +| [DONE] 3/3 | StatusBadge (small) + "complete/total tasks" ++------------------------+ ++------------------------+ +|*2. OAuth Flow *| * = selected (border-l-2 border-primary bg-accent) +| [READY] 0 tasks | "0 tasks" when no tasks decomposed yet +| dep: 1 | dependency indicator ++------------------------+ ++------------------------+ +| 3. UI Components | +| [PENDING] 5 tasks | +| dep: 1, 2 | multiple dependency shorthand ++------------------------+ +``` + +--- + +## Phase Actions (sidebar header) + +``` + Phases [Detailing (2)] [+] [Detail All] + ^^^^^^^^^^^^^^ ^^^^^^^^^^^ + spinner + count sparkles icon + when detail agents disabled when no + are active eligible phases +``` + +- `[Detailing (N)]` spinner badge shows when N detail agents are running +- `[Detail All]` spawns detail agents for all phases with 0 tasks +- `[+]` opens inline input for new phase name + +--- + +## Save Indicator on Phase Description + +Same `` component as Content tab, positioned top-right of phase detail header. + +### Saving + +``` + Phase: OAuth Implementation [spinner] Saving... +``` + +### Saved + +``` + Phase: OAuth Implementation [✓] Saved +``` + +### Failed + +``` + Phase: OAuth Implementation [!] Failed [retry] +``` + +--- + +## Empty State -- No Phases (no agent running) + +``` ++=============================================================================+ +| Phases | | +| --------------------------+ | +| (empty) | +------------------------------------------+ | +| | | | | +| | | [layers icon] | | +| | | No phases yet | | +| | | | | +| | | Add a phase to start planning, | | +| | | or let an agent plan for you. | | +| | | | | +| | | [sparkles Plan with Agent] [+ Add Phase] | +| | | | | +| | +------------------------------------------+ | +| | | ++=============================================================================+ +``` + +- Centered `EmptyState` card in the detail panel area +- `[layers icon]` -- Lucide `Layers` icon, `text-muted-foreground`, 48px +- Two action buttons side by side: + - `[sparkles Plan with Agent]` -- primary variant, calls `planSpawn.spawn()` + - `[+ Add Phase]` -- outline variant, calls `onAddPhase()` for inline input +- Only shown when: `phasesLoaded && phases.length === 0 && !isPlanRunning` + +--- + +## Empty State -- Planning in Progress (agent running) + +``` ++=============================================================================+ +| Phases | | +| --------------------------+ | +| (empty) | +------------------------------------------+ | +| | | | | +| | | [spinner] | | +| | | Planning in progress... | | +| | | | | +| | | Agent is analyzing the initiative | | +| | | and creating phases. | | +| | | | | +| | +------------------------------------------+ | +| | | ++=============================================================================+ +``` + +- `[spinner]` -- `Loader2` with `animate-spin`, 32px, `text-primary` +- "Planning in progress..." -- `text-base font-medium` +- Subtext -- `text-sm text-muted-foreground` +- NO action buttons -- prevents user from adding phases while agent works +- Shown when: `phasesLoaded && phases.length === 0 && isPlanRunning` + +--- + +## Pending Review Banner (top-level) + +``` ++=============================================================================+ +| +---------------------------------------------------------------------+ | +| | [sparkles] Agent has proposals ready for review [Go to Review ->] | | +| +---------------------------------------------------------------------+ | +| Phases [+] | Phase: OAuth Implementation | +| --------------------------+ [✓] Saved | +| ... | ... | ++=============================================================================+ +``` + +- Full-width banner above the sidebar+detail grid +- `[sparkles]` -- Lucide `Sparkles` icon, `text-amber-500` +- Background: `bg-amber-50 dark:bg-amber-950/20 border border-amber-200 dark:border-amber-800` +- `[Go to Review ->]` -- text button that navigates to `?tab=review` +- Shown when: proposals with `status === 'pending'` exist for this initiative +- Query: `trpc.listProposals.useQuery({ initiativeId, status: 'pending' })` + +--- + +## Phase Detail Header + +``` ++------------------------------------------------------------------------+ +| Phase 2: OAuth Implementation [READY] [git] branch [...] | +| | | | +| branch badge v | +| +--------+ +| | Delete | +| | Phase | +| +--------+ ++------------------------------------------------------------------------+ +``` + +- Phase name is inline-editable (click to toggle input) +- `[...]` dropdown: Delete Phase (Shift+click bypasses confirm) +- `[git] branch` badge shows initiative branch when set + +--- + +## Detailing In Progress (per-phase) + +``` ++------------------------------------------------------------------------+ +| Phase 2: OAuth Implementation [READY] [spinner] Detailing | ++------------------------------------------------------------------------+ +``` + +- Shown in phase detail header when a detail agent is active for this phase +- `[spinner]` -- `Loader2` with `animate-spin` + +--- + +## Per-Phase Pending Review Banner + +``` ++------------------------------------------------------------------------+ +| [!] This phase is pending review. Go to the Review tab to review. | ++------------------------------------------------------------------------+ +``` + +- Shown below phase header when phase-specific proposals are pending +- `[!]` -- `AlertCircle` icon, `text-amber-500` +- Distinct from top-level banner (this is phase-scoped) + +--- + +## Task Detail Modal (overlay) + +``` ++----------------------------------------------------------+ +| Set up OAuth routes [x] | +| | +| Status [PENDING] | Category execute | +| Phase 2. OAuth Flow | Priority normal | +| Agent (none) | | +| | +| Description | +| Create Express routes for /auth/login, /auth/callback, | +| and /auth/logout with proper middleware chain. | +| | +| Dependencies | +| * DB Schema Migration [DONE] | +| | +| Blocks | +| * GitHub provider adapter [BLOCKED] | +| | +| [ Queue Task ] [ Stop Task ] | ++----------------------------------------------------------+ +``` + +- Opened by clicking any task row +- `[x]` close button top-right +- `[ Queue Task ]` dispatches the task; `[ Stop Task ]` kills the running agent +- Shared component via `` in `ExecutionContext` + +--- + +## Source + +- `packages/web/src/components/ExecutionTab.tsx` (Plan tab = ExecutionTab with sidebar+detail layout) +- `packages/web/src/components/execution/PlanSection.tsx` +- `packages/web/src/components/execution/PhaseSidebarItem.tsx` +- `packages/web/src/components/execution/PhaseDetailPanel.tsx` +- `packages/web/src/components/execution/PhaseActions.tsx` +- `packages/web/src/components/execution/TaskModal.tsx` +- `packages/web/src/components/TaskRow.tsx` +- `packages/web/src/hooks/useAutoSave.ts` diff --git a/docs/wireframes/v2/review-tab.md b/docs/wireframes/v2/review-tab.md new file mode 100644 index 0000000..b2dfd04 --- /dev/null +++ b/docs/wireframes/v2/review-tab.md @@ -0,0 +1,205 @@ +# Review Tab (v2) + +### Route: `/initiatives/$id` (Review tab) +### Source: `packages/web/src/components/review/ReviewTab.tsx` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Loading state | Plain "Loading diff..." text | Centered `[spinner]` + "Loading diff..." text | +| Error state | None | `[AlertCircle]` + error message + `[Retry]` button | +| Unresolved threads | Count in diff header only | Count in header + `[v]` jump-to-next button in sidebar | +| Empty state | "No changes in this phase" | `[check-circle]` icon + "No changes to review" + description | + +--- + +## Default State (with diff and threads) + +``` ++=============================================================================+ +| Diff: src/auth/oauth.ts 3 unresolved | Threads [v] | +| ----------------------------------------------------------- 300px | +| @@ -12,6 +12,14 @@ | | +| import { generatePKCE } from './pkce'; | Thread #1 | +| +export async function authorize( | Line 14 | +| + client: OAuthClient, | "Should we | +| + scopes: string[] | validate | +| +) { | scopes?" | +| + const { verifier, challenge } = generatePKCE(); | [Reply___] | +| + // ... | [Resolve] | +| +} | | +| | Thread #2 | +| export function validateToken( | Line 28 | +| - token: string | "Add expiry | +| + token: string, | check here" | +| + options?: ValidateOptions | [RESOLVED] | +| | | ++=============================================================================+ +``` + +`[v]` is the "Jump to next unresolved" button. Clicking it scrolls the diff viewer +to the next unresolved comment thread. Disabled when all threads are resolved. + +### Diff header anatomy + +``` ++------------------------------------------------------------------------+ +| Diff: N unresolved | ++------------------------------------------------------------------------+ +``` + +- File path is the currently-focused file +- "N unresolved" uses `text-status-warning-fg` when N > 0, `text-muted-foreground` when 0 + +### Sidebar header anatomy + +``` ++---------------------------+ +| Threads [v] | ++---------------------------+ +``` + +- `[v]` (ChevronDown icon) scrolls to next unresolved thread in diff viewer +- Tooltip: "Jump to next unresolved" + +--- + +## Loading State + +``` ++=============================================================================+ +| | +| | +| [spinner] | +| Loading diff... | +| | +| | ++=============================================================================+ +``` + +Centered vertically and horizontally within the tab content area. +Spinner uses `animate-spin` on a `Loader2` Lucide icon. +Text is `text-sm text-muted-foreground`. + +--- + +## Error State + +``` ++=============================================================================+ +| | +| | +| [AlertCircle] | +| Failed to load diff data | +| | +| [ Retry ] | +| | ++=============================================================================+ +``` + +- `[AlertCircle]` icon: `h-8 w-8 text-destructive` +- Error message: `text-sm text-destructive` +- `[ Retry ]` button: `variant="outline" size="sm"`, calls `diffQuery.refetch()` + +--- + +## Empty State (no proposals/diffs) + +``` ++=============================================================================+ +| | +| [check-circle] | +| No changes to review | +| All proposals have been reviewed. | +| | ++=============================================================================+ +``` + +- `[check-circle]` icon: `h-8 w-8 text-status-success-fg` +- Title: `text-sm font-medium` +- Description: `text-sm text-muted-foreground` + +This replaces the v1 "No phases pending review" plain text. + +--- + +## Full Page Context (within initiative detail) + +``` ++=============================================================================+ +| [CW] [*Initiatives*] Agents *3 Inbox (2) Settings [cmd-k] [sun] * | ++=============================================================================+ +| < Back | +| Auth System Overhaul [ACTIVE] [REVIEW] [git] cw/auth [P] backend | +| [ Content ] [ Plan ] [ Execution ] [*Review*] | ++-----------------------------------------------------------------------------| +| | +| Phase: [*Phase 2: OAuth*] [Phase 3: UI] | +| | +| +-----------------------------------------------------------+ | +| | [play] Start Preview | | +| +-----------------------------------------------------------+ | +| | +| Diff: src/auth/oauth.ts 3 unresolved | Threads [v] | +| ---------------------------------------------------+ 300px | +| @@ -12,6 +12,14 @@ | | +| import { generatePKCE } from './pkce'; | Thread #1 | +| +export async function authorize( | Line 14 | +| + client: OAuthClient, | "Should we | +| + scopes: string[] | validate | +| +) { | scopes?" | +| + const { verifier, challenge } = ... | [Reply___] | +| + // ... | [Resolve] | +| +} | | +| | Thread #2 | +| export function validateToken( | Line 28 | +| - token: string | "Add expiry | +| + token: string, | check here" | +| + options?: ValidateOptions | [RESOLVED] | +| | | ++=============================================================================+ +``` + +--- + +## Thread States + +### Unresolved thread (in sidebar) + +``` ++---------------------------+ +| Thread #1 | +| Line 14 | +| "Should we validate | +| scopes?" | +| [Reply_______________] | +| [Resolve] | ++---------------------------+ +``` + +### Resolved thread (in sidebar) + +``` ++---------------------------+ +| Thread #2 | +| Line 28 | +| "Add expiry check here" | +| [RESOLVED] | ++---------------------------+ +``` + +- Resolved threads are visually dimmed: `opacity-60` +- `[RESOLVED]` badge uses `bg-status-success-bg text-status-success-fg` + +--- + +## Source + +- `packages/web/src/components/review/ReviewTab.tsx` +- `packages/web/src/components/review/DiffViewer.tsx` +- `packages/web/src/components/review/ReviewSidebar.tsx` +- `packages/web/src/components/review/PreviewPanel.tsx` +- `packages/web/src/components/review/FileCard.tsx` diff --git a/docs/wireframes/v2/settings.md b/docs/wireframes/v2/settings.md new file mode 100644 index 0000000..b06c5e5 --- /dev/null +++ b/docs/wireframes/v2/settings.md @@ -0,0 +1,186 @@ +# Settings Pages (v2) + +### Route: `/settings/*` +### Source: `packages/web/src/routes/settings.tsx`, `packages/web/src/routes/settings/projects.tsx` + +--- + +## v1 -> v2 Changes + +| Aspect | v1 | v2 | +|--------|----|----| +| Projects error state | None | `[AlertCircle]` + "Failed to load projects" + `[Retry]` | +| Delete button | Immediate (with `window.confirm`) | Shows `[spinner]` during mutation to prevent double-click | +| Health Check | Unchanged | Unchanged | + +--- + +## Settings Layout (unchanged) + +``` ++=============================================================================+ +| [CW] Initiatives Agents [*Settings*] [cmd-k] [sun] * | ++=============================================================================+ +| | +| Settings | +| [ Health Check ] [*Projects*] | +| ----------------------------------------------------------------------- | +| | +| | +| | ++=============================================================================+ +``` + +`/settings` redirects to `/settings/health`. + +--- + +## Projects -- Default State + +``` ++=============================================================================+ +| Settings | +| [ Health Check ] [*Projects*] | +| ----------------------------------------------------------------------- | +| [ Register Project ] | +| | +| +-----------------------------------------------------------------------+ | +| | backend | | +| | github.com/org/backend-api | | +| | | | +| | Default branch: main [pencil] [trash] | | +| +-----------------------------------------------------------------------+ | +| | +| +-----------------------------------------------------------------------+ | +| | frontend | | +| | github.com/org/frontend-app | | +| | | | +| | Default branch: develop [pencil] [trash] | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +### Project card anatomy + +``` ++------------------------------------------------------------------------+ +| | +| | +| | +| Default branch: [pencil] [trash] | ++------------------------------------------------------------------------+ +``` + +- `[pencil]` (Pencil icon): click to toggle inline edit mode +- `[trash]` (Trash2 icon): click triggers `window.confirm()`; Shift+click bypasses + +### Inline branch editing (click pencil) + +``` +| Default branch: [develop________] [enter to save / esc to cancel] | +``` + +--- + +## Projects -- Error State + +``` ++=============================================================================+ +| Settings | +| [ Health Check ] [*Projects*] | +| ----------------------------------------------------------------------- | +| | +| [AlertCircle] | +| Failed to load projects | +| | +| [ Retry ] | +| | ++=============================================================================+ +``` + +- `[AlertCircle]` icon: `h-8 w-8 text-destructive` +- Error message: `text-sm text-destructive` +- `[ Retry ]` button: `variant="outline" size="sm"`, calls `projectsQuery.refetch()` +- `[ Register Project ]` button is hidden during error state + +--- + +## Projects -- Delete In Progress + +``` ++------------------------------------------------------------------------+ +| backend | +| github.com/org/backend-api | +| | +| Default branch: main [pencil] [spinner] | ++------------------------------------------------------------------------+ +``` + +- `[trash]` icon replaced by `[spinner]` (`Loader2` with `animate-spin`) during `deleteMutation.isPending` +- Button is `disabled` to prevent double-click +- Only the card being deleted shows the spinner; other cards remain interactive + +--- + +## Projects -- Empty State + +``` ++=============================================================================+ +| Settings | +| [ Health Check ] [*Projects*] | +| ----------------------------------------------------------------------- | +| | +| +-----------------------------------------------------------------------+ | +| | | | +| | No projects registered | | +| | | | +| | [ Register Project ] | | +| | | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +- `[ Register Project ]` button opens `RegisterProjectDialog` +- The top-right `[ Register Project ]` button is hidden when the empty state CTA is visible + +--- + +## Projects -- Loading State + +``` ++=============================================================================+ +| Settings | +| [ Health Check ] [*Projects*] | +| ----------------------------------------------------------------------- | +| [ Register Project ] | +| | +| +-----------------------------------------------------------------------+ | +| | ░░░░░░░░░░░░░░ | | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | | +| | | | +| | ░░░░░░░░░░░░░░░░░░░░░ ░░ ░░ | | +| +-----------------------------------------------------------------------+ | +| | +| +-----------------------------------------------------------------------+ | +| | ░░░░░░░░░░░░░░ | | +| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | | +| | | | +| | ░░░░░░░░░░░░░░░░░░░░░ ░░ ░░ | | +| +-----------------------------------------------------------------------+ | +| | ++=============================================================================+ +``` + +Skeleton cards mirror project card anatomy: name line, URL line, branch line with +pencil/trash placeholders. Shimmer animation sweeps left-to-right on a 1.5s loop. + +--- + +## Source + +- `packages/web/src/routes/settings.tsx` +- `packages/web/src/routes/settings/health.tsx` +- `packages/web/src/routes/settings/projects.tsx` +- `packages/web/src/components/RegisterProjectDialog.tsx` diff --git a/docs/wireframes/v2/shared-components.md b/docs/wireframes/v2/shared-components.md new file mode 100644 index 0000000..63fe77f --- /dev/null +++ b/docs/wireframes/v2/shared-components.md @@ -0,0 +1,440 @@ +# Shared Components (v2) + +### NEW — no v1 equivalent + +Reusable component specifications used across multiple pages. Each component +includes: description, props/API, ASCII mockups of all states, usage locations, +and proposed source file path. + +--- + +## 1. `` + +Inline status indicator for auto-save operations. Shows current save state +with icon + label, auto-hides on success. + +### Props + +```ts +interface SaveIndicatorProps { + status: 'idle' | 'saving' | 'saved' | 'error'; + onRetry?: () => void; +} +``` + +### States + +``` +Idle: (hidden — renders nothing) + +Saving: [spinner] Saving... + +Saved: [v] Saved <- fades out after 2s, transitions to idle + +Error: [!] Failed to save [retry] +``` + +### Detailed ASCII + +**Saving** +``` ++---------------------------+ +| [spinner] Saving... | ++---------------------------+ +``` +- `text-muted-foreground text-sm` +- Spinner is a 14px animated SVG + +**Saved** +``` ++---------------------------+ +| [v] Saved | ++---------------------------+ +``` +- `text-green-600 text-sm` +- Checkmark icon, 14px +- Fades to `opacity-0` over 300ms after 2s delay, then sets status to `idle` + +**Error** +``` ++--------------------------------------+ +| [!] Failed to save [retry] | ++--------------------------------------+ +``` +- `text-destructive text-sm` +- Alert icon, 14px +- `[retry]` is a text button: `text-destructive underline cursor-pointer` +- Does not auto-hide — persists until retry succeeds or user navigates away + +### Usage + +| Page | Location | +|------|----------| +| Content tab | Top-right of Tiptap editor toolbar (see content-tab.md) | +| Plan tab | Top-right of phase description editor (see plan-tab.md) | + +### Source + +- `packages/web/src/components/SaveIndicator.tsx` (proposed) + +--- + +## 2. `` + +Centered placeholder shown when a list or section has no items. Configurable +icon, messaging, and optional CTA button. + +### Props + +```ts +interface EmptyStateProps { + icon: React.ReactNode; // e.g., , , + title: string; // e.g., "No initiatives yet" + description?: string; // e.g., "Create an initiative to get started." + action?: { + label: string; // e.g., "New Initiative" + onClick: () => void; + }; +} +``` + +### Default (with action) + +``` ++-------------------------------------------+ +| | +| [icon] | +| No phases yet | +| Add a phase to start planning. | +| | +| [ Add Phase ] | +| | ++-------------------------------------------+ +``` + +### Without action + +``` ++-------------------------------------------+ +| | +| [icon] | +| No conversations yet | +| Questions from agents appear here. | +| | ++-------------------------------------------+ +``` + +### Without description + +``` ++-------------------------------------------+ +| | +| [icon] | +| No agents running | +| | ++-------------------------------------------+ +``` + +### Styling + +- Container: `flex flex-col items-center justify-center py-16 text-center` +- Icon: `h-12 w-12 text-muted-foreground mb-4` +- Title: `text-lg font-medium text-foreground mb-1` +- Description: `text-sm text-muted-foreground mb-6` +- Action button: default shadcn `` + +### Usage + +| Page | Message | Has Retry? | +|------|---------|------------| +| Initiatives list | "Failed to load initiatives" | Yes | +| Initiative detail | "Error loading initiative" | No (nav button) | +| Review tab | "Failed to load proposals" | Yes | +| Agents page | "Failed to load agents" | Yes | +| Settings page | "Failed to load settings" | Yes | + +### Source + +- `packages/web/src/components/ErrorState.tsx` (proposed) + +--- + +## 4. `` + +Global search overlay triggered by keyboard shortcut. Provides quick +navigation to initiatives, agents, tasks, and settings pages. + +### Trigger + +- `Cmd+K` (Mac) / `Ctrl+K` (Windows) +- Header button `[cmd-k]` (see app-layout.md) + +### Props + +```ts +interface CommandPaletteProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} +``` + +Internally fetches data via tRPC queries (initiatives, agents, tasks). + +### Default State (open, empty query) + +``` ++----------------------------------------------------------+ +| [search] Search everything... [Esc] | +| --------------------------------------------------------| +| Recent | +| -> Auth System Overhaul | +| -> Agents | +| -> Settings | ++----------------------------------------------------------+ +``` + +Shows recent navigation history when search is empty. + +### With Search Results + +``` ++----------------------------------------------------------+ +| [search] auth___ [Esc] | +| --------------------------------------------------------| +| Initiatives | +| Auth System Overhaul [ACTIVE] | +| --------------------------------------------------------| +| Tasks | +| Implement auth middleware [execute] 3/7 | +| Review auth token flow [verify] done | +| --------------------------------------------------------| +| Agents | +| blue-fox-7 (auth middleware) [RUNNING] | ++----------------------------------------------------------+ +``` + +### No Results + +``` ++----------------------------------------------------------+ +| [search] xyznonexistent___ [Esc] | +| --------------------------------------------------------| +| | +| No results for "xyznonexistent" | +| | ++----------------------------------------------------------+ +``` + +### Keyboard Navigation + +``` + [search] auth___ [Esc] + --------------------------------------------------------- + Initiatives + > Auth System Overhaul [ACTIVE] <- highlighted + --------------------------------------------------------- + Tasks + Implement auth middleware [execute] 3/7 +``` + +- `>` indicates the currently highlighted item +- `Arrow Up` / `Arrow Down` — move highlight +- `Enter` — navigate to highlighted item, close palette +- `Esc` — close palette +- Type to filter — results update as you type (debounced 100ms) + +### Result Groups + +| Group | Source | Display | +|-------|--------|---------| +| Initiatives | `listInitiatives` query | Name + status badge | +| Tasks | `listTasks` query (across initiatives) | Name + category + status | +| Agents | `listAgents` query | Name + task description + status badge | +| Pages | `Go to Initiatives`, `Go to Agents`, etc. | Static navigation items | + +Groups are hidden when they have 0 matches. Max 5 items per group. + +### Overlay Behavior + +- Renders as a centered modal with backdrop (`bg-black/50`) +- Width: `max-w-lg` (512px) +- Position: `top-[20%]` of viewport +- Backdrop click closes the palette +- Focus is trapped inside the modal while open +- Search input auto-focuses on open + +### Source + +- `packages/web/src/components/CommandPalette.tsx` (proposed) + +--- + +## 5. `` + +3-state segmented control for switching between light, system, and dark +color schemes. Persists selection to `localStorage`. + +### Props + +```ts +// No props — reads/writes theme from ThemeProvider context +// ThemeProvider wraps the app root +type Theme = 'light' | 'system' | 'dark'; +``` + +### States + +``` +Light active: [*sun*] [monitor] [moon] +System active: [sun] [*monitor*] [moon] +Dark active: [sun] [monitor] [*moon*] +``` + +### Detailed ASCII + +**Light mode selected** +``` ++-----+----------+---------+ +| sun | monitor | moon | ++-----+----------+---------+ + ^^^ + filled bg, active text +``` + +**System mode selected (default)** +``` ++-----+----------+---------+ +| sun | monitor | moon | ++-----+----------+---------+ + ^^^^^^^^ + filled bg, active text +``` + +**Dark mode selected** +``` ++-----+----------+---------+ +| sun | monitor | moon | ++-----+----------+---------+ + ^^^^^^ + filled bg, active text +``` + +### Styling + +- Container: `inline-flex rounded-lg bg-muted p-0.5 gap-0.5` +- Segment (inactive): `px-2 py-1 rounded-md text-muted-foreground hover:text-foreground` +- Segment (active): `px-2 py-1 rounded-md bg-background text-foreground shadow-sm` +- Icons: 16px lucide-react icons (`Sun`, `Monitor`, `Moon`) +- No text labels — icons only in the header (compact) + +### Tooltip on hover + +``` + [sun] -> "Light" + [monitor] -> "System" + [moon] -> "Dark" +``` + +### Persistence + +- Stored in `localStorage` key `cw-theme` +- Default: `system` +- On load: reads `localStorage`, applies `class="dark"` to `` if + theme is `dark` or if theme is `system` and `prefers-color-scheme: dark` + +### Usage + +| Page | Location | +|------|----------| +| App layout (header) | Right cluster, between Cmd+K button and health dot | + +See app-layout.md for header placement. + +### Source + +- `packages/web/src/components/ThemeToggle.tsx` (proposed) +- `packages/web/src/providers/ThemeProvider.tsx` (proposed) + +--- + +## Cross-Reference Index + +Quick lookup of which shared components are used on each page. + +| Component | app-layout | initiatives-list | initiative-detail | content-tab | plan-tab | execution-tab | review-tab | agents | inbox | settings | +|-----------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| SaveIndicator | | | | x | x | | | | | | +| EmptyState | | x | | | x | x | | x | x | x | +| ErrorState | | x | x | | | | x | x | | x | +| CommandPalette | x | | | | | | | | | | +| ThemeToggle | x | | | | | | | | | | diff --git a/docs/wireframes/v2/theme.md b/docs/wireframes/v2/theme.md new file mode 100644 index 0000000..f0803ac --- /dev/null +++ b/docs/wireframes/v2/theme.md @@ -0,0 +1,769 @@ +# Theme — Design System v2 + +Complete design system overhaul. Replaces the achromatic shadcn/ui defaults with an indigo-branded, status-aware, dark-mode-first token system. + +## Current State (v1 Problems) + +``` +:root (light) .dark +--secondary: 0 0% 96.1% --secondary: 0 0% 14.9% +--muted: 0 0% 96.1% <-- SAME --muted: 0 0% 14.9% <-- SAME +--accent: 0 0% 96.1% <-- SAME --accent: 0 0% 14.9% <-- SAME +--primary: 0 0% 9% <-- black --primary: 0 0% 98% <-- white +--ring: 0 0% 3.9% <-- black --ring: 0 0% 83.1% <-- grey +--radius: 0.5rem (8px) +``` + +Problems: +- Zero brand identity (achromatic everywhere) +- `secondary`, `muted`, and `accent` all resolve to the **same** gray value +- No status color tokens (active/success/warning/error) +- No terminal tokens (AgentOutputViewer always-dark surface) +- No diff tokens (review components) +- No dark mode toggle +- Ring color matches foreground instead of brand color +- 8px radius is visually heavy + +--- + +## 1. Color System — Indigo Brand (#6366F1) + +### Light Mode + +```css +:root { + --background: 0 0% 99%; /* #FCFCFC — slight warmth, not pure white */ + --foreground: 240 6% 10%; /* #18181B — zinc-900 equivalent */ + + --card: 0 0% 100%; /* #FFFFFF */ + --card-foreground: 240 6% 10%; + + --popover: 0 0% 100%; /* #FFFFFF */ + --popover-foreground: 240 6% 10%; + + --primary: 239 84% 67%; /* #6366F1 — indigo-500 */ + --primary-foreground: 0 0% 100%; /* white on indigo */ + + --secondary: 240 5% 96%; /* #F4F4F5 — zinc-100 */ + --secondary-foreground: 240 4% 16%;/* #27272A — zinc-800 */ + + --muted: 240 5% 93%; /* #ECECEE — distinguishable from secondary */ + --muted-foreground: 240 4% 46%; /* #71717A — zinc-500 */ + + --accent: 226 100% 97%; /* #EEF0FF — light indigo tint */ + --accent-foreground: 239 84% 67%; /* indigo on tint */ + + --destructive: 0 84% 60%; /* #EF4444 — red-500 */ + --destructive-foreground: 0 0% 100%; + + --border: 240 6% 90%; /* #E4E4E7 — zinc-200 */ + --input: 240 6% 90%; + --ring: 239 84% 67%; /* indigo focus ring */ + + --radius: 0.375rem; /* 6px — down from 8px */ +} +``` + +### Dark Mode + +```css +.dark { + --background: 240 6% 7%; /* #111114 */ + --foreground: 240 5% 96%; /* #F4F4F5 */ + + --card: 240 5% 10%; /* #19191D — surface level 1 */ + --card-foreground: 240 5% 96%; + + --popover: 240 5% 13%; /* #202025 — surface level 2 */ + --popover-foreground: 240 5% 96%; + + --primary: 239 84% 67%; /* #6366F1 — same indigo */ + --primary-foreground: 0 0% 100%; + + --secondary: 240 4% 16%; /* #27272A */ + --secondary-foreground: 240 5% 96%; + + --muted: 240 4% 16%; /* #27272A */ + --muted-foreground: 240 5% 65%; /* #A1A1AA — zinc-400 */ + + --accent: 239 40% 16%; /* dark indigo tint */ + --accent-foreground: 239 84% 75%; /* lighter indigo on dark */ + + --destructive: 0 63% 31%; + --destructive-foreground: 0 0% 100%; + + --border: 240 4% 16%; + --input: 240 4% 16%; + --ring: 239 84% 67%; +} +``` + +### Surface Hierarchy (Dark Mode) + +Three-tier elevation model using lightness alone (no box shadows in dark mode): + +``` +Level 0 --background 240 6% 7% #111114 Page background +Level 1 --card 240 5% 10% #19191D Cards, panels, sidebars +Level 2 --popover 240 5% 13% #202025 Dropdowns, tooltips, command palette +``` + +Visual reference: + +``` ++============================================================================+ +| Level 0 (7%) ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | +| | +| +------------------------------------------------------------------+ | +| | Level 1 (10%) ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ | | +| | | | +| | +----------------------------------------------+ | | +| | | Level 2 (13%) ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ | | | +| | +----------------------------------------------+ | | +| | | | +| +------------------------------------------------------------------+ | +| | ++============================================================================+ +``` + +--- + +## 2. Status Tokens + +Six semantic status sets with bg/fg/border/dot variants for both light and dark modes. + +### Light Mode + +| Status | Use Case | bg | fg | border | dot | +|--------|----------|----|----|--------|-----| +| `active` | Running agents, in-progress tasks | `210 100% 95%` | `210 100% 40%` | `210 100% 80%` | `210 100% 50%` | +| `success` | Completed tasks, healthy services | `142 72% 94%` | `142 72% 29%` | `142 72% 80%` | `142 72% 45%` | +| `warning` | Pending approval, exhausted accounts | `38 92% 95%` | `38 92% 30%` | `38 92% 80%` | `38 92% 50%` | +| `error` | Failed tasks, crashed agents | `0 84% 95%` | `0 84% 40%` | `0 84% 80%` | `0 84% 50%` | +| `neutral` | Exited agents, idle states | `240 5% 96%` | `240 4% 46%` | `240 6% 90%` | `240 4% 46%` | +| `urgent` | Blocked tasks, attention needed | `270 91% 95%` | `270 91% 40%` | `270 91% 80%` | `270 91% 55%` | + +### Dark Mode + +| Status | bg | fg | border | dot | +|--------|----|----|--------|-----| +| `active` | `210 100% 12%` | `210 100% 70%` | `210 100% 25%` | `210 100% 50%` | +| `success` | `142 72% 10%` | `142 72% 65%` | `142 72% 22%` | `142 72% 45%` | +| `warning` | `38 92% 10%` | `38 92% 65%` | `38 92% 22%` | `38 92% 50%` | +| `error` | `0 84% 12%` | `0 84% 65%` | `0 84% 25%` | `0 84% 50%` | +| `neutral` | `240 4% 16%` | `240 5% 65%` | `240 4% 22%` | `240 5% 50%` | +| `urgent` | `270 91% 12%` | `270 91% 70%` | `270 91% 25%` | `270 91% 55%` | + +### CSS Custom Properties + +```css +:root { + /* active */ + --status-active-bg: 210 100% 95%; + --status-active-fg: 210 100% 40%; + --status-active-border: 210 100% 80%; + --status-active-dot: 210 100% 50%; + + /* success */ + --status-success-bg: 142 72% 94%; + --status-success-fg: 142 72% 29%; + --status-success-border: 142 72% 80%; + --status-success-dot: 142 72% 45%; + + /* warning */ + --status-warning-bg: 38 92% 95%; + --status-warning-fg: 38 92% 30%; + --status-warning-border: 38 92% 80%; + --status-warning-dot: 38 92% 50%; + + /* error */ + --status-error-bg: 0 84% 95%; + --status-error-fg: 0 84% 40%; + --status-error-border: 0 84% 80%; + --status-error-dot: 0 84% 50%; + + /* neutral */ + --status-neutral-bg: 240 5% 96%; + --status-neutral-fg: 240 4% 46%; + --status-neutral-border: 240 6% 90%; + --status-neutral-dot: 240 4% 46%; + + /* urgent */ + --status-urgent-bg: 270 91% 95%; + --status-urgent-fg: 270 91% 40%; + --status-urgent-border: 270 91% 80%; + --status-urgent-dot: 270 91% 55%; +} + +.dark { + --status-active-bg: 210 100% 12%; + --status-active-fg: 210 100% 70%; + --status-active-border: 210 100% 25%; + --status-active-dot: 210 100% 50%; + + --status-success-bg: 142 72% 10%; + --status-success-fg: 142 72% 65%; + --status-success-border: 142 72% 22%; + --status-success-dot: 142 72% 45%; + + --status-warning-bg: 38 92% 10%; + --status-warning-fg: 38 92% 65%; + --status-warning-border: 38 92% 22%; + --status-warning-dot: 38 92% 50%; + + --status-error-bg: 0 84% 12%; + --status-error-fg: 0 84% 65%; + --status-error-border: 0 84% 25%; + --status-error-dot: 0 84% 50%; + + --status-neutral-bg: 240 4% 16%; + --status-neutral-fg: 240 5% 65%; + --status-neutral-border: 240 4% 22%; + --status-neutral-dot: 240 5% 50%; + + --status-urgent-bg: 270 91% 12%; + --status-urgent-fg: 270 91% 70%; + --status-urgent-border: 270 91% 25%; + --status-urgent-dot: 270 91% 55%; +} +``` + +### Tailwind Utility Class Mapping + +Extend `tailwind.config.ts` to expose status tokens as utilities: + +```ts +// tailwind.config.ts +theme: { + extend: { + colors: { + status: { + 'active-bg': 'hsl(var(--status-active-bg))', + 'active-fg': 'hsl(var(--status-active-fg))', + 'active-border': 'hsl(var(--status-active-border))', + 'active-dot': 'hsl(var(--status-active-dot))', + + 'success-bg': 'hsl(var(--status-success-bg))', + 'success-fg': 'hsl(var(--status-success-fg))', + 'success-border': 'hsl(var(--status-success-border))', + 'success-dot': 'hsl(var(--status-success-dot))', + + 'warning-bg': 'hsl(var(--status-warning-bg))', + 'warning-fg': 'hsl(var(--status-warning-fg))', + 'warning-border': 'hsl(var(--status-warning-border))', + 'warning-dot': 'hsl(var(--status-warning-dot))', + + 'error-bg': 'hsl(var(--status-error-bg))', + 'error-fg': 'hsl(var(--status-error-fg))', + 'error-border': 'hsl(var(--status-error-border))', + 'error-dot': 'hsl(var(--status-error-dot))', + + 'neutral-bg': 'hsl(var(--status-neutral-bg))', + 'neutral-fg': 'hsl(var(--status-neutral-fg))', + 'neutral-border': 'hsl(var(--status-neutral-border))', + 'neutral-dot': 'hsl(var(--status-neutral-dot))', + + 'urgent-bg': 'hsl(var(--status-urgent-bg))', + 'urgent-fg': 'hsl(var(--status-urgent-fg))', + 'urgent-border': 'hsl(var(--status-urgent-border))', + 'urgent-dot': 'hsl(var(--status-urgent-dot))', + }, + }, + }, +} +``` + +Usage in components: + +```tsx + + [RUNNING] + + + {/* green dot */} +``` + +### Status-to-Entity Mapping + +| Entity State | Status Token | +|--------------|-------------| +| Agent: running | `active` | +| Agent: waiting_for_input | `warning` | +| Agent: exited / completed | `neutral` | +| Agent: crashed | `error` | +| Task: in_progress | `active` | +| Task: completed | `success` | +| Task: pending | `neutral` | +| Task: pending_approval | `warning` | +| Task: blocked | `urgent` | +| Task: failed | `error` | +| Account: active | `success` | +| Account: exhausted | `warning` | +| Preview: building | `active` | +| Preview: running | `success` | +| Preview: failed | `error` | +| Preview: stopped | `neutral` | +| Server health: connected | `success` | +| Server health: disconnected | `error` | + +--- + +## 3. Terminal Tokens + +For `AgentOutputViewer` — always-dark surface even in light mode (terminal aesthetic), card-level in dark mode. + +```css +:root { + --terminal-bg: 240 6% 7%; /* always dark — #111114 */ + --terminal-fg: 120 100% 80%; /* green-on-dark */ + --terminal-muted: 240 5% 55%; /* dimmed text */ + --terminal-border: 240 4% 16%; + --terminal-selection: 239 84% 67%; /* indigo selection, use with /0.2 alpha */ + + --terminal-system: 240 5% 55%; /* [System] badge text */ + --terminal-tool: 217 91% 60%; /* [Tool Call] blue accent */ + --terminal-result: 142 72% 45%; /* [Result] green accent */ + --terminal-error: 0 84% 60%; /* [Error] red accent */ +} + +.dark { + --terminal-bg: 240 5% 10%; /* matches card surface */ + --terminal-fg: 120 100% 80%; + --terminal-muted: 240 5% 55%; + --terminal-border: 240 4% 16%; + --terminal-selection: 239 84% 67%; + + --terminal-system: 240 5% 55%; + --terminal-tool: 217 91% 60%; + --terminal-result: 142 72% 45%; + --terminal-error: 0 84% 60%; +} +``` + +Tailwind extension: + +```ts +terminal: { + DEFAULT: 'hsl(var(--terminal-bg))', + fg: 'hsl(var(--terminal-fg))', + muted: 'hsl(var(--terminal-muted))', + border: 'hsl(var(--terminal-border))', + system: 'hsl(var(--terminal-system))', + tool: 'hsl(var(--terminal-tool))', + result: 'hsl(var(--terminal-result))', + error: 'hsl(var(--terminal-error))', +}, +``` + +Visual reference (AgentOutputViewer): + +``` ++------------------------------------------------------------------------+ +| bg: --terminal-bg | +| | +| [System] Starting task... <-- text: --terminal-system | +| | +| > I'll examine the existing code. <-- text: --terminal-fg | +| | +| | [Tool Call] Read <-- border/text: --terminal-tool +| | file_path: src/auth/index.ts <-- text: --terminal-muted | +| | +| | [Result] <-- border/text: --terminal-result +| | import { Router } from 'express'; <-- text: --terminal-muted | +| | +| | [Error] <-- border/text: --terminal-error +| | Permission denied: /etc/hosts | +| | ++------------------------------------------------------------------------+ +``` + +--- + +## 4. Diff Tokens + +For review tab components — file diffs, proposal diffs, merge previews. + +```css +:root { + --diff-add-bg: 142 72% 94%; /* light green background */ + --diff-add-fg: 142 72% 29%; /* dark green text */ + --diff-add-border: 142 72% 80%; + + --diff-remove-bg: 0 84% 95%; /* light red background */ + --diff-remove-fg: 0 84% 40%; /* dark red text */ + --diff-remove-border: 0 84% 80%; + + --diff-hunk-bg: 226 100% 97%; /* indigo-tinted hunk header */ +} + +.dark { + --diff-add-bg: 142 72% 10%; + --diff-add-fg: 142 72% 65%; + --diff-add-border: 142 72% 22%; + + --diff-remove-bg: 0 84% 12%; + --diff-remove-fg: 0 84% 65%; + --diff-remove-border: 0 84% 25%; + + --diff-hunk-bg: 239 40% 16%; +} +``` + +Tailwind extension: + +```ts +diff: { + 'add-bg': 'hsl(var(--diff-add-bg))', + 'add-fg': 'hsl(var(--diff-add-fg))', + 'add-border': 'hsl(var(--diff-add-border))', + 'remove-bg': 'hsl(var(--diff-remove-bg))', + 'remove-fg': 'hsl(var(--diff-remove-fg))', + 'remove-border':'hsl(var(--diff-remove-border))', + 'hunk-bg': 'hsl(var(--diff-hunk-bg))', +}, +``` + +Visual reference: + +``` ++------------------------------------------------------------------------+ +| @@ -12,6 +12,8 @@ function setup() bg: --diff-hunk-bg | +| const router = Router(); | +| router.get('/health', ...); | +| + router.get('/oauth/callback', ...); bg: --diff-add-bg | +| + router.get('/oauth/login', ...); fg: --diff-add-fg | +| - router.get('/legacy', ...); bg: --diff-remove-bg | +| export default router; fg: --diff-remove-fg | ++------------------------------------------------------------------------+ +``` + +--- + +## 5. Typography — Geist Sans + Geist Mono + +### Installation + +```bash +cd packages/web +npm install geist +``` + +### CSS Import + +Add to `packages/web/src/index.css` before `@tailwind` directives: + +```css +@import 'geist/font/sans.css'; +@import 'geist/font/mono.css'; +``` + +### Tailwind Config + +```ts +// packages/web/tailwind.config.ts +import defaultTheme from 'tailwindcss/defaultTheme'; + +export default { + theme: { + extend: { + fontFamily: { + sans: ['Geist Sans', ...defaultTheme.fontFamily.sans], + mono: ['Geist Mono', ...defaultTheme.fontFamily.mono], + }, + }, + }, +}; +``` + +### Usage + +- Body text: `font-sans` (Geist Sans) — applied via Tailwind base +- Code blocks, terminal output: `font-mono` (Geist Mono) +- Agent names: `font-mono text-sm` +- Timestamps, metadata: `font-mono text-xs text-muted-foreground` + +No font size scale changes; keep Tailwind defaults (`text-xs` through `text-4xl`). + +--- + +## 6. Radius — 6px Base + +```css +:root { + --radius: 0.375rem; /* 6px, down from 0.5rem/8px */ +} +``` + +Tailwind border-radius mapping (shadcn/ui convention): + +```ts +borderRadius: { + lg: 'var(--radius)', // 6px — cards, dialogs + md: 'calc(var(--radius) - 2px)', // 4px — buttons, inputs + sm: 'calc(var(--radius) - 4px)', // 2px — badges, small elements +}, +``` + +v1 vs v2: + +| Token | v1 | v2 | +|-------|----|----| +| `--radius` | `0.5rem` (8px) | `0.375rem` (6px) | +| `rounded-lg` | 8px | 6px | +| `rounded-md` | 6px | 4px | +| `rounded-sm` | 4px | 2px | + +--- + +## 7. Shadows — Layered System + +### Light Mode + +```css +:root { + --shadow-xs: 0 1px 2px hsl(0 0% 0% / 0.04); + --shadow-sm: 0 1px 3px hsl(0 0% 0% / 0.06), 0 1px 2px hsl(0 0% 0% / 0.04); + --shadow-md: 0 4px 6px hsl(0 0% 0% / 0.06), 0 2px 4px hsl(0 0% 0% / 0.04); + --shadow-lg: 0 10px 15px hsl(0 0% 0% / 0.06), 0 4px 6px hsl(0 0% 0% / 0.04); + --shadow-xl: 0 20px 25px hsl(0 0% 0% / 0.08), 0 8px 10px hsl(0 0% 0% / 0.04); +} +``` + +### Dark Mode + +```css +.dark { + --shadow-xs: none; + --shadow-sm: none; + --shadow-md: none; + --shadow-lg: none; + --shadow-xl: none; +} +``` + +Dark mode uses **borders + surface lightness hierarchy** instead of box shadows. This avoids the muddy appearance of shadows on dark backgrounds. + +Tailwind extension: + +```ts +boxShadow: { + xs: 'var(--shadow-xs)', + sm: 'var(--shadow-sm)', + md: 'var(--shadow-md)', + lg: 'var(--shadow-lg)', + xl: 'var(--shadow-xl)', +}, +``` + +Usage guidance: + +| Component | Shadow Level | +|-----------|-------------| +| Buttons (hover) | `xs` | +| Cards, panels | `sm` | +| Dropdowns, popovers | `md` | +| Dialogs, modals | `lg` | +| Command palette | `xl` | + +--- + +## 8. Dark Mode Implementation + +### Default Behavior + +- First load: detect `prefers-color-scheme` media query +- Persist choice in `localStorage` key: `cw-theme` +- Values: `light`, `dark`, `system` +- Default (no stored value): `system` + +### Class Toggling + +Add/remove `.dark` class on the `` element: + +```ts +function applyTheme(theme: 'light' | 'dark' | 'system') { + const root = document.documentElement; + const isDark = + theme === 'dark' || + (theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches); + + root.classList.toggle('dark', isDark); + localStorage.setItem('cw-theme', theme); +} +``` + +### Inline Script (prevent flash) + +Add to `` in `index.html` before any stylesheets: + +```html + +``` + +### Toggle Component + +3-state toggle in the header bar, right-aligned: + +``` ++-------------------------------------------+ +| [logo] Codewalk District [Sun|Monitor|Moon] | ++-------------------------------------------+ + +States: + [*Sun*] Monitor Moon = light mode forced + Sun [*Monitor*] Moon = system preference + Sun Monitor [*Moon*] = dark mode forced +``` + +- Icons: `Sun` (Lucide `Sun`), `Monitor` (Lucide `Monitor`), `Moon` (Lucide `Moon`) +- Active state: `bg-accent` highlight +- Location: right side of header bar, same row as logo + +### React Context + +```ts +// packages/web/src/lib/theme.tsx +type Theme = 'light' | 'dark' | 'system'; + +const ThemeContext = createContext<{ + theme: Theme; + setTheme: (t: Theme) => void; + isDark: boolean; +}>(null!); + +function ThemeProvider({ children }: { children: ReactNode }) { + const [theme, setThemeState] = useState( + () => (localStorage.getItem('cw-theme') as Theme) || 'system' + ); + + const isDark = useMemo(() => { + if (theme === 'system') { + return window.matchMedia('(prefers-color-scheme: dark)').matches; + } + return theme === 'dark'; + }, [theme]); + + const setTheme = useCallback((t: Theme) => { + setThemeState(t); + applyTheme(t); + }, []); + + // Listen for system theme changes when in 'system' mode + useEffect(() => { + const mq = window.matchMedia('(prefers-color-scheme: dark)'); + const handler = () => { if (theme === 'system') applyTheme('system'); }; + mq.addEventListener('change', handler); + return () => mq.removeEventListener('change', handler); + }, [theme]); + + return ( + + {children} + + ); +} +``` + +--- + +## 9. Migration Notes + +### Token Changes: v1 to v2 + +#### Light Mode (`:root`) + +| Token | v1 | v2 | Notes | +|-------|----|----|-------| +| `--background` | `0 0% 100%` | `0 0% 99%` | Slight warmth | +| `--foreground` | `0 0% 3.9%` | `240 6% 10%` | Zinc-tinted, not pure black | +| `--card` | `0 0% 100%` | `0 0% 100%` | Unchanged | +| `--card-foreground` | `0 0% 3.9%` | `240 6% 10%` | Matches foreground | +| `--popover` | `0 0% 100%` | `0 0% 100%` | Unchanged | +| `--popover-foreground` | `0 0% 3.9%` | `240 6% 10%` | Matches foreground | +| `--primary` | `0 0% 9%` | `239 84% 67%` | **Black to indigo** | +| `--primary-foreground` | `0 0% 98%` | `0 0% 100%` | Near-white to white | +| `--secondary` | `0 0% 96.1%` | `240 5% 96%` | Zinc-tinted | +| `--secondary-foreground` | `0 0% 9%` | `240 4% 16%` | Zinc-tinted | +| `--muted` | `0 0% 96.1%` | `240 5% 93%` | **Now 93%, not 96.1%** | +| `--muted-foreground` | `0 0% 45.1%` | `240 4% 46%` | Zinc-tinted | +| `--accent` | `0 0% 96.1%` | `226 100% 97%` | **Grey to indigo tint** | +| `--accent-foreground` | `0 0% 9%` | `239 84% 67%` | **Black to indigo** | +| `--destructive` | `0 84.2% 60.2%` | `0 84% 60%` | Rounded values | +| `--destructive-foreground` | `0 0% 98%` | `0 0% 100%` | Near-white to white | +| `--border` | `0 0% 89.8%` | `240 6% 90%` | Zinc-tinted | +| `--input` | `0 0% 89.8%` | `240 6% 90%` | Zinc-tinted | +| `--ring` | `0 0% 3.9%` | `239 84% 67%` | **Black to indigo** | +| `--radius` | `0.5rem` | `0.375rem` | **8px to 6px** | + +#### Dark Mode (`.dark`) + +| Token | v1 | v2 | Notes | +|-------|----|----|-------| +| `--background` | `0 0% 3.9%` | `240 6% 7%` | Lighter, zinc-tinted | +| `--foreground` | `0 0% 98%` | `240 5% 96%` | Zinc-tinted | +| `--card` | `0 0% 3.9%` | `240 5% 10%` | **Elevated above bg** | +| `--card-foreground` | `0 0% 98%` | `240 5% 96%` | Zinc-tinted | +| `--popover` | `0 0% 3.9%` | `240 5% 13%` | **Elevated above card** | +| `--popover-foreground` | `0 0% 98%` | `240 5% 96%` | Zinc-tinted | +| `--primary` | `0 0% 98%` | `239 84% 67%` | **White to indigo** | +| `--primary-foreground` | `0 0% 9%` | `0 0% 100%` | Black to white | +| `--secondary` | `0 0% 14.9%` | `240 4% 16%` | Zinc-tinted | +| `--secondary-foreground` | `0 0% 98%` | `240 5% 96%` | Zinc-tinted | +| `--muted` | `0 0% 14.9%` | `240 4% 16%` | Zinc-tinted | +| `--muted-foreground` | `0 0% 63.9%` | `240 5% 65%` | Zinc-tinted | +| `--accent` | `0 0% 14.9%` | `239 40% 16%` | **Grey to dark indigo** | +| `--accent-foreground` | `0 0% 98%` | `239 84% 75%` | **White to light indigo** | +| `--destructive` | `0 62.8% 30.6%` | `0 63% 31%` | Rounded values | +| `--destructive-foreground` | `0 0% 98%` | `0 0% 100%` | Near-white to white | +| `--border` | `0 0% 14.9%` | `240 4% 16%` | Zinc-tinted | +| `--input` | `0 0% 14.9%` | `240 4% 16%` | Zinc-tinted | +| `--ring` | `0 0% 83.1%` | `239 84% 67%` | **Grey to indigo** | + +### Key Breaking Changes + +1. **`--primary` is no longer achromatic.** Any component using `bg-primary` will render indigo instead of black/white. This is intentional — buttons, links, and focus rings gain brand color. + +2. **`--secondary`, `--muted`, `--accent` are now distinct values.** Components relying on them being identical need review: + - `--secondary` = neutral surface (96% light) + - `--muted` = recessed surface (93% light) — **3% darker than secondary** + - `--accent` = indigo-tinted surface (97% light, blue hue) — **colored, not grey** + +3. **`--ring` is now indigo**, not foreground-colored. All focus outlines will be indigo. + +4. **`--radius` shrinks from 8px to 6px.** All rounded corners tighten slightly. + +5. **Dark mode card/popover surfaces are elevated.** v1 had card = background (both 3.9%). v2 separates them: background 7% < card 10% < popover 13%. + +### New Token Categories + +These are entirely new in v2 — no v1 equivalents exist: + +- **Status tokens** (24 light + 24 dark = 48 new properties) +- **Terminal tokens** (10 new properties) +- **Diff tokens** (14 new properties) +- **Shadow tokens** (5 new properties, `none` in dark) + +### File Changes Required + +| File | Changes | +|------|---------| +| `packages/web/src/index.css` | Replace all `:root` and `.dark` token values; add status, terminal, diff, shadow tokens; add Geist font imports | +| `packages/web/tailwind.config.ts` | Add `fontFamily`, `colors.status`, `colors.terminal`, `colors.diff`, `boxShadow`, `borderRadius` extensions | +| `packages/web/package.json` | Add `geist` dependency | +| `packages/web/index.html` | Add dark mode inline script in `` | +| `packages/web/src/lib/theme.tsx` | New file: ThemeProvider context | +| `packages/web/src/layouts/AppLayout.tsx` | Add ThemeToggle to header | +| `packages/web/src/App.tsx` (or root) | Wrap with ThemeProvider | + +--- + +## Source + +- `packages/web/src/index.css` (current theme) +- `packages/web/tailwind.config.ts` (current Tailwind config)