# Runtime Dashboard — Compact View

## Summary

Add a compact/collapsed view mode to the runtime dashboard (AgentHealth page). In compact mode, each application is rendered as a small status card showing app name, live agent count, and last heartbeat, with health-state coloring (tinted background + tinted border + thick left accent border). Individual cards can be expanded inline to reveal the full agent instance table. A global toggle switches between compact and expanded views.

## Decisions

| Question | Decision |
|----------|----------|
| Default view | Compact — all apps start collapsed |
| Expand behavior | Inline — card expands in place, pushes others down |
| Grid layout | Auto-fill responsive (`repeat(auto-fill, minmax(220px, 1fr))`) |
| Toggle location | Inside stat strip area (icon toggle, no new toolbar row) |
| Health coloring | Same logic as existing `appHealth()` — worst agent state wins |
| Card styling | Tinted background (8% health color) + tinted border (25%) + 3px left accent border |

## View Mode State

- `viewMode: 'compact' | 'expanded'` — persisted to `localStorage` key `cameleer:runtime:viewMode`, default `'compact'`.
- `expandedApps: Set<string>` — tracks individually expanded app IDs. Ephemeral (not persisted).
- When `viewMode === 'compact'`: apps in `expandedApps` show the full GroupCard table. All others show compact cards.
- When `viewMode === 'expanded'`: all apps show the full GroupCard table (current behavior). `expandedApps` is ignored.
- Collapse all / expand all: sets `viewMode` and clears `expandedApps`.

## View Toggle

- Two-button icon toggle rendered in the `.statStrip` area after the last StatCard.
- Uses `Button` from `@cameleer/design-system` with `variant="ghost"`. Active button styled with `var(--running)` background.
- Icons: `LayoutGrid` (compact) / `List` (expanded) from `lucide-react`.
- `.statStrip` grid changes from `repeat(5, 1fr)` to `repeat(5, 1fr) auto` to accommodate the toggle.

## Compact Card (CompactAppCard)

Inline component in `AgentHealth.tsx` (page-specific, not reusable).

**Props:** `group: AppGroup`, `onExpand: () => void`.

**Layout:**
- Container: `var(--bg-raised)` with health-color tint via CSS custom property `--card-accent`.
  - `background: color-mix(in srgb, var(--card-accent) 8%, var(--bg-raised))`
  - `border: 1px solid color-mix(in srgb, var(--card-accent) 25%, transparent)`
  - `border-left: 3px solid var(--card-accent)`
  - `border-radius: var(--radius-md)`
  - Cursor pointer, entire card clickable.
- **Row 1:** App name (font-weight 700, `var(--text-primary)`) left. `ChevronRight` icon (`var(--text-muted)`) right.
- **Row 2:** `"{liveCount}/{total} live"` in health color (font-weight 600) left. Last heartbeat right — computed as `timeAgo(max(instances.map(i => i.lastHeartbeat)))` (most recent across all instances in the group). Color: `var(--text-muted)` when healthy, health color when stale/dead.
- **Hover:** tint increases to 12%.

**Health color mapping** (reuses existing `appHealth()` function):
- `.compactCardSuccess` → `--card-accent: var(--success)`
- `.compactCardWarning` → `--card-accent: var(--warning)`
- `.compactCardError` → `--card-accent: var(--error)`

## Expanded Card in Compact Mode

- Renders existing `GroupCard` + `DataTable` unchanged.
- GroupCard header gets a `ChevronDown` icon button to collapse back.
- Expanded card gets `grid-column: span 2` in the compact grid for adequate table width (roughly matches current half-page GroupCard width).
- **Animation (single card toggle):** CSS transition on wrapper div with `overflow: hidden`. Expand: compact fades out (opacity 0, ~100ms), expanded fades in + grows (opacity 1, max-height transition, ~200ms ease-out). Collapse: reverse. Collapse-all / expand-all toggle is instant (no animation).

## Grid Layout

- `viewMode === 'expanded'`: existing `.groupGrid` (2-column `1fr 1fr`, unchanged).
- `viewMode === 'compact'`: new `.compactGrid` class:
  ```css
  .compactGrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 10px;
    margin-bottom: 20px;
  }
  ```

## Files Changed

| File | Change |
|------|--------|
| `ui/src/pages/AgentHealth/AgentHealth.tsx` | `viewMode`/`expandedApps` state, localStorage init, `CompactAppCard` component, view toggle in stat strip, conditional grid rendering |
| `ui/src/pages/AgentHealth/AgentHealth.module.css` | `.compactGrid`, `.compactCard`, `.compactCardSuccess/Warning/Error`, `.compactCardHeader`, `.compactCardMeta`, `.viewToggle`, `.viewToggleActive`, animation classes, `.statStrip` column adjustment |
| `.claude/rules/ui.md` | Add compact view mention under Runtime section |

No new files. No new design system components. No API changes. Feature is entirely contained in the AgentHealth page and its CSS module.

## Style Guide Compliance

- All colors via `@cameleer/design-system` CSS variables (`var(--success)`, `var(--warning)`, `var(--error)`, `var(--bg-raised)`, `var(--text-primary)`, `var(--text-muted)`, `var(--running)`, `var(--radius-md)`, `var(--border-subtle)`).
- No hardcoded hex values.
- Shared CSS modules imported where applicable.
- Design system components (`Button`, `GroupCard`, `DataTable`, `StatusDot`, `Badge`, `MonoText`) used throughout.
- `lucide-react` for icons (consistent with rest of UI).
