import { useEffect, useState } from "react"; import { get } from "../api"; import { useT, type Key } from "../i18n"; import { Spinner } from "./Activity"; import { Icon } from "./Icon"; interface MediaItem { kind: "image" | "video" | "audio"; url: string; name: string; source: "generated" | "tool" | "written" | "answer" | "upload"; created_at: number; message_id?: string; } type Filter = "all" | MediaItem["kind"]; /** * All images, videos and audio files of a chat (with its subagents), newest first. * An item opens large; "show in chat" jumps to the message it came with. */ export function MediaGallery({ sessionId, onClose, onJump }: { sessionId: string; onClose: () => void; onJump: (messageId: string) => void }) { const t = useT(); const [items, setItems] = useState(); const [error, setError] = useState(); const [filter, setFilter] = useState("all"); const [open, setOpen] = useState(); useEffect(() => { get(`/sessions/${sessionId}/media`) .then(setItems) .catch((e) => setError(e.message)); }, [sessionId]); const shown = (items ?? []).filter((i) => filter === "all" || i.kind === filter); const count = (k: Filter) => (items ?? []).filter((i) => k === "all" || i.kind === k).length; const current = open !== undefined ? shown[open] : undefined; // Keyboard: Esc closes, arrows step through the open item. useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") return current ? setOpen(undefined) : onClose(); if (current && e.key === "ArrowRight") setOpen((o) => Math.min((o ?? 0) + 1, shown.length - 1)); if (current && e.key === "ArrowLeft") setOpen((o) => Math.max((o ?? 0) - 1, 0)); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [current, shown.length, onClose]); return (
e.stopPropagation()}>

{t("media.title")}

{(["all", "image", "video", "audio"] as Filter[]).map((k) => ( ))}
{error &&
{error}
} {!items && !error && (
)} {items && shown.length === 0 &&
{t("media.none")}
} {current ? (
{current.kind === "image" && {current.name}} {current.kind === "video" &&
{current.name} · {t(`media.source.${current.source}` as Key)} · {new Date(current.created_at).toLocaleString(undefined, { dateStyle: "short", timeStyle: "short" })} {current.message_id && ( )} {t("media.open")}
) : (
{shown.map((m, i) => ( ))}
)}
); }