"use client"; import React, { useState } from 'react'; import { Video } from '../features/video'; import { ImageGalleryModal } from '../ui/image-gallery-modal'; export interface MediaGalleryStripItem { media_type: string; media_url: string; title?: string | null; /** Stable key when present (e.g. release_media rows); falls back to index. */ id?: string; } /** Still-images open the lightbox; clips ('video'/'demo') play inline. */ function isGalleryImage(mediaType: string): boolean { return mediaType !== 'video' && mediaType !== 'demo'; } export interface MediaGalleryStripProps { items: MediaGalleryStripItem[]; className?: string; } /** * Read-only media gallery — a horizontal-scroll strip of 240×200 tiles where * still-images open an {@link ImageGalleryModal} lightbox and video clips play * inline via {@link Video}. The lightbox index is the tile's rank among the * IMAGE-only items (clips are skipped), so the modal opens on the correct image. * * Single source of truth for the detail-page media strip — replaces the markup * that was hand-rolled inline in the product-release and "What I Shipped" detail * pages (the release copy also had a raw-index lightbox bug this component fixes). */ export function MediaGalleryStrip({ items, className }: MediaGalleryStripProps) { const [galleryOpen, setGalleryOpen] = useState(false); const [galleryIndex, setGalleryIndex] = useState(0); if (!items || items.length === 0) return null; const galleryImages = items.filter((m) => isGalleryImage(m.media_type)).map((m) => m.media_url); const tileClass = 'shrink-0 w-[240px] h-[200px] rounded-md overflow-hidden border border-ods-border bg-black transition-opacity'; return ( <>