{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "elements-inline-citation",
  "type": "registry:component",
  "title": "Inline citation",
  "description": "Numbered references inside a sentence, each with a hover preview of its source.",
  "registryDependencies": [
    "https://r.assistant-ui.com/elements-surfaces.json"
  ],
  "dependencies": [
    "@base-ui/react"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/inline-citation.tsx",
      "content": "\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { PreviewCard } from \"@base-ui/react/preview-card\";\nimport { cn } from \"@/lib/utils\";\nimport { floating, mono } from \"./surfaces\";\n\nexport interface Source {\n  domain: string;\n  title: string;\n  snippet: string;\n}\n\ninterface CitationProps {\n  index: number;\n  source: Source;\n  open: boolean;\n  onOpenChange: (open: boolean) => void;\n}\n\nfunction Citation({ index, source, open, onOpenChange }: CitationProps) {\n  return (\n    <PreviewCard.Root open={open} onOpenChange={onOpenChange}>\n      <PreviewCard.Trigger\n        delay={0}\n        render={<button type=\"button\" />}\n        className={cn(\n          \"mx-0.5 inline-flex h-4 min-w-4 translate-y-[-2px] cursor-default items-center justify-center rounded-[5px] px-1 align-middle font-mono text-[10px] font-medium tabular-nums transition-colors\",\n          open\n            ? \"bg-foreground text-background\"\n            : \"bg-foreground/[0.06] text-foreground/45 hover:text-foreground/90\",\n        )}\n      >\n        {index + 1}\n      </PreviewCard.Trigger>\n      <PreviewCard.Portal>\n        <PreviewCard.Positioner side=\"top\" sideOffset={8}>\n          <PreviewCard.Popup\n            className={cn(\n              floating,\n              \"z-50 w-64 origin-(--transform-origin) rounded-2xl p-3.5 outline-none\",\n              \"transition-[opacity,scale] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-none\",\n              \"data-[starting-style]:scale-[0.97] data-[starting-style]:opacity-0\",\n              \"data-[ending-style]:scale-[0.97] data-[ending-style]:opacity-0\",\n            )}\n          >\n            <div className=\"flex items-center gap-1.5\">\n              <span className=\"bg-foreground/[0.06] text-foreground/45 flex size-4 items-center justify-center rounded text-[9px] font-medium\">\n                {source.domain[0]?.toUpperCase()}\n              </span>\n              <span className={cn(mono, \"text-foreground/40\")}>\n                {source.domain}\n              </span>\n            </div>\n            <p className=\"mt-2 text-[13px] leading-snug font-medium\">\n              {source.title}\n            </p>\n            <p className=\"text-foreground/50 mt-1 text-[13px] leading-relaxed\">\n              {source.snippet}\n            </p>\n          </PreviewCard.Popup>\n        </PreviewCard.Positioner>\n      </PreviewCard.Portal>\n    </PreviewCard.Root>\n  );\n}\n\nexport interface InlineCitationProps extends Omit<\n  ComponentProps<\"p\">,\n  \"children\"\n> {\n  sources: Source[];\n  openIndex: number | null;\n  onOpenIndexChange: (index: number | null) => void;\n}\n\nexport function InlineCitation({\n  sources,\n  openIndex,\n  onOpenIndexChange,\n  className,\n  ...props\n}: InlineCitationProps) {\n  return (\n    <p\n      data-slot=\"inline-citation\"\n      className={cn(\n        \"text-foreground/90 max-w-sm text-sm leading-relaxed\",\n        className,\n      )}\n\n      {...props}\n    >\n      Optimistic updates keep the thread responsive while the server confirms\n      the write\n      {sources[0] && (\n        <Citation\n          index={0}\n          source={sources[0]}\n          open={openIndex === 0}\n          onOpenChange={(open) => onOpenIndexChange(open ? 0 : null)}\n        />\n      )}\n      . The store already exposes a consistent snapshot for every subscriber\n      {sources[1] && (\n        <Citation\n          index={1}\n          source={sources[1]}\n          open={openIndex === 1}\n          onOpenChange={(open) => onOpenIndexChange(open ? 1 : null)}\n        />\n      )}\n      , so no extra reconciliation pass is needed.\n    </p>\n  );\n}\n"
    }
  ]
}