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)
This commit is contained in:
Lukas May
2026-03-02 18:13:17 +09:00
parent 41b1d0e986
commit 478a7f18e9
14 changed files with 4039 additions and 0 deletions

View File

@@ -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 |

View File

@@ -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: <task name>" linking to the initiative detail (Execution tab)
- Else if agent has an `initiativeId`: show "Init: <initiative name>" 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`

View File

@@ -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 |
| |
| <Page Outlet> |
| |
| |
| |
| |
| |
+=============================================================================+
```
## Active Tab Highlighted
```
+=============================================================================+
| [CW] [*Initiatives*] Agents *3 Inbox (2) Settings [cmd-k] [sun] * |
+=============================================================================+
| |
| <Page Outlet> |
| |
+=============================================================================+
```
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] * |
+=============================================================================+
| |
| <Page Outlet> |
| |
+=============================================================================+
```
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] * [☰] |
+=============================================================================+
| |
| <Page Outlet> |
| |
+=============================================================================+
```
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 `<a>` 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)
- `<Toaster>` (sonner) — bottom-right toast notifications
- `<ErrorBoundary>` — wraps the page outlet
- `<CommandPalette>` — 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)

View File

@@ -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 | `<SaveIndicator>` 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: <message> |
| |
| 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`

View File

@@ -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] <verb>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/<slugified-name>`) 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... |
| |
| <error message text> |
| [ 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`

View File

@@ -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 `<TaskModal>` (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`

260
docs/wireframes/v2/inbox.md Normal file
View File

@@ -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`

View File

@@ -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/<branch>` — 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 `<ErrorState>` 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)

View File

@@ -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/<branch> [P] project1, project2 . <relative-time> |
| [==========> ] N/M tasks [...] |
+-------------------------------------------------------------------------+
```
### Metadata row elements
- `[git] cw/<branch>` — GitBranch icon + branch name. Hidden if no branch yet.
- `[P] project1, project2` — Folder icon + comma-separated project names.
Hidden if no projects linked.
- `. <relative-time>` — 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 `<ErrorState>` 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 `<EmptyState>` 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)

View File

@@ -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 | `<SaveIndicator>` 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 `<SaveIndicator>` 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 `<TaskModal>` 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`

View File

@@ -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: <file-path> 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`

View File

@@ -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 Page Outlet> |
| |
+=============================================================================+
```
`/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
```
+------------------------------------------------------------------------+
| <project-name> |
| <project-url> |
| |
| Default branch: <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`

View File

@@ -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. `<SaveIndicator>`
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. `<EmptyState>`
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., <Inbox />, <ListTodo />, <Users />
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 `<Button>` variant
### Usage
| Page | Title | Has Action? |
|------|-------|-------------|
| Initiatives list (no data) | "No initiatives yet" | Yes — "New Initiative" |
| Initiatives list (no match) | "No matching initiatives" | No — text link instead |
| Plan tab (no phases) | "No phases yet" | Yes — "Add Phase" |
| Execution tab (no tasks) | "No tasks to execute" | No |
| Agents page (no agents) | "No agents running" | No |
| Inbox page (no conversations) | "No conversations yet" | No |
| Settings > Accounts (none) | "No accounts registered" | Yes — "Add Account" |
### Source
- `packages/web/src/components/EmptyState.tsx` (proposed)
---
## 3. `<ErrorState>`
Centered error display with optional retry action. Used when a data fetch
fails or a section encounters an unrecoverable error.
### Props
```ts
interface ErrorStateProps {
message: string; // e.g., "Failed to load initiatives"
onRetry?: () => void; // If provided, shows Retry button
}
```
### With retry
```
+-------------------------------------------+
| |
| [AlertCircle] |
| Failed to load initiatives |
| |
| [ Retry ] |
| |
+-------------------------------------------+
```
### Without retry (navigation fallback)
```
+-------------------------------------------+
| |
| [AlertCircle] |
| Error loading initiative |
| Could not fetch initiative data. |
| |
| [ Back to Initiatives ] |
| |
+-------------------------------------------+
```
Note: The "without retry" variant is not a prop configuration of `<ErrorState>`
itself. Pages that need navigation instead of retry render `<ErrorState>`
without `onRetry` and add their own navigation button below it.
### Styling
- Container: `flex flex-col items-center justify-center py-16 text-center`
- Icon: `h-12 w-12 text-destructive mb-4` (AlertCircle from lucide-react)
- Message: `text-lg font-medium text-foreground mb-4`
- Retry button: `<Button variant="outline">Retry</Button>`
### 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. `<CommandPalette>`
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. `<ThemeToggle>`
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 `<html>` 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 | | | | | | | | | |

769
docs/wireframes/v2/theme.md Normal file
View File

@@ -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
<span className="bg-status-active-bg text-status-active-fg border border-status-active-border">
[RUNNING]
</span>
<span className="h-2 w-2 rounded-full bg-status-success-dot" /> {/* 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 `<html>` 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 `<head>` in `index.html` before any stylesheets:
```html
<script>
(function() {
var t = localStorage.getItem('cw-theme') || 'system';
var d = t === 'dark' || (t === 'system' && matchMedia('(prefers-color-scheme: dark)').matches);
if (d) document.documentElement.classList.add('dark');
})();
</script>
```
### 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<Theme>(
() => (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 (
<ThemeContext.Provider value={{ theme, setTheme, isDark }}>
{children}
</ThemeContext.Provider>
);
}
```
---
## 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 `<head>` |
| `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)