import { Link, useSearchParams } from 'react-router'; import { X } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { fileUrl } from '../lib/api'; import type { GenerationRow } from '../lib/api'; import { Lightbox } from './Lightbox'; import { useLightboxManager } from './useLightboxManager'; import { useGalleryManager } from './useGalleryManager'; /** * The main surface: every image, or one collection's images when `?collection=` is set. * * The collection view is HERE rather than in the Collections panel because that panel is a narrow * selector — showing a grid of images inside it meant two thumbnails per row, and it replaced the * list you were selecting from with the thing you selected. */ export const Gallery = () => { const [searchParams] = useSearchParams(); const collectionId = searchParams.get('collection') ?? undefined; const manager = useGalleryManager(collectionId); // Hooks run before any early return, or the order changes between renders. const lightbox = useLightboxManager(manager.rows); const allImagesTo = (() => { const next = new URLSearchParams(searchParams); next.delete('collection'); next.delete('selected'); const search = next.toString(); return { search: search ? `?${search}` : '' }; })(); if (manager.loading) return

Loading…

; if (manager.error) return

{manager.error}

; return (
{manager.collection && ( <>

{manager.collection.name}

All images )}

{manager.succeeded.length} image{manager.succeeded.length === 1 ? '' : 's'} {manager.totalCost > 0 && ` · $${manager.totalCost.toFixed(4)} total`}

{manager.rows.length === 0 ? (

{manager.collection ? 'Nothing filed here yet.' : 'Nothing generated yet.'}

) : (
{manager.rows.map((row) => ( void manager.removeFromCollection(row.id) : undefined} /> ))}
)}
); }; type TileProps = { row: GenerationRow; searchParams: URLSearchParams; onRemove?: () => void }; const Tile = ({ row, searchParams, onRemove }: TileProps) => { const src = fileUrl(row.url); const to = (() => { const next = new URLSearchParams(searchParams); next.set('selected', row.id); return { search: `?${next.toString()}` }; })(); return (
{src ? ( // A real link, not a button with an onClick — the selection lives in the URL // (`?selected=`), so this is a navigation and it earns cmd-click, middle-click and // focus-visible for free. {row.prompt} ) : ( {row.error ?? row.status} )}
{/* Only in collection mode: "remove" means remove from THIS collection, and on the all-images view there is nothing to remove it from. */} {onRemove && ( )}

{row.prompt}

{row.model} {row.cost_usd && ` · $${Number(row.cost_usd).toFixed(4)}`} {row.duration_ms != null && ` · ${(row.duration_ms / 1000).toFixed(1)}s`}

); };