# Circuit: Agent Orchestrator

> Issues, threads and tickets go into an orchestrator, which hands each one to the next free agent with `fanout="one"`. Every card is a plain `div` with `data-circuit`; they light up on `data-circuit-pulse` and the counters tick through `onArrive`. Hover a card to see the path through it. Narrow the frame and the columns become rows: the traces turn to run top to bottom by themselves.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/circuit-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/circuit-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/circuit-demo-light-4cf0acaf6bd8b42b.png
- Registry item: https://keyframeui.com/r/circuit-demo.json
- Categories: diagram, svg, interaction, landing
- Listed: 2026-10-15, source last changed 2026-10-15

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/keyframeui/circuit-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/circuit.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/circuit#circuit-demo

## Files

### `registry/demos/circuit-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { Circuit } from "@/components/ui/circuit";

const SOURCES = [
  { id: "github", name: "GitHub", meta: "issue #482", icon: "M8 3.5a4.5 4.5 0 1 0 0 9 4.5 4.5 0 0 0 0-9Zm0 3a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Z" },
  { id: "slack", name: "Slack", meta: "#support", icon: "M3 4.5A1.5 1.5 0 0 1 4.5 3h7A1.5 1.5 0 0 1 13 4.5v5a1.5 1.5 0 0 1-1.5 1.5H7l-3 2.5V11h.5A1.5 1.5 0 0 1 3 9.5Z" },
  { id: "linear", name: "Linear", meta: "ENG-219", icon: "M3 5.5 5.5 3h7.5v7.5L10.5 13H3Zm3 1.5h4M6 9h2.5" },
];

const AGENTS = [
  { id: "coder", name: "Coder", verb: "patched" },
  { id: "reviewer", name: "Reviewer", verb: "reviewed" },
  { id: "tester", name: "Tester", verb: "verified" },
];

// What every card shares: dims when another card is hovered, lights when a signal lands.
const card =
  "rounded-xl border border-zinc-200 bg-white shadow-xs transition-[opacity,border-color,box-shadow] duration-300 dark:border-zinc-800 dark:bg-zinc-900 data-[circuit-state=dimmed]:opacity-45 data-[circuit-state=active]:border-zinc-400 dark:data-[circuit-state=active]:border-zinc-600 data-circuit-pulse:border-sky-400 data-circuit-pulse:shadow-[0_0_0_4px_rgb(14_165_233/0.14)] dark:data-circuit-pulse:border-sky-500";

function Icon({ d }: { d: string }) {
  return (
    <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth={1.4} strokeLinejoin="round" className="size-4" aria-hidden="true">
      <path d={d} />
    </svg>
  );
}

export function CircuitDemo() {
  const [done, setDone] = React.useState<Record<string, number>>({ coder: 14, reviewer: 9, tester: 11 });
  const [routed, setRouted] = React.useState(128);

  return (
    <div className="@container w-full max-w-4xl">
      <Circuit
        flow="github, slack, linear -> orchestrator -> coder, reviewer, tester"
        fanout="one"
        onArrive={(node) => {
          if (node === "orchestrator") setRouted((n) => n + 1);
          else setDone((d) => ({ ...d, [node]: (d[node] ?? 0) + 1 }));
        }}
        className="grid items-center gap-y-12 px-1 py-2 @3xl:grid-cols-[1fr_minmax(0,15rem)_1fr] @3xl:gap-x-16"
      >
        <div className="grid grid-cols-3 gap-2.5 @3xl:grid-cols-1 @3xl:gap-3">
          {SOURCES.map((s) => (
            <div key={s.id} data-circuit={s.id} tabIndex={0} className={`${card} flex flex-col items-center gap-1.5 px-2 py-3 outline-none focus-visible:ring-2 focus-visible:ring-sky-400/60 @3xl:flex-row @3xl:gap-3 @3xl:px-3.5 @3xl:py-3`}>
              <span className="grid size-7 shrink-0 place-items-center rounded-lg bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300">
                <Icon d={s.icon} />
              </span>
              <span className="flex min-w-0 flex-col items-center @3xl:items-start">
                <span className="text-sm font-medium text-zinc-900 dark:text-zinc-100">{s.name}</span>
                <span className="font-mono text-[11px] text-zinc-500">{s.meta}</span>
              </span>
            </div>
          ))}
        </div>

        <div data-circuit="orchestrator" tabIndex={0} className={`${card} mx-auto w-full max-w-xs p-4 outline-none focus-visible:ring-2 focus-visible:ring-sky-400/60`}>
          <div className="flex items-center justify-between">
            <span className="rounded-md bg-sky-500/10 px-1.5 py-0.5 font-mono text-[10px] font-medium tracking-wide text-sky-700 dark:text-sky-300">
              ORCHESTRATOR
            </span>
            <span className="relative flex size-2">
              <span className="absolute inset-0 animate-ping rounded-full bg-emerald-400/60 motion-reduce:animate-none" />
              <span className="relative size-2 rounded-full bg-emerald-500" />
            </span>
          </div>
          <p className="mt-3 text-[15px] font-semibold tracking-tight text-zinc-900 dark:text-zinc-100">Triage &amp; assign</p>
          <p className="mt-1 text-xs leading-relaxed text-zinc-500 dark:text-zinc-400">
            Dedupes the event, pulls repo context and hands it to the next free agent.
          </p>
          <p className="mt-3 border-t border-zinc-100 pt-2.5 font-mono text-[11px] text-zinc-500 tabular-nums dark:border-zinc-800">
            {routed} routed today
          </p>
        </div>

        <div className="grid grid-cols-3 gap-2.5 @3xl:grid-cols-1 @3xl:gap-3">
          {AGENTS.map((a) => (
            <div key={a.id} data-circuit={a.id} tabIndex={0} className={`${card} flex flex-col items-center gap-0.5 px-2 py-3 outline-none focus-visible:ring-2 focus-visible:ring-sky-400/60 @3xl:flex-row @3xl:justify-between @3xl:px-3.5`}>
              <span className="flex items-center gap-2 text-sm font-medium text-zinc-900 dark:text-zinc-100">
                <span className="hidden size-1.5 rounded-full bg-sky-500 @3xl:block" />
                {a.name}
              </span>
              <span className="font-mono text-[11px] text-zinc-500 tabular-nums">
                {done[a.id]} {a.verb}
              </span>
            </div>
          ))}
        </div>
      </Circuit>
    </div>
  );
}
```
