{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "link-card",
  "type": "registry:ui",
  "title": "Link card",
  "description": "A link as a glass object: the host and path on a screen tinted by the host, a LINK tag and one OPEN ↗ action.",
  "dependencies": [],
  "registryDependencies": [
    "https://metalui.dev/r/tokens.json"
  ],
  "files": [
    {
      "path": "packages/metalui/src/blocks/link-card/link-card.tsx",
      "type": "registry:ui",
      "target": "components/metalui/blocks/link-card/link-card.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { GlassFace } from '../../components/glass-face/glass-face';\nimport { Chip } from '../../components/chip/chip';\n\n/* ─────────────────────────────────────────────────────────\n * LINK CARD (the reference design's .linkobj): a custom block\n *   GlassFace › Screen tinted by the host (the link-card recipe) › Chip(glass, LED link) LINK\n *   + Chip(glass-action, a link) OPEN ↗ + the host and the path\n * Custom because the tinted screen and its type are drawn by no component. The card is not a click\n * target: only OPEN is, and it opens in a new tab.\n *\n *   plain     the host big, the path under it (no preview yet, a secret block, the past, a failure)\n *   preview   the page's title becomes the big line (2 lines at most); the host and path move into\n *             a small line with the site's icon; the page's image sits behind the tinted glow,\n *             shaded to the bottom so the words stay readable; the card grows to the preview\n *             height and the preview fades in on settle, so nothing jumps\n * ───────────────────────────────────────────────────────── */\n\n/* The card's own colour is the recipe's tint unless a host passes one; the tinted screen replaces the\n * glass face's, and its type is the recipe's. The tag and action sit at the chip inset. */\nconst CARD = 'mu-linkcard w-link-card-width link-card-tint';\nconst SCREEN = 'mu-linkcard-screen box-border flex flex-col justify-end h-link-card-screen-height py-link-card-screen-pad-y px-link-card-screen-pad-x !recipe-link-card-screen link-card-grow reduced-motion:transition-none';\nconst TAG = 'mu-linkcard-tag !absolute left-link-card-chip-inset top-link-card-chip-inset';\nconst OPEN = 'mu-linkcard-open !absolute right-link-card-chip-inset top-link-card-chip-inset z-2';\nconst HOST = 'mu-linkcard-host type-link-card-host text-link-card-host-ink';\nconst PATH = 'mu-linkcard-path overflow-hidden text-ellipsis whitespace-nowrap uppercase type-link-card-path text-link-card-path-ink';\nconst TALL = '!h-link-card-preview-height';\nconst IMAGE = 'mu-linkcard-image pointer-events-none absolute inset-0 size-full object-cover opacity-link-card-preview-image-opacity link-card-preview-in';\nconst SHADE = 'pointer-events-none absolute inset-0 link-card-image-shade';\nconst TITLE = 'mu-linkcard-title relative link-card-title-clamp type-link-card-title text-link-card-title-ink link-card-preview-in';\nconst META = 'mu-linkcard-meta relative flex min-w-0 items-center gap-link-card-meta-gap text-link-card-meta-ink link-card-preview-in';\nconst ICON = 'size-link-card-meta-icon flex-none rounded-round';\nconst META_TEXT = 'min-w-0 overflow-hidden text-ellipsis whitespace-nowrap uppercase type-link-card-path';\n\n/** What the backend found at the link (GET /preview): all optional. */\nexport interface LinkPreview {\n  title?: string | null;\n  description?: string | null;\n  image?: string | null;\n  icon?: string | null;\n}\n\n/** The hue (0-359) the reference derives from a host, for a host to tint the screen with:\n * the reference tints with that hue at the recipe's tint-saturation and tint-lightness. */\nexport function linkHueDegrees(host: string) {\n  let h = 0;\n  for (const c of host) h = (h * 31 + c.charCodeAt(0)) % 360;\n  return h;\n}\n\nexport interface LinkCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  href: string;\n  /** Default: the URL's host without www. */\n  host?: string;\n  /** Default: the URL's path and query; \"/\" when empty. */\n  path?: string;\n  /** The screen's tint, any CSS colour. Default: the recipe's tint. */\n  hue?: string;\n  /** The tag's word. Default LINK. */\n  tag?: string;\n  /** The action's words. Default OPEN ↗. */\n  openLabel?: string;\n  /** The page's title, image and icon from the backend. Never pass one for a secret block or in the past. */\n  preview?: LinkPreview | null;\n}\n\nconst parts = (href: string) => {\n  try {\n    const u = new URL(href);\n    return { host: u.hostname.replace(/^www\\./, ''), path: decodeURIComponent(u.pathname + u.search).replace(/^\\/$/, '') };\n  } catch {\n    return { host: href, path: '' };\n  }\n};\n\n/** A link as a glass object with one explicit Open action. */\nexport const LinkCard = React.forwardRef<HTMLDivElement, LinkCardProps>(function LinkCard(\n  { href, host, path, hue, tag = 'LINK', openLabel = 'OPEN ↗', preview, className, style, ...props },\n  ref,\n) {\n  const p = parts(href);\n  const h = host ?? p.host;\n  const shownPath = (path ?? p.path) || '/';\n  const title = preview?.title?.trim() || null;\n  return (\n    <GlassFace.Root\n      ref={ref}\n      data-mu-self=\"\"\n      className={className ? `${CARD} ${className}` : CARD}\n      style={hue ? ({ '--mu-self': hue, ...style } as React.CSSProperties) : style}\n      {...props}\n    >\n      <GlassFace.Screen className={title ? `${SCREEN} ${TALL}` : SCREEN}>\n        {title && preview?.image && <img src={preview.image} alt=\"\" className={IMAGE} />}\n        {title && preview?.image && <span aria-hidden className={SHADE} />}\n        <Chip variant=\"glass\" className={TAG}>\n          <Chip.Lead led=\"link\" />\n          <Chip.Text>{tag}</Chip.Text>\n        </Chip>\n        <Chip as=\"a\" variant=\"glass-action\" className={OPEN} href={href} target=\"_blank\" rel=\"noopener noreferrer\">\n          {openLabel}\n        </Chip>\n        {title ? (\n          <>\n            <div className={TITLE}>{title}</div>\n            <div className={META}>\n              {preview?.icon && <img src={preview.icon} alt=\"\" className={ICON} />}\n              <span className={META_TEXT}>{h}{shownPath !== '/' ? ` · ${shownPath}` : ''}</span>\n            </div>\n          </>\n        ) : (\n          <>\n            <div className={HOST}>{h}</div>\n            <div className={PATH}>{shownPath}</div>\n          </>\n        )}\n      </GlassFace.Screen>\n    </GlassFace.Root>\n  );\n});\n"
    }
  ],
  "docs": "Agent guide: https://metalui.dev/r/link-card.md. SwiftUI: MetalLinkCard in the MetalUI Swift package."
}
