{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tool-call",
  "type": "registry:component",
  "title": "Tool call",
  "description": "Shows tool input, output, and execution state in a native disclosure with a subtle 1px outline, 4px corners, and a 44px summary.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/vlak-lib.json"
  ],
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.",
  "files": [
    {
      "path": "vlak/tool-call.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\n\nexport type ToolCallState = \"queued\" | \"running\" | \"complete\" | \"error\";\n\nexport interface ToolCallProps extends Omit<React.DetailsHTMLAttributes<HTMLDetailsElement>, \"title\"> {\n  /** Name of the application-owned tool operation. */\n  title: React.ReactNode;\n  state?: ToolCallState;\n  /** More precise supplied progress, for example approval or input preparation. */\n  statusLabel?: string;\n  /** Strings and numbers are rendered as plain preformatted text. Format objects before passing them. */\n  input?: React.ReactNode;\n  output?: React.ReactNode;\n  error?: string;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n}\n\nconst styles = stylex.create({\n  root: { boxSizing: \"border-box\", minWidth: 0, borderWidth: vlak.hairline, borderStyle: \"solid\", borderColor: { default: vlak.divider, [mq.forcedColors]: \"CanvasText\" }, borderRadius: vlak.radiusSm, color: vlak.ink, backgroundColor: vlak.paper },\n  summary: {\n    display: \"flex\", alignItems: \"center\", gap: \"0.75rem\", boxSizing: \"border-box\", minHeight: vlak.hit, padding: \"0.75rem 1rem\", borderRadius: \"inherit\", cursor: \"pointer\", listStyle: \"none\",\n    \"::-webkit-details-marker\": { display: \"none\" },\n    outlineWidth: { default: null, \":focus-visible\": 2 }, outlineStyle: { default: null, \":focus-visible\": \"solid\" }, outlineColor: { default: null, \":focus-visible\": vlak.ink }, outlineOffset: { default: null, \":focus-visible\": -2 },\n  },\n  title: { flex: \"1 1 auto\", minWidth: 0, fontSize: vlak.controlFs, fontWeight: 600, overflowWrap: \"anywhere\" },\n  state: { flexShrink: 0, fontSize: vlak.controlLabel, color: vlak.gray },\n  indicator: { display: \"block\", flexShrink: 0, width: \"1rem\", height: \"1rem\" },\n  body: { minWidth: 0, padding: \"1rem\", borderTopWidth: vlak.hairline, borderTopStyle: \"solid\", borderTopColor: vlak.divider, display: \"grid\", gap: \"1rem\" },\n  section: { display: \"grid\", gap: \"0.5rem\", minWidth: 0 },\n  label: { margin: 0, fontSize: vlak.controlLabel, fontWeight: 600 },\n  value: { minWidth: 0, fontSize: vlak.controlFs, lineHeight: 1.45, overflowWrap: \"anywhere\" },\n  code: { margin: 0, padding: \"0.75rem\", minWidth: 0, whiteSpace: \"pre-wrap\", overflowWrap: \"anywhere\", fontFamily: \"ui-monospace, SFMono-Regular, Menlo, monospace\", fontSize: \"0.8125rem\", lineHeight: 1.45, backgroundColor: vlak.tableAlt },\n  error: { margin: 0, fontSize: vlak.controlFs, lineHeight: 1.45, overflowWrap: \"anywhere\" },\n});\n\nconst stateLabels: Record<ToolCallState, string> = { queued: \"Queued\", running: \"Running\", complete: \"Complete\", error: \"Error\" };\n\n/** Displays supplied tool activity. The application owns execution and its state. */\nexport const ToolCall = React.forwardRef<HTMLDetailsElement, ToolCallProps>(function ToolCall({\n  title, state = \"queued\", statusLabel, input, output, error, open, defaultOpen = false, onOpenChange, onToggle, className, style, children, ...props\n}, ref) {\n  const [innerOpen, setInnerOpen] = React.useState(defaultOpen);\n  const isOpen = open ?? innerOpen;\n  const root = rs([\"rs-tool-call\", className], styles.root);\n  const summary = rs([\"rs-tool-call-summary\"], styles.summary);\n  const heading = rs([\"rs-tool-call-title\"], styles.title);\n  const status = rs([\"rs-tool-call-state\"], styles.state);\n  const indicator = rs([\"rs-tool-call-indicator\"], styles.indicator);\n  const body = rs([\"rs-tool-call-body\"], styles.body);\n  const section = rs([\"rs-tool-call-section\"], styles.section);\n  const label = rs([\"rs-tool-call-label\"], styles.label);\n  const value = rs([\"rs-tool-call-value\"], styles.value);\n  const code = rs([\"rs-tool-call-code\"], styles.code);\n  const failure = rs([\"rs-tool-call-error\"], styles.error);\n  const renderValue = (content: React.ReactNode) => typeof content === \"string\" || typeof content === \"number\"\n    ? <pre className={code.className} style={code.style}>{content}</pre>\n    : <div className={value.className} style={value.style}>{content}</div>;\n  return <details {...props} ref={ref} open={isOpen} data-state={state} className={root.className} style={{ ...root.style, ...style }} onToggle={(event) => {\n    const next = event.currentTarget.open;\n    if (next !== isOpen) {\n      if (open === undefined) setInnerOpen(next);\n      else event.currentTarget.open = isOpen;\n      onOpenChange?.(next);\n    }\n    onToggle?.(event);\n  }}>\n    <summary className={summary.className} style={summary.style} onClick={(event) => {\n      if (open !== undefined) {\n        event.preventDefault();\n        onOpenChange?.(!open);\n      }\n    }}>\n      <span className={heading.className} style={heading.style}>{title}</span>\n      <span role=\"status\" className={status.className} style={status.style}>{statusLabel ?? stateLabels[state]}</span>\n      <svg {...indicator} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d={isOpen ? \"m6 9 6 6 6-6\" : \"m9 6 6 6-6 6\"} /></svg>\n    </summary>\n    <div className={body.className} style={body.style}>\n      {input !== undefined && input !== null && <div className={section.className} style={section.style}><p className={label.className} style={label.style}>Input</p>{renderValue(input)}</div>}\n      {output !== undefined && output !== null && <div className={section.className} style={section.style}><p className={label.className} style={label.style}>Output</p>{renderValue(output)}</div>}\n      {error !== undefined && <p role=\"alert\" className={failure.className} style={failure.style}>{error}</p>}\n      {children}\n    </div>\n  </details>;\n});\n\n/** Provider-neutral shape for the AI SDK static and dynamic tool lifecycle. */\nexport interface ToolPartPresentationInput {\n  type: string;\n  toolName?: string;\n  state: \"input-streaming\" | \"input-available\" | \"approval-requested\" | \"approval-responded\" | \"output-available\" | \"output-error\" | \"output-denied\";\n  approval?: { id: string; approved?: boolean; isAutomatic?: boolean };\n  input?: unknown;\n  output?: unknown;\n  errorText?: string;\n}\nexport interface ToolPartPresentation {\n  title: string;\n  state: ToolCallState;\n  statusLabel: string;\n  approvalStatus?: \"pending\" | \"accepted\" | \"rejected\";\n  approvalId?: string;\n  input?: unknown;\n  output?: unknown;\n  error?: string;\n}\n/** Maps supplied tool lifecycle without inferring that ready arguments have started execution. */\nexport function getToolCallPresentation(part: ToolPartPresentationInput): ToolPartPresentation {\n  const title = part.type === \"dynamic-tool\" ? part.toolName || \"Tool\" : part.type.startsWith(\"tool-\") ? part.type.slice(5) : part.toolName || part.type;\n  const base = { title, input: part.input, output: part.output, approvalId: part.approval?.id };\n  switch (part.state) {\n    case \"input-streaming\": return { ...base, state: \"queued\", statusLabel: \"Preparing input\" };\n    case \"input-available\": return { ...base, state: \"queued\", statusLabel: \"Ready\" };\n    case \"approval-requested\": return { ...base, state: \"queued\", statusLabel: part.approval?.isAutomatic ? \"Checking approval policy\" : \"Approval requested\", approvalStatus: part.approval?.isAutomatic ? undefined : \"pending\" };\n    case \"approval-responded\": return { ...base, state: \"queued\", statusLabel: part.approval?.approved === true ? \"Approved, awaiting execution\" : part.approval?.approved === false ? \"Denied\" : \"Approval decision unavailable\", approvalStatus: part.approval?.approved === true ? \"accepted\" : part.approval?.approved === false ? \"rejected\" : undefined };\n    case \"output-available\": return { ...base, state: \"complete\", statusLabel: \"Complete\" };\n    case \"output-error\": return { ...base, state: \"error\", statusLabel: \"Error\", error: part.errorText || \"The tool could not complete.\" };\n    case \"output-denied\": return { ...base, state: \"complete\", statusLabel: \"Denied\", approvalStatus: \"rejected\" };\n  }\n}\n",
      "type": "registry:component",
      "target": "components/vlak/tool-call.tsx"
    },
    {
      "path": "vlak/styles/tool-call.css",
      "content": "/* ── tool-call: generated from packages/react/src/components/tool-call.tsx ── */\n.rs-tool-call{box-sizing:border-box;min-width:0;border-width:1px;border-style:solid;border-color:var(--divider);border-radius:var(--radius-sm);color:var(--text);background-color:var(--bg)}\n@media (forced-colors: active){.rs-tool-call{border-color:CanvasText}}\n.rs-tool-call-summary{display:flex;align-items:center;gap:0.75rem;box-sizing:border-box;min-height:var(--hit);padding:0.75rem 1rem;border-radius:inherit;cursor:pointer;list-style:none}\n.rs-tool-call-summary::-webkit-details-marker{display:none}\n.rs-tool-call-summary:focus-visible{outline-width:2px;outline-style:solid;outline-color:var(--text);outline-offset:-2px}\n.rs-tool-call-title{flex:1 1 auto;min-width:0;font-size:var(--control-fs);font-weight:600;overflow-wrap:anywhere}\n.rs-tool-call-state{flex-shrink:0;font-size:var(--control-label);color:var(--text-secondary)}\n.rs-tool-call-indicator{display:block;flex-shrink:0;width:1rem;height:1rem}\n.rs-tool-call-body{min-width:0;padding:1rem;border-top-width:1px;border-top-style:solid;border-top-color:var(--divider);display:grid;gap:1rem}\n.rs-tool-call-section{display:grid;gap:0.5rem;min-width:0}\n.rs-tool-call-label{margin:0;font-size:var(--control-label);font-weight:600}\n.rs-tool-call-value{min-width:0;font-size:var(--control-fs);line-height:1.45;overflow-wrap:anywhere}\n.rs-tool-call-code{margin:0;padding:0.75rem;min-width:0;white-space:pre-wrap;overflow-wrap:anywhere;font-family:ui-monospace, SFMono-Regular, Menlo, monospace;font-size:0.8125rem;line-height:1.45;background-color:var(--table-alt)}\n.rs-tool-call-error{margin:0;font-size:var(--control-fs);line-height:1.45;overflow-wrap:anywhere}\n",
      "type": "registry:file",
      "target": "styles/vlak/tool-call.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "ai",
      "classes": [
        "rs-tool-call",
        "rs-tool-call-summary",
        "rs-tool-call-title",
        "rs-tool-call-state",
        "rs-tool-call-indicator",
        "rs-tool-call-body",
        "rs-tool-call-section",
        "rs-tool-call-label",
        "rs-tool-call-value",
        "rs-tool-call-code",
        "rs-tool-call-error"
      ],
      "snippet": "<details class=\"rs-tool-call\" open><summary class=\"rs-tool-call-summary\"><span class=\"rs-tool-call-title\">Search documents</span><span class=\"rs-tool-call-state\" role=\"status\">Complete</span><svg class=\"rs-tool-call-indicator\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m6 9 6 6 6-6\" /></svg></summary><div class=\"rs-tool-call-body\"><div class=\"rs-tool-call-section\"><p class=\"rs-tool-call-label\">Output</p><pre class=\"rs-tool-call-code\">Three matching documents</pre></div></div></details>",
      "cssOnly": false,
      "registryDependencies": [
        "vlak-lib"
      ],
      "aliases": [
        "ToolCall",
        "AI Elements Tool",
        "Tool invocation",
        "Tool result",
        "Function call"
      ],
      "example": "import { ToolCall } from \"@noorddev/vlak-react\";\n\n<ToolCall title=\"Search documents\" state=\"complete\" input={JSON.stringify({ query: \"Renewal terms\" }, null, 2)} output=\"Three matching documents\" defaultOpen />",
      "usage": {
        "use": [
          "Showing application-supplied tool activity within a conversation.",
          "Queued, running, complete, or error states with optional input, output, and additional content."
        ],
        "avoid": [
          "Executing a tool or granting permission; the application owns execution and authorization.",
          "Passing raw objects or booleans as display content; serialize them or use your own renderer."
        ]
      },
      "keyboard": [
        {
          "keys": "Tab",
          "does": "Focuses the native disclosure summary and any interactive content when expanded."
        },
        {
          "keys": "Enter, Space",
          "does": "Requests expansion or collapse through the browser's native summary behavior."
        }
      ],
      "a11y": [
        "The native details and summary expose expansion state. The summary has a 44px minimum target and visible focus outline.",
        "The decorative 16px chevron points right when closed and down when open. Forced colors retain a system color outline and readable controls.",
        "State is visible text in a polite status; error text has an alert role. No color is needed to distinguish states.",
        "Strings and numbers are rendered as escaped preformatted text. Empty strings and zero are retained; React nodes remain application-rendered content.",
        "open is authoritative when controlled; onOpenChange reports requested changes. defaultOpen sets the initial uncontrolled state. The ref reaches details and native attributes pass through.",
        "The component displays the supplied execution state and never runs a tool itself."
      ]
    }
  }
}
