Files
Control-Center/design/command-hub-spec.md

512 lines
20 KiB
Markdown
Raw Permalink Normal View History

2026-04-25 19:02:57 +00:00
# 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`. 35 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 | 0599px | Mobile | Bottom Nav | 1 |
| Medium | 6001023px | 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; // 0100
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)*