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

12 KiB

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

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

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

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

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

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