{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tool-chips",
  "title": "Tool Chips",
  "description": "Compact tool-call chips that expand into their arguments and result.",
  "files": [
    {
      "path": "components/spectrumui/blocks/ai-assistants/tool-chips.tsx",
      "content": "'use client';\n\nimport { useState } from 'react';\nimport { cn } from '@/lib/utils';\nimport type { ToolCall, ToolCallStatus } from './types';\n\nconst DOT: Record<ToolCallStatus, string> = {\n  pending: 'bg-neutral-300 dark:bg-neutral-600',\n  running: 'bg-sky-500/80 motion-safe:animate-pulse',\n  success: 'bg-emerald-500/80',\n  error: 'bg-red-500/80',\n  cancelled: 'bg-neutral-400 dark:bg-neutral-500',\n};\n\nexport type ToolChipsVariant = 'Row' | 'Stack';\n\nexport interface ToolChipsProps {\n  calls: ToolCall[];\n  variant?: ToolChipsVariant;\n  className?: string;\n}\n\nfunction duration(call: ToolCall) {\n  if (!call.startedAt || !call.completedAt) return null;\n  return `${((call.completedAt - call.startedAt) / 1000).toFixed(1)}s`;\n}\n\nexport function ToolChips({ calls, variant = 'Row', className }: ToolChipsProps) {\n  const [openId, setOpenId] = useState<string | null>(null);\n  const open = calls.find((call) => call.id === openId);\n\n  return (\n    <div className={cn('w-full max-w-[480px]', className)}>\n      <div className={cn('flex gap-1.5', variant === 'Stack' ? 'flex-col' : 'flex-wrap')}>\n        {calls.map((call) => {\n          const active = call.id === openId;\n          const elapsed = duration(call);\n          return (\n            <button\n              key={call.id}\n              type=\"button\"\n              aria-expanded={active}\n              onClick={() => setOpenId(active ? null : call.id)}\n              className={cn(\n                'flex items-center gap-2 rounded-lg border py-1.5 pl-2.5 pr-3 text-left',\n                'transition-[border-color,background-color,transform] duration-150 active:scale-[0.98]',\n                'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-neutral-400',\n                active\n                  ? 'border-black/[0.16] bg-black/[0.03] dark:border-white/[0.22] dark:bg-white/[0.05]'\n                  : 'border-black/[0.07] hover:border-black/[0.14] dark:border-white/[0.08] dark:hover:border-white/[0.16]',\n                variant === 'Stack' && 'w-full',\n              )}\n            >\n              <span className={cn('size-1.5 shrink-0 rounded-full', DOT[call.status])} />\n              <span className=\"truncate font-mono text-[11.5px] font-medium text-neutral-700 dark:text-neutral-300\">\n                {call.name}\n              </span>\n              {call.status === 'running' ? (\n                <span className=\"ml-auto shrink-0 font-mono text-[10px] uppercase tracking-wide text-sky-600 dark:text-sky-400\">\n                  running\n                </span>\n              ) : elapsed ? (\n                <span className=\"ml-auto shrink-0 font-mono text-[10px] tabular-nums text-neutral-400 dark:text-neutral-600\">\n                  {elapsed}\n                </span>\n              ) : null}\n            </button>\n          );\n        })}\n      </div>\n\n      <div\n        className=\"grid transition-[grid-template-rows] duration-[240ms] ease-[cubic-bezier(0.32,0.72,0,1)]\"\n        style={{ gridTemplateRows: open?.result ? '1fr' : '0fr' }}\n      >\n        <div className=\"overflow-hidden\">\n          {open?.result && (\n            <div className=\"mt-2 rounded-lg bg-black/[0.03] px-3 py-2.5 dark:bg-white/[0.04]\">\n              {open.args && (\n                <p className=\"mb-1.5 truncate font-mono text-[10.5px] text-neutral-400 dark:text-neutral-600\">\n                  {JSON.stringify(open.args)}\n                </p>\n              )}\n              <p className=\"text-[12.5px] leading-[1.6] text-neutral-600 dark:text-neutral-400\">\n                {open.result}\n              </p>\n            </div>\n          )}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default ToolChips;\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/ai-assistants/tool-chips.tsx"
    },
    {
      "path": "components/spectrumui/blocks/ai-assistants/types.ts",
      "content": "export type MessageRole = 'user' | 'assistant' | 'system';\n\nexport type MessageState = 'streaming' | 'complete' | 'error';\n\nexport interface Attachment {\n  id: string;\n  name: string;\n  size: number;\n  type: string;\n  url?: string;\n  previewUrl?: string;\n}\n\nexport interface Citation {\n  id: string;\n  index: number;\n  url: string;\n  title: string;\n  snippet?: string;\n  favicon?: string;\n}\n\nexport type ToolCallStatus = 'pending' | 'running' | 'success' | 'error' | 'cancelled';\n\nexport interface ToolCall {\n  id: string;\n  name: string;\n  args?: Record<string, unknown>;\n  result?: string;\n  status: ToolCallStatus;\n  startedAt?: number;\n  completedAt?: number;\n  children?: ToolCall[];\n  parallel?: boolean;\n}\n\nexport interface ReasoningStep {\n  id: string;\n  content: string;\n}\n\nexport interface Reasoning {\n  steps: ReasoningStep[];\n  status: 'thinking' | 'complete';\n  durationMs?: number;\n}\n\nexport interface Message {\n  id: string;\n  role: MessageRole;\n  content: string;\n  createdAt?: number;\n  state?: MessageState;\n  attachments?: Attachment[];\n  citations?: Citation[];\n  toolCalls?: ToolCall[];\n  reasoning?: Reasoning;\n}\n\nexport interface Conversation {\n  id: string;\n  label: string;\n}\n\nexport interface ModelOption {\n  id: string;\n  name: string;\n  description?: string;\n  badge?: string;\n  disabled?: boolean;\n}\n\nexport interface SuggestedPrompt {\n  id: string;\n  label: string;\n  prompt?: string;\n}\n\nexport type MessageFeedback = 'positive' | 'negative';\n\nexport interface UsageState {\n  promptTokens: number;\n  completionTokens: number;\n  contextWindow: number;\n  estimatedCostUsd?: number;\n}\n",
      "type": "registry:file",
      "target": "components/spectrumui/blocks/ai-assistants/types.ts"
    }
  ],
  "type": "registry:block"
}
