{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sources",
  "type": "registry:component",
  "title": "Sources",
  "description": "Display URL sources with favicon, title, and an external link.",
  "dependencies": [
    "@assistant-ui/react",
    "lucide-react"
  ],
  "registryDependencies": [
    "https://r.assistant-ui.com/badge.json"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/sources.aui.tsx",
      "sourcePath": "packages/ui/src/components/react/assistant-ui/elements/sources.aui.tsx",
      "content": "\"use client\";\n\nimport { memo, useState, type ComponentProps } from \"react\";\nimport { FileTextIcon } from \"lucide-react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { SourceMessagePartComponent } from \"@assistant-ui/react\";\nimport { cn } from \"@/lib/utils\";\nimport { Badge } from \"@/components/ui/badge\";\n\nconst sourceVariants = cva(\n  \"inline-flex items-center justify-center gap-1 rounded-md text-xs font-medium transition-colors [&_svg]:size-3 [&_svg]:shrink-0\",\n  {\n    variants: {\n      variant: {\n        outline:\n          \"border-input text-muted-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground border bg-transparent\",\n        secondary:\n          \"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/80\",\n        muted:\n          \"bg-muted text-muted-foreground [a&]:hover:bg-muted/80 [a&]:hover:text-foreground\",\n        ghost:\n          \"text-muted-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground bg-transparent\",\n        info: \"bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300 [a&]:hover:bg-blue-100/80\",\n        warning:\n          \"bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300 [a&]:hover:bg-amber-100/80\",\n        success:\n          \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/50 dark:text-emerald-300 [a&]:hover:bg-emerald-100/80\",\n        destructive:\n          \"bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300 [a&]:hover:bg-red-100/80\",\n      },\n      size: {\n        sm: \"px-1.5 py-0.5\",\n        default: \"px-2 py-1\",\n        lg: \"px-2.5 py-1.5 text-sm\",\n      },\n    },\n    defaultVariants: {\n      variant: \"outline\",\n      size: \"default\",\n    },\n  },\n);\n\nconst extractDomain = (url: string): string => {\n  try {\n    return new URL(url).hostname.replace(/^www\\./, \"\");\n  } catch {\n    return url;\n  }\n};\n\nconst defaultFaviconUrl = (domain: string) =>\n  `https://icons.duckduckgo.com/ip3/${domain}.ico`;\n\nfunction SourceIcon({\n  url,\n  className,\n  faviconUrl = defaultFaviconUrl,\n  ...props\n}: ComponentProps<\"span\"> & {\n  url: string;\n  faviconUrl?: (domain: string) => string;\n}) {\n  const domain = extractDomain(url);\n  const src = faviconUrl(domain);\n  const [errorSrc, setErrorSrc] = useState<string | undefined>(undefined);\n  const hasError = errorSrc === src;\n\n  if (hasError) {\n    return (\n      <span\n        data-slot=\"source-icon-fallback\"\n        className={cn(\n          \"bg-muted flex size-3 shrink-0 items-center justify-center rounded-sm text-[10px] font-medium\",\n          className,\n        )}\n        {...props}\n      >\n        {domain.charAt(0).toUpperCase() || \"?\"}\n      </span>\n    );\n  }\n\n  return (\n    <img\n      data-slot=\"source-icon\"\n      src={src}\n      alt=\"\"\n      className={cn(\"size-3 shrink-0 rounded-sm\", className)}\n      onError={() => setErrorSrc(src)}\n      {...(props as ComponentProps<\"img\">)}\n      // A server-rendered image that fails before hydration never fires onError.\n      ref={(el) => {\n        if (el?.complete && el.naturalWidth === 0) setErrorSrc(src);\n      }}\n    />\n  );\n}\n\nfunction SourceTitle({ className, ...props }: ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"source-title\"\n      className={cn(\"max-w-37.5 truncate\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DocumentSourceIcon({ className, ...props }: ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"source-document-icon\"\n      className={cn(\n        \"text-muted-foreground flex size-3 shrink-0 items-center justify-center\",\n        className,\n      )}\n      {...props}\n    >\n      <FileTextIcon className=\"size-3\" />\n    </span>\n  );\n}\n\nexport type SourceProps = ComponentProps<\"a\"> &\n  VariantProps<typeof sourceVariants>;\n\nfunction Source({\n  className,\n  variant,\n  size,\n  target = \"_blank\",\n  rel = \"noopener noreferrer\",\n  ...props\n}: SourceProps) {\n  return (\n    <a\n      data-slot=\"source\"\n      className={cn(\n        sourceVariants({ variant, size }),\n        \"focus-visible:border-ring focus-visible:ring-ring/50 cursor-pointer outline-none focus-visible:ring-1\",\n        className,\n      )}\n      target={target}\n      rel={rel}\n      {...props}\n    />\n  );\n}\n\nconst SourcesImpl: SourceMessagePartComponent = (part) => {\n  if (part.sourceType === \"url\" && part.url) {\n    const domain = extractDomain(part.url);\n    const displayTitle = part.title || domain;\n\n    return (\n      <Source href={part.url}>\n        <SourceIcon url={part.url} />\n        <SourceTitle>{displayTitle}</SourceTitle>\n      </Source>\n    );\n  }\n\n  if (part.sourceType === \"document\") {\n    return (\n      <Badge\n        variant=\"secondary\"\n        className=\"focus-visible:border-ring focus-visible:ring-ring/50 outline-none focus-visible:ring-1\"\n      >\n        <span data-slot=\"source\" className=\"inline-flex items-center gap-1.5\">\n          <DocumentSourceIcon />\n          <SourceTitle>{part.title}</SourceTitle>\n        </span>\n      </Badge>\n    );\n  }\n\n  return null;\n};\n\nconst Sources = memo(SourcesImpl) as unknown as SourceMessagePartComponent & {\n  Root: typeof Source;\n  Icon: typeof SourceIcon;\n  Title: typeof SourceTitle;\n};\n\nSources.displayName = \"Sources\";\nSources.Root = Source;\nSources.Icon = SourceIcon;\nSources.Title = SourceTitle;\n\nexport { Sources, Source, SourceIcon, SourceTitle, sourceVariants };\n"
    }
  ]
}