# Shared Components (v2) ### NEW — no v1 equivalent Reusable component specifications used across multiple pages. Each component includes: description, props/API, ASCII mockups of all states, usage locations, and proposed source file path. --- ## 1. `` Inline status indicator for auto-save operations. Shows current save state with icon + label, auto-hides on success. ### Props ```ts interface SaveIndicatorProps { status: 'idle' | 'saving' | 'saved' | 'error'; onRetry?: () => void; } ``` ### States ``` Idle: (hidden — renders nothing) Saving: [spinner] Saving... Saved: [v] Saved <- fades out after 2s, transitions to idle Error: [!] Failed to save [retry] ``` ### Detailed ASCII **Saving** ``` +---------------------------+ | [spinner] Saving... | +---------------------------+ ``` - `text-muted-foreground text-sm` - Spinner is a 14px animated SVG **Saved** ``` +---------------------------+ | [v] Saved | +---------------------------+ ``` - `text-green-600 text-sm` - Checkmark icon, 14px - Fades to `opacity-0` over 300ms after 2s delay, then sets status to `idle` **Error** ``` +--------------------------------------+ | [!] Failed to save [retry] | +--------------------------------------+ ``` - `text-destructive text-sm` - Alert icon, 14px - `[retry]` is a text button: `text-destructive underline cursor-pointer` - Does not auto-hide — persists until retry succeeds or user navigates away ### Usage | Page | Location | |------|----------| | Content tab | Top-right of Tiptap editor toolbar (see content-tab.md) | | Plan tab | Top-right of phase description editor (see plan-tab.md) | ### Source - `packages/web/src/components/SaveIndicator.tsx` (proposed) --- ## 2. `` Centered placeholder shown when a list or section has no items. Configurable icon, messaging, and optional CTA button. ### Props ```ts interface EmptyStateProps { icon: React.ReactNode; // e.g., , , title: string; // e.g., "No initiatives yet" description?: string; // e.g., "Create an initiative to get started." action?: { label: string; // e.g., "New Initiative" onClick: () => void; }; } ``` ### Default (with action) ``` +-------------------------------------------+ | | | [icon] | | No phases yet | | Add a phase to start planning. | | | | [ Add Phase ] | | | +-------------------------------------------+ ``` ### Without action ``` +-------------------------------------------+ | | | [icon] | | No conversations yet | | Questions from agents appear here. | | | +-------------------------------------------+ ``` ### Without description ``` +-------------------------------------------+ | | | [icon] | | No agents running | | | +-------------------------------------------+ ``` ### Styling - Container: `flex flex-col items-center justify-center py-16 text-center` - Icon: `h-12 w-12 text-muted-foreground mb-4` - Title: `text-lg font-medium text-foreground mb-1` - Description: `text-sm text-muted-foreground mb-6` - Action button: default shadcn `` ### Usage | Page | Message | Has Retry? | |------|---------|------------| | Initiatives list | "Failed to load initiatives" | Yes | | Initiative detail | "Error loading initiative" | No (nav button) | | Review tab | "Failed to load proposals" | Yes | | Agents page | "Failed to load agents" | Yes | | Settings page | "Failed to load settings" | Yes | ### Source - `packages/web/src/components/ErrorState.tsx` (proposed) --- ## 4. `` Global search overlay triggered by keyboard shortcut. Provides quick navigation to initiatives, agents, tasks, and settings pages. ### Trigger - `Cmd+K` (Mac) / `Ctrl+K` (Windows) - Header button `[cmd-k]` (see app-layout.md) ### Props ```ts interface CommandPaletteProps { open: boolean; onOpenChange: (open: boolean) => void; } ``` Internally fetches data via tRPC queries (initiatives, agents, tasks). ### Default State (open, empty query) ``` +----------------------------------------------------------+ | [search] Search everything... [Esc] | | --------------------------------------------------------| | Recent | | -> Auth System Overhaul | | -> Agents | | -> Settings | +----------------------------------------------------------+ ``` Shows recent navigation history when search is empty. ### With Search Results ``` +----------------------------------------------------------+ | [search] auth___ [Esc] | | --------------------------------------------------------| | Initiatives | | Auth System Overhaul [ACTIVE] | | --------------------------------------------------------| | Tasks | | Implement auth middleware [execute] 3/7 | | Review auth token flow [verify] done | | --------------------------------------------------------| | Agents | | blue-fox-7 (auth middleware) [RUNNING] | +----------------------------------------------------------+ ``` ### No Results ``` +----------------------------------------------------------+ | [search] xyznonexistent___ [Esc] | | --------------------------------------------------------| | | | No results for "xyznonexistent" | | | +----------------------------------------------------------+ ``` ### Keyboard Navigation ``` [search] auth___ [Esc] --------------------------------------------------------- Initiatives > Auth System Overhaul [ACTIVE] <- highlighted --------------------------------------------------------- Tasks Implement auth middleware [execute] 3/7 ``` - `>` indicates the currently highlighted item - `Arrow Up` / `Arrow Down` — move highlight - `Enter` — navigate to highlighted item, close palette - `Esc` — close palette - Type to filter — results update as you type (debounced 100ms) ### Result Groups | Group | Source | Display | |-------|--------|---------| | Initiatives | `listInitiatives` query | Name + status badge | | Tasks | `listTasks` query (across initiatives) | Name + category + status | | Agents | `listAgents` query | Name + task description + status badge | | Pages | `Go to Initiatives`, `Go to Agents`, etc. | Static navigation items | Groups are hidden when they have 0 matches. Max 5 items per group. ### Overlay Behavior - Renders as a centered modal with backdrop (`bg-black/50`) - Width: `max-w-lg` (512px) - Position: `top-[20%]` of viewport - Backdrop click closes the palette - Focus is trapped inside the modal while open - Search input auto-focuses on open ### Source - `packages/web/src/components/CommandPalette.tsx` (proposed) --- ## 5. `` 3-state segmented control for switching between light, system, and dark color schemes. Persists selection to `localStorage`. ### Props ```ts // No props — reads/writes theme from ThemeProvider context // ThemeProvider wraps the app root type Theme = 'light' | 'system' | 'dark'; ``` ### States ``` Light active: [*sun*] [monitor] [moon] System active: [sun] [*monitor*] [moon] Dark active: [sun] [monitor] [*moon*] ``` ### Detailed ASCII **Light mode selected** ``` +-----+----------+---------+ | sun | monitor | moon | +-----+----------+---------+ ^^^ filled bg, active text ``` **System mode selected (default)** ``` +-----+----------+---------+ | sun | monitor | moon | +-----+----------+---------+ ^^^^^^^^ filled bg, active text ``` **Dark mode selected** ``` +-----+----------+---------+ | sun | monitor | moon | +-----+----------+---------+ ^^^^^^ filled bg, active text ``` ### Styling - Container: `inline-flex rounded-lg bg-muted p-0.5 gap-0.5` - Segment (inactive): `px-2 py-1 rounded-md text-muted-foreground hover:text-foreground` - Segment (active): `px-2 py-1 rounded-md bg-background text-foreground shadow-sm` - Icons: 16px lucide-react icons (`Sun`, `Monitor`, `Moon`) - No text labels — icons only in the header (compact) ### Tooltip on hover ``` [sun] -> "Light" [monitor] -> "System" [moon] -> "Dark" ``` ### Persistence - Stored in `localStorage` key `cw-theme` - Default: `system` - On load: reads `localStorage`, applies `class="dark"` to `` if theme is `dark` or if theme is `system` and `prefers-color-scheme: dark` ### Usage | Page | Location | |------|----------| | App layout (header) | Right cluster, between Cmd+K button and health dot | See app-layout.md for header placement. ### Source - `packages/web/src/components/ThemeToggle.tsx` (proposed) - `packages/web/src/providers/ThemeProvider.tsx` (proposed) --- ## Cross-Reference Index Quick lookup of which shared components are used on each page. | Component | app-layout | initiatives-list | initiative-detail | content-tab | plan-tab | execution-tab | review-tab | agents | inbox | settings | |-----------|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| | SaveIndicator | | | | x | x | | | | | | | EmptyState | | x | | | x | x | | x | x | x | | ErrorState | | x | x | | | | x | x | | x | | CommandPalette | x | | | | | | | | | | | ThemeToggle | x | | | | | | | | | |