512 lines
20 KiB
Markdown
512 lines
20 KiB
Markdown
# 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)*
|