import type React from 'react' /** * Visual layout for a `LinkAttachment.*` card. * * - `featured` — full card with a 180px hero thumbnail above the body. * The default; matches the hero-image "LinkApps" frames in Figma * (Spotify with cover art, TikTok with a frame, etc.). * - `classic` — compact card without a hero thumbnail. Title / * description / URL / CTA only. Used for Link App embeds that don't * carry artwork (and for plain link previews without OG imagery). * * For document / image / video / audio chat attachments, reach for * `MessageAttachment.{Image,Video,Audio,Pdf,File}` instead — those * render as bubbles with built-in viewers and download support and * carry a `text` slot for accompanying captions. */ export type LinkAttachmentLayout = 'featured' | 'classic' /** * Render state of a link card. Mirrors Stream's `LinkPreviewStatus` 1:1 * (`LOADING` -> `'loading'`, `LOADED` -> `'ready'`, `FAILED` -> `'failed'`) * so a composer can forward its preview state without inventing a parallel * vocabulary. */ export type LinkAttachmentStatus = 'loading' | 'ready' | 'failed' /** * Shared props for the three `LinkAttachment.*` variants (Composer, Sent, * Received). Maps to the "Attachments" + "LinkApps" sections of the messaging * design system in Figma — a 290px-wide, 250px-tall card whose hero thumbnail * absorbs whatever height the chin doesn't use, a title (optionally prefixed * with a brand badge for Link Apps), a description, and either a URL footer * or a CTA button. */ export interface LinkAttachmentBaseProps { title?: string /** Placeholder shown in the title slot before one is configured (dark variants only). */ placeholderTitle?: string /** Secondary description rendered below the title. */ description?: string /** * Max lines the description renders before truncating with an ellipsis. * Defaults to `1` (single-line truncate — the link-preview default). Set * higher for cards whose body is prose (e.g. official messages) so it wraps * across multiple lines like the mobile card. */ descriptionLines?: number /** * Optional URL displayed at the bottom of the card (e.g. `tr.ee/briemix`). * Ignored when `cta` is provided. Also used as the navigation target for * the Received card when no `cta` is set. */ url?: string /** MIME type of the hero thumbnail — drives the type icon for empty states. */ mimeType?: string /** Hero thumbnail shown above the title block; fills the card above the chin. */ thumbnailUrl?: string /** * Source URL for playable media in the hero region. When provided * alongside a video / audio `mimeType`, the hero renders an inline * native player (used by media-rich link previews that embed a * preview clip). For chat document / file attachments, reach for * `MessageAttachment.{Pdf,File}` instead. */ sourceUrl?: string /** * Visual layout — `'featured'` keeps the 180px hero thumbnail above the * body, `'classic'` drops the hero entirely for a compact text-only * card. Defaults to `'featured'`. */ layout?: LinkAttachmentLayout /** * Optional 16x16 brand badge rendered before the title (used by Link Apps: * Spotify, TikTok, FAQ, Form, etc.). Consumers render whatever they want * — typically a colored 4px-rounded square containing a glyph or ``. */ appIcon?: React.ReactNode /** * Optional call-to-action rendered below the description. When set, * replaces the URL footer (e.g. FAQ "View FAQs", Form "Complete form"). */ cta?: LinkAttachmentCta /** * Dominant colour extracted from `thumbnailUrl`, as a hex string * (`#RRGGBB` or `#RGB`). Paints the card and — when the card is the last * bubble in a same-author run — its tail. Its hue is kept while the lightness * is swapped to a fixed per-side stop (`oklch(from …)` in `styles.css`); the * stops clear WCAG AA at every hue by construction, so callers never pass a * text colour. * * Only painted while the hero image it was extracted from is actually on * screen. Omitted, unparseable, `layout: 'classic'`, no `thumbnailUrl`, * `status` other than `'ready'`, a playable-audio `mimeType` + `sourceUrl` * (whose hero is the native player rather than an image), or a hero that fails * to load => the card keeps the plain bubble fill. Browsers without * relative-colour-syntax support also keep that fill (progressive enhancement). */ accentColor?: string /** * Render state. `'loading'` shows a neutral pulsing hero plus two skeleton * text bars and ignores `accentColor` (there is no image yet). `'failed'` * renders the fallback chin with whatever text is available. Defaults to * `'ready'`. * * `'loading'` outranks the hero shape that `mimeType` + `sourceUrl` would * otherwise select: a card mid-scrape does not yet know what the asset is, so * it renders the skeleton rather than a live `