Files
Codewalkers/docs/wireframes/v2/shared-components.md
Lukas May 478a7f18e9 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)
2026-03-02 18:13:17 +09:00

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