{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-timeline",
  "title": "Agent Timeline",
  "description": "A vertical timeline of agent steps with live status, a spring-filled connector, and collapsible reasoning or tool-output bodies that morph open.",
  "dependencies": ["framer-motion"],
  "registryDependencies": ["@godui/godui-theme"],
  "files": [
    {
      "path": "packages/components/src/agent-timeline/agent-timeline.tsx",
      "content": "\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport * as React from \"react\";\n\nexport type StepStatus = \"pending\" | \"running\" | \"success\" | \"error\";\n\nexport type AgentTimelineProps = React.HTMLAttributes<HTMLOListElement>;\n\nconst AgentTimeline = React.forwardRef<HTMLOListElement, AgentTimelineProps>(\n  ({ className, children, ...props }, ref) => (\n    <ol\n      ref={ref}\n      data-slot=\"agent-timeline\"\n      className={`flex flex-col ${className ?? \"\"}`}\n      {...props}\n    >\n      {children}\n    </ol>\n  ),\n);\nAgentTimeline.displayName = \"AgentTimeline\";\n\nexport type AgentStepProps = Omit<\n  React.HTMLAttributes<HTMLLIElement>,\n  \"title\"\n> & {\n  status?: StepStatus;\n  title: React.ReactNode;\n  /** Optional sub-label (tool name, duration, …). */\n  meta?: React.ReactNode;\n  /** Collapsible body (reasoning text, tool I/O). */\n  children?: React.ReactNode;\n  defaultOpen?: boolean;\n  /** Hide the connector below this step (use on the last item). */\n  last?: boolean;\n};\n\nconst STATUS_RING: Record<StepStatus, string> = {\n  pending: \"border-border bg-background text-muted-foreground\",\n  running: \"border-primary bg-primary/10 text-primary\",\n  success: \"border-primary bg-primary text-primary-foreground\",\n  error: \"border-destructive bg-destructive text-white\",\n};\n\nconst CONNECTOR_FILL: Record<StepStatus, string> = {\n  pending: \"bg-border\",\n  running: \"bg-gradient-to-b from-primary to-border\",\n  success: \"bg-primary\",\n  error: \"bg-destructive\",\n};\n\nconst AgentStep = React.forwardRef<HTMLLIElement, AgentStepProps>(\n  (\n    {\n      status = \"pending\",\n      title,\n      meta,\n      children,\n      defaultOpen = false,\n      last = false,\n      className,\n      ...props\n    },\n    ref,\n  ) => {\n    const reduce = useReducedMotion();\n    const [open, setOpen] = React.useState(defaultOpen);\n    const hasBody = Boolean(children);\n\n    return (\n      <li\n        ref={ref}\n        data-slot=\"agent-step\"\n        data-status={status}\n        className={`relative flex gap-3 pb-2 ${className ?? \"\"}`}\n        {...props}\n      >\n        {/* Rail */}\n        <div className=\"relative flex flex-col items-center\">\n          <span\n            className={`relative z-raised flex size-6 shrink-0 items-center justify-center rounded-full border ${STATUS_RING[status]} transition-colors`}\n          >\n            {status === \"running\" ? (\n              <span className=\"size-2.5 animate-spin rounded-full border-2 border-current border-t-transparent motion-reduce:animate-none\" />\n            ) : status === \"success\" ? (\n              <CheckIcon className=\"size-3.5\" />\n            ) : status === \"error\" ? (\n              <CloseIcon className=\"size-3.5\" />\n            ) : (\n              <span className=\"size-1.5 rounded-full bg-current\" />\n            )}\n            {status === \"running\" ? (\n              <span className=\"absolute inset-0 animate-ping rounded-full border border-primary/50 motion-reduce:animate-none\" />\n            ) : null}\n          </span>\n          {!last ? (\n            <span className=\"relative my-1 w-px flex-1 overflow-hidden rounded bg-border\">\n              <motion.span\n                initial={false}\n                animate={{\n                  scaleY:\n                    status === \"success\" || status === \"error\"\n                      ? 1\n                      : status === \"running\"\n                        ? 0.5\n                        : 0,\n                }}\n                transition={\n                  reduce\n                    ? { duration: 0 }\n                    : { type: \"spring\", stiffness: 320, damping: 32, mass: 0.9 }\n                }\n                className={`absolute inset-0 origin-top ${CONNECTOR_FILL[status]}`}\n              />\n            </span>\n          ) : null}\n        </div>\n\n        {/* Content */}\n        <div className=\"min-w-0 flex-1 pb-2\">\n          <button\n            type=\"button\"\n            disabled={!hasBody}\n            aria-expanded={hasBody ? open : undefined}\n            onClick={() => hasBody && setOpen((o) => !o)}\n            className={`flex w-full items-center gap-2 rounded-md py-0.5 text-left ${hasBody ? \"cursor-pointer hover:opacity-80\" : \"cursor-default\"}`}\n          >\n            <span\n              className={`truncate text-sm font-medium ${status === \"error\" ? \"text-destructive\" : \"text-foreground\"} ${status === \"running\" ? \"text-muted-foreground motion-safe:animate-pulse\" : \"\"}`}\n            >\n              {title}\n            </span>\n            {meta ? (\n              <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\">\n                {meta}\n              </span>\n            ) : null}\n            {hasBody ? (\n              <ChevronIcon\n                className={`ml-auto size-4 shrink-0 text-muted-foreground transition-transform ${open ? \"rotate-90\" : \"\"}`}\n              />\n            ) : null}\n          </button>\n\n          <AnimatePresence initial={false}>\n            {hasBody && open ? (\n              <motion.div\n                initial={{ height: 0, opacity: 0 }}\n                animate={{ height: \"auto\", opacity: 1 }}\n                exit={{ height: 0, opacity: 0 }}\n                transition={\n                  reduce\n                    ? { duration: 0 }\n                    : { type: \"spring\", stiffness: 320, damping: 32, mass: 0.9 }\n                }\n                className=\"overflow-hidden\"\n              >\n                <div className=\"mt-1.5 rounded-lg border border-border bg-muted/40 p-3 text-xs leading-5 text-muted-foreground\">\n                  {children}\n                </div>\n              </motion.div>\n            ) : null}\n          </AnimatePresence>\n        </div>\n      </li>\n    );\n  },\n);\nAgentStep.displayName = \"AgentStep\";\n\ntype IconProps = { className?: string };\nfunction CheckIcon({ className }: IconProps) {\n  return (\n    <svg\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={3}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <path d=\"M20 6 9 17l-5-5\" />\n    </svg>\n  );\n}\nfunction CloseIcon({ className }: IconProps) {\n  return (\n    <svg\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={3}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <path d=\"M18 6 6 18M6 6l12 12\" />\n    </svg>\n  );\n}\nfunction ChevronIcon({ className }: IconProps) {\n  return (\n    <svg\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <path d=\"m9 18 6-6-6-6\" />\n    </svg>\n  );\n}\n\nexport { AgentStep, AgentTimeline };\n",
      "type": "registry:ui",
      "target": "components/godui/agent-timeline.tsx"
    }
  ],
  "type": "registry:ui"
}
