import { type JSX, splitProps, createSignal, Show, For } from 'solid-js'; import { cn } from '../utils/cn'; import { Collapsible, CollapsibleTrigger, CollapsibleContent, type CollapsibleController } from '../ui/collapsible'; import { Button } from '../ui/button'; import { CheckCircle, ChevronDown, Loader2, Settings, XCircle } from 'lucide-solid'; import type { ToolPart } from './tool-types'; export type { ToolPart } from './tool-types'; export interface ToolProps { toolPart: ToolPart; defaultOpen?: boolean; /** Gate the disclosure trigger (programmatic control still works). */ disabled?: boolean; /** Receive the open controller once mounted (forwarded from the Collapsible), * so a parent (the kai-tool facade) can drive/observe open state. */ controllerRef?: (api: CollapsibleController) => void; class?: string; } function formatValue(value: unknown): string { if (value === null) return 'null'; if (value === undefined) return 'undefined'; if (typeof value === 'string') return value; if (typeof value === 'object') return JSON.stringify(value, null, 2); return String(value); } function ToolStateIcon(props: { state: ToolPart['state'] }) { return ( <> ); } // Status chips: a hue as text over a 15% translucent fill of the same hue // (mirroring the inline-code chip). The TEXT color comes from a theme token // (--color-tool-*) whose light value is darkened so it reaches WCAG AA (4.5:1) // on the faint fill, while dark mode keeps a brighter hue for AA on the dark // surface — both modes resolve via the token's `.dark` override. The FILL keeps // a fixed bright hue so the chip's colored tint looks the same in both modes. const STATE_TOKEN: Record = { 'input-streaming': 'var(--color-tool-blue)', 'input-available': 'var(--color-tool-amber)', 'output-available': 'var(--color-tool-green)', 'output-error': 'var(--color-tool-red)', }; const STATE_FILL: Record = { 'input-streaming': 'hsl(217 91% 60%)', // blue 'input-available': 'hsl(38 92% 50%)', // amber 'output-available': 'hsl(142 71% 45%)', // green 'output-error': 'hsl(0 84% 60%)', // red }; function stateChip(state: ToolPart['state']): JSX.CSSProperties { return { color: STATE_TOKEN[state], background: `color-mix(in oklab, ${STATE_FILL[state]} 15%, transparent)`, }; } function ToolStateBadge(props: { state: ToolPart['state'] }) { const baseClasses = 'px-2 py-1 rounded-full text-xs font-medium'; return ( <> Processing Ready Completed Error ); } function Tool(props: ToolProps) { const [local, rest] = splitProps(props, ['toolPart', 'defaultOpen', 'disabled', 'controllerRef', 'class']); // Drive the Collapsible UNCONTROLLED (seed from defaultOpen) and read its open // state via the controller it hands up — that same controller is forwarded to // the facade so wireDisclosure can drive show/hide/toggle. const [api, setApi] = createSignal(); const isOpen = () => api()?.open() ?? (local.defaultOpen ?? false); const state = () => local.toolPart.state; const input = () => local.toolPart.input; const output = () => local.toolPart.output; return (
{ setApi(a); local.controllerRef?.(a); }} > ) => ( )} />
0}>

Input

{([key, value]) => (
{key}:{' '} {formatValue(value)}
)}

Output

{formatValue(output())}

Error

{local.toolPart.errorText}
Processing tool call...
Call ID: {local.toolPart.toolCallId}
); } export { Tool };