Skip to content

UI Design ​

Layout patterns and interaction modes for the Flightdeck frontend (React + Vite + Tailwind).

Lead Dashboard Layout ​

The lead dashboard is the primary workspace when managing a team of agents. It's split into a main chat panel and a right sidebar.

The sidebar has two zones:

  1. Decisions panel (always visible, pinned at top) — Shows pending decisions requiring user confirmation. Each decision card has the title, rationale, and Accept/Reject buttons with an optional reason text field. Optimistic UI — buttons hide immediately on click before server response for responsive feel.

  2. Tabbed panel (bottom section) — Four tabs displayed as reorderable drag-and-drop tabs:

    • Team — Compact team cards showing each agent's status and latest activity
    • Comms — Inter-agent message bus (direct messages + group messages in unified feed)
    • Groups — Chat group list and group message history, with unread badges showing per-group unread counts
    • DAG — Task dependency graph visualization

    Tab order is persisted to localStorage so the user's preferred arrangement survives page reloads. Tabs are reordered via drag-and-drop on the tab bar.

Chat Panel (Main Area) ​

The central panel serves dual purpose:

  • User ↔ Lead conversation — Messages between the human user and the project lead agent
  • Interleaved activity — Agent activity events (spawns, task completions, decisions, errors) appear inline in the chat flow, distinguished by styling. This replaces the old separate Activity tab — activity is now contextual within the conversation timeline.
  • User-message highlighting — Messages from the human user are rendered with a blue tint (bg-blue-600 text-white) to visually distinguish them from agent messages (bg-gray-800). The "Human User" label appears in text-blue-400.

Three-Tier Message Hierarchy ​

The Comms panel classifies messages into three priority tiers with visual treatment and filter toggles:

TierStyleTriggers
Critical 🔴Red accent, animated pulse dotBuild/test/compile failures, crashes, blocked tasks, P0/URGENT, timeouts, OOM, SIGTERM, segfault, decision needed
Notable 🔵Blue accentTask completed, build passes, merged, shipped, review done, progress updates, delegated, new features, fixes
Routine ⚪Dimmed/collapsedSecretary messages, short notifications (<200 chars), routine status changes

Rules:

  • Messages addressed to the lead are automatically bumped to ≥Notable
  • Content >200 characters defaults to Notable (unless matched as Critical)
  • Filter toggles: All / Important (Critical + Notable) / Critical Only

Catch-Up Summary Banner ​

A floating banner that appears when the user returns after a period of inactivity:

  1. Trigger — 60+ seconds of no interaction (clicks, keypresses)
  2. Snapshot — When the user is active, the system snapshots current counts (tasks, messages, decisions, reports)
  3. Comparison — On inactivity threshold, compares current state to snapshot
  4. Shows if — ≥5 new items accumulated OR pending decisions exist
  5. Content — "While you were away: X tasks completed, Y decisions pending (Zm old), N new messages, M reports"
  6. Dismiss — Auto-dismisses on scroll or user interaction; resets on project switch
  7. Accessibility — role="status", aria-live="polite", keyboard dismissible (Escape/Enter)

Thinking/Reasoning Text ​

When an agent emits agent_thought_chunk ACP events (thinking/reasoning text), these are displayed as collapsible reasoning blocks in both agent chats and the main lead chat:

  • Collapsed by default — Shows a 💡 icon with a preview of the first ~80 characters
  • Click to expand — Reveals the full reasoning text
  • Paragraph breaks — A pendingNewline flag in the store ensures proper paragraph separation after reasoning blocks, preventing content from being misidentified as user-directed
  • Empty text guard — CollapsibleReasoningBlock returns null for empty/whitespace-only reasoning to avoid rendering empty blocks
  • Consistent across views — Same collapsed/expandable pattern in both agent detail chats and the main lead dashboard chat

Compact Team Cards ​

Each agent on the team is represented by a single-row compact card in the Team tab:

  • Left: Role icon + agent name/role + model badge
  • Center: Current task summary (truncated) or "idle" status
  • Right: Status indicator (running/idle/exited) + latest activity shown inline

Key design choices:

  • Single row — No expanded card view. All essential info is visible at a glance without clicking.
  • Activity merged in — The latest activity for each agent (e.g., "Acquired lock on src/auth.ts") appears directly on the card, eliminating the need for a separate activity panel.
  • Activity tab removed — Activity is no longer a separate tab. It's split between team cards (per-agent latest) and the chat panel (interleaved timeline).

Agents Page ​

The Agents page (/agents) provides a system-wide view of all agents across all leads.

List View ​

