{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "citation-sources",
  "title": "Citation Sources",
  "description": "Inline citation markers with hover previews or a footnote list.",
  "files": [
    {
      "path": "components/spectrumui/blocks/ai-assistants/citation-sources.tsx",
      "content": "'use client';\n\nimport { cn } from '@/lib/utils';\nimport type { Citation } from './types';\n\nexport type CitationSourcesVariant = 'Inline' | 'Footnotes';\n\nexport interface CitationSourcesProps {\n  text: string;\n  citations: Citation[];\n  variant?: CitationSourcesVariant;\n  className?: string;\n}\n\nexport function CitationSources({\n  text,\n  citations,\n  variant = 'Inline',\n  className,\n}: CitationSourcesProps) {\n  const byIndex = new Map(citations.map((citation) => [citation.index, citation]));\n  const parts = text.split(/(\\[\\d+\\])/g).filter(Boolean);\n\n  return (\n    <div className={cn('w-full max-w-[520px] text-[13.5px]', className)}>\n      <p className=\"leading-[1.7] text-neutral-700 dark:text-neutral-300\">\n        {parts.map((part, index) => {\n          const match = /^\\[(\\d+)\\]$/.exec(part);\n          if (!match) return <span key={index}>{part}</span>;\n          const citation = byIndex.get(Number(match[1]));\n          if (!citation) return <span key={index}>{part}</span>;\n          return <CitationMarker key={index} citation={citation} />;\n        })}\n      </p>\n\n      {variant === 'Footnotes' && (\n        <ol className=\"mt-4 space-y-2 border-t border-black/[0.06] pt-3 dark:border-white/[0.07]\">\n          {citations.map((citation) => (\n            <li key={citation.id} className=\"flex gap-2.5 text-[12.5px] leading-[1.55]\">\n              <span className=\"grid size-[16px] shrink-0 translate-y-0.5 place-items-center rounded bg-black/[0.06] font-mono text-[9px] font-semibold text-neutral-600 dark:bg-white/[0.08] dark:text-neutral-300\">\n                {citation.index}\n              </span>\n              <span className=\"min-w-0\">\n                <span className=\"font-medium text-neutral-800 dark:text-neutral-200\">\n                  {citation.title}\n                </span>\n                <span className=\"ml-1.5 font-mono text-[10.5px] text-neutral-400 dark:text-neutral-600\">\n                  {new URL(citation.url).hostname}\n                </span>\n                {citation.snippet && (\n                  <span className=\"mt-0.5 block text-neutral-500 dark:text-neutral-400\">\n                    {citation.snippet}\n                  </span>\n                )}\n              </span>\n            </li>\n          ))}\n        </ol>\n      )}\n    </div>\n  );\n}\n\nfunction CitationMarker({ citation }: { citation: Citation }) {\n  return (\n    <span className=\"group/cite relative inline-block\">\n      <button\n        type=\"button\"\n        aria-label={`Source ${citation.index}: ${citation.title}`}\n        className=\"mx-0.5 inline-grid size-[15px] -translate-y-1 place-items-center rounded bg-black/[0.07] font-mono text-[9px] font-semibold text-neutral-600 transition-[background-color,color,transform] duration-150 hover:bg-neutral-900 hover:text-white active:scale-[0.9] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-neutral-400 dark:bg-white/[0.1] dark:text-neutral-300 dark:hover:bg-neutral-100 dark:hover:text-neutral-900\"\n      >\n        {citation.index}\n      </button>\n\n      <span\n        role=\"tooltip\"\n        className={cn(\n          'pointer-events-none absolute bottom-full left-1/2 z-10 mb-1.5 w-60 -translate-x-1/2 rounded-xl border border-black/[0.08] bg-white p-3 text-left shadow-lg dark:border-white/[0.1] dark:bg-neutral-900',\n          'origin-bottom scale-[0.97] opacity-0 blur-[2px] transition-[opacity,transform,filter] duration-[140ms] ease-[cubic-bezier(0.23,1,0.32,1)]',\n          'group-hover/cite:scale-100 group-hover/cite:opacity-100 group-hover/cite:blur-0',\n          'group-focus-within/cite:scale-100 group-focus-within/cite:opacity-100 group-focus-within/cite:blur-0',\n        )}\n      >\n        <span className=\"block text-[12px] font-medium leading-snug text-neutral-900 dark:text-neutral-100\">\n          {citation.title}\n        </span>\n        <span className=\"mt-0.5 block font-mono text-[10px] text-neutral-400 dark:text-neutral-500\">\n          {new URL(citation.url).hostname}\n        </span>\n        {citation.snippet && (\n          <span className=\"mt-1.5 block text-[11.5px] leading-[1.55] text-neutral-500 dark:text-neutral-400\">\n            {citation.snippet}\n          </span>\n        )}\n      </span>\n    </span>\n  );\n}\n\nexport default CitationSources;\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/ai-assistants/citation-sources.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"
}
