Files
Codewalkers/docs/frontend.md

217 lines
13 KiB
Markdown

# Frontend
`apps/web/` — React web UI for managing initiatives, agents, and content.
## Tech Stack
| Technology | Purpose |
|-----------|---------|
| React 19 | UI framework |
| TanStack Router | File-based routing |
| tRPC React Query | Type-safe API client with caching |
| Tailwind CSS | Utility-first styling |
| shadcn/ui | Component library (button, card, dialog, dropdown, input, label, textarea, badge, sonner, tooltip) |
| Tiptap | Rich text editor (ProseMirror-based) |
| Lucide | Icon library |
| Geist Sans/Mono | Typography (variable fonts in `public/fonts/`) |
| react-window 2.x | Virtualized list rendering for large file trees in ReviewSidebar |
## Design System (v2)
Theme spec: `docs/wireframes/v2/theme.md`
- **Brand**: Indigo (#6366F1) — `--primary` is indigo, not black
- **Dark mode**: 3-state toggle (light/system/dark), persisted in `localStorage('cw-theme')`
- **Status tokens**: 6 semantic statuses (active/success/warning/error/neutral/urgent) with bg/fg/border/dot variants. Use `bg-status-{status}-bg`, `text-status-{status}-fg`, etc.
- **Terminal tokens**: Always-dark surface for agent output. Use `bg-terminal`, `text-terminal-fg`, etc.
- **Diff tokens**: `bg-diff-add-bg`, `text-diff-remove-fg`, etc.
- **Shadows**: 5-level system (xs-xl). Dark mode uses inset highlights + ambient glow.
- **Transitions**: `duration-fast`, `duration-normal`, `ease-default`, `ease-spring`
- **Z-index**: Named scale from `z-base` to `z-tooltip`
- **Radius**: 6px base (down from 8px)
- **Flash prevention**: Inline `<script>` in `index.html` reads theme before paint
### Status-to-Entity Mapping
Use `mapEntityStatus(rawStatus)` from `StatusDot.tsx` to convert raw entity statuses to semantic tokens. `StatusDot` and `StatusBadge` both use this automatically.
## Path Alias
`@/*` maps to `./src/*` (configured in `tsconfig.app.json`).
## Routes
| Route | Component | Purpose |
|-------|-----------|---------|
| `/` | `routes/index.tsx` | Dashboard / initiative list |
| `/hq` | `routes/hq.tsx` | Headquarters — action items requiring user attention |
| `/initiatives/$id` | `routes/initiatives/$initiativeId.tsx` | Initiative detail (tabbed) |
| `/agents` | `routes/agents.tsx` | Agent list with Output / Details tab panel |
| `/settings` | `routes/settings/index.tsx` | Settings page |
## Initiative Detail Tabs
The initiative detail page has three tabs managed via local state (not URL params):
1. **Content Tab** — Page tree + Tiptap editor, proposal review
2. **Execution Tab** — Pipeline visualization, phase management, task dispatch
3. **Review Tab** — Pending proposals from agents
## Component Inventory (74 components)
### Core Components (`src/components/`)
| Component | Purpose |
|-----------|---------|
| `InitiativeCard` | Initiative list card with activity indicator (dot + label + phase progress), overflow menu |
| `InitiativeHeader` | Initiative name, project badges, inline-editable execution mode & branch |
| `InitiativeContent` | Content tab with page tree + editor |
| `StatusDot` | Small colored dot using status tokens, with pulse animation |
| `StatusBadge` | Colored badge using status tokens |
| `TaskRow` | Task list item with status, priority, category |
| `QuestionForm` | Agent question form with options |
| `AgentDetailsPanel` | Details tab for agent right-panel: metadata, input files, effective prompt |
| `InboxDetailPanel` | Agent message detail + response form |
| `ProjectPicker` | Checkbox list for project selection |
| `RegisterProjectDialog` | Dialog to register new git project |
| `Skeleton` | Loading placeholder with shimmer animation |
| `SkeletonCard` | Composite skeleton layouts (agent-card, initiative-card, etc.) |
| `EmptyState` | Shared empty state with icon, title, description, action |
| `ErrorState` | Shared error state with retry |
| `SaveIndicator` | Saving/saved/error status indicator |
| `CommandPalette` | Cmd+K search palette (initiatives, agents, navigation) |
| `ThemeToggle` | 3-state theme toggle (Sun/Monitor/Moon) |
| `NavBadge` | Numeric badge for nav items |
| `KeyboardShortcutHint` | Formatted keyboard shortcut display |
| `ConnectionBanner` | Offline/reconnecting state banner |
| `HealthDot` | Server health indicator with tooltip |
| `BrowserTitleUpdater` | Dynamic document.title with agent counts |
### Editor Components (`src/components/editor/`)
| Component | Purpose |
|-----------|---------|
| `TiptapEditor` | Core rich text editor wrapper |
| `PageEditor` | Page content editor with auto-save |
| `PhaseContentEditor` | Phase content editor |
| `ContentProposalReview` | Accept/dismiss proposals from agents |
| `SlashCommandMenu` | Slash command popup in editor |
### Execution Components (`src/components/execution/`)
| Component | Purpose |
|-----------|---------|
| `ExecutionTab` | Main execution view container |
| `ExecutionContext` | React context for execution state |
| `PhaseDetailPanel` | Phase detail with tasks, dependencies, plan |
| `PhaseSidebar` | Phase list sidebar |
| `TaskDetailPanel` | Task detail with agent status, output |
### Pipeline Components (`src/components/pipeline/`)
| Component | Purpose |
|-----------|---------|
| `PipelineTab` | Execution tab entry — fetches tasks, phase deps, task deps |
| `PipelineGraph` | Horizontal DAG of phase columns with connectors |
| `PipelineStageColumn` | Single depth column containing phase groups |
| `PipelinePhaseGroup` | Phase card with status border accent, progress bar, TaskGraph |
### Review Components (`src/components/review/`)
| Component | Purpose |
|-----------|---------|
| `ReviewTab` | Review tab container — orchestrates header, diff, sidebar, and preview. Phase-level review has threaded inline comments (with reply support) + Request Changes; initiative-level review has Request Changes (summary prompt) + Push Branch / Merge & Push. Phase diff uses metadata-only `FileDiff[]` from `getPhaseReviewDiff`; commit diff parses `rawDiff` via `parseUnifiedDiff``FileDiffDetail[]`. Passes `commitMode`, `phaseId`, `expandAll` to DiffViewer |
| `ReviewHeader` | Consolidated toolbar: phase selector pills, branch info, stats (uses `totalAdditions`/`totalDeletions` props when available, falls back to summing files), preview controls, Expand all button, approve/reject actions |
| `ReviewSidebar` | VSCode-style icon strip (Files/Commits views) with file list, root-only comment counts, and commit navigation. FilesView uses react-window 2.x `List` for virtualized rendering when the row count exceeds 50 (dir-headers + file rows). Scroll position is preserved across Files ↔ Commits tab switches. Directories are collapsible. Clicking a file scrolls the virtual list to that row. |
| `DiffViewer` | Unified diff renderer with threaded inline comments (root + reply threads). Accepts `FileDiff[] | FileDiffDetail[]`, `phaseId`, `commitMode`, `expandAll` props |
| `CommentThread` | Renders root comment with resolve/reopen + nested reply threads (agent replies styled with primary border). Inline reply form |
| `ConflictResolutionPanel` | Merge conflict detection + agent resolution in initiative review. Shows conflict files, spawns conflict agent, inline questions, re-check on completion |
| `PreviewPanel` | Docker preview status: building/running/failed with start/stop (legacy, now integrated into ReviewHeader) |
| `ProposalCard` | Individual proposal display |
#### Syntax Highlighting (`use-syntax-highlight.ts` + `highlight-worker.ts`)
`useHighlightedFile(filePath, allLines)` returns `LineTokenMap | null`. Tokenisation runs off the main thread:
- **Worker path** (default): a module-level pool of 2 ES module Web Workers (`highlight-worker.ts`) each import shiki's `codeToTokens` dynamically. Requests are round-robined by `requestCount % 2`. Responses are correlated by UUID. Late responses after unmount are silently discarded via the `pending` Map.
- **Fallback path** (CSP / browser-compat): if `Worker` construction throws, `createHighlighter` is used on the main thread but processes 200 lines per chunk, yielding between chunks via `scheduler.yield()` or `setTimeout(0)`.
Callers receive `null` while highlighting is in progress and a populated `Map<lineNumber, ThemedToken[]>` once it resolves. `LineWithComments` already renders plain text when `null`, so no caller changes are needed.
Vite must be configured with `worker.format: 'es'` (added to `vite.config.ts`) for the worker chunk to bundle correctly alongside code-split app chunks.
### UI Primitives (`src/components/ui/`)
shadcn/ui components: badge (6 status variants + xs size), button, card, dialog, dropdown-menu, input, label, select, sonner, textarea, tooltip.
## Custom Hooks (`src/hooks/`)
| Hook | Purpose |
|------|---------|
| `useRefineAgent` | Manages refine agent lifecycle for initiative |
| `useConflictAgent` | Manages conflict resolution agent lifecycle for initiative review |
| `useDetailAgent` | Manages detail agent for phase planning |
| `useAgentOutput` | Subscribes to live agent output stream |
| `useChatSession` | Manages chat session for phase/task refinement |
| `useConnectionStatus` | Tracks online/offline/reconnecting state |
| `useGlobalKeyboard` | Global keyboard shortcuts (1-4 nav, Cmd+K) |
## Theme (`src/lib/theme.tsx`)
`ThemeProvider` wraps the app root. `useTheme()` returns `{ theme, setTheme, isDark }`. The provider listens for OS `prefers-color-scheme` changes when in `system` mode.
## tRPC Client
Configured in `src/lib/trpc.ts`. Uses `@trpc/react-query` with TanStack Query for caching and optimistic updates.
## Key User Flows
### Creating an Initiative
1. Dashboard → "New Initiative" → enter name, optional description, select projects
2. `createInitiative` mutation → auto-creates root page (seeded with description as tiptap content); if description provided, auto-spawns refine agent
3. Navigate to initiative detail page on success
### Managing Content (Pages)
1. Content tab → page tree sidebar
2. Click page → Tiptap editor loads content
3. Edit → auto-saves via `updatePage` mutation
4. Use slash commands for formatting
### Refining Content with AI
1. Content tab → "Refine" button
2. `spawnArchitectRefine` mutation → agent analyzes pages
3. Agent creates proposals (page edits, new phases, tasks)
4. Proposals appear in review section → accept/dismiss each
### Pipeline Visualization
1. Execution tab → pipeline DAG shows phases as nodes
2. Drag to add dependencies between phases
3. Per-phase Play button: if phase is `pending` (with non-detail tasks), approves then queues in one click; if already `approved`, just queues
4. "Execute N phases" top-level button: approves all `pending` phases that have tasks, then calls `queueAllPhases` — count includes both `pending` (with tasks) and `approved` phases
5. Tasks auto-queued when phase starts
### Detailing Phases
1. Select phase → "Detail" button
2. `spawnArchitectDetail` mutation → agent creates task proposals
3. Accept proposals → tasks created under phase
4. View tasks in phase detail panel
### Chat with Phase/Task
1. Execution tab → select phase → "Chat" button (or open task → "Chat" button in footer)
2. `ChatSlideOver` opens as right-side panel
3. Send message → `sendChatMessage` mutation → agent spawns (or resumes) in `'chat'` mode
4. Agent applies changes (create/update/delete phases, tasks, pages) → changeset created
5. Assistant message appears with inline changeset summary + revert button
6. Send next message → agent resumes → repeat
7. Close chat → session closed, agent dismissed
Components: `ChatSlideOver`, `ChatBubble`, `ChatInput`, `ChangeSetInline` in `src/components/chat/`.
## Shared Package
`packages/shared/` exports:
- `sortByPriorityAndQueueTime()` — priority-based task sorting
- `topologicalSort()` / `groupByPipelineColumn()` — phase DAG layout
- `InitiativeActivity` / `InitiativeActivityState` — server-computed activity state for initiative cards
- Shared type re-exports from `packages/shared/src/types.ts` (which re-exports from `apps/server/`)
## Initiative Activity Indicator
`listInitiatives` returns an `activity` field on each initiative, computed server-side from phase statuses via `deriveInitiativeActivity()` in `apps/server/trpc/routers/initiative-activity.ts`. This eliminates per-card N+1 `listPhases` queries.
Activity states (priority order): conflict agent > `archived` > active architect agents > `pending_review` > `executing` > `blocked` > `complete` > `ready` > `planning` > `idle`. Each state maps to a `StatusVariant` + pulse animation in `InitiativeCard`'s `activityVisual()` function. Active conflict agents (name starts with `conflict-`) are checked first — returning `resolving_conflict` (urgent variant, pulsing). Active architect agents (modes: discuss, plan, detail, refine) are checked next — mapping to `discussing`, `detailing`, `detailing`, `refining` states respectively — so auto-spawned agents surface activity even when no phases exist yet. `PhaseSidebarItem` also shows a spinner when a detail agent is active for its phase.