# 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) Codewalkers` — number prefix for unread attention items
- Counts: crashed agents + pending approvals + unanswered inbox items
- Resets to `Codewalkers` 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) Codewalkers` 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?
