Update all user-facing strings (HTML title, manifest, header logo, browser title updater), code comments, and documentation references. Folder name retained as-is.
21 KiB
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-monofor 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) Codewalkers— number prefix for unread attention items- Counts: crashed agents + pending approvals + unanswered inbox items
- Resets to
Codewalkerswhen 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>withgap-1.5between 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/4navigate to tabs (when no input is focused)
Badge rendering — unified <NavBadge> component
Both tabs use the same badge component for visual consistency:
// 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-numsso width doesn't shift when count changes (e.g., 9 -> 10) - Pill shape:
rounded-fullwith tightpx-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:⌘Kon Mac,Ctrl+Kon 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.cwrcor 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< 1024pxto 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-1between items - Between clusters:
flex-1spacer pushes them apart - Right cluster:
gap-2between items (tightened fromgap-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, managesdocument.titlewith 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.tsxpackages/web/src/layouts/AppLayout.tsxpackages/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) Codewalkersprefix 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?