import AudioAttachment, { type AudioComposerProps, type AudioReceivedProps, type AudioSentProps, } from './Audio' import FileAttachment, { type FileComposerProps, type FileReceivedProps, type FileSentProps, } from './File' import ImageAttachment, { type ImageComposerProps, type ImageReceivedProps, type ImageSentProps, } from './Image' import PdfAttachment, { type PdfComposerProps, type PdfReceivedProps, type PdfSentProps, } from './Pdf' import VideoAttachment, { type VideoComposerProps, type VideoReceivedProps, type VideoSentProps, } from './Video' /** * Per-MIME chat attachment cards used inside the messaging stream. * * Each subnamespace (`Image`, `Video`, `Audio`, `Pdf`, `File`) exposes * three states (`Composer`, `Sent`, `Received`) that map to the * sender / recipient sides of a chat: * * - `Composer` — sender draft state with a dismiss `×` affordance. * - `Sent` — sender-side state shown after posting. * - `Received` — recipient-side state. * * Sent / Received variants accept an optional `text` prop that renders * inside the same bubble as the attachment — mirroring the mobile chat * where a sender can pair a caption with an attachment ('Here is the * file', 'Here is the image'). Composer prop types intentionally omit * `text` (and `items` / `groupPosition`) so the type system enforces * the same contract: captions live in the message-input textarea, not * inside the draft attachment preview. * * Stacked attachments — every type except `Composer` accepts an * `items={[…]}` prop on Sent / Received that takes precedence over * the singular `src`/`filename`/etc. inputs: * * - Image / Video render a 1 / 2 / 3 / 4-tile grid (5+ collapse * into a `+N` overflow tile). Activating any tile opens the * built-in `ImageViewer` / `VideoViewer` at that index; when * `items.length > 1` the lightbox becomes a carousel (prev / next * chrome, ArrowLeft / ArrowRight, current / total counter) so the * user can browse siblings without closing the modal. * - Pdf / Audio / File render their compact rows / players in a * vertical stack with an 8px gap between rows. Each row keeps * its own activation target — Pdf opens the carousel-aware * `PdfViewer` at that index, Audio gets its own native player, * and File downloads the asset for that row. * * The Composer surface accepts only a single attachment at a time, * so its props omit `items` / `text`. * * Image / Video Composer renders bare — just the media with rounded * corners and a dismiss overlay, no surrounding bubble — so the draft * preview reads as an in-progress attachment instead of a sent * message. Sent / Received wrap the media in the shared bubble chrome. * * Image / Video / Pdf are interactive in every state (Composer, Sent, * Received) — clicks open the corresponding native lightbox ``, * which centers the media at reasonable max dimensions (Escape and * backdrop click also dismiss). The Image viewer adds an explicit * download button to the chrome; the Video viewer relies on the * native `