{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tool-call-card",
  "title": "Tool Call Card",
  "description": "A collapsible card for one AI tool call, showing its name, status and duration, with the input and result as formatted JSON.",
  "dependencies": [
    "lucide-react",
    "@radix-ui/react-collapsible"
  ],
  "files": [
    {
      "path": "registry/tealmac/tool-call-card.tsx",
      "content": "// Adapted from Ballmac UI (https://github.com/vamsiy78/ballmac-ui), MIT License, Copyright (c) 2026 Ballmac.\n\"use client\"\n\nimport * as React from \"react\"\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\"\nimport {\n  ChevronRight,\n  CircleCheck,\n  CircleDashed,\n  CircleX,\n  LoaderCircle,\n  Wrench,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype ToolCallStatus = \"pending\" | \"running\" | \"success\" | \"error\"\n\nconst statusConfig: Record<\n  ToolCallStatus,\n  { label: string; icon: React.ElementType; className: string }\n> = {\n  pending: {\n    label: \"Pending\",\n    icon: CircleDashed,\n    className: \"text-muted-foreground\",\n  },\n  running: {\n    label: \"Running\",\n    icon: LoaderCircle,\n    className: \"text-foreground\",\n  },\n  success: { label: \"Success\", icon: CircleCheck, className: \"text-chart-3\" }, // icon only; chart-3 is too light for small text\n  error: { label: \"Error\", icon: CircleX, className: \"text-destructive\" },\n}\n\n/** 340 -> \"340ms\", 1240 -> \"1.2s\", 72000 -> \"1m 12s\". Locale-independent so SSR and the browser agree. */\nfunction formatToolDuration(ms: number) {\n  if (ms < 1000) return `${Math.max(0, Math.round(ms))}ms`\n  if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`\n  const s = Math.round(ms / 1000)\n  return `${Math.floor(s / 60)}m ${s % 60}s`\n}\n\nfunction formatPayload(value: unknown) {\n  if (typeof value === \"string\") return value\n  try {\n    return JSON.stringify(value, null, 2) ?? String(value)\n  } catch {\n    return String(value)\n  }\n}\n\ntype ToolCallStatusBadgeProps = React.ComponentProps<\"span\"> & {\n  /** The tool call's lifecycle state. Rendered as an icon plus text, never color alone. */\n  status: ToolCallStatus\n}\n\nfunction ToolCallStatusBadge({\n  status,\n  className,\n  ...props\n}: ToolCallStatusBadgeProps) {\n  const { label, icon: Icon, className: tone } = statusConfig[status]\n  return (\n    <span\n      data-slot=\"tool-call-status\"\n      data-status={status}\n      className={cn(\n        \"inline-flex shrink-0 items-center gap-1 text-xs font-medium\",\n        status === \"pending\" ? \"text-muted-foreground\" : \"text-foreground\",\n        className\n      )}\n      {...props}\n    >\n      <Icon\n        aria-hidden=\"true\"\n        className={cn(\n          \"size-3.5\",\n          tone,\n          status === \"running\" && \"animate-spin motion-reduce:animate-none\"\n        )}\n      />\n      {label}\n    </span>\n  )\n}\n\nfunction PayloadBlock({\n  label,\n  value,\n  tone,\n}: {\n  label: string\n  value: unknown\n  tone?: \"error\"\n}) {\n  const text = formatPayload(value)\n  const preRef = React.useRef<HTMLPreElement>(null)\n  // Take a tab stop only when the block overflows, so keyboard users can scroll it.\n  React.useEffect(() => {\n    const node = preRef.current\n    if (!node || typeof ResizeObserver === \"undefined\") return\n    const measure = () => {\n      const overflows =\n        node.scrollHeight > node.clientHeight ||\n        node.scrollWidth > node.clientWidth\n      if (overflows) node.setAttribute(\"tabindex\", \"0\")\n      else node.removeAttribute(\"tabindex\")\n    }\n    measure()\n    const observer = new ResizeObserver(measure)\n    observer.observe(node)\n    return () => observer.disconnect()\n  }, [text])\n\n  return (\n    <div data-slot=\"tool-call-section\" className=\"grid gap-1.5\">\n      <div className=\"text-muted-foreground font-mono text-[11px] tracking-wide uppercase\">\n        {label}\n      </div>\n      <pre\n        ref={preRef}\n        dir=\"ltr\"\n        aria-label={label}\n        className={cn(\n          \"bg-muted/50 focus-visible:ring-ring/50 max-h-64 overflow-auto rounded-md border px-3 py-2.5 font-mono text-xs leading-5 outline-none focus-visible:ring-[3px]\",\n          tone === \"error\" && \"border-destructive/30 text-destructive\"\n        )}\n      >\n        {text}\n      </pre>\n    </div>\n  )\n}\n\ntype ToolCallCardProps = Omit<React.ComponentProps<\"div\">, \"dir\"> & {\n  /** Tool name, shown in monospace, for example \"search_docs\". */\n  name: string\n  /** Lifecycle state of the call. */\n  status?: ToolCallStatus\n  /** How long the call took, in milliseconds. */\n  duration?: number\n  /** Arguments the model passed. Objects are pretty-printed as JSON; strings are shown as-is. */\n  input?: unknown\n  /** What the tool returned, or the error message when status is \"error\". */\n  result?: unknown\n  /** Short human description shown under the name. */\n  description?: React.ReactNode\n  /** Controlled open state of the details panel. */\n  open?: boolean\n  /** Initial open state when uncontrolled. */\n  defaultOpen?: boolean\n  /** Called when the details panel opens or closes. */\n  onOpenChange?: (open: boolean) => void\n}\n\nfunction ToolCallCard({\n  name,\n  status = \"pending\",\n  duration,\n  input,\n  result,\n  description,\n  open,\n  defaultOpen = false,\n  onOpenChange,\n  className,\n  children,\n  ...props\n}: ToolCallCardProps) {\n  const hasInput = input !== undefined\n  const hasResult = result !== undefined\n  const hasDetails = hasInput || hasResult || children != null\n\n  return (\n    <CollapsiblePrimitive.Root\n      data-slot=\"tool-call-card\"\n      data-status={status}\n      open={open}\n      defaultOpen={defaultOpen}\n      onOpenChange={onOpenChange}\n      disabled={!hasDetails}\n      className={cn(\n        \"group/tool bg-card text-card-foreground w-full overflow-hidden rounded-xl border\",\n        className\n      )}\n      {...props}\n    >\n      <div className=\"flex min-w-0 items-center gap-2 px-3 py-2\">\n        <CollapsiblePrimitive.Trigger\n          data-slot=\"tool-call-trigger\"\n          className=\"focus-visible:ring-ring/50 -mx-1 flex min-w-0 flex-1 items-center gap-2 rounded-md px-1 py-1 text-start outline-none focus-visible:ring-[3px] disabled:cursor-default\"\n        >\n          <ChevronRight\n            aria-hidden=\"true\"\n            className={cn(\n              \"text-muted-foreground size-3.5 shrink-0 transition-transform duration-150 group-data-[state=open]/tool:rotate-90 motion-reduce:transition-none rtl:group-data-[state=closed]/tool:rotate-180\",\n              !hasDetails && \"invisible\"\n            )}\n          />\n          <Wrench\n            aria-hidden=\"true\"\n            className=\"text-muted-foreground size-3.5 shrink-0\"\n          />\n          <span className=\"min-w-0\">\n            <span className=\"block truncate font-mono text-[13px] font-medium\">\n              {name}\n            </span>\n            {description ? (\n              <span className=\"text-muted-foreground block truncate text-xs\">\n                {description}\n              </span>\n            ) : null}\n          </span>\n        </CollapsiblePrimitive.Trigger>\n        <span aria-live=\"polite\" className=\"flex shrink-0 items-center gap-2.5\">\n          <ToolCallStatusBadge status={status} />\n          {duration !== undefined ? (\n            <span className=\"text-muted-foreground font-mono text-[11px] tabular-nums\">\n              {formatToolDuration(duration)}\n            </span>\n          ) : null}\n        </span>\n      </div>\n      {hasDetails ? (\n        <CollapsiblePrimitive.Content\n          data-slot=\"tool-call-details\"\n          className=\"grid gap-3 border-t px-3 py-3\"\n        >\n          {hasInput ? <PayloadBlock label=\"Input\" value={input} /> : null}\n          {hasResult ? (\n            <PayloadBlock\n              label={status === \"error\" ? \"Error\" : \"Result\"}\n              value={result}\n              tone={status === \"error\" ? \"error\" : undefined}\n            />\n          ) : null}\n          {children}\n        </CollapsiblePrimitive.Content>\n      ) : null}\n    </CollapsiblePrimitive.Root>\n  )\n}\n\nexport {\n  ToolCallCard,\n  ToolCallStatusBadge,\n  formatToolDuration,\n  type ToolCallCardProps,\n  type ToolCallStatusBadgeProps,\n  type ToolCallStatus,\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}