A single table/list layout (the card grid view was removed for information density):

  • Each row shows: agent name, role, model selector, status, current task, plan progress
  • Model selector: Inline <select> in the table row to change an agent's model without navigating away
  • Plan progress: A compact progress indicator showing completed/total plan steps (e.g., "3/7")
  • Hierarchy: Parent-child relationships shown with tree connectors (vertical + horizontal lines) for indented child agents under their parent

Group by Project ​

Agents are grouped under their lead's project name:

  • Each group is a collapsible section with the project name as header
  • Agents without a lead appear in an "Unassigned" group
  • Collapse state persists in localStorage

This grouping makes it easy to see which agents belong to which project when multiple leads are running simultaneously.

Project Rename ​

Projects (lead agent instances) can be renamed:

  • Double-click the project name in the sidebar to enter inline edit mode
  • Pencil icon appears on hover as an alternative affordance
  • Press Enter to save, Escape to cancel
  • The rename updates the sidebar, agents page group headers, and any other references

Chat Input Modes ​

The chat input area follows specific keyboard conventions optimized for the queue-based communication model:

InputAction
EnterQueue the message — adds it to the agent's message queue for delivery on next turn
Ctrl+EnterInsert a newline in the input field (for multi-line messages)
Interrupt buttonExplicitly interrupt the agent's current turn (red button in the toolbar)

Key design rationale:

  • Enter = queue (not send immediately) because agents process messages in turns. Queuing is the normal flow.
  • Ctrl+Enter = newline (not interrupt) because interrupts are destructive and should require deliberate action, not an accidental key combo.
  • Interrupt via button only — Forces the user to make a conscious choice to interrupt, preventing accidental disruption of agent work.

Communication Flow Visualization ​

In the org chart / team hierarchy view:

  • Message items are clickable — clicking an inter-agent message in the Comms tab highlights the sender and receiver in the org chart
  • Expandable message items — Messages can be expanded to show the full content, with sender role, timestamp, and delivery status
  • Connection lines in the org chart animate briefly when a message flows between two agents
  • Group messages in comms feed — Group chat messages appear alongside 1:1 messages with a distinct visual treatment (group icon + group name)
  • Project tabs — Always-visible tab bar for switching between active projects, consistent with Timeline tabs
  • Communication heatmap tab — Third tab in the Communication section (alongside List and Matrix) showing a CommHeatmap with typed messages derived from the Org Chart's CommEntry list

Agent Identity in Messages ​

Agent messages and notes now include the agent's short ID alongside the role name — e.g., [From Developer (9b63ec92)] instead of just [From Developer]. This disambiguates messages when multiple agents share the same role.

Timeline Visualization ​

The Timeline view (/timeline) provides a swim-lane visualization of agent activity over time, built with visx.

Layout ​

  • Swim lanes — One horizontal lane per agent, labeled with role icon and agent name
  • Time axis — Horizontal axis shows elapsed time from session start
  • Status segments — Colored bars show agent status over time:
    • 🟢 Running (active, producing output)
    • 🟡 Creating (agent is being spawned)
    • ⚪ Idle (no output, shown with hatch pattern to distinguish from "not started")
    • 🔵 Completed
    • 🔴 Failed/Terminated
  • Communication links — Lines drawn between swim lanes when agents message each other, with directional arrows

Interactive Features ​

FeatureControlDescription
Brush time selectorClick-drag on mini-timelineSelect a time range to zoom into. The mini-timeline at the bottom shows the full session; the main view shows the selected range. Brush area is aligned with the chart via leftOffset to account for the label column width.
Keyboard navigation←/→ arrowsPan the view left/right through time
Zoom+/- keysZoom in/out on the time axis. Uses a liveModeRef to prevent race conditions between zoom gestures and SSE data updates.
FilteringDropdown menusFilter by agent role, communication type (direct/broadcast/group), or agent status
Live modeToggle buttonAuto-scrolls to follow the latest activity as it happens
Hover tooltipsMouse over segmentShows status badge, task label, time span (start → end), and duration. Uses @visx/tooltip with TooltipWithBounds for smart positioning.
Project tabsTab barAlways-visible project tab bar for switching between active projects. Each tab preserves its own state (zoom, filters, expanded lanes).
Clear TimelineButtonResets cached timeline data for the current project, triggering a fresh SSE reconnection.
Adaptive date displayAutomaticShows time-only (HH:MM:SS) for sessions <24 hours, date+time (MMM DD HH:MM) for multi-day sessions. Powered by the shared formatTimestamp() utility.

State Persistence ​

