{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sources",
  "title": "Sources",
  "description": "what an answer is based on · numbered chips with previews · `[n]` in text → inline citations",
  "dependencies": [
    "cn@^0.4.0",
    "lucide-react@^1.52.0"
  ],
  "registryDependencies": [
    "popover"
  ],
  "files": [
    {
      "path": "components/kit/sources.tsx",
      "content": "'use client';\nimport { ExternalLinkIcon, FileTextIcon } from 'lucide-react';\nimport { cn } from 'cn';\nimport * as React from 'react';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\n\nexport type Source = {\n  title: string;\n  /** Where it lives: \"Invoice · 28 May\", \"help.northwind.co\". */\n  meta?: string;\n  /** The passage the answer used, one or two sentences. */\n  snippet?: string;\n  href?: string;\n};\n\nfunction Preview({ n, source }: { n: number; source: Source }) {\n  return (\n    <div className=\"flex flex-col gap-2 text-sm\">\n      <p className=\"flex items-start gap-2 font-medium\">\n        <span className=\"bg-muted text-foreground flex size-5 shrink-0 items-center justify-center rounded text-xs tabular-nums\">{n}</span>\n        {source.title}\n      </p>\n      {source.meta ? <p className=\"text-muted-foreground text-xs\">{source.meta}</p> : null}\n      {source.snippet ? <blockquote className=\"border-border text-muted-foreground border-l-2 pl-3 leading-6\">{source.snippet}</blockquote> : null}\n      {source.href ? (\n        <a href={source.href} target=\"_blank\" rel=\"noreferrer\" className=\"text-foreground inline-flex items-center gap-1 text-xs font-medium underline underline-offset-4\">\n          Open source <ExternalLinkIcon className=\"size-3\" aria-hidden />\n        </a>\n      ) : null}\n    </div>\n  );\n}\n\n/** The sources under an answer: numbered chips, each opening a preview of what was used. */\nexport function SourceList({ sources, label = 'Sources', className }: { sources: Source[]; label?: string; className?: string }) {\n  const id = React.useId();\n  if (!sources.length) return null;\n  return (\n    <div className={cn('flex flex-col gap-2', className)}>\n      <p id={id} className=\"text-muted-foreground text-xs font-medium\">\n        {label}\n      </p>\n      <ol aria-labelledby={id} className=\"flex flex-wrap gap-2\">\n        {sources.map((s, i) => (\n          <li key={`${s.title}-${i}`}>\n            <Popover>\n              <PopoverTrigger className=\"border-border bg-card hover:bg-accent focus-visible:ring-ring flex max-w-60 items-center gap-2 rounded-lg border px-2 py-1 text-left text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none\">\n                <span className=\"bg-muted flex size-4 shrink-0 items-center justify-center rounded text-xs leading-none font-medium tabular-nums\">{i + 1}</span>\n                <FileTextIcon className=\"text-muted-foreground size-3.5 shrink-0\" aria-hidden />\n                <span className=\"truncate\">{s.title}</span>\n              </PopoverTrigger>\n              <PopoverContent align=\"start\" className=\"w-80\">\n                <Preview n={i + 1} source={s} />\n              </PopoverContent>\n            </Popover>\n          </li>\n        ))}\n      </ol>\n    </div>\n  );\n}\n\n/**\n * An answer with inline citations: every `[n]` in `text` becomes a small numbered button that opens the\n * nth source's preview. While an answer streams, show it with `stripCitations` instead.\n */\nexport function CitedText({ text, sources, className }: { text: string; sources: Source[]; className?: string }) {\n  const parts = text.split(/(\\[\\d+\\])/g);\n  return (\n    <p className={className}>\n      {parts.map((part, i) => {\n        const n = /^\\[(\\d+)\\]$/.exec(part)?.[1];\n        const source = n ? sources[Number(n) - 1] : undefined;\n        if (!n || !source) return <React.Fragment key={i}>{part}</React.Fragment>;\n        return (\n          <Popover key={i}>\n            <PopoverTrigger\n              aria-label={`Source ${n}: ${source.title}`}\n              className=\"bg-muted text-foreground hover:bg-accent focus-visible:ring-ring mx-0.5 inline-flex h-4 min-w-4 -translate-y-1 items-center justify-center rounded px-0.5 align-baseline text-xs leading-none font-medium tabular-nums focus-visible:ring-2 focus-visible:outline-none\">\n              {n}\n            </PopoverTrigger>\n            <PopoverContent align=\"start\" className=\"w-80\">\n              <Preview n={Number(n)} source={source} />\n            </PopoverContent>\n          </Popover>\n        );\n      })}\n    </p>\n  );\n}\n\n/** The answer without its `[n]` markers, for StreamingText. */\nexport const stripCitations = (text: string) => text.replace(/\\s?\\[\\d+\\]/g, '');\n",
      "type": "registry:component",
      "target": "components/kit/sources.tsx"
    }
  ],
  "meta": {
    "exports": [
      "SourceList",
      "CitedText",
      "stripCitations"
    ],
    "api": "<CitedText text=\"… [1]\" sources={[{ title, meta?, snippet?, href? }]} /><SourceList sources />"
  },
  "docs": "Needs the kit theme (`@kit/theme`) and a next-themes ThemeProvider (attribute=\"class\") at the root.",
  "categories": [
    "ai"
  ],
  "type": "registry:component"
}