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)
441 lines
12 KiB
Markdown
441 lines
12 KiB
Markdown
# 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 | | | | | | | | | |
|