Timeline state is managed by a Zustand store (timelineStore.ts) that survives React Router unmounts:

  • Per-lead state — Each project/lead has its own cached data, expanded agents, and view settings
  • LRU eviction — Cached data is capped at 10 entries; oldest entries are evicted by insertion order when the limit is exceeded
  • Stable selectors — getExpandedAgents() returns a shared EMPTY_SET constant for leads with no expanded agents, avoiding unnecessary re-renders

Design Choices ​

  • visx over chart libraries — Custom swim-lane rendering required more control than standard chart libraries provide. visx gives D3-like power with React composability.
  • Idle hatch patterns — SVG <pattern> with diagonal lines (45° rotation, 6×6 pixel repeat) distinguishes "agent is idle" from "no data." Without this, users couldn't tell if an agent was waiting or simply hadn't been assigned work yet.
  • Brush selector — Long sessions can span hours. The brush selector lets users zoom into a specific time window without losing context of the full session.

Project Grouping UI ​

The Tasks view groups agents and tasks by project:

  • Collapsible project sections — Each lead's project is a collapsible group with the project name as header
  • Task dedup detection — When delegating, findSimilarActiveDelegation() checks for overlapping work using word-overlap similarity (>50% match). The lead receives a warning if a similar delegation is already active.
  • Project filtering — Filter tasks by project to focus on one workstream

DAG Gantt Chart ​

The DAG Gantt chart (DagGantt.tsx) provides a time-based visualization of task execution:

  • Scrollable — overflow-auto container with max-h-60vh for large DAGs
  • Sticky labels — Task labels stay visible (sticky left-0) during horizontal scroll
  • Zoom controls — ZoomIn/ZoomOut/Reset buttons with 1×–5× range (0.5× steps). Zoom scales inner content via minWidth: ${100 * zoom}%
  • Local timezone — fmtTime() uses toLocaleTimeString() for local timezone display
  • Title field — DAG tasks display their title alongside the task ID

The sidebar navigation shows unread indicators for group chats:

  • Blue dot/number badge — Appears next to the "Groups" navigation item when unread group messages exist
  • Per-group tracking — lastSeen timestamp per group stored in localStorage
  • Auto-reset — Visiting a group resets its unread count
  • Overflow — Shows 99+ when count exceeds 99

Mission Control ​

The Mission Control page (/mission-control) provides a single-screen project overview — answering "how's the project?" in 3 seconds.

8 Configurable Panels ​

PanelContent
HealthSummaryDAG completion %, active/idle/completed agent counts, task status breakdown
AgentFleetAll team agents with status badges (running/idle/creating), context pressure bars, model badges
TokenEconomicsPer-agent token breakdown (input/output/total), context pressure bars (80% yellow, 90% red), % of total usage
AlertsPanelProactive alerts derived from store data: context overflow, stuck agents, pending decisions >3min, failures, idle+ready mismatch, blocked tasks. Zero height when no alerts. Color-coded by severity.
ActivityFeedMerged activity + comms feed, last 30 events, live via WebSocket
DagMinimapStacked horizontal status bar (done=green, running=blue, pending=gray, failed=red), recent completions, running tasks
CommHeatmapCommunication frequency heatmap with type filter chips (Delegations, DMs, Groups, Broadcasts, Reports) — each with a colored badge. Filters auto-shown when messages include type info. Also integrated as a tab in the Org Chart communication section.
PerformanceAgent performance scorecards: throughput, first-pass rate, velocity, cost efficiency

Panel Customization ​

Panels are configurable via the Settings → Dashboard Customizer:

  • Toggle visibility — Show/hide individual panels
  • Drag-and-drop reorder — HTML5 DnD API to rearrange panel order
  • Layout persistence — Panel config stored in localStorage via useDashboardLayout hook
  • New panel auto-merge — When new panels are added in code, they automatically appear in existing users' layouts

Auto-Discovery ​

Mission Control automatically discovers lead agents from the app store and registers them in the lead store. This means:

  • No manual navigation to the Lead Dashboard is needed first
  • On app startup, both active leads (from /api/lead) and persisted projects (from /api/projects) are loaded into the store
  • Chat history for active leads is pre-fetched from /api/agents/:id/messages

Data Sources ​

All panels read from existing Zustand stores — no new API endpoints needed. WebSocket events provide real-time updates.

Token Economics ​

The Token Economics panel (also available as a tab in the Lead Dashboard) visualizes context window usage:

  • Per-agent breakdown — Input tokens, output tokens, total, percentage of overall usage
  • Context pressure bars — Visual bars showing context window utilization per agent
    • 80–90% — Yellow warning ("consider wrapping up")
    • 90%+ — Red critical ("nearing limit, may lose context")
  • Totals — Formatted as M/k (e.g., "1.2M tokens")
  • Sorted by total tokens descending

