{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "inline-citation",
  "title": "InlineCitation",
  "description": "Inline source pill that previews stacked citations on hover and links to the source on click.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.neon.com/r/neon-tokens.json"
  ],
  "files": [
    {
      "path": "src/components/inline-citation/inline-citation.tsx",
      "content": "\"use client\";\n\nimport { PreviewCard } from \"@base-ui/react/preview-card\";\nimport type { ComponentProps } from \"react\";\nimport { useState } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/** Strip a URL down to its readable hostname. */\nconst hostnameOf = (url: string): string => {\n  try {\n    return new URL(url).hostname.replace(/^www\\./u, \"\");\n  } catch {\n    return url;\n  }\n};\n\nconst faviconOf = (url: string): string | null => {\n  try {\n    const host = new URL(url).hostname;\n    return `https://www.google.com/s2/favicons?domain=${host}&sz=64`;\n  } catch {\n    return null;\n  }\n};\n\n/**\n * The source site's favicon, resolved through Google's favicon\n * service. Decorative — vanishes silently if it fails to load, so\n * the pill never shows a broken-image glyph.\n */\nconst SourceFavicon = ({\n  className,\n  url,\n}: {\n  url: string;\n  className?: string;\n}) => {\n  const [failed, setFailed] = useState(false);\n  const src = faviconOf(url);\n\n  if (!src || failed) {\n    return null;\n  }\n\n  return (\n    // oxlint-disable-next-line react/no-unknown-property -- plain img: registry components stay framework-agnostic\n    <img\n      alt=\"\"\n      aria-hidden=\"true\"\n      className={cn(\"size-3 shrink-0 select-none rounded-[2px]\", className)}\n      data-slot=\"inline-citation-favicon\"\n      loading=\"lazy\"\n      onError={() => setFailed(true)}\n      src={src}\n    />\n  );\n};\n\nexport type InlineCitationProps = ComponentProps<\"span\">;\n\n/* ─────────────────────────────────────────────────────\n * A sourced claim inline with prose: the text reads\n * normally, a small mono pill names where it came from,\n * and hovering (or focusing) the pill previews the\n * source without leaving the paragraph. Sources stack\n * in one readable list — no carousel to page through.\n * Inspired by Inline Citation from Vercel's AI SDK\n * Elements (elements.ai-sdk.dev), rebuilt on Base UI.\n * ─────────────────────────────────────────────────── */\nexport const InlineCitation = ({\n  className,\n  ...props\n}: InlineCitationProps) => (\n  <span\n    className={cn(\"group/citation inline items-baseline\", className)}\n    data-slot=\"inline-citation\"\n    {...props}\n  />\n);\n\nexport type InlineCitationTextProps = ComponentProps<\"span\">;\n\n/** The cited span itself; tints gently while its pill is hovered. */\nexport const InlineCitationText = ({\n  className,\n  ...props\n}: InlineCitationTextProps) => (\n  <span\n    className={cn(\n      \"transition-colors group-hover/citation:bg-muted/60\",\n      className\n    )}\n    data-slot=\"inline-citation-text\"\n    {...props}\n  />\n);\n\nexport type InlineCitationCardProps = PreviewCard.Root.Props;\n\n/** The hover-preview boundary around a trigger pill and its body. */\nexport const InlineCitationCard = (props: InlineCitationCardProps) => (\n  <PreviewCard.Root {...props} />\n);\n\nexport type InlineCitationCardTriggerProps = Omit<\n  PreviewCard.Trigger.Props,\n  \"href\"\n> & {\n  /** Source URLs. The first is the link target and names the pill. */\n  sources: string[];\n};\n\n/**\n * The citation pill: a mono chip naming the first source's hostname,\n * with a `+n` tail when more sources back the claim. It is a real\n * link — click goes to the source, hover previews it.\n */\nexport const InlineCitationCardTrigger = ({\n  className,\n  sources,\n  ...props\n}: InlineCitationCardTriggerProps) => {\n  const [first] = sources;\n\n  return (\n    <PreviewCard.Trigger\n      className={cn(\n        \"ml-1 inline-flex max-w-40 items-center gap-1 rounded-sm border border-border/60 bg-muted/50 px-1 align-text-bottom font-mono text-[10px] text-muted-foreground leading-4 no-underline transition-colors hover:border-border hover:text-foreground focus-visible:border-primary focus-visible:outline-none\",\n        className\n      )}\n      data-slot=\"inline-citation-card-trigger\"\n      href={first}\n      rel=\"noopener noreferrer\"\n      target=\"_blank\"\n      {...props}\n    >\n      {first ? <SourceFavicon url={first} /> : null}\n      <span className=\"truncate\">{first ? hostnameOf(first) : \"source\"}</span>\n      {sources.length > 1 ? (\n        <span className=\"shrink-0 text-muted-foreground/60\">\n          +{sources.length - 1}\n        </span>\n      ) : null}\n    </PreviewCard.Trigger>\n  );\n};\n\nexport type InlineCitationCardBodyProps = PreviewCard.Popup.Props & {\n  align?: PreviewCard.Positioner.Props[\"align\"];\n  sideOffset?: PreviewCard.Positioner.Props[\"sideOffset\"];\n};\n\n/** The preview popup: stacked sources, quotes, and anything else. */\nexport const InlineCitationCardBody = ({\n  align = \"start\",\n  className,\n  sideOffset = 6,\n  ...props\n}: InlineCitationCardBodyProps) => (\n  <PreviewCard.Portal>\n    <PreviewCard.Positioner align={align} sideOffset={sideOffset}>\n      <PreviewCard.Popup\n        className={cn(\n          \"fade-in-0 slide-in-from-bottom-1 z-50 w-72 max-w-[calc(100vw-2rem)] animate-in space-y-3 rounded-lg border border-border/60 bg-card p-3 duration-200 motion-reduce:animate-none\",\n          className\n        )}\n        data-slot=\"inline-citation-card-body\"\n        {...props}\n      />\n    </PreviewCard.Positioner>\n  </PreviewCard.Portal>\n);\n\nexport type InlineCitationSourceProps = ComponentProps<\"div\"> & {\n  title?: string;\n  url?: string;\n  description?: string;\n};\n\n/** One source: title, mono URL, and an optional dimmed description. */\nexport const InlineCitationSource = ({\n  children,\n  className,\n  description,\n  title,\n  url,\n  ...props\n}: InlineCitationSourceProps) => (\n  <div\n    className={cn(\"space-y-0.5\", className)}\n    data-slot=\"inline-citation-source\"\n    {...props}\n  >\n    {title ? (\n      <div className=\"flex items-center gap-1.5 font-medium text-foreground text-sm leading-snug\">\n        {url ? <SourceFavicon className=\"size-3.5\" url={url} /> : null}\n        <span className=\"min-w-0 truncate\">{title}</span>\n      </div>\n    ) : null}\n    {url ? (\n      <div className=\"truncate font-mono text-muted-foreground text-xs\">\n        {url}\n      </div>\n    ) : null}\n    {description ? (\n      <div className=\"text-muted-foreground/80 text-xs leading-relaxed\">\n        {description}\n      </div>\n    ) : null}\n    {children}\n  </div>\n);\n\nexport type InlineCitationQuoteProps = ComponentProps<\"blockquote\">;\n\n/** A short excerpt from the source, on the house rail. */\nexport const InlineCitationQuote = ({\n  className,\n  ...props\n}: InlineCitationQuoteProps) => (\n  <blockquote\n    className={cn(\n      \"border-border/60 border-l-2 pl-2.5 text-muted-foreground text-xs italic leading-relaxed\",\n      className\n    )}\n    data-slot=\"inline-citation-quote\"\n    {...props}\n  />\n);\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}