{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sources",
  "title": "Sources",
  "author": "Jamie Thompson <jamie@jamiethompson.design> (https://jamiethompson.design)",
  "description": "Collapsible list of the sources an AI answer cited. Sources wraps a collapsible; SourcesTrigger shows \"Used N sources\" (singular for one) with a chevron; SourcesContent reveals Source links (book icon + title; absolute URLs open in a new tab). A Source without href renders as plain text. Use under an assistant message to surface citations from web-search or retrieval tool results.",
  "dependencies": [
    "cn",
    "lucide-react"
  ],
  "registryDependencies": [
    "collapsible"
  ],
  "files": [
    {
      "path": "registry/ai/sources.tsx",
      "content": "// Derived from Vercel AI Elements sources.tsx (Apache-2.0, Copyright 2023 Vercel, Inc.).\n// Modified for uifiles: ported from Radix UI to Base UI; dependencies point at @uifiles.\n\"use client\"\n\nimport { cn } from \"cn\"\nimport { BookIcon, ChevronDownIcon } from \"lucide-react\"\nimport type { ComponentProps } from \"react\"\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\"\n\nexport type SourcesProps = ComponentProps<\"div\"> &\n  Pick<\n    ComponentProps<typeof Collapsible>,\n    \"open\" | \"defaultOpen\" | \"onOpenChange\" | \"disabled\"\n  >\n\nexport const Sources = ({ className, ...props }: SourcesProps) => (\n  <Collapsible\n    className={cn(\"not-prose mb-4 text-xs text-primary\", className)}\n    {...props}\n  />\n)\n\nexport type SourcesTriggerProps = ComponentProps<typeof CollapsibleTrigger> & {\n  count: number\n}\n\nexport const SourcesTrigger = ({\n  className,\n  count,\n  children,\n  ...props\n}: SourcesTriggerProps) => (\n  <CollapsibleTrigger\n    className={cn(\"flex min-h-6 items-center gap-2\", className)}\n    {...props}\n  >\n    {children ?? (\n      <>\n        <span className=\"font-medium\">\n          {`Used ${count} ${count === 1 ? \"source\" : \"sources\"}`}\n        </span>\n        <ChevronDownIcon className=\"h-4 w-4\" />\n      </>\n    )}\n  </CollapsibleTrigger>\n)\n\nexport type SourcesContentProps = ComponentProps<typeof CollapsibleContent>\n\nexport const SourcesContent = ({\n  className,\n  ...props\n}: SourcesContentProps) => (\n  <CollapsibleContent\n    className={cn(\n      \"mt-3 flex w-fit flex-col gap-2\",\n      \"outline-none data-open:animate-in data-open:slide-in-from-top-2 data-closed:animate-out data-closed:fade-out-0 data-closed:slide-out-to-top-2\",\n      className\n    )}\n    {...props}\n  />\n)\n\nexport type SourceProps = ComponentProps<\"a\">\n\n/** Absolute (`https://…`, `mailto:`) or protocol-relative URLs leave the app. */\nconst isExternal = (href: string) => /^(?:[a-z][a-z\\d+.-]*:|\\/\\/)/i.test(href)\n\nconst hostnameOf = (href: string) => {\n  try {\n    return new URL(href).hostname\n  } catch {\n    return \"\"\n  }\n}\n\nexport const Source = ({\n  href,\n  title,\n  children,\n  className,\n  rel,\n  target,\n  ...props\n}: SourceProps) => {\n  // Providers do return empty titles; an empty string is as unusable as a\n  // missing one, so both fall through to the hostname.\n  const content = children || (\n    <>\n      <BookIcon className=\"h-4 w-4\" />\n      <span className=\"block font-medium\">\n        {title || (href ? hostnameOf(href) || href : undefined)}\n      </span>\n    </>\n  )\n  const classes = cn(\"flex min-h-6 items-center gap-2\", className)\n\n  if (!href) {\n    // An <a> without href is neither a link nor focusable; render plain text.\n    return (\n      <span className={classes} {...(props as ComponentProps<\"span\">)}>\n        {content}\n      </span>\n    )\n  }\n\n  const external = isExternal(href)\n\n  return (\n    <a\n      className={classes}\n      href={href}\n      rel={rel ?? (external ? \"noreferrer noopener\" : undefined)}\n      target={target ?? (external ? \"_blank\" : undefined)}\n      {...props}\n    >\n      {content}\n    </a>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ai/sources.tsx"
    }
  ],
  "docs": "Derived from Vercel AI Elements (Apache-2.0, Copyright 2023 Vercel, Inc.); ported to Base UI and maintained by uifiles. Built for the Base UI styles (base-nova): it composes the Base UI wrappers from components/ui and relies on their render prop and event signatures, so it does not work on Radix or React Aria styles. Ported to Base UI Collapsible: SourcesTrigger and SourcesContent accept Base UI Trigger/Panel props (render instead of asChild; keepMounted instead of forceMount). Open/closed animation classes use data-open/data-closed instead of data-[state=open|closed]. Sources types `open`, `defaultOpen`, `onOpenChange` and `disabled` (Base UI root props) on top of upstream's div props. Divergences from upstream: the trigger pluralizes (\"Used 1 source\") and is a 24px-tall target; Source without `href` renders a <span> rather than an anchor; absolute URLs get target=\"_blank\" rel=\"noreferrer noopener\" while relative links open in the same tab; a missing or empty `title` falls back to the URL's hostname (or the href itself when it has none), and empty `children` fall back to the icon and title, so a provider's empty title never yields an unlabeled link.",
  "categories": [
    "ai",
    "chat"
  ],
  "type": "registry:component"
}