Queued Message Visibility ​

When the user sends a message while an agent is busy:

  • Messages are marked queued: true and shown in a distinct section at the bottom of the chat
  • Blue bubbles with a clock icon and "Queued" label distinguish them from delivered messages
  • Right-aligned to match user-message styling
  • Promoted to normal display when the agent responds (next agent message after queued user messages)

Agent Chat Reply Highlighting ​

In the agent chat view (AcpOutput.tsx), agent responses to user messages receive subtle visual emphasis:

  • When the previous timeline item is a user message, the agent's reply gets bg-blue-500/[0.06] background + border-l-2 border-l-blue-400/30 left border
  • Creates a visual connection between user input and agent response
  • Applied to both rich content (images, code) and text messages

Theme System ​

The theme system supports three modes: Light, Dark, and Follow System.

Implementation ​

  • CSS custom properties — 10 semantic tokens (--th-bg, --th-text, --th-border, etc.) defined on :root, .light and .dark selectors
  • Tailwind th.* namespace — Semantic color tokens referencing CSS variables with <alpha-value> opacity support
  • Zustand settingsStore — Theme state persists to localStorage via shared store (not component-local state)
  • Follow System mode — Listens to prefers-color-scheme media query changes via matchMedia and auto-applies the corresponding theme
  • No FOWT — An inline <head> script reads localStorage and sets the dark class before React renders, avoiding flash-of-wrong-theme
  • Accent color contrast — dark: variants used for accent colors (e.g., text-yellow-600 dark:text-yellow-300) to ensure WCAG contrast in both themes

Theme Toggle Locations ​

  • Settings panel — Three radio buttons (Light / Dark / System)
  • Command palette — Toggle Dark Mode action (Cmd+K → type "theme")

Command Palette ​

A global command palette accessible via Cmd+K (or Ctrl+K on Windows/Linux).

  • 12 navigation/action commands — Navigate to any page, toggle theme, create project, search
  • Fuzzy matching — Type to filter commands
  • Keyboard navigation — Arrow keys to select, Enter to execute, Escape to dismiss
  • Extensible — Commands registered as a simple array of {label, action, shortcut?} objects

Settings ​

The Settings panel (/settings) provides configuration for:

SettingDescription
Max Concurrent AgentsSlider from 1–50 (server default is 50)
ThemeLight / Dark / Follow System with persistence
SoundToggle notification sounds
Dashboard PanelsDrag-and-drop reorder, toggle visibility of Mission Control panels
Custom RolesCreate roles with custom system prompts, colors, icons, and default models

Onboarding Wizard ​

An 8-step guided tour for new users, rendered as a modal overlay:

  1. Welcome — Overview of Flightdeck
  2. Create Project — How to start a project
  3. Lead Dashboard — Understanding the chat + sidebar layout
  4. Team Management — Agent roles and models
  5. Communication — Messaging, groups, broadcasts
  6. Task DAG — Understanding dependency graphs
  7. Mission Control — Monitoring dashboard overview
  8. Settings — Customization options

Search Dialog ​

A global search dialog that searches across:

  • Messages — Agent and user messages
  • Tasks — DAG tasks and delegations
  • Decisions — Architectural decisions
  • Activity — Activity log entries

Powered by the server-side SearchEngine which provides full-text search with relevance scoring.

Zustand Store Patterns ​

Critical patterns for zustand v5 stores used throughout the frontend:

Anti-Patterns (cause infinite re-renders) ​

PatternProblemFix
useStore() without selectorSubscribes to entire state; any set() triggers re-renderUse useShallow or specific field selectors
useStore((s) => Object.keys(s.x))Object.keys() returns new array every callSelect s.x directly, derive keys outside
useStore((s) => s.foo ?? [])[] is a new reference when foo is undefinedUse module-level constant: const EMPTY: T[] = []
useStore((s) => s.items.filter(...)).filter() / .map() always returns new arrayUse useShallow or memoize outside selector

Correct Patterns ​

tsx
// Good: specific field selector
const projects = useLeadStore((s) => s.projects);
const projectIds = Object.keys(projects);

// Good: useShallow for multiple fields
const { messages, decisions } = useLeadStore(useShallow((s) => ({
  messages: s.projects[id]?.messages,
  decisions: s.projects[id]?.decisions,
})));

// Good: module-level empty constant
const EMPTY_ACTIVITIES: Activity[] = [];
const activities = useLeadStore((s) => s.projects[id]?.activities ?? EMPTY_ACTIVITIES);
Documentation generated by AI

Documentation generated by AI