{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "elements-link-preview",
  "type": "registry:component",
  "title": "Link preview",
  "description": "A returned URL unfurled into a card with enough context to decide whether to open it.",
  "registryDependencies": [
    "https://r.assistant-ui.com/elements-surfaces.json",
    "https://r.assistant-ui.com/elements-href.json"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/link-preview.tsx",
      "sourcePath": "packages/ui/src/components/react/assistant-ui/elements/link-preview.tsx",
      "content": "\"use client\";\n\nimport { useState, type ComponentProps } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { hostOf, safeHref } from \"../utils/href\";\nimport { field, mono, paper } from \"./surfaces\";\n\nexport interface LinkPreviewProps extends Omit<\n  ComponentProps<\"article\">,\n  \"children\" | \"title\"\n> {\n  href: string;\n  title?: string | undefined;\n  description?: string | undefined;\n  image?: string | undefined;\n  imageAlt?: string | undefined;\n  siteName?: string | undefined;\n  favicon?: string | undefined;\n  layout?: \"card\" | \"compact\" | undefined;\n}\n\nexport function LinkPreview({\n  href,\n  title,\n  description,\n  image,\n  imageAlt,\n  siteName,\n  favicon,\n  layout = \"card\",\n  className,\n  ...props\n}: LinkPreviewProps) {\n  const [failedImage, setFailedImage] = useState<string | undefined>();\n  const [failedFavicon, setFailedFavicon] = useState<string | undefined>();\n  const safeUrl = safeHref(href);\n  const host = hostOf(href);\n  const site = siteName ?? host;\n  const label = title || host || safeUrl || \"Untitled link\";\n  const hasImage = image !== undefined && image !== \"\" && failedImage !== image;\n  const hasFavicon =\n    favicon !== undefined && favicon !== \"\" && failedFavicon !== favicon;\n\n  const imageFrame = hasImage ? (\n    <div\n      data-slot=\"link-preview-image\"\n      className={cn(\n        field,\n        \"relative shrink-0 overflow-hidden\",\n        layout === \"card\" ? \"aspect-video w-full\" : \"size-16\",\n      )}\n    >\n      <img\n        src={image}\n        alt={imageAlt ?? \"\"}\n        className=\"size-full object-cover\"\n        onError={() => setFailedImage(image)}\n      />\n    </div>\n  ) : null;\n\n  return (\n    <article\n      data-slot=\"link-preview\"\n      data-layout={layout}\n      className={cn(\n        paper,\n        \"focus-within:ring-foreground/20 relative flex w-full max-w-sm overflow-hidden rounded-2xl transition-transform duration-150 ease-[cubic-bezier(0.23,1,0.32,1)] outline-none focus-within:ring-1 hover:-translate-y-px motion-reduce:transition-none\",\n        layout === \"card\" ? \"flex-col\" : \"items-stretch\",\n        className,\n      )}\n      {...props}\n    >\n      {imageFrame}\n      <div className=\"flex min-w-0 flex-1 flex-col gap-1 p-3\">\n        <div className=\"flex min-w-0 items-center gap-1.5\">\n          {hasFavicon ? (\n            <img\n              data-slot=\"link-preview-favicon\"\n              src={favicon}\n              alt=\"\"\n              className=\"size-4 shrink-0 rounded-sm object-cover\"\n              onError={() => setFailedFavicon(favicon)}\n            />\n          ) : (\n            <span\n              aria-hidden\n              data-slot=\"link-preview-site-initial\"\n              className={cn(\n                field,\n                \"text-foreground/45 flex size-4 shrink-0 items-center justify-center rounded-sm text-[9px] font-medium\",\n              )}\n            >\n              {site?.charAt(0).toUpperCase() ?? \"?\"}\n            </span>\n          )}\n          {site ? (\n            <span className={cn(mono, \"text-foreground/40 truncate\")}>\n              {site}\n            </span>\n          ) : null}\n        </div>\n        {safeUrl ? (\n          <a\n            href={safeUrl}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            className=\"text-foreground/90 line-clamp-2 text-[13.5px] leading-5 font-medium after:absolute after:inset-0 after:z-10\"\n          >\n            {label}\n            <span className=\"sr-only\">(opens in a new tab)</span>\n          </a>\n        ) : (\n          <span className=\"text-foreground/90 line-clamp-2 text-[13.5px] leading-5 font-medium\">\n            {label}\n          </span>\n        )}\n        {description ? (\n          <span className=\"text-foreground/50 line-clamp-2 text-xs leading-relaxed\">\n            {description}\n          </span>\n        ) : null}\n      </div>\n    </article>\n  );\n}\n"
    }
  ]
}