# OpenClaw Control Center — Command Hub ## Senior Product Designer Specification (7-Point) **Version:** 1.0 **Date:** 2026-04-21 **Designer:** Sketch (UI/UX Agent) **Status:** Implementation-Ready **Design System:** Material Design 3 (https://m3.material.io/) **Aesthetic:** Tactical Dark Mode --- ## 1. Objective Design the **Command Hub** — the primary entry point and nerve centre for the OpenClaw Control Center. This screen enables operators to **monitor all active agents at a glance**, assess fleet health in under 3 seconds, and **jump directly into any agent session** without friction. The design must serve two primary mental models: - **Triage Mode** — scan for errors, stalls, and anomalies fast - **Navigation Mode** — get into a specific agent session instantly This is a utility-first, operator-grade interface. It is not a marketing page. Every pixel earns its place. --- ## 2. Screen Inventory | Screen | Description | Trigger | |--------|-------------|---------| | **Command Hub** | Fleet Status grid + global nav | App launch / Home | | **Agent Session View** | Full session detail for one agent | Quick-Jump tap | | **Session Log** | Full scrollable log history | Long-press agent card or log icon | | **Task Breakdown** | Subtask waterfall for active task | Tap active task chip on card | | **Settings / Config** | Global config, theme, agents list | Nav Rail → Settings | | **Notifications Panel** | Alert history and system events | Bell icon / swipe | --- ## 3. Layout Specification ### 3.1 Kiosk Layout (≥ 1024px — Fixed Display / Raspberry Pi HDMI) ``` ┌─────────────────────────────────────────────────────────────────┐ │ NAV RAIL (72px) │ MAIN CONTENT AREA │ │ │ │ │ [☰] OpenClaw │ ┌─ HEADER BAR ─────────────────────────┐ │ │ │ │ "Command Hub" [🔔 3] [●Live] [⚙] │ │ │ [⚡] Command Hub │ └──────────────────────────────────────┘ │ │ [📋] Projects │ │ │ [🗂] Sessions │ ┌─ FLEET STATUS ────────────────────────┐ │ │ [📊] Logs │ │ FILTER: [All ▾] [Active] [Error] │ │ │ ───────── │ │ │ │ │ [⚙] Settings │ │ ┌──────────┐ ┌──────────┐ ┌──────┐ │ │ │ [👤] Profile │ │ │ AGENT │ │ AGENT │ │AGENT │ │ │ │ │ │ │ CARD │ │ CARD │ │ CARD │ │ │ │ │ │ └──────────┘ └──────────┘ └──────┘ │ │ │ │ │ ┌──────────┐ ┌──────────┐ ... │ │ │ │ │ │ AGENT │ │ AGENT │ │ │ │ │ │ │ CARD │ │ CARD │ │ │ │ │ │ └──────────┘ └──────────┘ │ │ │ │ └────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` **Nav Rail:** 72px collapsed, 256px expanded. M3 `NavigationRail` component. Always visible on kiosk. **Main Content:** 3-column agent card grid (CSS Grid, `repeat(auto-fill, minmax(340px, 1fr))`). 24px gutter. **Header Bar:** 64px tall. App title + live indicator + notification bell + settings. --- ### 3.2 Mobile Layout (< 768px — Phone / Tablet) ``` ┌───────────────────────────────┐ │ ┌─ HEADER ──────────────────┐│ │ │ ≡ Command Hub 🔔 ⚙ ││ │ └────────────────────────────│ │ │ │ [All ▾] [Active 4] [Error 1] │ │ │ │ ┌──────────────────────────┐ │ │ │ AGENT CARD (full width) │ │ │ └──────────────────────────┘ │ │ ┌──────────────────────────┐ │ │ │ AGENT CARD (full width) │ │ │ └──────────────────────────┘ │ │ ┌──────────────────────────┐ │ │ │ AGENT CARD (full width) │ │ │ └──────────────────────────┘ │ │ │ │ ───────────────────────────── │ │ [⚡Hub] [📋Projects] [📊Logs] │ │ BOTTOM NAV BAR │ └───────────────────────────────┘ ``` **Bottom Navigation:** M3 `NavigationBar`. 3–5 destinations. Active destination uses M3 indicator pill. **Cards:** Single-column, full-width. Scrollable list. 16px horizontal padding. --- ### 3.3 Agent Card Component (Core Unit) ``` ┌─────────────────────────────────────────────────────┐ │ ● ACTIVE [otto] [→ Jump] │ │ ─────────────────────────────────────────────────── │ │ 🤖 Otto │ │ Orchestrator Agent │ │ │ │ TASK: Reviewing PR #42 — extrudex-ui │ │ ████████████░░░░░░ 62% — 04m 12s elapsed │ │ │ │ SESSION: agent:otto:telegram:direct:8787... │ │ CHANNEL: Telegram [●] Last msg: 2m ago │ └─────────────────────────────────────────────────────┘ ``` **Card anatomy:** - **Status Badge** (top-left): Color pill — Active / Idle / Thinking / Error - **Agent Tag** (top-center): Short ID chip - **Quick-Jump CTA** (top-right): `→ Jump` icon button — M3 `FilledTonalIconButton` - **Avatar + Name + Role** (mid-section): M3 List tile pattern - **Active Task Row**: Task name + linear progress indicator - **Session Footer**: Session key truncated + channel + last activity timestamp **Card states:** - **Default** — surface container background, subtle border - **Active** — cyan left-border accent (4px), slightly elevated `+2dp` - **Error** — red border, pulsing status dot - **Thinking** — purple status + animated shimmer on task row - **Idle** — muted, desaturated, lower elevation - **Hover/Focus** — M3 state layer overlay (8% primary) --- ### 3.4 Quick-Jump Mechanism On tap of `→ Jump` button on any agent card: 1. **Modal Bottom Sheet** (mobile) — M3 `ModalBottomSheet` slides up with: - Agent name + status - Last 3 messages (truncated) - [Open Full Session] FAB - [View Logs] secondary action 2. **Side Drawer Panel** (kiosk/desktop) — M3 `NavigationDrawer` (modal variant) slides in from right (480px wide) with: - Agent name + session context - Live log tail (last 20 lines, auto-scroll) - [Open Full Session] primary button - [Pin to Dashboard] secondary action **Keyboard / touch shortcut:** Long-press card = opens Session Log directly (bypasses Quick-Jump drawer). --- ### 3.5 Global Navigation Structure | Destination | Icon | Badge | |-------------|------|-------| | Command Hub | ⚡ Flash | None | | Projects | 📋 Clipboard | Active count | | Sessions | 🗂 Stack | Unread count | | Logs | 📊 Chart | Error count | | Settings | ⚙ Gear | — | **Rail behavior:** Collapsed by default on kiosk. Tap hamburger `☰` to expand with labels. **Bottom nav (mobile):** Always shows labels. Max 5 destinations. Overflow → menu. --- ## 4. UX Rationale ### Why Navigation Rail (Desktop) + Bottom Nav (Mobile)? M3's adaptive navigation pattern — `NavigationRail` is the correct M3 component for persistent navigation on expanded screens. It keeps the full content width for the fleet grid. On mobile, `NavigationBar` (bottom) is ergonomically correct for thumb reach. The two layouts share identical navigation destinations but adapt the component. ### Why Card Grid, Not Table? - Cards are **touch-friendly** (minimum 56px tap area per cell) - Cards support **rich visual status** — tables flatten the hierarchy - Cards allow **progressive disclosure** (collapsed vs expanded state) - Tables are for data comparison; cards are for status triage ### Why Quick-Jump Drawer, Not Page Navigation? Operators often want to **peek** at an agent without losing the fleet overview. A drawer preserves context — the fleet grid remains visible behind it. Full navigation to the agent session view is a secondary action requiring intentional tap. ### Why Left-Border Accent on Active Cards? It's a scannable, pre-attentive visual cue. Eyes detect vertical color stripes faster than color fills. Lifted from terminal/IDE design patterns (like VS Code activity indicators). Consistent with Tactical Dark Mode aesthetics. ### Information Hierarchy on Card Status → Identity → Current Task → Session Context This ordering maps to the triage mental model: "Is it ok? Who is it? What's it doing? Where is it?" --- ## 5. Visual Direction ### 5.1 Color Palette (M3 Dynamic Color — Custom Dark Scheme) | Role | Token | Hex | Usage | |------|-------|-----|-------| | Background | `md.sys.color.background` | `#0D0F12` | App background | | Surface | `md.sys.color.surface` | `#13161A` | Card background | | Surface Container | `md.sys.color.surface-container` | `#1C2027` | Elevated cards | | Surface Container High | `md.sys.color.surface-container-high` | `#252B33` | Header, Nav Rail | | On Surface | `md.sys.color.on-surface` | `#E2E8F0` | Primary text | | On Surface Variant | `md.sys.color.on-surface-variant` | `#8A9BB0` | Secondary text | | Outline | `md.sys.color.outline` | `#2D3748` | Card borders | | Primary | `md.sys.color.primary` | `#38BDF8` | Active state, CTAs (Electric Blue) | | On Primary | `md.sys.color.on-primary` | `#0C1825` | Text on primary | | Secondary | `md.sys.color.secondary` | `#64748B` | Nav inactive | | Tertiary | `md.sys.color.tertiary` | `#A78BFA` | Thinking state (Amethyst) | **Status Colors (Semantic — outside M3 tonal system):** | Status | Color | Hex | Dot Animation | |--------|-------|-----|---------------| | Active | Electric Blue | `#38BDF8` | Steady pulse (2s) | | Idle | Muted Teal | `#2DD4BF` | Static | | Thinking | Amethyst | `#A78BFA` | Slow pulse (3s) | | Error | Vivid Red | `#F87171` | Fast pulse (0.8s) | ### 5.2 Typography M3 Type Scale using **Inter** (primary) with **Roboto Mono** for session IDs and log output. | Role | M3 Token | Size | Weight | Usage | |------|----------|------|--------|-------| | Display Small | `displaySmall` | 36sp | 400 | Screen title | | Headline Medium | `headlineMedium` | 28sp | 400 | Agent name | | Title Large | `titleLarge` | 22sp | 500 | Section headers | | Title Medium | `titleMedium` | 16sp | 500 | Card title | | Body Medium | `bodyMedium` | 14sp | 400 | Task description | | Label Large | `labelLarge` | 14sp | 500 | Buttons, badges | | Label Small | `labelSmall` | 11sp | 500 | Status chips, timestamps | | Mono Body | — | 13sp | 400 | Session IDs, log text | ### 5.3 Elevation & Surfaces Using M3's tonal surface elevation (not drop shadows): | Level | Overlay | Usage | |-------|---------|-------| | Level 0 | 0% | App background | | Level 1 | 5% | Cards (default) | | Level 2 | 8% | Cards (active/hover) | | Level 3 | 11% | Navigation Rail | | Level 4 | 12% | Header / App Bar | | Level 5 | 14% | Modal overlays | ### 5.4 Spacing & Grid - Base unit: **8px** - Card padding: **20px** (2.5 units) - Card gap (grid): **16px** (2 units) - Section padding: **24px** (3 units) - Nav Rail width collapsed: **72px** - Nav Rail width expanded: **256px** - Card min-width: **320px**, preferred: **360px** - Card border-radius: **16px** (M3 ExtraLarge shape token) - Status dot size: **10px** ### 5.5 Iconography M3 Material Symbols (Outlined weight, `FILL=0`, `wght=400`, `GRAD=0`, `opsz=24`). Key icons: - Agent status dot: Custom SVG animated circle - Quick-Jump: `arrow_forward` or `open_in_new` - Active: `bolt` - Thinking: `psychology` or `autorenew` - Error: `error_outline` - Idle: `pause_circle` - Settings: `settings` - Notifications: `notifications` --- ## 6. Responsiveness ### Breakpoints (M3 Adaptive Layout) | Breakpoint | Width | Layout | Navigation | Columns | |------------|-------|---------|------------|---------| | Compact | 0–599px | Mobile | Bottom Nav | 1 | | Medium | 600–1023px | Tablet | Nav Rail (collapsed) | 2 | | Expanded | ≥ 1024px | Desktop/Kiosk | Nav Rail (expandable) | 3+ | ### Adaptive Behaviors **Compact (Phone):** - Cards: Full-width, stacked vertically - Quick-Jump: Modal Bottom Sheet - Header: Small top app bar (M3 `SmallTopAppBar`) - Status filter: Horizontal scroll chip group **Medium (Tablet):** - Cards: 2-column grid - Nav Rail: Collapsed (icons only) - Quick-Jump: Side panel (half-width drawer) **Expanded (Kiosk/Desktop):** - Cards: 3+ column auto-fill grid - Nav Rail: Expandable with labels - Quick-Jump: Side drawer (480px) - Header: Medium top app bar with subtitle ### Touch Targets - All interactive elements: minimum **48×48dp** (M3 spec) - Card tap area: full card (not just button) - Quick-Jump button: **56×56dp** (touch-optimized) - Bottom nav items: minimum **48dp** height --- ## 7. Developer Handoff Notes ### 7.1 Technology Stack - **Frontend:** Angular 17+ with Angular Material (M3 theming) - **Realtime:** SignalR WebSocket hub — `AgentStatusHub` - **State Management:** NgRx (recommended) or Angular Signals - **Styling:** Angular Material theming + custom CSS custom properties for status colors ### 7.2 M3 Theme Configuration (Angular Material) ```scss // _theme.scss @use '@angular/material' as mat; $dark-theme: mat.define-theme(( color: ( theme-type: dark, primary: mat.$cyan-palette, // #38BDF8 family tertiary: mat.$violet-palette, // #A78BFA family ), typography: ( brand-family: 'Inter, sans-serif', plain-family: 'Inter, sans-serif', bold-weight: 600, medium-weight: 500, regular-weight: 400, ), density: ( scale: 0, ), )); // Custom CSS vars for status colors :root { --status-active: #38BDF8; --status-idle: #2DD4BF; --status-thinking: #A78BFA; --status-error: #F87171; --status-active-bg: rgba(56, 189, 248, 0.12); --status-error-bg: rgba(248, 113, 113, 0.12); } ``` ### 7.3 Agent Card Component Interface ```typescript interface AgentCardData { id: string; // e.g., "otto" displayName: string; // e.g., "Otto" role: string; // e.g., "Orchestrator Agent" status: AgentStatus; // 'active' | 'idle' | 'thinking' | 'error' currentTask?: string; // e.g., "Reviewing PR #42" taskProgress?: number; // 0–100 taskElapsed?: string; // e.g., "04m 12s" sessionKey: string; // full session key channel: string; // e.g., "telegram" lastActivity: Date; errorMessage?: string; // populated on error state } type AgentStatus = 'active' | 'idle' | 'thinking' | 'error'; ``` ### 7.4 SignalR Integration ```typescript // agent-status.service.ts export class AgentStatusService { private hubConnection: HubConnection; connect() { this.hubConnection = new HubConnectionBuilder() .withUrl('/hubs/agent-status') .withAutomaticReconnect() .build(); this.hubConnection.on('AgentStatusChanged', (update: AgentStatusUpdate) => { this.store.dispatch(AgentActions.statusUpdated({ update })); }); this.hubConnection.on('AgentTaskProgress', (progress: TaskProgressUpdate) => { this.store.dispatch(AgentActions.taskProgressUpdated({ progress })); }); } } ``` ### 7.5 Animation Specs ```css /* Status dot pulse animations */ @keyframes pulse-active { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.7; transform: scale(1.15); } } @keyframes pulse-error { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } @keyframes pulse-thinking { 0%, 100% { opacity: 0.8; } 50% { opacity: 0.4; } } .status-dot--active { animation: pulse-active 2s ease-in-out infinite; } .status-dot--error { animation: pulse-error 0.8s ease-in-out infinite; } .status-dot--thinking { animation: pulse-thinking 3s ease-in-out infinite; } .status-dot--idle { /* static */ } /* Quick-jump card border accent */ .agent-card--active { border-left: 4px solid var(--status-active); } .agent-card--error { border-left: 4px solid var(--status-error); } .agent-card--thinking { border-left: 4px solid var(--status-thinking); } .agent-card--idle { border-left: 2px solid var(--md-sys-color-outline); } ``` ### 7.6 Angular Component Structure ``` src/app/ command-hub/ command-hub.component.ts ← Shell, grid layout command-hub.component.html command-hub.component.scss components/ agent-card/ agent-card.component.ts ← Core card unit agent-card.component.html agent-card.component.scss fleet-status-filter/ fleet-status-filter.component.ts quick-jump-drawer/ quick-jump-drawer.component.ts ← Side drawer (desktop) quick-jump-sheet/ quick-jump-sheet.component.ts ← Bottom sheet (mobile) services/ agent-status.service.ts store/ agent.actions.ts agent.reducer.ts agent.selectors.ts agent.effects.ts ``` ### 7.7 Accessibility - All status indicators must have text alternatives (`aria-label="Status: Active"`) - Cards are `role="article"` with `aria-labelledby` pointing to agent name - Color is never the ONLY differentiator — icons accompany all status colors - Focus ring: M3 focus indicator (3dp ring, `md.sys.color.secondary`) - Reduced motion: all pulse animations respect `prefers-reduced-motion: reduce` - Live region for status changes: `aria-live="polite"` on status badge ### 7.8 Performance Notes - Agent cards use `@defer` (Angular 17) for lazy rendering below the fold - SignalR updates trigger only targeted card updates via NgRx selectors (no full re-render) - Status dot animations use `will-change: transform, opacity` for GPU compositing - Limit fleet grid to visible viewport; virtual scroll for > 20 agents (`@angular/cdk/scrolling`) --- ## Appendix A: Status Color Reference Card ``` ● #38BDF8 ACTIVE — Agent currently processing a task ● #2DD4BF IDLE — Agent online, no active task ● #A78BFA THINKING — Agent processing/reasoning (LLM call in flight) ● #F87171 ERROR — Agent encountered an unhandled error ○ #64748B OFFLINE — Agent unreachable / session terminated ``` ## Appendix B: Quick-Jump UX Flow ``` User taps [→ Jump] on Agent Card │ ├─ Desktop/Kiosk ──► Right side drawer slides in (480px) │ Shows: live log tail, last msgs, [Open Full Session] │ └─ Mobile ──────────► Modal Bottom Sheet rises Shows: agent name, 3 recent msgs, [Open Full Session] User taps [Open Full Session] │ └─ Router navigate to /sessions/:agentId Full page Agent Session View ``` --- *Spec v1.0 — Ready for implementation. Questions → Sketch (UI/UX Agent)*