{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "usage-meter",
  "title": "Usage Meter",
  "description": "A context-window meter with token counts and estimated cost.",
  "files": [
    {
      "path": "components/spectrumui/blocks/ai-assistants/usage-meter.tsx",
      "content": "'use client';\n\nimport { cn } from '@/lib/utils';\nimport type { UsageState } from './types';\n\nexport type UsageMeterVariant = 'Bar' | 'Inline';\n\nexport interface UsageMeterProps {\n  usage: UsageState;\n  variant?: UsageMeterVariant;\n  className?: string;\n}\n\nfunction formatTokens(count: number) {\n  if (count < 1000) return `${count}`;\n  return `${(count / 1000).toFixed(1)}k`;\n}\n\nexport function UsageMeter({ usage, variant = 'Bar', className }: UsageMeterProps) {\n  const used = usage.promptTokens + usage.completionTokens;\n  const ratio = Math.min(1, used / usage.contextWindow);\n  const percent = Math.round(ratio * 100);\n\n  const barColor =\n    ratio > 0.9\n      ? 'bg-red-500/80'\n      : ratio > 0.75\n        ? 'bg-amber-500/80'\n        : 'bg-neutral-900 dark:bg-neutral-100';\n\n  if (variant === 'Inline') {\n    return (\n      <div\n        className={cn(\n          'flex w-fit items-center gap-2.5 font-mono text-[11.5px] tabular-nums text-neutral-500 dark:text-neutral-400',\n          className,\n        )}\n      >\n        <span aria-hidden className=\"h-1 w-16 overflow-hidden rounded-full bg-black/[0.07] dark:bg-white/[0.09]\">\n          <span\n            className={cn('block h-full rounded-full transition-[width] duration-500 ease-[cubic-bezier(0.23,1,0.32,1)]', barColor)}\n            style={{ width: `${percent}%` }}\n          />\n        </span>\n        <span>\n          {formatTokens(used)} / {formatTokens(usage.contextWindow)}\n        </span>\n        {usage.estimatedCostUsd !== undefined && (\n          <span className=\"text-neutral-400 dark:text-neutral-600\">\n            ${usage.estimatedCostUsd.toFixed(4)}\n          </span>\n        )}\n      </div>\n    );\n  }\n\n  return (\n    <div className={cn('w-full max-w-[380px]', className)}>\n      <div className=\"flex items-baseline justify-between gap-3\">\n        <span className=\"text-[12.5px] font-medium text-neutral-800 dark:text-neutral-200\">\n          Context window\n        </span>\n        <span className=\"font-mono text-[11.5px] tabular-nums text-neutral-500 dark:text-neutral-400\">\n          {formatTokens(used)} / {formatTokens(usage.contextWindow)} · {percent}%\n        </span>\n      </div>\n\n      <div\n        role=\"meter\"\n        aria-valuemin={0}\n        aria-valuemax={usage.contextWindow}\n        aria-valuenow={used}\n        aria-label=\"Context window usage\"\n        className=\"mt-2 h-1.5 overflow-hidden rounded-full bg-black/[0.06] dark:bg-white/[0.08]\"\n      >\n        <span\n          className={cn(\n            'block h-full rounded-full transition-[width] duration-500 ease-[cubic-bezier(0.23,1,0.32,1)]',\n            barColor,\n          )}\n          style={{ width: `${percent}%` }}\n        />\n      </div>\n\n      <div className=\"mt-2.5 flex items-center justify-between font-mono text-[10.5px] tabular-nums text-neutral-400 dark:text-neutral-600\">\n        <span>\n          prompt {formatTokens(usage.promptTokens)} · completion {formatTokens(usage.completionTokens)}\n        </span>\n        {usage.estimatedCostUsd !== undefined && <span>≈ ${usage.estimatedCostUsd.toFixed(4)}</span>}\n      </div>\n    </div>\n  );\n}\n\nexport default UsageMeter;\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/ai-assistants/usage-meter.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"
}
