# PR3 — Build Plan · Warm-Hive Redesign · Screens 01 / 02 / 03 > Rebuilds three screens to the warm-Hive design **inside** the existing `apps/web` > stack (React 19 + TS + Vite + Tailwind 3 + shadcn/ui + React Router 6). Recreate the > concept — do **not** paste the HTML. One branch, one PR, commit-per-screen. > > Synthesised from the four recon files > (`docs/redesign-warm-hive/pr3-recon/{home,chat,workspace,primitives}.md`), > `BUILD-PLAN.md` §6, and `docs/design_handoff_waggle_app/{SCREENS.md §01/02/03, DESIGN_POV.md}`. > Citations are `file:line` against the branch `feature/warm-hive-redesign`. --- ## 0. Scope + ground rules ### In scope (3 screens, ship-variation locked) - **Screen 01 — Home / Cockpit → Variation A "Editorial"** (`HomeCockpit.tsx`). - **Screen 02 — Chat / agent runtime → Variation B "Split work canvas"** (`ChatApp.tsx` inside the kept-alive `ChatHost` portal subtree). - **Screen 03 — Workspace → Variation A "Overview + tabs", Memory stays a tab** (`WorkspaceDesktopApp.tsx` + `WorkspaceRoute.tsx`). ### Out of scope (do NOT build here) - **PR3.5 — Memory-Trust layer (screen 19, DESIGN_POV #1).** Confidence/freshness on every memory, Forget/Correct, stale-review, "why did you do that?" trace. Separate later PR. **PR3 hooks to leave** (cheap, non-fabricating): - Use the existing `ConfidenceBadge` + `EvidenceChip`/`EvidencePanel`/`DetailDrawer` primitives (primitives.md §2) for fact rows and activity-stream provenance, so PR3.5 can attach forget/correct affordances to the **same** components rather than re-laying them out. - Make every "What Waggle knows" fact row (Workspace) and every memory step (Chat) **clickable into a `DetailDrawer`** placeholder (or no-op with a `data-memory-id` attribute) so PR3.5 wires the trace/correct flow without a re-layout. - Keep the J08 review banner + `home-cockpit-review-cta` event payload intact (it is the Home entry-point into the unreviewed-memory queue PR3.5 expands). - **Do not** invent confidence/freshness numbers in PR3 — render provenance/confidence UI only where real data exists (see §5); leave the affordance, not fake data. - **PR4+ surfaces** (Marketplace, Settings, Onboarding, Launcher, Power surfaces, etc.) — untouched. PR3 only restyles the three named screens; the 5-item spine + ⌘K (PR2) and tokens/ThemeProvider/fonts (PR1) are **already shipped** — reuse, do not re-author. ### Two deferred PR1 LOW items to clear in PR3 (BUILD-PLAN §9) 1. **Chat spine no-workspace dead-click.** `routeFor('chat')` falls back to `/home` when there is no real active workspace, so the Chat spine item isn't highlighted (Home wins) and the click feels dead. **Fix in PR3:** when `!hasRealActiveWorkspace`, route the Chat spine item to the workspace switcher / Home selector and/or dim it. Touch `Sidebar.tsx` consumer wiring (the spine-item builder), not the screen bodies. 2. **Sidebar user-row `userName={null}`** renders "Account" + "W". A real name exists in `HomeBriefing.userName` (home.ts:261-270). **Fix in PR3:** thread `briefing.userName` through to the Sidebar user row once Home loads the briefing (or via the existing identity surface). This is the "user-identity surface lands (PR3)" note. ### Execution model + process - **In-place refactor of `apps/web`.** No Lovable, no new app. Restyle/replace the render bodies of the three existing components; keep route wrappers, data hooks, load() states, and all test-locked testids/behaviours. - **One branch (`feature/warm-hive-redesign`), one PR, commit-per-screen.** Suggested commits: `Phase 0 primitives`, `Phase A Home`, `Phase B Chat`, `Phase C Workspace`, `LOW fixes (chat no-ws + userName)`, then verification fixups. - **Dark-first** (BUILD-PLAN §6); light mode inherits via tokens — every new color must resolve from a `var(--token)` so the WCAG ratchet (`light-mode-tokens.test.ts`) stays green. Honor `prefers-reduced-motion` on every animated atom. - **Reuse > rebuild; degrade gracefully on missing data; surgical changes** (CLAUDE.md §3). --- ## 1. Phase 0 — Shared primitives first (build BEFORE the screens) All warm tokens + utility classes the design references **already exist** (primitives.md §1: `--bg --surface* --line* --text* --honey* --work --intel --healthy --attention --risk` + washes, `--r/-lg/-xl`, shadows, `.hex`, `.comb`, `.dot-live`, Hanken/JetBrains). **Do not re-add tokens.** Build only the **net-new composite atoms** (primitives.md §3) that no component provides today, in a **new folder `apps/web/src/components/os/warm/`**. Keep each < 80 LOC, token-driven, a11y-labeled, `prefers-reduced-motion`-safe. **Build order** (shared atoms first, then screen-specific composites): | # | Primitive | File | Minimal prop API | Consumed by | |---|---|---|---|---| | 0.1 | `HexAvatar` | `os/warm/HexAvatar.tsx` | `label: string; size?: number; gradient?: boolean; className?` — `.hex` clip + honey gradient + `#1a1407` initial; extract from `Sidebar.tsx:116` | **Home** (ws cards), **Chat** (context header + bot avatar), **Workspace** (46px header) | | 0.2 | `SectionLabel` | `os/warm/SectionLabel.tsx` | `children; className?` — 11px mono, uppercase, `.12–.14em` tracking, `--text-dim`, trailing hairline rule (ref `Sidebar.tsx:102`) | all 3 screens | | 0.3 | `DotLive` | `os/warm/DotLive.tsx` | `tone?: 'healthy'\|'attention'\|'risk'\|'work'\|'intel'\|'honey'; className?` — colored dot + `.dot-live` breathe; `prefers-reduced-motion` | **Home** greeting, **Chat** model pill, **Workspace** "agent live" | | 0.4 | `ProvenanceLine` | `os/warm/ProvenanceLine.tsx` | `source: string; when?: string; onClick?` — `⬡ source · when`, mono `--intel`; thin wrapper over `EvidenceChip` styled to `--intel` | **Workspace** fact + recent-work rows; **Chat** activity steps | | 0.5 | `RunChip` | `os/warm/RunChip.tsx` | `label: string; tone?: StatusTone` — status dot + label inline chip | **Home** overnight hero | | 0.6 | `IconTile` | `os/warm/IconTile.tsx` | `icon: ElementType; tone?: StatusTone; size?: number` — tinted (`*-wash`) rounded icon tile | **Home** "Waggle suggests"; **Workspace** recent-work ext tiles | | 0.7 | `HexCheckTile` | `os/warm/HexCheckTile.tsx` | `tone?; size?` — small `.hex` tile w/ check glyph | **Workspace** "What Waggle knows" fact rows | | 0.8 | `StreakChip` | `os/warm/StreakChip.tsx` | `days: number; weekDots?: boolean[]; className?` — 🔥 + "N-day streak", `--honey-wash`/`--honey-line` | **Home** greeting (mock data — §5) | | 0.9 | `ModelPill` | `os/warm/ModelPill.tsx` | `mode?: string; model: string; onClick?; title?` — "auto · Claude Sonnet" pill + healthy `DotLive` | **Chat** context header | | 0.10 | `OvernightHero` | `os/warm/OvernightHero.tsx` (Home composite) | `eyebrow; statement: ReactNode; runs: RunChipProps[]` — `--r-xl` gradient card + honey radial glow; renders gracefully with `runs=[]` | **Home** (composes `RunChip`) | | 0.11 | `AskBar` | `os/warm/AskBar.tsx` | `placeholder?; onSubmit: (text) => void; cmdkHint?: boolean; onPlus?` — full-width pill, honey `+`, ⌘K hint, honey send | **Home** (Chat composer reuses the send affordance only) | | 0.12 | `ActivityStream` | `os/warm/ActivityStream.tsx` (Chat composite) | `summary: string; durationMs?; steps: {tone; text: ReactNode; provenance?: {source; when?}}[]; defaultOpen?: boolean` — collapsible `--bg-2` card, violet spark, per-step `DotLive` + `ProvenanceLine` | **Chat** activity card | | 0.13 | `InlineApprovalCard` | `os/warm/InlineApprovalCard.tsx` | reuse `ApprovalRequest`; `onApprove; onDecline; alwaysAllow?` — `--honey-wash` bg, attention border, warning icon (inline, NOT a modal); shares risk vocab w/ `risk-display.tsx`/`ApprovalModal` `RISK_LABELS` | **Chat** approval card | **Reuse as-is (do NOT rebuild — primitives.md §2):** `ConfidenceBadge`, `EvidenceChip`, `EvidencePanel`, `StatusBadge`, `ApprovalModal` (vocab source), `DetailDrawer`, all shadcn `ui/` primitives (tabs/card/button/badge/input/textarea/tooltip/popover/scroll-area), `Sidebar` active-state recipe, `command-catalog.ts` (⌘K vocabulary). **Phase 0 footprint: ~13 new files (all in `os/warm/`), 0 modified.** Verify after Phase 0: `tsc -p apps/web/tsconfig.app.json` → 0; new atoms render in isolation (optional smoke test file). No screen wiring yet. --- ## 2. Phase A — Home (Editorial) **Primary file:** `apps/web/src/components/os/apps/HomeCockpit.tsx`. **Keep the component, its route (`HomeRoute.tsx:13`), props, `load()` (`:467-497`), the `connecting` race guard (`:499-507`), and all five render states.** Restyle/replace **only the normal-render body** (`HomeCockpit.tsx:580`, `data-testid="home-cockpit"`) and the sub-state shells. ### A.1 Section build order (top → bottom, home.md §4) 1. **Container** — swap `max-w-4xl mx-auto p-6` (`:580`) → centered `max-w-[920px] mx-auto px-8 pt-[46px] pb-20`. Optional `.comb` honeycomb layer behind. 2. **Greeting** (rework `GreetingHeader` `:136-159`): mono date row (`--honey`, uppercase, `.1em` tracking) with leading `DotLive tone="healthy"` + right-aligned `StreakChip`; two-line H1 (Hanken 600, `clamp(34px,5vw,52px)`) with the keyword honey-spanned. **Keep the human-date rendering** (`formatBriefingDate` semantics, test 6). 3. **J08 review banner** (`:601-625`) — **KEEP intact** (testid `home-cockpit-review-banner`, cta `home-cockpit-review-cta`, event `{appId:'memory',filter:'unreviewed'}`); may restyle to a warm attention row. Test-locked (test 2/3). 4. **`OvernightHero`** (new, replaces `OvernightPanel` `:230-288`): eyebrow "While you slept" + composed story sentence + ≤4 `RunChip`s. **Must render when overnight is null/empty** (the test stubs `getHomeOvernight → null`) — degrade to a quiet "Nothing ran overnight" line, never crash. 5. **"Pick up where you left off"** (restyle `RecentWorkspacesPanel` `:162-227`): change head copy "You were working on" → **"Pick up where you left off"** via `SectionLabel`; 2-col grid of warm `.ws-card` with `HexAvatar` (glyph from `name[0]`), title/time/summary, **"Continue →"**, optional status badge. **Keep `WorkspaceActionsMenu` kebab** (real G1 CRUD). Returns null at 0 cards. 6. **"Waggle suggests"** (restyle `SuggestedActionsPanel` `:321-343`): head → **"Waggle suggests"**; pills → stacked `.move` rows (`IconTile` + title + composed sub-line + sliding arrow). Click → `onContinue(workspaceId, sessionId)`. 7. **"Up next"** (`UpNextPanel` `:291-318`): **KEEP conditional** — testid `home-cockpit-upnext` present-when-items / absent-when-empty is test-locked (test 4/5). Restyle only. 8. **`AskBar`** (new, replaces `QuickCapturePanel` `:346-434`): pill + honey "+" + ⌘K hint + honey send. Wire send → `onContinue`/new-chat; `+` keeps quick-capture (`adapter.quickCapture`) or opens ⌘K (`CommandCenter`). 9. **Drop:** `ActiveModelsTile` (`:437-448`, never populated) and the segmented QuickCapture panel. ### A.2 Data wiring (home.md §2 — real vs graceful-mock) - **REAL (wire directly):** `briefing.greeting`, `briefing.userName` (also feeds the LOW user-row fix §0), `briefing.date` (humanize + add time), `recentWorkspaces[]` (name/group/summary/`formatRelative(lastActive)`/pendingCount), `suggestedActions[].label`, `upNext[]`, `overnight.{consolidated,artifactsCreated,automationsCompleted,failures}`, `needsReviewCount`. - **COMPOSED client-side (honest, from real fields):** overnight **story sentence** from counts + failures; run-chip labels for `"N memories consolidated"`/`"N artifacts created"`/ `"N export failed"`; suggestion **sub-lines** from workspace name + relative time. Hex glyph from `name[0]`. - **MOCK — no data source anywhere (flag with `// TODO(backend): no data source yet`):** - 🔥 **streak** value (no `streak` field exists; habit-loop SCREENS §15 unimplemented). - "You're **ahead** of yesterday" second H1 line (no ahead-vs-yesterday signal). - "Teardown drafted · 9 competitors" first run chip + the "9 competitors" count (no per-run label field). - Workspace-card "agent live" badge (no per-card live flag) → derive or omit, don't fake. ### A.3 Exact copy (SCREENS §01 / home.html) - Date row e.g. `"Friday · June 14 · 8:42"` (compose from `briefing.date`); streak `"🔥 12-day streak"`. - H1 line 1 = `briefing.greeting` ("Good morning, Mara."); line 2 = "You're **ahead** of yesterday." (mock). - Eyebrow "While you slept"; story "Waggle finished the **Q2 competitor teardown**, folded **14 new memories** into the hive, and ran into **one snag** worth a look." (numbers from real counts). - Run chips: "Teardown drafted · 9 competitors" (healthy), "14 memories consolidated" (intel), "2 artifacts created" (work), "1 export failed" (risk). - Section heads "Pick up where you left off" / "Waggle suggests". - Ask-bar placeholder "Start something new — \"draft the board update from this week's work\"…". ### A.4 Test contract to keep green — `apps/web/src/test/p2-home-desktop.test.tsx` 1. `home-cockpit` root testid present after load. 2. `home-cockpit-review-banner` text "3 imported memories need your review" + `home-cockpit-review-cta` click dispatches `waggle:open-app {appId:'memory',filter:'unreviewed'}`. 3. Banner omitted when `needsReviewCount` undefined. 4. `home-cockpit-upnext` absent when `upNext` empty/undefined. 5. `home-cockpit-upnext` contains item label when ≥1 item. 6. Raw ISO must NOT appear; date humanized via `toLocaleDateString`. - Must render `home-cockpit` with **all-empty arrays** + `getHomeOvernight → null` (no crash). - Other testids (`home-cockpit-overnight/-suggested/-continue-*`, quickcapture) are **not** asserted → free to rename/remove. ### A.5 Files touched (Phase A) - **Modify:** `HomeCockpit.tsx` (normal-render body + sub-states). Optionally co-locate presentational subcomponents in a `home/` dir (CLAUDE.md "many small files") — at minimum `OvernightHero`, `AskBar`, `StreakChip` come from `os/warm/`. - **Touch (LOW fix #2):** Sidebar user-row wiring to pass `briefing.userName`. - **Verify:** `tsc -p apps/web/tsconfig.app.json` 0; `npm run test --root apps/web` → `p2-home-desktop.test.tsx` green; light ratchet green; live smoke (cold load + empty arrays + null overnight render `home-cockpit`). **Phase A footprint: ~1–4 files modified (HomeCockpit + optional `home/` subcomponents + Sidebar wiring), 0–3 created.** --- ## 3. Phase B — Chat (split work-canvas) **Primary file:** `apps/web/src/components/os/apps/ChatApp.tsx`. The thread, tool cards, artifact card, approval gate, model picker, persona pill, composer, and SSE/block reduction (`useChat.ts`) **all already exist and are design-compatible** — most of Phase B is RESTYLE + token migration. Two things are net-new: the **right work canvas** and the **collapsible ActivityStream**. ### B.1 Where the canvas lives (chat.md §1 — critical) Append `` as a **sibling of ChatApp's chat column** inside ChatApp's existing flex root — root `
` (`ChatApp.tsx:697`), chat column `
` (`:744`). This keeps the canvas **inside the kept-alive portal subtree** (`ChatHost.tsx`), so its state survives navigation for free. **Do NOT** add the canvas as a `WorkspaceDesktopApp` pane — that breaks the `chatSlot`/`activeTab` contract pinned by `p1a-chat-state.test.tsx` and lives outside keep-alive. ### B.2 Build order 1. **Token migration** — Hive-DS classes (`bg-primary`, `text-emerald-400`, `bg-secondary`, `border-border`) → warm tokens (`--surface`, `--honey`, `--intel`, `--bg-2`, `--line-soft`). 2. **Context header** (restyle `chat-header` `:746`): `HexAvatar` "C" + workspace name + mono sub "workspace · N memories · N sources" + right-side `ModelPill` (the current model picker `:931` restyled with a healthy `DotLive` + "auto ·" prefix) + Memory icon button (the current Memory chip `:815`). Autonomy/storage/team chips not in the design header → fold into the overflow menu via the existing `chat-header-layout.ts` decision. 3. **Thread restyle** (`:1050-1191`): - User bubble (`:1108`): `bg-primary rounded-xl` → `--surface` bubble, asymmetric radius `4px 14px 14px 14px`, 15.5px/1.55. - Bot message: `HexAvatar` "W" + meta line "Waggle · {persona} · {model}"; honey `◆` bullets. 4. **`ActivityStream`** (new, chat.md §5 #3): a grouping wrapper in `chat-blocks/` — `BlockRenderer` collapses a consecutive run of `step` (and optionally tool) blocks into one collapsible card: violet spark + "Worked across memory, web & files · N steps · Ns" header + chevron. Derive step count + duration from the grouped blocks. **Default-open on the active turn** (track `isStreaming`/active turn), collapsed on prior turns. Per-step `DotLive` + `ProvenanceLine` **only when source data exists** — do NOT fabricate `⬡ mem://hive` (SSE `step` carries no source field today; flag to backend in §5). 5. **`InlineApprovalCard`** (restyle `ApprovalGate` `:223`): `--honey-wash` bg + attention border + warning icon; copy "Approve before I leave your machine" + mono external target + "Approve & export"/"Not now". **Keep `RiskBadge` + the Always-allow gating logic.** 6. **`ChatWorkCanvas`** (new `