PlanVisualizer

AG-UI React Live collapsible hierarchical tree for agent plans. Streams in node-by-node, animates state transitions, supports keyboard nav. When your agent says "I'm going to do X → Y → Z, and Y has 3 substeps" — this is how you render it.

idle
① Live plan stream (~15s)
② Side-by-side themes (mid-run state)
Light
Dark
③ Failure + cancelled states
Failed mid-plan
Cancelled branch

Copy-paste snippet

// Controlled mode — pass plan tree directly
import { PlanVisualizer } from './PlanVisualizer.jsx';

const plan = {
  id: 'root', label: 'Research competitor pricing',
  status: 'in_progress',
  children: [
    { id: 'crawl', label: 'Crawl competitor pages', status: 'completed', result: 'Found 14 pricing pages' },
    { id: 'extract', label: 'Extract pricing data', status: 'in_progress',
      children: [
        { id: 'e1', label: 'Parse pricing tables',    status: 'completed' },
        { id: 'e2', label: 'Normalize currency',      status: 'in_progress' },
        { id: 'e3', label: 'Detect plan tiers',      status: 'proposed' },
      ]
    },
    { id: 'report', label: 'Generate report',      status: 'proposed' },
  ]
};

return <PlanVisualizer
  plan={plan}
  theme="light"
  defaultExpanded={true}
  density="compact"
  onNodeClick={(node) => console.log(node.id)}
/>;

// AG-UI stream mode — auto-wired to event bus
import { PlanVisualizerContainer } from './PlanVisualizer.jsx';

const bus = new EventTarget();

// Agent dispatches plan proposal
bus.dispatchEvent(new CustomEvent('PLAN_PROPOSED', {
  detail: { plan: { id: 'root', label: 'Main task', status: 'proposed', children: [] } }
}));

// Node streams in under parent
bus.dispatchEvent(new CustomEvent('PLAN_NODE_ADDED', {
  detail: { parentId: 'root', node: { id: 'step-1', label: 'Fetch', status: 'proposed' } }
}));

// Node starts executing
bus.dispatchEvent(new CustomEvent('PLAN_NODE_UPDATED', {
  detail: { nodeId: 'step-1', patch: { status: 'in_progress', description: 'Fetching from API…' } }
}));

// Node completes with result
bus.dispatchEvent(new CustomEvent('PLAN_NODE_COMPLETED', {
  detail: { nodeId: 'step-1' }
}));

bus.dispatchEvent(new CustomEvent('PLAN_NODE_FAILED', {
  detail: { nodeId: 'step-2' }
}));

return <PlanVisualizerContainer
  eventSource={bus}
  theme="light"
  defaultExpanded={true}
  density="compact"
/>;

// PlanNode status values:
// 'proposed'     → dashed circle + "proposed" badge (awaiting execution)
// 'in_progress'  → animated dot + "running" badge + soft glow
// 'completed'    → green check + "done" badge
// 'failed'       → red × + "failed" badge
// 'cancelled'    → slash circle + strikethrough label

// Keyboard nav: Arrow keys to traverse, Enter/Space to expand/collapse