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)*
|