{
  "name": "tool-call",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "tool-call.tsx",
      "content": "\"use client\";\r\n\r\nimport { createVariants, css, keyframes, themeVars as theme } from \"@yugnex/core\";\r\nimport { useControllableState } from \"@yugnex/core/client\";\r\nimport { useId, type ReactNode } from \"react\";\r\n\r\nexport type ToolCallStatus = \"pending\" | \"running\" | \"success\" | \"error\";\r\n\r\nconst spin = keyframes({ from: { transform: \"rotate(0deg)\" }, to: { transform: \"rotate(360deg)\" } });\r\n\r\nconst rootVariants = createVariants({\r\n  base: {\r\n    borderRadius: theme.radius.md,\r\n    border: `1px solid ${theme.color.border}`,\r\n    backgroundColor: theme.color.card,\r\n    overflow: \"hidden\",\r\n    fontFamily: theme.fontFamily.sans,\r\n  },\r\n  variants: {\r\n    status: {\r\n      pending: {},\r\n      running: { borderColor: theme.color.primary },\r\n      success: {},\r\n      error: { borderColor: theme.color.destructive },\r\n    },\r\n  },\r\n  defaultVariants: { status: \"pending\" },\r\n});\r\n\r\nconst triggerClass = css({\r\n  display: \"flex\",\r\n  alignItems: \"center\",\r\n  gap: theme.space[2],\r\n  width: \"100%\",\r\n  padding: `${theme.space[2.5]} ${theme.space[3]}`,\r\n  border: \"none\",\r\n  background: \"transparent\",\r\n  color: theme.color.foreground,\r\n  cursor: \"pointer\",\r\n  textAlign: \"left\",\r\n  fontSize: theme.fontSize.sm,\r\n  transitionProperty: \"background-color\",\r\n  transitionDuration: theme.duration.fast,\r\n  \"&:hover\": { backgroundColor: theme.color.muted },\r\n  \"&:focus-visible\": { outline: `2px solid ${theme.color.ring}`, outlineOffset: \"-2px\" },\r\n});\r\n\r\nconst nameClass = css({\r\n  fontFamily: theme.fontFamily.mono,\r\n  fontSize: theme.fontSize.xs,\r\n  fontWeight: theme.fontWeight.medium,\r\n  flex: 1,\r\n  minWidth: 0,\r\n  overflow: \"hidden\",\r\n  textOverflow: \"ellipsis\",\r\n  whiteSpace: \"nowrap\",\r\n});\r\n\r\nconst chevronClass = css({\r\n  flexShrink: 0,\r\n  transitionProperty: \"transform\",\r\n  transitionDuration: theme.duration.fast,\r\n  transitionTimingFunction: theme.easing.standard,\r\n  '[data-state=\"open\"] &': { transform: \"rotate(90deg)\" },\r\n});\r\n\r\nconst bodyClass = css({\r\n  borderTop: `1px solid ${theme.color.border}`,\r\n  padding: theme.space[3],\r\n  display: \"flex\",\r\n  flexDirection: \"column\",\r\n  gap: theme.space[3],\r\n});\r\n\r\nconst sectionLabelClass = css({\r\n  fontSize: theme.fontSize.xs,\r\n  fontWeight: theme.fontWeight.semibold,\r\n  letterSpacing: theme.letterSpacing.wide,\r\n  textTransform: \"uppercase\",\r\n  color: theme.color.mutedForeground,\r\n  marginBottom: theme.space[1],\r\n});\r\n\r\nconst preClass = css({\r\n  margin: 0,\r\n  padding: theme.space[2.5],\r\n  borderRadius: theme.radius.sm,\r\n  backgroundColor: theme.color.muted,\r\n  fontFamily: theme.fontFamily.mono,\r\n  fontSize: theme.fontSize.xs,\r\n  lineHeight: theme.lineHeight.sm,\r\n  overflowX: \"auto\",\r\n  whiteSpace: \"pre-wrap\",\r\n  wordBreak: \"break-word\",\r\n  color: theme.color.foreground,\r\n});\r\n\r\nconst errorPreClass = css({ color: theme.color.destructive });\r\n\r\nconst statusDotClass = css({\r\n  width: \"8px\",\r\n  height: \"8px\",\r\n  borderRadius: \"9999px\",\r\n  flexShrink: 0,\r\n});\r\n\r\nconst spinnerClass = css({\r\n  width: \"10px\",\r\n  height: \"10px\",\r\n  flexShrink: 0,\r\n  borderRadius: \"9999px\",\r\n  border: `2px solid ${theme.color.primary}`,\r\n  borderTopColor: \"transparent\",\r\n  animation: `${spin} 0.7s linear infinite`,\r\n});\r\n\r\nconst STATUS_COLOR: Record<ToolCallStatus, string> = {\r\n  pending: theme.color.mutedForeground,\r\n  running: theme.color.primary,\r\n  success: theme.color.success,\r\n  error: theme.color.destructive,\r\n};\r\n\r\nconst STATUS_LABEL: Record<ToolCallStatus, string> = {\r\n  pending: \"Queued\",\r\n  running: \"Running\",\r\n  success: \"Completed\",\r\n  error: \"Failed\",\r\n};\r\n\r\nfunction formatPayload(value: unknown): string {\r\n  if (value == null) return \"\";\r\n  if (typeof value === \"string\") return value;\r\n  try {\r\n    return JSON.stringify(value, null, 2);\r\n  } catch {\r\n    return String(value);\r\n  }\r\n}\r\n\r\nexport interface ToolCallProps {\r\n  /** The tool/function name, e.g. \"search_docs\". */\r\n  name: string;\r\n  status?: ToolCallStatus;\r\n  /** Arguments the model passed. Objects are pretty-printed as JSON. */\r\n  args?: unknown;\r\n  /** What the tool returned. Objects are pretty-printed as JSON. */\r\n  result?: unknown;\r\n  /** Error text, shown in place of the result when status is \"error\". */\r\n  error?: string;\r\n  open?: boolean;\r\n  defaultOpen?: boolean;\r\n  onOpenChange?: (open: boolean) => void;\r\n  /** Extra content rendered at the bottom of the expanded body. */\r\n  children?: ReactNode;\r\n  className?: string;\r\n}\r\n\r\n/**\r\n * A collapsible record of one tool/function invocation by an agent — name,\r\n * live status, the arguments it was called with, and what it returned. This is\r\n * the thing you need to make an agent's reasoning legible instead of a black box.\r\n */\r\nexport function ToolCall({\r\n  name,\r\n  status = \"pending\",\r\n  args,\r\n  result,\r\n  error,\r\n  open,\r\n  defaultOpen = false,\r\n  onOpenChange,\r\n  children,\r\n  className,\r\n}: ToolCallProps) {\r\n  const [isOpen, setOpen] = useControllableState({\r\n    value: open,\r\n    defaultValue: defaultOpen,\r\n    onChange: onOpenChange,\r\n  });\r\n  const bodyId = useId();\r\n\r\n  const argsText = formatPayload(args);\r\n  const resultText = error ?? formatPayload(result);\r\n  const hasBody = Boolean(argsText || resultText || children);\r\n\r\n  return (\r\n    <div className={rootVariants({ status, className })} data-state={isOpen ? \"open\" : \"closed\"}>\r\n      <button\r\n        type=\"button\"\r\n        className={triggerClass}\r\n        aria-expanded={isOpen}\r\n        aria-controls={hasBody ? bodyId : undefined}\r\n        onClick={() => hasBody && setOpen(!isOpen)}\r\n      >\r\n        {status === \"running\" ? (\r\n          <span className={spinnerClass} aria-hidden=\"true\" />\r\n        ) : (\r\n          <span\r\n            className={statusDotClass}\r\n            style={{ backgroundColor: STATUS_COLOR[status] }}\r\n            aria-hidden=\"true\"\r\n          />\r\n        )}\r\n        <span className={nameClass}>{name}</span>\r\n        <span style={{ fontSize: \"0.75rem\", color: STATUS_COLOR[status] }}>{STATUS_LABEL[status]}</span>\r\n        {hasBody ? (\r\n          <svg\r\n            className={chevronClass}\r\n            width=\"14\"\r\n            height=\"14\"\r\n            viewBox=\"0 0 14 14\"\r\n            fill=\"none\"\r\n            aria-hidden=\"true\"\r\n          >\r\n            <path d=\"M5 3L9 7L5 11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\r\n          </svg>\r\n        ) : null}\r\n      </button>\r\n\r\n      {isOpen && hasBody ? (\r\n        <div className={bodyClass} id={bodyId}>\r\n          {argsText ? (\r\n            <div>\r\n              <p className={sectionLabelClass}>Arguments</p>\r\n              <pre className={preClass}>{argsText}</pre>\r\n            </div>\r\n          ) : null}\r\n          {resultText ? (\r\n            <div>\r\n              <p className={sectionLabelClass}>{error ? \"Error\" : \"Result\"}</p>\r\n              <pre className={error ? `${preClass} ${errorPreClass}` : preClass}>{resultText}</pre>\r\n            </div>\r\n          ) : null}\r\n          {children}\r\n        </div>\r\n      ) : null}\r\n    </div>\r\n  );\r\n}\r\n",
      "type": "registry:component"
    }
  ]
}