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:
51
docs/wireframes/v2/README.md
Normal file
51
docs/wireframes/v2/README.md
Normal 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 |
|
||||
289
docs/wireframes/v2/agents.md
Normal file
289
docs/wireframes/v2/agents.md
Normal 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`
|
||||
178
docs/wireframes/v2/app-layout.md
Normal file
178
docs/wireframes/v2/app-layout.md
Normal 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)
|
||||
234
docs/wireframes/v2/content-tab.md
Normal file
234
docs/wireframes/v2/content-tab.md
Normal 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`
|
||||
358
docs/wireframes/v2/dialogs.md
Normal file
358
docs/wireframes/v2/dialogs.md
Normal 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`
|
||||
309
docs/wireframes/v2/execution-tab.md
Normal file
309
docs/wireframes/v2/execution-tab.md
Normal 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
260
docs/wireframes/v2/inbox.md
Normal 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`
|
||||
209
docs/wireframes/v2/initiative-detail.md
Normal file
209
docs/wireframes/v2/initiative-detail.md
Normal 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)
|
||||
267
docs/wireframes/v2/initiatives-list.md
Normal file
267
docs/wireframes/v2/initiatives-list.md
Normal 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)
|
||||
284
docs/wireframes/v2/plan-tab.md
Normal file
284
docs/wireframes/v2/plan-tab.md
Normal 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`
|
||||
205
docs/wireframes/v2/review-tab.md
Normal file
205
docs/wireframes/v2/review-tab.md
Normal 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`
|
||||
186
docs/wireframes/v2/settings.md
Normal file
186
docs/wireframes/v2/settings.md
Normal 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`
|
||||
440
docs/wireframes/v2/shared-components.md
Normal file
440
docs/wireframes/v2/shared-components.md
Normal 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
769
docs/wireframes/v2/theme.md
Normal 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)
|
||||
Reference in New Issue
Block a user