{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-status",
  "type": "registry:ui",
  "title": "Agent Status",
  "description": "The canonical agent-state indicator — waiting/working/ready/done/error, as dot or chip.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://seamui.dev/r/utils.json",
    "https://seamui.dev/r/badge.json"
  ],
  "files": [
    {
      "path": "registry/seam/ui/agent-status.tsx",
      "content": "import type * as React from \"react\"\nimport { Check, CircleAlert, Eye, X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Badge } from \"./badge\"\n\n// The canonical agent-state machine — the vocabulary session sidebars,\n// headers, hover cards, and terminal blocks all share:\n// waiting (on you) / working / ready (to review) / done / error.\ntype AgentState = \"waiting\" | \"working\" | \"ready\" | \"done\" | \"error\"\n\n// The theme is monochrome, so state reads through shape + animation, not hue:\n// waiting = filled + attention halo, working = filled + ambient pulse,\n// ready = hollow, done = faint, error = the one sanctioned color.\n// The working pulse is CSS animate-pulse — the Spinner precedent for ambient\n// loops: opacity-only, so it reads identically under reduced motion, and it\n// keeps running inside AnimatePresence parents where motion.dev repeat stalls.\nconst DOT: Record<AgentState, string> = {\n  waiting: \"bg-primary ring-[3px] ring-primary/20\",\n  working: \"bg-primary animate-pulse\",\n  ready: \"border-2 border-primary bg-transparent\",\n  done: \"bg-muted-foreground/40\",\n  error: \"bg-destructive\",\n}\n\nconst LABELS: Record<AgentState, string> = {\n  waiting: \"Waiting on you\",\n  working: \"Working\",\n  ready: \"Ready to review\",\n  done: \"Done\",\n  error: \"Error\",\n}\n\n// Bare dot for dense rows (session lists, tab strips). With an aria-label it\n// announces as a status; without one it's decoration next to visible text.\nfunction AgentStatusDot({\n  status,\n  className,\n  ...props\n}: React.ComponentProps<\"span\"> & { status: AgentState }) {\n  const labelled = props[\"aria-label\"] !== undefined\n\n  return (\n    <span\n      data-slot=\"agent-status-dot\"\n      data-status={status}\n      role={labelled ? \"status\" : undefined}\n      aria-hidden={labelled ? undefined : true}\n      className={cn(\n        \"inline-block size-2 shrink-0 rounded-full\",\n        DOT[status],\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nconst CHIP: Record<\n  AgentState,\n  {\n    variant: React.ComponentProps<typeof Badge>[\"variant\"]\n    icon: React.ReactNode\n  }\n> = {\n  // waiting is the loudest thing in a row — an embossed primary key.\n  waiting: { variant: \"default\", icon: <CircleAlert className=\"size-3\" /> },\n  working: {\n    variant: \"secondary\",\n    icon: <AgentStatusDot status=\"working\" className=\"size-1.5\" />,\n  },\n  ready: { variant: \"secondary\", icon: <Eye className=\"size-3\" /> },\n  // done settles into the surface — the debossed muted chip.\n  done: { variant: \"muted\", icon: <Check className=\"size-3\" /> },\n  error: { variant: \"destructive\", icon: <X className=\"size-3\" /> },\n}\n\n// Labeled chip on Badge — the ToolStatus pattern promoted to session level.\n// Announced politely so a state change is heard without interrupting.\nfunction AgentStatus({\n  status,\n  className,\n  children,\n  ...props\n}: Omit<React.ComponentProps<typeof Badge>, \"variant\"> & {\n  status: AgentState\n}) {\n  const s = CHIP[status]\n  return (\n    <Badge\n      data-slot=\"agent-status\"\n      data-status={status}\n      variant={s.variant}\n      aria-live=\"polite\"\n      className={cn(\"gap-1.5\", className)}\n      {...props}\n    >\n      {s.icon}\n      {children ?? LABELS[status]}\n    </Badge>\n  )\n}\n\nexport { AgentStatus, AgentStatusDot, type AgentState }\n",
      "type": "registry:ui",
      "target": "components/ui/agent-status.tsx"
    }
  ]
}