{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"tool-call-card","type":"registry:ui","title":"Tool Call Card","description":"An agent's tool call in one line – live timer, status and summary, folds open to highlighted arguments and result with copy, retry on errors and a group that sums them up","dependencies":[],"files":[{"path":"registry/tool-call-card/tool-call-card.tsx","type":"registry:ui","content":"\"use client\";\n\nimport {\n  Children,\n  isValidElement,\n  useEffect,\n  useId,\n  useLayoutEffect,\n  useRef,\n  useState,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\n\n/*\n * Tool Call Card – one tool call of an AI agent, in a single self-contained file.\n * A header with the tool's icon, its name in mono, a short summary and the status:\n * a spinner with a live timer, a check with the duration, or \"Failed\". Click it open to\n * see the arguments and the result as highlighted JSON, each with a copy button; long\n * blocks are capped with a fade and \"Show all\". Errors show the message and a Retry button.\n * ToolCallGroup folds several cards into one line: \"Used 3 tools · 2.4s\".\n * The styles are injected below, so no extra files are needed.\n */\n\nexport type ToolCallStatus = \"running\" | \"success\" | \"error\";\n\nexport type ToolCallCardProps = {\n  /** Tool name as the agent calls it, shown in mono – e.g. \"search_web\" */\n  name: string;\n  /** Short human summary – e.g. \"Searched the web for 'CRM pricing 2026'\" */\n  summary?: ReactNode;\n  /** Icon in the tile on the left – defaults to a wrench */\n  icon?: ReactNode;\n  status: ToolCallStatus;\n  /** Arguments the tool was called with – objects are pretty-printed, JSON strings parsed */\n  args?: unknown;\n  /** What the tool returned – objects are pretty-printed, JSON strings parsed, other text shown as is */\n  result?: unknown;\n  /** Error message, shown when status is \"error\" */\n  error?: string;\n  /** When the call started (ms since epoch, e.g. Date.now()) – the live timer counts from here */\n  startedAt?: number;\n  /** Final duration in ms – otherwise measured while the card shows \"running\" */\n  duration?: number;\n  /** Start expanded (uncontrolled) */\n  defaultOpen?: boolean;\n  /** Expanded state (controlled) */\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /** Opens the card when the call fails and closes it again on the next run, unless someone toggled it */\n  openOnError?: boolean;\n  /** Shows a Retry button next to the error */\n  onRetry?: () => void;\n  /** Height in px after which arguments and result are capped with a fade and \"Show all\" */\n  maxCodeHeight?: number;\n  className?: string;\n};\n\nexport type ToolCallGroupProps = {\n  /** ToolCallCard elements – the group reads their name, icon, status and duration */\n  children: ReactNode;\n  /** Label once all calls are done – defaults to \"Used 3 tools\" */\n  label?: string;\n  /** Label while any call is running */\n  runningLabel?: string;\n  /** Total duration in ms – otherwise the sum of the cards' durations, or the time measured while running */\n  duration?: number;\n  defaultOpen?: boolean;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  className?: string;\n};\n\n/* ---------- Card ---------- */\n\nexport function ToolCallCard({\n  name,\n  summary,\n  icon,\n  status,\n  args,\n  result,\n  error,\n  startedAt,\n  duration,\n  defaultOpen = false,\n  open: openProp,\n  onOpenChange,\n  openOnError = true,\n  onRetry,\n  maxCodeHeight = 220,\n  className,\n}: ToolCallCardProps) {\n  const id = useId();\n  const [openState, setOpenState] = useState(defaultOpen);\n  const open = openProp ?? openState;\n  const setOpen = (next: boolean) => {\n    if (openProp === undefined) setOpenState(next);\n    onOpenChange?.(next);\n  };\n\n  // Open on failure, close again on the next run – only while nobody has toggled it by hand\n  const [prevStatus, setPrevStatus] = useState(status);\n  const [autoOpened, setAutoOpened] = useState(false);\n  if (status !== prevStatus) {\n    setPrevStatus(status);\n    if (openProp === undefined && openOnError) {\n      if (status === \"error\" && !openState) {\n        setOpenState(true);\n        setAutoOpened(true);\n      } else if (prevStatus === \"error\" && autoOpened) {\n        setOpenState(false);\n        setAutoOpened(false);\n      }\n    }\n  }\n\n  const measured = useStopwatch(status === \"running\", startedAt, false);\n  const ms = status === \"running\" ? (measured ?? 0) : (duration ?? measured);\n\n  const announce =\n    status === \"running\" ? `${name} running` : status === \"success\" ? `${name} finished` : `${name} failed${error ? `: ${error}` : \"\"}`;\n\n  return (\n    <div\n      className={[\"ui-tcl\", className].filter(Boolean).join(\" \")}\n      data-status={status}\n      data-open={open || undefined}\n    >\n      {/* React 19 hoists this into <head> once, no matter how many cards render */}\n      <style href=\"ui-tool-call\" precedence=\"default\">\n        {css}\n      </style>\n      <span className=\"ui-tcl-sr\" role=\"status\" aria-live=\"polite\">\n        {announce}\n      </span>\n\n      <button\n        type=\"button\"\n        className=\"ui-tcl__head\"\n        aria-expanded={open}\n        aria-controls={`${id}-body`}\n        onClick={() => {\n          setAutoOpened(false);\n          setOpen(!open);\n        }}\n      >\n        <span className=\"ui-tcl__icon\" aria-hidden=\"true\">\n          {icon ?? <WrenchIcon />}\n        </span>\n        <span className=\"ui-tcl__main\">\n          <code className=\"ui-tcl__name\" title={name}>\n            {name}\n          </code>\n          {summary != null && (\n            <span className=\"ui-tcl__summary\" dir=\"auto\" title={typeof summary === \"string\" ? summary : undefined}>\n              {summary}\n            </span>\n          )}\n        </span>\n        <span className=\"ui-tcl__status\" key={status}>\n          {status === \"running\" && <Spinner />}\n          {status === \"success\" && (\n            <svg className=\"ui-tcl__ok\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n              <path d=\"M3.5 8.4 6.6 11.3 12.5 4.9\" pathLength={1} />\n            </svg>\n          )}\n          {status === \"error\" && <ErrorIcon />}\n          <span className=\"ui-tcl__time\">\n            {status === \"error\" ? \"Failed\" : ms != null ? formatMs(ms, status === \"running\") : null}\n          </span>\n        </span>\n        <Chevron />\n      </button>\n\n      <div className=\"ui-tcl__body\" id={`${id}-body`} inert={!open}>\n        <div className=\"ui-tcl__clip\">\n          <div className=\"ui-tcl__sections\">\n            {status === \"error\" && (\n              <div className=\"ui-tcl__error\">\n                <ErrorIcon />\n                <p className=\"ui-tcl__error-text\" dir=\"auto\">{error ?? \"The tool call failed.\"}</p>\n                {onRetry && (\n                  <button type=\"button\" className=\"ui-tcl__retry\" onClick={onRetry}>\n                    <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n                      <path d=\"M2.75 8a5.25 5.25 0 1 0 1.6-3.77\" />\n                      <path d=\"M2.5 2.5v2.75h2.75\" />\n                    </svg>\n                    Retry\n                  </button>\n                )}\n              </div>\n            )}\n            {args !== undefined && <CodeSection label=\"Arguments\" value={args} cap={maxCodeHeight} />}\n            {result !== undefined ? (\n              <CodeSection label=\"Result\" value={result} cap={maxCodeHeight} />\n            ) : status === \"running\" ? (\n              <div className=\"ui-tcl__section\">\n                <div className=\"ui-tcl__section-head\">\n                  <span className=\"ui-tcl__label\">Result</span>\n                </div>\n                <p className=\"ui-tcl__pending\">Waiting for result…</p>\n              </div>\n            ) : null}\n            {args === undefined && result === undefined && status === \"success\" && (\n              <p className=\"ui-tcl__pending\">No arguments or result.</p>\n            )}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\n/* ---------- Group ---------- */\n\nexport function ToolCallGroup({\n  children,\n  label,\n  runningLabel = \"Using tools…\",\n  duration,\n  defaultOpen = false,\n  open: openProp,\n  onOpenChange,\n  className,\n}: ToolCallGroupProps) {\n  const id = useId();\n  const [openState, setOpenState] = useState(defaultOpen);\n  const open = openProp ?? openState;\n\n  // Read the cards' props to summarise them in the header\n  const calls = Children.toArray(children)\n    .filter(isValidElement)\n    .map((c) => (c as ReactElement<Partial<ToolCallCardProps>>).props);\n  const running = calls.some((c) => c.status === \"running\");\n  const failed = calls.filter((c) => c.status === \"error\").length;\n\n  // Time spent with tools running, added up across stretches (e.g. a pause before a retry)\n  // Counts from the earliest running card that knows when it started, so the group never shows less than its cards\n  const since = calls.reduce<number | undefined>(\n    (min, c) => (c.status === \"running\" && c.startedAt != null && (min == null || c.startedAt < min) ? c.startedAt : min),\n    undefined,\n  );\n  const measured = useStopwatch(running, since, true);\n  const sum = calls.length && calls.every((c) => c.duration != null) ? calls.reduce((t, c) => t + (c.duration ?? 0), 0) : null;\n  const ms = running ? measured : (duration ?? sum ?? measured);\n\n  const count = `Used ${calls.length.toLocaleString(\"en-US\")} ${calls.length === 1 ? \"tool\" : \"tools\"}`;\n  const title = running ? runningLabel : (label ?? count);\n  const icons = calls.slice(-3);\n\n  return (\n    <div\n      className={[\"ui-tcl-group\", className].filter(Boolean).join(\" \")}\n      data-running={running || undefined}\n      data-open={open || undefined}\n    >\n      <style href=\"ui-tool-call\" precedence=\"default\">\n        {css}\n      </style>\n      <span className=\"ui-tcl-sr\" role=\"status\" aria-live=\"polite\">\n        {running ? runningLabel : `${count}${failed ? `, ${failed} failed` : \"\"}`}\n      </span>\n\n      <button\n        type=\"button\"\n        className=\"ui-tcl-group__head\"\n        aria-expanded={open}\n        aria-controls={`${id}-list`}\n        onClick={() => {\n          if (openProp === undefined) setOpenState(!open);\n          onOpenChange?.(!open);\n        }}\n      >\n        <span className=\"ui-tcl-group__stack\" aria-hidden=\"true\">\n          {icons.map((c, i) => (\n            <span className=\"ui-tcl__icon\" key={calls.length - icons.length + i}>\n              {c.icon ?? <WrenchIcon />}\n            </span>\n          ))}\n        </span>\n        <span className=\"ui-tcl-group__title\" key={running ? \"busy\" : \"done\"}>\n          <span className={running ? \"ui-tcl-group__label ui-tcl__shine\" : \"ui-tcl-group__label\"} data-text={title}>\n            {title}\n          </span>\n          {!running && failed > 0 && <span className=\"ui-tcl-group__failed\">· {failed} failed</span>}\n          {ms != null && <span className=\"ui-tcl-group__time\">· {formatMs(ms, running)}</span>}\n        </span>\n        <Chevron />\n      </button>\n\n      <div className=\"ui-tcl__body\" id={`${id}-list`} inert={!open}>\n        <div className=\"ui-tcl__clip\">\n          <div className=\"ui-tcl-group__list\">{children}</div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\n/* ---------- Pieces ---------- */\n\nconst OPEN_MAX = 640;\n\nfunction CodeSection({ label, value, cap }: { label: string; value: unknown; cap: number }) {\n  const { text, json } = toText(value);\n  const pre = useRef<HTMLPreElement>(null);\n  const [full, setFull] = useState(false);\n  const [height, setHeight] = useState(0);\n\n  // Natural height of the block – decides whether it needs the cap and how far \"Show all\" opens\n  useLayoutEffect(() => {\n    const el = pre.current;\n    const inner = el?.firstElementChild;\n    if (!el || !inner) return;\n    const measure = () => setHeight(el.scrollHeight);\n    measure();\n    const ro = new ResizeObserver(measure);\n    ro.observe(inner);\n    return () => ro.disconnect();\n  }, [text]);\n\n  const capped = height > cap + 24;\n\n  return (\n    <div className=\"ui-tcl__section\">\n      <div className=\"ui-tcl__section-head\">\n        <span className=\"ui-tcl__label\">{label}</span>\n        <CopyButton text={text} label={label.toLowerCase()} />\n      </div>\n      <pre\n        ref={pre}\n        className=\"ui-tcl__code\"\n        data-capped={(capped && !full) || undefined}\n        data-scroll={(capped && full && height > OPEN_MAX) || undefined}\n        // Open, a very long block stops at a few screens and scrolls inside – no 200,000px card\n        style={capped ? { maxHeight: full ? Math.min(height, OPEN_MAX) : cap } : undefined}\n      >\n        <code>{json ? highlight(text) : text}</code>\n      </pre>\n      {capped && (\n        <button type=\"button\" className=\"ui-tcl__more\" aria-expanded={full} onClick={() => setFull(!full)}>\n          {full ? \"Show less\" : \"Show all\"}\n        </button>\n      )}\n    </div>\n  );\n}\n\nfunction CopyButton({ text, label }: { text: string; label: string }) {\n  const [copied, setCopied] = useState(false);\n  useEffect(() => {\n    if (!copied) return;\n    const t = window.setTimeout(() => setCopied(false), 1400);\n    return () => window.clearTimeout(t);\n  }, [copied]);\n\n  return (\n    <button\n      type=\"button\"\n      className=\"ui-tcl__copy\"\n      data-copied={copied || undefined}\n      aria-label={copied ? \"Copied\" : `Copy ${label}`}\n      title={copied ? \"Copied\" : `Copy ${label}`}\n      onClick={() => copyText(text).then((ok) => ok && setCopied(true))}\n    >\n      <svg className=\"ui-tcl__copy-icon\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n        <rect x=\"5.25\" y=\"5.25\" width=\"8\" height=\"8\" rx=\"2\" />\n        <path d=\"M10.75 5.25v-1a1.5 1.5 0 0 0-1.5-1.5h-4.5a2 2 0 0 0-2 2v4.5a1.5 1.5 0 0 0 1.5 1.5h1\" />\n      </svg>\n      <svg className=\"ui-tcl__copy-done\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n        <path d=\"M3.5 8.4 6.6 11.3 12.5 4.9\" pathLength={1} />\n      </svg>\n    </button>\n  );\n}\n\nfunction Spinner() {\n  return (\n    <svg className=\"ui-tcl__spinner\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <circle cx=\"8\" cy=\"8\" r=\"5.75\" />\n      <path d=\"M8 2.25a5.75 5.75 0 0 1 5.75 5.75\" />\n    </svg>\n  );\n}\n\nfunction ErrorIcon() {\n  return (\n    <svg className=\"ui-tcl__err\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <circle cx=\"8\" cy=\"8\" r=\"6\" />\n      <path d=\"M8 5v3.4M8 10.9v.1\" />\n    </svg>\n  );\n}\n\nfunction Chevron() {\n  return (\n    <svg className=\"ui-tcl__chevron\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n      <path d=\"M4.5 6.25 8 9.75l3.5-3.5\" />\n    </svg>\n  );\n}\n\nfunction WrenchIcon() {\n  return (\n    <svg viewBox=\"0 0 16 16\">\n      <path d=\"M10.2 2.4a3.5 3.5 0 0 0-4.3 4.6L2.6 10.3a1.4 1.4 0 0 0 2 2L7.9 9a3.5 3.5 0 0 0 4.6-4.3l-2 2-1.6-.4-.4-1.6 1.7-2.3Z\" />\n    </svg>\n  );\n}\n\n/* ---------- Helpers ---------- */\n\n/** Live ms while running, the final value after. accumulate = add up several running stretches. */\nfunction useStopwatch(running: boolean, startedAt: number | undefined, accumulate: boolean) {\n  const start = useRef<number | null>(null);\n  const base = useRef(0);\n  const [ms, setMs] = useState<number | null>(null);\n  useEffect(() => {\n    if (!running) {\n      if (start.current !== null) {\n        base.current += Date.now() - start.current;\n        start.current = null;\n        setMs(base.current);\n      }\n      return;\n    }\n    if (!accumulate) base.current = 0;\n    start.current = startedAt ?? Date.now();\n    const tick = () => setMs(base.current + Date.now() - (start.current ?? Date.now()));\n    tick();\n    const t = window.setInterval(tick, 100);\n    return () => window.clearInterval(t);\n  }, [running, startedAt, accumulate]);\n  return ms;\n}\n\n/** 0.4s, 12.3s, 2m 05s, 3h 12m, 2d 4h – the live timer always shows tenths below a minute */\nfunction formatMs(ms: number, live: boolean) {\n  if (!live && ms < 1000) return `${Math.max(1, Math.round(ms))}ms`;\n  if (ms < 60_000) return `${(Math.floor(ms / 100) / 10).toFixed(1)}s`;\n  const s = Math.floor(ms / 1000);\n  if (s < 3600) return `${Math.floor(s / 60)}m ${String(s % 60).padStart(2, \"0\")}s`;\n  const h = Math.floor(s / 3600);\n  if (h < 24) return `${h}h ${String(Math.floor(s / 60) % 60).padStart(2, \"0\")}m`;\n  return `${Math.floor(h / 24)}d ${h % 24}h`;\n}\n\nfunction toText(value: unknown): { text: string; json: boolean } {\n  if (typeof value === \"string\") {\n    const t = value.trim();\n    if (t.startsWith(\"{\") || t.startsWith(\"[\")) {\n      try {\n        return { text: JSON.stringify(JSON.parse(t), null, 2), json: true };\n      } catch {\n        /* not JSON – shown as plain text */\n      }\n    }\n    return { text: value, json: false };\n  }\n  try {\n    const s = JSON.stringify(value, null, 2);\n    return s === undefined ? { text: String(value), json: false } : { text: s, json: true };\n  } catch {\n    return { text: String(value), json: false };\n  }\n}\n\n// Strings (keys when followed by a colon), literals and numbers – everything else stays punctuation\nconst TOKEN = /(\"(?:[^\"\\\\]|\\\\.)*\")(\\s*:)?|\\b(?:true|false|null)\\b|-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?/g;\n\nfunction highlight(text: string): ReactNode[] {\n  const out: ReactNode[] = [];\n  const re = new RegExp(TOKEN.source, \"g\");\n  let last = 0;\n  let m: RegExpExecArray | null;\n  while ((m = re.exec(text))) {\n    if (m.index > last) out.push(text.slice(last, m.index));\n    if (m[1] && m[2]) {\n      out.push(\n        <span key={m.index} className=\"ui-tcl__t-key\">\n          {m[1]}\n        </span>,\n        m[2],\n      );\n    } else {\n      const kind = m[1] ? \"str\" : /^[tfn]/.test(m[0]) ? \"lit\" : \"num\";\n      out.push(\n        <span key={m.index} className={`ui-tcl__t-${kind}`}>\n          {m[0]}\n        </span>,\n      );\n    }\n    last = m.index + m[0].length;\n  }\n  if (last < text.length) out.push(text.slice(last));\n  return out;\n}\n\nasync function copyText(text: string) {\n  try {\n    await navigator.clipboard.writeText(text);\n    return true;\n  } catch {\n    // Older browsers and non-secure contexts\n    const area = document.createElement(\"textarea\");\n    area.value = text;\n    area.style.cssText = \"position:fixed;opacity:0\";\n    document.body.appendChild(area);\n    area.select();\n    const ok = document.execCommand(\"copy\");\n    area.remove();\n    return ok;\n  }\n}\n\nconst css = /* css */ `\n.ui-tcl,\n.ui-tcl-group {\n  --ui-tcl-ink: var(--aui-ink, #171717);\n  --ui-tcl-text: var(--aui-muted, #6b6b6b);\n  --ui-tcl-muted: var(--aui-muted, #8a8a8a);\n  --ui-tcl-bg: var(--aui-surface, #ffffff);\n  --ui-tcl-line: var(--aui-border, #e7e7e7);\n  --ui-tcl-hover: var(--aui-subtle, #fafafa);\n  --ui-tcl-tile: #f5f5f5;\n  --ui-tcl-code: #fafafa;\n  --ui-tcl-ring: #ffffff;\n  --ui-tcl-red: var(--aui-danger, #c4320a);\n  --ui-tcl-red-bg: color-mix(in srgb, var(--aui-red, #f04438) 6%, transparent);\n  --ui-tcl-red-line: color-mix(in srgb, var(--aui-red, #f04438) 16%, transparent);\n  --ui-tcl-key: #171717;\n  --ui-tcl-str: #0b8576;\n  --ui-tcl-num: var(--aui-warning, #b25e00);\n  --ui-tcl-lit: #8a35d6;\n  --ui-tcl-shine: rgba(255, 255, 255, 0.82);\n  --ui-tcl-focus: var(--aui-focus, rgba(0, 96, 226, 0.7));\n  --ui-tcl-ease: var(--aui-ease, cubic-bezier(0.22, 1, 0.36, 1));\n  --ui-tcl-mono: var(--aui-font-mono, ui-monospace, \"SF Mono\", Menlo, monospace);\n  box-sizing: border-box;\n  min-width: 0;\n  color: var(--ui-tcl-text);\n  font-family: var(--aui-font, \"Inter\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);\n  font-size: 13.5px;\n  line-height: 1.4;\n  letter-spacing: -0.006em;\n  -webkit-font-smoothing: antialiased;\n}\n.ui-tcl *,\n.ui-tcl-group * { box-sizing: border-box; }\n\n.ui-tcl {\n  position: relative;\n  border-radius: calc(var(--aui-radius-lg, 16px) - 4px);\n  background: var(--ui-tcl-bg);\n  box-shadow: var(--aui-shadow-card, inset 0 0 0 1px var(--ui-tcl-line), 0 1px 2px rgba(0, 0, 0, 0.04));\n}\n.ui-tcl-sr {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  overflow: hidden;\n  clip-path: inset(50%);\n  white-space: nowrap;\n}\n\n/* Header: icon, name + summary, status, chevron – everything truncates before it overflows */\n.ui-tcl__head {\n  all: unset;\n  box-sizing: border-box;\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  width: 100%;\n  min-height: 44px;\n  padding: 8px 10px 8px 9px;\n  border-radius: calc(var(--aui-radius-lg, 16px) - 4px);\n  cursor: pointer;\n  transition: background-color 160ms ease;\n}\n.ui-tcl[data-open] .ui-tcl__head { border-radius: calc(var(--aui-radius-lg, 16px) - 4px) calc(var(--aui-radius-lg, 16px) - 4px) 0 0; }\n.ui-tcl__head:focus-visible,\n.ui-tcl-group__head:focus-visible,\n.ui-tcl__copy:focus-visible,\n.ui-tcl__more:focus-visible,\n.ui-tcl__retry:focus-visible {\n  outline: 2px solid var(--ui-tcl-focus);\n  outline-offset: 2px;\n}\n@media (hover: hover) {\n  .ui-tcl__head:hover { background: var(--ui-tcl-hover); }\n  .ui-tcl-group__head:hover { background: var(--ui-tcl-hover); color: var(--ui-tcl-ink); }\n  .ui-tcl__copy:hover { background: var(--ui-tcl-hover); color: var(--ui-tcl-ink); }\n  .ui-tcl__more:hover { color: var(--ui-tcl-ink); }\n  .ui-tcl__retry:hover { background: var(--ui-tcl-hover); }\n}\n\n.ui-tcl__icon {\n  display: grid;\n  place-items: center;\n  flex: none;\n  width: 26px;\n  height: 26px;\n  border-radius: 7px;\n  background: var(--ui-tcl-tile);\n  box-shadow: inset 0 0 0 1px var(--ui-tcl-line);\n  color: var(--ui-tcl-ink);\n}\n.ui-tcl__icon > svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }\n\n.ui-tcl__main {\n  display: flex;\n  align-items: baseline;\n  gap: 8px;\n  flex: 1;\n  min-width: 0;\n}\n.ui-tcl__name {\n  flex: 0 1 auto;\n  max-width: 55%;\n  overflow: hidden;\n  color: var(--ui-tcl-ink);\n  font-family: var(--ui-tcl-mono);\n  font-size: 12.5px;\n  font-weight: 500;\n  letter-spacing: 0;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n.ui-tcl__summary {\n  flex: 1 1 0;\n  min-width: 0;\n  overflow: hidden;\n  color: var(--ui-tcl-text);\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.ui-tcl__status {\n  display: inline-flex;\n  align-items: center;\n  gap: 6px;\n  flex: none;\n  color: var(--ui-tcl-muted);\n  font-size: 12.5px;\n  font-variant-numeric: tabular-nums;\n  white-space: nowrap;\n  animation: ui-tcl-in 360ms var(--ui-tcl-ease) both;\n}\n.ui-tcl[data-status=\"error\"] .ui-tcl__status { color: var(--ui-tcl-red); }\n.ui-tcl__status > svg { width: 14px; height: 14px; flex: none; fill: none; stroke-linecap: round; stroke-linejoin: round; }\n.ui-tcl__spinner circle { stroke: currentColor; stroke-width: 1.6; opacity: 0.22; }\n.ui-tcl__spinner path { stroke: var(--ui-tcl-ink); stroke-width: 1.6; }\n.ui-tcl__spinner { animation: ui-tcl-spin 800ms linear infinite; }\n.ui-tcl__ok { stroke: var(--ui-tcl-ink); stroke-width: 1.8; }\n.ui-tcl__ok path { stroke-dasharray: 1; stroke-dashoffset: 1; animation: ui-tcl-draw 360ms var(--ui-tcl-ease) 80ms forwards; }\n.ui-tcl__err { stroke: var(--ui-tcl-red); stroke-width: 1.5; }\n\n.ui-tcl__chevron {\n  flex: none;\n  width: 14px;\n  height: 14px;\n  fill: none;\n  stroke: var(--ui-tcl-muted);\n  stroke-width: 1.5;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n  transition: transform 360ms var(--ui-tcl-ease);\n}\n.ui-tcl[data-open] > .ui-tcl__head .ui-tcl__chevron,\n.ui-tcl-group[data-open] > .ui-tcl-group__head .ui-tcl__chevron { transform: rotate(180deg); }\n\n/* Body: grows from 0fr to 1fr, so the height animates without measuring */\n.ui-tcl__body {\n  display: grid;\n  grid-template-rows: 0fr;\n  opacity: 0;\n  transition: grid-template-rows 380ms var(--ui-tcl-ease), opacity 240ms ease;\n}\n.ui-tcl[data-open] > .ui-tcl__body,\n.ui-tcl-group[data-open] > .ui-tcl__body {\n  grid-template-rows: 1fr;\n  opacity: 1;\n}\n.ui-tcl__clip { min-height: 0; overflow: hidden; }\n.ui-tcl__sections {\n  display: grid;\n  gap: 12px;\n  padding: 12px;\n  border-top: 1px solid var(--ui-tcl-line);\n}\n\n.ui-tcl__section { display: grid; gap: 6px; min-width: 0; }\n.ui-tcl__section-head {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 8px;\n  min-height: 24px;\n}\n.ui-tcl__label { color: var(--ui-tcl-muted); font-size: 12px; font-weight: 500; }\n.ui-tcl__pending { margin: 0; color: var(--ui-tcl-muted); font-size: 12.5px; }\n\n.ui-tcl__copy {\n  all: unset;\n  position: relative;\n  display: grid;\n  place-items: center;\n  width: 24px;\n  height: 24px;\n  border-radius: calc(var(--aui-radius-sm, 7px) - 1px);\n  color: var(--ui-tcl-muted);\n  cursor: pointer;\n  transition: background-color 160ms ease, color 160ms ease;\n}\n.ui-tcl__copy svg {\n  position: absolute;\n  width: 14px;\n  height: 14px;\n  fill: none;\n  stroke: currentColor;\n  stroke-width: 1.4;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n  transition: opacity 200ms ease, transform 320ms var(--ui-tcl-ease), filter 200ms ease;\n}\n.ui-tcl__copy-done { opacity: 0; transform: scale(0.6); filter: blur(2px); stroke-width: 1.7; }\n.ui-tcl__copy[data-copied] { color: var(--ui-tcl-ink); }\n.ui-tcl__copy[data-copied] .ui-tcl__copy-icon { opacity: 0; transform: scale(0.6); filter: blur(2px); }\n.ui-tcl__copy[data-copied] .ui-tcl__copy-done { opacity: 1; transform: none; filter: none; }\n\n/* Code: wraps instead of scrolling sideways; capped blocks fade out at the bottom */\n.ui-tcl__code {\n  position: relative;\n  margin: 0;\n  padding: 10px 12px;\n  overflow: hidden;\n  border-radius: calc(var(--aui-radius-sm, 7px) + 1px);\n  background: var(--ui-tcl-code);\n  box-shadow: inset 0 0 0 1px var(--ui-tcl-line);\n  color: var(--ui-tcl-muted);\n  font-family: var(--ui-tcl-mono);\n  font-size: 12px;\n  line-height: 1.6;\n  white-space: pre-wrap;\n  overflow-wrap: anywhere;\n  tab-size: 2;\n  transition: max-height 420ms var(--ui-tcl-ease);\n}\n.ui-tcl__code code { display: block; font: inherit; }\n.ui-tcl__code::after {\n  content: \"\";\n  position: absolute;\n  left: 1px;\n  right: 1px;\n  bottom: 1px;\n  height: 56px;\n  border-radius: 0 0 var(--aui-radius-sm, 7px) var(--aui-radius-sm, 7px);\n  background: linear-gradient(to bottom, transparent, var(--ui-tcl-code));\n  opacity: 0;\n  transition: opacity 240ms ease;\n  pointer-events: none;\n}\n.ui-tcl__code[data-capped]::after { opacity: 1; }\n.ui-tcl__code[data-scroll] { overflow-y: auto; overscroll-behavior: contain; }\n.ui-tcl__t-key { color: var(--ui-tcl-key); }\n.ui-tcl__t-str { color: var(--ui-tcl-str); }\n.ui-tcl__t-num { color: var(--ui-tcl-num); }\n.ui-tcl__t-lit { color: var(--ui-tcl-lit); }\n\n.ui-tcl__more {\n  all: unset;\n  justify-self: start;\n  padding: 2px 4px;\n  margin-left: -4px;\n  border-radius: 5px;\n  color: var(--ui-tcl-text);\n  font-size: 12.5px;\n  font-weight: 500;\n  cursor: pointer;\n  transition: color 160ms ease;\n}\n\n/* Error: the message and an optional retry */\n.ui-tcl__error {\n  display: flex;\n  align-items: flex-start;\n  gap: 8px;\n  padding: 9px 10px 9px 11px;\n  border-radius: calc(var(--aui-radius-sm, 7px) + 1px);\n  background: var(--ui-tcl-red-bg);\n  box-shadow: inset 0 0 0 1px var(--ui-tcl-red-line);\n}\n.ui-tcl__error > .ui-tcl__err { flex: none; width: 14px; height: 14px; margin-top: 2px; fill: none; stroke-linecap: round; }\n.ui-tcl__error-text {\n  flex: 1;\n  min-width: 0;\n  max-height: 200px;\n  margin: 0;\n  overflow-y: auto;\n  overscroll-behavior: contain;\n  white-space: pre-line;\n  color: var(--ui-tcl-red);\n  font-size: 13px;\n  line-height: 1.45;\n  overflow-wrap: anywhere;\n}\n.ui-tcl__retry {\n  all: unset;\n  display: inline-flex;\n  align-items: center;\n  gap: 5px;\n  flex: none;\n  height: 26px;\n  margin: -3px 0;\n  padding: 0 10px 0 8px;\n  border-radius: var(--aui-radius-sm, 7px);\n  background: var(--ui-tcl-bg);\n  box-shadow: inset 0 0 0 1px var(--ui-tcl-line), 0 1px 2px rgba(0, 0, 0, 0.04);\n  color: var(--ui-tcl-ink);\n  font-size: 12.5px;\n  font-weight: 500;\n  cursor: pointer;\n  transition: background-color 160ms ease;\n}\n.ui-tcl__retry svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }\n.ui-tcl__retry:active svg { transform: rotate(-60deg); transition: transform 200ms var(--ui-tcl-ease); }\n\n/* Group: one quiet line with the tool icons stacked, the cards below */\n.ui-tcl-group { display: grid; }\n.ui-tcl-group__head {\n  all: unset;\n  box-sizing: border-box;\n  display: flex;\n  align-items: center;\n  gap: 9px;\n  justify-self: start;\n  max-width: 100%;\n  min-height: 36px;\n  padding: 4px 10px 4px 5px;\n  border-radius: var(--aui-radius, 10px);\n  color: var(--ui-tcl-text);\n  cursor: pointer;\n  transition: background-color 160ms ease, color 160ms ease;\n}\n.ui-tcl-group__stack { display: flex; flex: none; }\n.ui-tcl-group__stack .ui-tcl__icon {\n  width: 24px;\n  height: 24px;\n  border-radius: 7px;\n  box-shadow: inset 0 0 0 1px var(--ui-tcl-line), 0 0 0 2px var(--ui-tcl-ring);\n  animation: ui-tcl-pop 420ms var(--ui-tcl-ease) both;\n}\n.ui-tcl-group__stack .ui-tcl__icon + .ui-tcl__icon { margin-left: -5px; }\n.ui-tcl-group__stack .ui-tcl__icon > svg { width: 13px; height: 13px; }\n.ui-tcl-group__title {\n  display: flex;\n  align-items: baseline;\n  gap: 5px;\n  min-width: 0;\n  white-space: nowrap;\n  animation: ui-tcl-in 360ms var(--ui-tcl-ease) both;\n}\n.ui-tcl-group__label {\n  position: relative;\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  color: inherit;\n  font-weight: 500;\n}\n.ui-tcl-group__failed { flex: none; color: var(--ui-tcl-red); }\n.ui-tcl-group__time { flex: none; color: var(--ui-tcl-muted); font-variant-numeric: tabular-nums; }\n.ui-tcl-group__list { display: grid; gap: 8px; padding-top: 8px; }\n\n/* A soft light sweeping over \"Using tools…\" */\n.ui-tcl__shine::after {\n  content: attr(data-text);\n  content: attr(data-text) / \"\";\n  position: absolute;\n  inset: 0;\n  white-space: nowrap;\n  color: transparent;\n  background: linear-gradient(90deg, transparent 38%, var(--ui-tcl-shine) 50%, transparent 62%) 100% 0 / 300% 100% no-repeat;\n  -webkit-background-clip: text;\n  background-clip: text;\n  animation: ui-tcl-sweep 1.8s linear infinite;\n  pointer-events: none;\n}\n\n@keyframes ui-tcl-in {\n  from { opacity: 0; transform: translateY(3px); filter: blur(2px); }\n  to { opacity: 1; transform: none; filter: none; }\n}\n@keyframes ui-tcl-pop {\n  from { opacity: 0; transform: scale(0.6); }\n  to { opacity: 1; transform: none; }\n}\n@keyframes ui-tcl-spin { to { transform: rotate(360deg); } }\n@keyframes ui-tcl-draw { to { stroke-dashoffset: 0; } }\n@keyframes ui-tcl-sweep {\n  from { background-position: 100% 0; }\n  to { background-position: 0% 0; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .ui-tcl *,\n  .ui-tcl-group *,\n  .ui-tcl *::after,\n  .ui-tcl-group *::after {\n    animation-duration: 1ms !important;\n    animation-delay: 0ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 1ms !important;\n  }\n  .ui-tcl__shine::after { display: none; }\n  .ui-tcl__ok path { stroke-dashoffset: 0; }\n}\n\n/* Dark mode: follows a .dark class or data-theme=\"dark\" on any ancestor, e.g. <html> */\n:where(.dark, [data-theme=\"dark\"]) .ui-tcl,\n:where(.dark, [data-theme=\"dark\"]) .ui-tcl-group {\n  --ui-tcl-ink: var(--aui-ink, #ededed);\n  --ui-tcl-text: var(--aui-muted, #a1a1a1);\n  --ui-tcl-muted: var(--aui-muted, #7c7c7c);\n  --ui-tcl-bg: var(--aui-surface, #161618);\n  --ui-tcl-line: var(--aui-border, rgba(255, 255, 255, 0.1));\n  --ui-tcl-hover: var(--aui-subtle, rgba(255, 255, 255, 0.035));\n  --ui-tcl-tile: #1d1d20;\n  --ui-tcl-code: #1d1d20;\n  --ui-tcl-ring: #161618;\n  --ui-tcl-red: var(--aui-danger, #ff8a7a);\n  --ui-tcl-red-bg: color-mix(in srgb, var(--aui-red, #ff6b5f) 8%, transparent);\n  --ui-tcl-red-line: color-mix(in srgb, var(--aui-red, #ff6b5f) 20%, transparent);\n  --ui-tcl-key: #ededed;\n  --ui-tcl-str: var(--aui-teal, #4fc3b4);\n  --ui-tcl-num: var(--aui-orange, #f2a15c);\n  --ui-tcl-lit: #cfaef5;\n  --ui-tcl-shine: rgba(255, 255, 255, 0.95);\n  --ui-tcl-focus: var(--aui-focus, rgba(77, 141, 255, 0.8));\n}\n:where(.dark, [data-theme=\"dark\"]) .ui-tcl { box-shadow: var(--aui-shadow-card, inset 0 0 0 1px var(--ui-tcl-line)); }\n`;\n"}]}