13 files reviewed with mission-control design lens. Key additions: - theme: extended indigo scale, 4-level surface hierarchy, 22 terminal tokens, transition/z-index/focus-visible token categories - All screens: keyboard shortcuts, loading/error/empty states hardened - 5 new shared components: StatusDot, SkeletonLoader, Toast, Badge, KeyboardShortcutHint - settings: expanded from 2 to 5 sub-pages (accounts, workspace, danger zone) - review-tab: 3-pane layout, inline comments, file nav, hunk controls - execution-tab: zoom, partial failure state, stale agent detection - dialogs: 2 bugs found (mutation locking, error placement) Total: 4,039 → 9,302 lines (+130% from review pass)
488 lines
21 KiB
Markdown
488 lines
21 KiB
Markdown
# App Layout (v2)
|
|
|
|
### Route: `*` (all pages)
|
|
### Source: `packages/web/src/routes/__root.tsx`
|
|
|
|
---
|
|
|
|
## v1 -> v2 Changes
|
|
|
|
| Aspect | v1 | v2 |
|
|
|--------|----|----|
|
|
| Header height | 56px + nav row | Single 48px row |
|
|
| Nav layout | Wordmark on row 1, tabs on row 2 | Logo + tabs + utilities all on one row |
|
|
| Badges | None | Live agent count on Agents, unresolved count on Inbox |
|
|
| Theme toggle | None | 3-state Sun/Monitor/Moon |
|
|
| Command palette | None | Cmd+K trigger in header |
|
|
| Health indicator | None | Status dot (green/yellow/red) |
|
|
|
|
---
|
|
|
|
## Default State
|
|
|
|
```
|
|
+=============================================================================+
|
|
| [CW] Initiatives Agents [*3] Inbox [2] Settings [⌘K] [sun] * myws |
|
|
+=============================================================================+
|
|
| |
|
|
| |
|
|
| full-width padded content (px-4 sm:px-6 lg:px-8) |
|
|
| |
|
|
| <Page Outlet> |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
+=============================================================================+
|
|
```
|
|
|
|
Note: `max-w-*` constraints are set per-page, not globally. Pages like Agents
|
|
and Initiative Detail use full-width layouts. The Initiatives list and Settings
|
|
may opt into `max-w-6xl` or similar. The root layout applies only horizontal
|
|
padding.
|
|
|
|
## Active Tab Highlighted
|
|
|
|
```
|
|
+=============================================================================+
|
|
| [CW] [*Initiatives*] Agents [*3] Inbox [2] Settings [⌘K] [sun] * myws |
|
|
+=============================================================================+
|
|
| |
|
|
| <Page Outlet> |
|
|
| |
|
|
+=============================================================================+
|
|
```
|
|
|
|
Active tab gets `bg-muted rounded-md` treatment. Text is `font-medium`
|
|
(not bold — `font-medium` is 500 weight, sufficient contrast without
|
|
the heaviness of `font-bold` at these small sizes).
|
|
|
|
## Zero Badges (no running agents, no pending conversations)
|
|
|
|
```
|
|
+=============================================================================+
|
|
| [CW] Initiatives Agents Inbox Settings [⌘K] [sun] * myws |
|
|
+=============================================================================+
|
|
| |
|
|
| <Page Outlet> |
|
|
| |
|
|
+=============================================================================+
|
|
```
|
|
|
|
Badges are completely hidden when count is 0 — no empty brackets or `*0`.
|
|
|
|
## Health Dot States
|
|
|
|
```
|
|
* ← green: WebSocket connected, last heartbeat < 5s ago
|
|
? ← yellow: connected but heartbeat > 5s (slow/degraded)
|
|
- ← red: WebSocket disconnected or server unreachable
|
|
```
|
|
|
|
The dot uses `status-success-dot`, `status-warning-dot`, or `status-error-dot`
|
|
tokens from the theme. The green state gets a subtle `animate-pulse` removed
|
|
after 3s of stable connection to avoid distraction.
|
|
|
|
### Tooltip on hover
|
|
|
|
Rich tooltip (not just `title` attr) with structured content:
|
|
|
|
```
|
|
+-------------------------------+
|
|
| ● Connected |
|
|
| Latency: 42ms |
|
|
| Uptime: 2h 14m |
|
|
| Last heartbeat: <1s ago |
|
|
+-------------------------------+
|
|
|
|
+-------------------------------+
|
|
| ◐ Degraded |
|
|
| Latency: 2,340ms |
|
|
| Last heartbeat: 8s ago |
|
|
| Server may be under load |
|
|
+-------------------------------+
|
|
|
|
+-------------------------------+
|
|
| ○ Disconnected |
|
|
| Lost connection 12s ago |
|
|
| Retrying... (attempt 3) |
|
|
+-------------------------------+
|
|
```
|
|
|
|
- Uses shadcn `<Tooltip>` with `<TooltipContent side="bottom" align="end">`
|
|
- Tooltip content is `text-xs font-mono` for data readability
|
|
- Status label uses matching status token color
|
|
|
|
## Connection Banner
|
|
|
|
When the WebSocket disconnects, a full-width banner slides down between the
|
|
header and page content. This is more aggressive than the dot alone — the dot
|
|
is for glanceable status, the banner is for "you need to know this NOW."
|
|
|
|
### Reconnecting
|
|
|
|
```
|
|
+=============================================================================+
|
|
| [CW] Initiatives Agents [*3] Inbox [2] Settings [⌘K] [sun] - myws |
|
|
+=============================================================================+
|
|
| [spinner] Reconnecting to server... [Dismiss] |
|
|
+=============================================================================+
|
|
| |
|
|
| <Page Outlet> |
|
|
+=============================================================================+
|
|
```
|
|
|
|
- `bg-status-warning-bg border-b border-status-warning-border`
|
|
- Text: `text-status-warning-fg text-sm`
|
|
- Spinner: 14px animated, same as SaveIndicator
|
|
- Dismiss hides the banner but the health dot stays red/yellow
|
|
|
|
### Offline (> 30s disconnected)
|
|
|
|
```
|
|
+=============================================================================+
|
|
| [CW] Initiatives Agents [*3] Inbox [2] Settings [⌘K] [sun] - myws |
|
|
+=============================================================================+
|
|
| [!] Server unreachable — data may be stale [Retry] [Dismiss] |
|
|
+=============================================================================+
|
|
| |
|
|
| <Page Outlet> |
|
|
+=============================================================================+
|
|
```
|
|
|
|
- `bg-status-error-bg border-b border-status-error-border`
|
|
- Text: `text-status-error-fg text-sm`
|
|
- Retry button: `text-status-error-fg underline font-medium`
|
|
|
|
### Reconnected (success, auto-dismisses)
|
|
|
|
```
|
|
+=============================================================================+
|
|
| [CW] Initiatives Agents [*3] Inbox [2] Settings [⌘K] [sun] * myws |
|
|
+=============================================================================+
|
|
| [v] Reconnected |
|
|
+=============================================================================+
|
|
```
|
|
|
|
- `bg-status-success-bg text-status-success-fg text-sm`
|
|
- Auto-dismisses after 3s with fade-out
|
|
|
|
### Timing
|
|
|
|
| Duration disconnected | Behavior |
|
|
|----------------------|----------|
|
|
| 0-3s | No banner (brief blips are silent, dot goes yellow) |
|
|
| 3-30s | "Reconnecting..." banner |
|
|
| >30s | "Server unreachable" banner with Retry |
|
|
| On reconnect | "Reconnected" for 3s, then dismiss |
|
|
|
|
---
|
|
|
|
## Notification Indicators
|
|
|
|
Important events must surface even when the user is on a different tab.
|
|
Three mechanisms work together:
|
|
|
|
### 1. Tab badges (already specified above)
|
|
|
|
`Agents [*3]` and `Inbox [2]` update in real-time via tRPC subscriptions.
|
|
|
|
### 2. Toast notifications (sonner)
|
|
|
|
Critical events fire a toast regardless of current page:
|
|
|
|
| Event | Toast | Duration |
|
|
|-------|-------|----------|
|
|
| Agent crashed | `"blue-fox-7 crashed"` + link to Agents | Persistent (manual dismiss) |
|
|
| Task needs approval | `"Task ready for approval"` + link | Persistent |
|
|
| All tasks complete | `"Initiative complete — ready for review"` | 8s auto-dismiss |
|
|
| Account exhausted | `"Account X exhausted, switched to Y"` | 5s auto-dismiss |
|
|
| Agent asking question | `"blue-fox-7 has a question"` + link to Inbox | 8s auto-dismiss |
|
|
|
|
Persistent toasts stack in bottom-right. Max 3 visible; older ones collapse
|
|
into a "+N more" indicator.
|
|
|
|
### 3. Browser tab title
|
|
|
|
When the app is backgrounded and events occur:
|
|
- `(2) Codewalk District` — number prefix for unread attention items
|
|
- Counts: crashed agents + pending approvals + unanswered inbox items
|
|
- Resets to `Codewalk District` when user returns to the tab
|
|
|
|
---
|
|
|
|
## Collapsed Mobile View (not yet implemented)
|
|
|
|
```
|
|
+=============================================================================+
|
|
| [CW] [⌘K] [sun] * [☰ 2] |
|
|
+=============================================================================+
|
|
| |
|
|
| <Page Outlet> |
|
|
| |
|
|
+=============================================================================+
|
|
```
|
|
|
|
Note: Mobile hamburger menu is not implemented. This is a placeholder for
|
|
future work. At `< 768px`, nav tabs collapse into a hamburger `[☰]` dropdown.
|
|
The hamburger icon shows a combined badge count (crashed agents + unread inbox)
|
|
so that attention items are never hidden behind the collapsed menu.
|
|
|
|
## 404 Page
|
|
|
|
```
|
|
+=============================================================================+
|
|
| [CW] Initiatives Agents Inbox Settings [⌘K] [sun] * myws |
|
|
+=============================================================================+
|
|
| |
|
|
| |
|
|
| [AlertCircle] |
|
|
| Page not found |
|
|
| |
|
|
| [ Back to Initiatives ] |
|
|
| |
|
|
| |
|
|
+=============================================================================+
|
|
```
|
|
|
|
---
|
|
|
|
## 48px Header Anatomy
|
|
|
|
48px is tight. This works because:
|
|
- Logo is icon-only (24px, no wordmark)
|
|
- Nav tabs use `px-3 py-1.5` (compact hit targets, 36px tall inside 48px)
|
|
- Badges are inline pills, not separate elements
|
|
- Right cluster items are icon-sized (no text labels except ⌘K)
|
|
|
|
If this still feels cramped at certain content widths, the Cmd+K button
|
|
drops its text label at `< 1024px` (see Responsive Notes below).
|
|
|
|
```
|
|
+-----------------------------------------------------------------------------+
|
|
| LEFT CLUSTER SPACER RIGHT CLUSTER |
|
|
| [CW] [Nav] [Nav [N]] [Nav [N]] [Nav] ------- [⌘K] [◐] [●] [workspace] |
|
|
+-----------------------------------------------------------------------------+
|
|
↑ ↑ ↑ ↑ ↑
|
|
24px icon Cmd+K Theme Health Workspace
|
|
```
|
|
|
|
### Left cluster (nav)
|
|
- `[CW]` — 24x24 logo icon, links to `/initiatives`
|
|
- Nav tabs: `Initiatives`, `Agents`, `Inbox`, `Settings`
|
|
- Each tab is an `<a>` with `gap-1.5` between label and optional badge
|
|
- Active tab: `bg-muted text-foreground font-medium rounded-md px-3 py-1.5`
|
|
- Inactive tab: `text-muted-foreground hover:text-foreground px-3 py-1.5`
|
|
- Keyboard: `1` / `2` / `3` / `4` navigate to tabs (when no input is focused)
|
|
|
|
### Badge rendering — unified `<NavBadge>` component
|
|
|
|
Both tabs use the same badge component for visual consistency:
|
|
|
|
```tsx
|
|
// Unified pill badge: inline, compact, color-coded
|
|
<span className="ml-1 inline-flex items-center rounded-full px-1.5 py-0.5
|
|
text-[10px] font-medium leading-none tabular-nums
|
|
{colorClasses}">
|
|
{count}
|
|
</span>
|
|
```
|
|
|
|
| Tab | Badge color | Example | Semantics |
|
|
|-----|-------------|---------|-----------|
|
|
| Agents | `bg-status-active-bg text-status-active-fg` (blue) | `Agents [3]` | Running agent count |
|
|
| Inbox | `bg-status-warning-bg text-status-warning-fg` (amber) | `Inbox [2]` | Unresolved conversation count |
|
|
|
|
Both badges:
|
|
- Hidden entirely when count is 0
|
|
- Use `tabular-nums` so width doesn't shift when count changes (e.g., 9 -> 10)
|
|
- Pill shape: `rounded-full` with tight `px-1.5 py-0.5`
|
|
- Font: `text-[10px]` — small enough to not compete with the tab label
|
|
|
|
**Attention escalation**: When an agent has crashed or a task needs approval,
|
|
the Agents badge switches to `bg-status-error-bg text-status-error-fg` (red)
|
|
to draw the eye. The count reflects crashed + running agents in this state.
|
|
|
|
### Right cluster (utilities)
|
|
- `[⌘K]` — Shows platform-aware shortcut hint as label: `⌘K` on Mac,
|
|
`Ctrl+K` on Windows/Linux. Rendered as a `<kbd>` style pill:
|
|
`bg-muted text-muted-foreground text-xs font-mono px-2 py-1 rounded-md border border-border`.
|
|
Clicking opens the CommandPalette overlay (see shared-components.md).
|
|
- `[◐]` — ThemeToggle, 3-state segmented control (see shared-components.md)
|
|
- `[●]` — Health status dot, 8px circle with color (see Health Dot States above)
|
|
- `[workspace]` — Workspace name, derived from `.cwrc` or directory basename.
|
|
`text-xs text-muted-foreground font-mono truncate max-w-[120px]`.
|
|
Tooltip shows full path on hover: `/Users/me/projects/my-app`.
|
|
Hidden at `< 1024px` to save space.
|
|
|
|
### Workspace identity
|
|
|
|
The workspace label solves a real problem: developers often run multiple
|
|
Codewalk instances for different projects. Without it, you can't tell which
|
|
instance you're looking at without checking the terminal.
|
|
|
|
```
|
|
Right cluster detail:
|
|
[⌘K] [sun|monitor|moon] ● codewalk-district
|
|
^^^^^^^^^^^^^^^^^^
|
|
text-xs font-mono text-muted-foreground
|
|
truncated at 120px, full path in tooltip
|
|
```
|
|
|
|
### Spacing
|
|
- Left cluster: `gap-1` between items
|
|
- Between clusters: `flex-1` spacer pushes them apart
|
|
- Right cluster: `gap-2` between items (tightened from `gap-3` — the
|
|
workspace label needs room)
|
|
|
|
---
|
|
|
|
## Responsive Notes
|
|
|
|
| Breakpoint | Header | Content |
|
|
|------------|--------|---------|
|
|
| `>= 1280px` | Full header as shown, workspace label visible | Page decides own `max-w-*` |
|
|
| `1024px - 1279px` | Workspace label hidden, ⌘K shows icon-only `[search]` | Page decides own `max-w-*` |
|
|
| `768px - 1023px` | Same as above, tab labels may truncate | Horizontal padding reduces |
|
|
| `< 768px` | Nav tabs collapse into hamburger (future) | Single-column, `px-4` |
|
|
|
|
Content area uses `px-4 sm:px-6 lg:px-8` horizontal padding only. There is
|
|
**no global** `max-w-7xl`. Each page sets its own max-width constraint (or
|
|
uses none for full-bleed layouts like the Agents split-pane view).
|
|
|
|
### Per-page width strategy
|
|
|
|
| Page | Width constraint | Rationale |
|
|
|------|-----------------|-----------|
|
|
| Initiatives list | `max-w-6xl mx-auto` | Card grid, doesn't benefit from extreme width |
|
|
| Initiative detail | Full width | Tab content (execution, agents) needs room |
|
|
| Agents | Full width | Split-pane: agent list + output viewer |
|
|
| Inbox | Full width | Split-pane: conversation list + detail |
|
|
| Settings | `max-w-4xl mx-auto` | Forms, narrow content |
|
|
|
|
---
|
|
|
|
## Global Components (rendered in root layout)
|
|
|
|
- `<Toaster>` (sonner) — bottom-right toast notifications, max 3 visible
|
|
- `<ErrorBoundary>` — wraps the page outlet
|
|
- `<CommandPalette>` — overlay, triggered by Cmd+K or header button
|
|
- `<ConnectionBanner>` — slides between header and content on disconnect
|
|
- `<BrowserTitleUpdater>` — invisible, manages `document.title` with unread counts
|
|
|
|
---
|
|
|
|
## Keyboard Shortcuts (global)
|
|
|
|
These are registered at the root layout level, suppressed when an input/textarea
|
|
is focused:
|
|
|
|
| Shortcut | Action |
|
|
|----------|--------|
|
|
| `⌘K` / `Ctrl+K` | Open command palette |
|
|
| `1` | Navigate to Initiatives |
|
|
| `2` | Navigate to Agents |
|
|
| `3` | Navigate to Inbox |
|
|
| `4` | Navigate to Settings |
|
|
| `?` | Show keyboard shortcut help overlay (future) |
|
|
|
|
---
|
|
|
|
## Source
|
|
|
|
- `packages/web/src/routes/__root.tsx`
|
|
- `packages/web/src/layouts/AppLayout.tsx`
|
|
- `packages/web/src/components/ThemeToggle.tsx` (proposed)
|
|
- `packages/web/src/components/CommandPalette.tsx` (proposed)
|
|
- `packages/web/src/components/HealthDot.tsx` (proposed)
|
|
- `packages/web/src/components/ConnectionBanner.tsx` (proposed)
|
|
- `packages/web/src/components/NavBadge.tsx` (proposed)
|
|
- `packages/web/src/hooks/useWorkspaceName.ts` (proposed)
|
|
- `packages/web/src/hooks/useBrowserTitle.ts` (proposed)
|
|
|
|
---
|
|
|
|
## Design Review Notes
|
|
|
|
Reviewed against MISSION CONTROL criteria (dense, status-at-a-glance,
|
|
keyboard-first, dark-mode-first). Changes applied inline above.
|
|
|
|
### 1. 48px header — is it cramped?
|
|
|
|
48px is fine IF the content is disciplined: icon-only logo, no wordmark, compact
|
|
tab padding, and small inline badges. Added an explicit note to the Header
|
|
Anatomy section explaining why 48px works and where the escape hatch is
|
|
(responsive breakpoints drop ⌘K text and workspace label). The real risk is
|
|
not height but **horizontal** crowding when all badges are showing and the
|
|
workspace name is long. The `truncate max-w-[120px]` on the workspace label
|
|
and responsive hiding address this.
|
|
|
|
### 2. Badge inconsistency (`*N` vs `(N)`)
|
|
|
|
This was a real problem. The v1 spec used `*N` for Agents and `(N)` for Inbox,
|
|
two completely different visual languages for the same concept (a count badge
|
|
on a nav tab). Unified both into a `<NavBadge>` component — a colored pill
|
|
with just the number. Color differentiates meaning (blue=active, amber=pending),
|
|
not punctuation. Added `tabular-nums` to prevent layout shift on count changes.
|
|
|
|
### 3. Health dot — too subtle?
|
|
|
|
The dot alone was easy to miss, yes. But the right fix is NOT making the dot
|
|
bigger — it is adding a **second tier of communication**. The dot stays as a
|
|
glanceable ambient indicator (like a server rack LED). The new ConnectionBanner
|
|
handles the "you need to stop and pay attention" case. Added a rich tooltip
|
|
with latency, uptime, and heartbeat data for the "I want details" case. Three
|
|
tiers: dot (ambient) -> tooltip (on-demand) -> banner (urgent).
|
|
|
|
### 4. Connection banner — missing
|
|
|
|
Added full spec: Reconnecting (3-30s), Offline (>30s), Reconnected (success).
|
|
The 3s delay before showing the banner prevents flicker on brief WebSocket
|
|
reconnections. The banner is dismissible but the dot stays colored. See
|
|
"Connection Banner" section above.
|
|
|
|
### 5. Workspace identity — missing
|
|
|
|
Added workspace name to the right cluster. Derived from `.cwrc` config or
|
|
directory basename. This is essential for multi-instance users. Hidden below
|
|
1024px because it is useful but not critical — the browser tab title or terminal
|
|
provides fallback identification.
|
|
|
|
### 6. Cmd+K shortcut hint
|
|
|
|
Changed from `[cmd-k]` text to a `<kbd>`-styled `[⌘K]` pill showing the
|
|
actual platform shortcut. This doubles as both a clickable trigger and a
|
|
discoverability hint. Users see the shortcut every time they glance at the
|
|
header, which builds muscle memory. On Windows/Linux it renders as `Ctrl+K`.
|
|
|
|
### 7. Notification system — missing
|
|
|
|
This was the biggest gap. Added three tiers:
|
|
- **Tab badges**: real-time counts (already existed, now specified with attention escalation)
|
|
- **Toast notifications**: critical events fire toasts regardless of current page.
|
|
Persistent for crashes/approvals, auto-dismiss for informational events.
|
|
- **Browser tab title**: `(2) Codewalk District` prefix when backgrounded with
|
|
unread attention items. Standard web app pattern.
|
|
|
|
The attention escalation on the Agents badge (blue -> red when something crashes)
|
|
is important — it means you do not need to be on the Agents page to know
|
|
something went wrong.
|
|
|
|
### 8. `max-w-7xl` constraint — wrong default
|
|
|
|
Removed. A mission control tool should default to full-width and let pages
|
|
opt into narrower layouts. The split-pane views (Agents, Inbox) and the
|
|
Initiative Detail tabs lose significant usable space under `max-w-7xl` (1280px).
|
|
Replaced with per-page width strategy: forms/lists get `max-w-4xl`/`max-w-6xl`,
|
|
operational views get full width. The root layout only applies horizontal padding.
|
|
|
|
### Open questions for next review
|
|
|
|
- **Favicon badge**: Should the browser favicon show a red dot for attention
|
|
items, like Slack does? More noticeable than title prefix alone.
|
|
- **Sound**: Should critical events (agent crash) play a subtle notification
|
|
sound? Power users managing 10+ agents may not be watching the screen.
|
|
- **Keyboard shortcut overlay**: `?` to show all shortcuts is specced as
|
|
"future" — should it be in v2 scope given the keyboard-first stance?
|