{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "trace",
  "title": "Trace",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "The one row behind reasoning, tool calls, sources and plans: an icon, a title and optional detail, with running, error and pending states, rows that expand, link or open a dialog, and shadcn's Trace API.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/shimmer-text.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/trace.tsx",
      "content": "// Ballmac UI: Trace. https://ui.ballmac.com/components/trace\n// Based on shadcn/ui's Trace (MIT, Copyright (c) 2023 shadcn): the same parts and props, restyled with tokens, a shimmer that follows the theme, and no Radix UI.\n\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronRight } from \"lucide-react\"\n\nimport { ShimmerText } from \"@/components/ballmac/shimmer-text\"\nimport { cn } from \"@/lib/utils\"\n\ntype TraceStatus = \"running\" | \"error\" | \"pending\"\n\nconst StatusContext = React.createContext<TraceStatus | undefined>(undefined)\n\n/** A list of rows. It also styles any trigger or link that wraps a `TraceLine`, so a row looks the same whatever it does. */\nfunction Trace({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"trace\"\n      className={cn(\n        \"flex w-full min-w-0 flex-col text-sm\",\n        \"*:has-[>[data-slot=trace-line]]:flex *:has-[>[data-slot=trace-line]]:w-fit *:has-[>[data-slot=trace-line]]:max-w-full *:has-[>[data-slot=trace-line]]:min-w-0 *:has-[>[data-slot=trace-line]]:text-start *:has-[>[data-slot=trace-line]]:outline-none\",\n        \"*:has-[>[data-slot=trace-line]]:rounded-md *:has-[>[data-slot=trace-line]]:focus-visible:ring-[3px] *:has-[>[data-slot=trace-line]]:focus-visible:ring-ring/50\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\ntype TraceLineProps = React.ComponentProps<\"span\"> & {\n  /** The state of the work the line reports. Say it in the title too: color and motion alone are not read out. */\n  status?: TraceStatus\n}\n\n/** One line: an icon and a title. It is a span, so it is not a button or a tab stop: wrap it in a trigger or a link to make it do something. */\nfunction TraceLine({ className, status, ...props }: TraceLineProps) {\n  return (\n    <StatusContext.Provider value={status}>\n      <span\n        data-slot=\"trace-line\"\n        data-status={status}\n        className={cn(\n          \"flex w-fit min-w-0 max-w-full items-center gap-2 py-1 text-muted-foreground transition-colors motion-reduce:transition-none\",\n          \"in-[a,button]:cursor-pointer in-[a:hover,button:hover]:text-foreground\",\n          \"data-[status=error]:text-destructive data-[status=error]:in-[a:hover,button:hover]:text-destructive data-[status=pending]:opacity-60\",\n          className\n        )}\n        {...props}\n      />\n    </StatusContext.Provider>\n  )\n}\n\n/** A decorative icon slot, hidden from assistive technology. Before the title it names the action; after it, it is the indicator. */\nfunction TraceIcon({ className, ...props }: React.ComponentProps<\"span\">) {\n  return <span data-slot=\"trace-icon\" aria-hidden=\"true\" className={cn(\"flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\", className)} {...props} />\n}\n\n/** One sentence on one line. It truncates, and shimmers while the line is running (and holds still when motion is reduced). */\nfunction TraceTitle({ className, children, ...props }: React.ComponentProps<\"span\">) {\n  const status = React.useContext(StatusContext)\n  return (\n    <span data-slot=\"trace-title\" className={cn(\"min-w-0 truncate\", className)} {...props}>\n      {status === \"running\" && typeof children === \"string\" ? <ShimmerText className=\"max-w-full truncate align-bottom\">{children}</ShimmerText> : children}\n    </span>\n  )\n}\n\n/** Secondary text after the title, such as a count or a duration. It does not truncate. */\nfunction TraceDescription({ className, ...props }: React.ComponentProps<\"span\">) {\n  return <span data-slot=\"trace-description\" className={cn(\"shrink-0 tabular-nums text-muted-foreground/70\", className)} {...props} />\n}\n\n// ------------------------------------------------------------------------------------------------- the row that expands\n\ntype CollapsibleState = { open: boolean; setOpen: (open: boolean) => void; contentId: string; triggerId: string; disabled: boolean }\nconst CollapsibleContext = React.createContext<CollapsibleState | null>(null)\nconst useRow = () => {\n  const c = React.useContext(CollapsibleContext)\n  if (!c) throw new Error(\"TraceCollapsibleTrigger and TraceCollapsibleContent must be used inside <TraceCollapsible>.\")\n  return c\n}\n\ntype TraceCollapsibleProps = Omit<React.ComponentProps<\"div\">, \"defaultValue\"> & {\n  /** The controlled open state. Pass it with `onOpenChange`. */\n  open?: boolean\n  /** Whether the row starts open. */\n  defaultOpen?: boolean\n  onOpenChange?: (open: boolean) => void\n  disabled?: boolean\n}\n\n/** A row that expands. It takes the props of a collapsible: `open`, `defaultOpen`, `onOpenChange` and `disabled`. */\nfunction TraceCollapsible({ open: openProp, defaultOpen = false, onOpenChange, disabled = false, className, children, ...props }: TraceCollapsibleProps) {\n  const [inner, setInner] = React.useState(defaultOpen)\n  const open = openProp ?? inner\n  const id = React.useId()\n  const setOpen = React.useCallback(\n    (next: boolean) => {\n      if (openProp === undefined) setInner(next)\n      onOpenChange?.(next)\n    },\n    [openProp, onOpenChange]\n  )\n  const value = React.useMemo(() => ({ open, setOpen, contentId: `${id}-content`, triggerId: `${id}-trigger`, disabled }), [open, setOpen, id, disabled])\n  return (\n    <CollapsibleContext.Provider value={value}>\n      <div data-slot=\"trace-collapsible\" data-state={open ? \"open\" : \"closed\"} className={cn(\"flex w-full min-w-0 flex-col\", className)} {...props}>\n        {children}\n      </div>\n    </CollapsibleContext.Provider>\n  )\n}\n\ntype TraceCollapsibleTriggerProps = Omit<React.ComponentProps<\"button\">, \"type\">\n\n/** The button that opens and closes the row. Put a `TraceLine` inside it; it adds a chevron after the line. */\nfunction TraceCollapsibleTrigger({ className, children, onClick, ...props }: TraceCollapsibleTriggerProps) {\n  const row = useRow()\n  return (\n    <button\n      type=\"button\"\n      id={row.triggerId}\n      data-slot=\"trace-collapsible-trigger\"\n      data-state={row.open ? \"open\" : \"closed\"}\n      aria-expanded={row.open}\n      aria-controls={row.contentId}\n      disabled={row.disabled}\n      onClick={(e) => {\n        onClick?.(e)\n        if (!e.defaultPrevented) row.setOpen(!row.open)\n      }}\n      className={cn(\n        \"flex w-fit min-w-0 max-w-full cursor-pointer items-center gap-2 rounded-md text-start text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 has-[[data-status=error]]:text-destructive motion-reduce:transition-none\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      <TraceIcon className=\"-ms-1\">\n        <ChevronRight className={cn(\"transition-transform duration-200 motion-reduce:transition-none\", row.open ? \"rotate-90\" : \"rtl:rotate-180\")} />\n      </TraceIcon>\n    </button>\n  )\n}\n\ntype TraceCollapsibleContentProps = React.ComponentProps<\"div\">\n\n/** The detail of a row that expands. Closed, it is out of the tab order and hidden from screen readers. Its text is muted: give blocks inside it `text-foreground`. */\nfunction TraceCollapsibleContent({ className, children, ...props }: TraceCollapsibleContentProps) {\n  const row = useRow()\n  return (\n    <div\n      id={row.contentId}\n      role=\"region\"\n      aria-labelledby={row.triggerId}\n      data-slot=\"trace-collapsible-content\"\n      data-state={row.open ? \"open\" : \"closed\"}\n      inert={!row.open}\n      aria-hidden={!row.open || undefined}\n      className={cn(\"grid min-w-0 transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none\", row.open ? \"grid-rows-[1fr]\" : \"grid-rows-[0fr]\")}\n    >\n      <div className=\"min-h-0 overflow-hidden\">\n        <div data-slot=\"trace-collapsible-content-inner\" className={cn(\"min-w-0 break-words py-1 text-muted-foreground\", className)} {...props}>\n          {children}\n        </div>\n      </div>\n    </div>\n  )\n}\n\nexport {\n  Trace,\n  TraceCollapsible,\n  TraceCollapsibleContent,\n  TraceCollapsibleTrigger,\n  TraceDescription,\n  TraceIcon,\n  TraceLine,\n  TraceTitle,\n  type TraceCollapsibleContentProps,\n  type TraceCollapsibleProps,\n  type TraceCollapsibleTriggerProps,\n  type TraceLineProps,\n  type TraceStatus,\n}\n",
      "type": "registry:ui",
      "target": "@components/ballmac/trace.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "trace",
      "agent",
      "reasoning",
      "tool call",
      "chain of thought",
      "steps",
      "shadcn"
    ],
    "version": "1.0.0",
    "updated": "2026-10-19",
    "tests": true,
    "ai": {
      "summary": "<Trace> holds rows. A row is <TraceLine status><TraceIcon/><TraceTitle/><TraceDescription/></TraceLine>. Wrap it in <TraceCollapsibleTrigger> inside a <TraceCollapsible> to expand, or in a link, or a dialog's trigger. The parts and props match shadcn's Trace.",
      "whenToUse": [
        "Showing what an AI agent did: reasoning, tool calls, searches, file edits",
        "Plans, task lists and sources where every row should look the same",
        "Rows that expand, link out or open a dialog or sheet"
      ],
      "whenNotToUse": [
        "The agent's reply text; use message or ai-message",
        "A single tool call with its full input and output; use tool-call-card",
        "A general disclosure; use collapsible or accordion"
      ],
      "composesWith": [
        "code-block",
        "questionnaire",
        "dialog",
        "shimmer-text",
        "ai-message"
      ],
      "a11y": [
        {
          "keys": "Enter / Space",
          "action": "On a TraceCollapsibleTrigger, opens or closes the row (aria-expanded and aria-controls are set)"
        },
        {
          "keys": "Tab",
          "action": "A TraceLine on its own is text and not a tab stop; the trigger, link or dialog trigger around it is"
        },
        {
          "keys": "Screen readers",
          "action": "Icons are hidden, so the title is the name: write a sentence (Edited trace.tsx) and put the state in it (Running pnpm build). Closed content is inert"
        },
        {
          "keys": "Reduced motion",
          "action": "The shimmer and the chevron turn stop; the title still says what is happening"
        }
      ],
      "customization": [
        "status on TraceLine: running | error | pending",
        "open, defaultOpen, onOpenChange, disabled on TraceCollapsible",
        "className on every part"
      ]
    },
    "statesNone": "A row shows the state of work reported to it (running, error, pending); it loads and fetches nothing itself.",
    "source": {
      "name": "shadcn/ui Trace",
      "url": "https://github.com/shadcn-ui/ui",
      "license": "MIT",
      "copyright": "Copyright (c) 2023 shadcn",
      "modified": true
    },
    "examples": [
      "trace-demo",
      "trace-status",
      "trace-collapsible",
      "trace-steps",
      "trace-sources",
      "trace-dialog",
      "trace-agent-turn"
    ],
    "url": "https://ui.ballmac.com/components/trace"
  },
  "categories": [
    "ai"
  ],
  "type": "registry:ui"
}