@tangle-network/agent-app 0.46.2 → 0.46.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +1 -1
  2. package/dist/assistant/index.js +3 -1
  3. package/dist/assistant/index.js.map +1 -1
  4. package/dist/chunk-KKBTPZIE.js +805 -0
  5. package/dist/chunk-KKBTPZIE.js.map +1 -0
  6. package/dist/{chunk-2RTYQU4W.js → chunk-LEGZX5MQ.js} +150 -4
  7. package/dist/chunk-LEGZX5MQ.js.map +1 -0
  8. package/dist/chunk-RLOHIX5Y.js +36 -0
  9. package/dist/chunk-RLOHIX5Y.js.map +1 -0
  10. package/dist/{chunk-MBKNKAN2.js → chunk-YTEUZXX2.js} +584 -1252
  11. package/dist/chunk-YTEUZXX2.js.map +1 -0
  12. package/dist/mention-editor-GVLG3W7C.js +521 -0
  13. package/dist/mention-editor-GVLG3W7C.js.map +1 -0
  14. package/dist/stories/studio/StudioProviders.d.ts +10 -0
  15. package/dist/stories/studio/fixtures.d.ts +10 -2
  16. package/dist/studio/audio-preview.d.ts +12 -0
  17. package/dist/studio/generation.d.ts +22 -1
  18. package/dist/studio/index.d.ts +2 -0
  19. package/dist/studio/index.js +29 -1
  20. package/dist/studio/ports.d.ts +54 -0
  21. package/dist/studio-react/composer-option-controls.d.ts +19 -8
  22. package/dist/studio-react/download-generations.d.ts +4 -0
  23. package/dist/studio-react/index.d.ts +38 -22
  24. package/dist/studio-react/index.js +2198 -545
  25. package/dist/studio-react/index.js.map +1 -1
  26. package/dist/studio-react/media-tile.d.ts +21 -0
  27. package/dist/studio-react/media-viewer.d.ts +12 -0
  28. package/dist/studio-react/studio-confirm.d.ts +8 -0
  29. package/dist/studio-react/studio-generation-screen.d.ts +42 -0
  30. package/dist/studio-react/studio-history-screen.d.ts +28 -0
  31. package/dist/studio-react/studio-home-screen.d.ts +20 -0
  32. package/dist/studio-react/studio-playback.d.ts +31 -0
  33. package/dist/studio-react/studio-toasts.d.ts +21 -0
  34. package/dist/studio-react/studio.css +142 -72
  35. package/dist/studio-react/use-batch-navigation.d.ts +20 -0
  36. package/dist/studio-react/use-deferred-delete.d.ts +19 -0
  37. package/dist/studio-react/use-generation-history.d.ts +22 -0
  38. package/dist/studio-react/vault-path-popover.d.ts +12 -0
  39. package/dist/teams/drizzle/invitations-schema.d.ts +1 -1
  40. package/dist/teams/drizzle/schema.d.ts +1 -1
  41. package/dist/web-react/chat-composer.d.ts +24 -4
  42. package/dist/web-react/index.d.ts +1 -0
  43. package/dist/web-react/index.js +12 -6
  44. package/dist/web-react/mention-boundaries.d.ts +33 -0
  45. package/dist/web-react/mention-editor.d.ts +84 -0
  46. package/dist/web-react/mention-list.d.ts +35 -0
  47. package/dist/web-react/mention-pill.d.ts +10 -0
  48. package/dist/web-react/mention-serialize.d.ts +44 -0
  49. package/dist/web-react/use-file-mentions.d.ts +28 -14
  50. package/package.json +33 -7
  51. package/dist/chunk-2RTYQU4W.js.map +0 -1
  52. package/dist/chunk-MBKNKAN2.js.map +0 -1
  53. package/dist/studio-react/generation-card.d.ts +0 -9
  54. package/dist/studio-react/generation-detail-modal.d.ts +0 -11
  55. package/dist/studio-react/generation-detail.d.ts +0 -6
  56. package/dist/studio-react/generation-grid.d.ts +0 -15
  57. package/dist/studio-react/library-drawer.d.ts +0 -12
  58. package/dist/studio-react/library-panel.d.ts +0 -15
  59. package/dist/studio-react/result-canvas.d.ts +0 -6
  60. package/dist/studio-react/studio-header.d.ts +0 -5
  61. package/dist/studio-react/studio-sheet.d.ts +0 -12
  62. package/dist/studio-react/studio-workspace.d.ts +0 -21
  63. package/dist/studio-react/type-config.d.ts +0 -11
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/studio-react/studio-workspace.tsx","../../src/studio-react/use-studio-generations.ts","../../src/studio-react/studio-composer.tsx","../../src/studio-react/composer-option-controls.tsx","../../src/studio-react/studio-header.tsx","../../src/studio-react/result-canvas.tsx","../../src/studio-react/type-config.ts","../../src/studio-react/library-drawer.tsx","../../src/studio-react/studio-sheet.tsx","../../src/studio-react/generation-grid.tsx","../../src/studio-react/generation-card.tsx","../../src/studio-react/generation-detail.tsx","../../src/studio-react/generation-notice.tsx","../../src/studio-react/library-panel.tsx","../../src/studio-react/generation-detail-modal.tsx"],"sourcesContent":["import { useRef, useState } from 'react'\nimport { useSearchParams } from 'react-router'\nimport { Button } from '@tangle-network/sandbox-ui/primitives'\nimport { Images } from 'lucide-react'\nimport type { Generation } from '../studio'\nimport { useStudioGenerations } from './use-studio-generations'\nimport { StudioComposer } from './studio-composer'\nimport { StudioHeader } from './studio-header'\nimport { ResultCanvas } from './result-canvas'\nimport { LibraryDrawer } from './library-drawer'\n\nexport type StudioRole = 'owner' | 'admin' | 'editor' | 'viewer'\n\nexport interface StudioWorkspaceProps {\n generations: Generation[]\n totalCost: number\n workspaceId?: string\n role: StudioRole\n /** Polling endpoint override (default `/api/generations`). */\n generationsEndpoint?: string\n /** Build a vault link, optionally to a specific file. Defaults to `/app/<workspaceId>/vault`. */\n vaultHref?: (filePath?: string | null) => string\n}\n\n/**\n * The full studio surface: header + composer + result canvas + library drawer,\n * with the generation orchestrator (merge/poll/revalidate) wired in. The host\n * route owns the loader (auth, RBAC, the generation query) and the server\n * endpoints (`/api/generate`, `/api/media-models`, `/api/generations`); this\n * shell renders that data and drives the live UI. Role gates the composer\n * (viewers get a read-only library).\n */\nexport function StudioWorkspace({\n generations,\n totalCost,\n workspaceId,\n role,\n generationsEndpoint,\n vaultHref,\n}: StudioWorkspaceProps) {\n const [searchParams, setSearchParams] = useSearchParams()\n const typeFilter = searchParams.get('type')\n const [drawerOpen, setDrawerOpen] = useState(false)\n const [selected, setSelected] = useState<Generation | null>(null)\n const canvasRef = useRef<HTMLDivElement>(null)\n const canGenerate = role !== 'viewer'\n // Default vault navigation to the conventional /app/<workspaceId>/… route;\n // a host on a different route scheme passes its own builder.\n const resolvedVaultHref = vaultHref ?? (workspaceId\n ? (filePath?: string | null) => (filePath\n ? `/app/${workspaceId}/vault?file=${encodeURIComponent(filePath)}`\n : `/app/${workspaceId}/vault`)\n : undefined)\n\n const { mergedGenerations, latestBatch, onGenerated } = useStudioGenerations(generations, {\n workspaceId,\n generationsEndpoint,\n })\n\n function setFilter(type: string | null) {\n if (type) setSearchParams({ type })\n else setSearchParams({})\n }\n\n function openLibrary() {\n setSelected(null)\n setDrawerOpen(true)\n }\n\n function openDetail(generation: Generation) {\n setSelected(generation)\n setDrawerOpen(true)\n }\n\n return (\n <div className=\"min-h-0 flex-1 overflow-y-auto bg-background\">\n <StudioHeader\n count={mergedGenerations.length}\n canGenerate={canGenerate}\n onOpenLibrary={openLibrary}\n />\n\n <div className=\"mx-auto w-full max-w-3xl space-y-4 px-4 py-6 sm:px-6\">\n {canGenerate ? (\n <>\n <StudioComposer\n workspaceId={workspaceId}\n onGenerated={(generation) => {\n onGenerated(generation)\n requestAnimationFrame(() => {\n canvasRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n }}\n />\n <div ref={canvasRef}>\n <ResultCanvas\n batch={latestBatch}\n onOpenLibrary={openLibrary}\n onSelect={openDetail}\n />\n </div>\n </>\n ) : (\n <section className=\"flex flex-col items-center justify-center gap-3 rounded-xl border border-border bg-card p-10 text-center shadow-sm\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-full bg-muted\">\n <Images className=\"h-6 w-6 text-muted-foreground/40\" />\n </div>\n <p className=\"text-sm font-medium text-foreground\">\n {mergedGenerations.length > 0\n ? `${mergedGenerations.length} asset${mergedGenerations.length !== 1 ? 's' : ''} in this workspace`\n : 'No generations yet'}\n </p>\n <p className=\"max-w-sm text-xs text-muted-foreground\">\n Team generations appear in the library.\n </p>\n <Button size=\"sm\" variant=\"outline\" onClick={openLibrary}>\n <Images className=\"mr-1.5 h-4 w-4\" />\n Open library\n </Button>\n </section>\n )}\n </div>\n\n <LibraryDrawer\n open={drawerOpen}\n onOpenChange={(open) => { setDrawerOpen(open); if (!open) setSelected(null) }}\n generations={mergedGenerations}\n totalCost={totalCost}\n typeFilter={typeFilter}\n onFilterChange={setFilter}\n vaultHref={resolvedVaultHref}\n selected={selected}\n onSelect={(generation) => { setSelected(generation); if (generation) setDrawerOpen(true) }}\n />\n </div>\n )\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useRevalidator } from 'react-router'\nimport {\n type Generation,\n generationStatus,\n isLocalGeneration,\n latestBatchOf,\n mergeLiveGeneration,\n mergeLoaderAndLive,\n} from '../studio'\n\n/**\n * The generation orchestrator behind a studio surface: it merges the loader's\n * rows with in-flight live generations, computes the latest batch for the\n * canvas, polls running generations until they settle, and revalidates the\n * route loader when a status changes.\n *\n * The merge keeps the canvas, the library, and the polling path looking at the\n * same full list. Polling hits `generationsEndpoint` (default `/api/generations`,\n * the convention both apps already serve); pass an override if a product routes\n * it elsewhere. `onGenerated` is wired to the composer's per-result callback.\n */\nexport function useStudioGenerations(\n loaderGenerations: Generation[],\n options: { workspaceId?: string; generationsEndpoint?: string } = {},\n) {\n const { workspaceId, generationsEndpoint = '/api/generations' } = options\n const revalidator = useRevalidator()\n const [liveGenerations, setLiveGenerations] = useState<Generation[]>([])\n\n const mergedGenerations = useMemo(\n () => mergeLoaderAndLive(loaderGenerations, liveGenerations),\n [loaderGenerations, liveGenerations],\n )\n\n const latestBatch = useMemo(() => latestBatchOf(mergedGenerations), [mergedGenerations])\n\n const runningGenerationIds = useMemo(() => mergedGenerations\n .filter((gen) => {\n const status = generationStatus(gen)\n return status === 'pending' || status === 'running'\n })\n .map((gen) => gen.id)\n .filter((id) => !id.startsWith('local-')), [mergedGenerations])\n\n // The poll loop must not churn the effect. `runningGenerationIds` is a fresh\n // array every render and react-router's `revalidator` identity flips while a\n // revalidation is in flight — depending on either restarts the interval on\n // every tick, collapsing the 4s cadence into a request flood. Key the effect\n // on the STABLE join of the running ids (re-subscribe only when the set\n // actually changes) and reach the live ids / revalidate through refs.\n const runningIdsRef = useRef(runningGenerationIds)\n runningIdsRef.current = runningGenerationIds\n const revalidateRef = useRef(revalidator.revalidate)\n revalidateRef.current = revalidator.revalidate\n const runningKey = runningGenerationIds.join(',')\n\n useEffect(() => {\n if (!workspaceId || !runningKey) return\n let cancelled = false\n const poll = async () => {\n const responses = await Promise.all(runningIdsRef.current.map(async (id) => {\n const res = await fetch(`${generationsEndpoint}?workspaceId=${encodeURIComponent(workspaceId)}&id=${encodeURIComponent(id)}`)\n if (!res.ok) return null\n const data = await res.json() as { generation?: Generation }\n return data.generation ?? null\n }))\n if (cancelled) return\n const refreshed = responses.filter((gen): gen is Generation => Boolean(gen))\n if (refreshed.length > 0) {\n setLiveGenerations((current) => refreshed.reduce(mergeLiveGeneration, current))\n }\n if (refreshed.some((gen) => generationStatus(gen) !== 'running' && generationStatus(gen) !== 'pending')) {\n revalidateRef.current()\n }\n }\n const interval = window.setInterval(() => { void poll() }, 4_000)\n void poll()\n return () => {\n cancelled = true\n window.clearInterval(interval)\n }\n }, [runningKey, workspaceId, generationsEndpoint])\n\n const onGenerated = useCallback((generation: Generation) => {\n setLiveGenerations((current) => mergeLiveGeneration(current, generation))\n if (!isLocalGeneration(generation)) revalidateRef.current()\n }, [])\n\n return { mergedGenerations, latestBatch, onGenerated }\n}\n","/**\n * The studio composer: one chat-shaped card — prompt, media-type switch, and a\n * band of option pills — that replaces the stacked field panel.\n *\n * The whole point of the rework is that the CONTROLS ARE THE MODEL'S OWN. A pill\n * renders only when the selected model publishes that parameter, its choices are\n * the published enum (or the published range), and the value goes onto the wire\n * exactly as published — seedance takes the string `'5'`, veo `'8s'`, kling the\n * number `5`, and coercing any of them is a 400 from the provider. A model that\n * publishes nothing (`ltx-video`) shows the model pill and nothing else: the\n * honest reading of \"we do not know what this takes\" is an absent control, not\n * an invented one and not a disabled one.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { AudioLines, ArrowUp, Image as ImageIcon, Video } from 'lucide-react'\nimport {\n type Generation,\n type MediaModelCatalogResponse,\n type MediaModelOption,\n type ModelOptionMetadata,\n type ModelOptionValue,\n type ModelOptionsMetadata,\n buildGenerationRequestBody,\n curateComposerModels,\n failedOptimisticGeneration,\n imageToVideoSibling,\n modelMessage,\n normalizeImageCount,\n optimisticGeneration,\n optionChoices,\n preferredModelId,\n reconcileOptionValues,\n resolveComposerOptions,\n supportsCustomImageSize,\n textToVideoSibling,\n userSafeGenerationMessage,\n} from '../studio'\nimport {\n AudioTogglePill,\n ComposerBand,\n CustomSizeForm,\n MediaTypeSegments,\n ModelPill,\n OptionPill,\n ReferencePill,\n type MediaTypeSegment,\n type OptionChoice,\n optionValueLabel,\n} from './composer-option-controls'\n\n/** The lanes the composer offers. Avatar and transcription stay disabled\n * (#451); \"Audio\" is the word for the `speech` lane everywhere on screen. */\nexport type ComposerType = 'image' | 'video' | 'speech'\n\nconst SEGMENTS: readonly MediaTypeSegment<ComposerType>[] = [\n { type: 'image', label: 'Image', icon: ImageIcon },\n { type: 'video', label: 'Video', icon: Video },\n { type: 'speech', label: 'Audio', icon: AudioLines },\n]\n\n/** Pill order per lane. A parameter absent from the model's metadata, or marked\n * `supported: false`, drops out of the row entirely. */\nconst PILL_ORDER: Record<ComposerType, readonly string[]> = {\n image: ['size', 'quality', 'n'],\n video: ['duration', 'resolution', 'aspect_ratio', 'mode'],\n speech: ['voice', 'speed'],\n}\n\nconst PARAM_LABELS: Record<string, string> = {\n size: 'Size',\n quality: 'Quality',\n n: 'Count',\n duration: 'Duration',\n resolution: 'Resolution',\n aspect_ratio: 'Aspect',\n mode: 'Mode',\n voice: 'Voice',\n speed: 'Speed',\n}\n\n/**\n * Speed is published as a continuous range (0.25–4.0 on OpenAI's endpoint), and\n * a range is not a menu: enumerating it at the step a user would actually pick\n * is 376 rows. These are the presets, filtered against whatever range the model\n * publishes, so a narrower range can never offer a speed it rejects.\n */\nconst SPEED_PRESETS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 4] as const\n\nfunction choicesFor(param: string, meta: ModelOptionMetadata): readonly ModelOptionValue[] {\n const { min, max } = meta\n if (param === 'speed' && !meta.values && min != null && max != null) {\n return SPEED_PRESETS.filter((speed) => speed >= min && speed <= max)\n }\n return optionChoices(meta)\n}\n\n/** The parameters a lane renders, in order, skipping unknown and unsupported\n * ones. `audio` and the reference image are not in here — neither is a value\n * menu. */\nfunction visibleParams(type: ComposerType, options: ModelOptionsMetadata | undefined): string[] {\n if (!options) return []\n return PILL_ORDER[type].filter((param) => {\n const meta = options[param]\n return Boolean(meta) && meta?.supported !== false\n })\n}\n\n/**\n * The lane's model. The catalog's own default wins when it survives curation;\n * otherwise the first curated model does — a default that #449 curation removed\n * (a Sora model, a non-gpt-image-2 image model) must not leave the lane with a\n * selection its own menu does not offer.\n */\nfunction defaultModelId(\n type: ComposerType,\n catalog: MediaModelCatalogResponse | null,\n curated: readonly MediaModelOption[],\n): string {\n const preferred = preferredModelId(type, catalog)\n if (preferred && curated.some((model) => model.id === preferred)) return preferred\n return curated.find((model) => model.status !== 'unavailable')?.id ?? curated[0]?.id ?? ''\n}\n\nexport interface StudioComposerProps {\n workspaceId?: string\n onGenerated: (generation: Generation) => void\n /** `home` (under a page heading) vs `docked` (pinned under a generation).\n * Presentational only — the card itself is identical either way. */\n variant?: 'home' | 'docked'\n /** Pick or upload a reference image, resolving to a URL or `null` on cancel.\n * Without it the Reference pill takes a URL instead, which is the most a host\n * with no upload endpoint can honestly offer. */\n pickReferenceImage?: () => Promise<string | null>\n className?: string\n}\n\n/**\n * The composer card. The host owns its width (the card fills its container) and\n * the heading above it; this owns the prompt, the controls, and the POST.\n */\nexport function StudioComposer({\n workspaceId,\n onGenerated,\n variant = 'home',\n pickReferenceImage,\n className,\n}: StudioComposerProps) {\n const [type, setType] = useState<ComposerType>('image')\n const [prompt, setPrompt] = useState('')\n const [catalog, setCatalog] = useState<MediaModelCatalogResponse | null>(null)\n const [catalogLoading, setCatalogLoading] = useState(false)\n const [catalogError, setCatalogError] = useState<string | null>(null)\n const [selectedModels, setSelectedModels] = useState<Partial<Record<ComposerType, string>>>({})\n const [optionValues, setOptionValues] = useState<Record<ComposerType, Record<string, ModelOptionValue>>>({\n image: {},\n video: {},\n speech: {},\n })\n const [referenceImageUrl, setReferenceImageUrl] = useState<string | null>(null)\n const [isSubmitting, setIsSubmitting] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const submitLockRef = useRef(false)\n const bandRef = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n if (!workspaceId) return\n let cancelled = false\n setCatalogLoading(true)\n setCatalogError(null)\n\n fetch(`/api/media-models?workspaceId=${encodeURIComponent(workspaceId)}`)\n .then(async (res) => {\n const data = await res.json() as MediaModelCatalogResponse\n if (!res.ok) throw new Error(data.error ?? 'Could not load media models')\n return data\n })\n .then((data) => {\n if (cancelled) return\n setCatalog(data)\n })\n .catch(() => {\n if (cancelled) return\n setCatalog(null)\n // Never surface the raw fetch/parse error — a non-JSON 404 page reads\n // as `Unexpected token 'N', \"Not Found\" is not valid JSON` to users.\n setCatalogError('Could not load media models')\n })\n .finally(() => {\n if (!cancelled) setCatalogLoading(false)\n })\n\n return () => {\n cancelled = true\n }\n }, [workspaceId])\n\n const laneModels = useMemo(() => catalog?.models[type] ?? [], [catalog, type])\n const curatedModels = useMemo(() => curateComposerModels(type, laneModels), [laneModels, type])\n // A retained pick survives only while the CURRENT catalog can honour it: with\n // no catalog yet keep it; an intentionally-uncurated i2v sibling stays (it is\n // reached by attaching a reference, never listed); anything else must be\n // present and available in the curated list, or the lane falls back to its\n // default. The deleted ComposerHero defended this via selectedModelsWithDefaults;\n // that helper resolves over the FULL catalog and would resurrect\n // curation-removed defaults, so the guard is re-derived here over curatedModels.\n const retained = selectedModels[type]\n const retainedUsable = retained !== undefined && (\n !catalog\n || Boolean(textToVideoSibling(retained))\n || curatedModels.some((model) => model.id === retained && model.status !== 'unavailable')\n )\n const modelId = retainedUsable ? retained : defaultModelId(type, catalog, curatedModels)\n // The image-to-video sibling is deliberately absent from the curated menu (it\n // is reached by attaching a reference image), so the row backing the pill is\n // looked up in the FULL lane list and may still be missing — a sibling the\n // catalog does not carry is sent by id and displayed as its id.\n const modelOption = laneModels.find((model) => model.id === modelId)\n\n const options = useMemo(\n () => (modelId\n ? resolveComposerOptions({\n type,\n modelId,\n provider: modelOption?.provider,\n catalogOptions: modelOption?.options,\n })\n : undefined),\n [modelId, modelOption, type],\n )\n\n // Re-reconcile whenever the resolved options change: a selection the new model\n // still accepts survives, an illegal one resets to that model's default, and a\n // parameter it does not publish is dropped rather than carried invisibly.\n useEffect(() => {\n setOptionValues((current) => {\n const next = reconcileOptionValues(options, current[type], {\n allowCustomSize: supportsCustomImageSize(modelId),\n })\n const unchanged = Object.keys(next).length === Object.keys(current[type]).length\n && Object.entries(next).every(([key, value]) => current[type][key] === value)\n return unchanged ? current : { ...current, [type]: next }\n })\n }, [modelId, options, type])\n\n const values = optionValues[type]\n const params = visibleParams(type, options)\n const audioMeta = type === 'video' ? options?.audio : undefined\n const audioSupported = Boolean(audioMeta) && audioMeta?.supported !== false\n // A model the catalog does not list is normally \"not ready\" — except for a\n // verified image-to-video sibling, which the composer selected itself by\n // attaching a reference image and must be able to send even when the catalog\n // never listed it.\n const unlistedSibling = !modelOption && Boolean(textToVideoSibling(modelId))\n const referenceSupported = type === 'video'\n && Boolean(imageToVideoSibling(modelId) ?? textToVideoSibling(modelId))\n\n const modelReady = (Boolean(modelOption) || unlistedSibling)\n && modelOption?.status !== 'unavailable'\n && !catalogLoading\n && !catalogError\n const canSubmit = Boolean(workspaceId) && modelReady && Boolean(prompt.trim()) && !isSubmitting\n\n function selectModel(id: string) {\n setSelectedModels((current) => ({ ...current, [type]: id }))\n }\n\n /** The model MENU's handler. While a reference is attached the selection must\n * keep the attachment ⇒ image-to-video invariant: a reference-capable pick is\n * re-mapped to its i2v sibling, and a pick with no sibling drops the\n * attachment explicitly rather than hiding it to reappear later. */\n function chooseModel(id: string) {\n if (!referenceImageUrl || type !== 'video') return selectModel(id)\n const sibling = imageToVideoSibling(id)\n if (sibling) return selectModel(sibling)\n if (!textToVideoSibling(id)) setReferenceImageUrl(null)\n return selectModel(id)\n }\n\n function setOption(param: string, value: ModelOptionValue) {\n setOptionValues((current) => ({ ...current, [type]: { ...current[type], [param]: value } }))\n }\n\n function attachReference(url: string) {\n setReferenceImageUrl(url)\n const sibling = imageToVideoSibling(modelId)\n if (sibling) selectModel(sibling)\n }\n\n function removeReference() {\n setReferenceImageUrl(null)\n const sibling = textToVideoSibling(modelId)\n if (sibling) selectModel(sibling)\n }\n\n async function generate() {\n if (!workspaceId || submitLockRef.current || isSubmitting) return\n const promptText = prompt.trim()\n if (!promptText) return\n if (!modelReady) {\n if (!catalogLoading) setError('Select an available model')\n return\n }\n submitLockRef.current = true\n setIsSubmitting(true)\n setError(null)\n\n const clientRequestId = crypto.randomUUID()\n const imageCount = type === 'image' ? normalizeImageCount(values.n ?? 1) : 1\n const localGenerations = Array.from({ length: imageCount }, (_, outputIndex) => optimisticGeneration({\n type,\n prompt: promptText,\n model: modelId,\n clientRequestId,\n outputIndex: type === 'image' ? outputIndex : undefined,\n outputCount: type === 'image' ? imageCount : undefined,\n }))\n localGenerations.slice().reverse().forEach(onGenerated)\n setPrompt('')\n\n let receivedServerGeneration = false\n try {\n const body = buildGenerationRequestBody({\n workspaceId,\n clientRequestId,\n type,\n model: modelId,\n prompt: promptText,\n // `duration`, `audio` and `speed` go on the wire in the TYPE the model\n // published them in — a `duration` of `'5'` that arrives as `5` is a\n // different request, and the three providers behind this lane disagree\n // about which is right. An absent value is omitted, never defaulted.\n image: {\n size: asText(values.size),\n quality: asText(values.quality),\n count: imageCount,\n },\n video: {\n duration: asWireScalar(values.duration),\n resolution: asText(values.resolution),\n aspectRatio: asText(values.aspect_ratio),\n referenceImageUrl: referenceSupported && referenceImageUrl ? referenceImageUrl : undefined,\n audio: audioSupported && typeof values.audio === 'boolean' ? values.audio : undefined,\n mode: asText(values.mode),\n },\n speech: {\n voice: asText(values.voice),\n speed: typeof values.speed === 'number' ? values.speed : undefined,\n },\n })\n\n const res = await fetch('/api/generate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n const data = await res.json() as { generation?: Generation; generations?: Generation[]; error?: string }\n const serverGenerations = data.generations?.length ? data.generations : data.generation ? [data.generation] : []\n if (serverGenerations.length > 0) {\n receivedServerGeneration = true\n serverGenerations.slice().reverse().forEach(onGenerated)\n }\n if (!res.ok || serverGenerations.length === 0) throw new Error(data.error ?? 'Generation failed')\n } catch (err) {\n if (!receivedServerGeneration) localGenerations.map(failedOptimisticGeneration).forEach(onGenerated)\n setError(err instanceof Error ? userSafeGenerationMessage(err.message) : 'Generation failed')\n } finally {\n submitLockRef.current = false\n setIsSubmitting(false)\n }\n }\n\n const statusMessage = unlistedSibling\n ? null\n : modelMessage(modelOption, catalogLoading, curatedModels.length)\n const notice = catalogError ?? error ?? statusMessage\n const noticeIsProblem = Boolean(catalogError || error || modelOption?.status === 'unavailable')\n\n return (\n <section\n data-variant={variant}\n className={`rounded-[14px] border border-border bg-card px-2.5 pb-[9px] pt-2 shadow-sm transition focus-within:border-primary focus-within:ring-[3px] focus-within:ring-ring/30 ${className ?? ''}`}\n >\n <textarea\n value={prompt}\n onChange={(event) => setPrompt(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== 'Enter' || event.shiftKey) return\n event.preventDefault()\n void generate()\n }}\n rows={3}\n aria-label=\"Prompt\"\n placeholder=\"Describe what you want to generate…\"\n className=\"block min-h-[66px] w-full resize-none border-0 bg-transparent px-1.5 pb-1 pt-1.5 text-[14.5px] leading-relaxed outline-none placeholder:text-muted-foreground\"\n />\n\n <div className=\"flex items-center gap-2\">\n <MediaTypeSegments value={type} segments={SEGMENTS} onChange={setType} />\n\n <ComposerBand bandRef={bandRef} resetKey={type}>\n <ModelPill\n models={curatedModels}\n value={modelId}\n displayName={modelOption?.name || modelId || 'Select a model'}\n provider={modelOption?.provider}\n onSelect={chooseModel}\n bandRef={bandRef}\n />\n\n {params.map((param) => (\n <div key={`${type}-${param}`} className=\"studio-pill-in flex-none\">\n <OptionPill\n label={PARAM_LABELS[param] ?? param}\n value={values[param]}\n choices={choicesForPill(param, options?.[param], values[param])}\n onSelect={(value) => setOption(param, value)}\n bandRef={bandRef}\n custom={param === 'size' && supportsCustomImageSize(modelId)\n ? {\n label: 'Custom…',\n render: ({ close }) => (\n <CustomSizeForm\n initial={asText(values.size)}\n onApply={(size) => {\n setOption('size', size)\n close()\n }}\n onCancel={close}\n />\n ),\n }\n : undefined}\n />\n </div>\n ))}\n\n {audioSupported && (\n <div key={`${type}-audio`} className=\"studio-pill-in flex-none\">\n <AudioTogglePill\n on={values.audio === true}\n onToggle={(on) => setOption('audio', on)}\n />\n </div>\n )}\n\n {referenceSupported && (\n <div key={`${type}-reference`} className=\"studio-pill-in flex-none\">\n <ReferencePill\n url={referenceImageUrl}\n onAttach={attachReference}\n onRemove={removeReference}\n pick={pickReferenceImage}\n bandRef={bandRef}\n />\n </div>\n )}\n </ComposerBand>\n\n <button\n type=\"button\"\n aria-label=\"Generate\"\n title=\"Generate\"\n disabled={!canSubmit}\n onClick={() => void generate()}\n className=\"studio-send ml-auto inline-flex h-8 w-8 flex-none items-center justify-center rounded-full text-white transition disabled:opacity-40\"\n >\n <ArrowUp className=\"h-4 w-4\" strokeWidth={2} />\n </button>\n </div>\n\n {notice && (\n <p className={`px-1.5 pt-1.5 text-[12px] ${noticeIsProblem ? 'text-destructive' : 'text-muted-foreground'}`}>\n {notice}\n </p>\n )}\n </section>\n )\n}\n\n/** A text-typed field's value, or `undefined` so the field is omitted entirely\n * — `String(undefined)` puts the literal `\"undefined\"` on the wire, which the\n * provider will try to honour. These fields are typed `string` by the\n * `/api/generate` contract, so a numeric published value is rendered as its\n * digits; the type-sensitive fields (`duration`, `audio`, `speed`) never pass\n * through here. */\nfunction asText(value: ModelOptionValue | undefined): string | undefined {\n return typeof value === 'string' ? value : typeof value === 'number' ? String(value) : undefined\n}\n\n/** A duration passes through as published: the string `'5'`, the string `'8s'`,\n * or the number `5`. Only a boolean (which no lane publishes here) is refused. */\nfunction asWireScalar(value: ModelOptionValue | undefined): string | number | undefined {\n return typeof value === 'string' || typeof value === 'number' ? value : undefined\n}\n\n/** The pill's rows: the published choices, plus the current value when it is a\n * legal off-enum one (a custom gpt-image-2 size), so the pill reports what is\n * actually selected instead of falling back to a dash. */\nfunction choicesForPill(\n param: string,\n meta: ModelOptionMetadata | undefined,\n value: ModelOptionValue | undefined,\n): OptionChoice[] {\n if (!meta) return []\n const choices = choicesFor(param, meta).map((choice) => ({\n value: choice,\n label: optionValueLabel(param, choice),\n }))\n if (value === undefined || choices.some((choice) => choice.value === value)) return choices\n return [...choices, { value, label: `${optionValueLabel(param, value)} · custom` }]\n}\n","/**\n * The studio composer's control row: the media-type segmented group, the\n * horizontally scrolling option band, and the pills that dock in it.\n *\n * Every pill that opens a panel opens it through `usePopover` + `PopoverSurface`\n * (`../web-react/controls`), never an in-place `absolute` panel: the band IS a\n * scroll container (`overflow-x-auto`), and a scroll container clips every\n * positioned descendant whose containing block sits inside it — the measured\n * failure that left the chat composer's own menus painting zero pixels. The\n * surface portals to `document.body` and re-anchors on capture-phase scroll, so\n * scrolling the band moves an open menu with its pill. The one rule the surface\n * cannot supply is when to give up: a menu whose pill has been scrolled out of\n * the band no longer points at anything, so it closes when the pill's\n * horizontal CENTRE leaves the band's box.\n */\n\nimport {\n useCallback,\n useEffect,\n useId,\n useState,\n type ReactNode,\n type RefObject,\n} from 'react'\nimport { ImagePlus, Volume2, VolumeX, X, type LucideIcon } from 'lucide-react'\nimport {\n CheckGlyph,\n ChevronDown,\n OVERLAY_SHADOW,\n POPOVER_OPTION_FOCUS,\n PopoverSurface,\n usePopover,\n} from '../web-react/controls'\nimport { ProviderLogo } from '../web-react/provider-logo'\nimport { type MediaModelOption, type ModelOptionValue, validateCustomImageSize } from '../studio'\n\n/** The resting pill: one row height, never wraps, shrink-proof in the band. */\nconst PILL = 'inline-flex h-7 flex-none items-center gap-1.5 whitespace-nowrap rounded-full border border-border bg-card px-2.5 text-[12.5px] font-medium text-foreground transition hover:bg-accent'\nconst MENU_PANEL = `flex min-w-[184px] flex-col overflow-y-auto rounded-xl border border-border bg-popover p-1 text-popover-foreground ${OVERLAY_SHADOW}`\nconst MENU_HEADER = 'px-2.5 pb-1 pt-1.5 text-[10.5px] font-semibold uppercase tracking-[0.08em] text-muted-foreground'\n\nfunction menuRowClass(selected: boolean): string {\n return `flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-[13px] transition ${POPOVER_OPTION_FOCUS} ${\n selected ? 'bg-primary/10 font-medium' : 'hover:bg-accent'\n }`\n}\n\n/** Deepest edge fade, in px. Beyond this the fade stops reading as \"there is\n * more\" and starts reading as a vignette. */\nconst MAX_FADE = 32\n\n/** One media lane's segment: the icon it shows collapsed, the word it shows\n * when active. */\nexport interface MediaTypeSegment<T extends string> {\n type: T\n label: string\n icon: LucideIcon\n}\n\n/**\n * The pinned-left media-type group: a track holding one filled pill (the active\n * lane, icon + word) and icon-only siblings. `aria-pressed` carries the state\n * and `aria-label` carries the word the collapsed items drop.\n */\nexport function MediaTypeSegments<T extends string>({\n value,\n segments,\n onChange,\n}: {\n value: T\n segments: readonly MediaTypeSegment<T>[]\n onChange: (type: T) => void\n}) {\n return (\n <div\n role=\"group\"\n aria-label=\"Media type\"\n className=\"flex flex-none items-center gap-0.5 rounded-full border border-border bg-muted p-[3px]\"\n >\n {segments.map(({ type, label, icon: Icon }) => {\n const active = type === value\n return (\n <button\n key={type}\n type=\"button\"\n aria-pressed={active}\n aria-label={label}\n title={label}\n onClick={() => onChange(type)}\n className={`inline-flex h-7 items-center gap-1.5 whitespace-nowrap rounded-full text-[12.5px] transition ${\n active\n ? 'bg-card px-2.5 font-medium text-foreground shadow-sm'\n : 'px-2 text-muted-foreground hover:text-foreground'\n }`}\n >\n <Icon className=\"h-4 w-4 shrink-0\" strokeWidth={1.5} />\n {active && <span>{label}</span>}\n </button>\n )\n })}\n </div>\n )\n}\n\n/**\n * The middle scroller. It hides its scrollbar and fades whichever edge has\n * content behind it, at the DEPTH of what is actually hidden (capped at\n * {@link MAX_FADE}) rather than a fixed gradient that claims more is off-screen\n * than there is. A media-type switch resets the scroll to the start, because\n * the control set itself changed; every other re-render leaves `scrollLeft`\n * alone, so picking a value cannot jump the band back to the beginning.\n */\nexport function ComposerBand({\n bandRef,\n resetKey,\n children,\n}: {\n bandRef: RefObject<HTMLDivElement | null>\n /** Changing this scrolls the band back to the start. */\n resetKey: string\n children: ReactNode\n}) {\n const sync = useCallback(() => {\n const band = bandRef.current\n if (!band) return\n const hiddenStart = band.scrollLeft\n const hiddenEnd = Math.max(0, band.scrollWidth - band.clientWidth - band.scrollLeft)\n const atStart = hiddenStart > 1\n const atEnd = hiddenEnd > 1\n band.dataset.overflow = atStart && atEnd ? 'both' : atStart ? 'start' : atEnd ? 'end' : 'none'\n band.style.setProperty('--fade-start', `${atStart ? Math.min(MAX_FADE, hiddenStart) : 0}px`)\n band.style.setProperty('--fade-end', `${atEnd ? Math.min(MAX_FADE, hiddenEnd) : 0}px`)\n }, [bandRef])\n\n // After every render: the pill set changes with the model and the lane, so\n // the hidden width this reads is only correct once the new pills are laid out.\n useEffect(sync)\n\n useEffect(() => {\n const band = bandRef.current\n window.addEventListener('resize', sync)\n // jsdom and older browsers have no ResizeObserver; the window listener still\n // covers the case that matters (the viewport getting narrower).\n const observer = typeof ResizeObserver === 'undefined' || !band ? null : new ResizeObserver(sync)\n if (band) observer?.observe(band)\n return () => {\n window.removeEventListener('resize', sync)\n observer?.disconnect()\n }\n }, [bandRef, sync])\n\n useEffect(() => {\n const band = bandRef.current\n if (band) band.scrollLeft = 0\n sync()\n }, [bandRef, resetKey, sync])\n\n return (\n <div\n ref={bandRef}\n data-overflow=\"none\"\n onScroll={sync}\n className=\"studio-band flex min-w-0 flex-1 flex-nowrap items-center gap-2 overflow-x-auto\"\n >\n {children}\n </div>\n )\n}\n\n/**\n * Close an open menu once its pill has been scrolled out of the band.\n *\n * `PopoverSurface` re-anchors on scroll, so a menu follows its pill for free —\n * which is right until the pill leaves the band entirely and the menu is left\n * pointing at a clipped edge. The centre is the test rather than either edge: a\n * pill half-way out is still the pill the user opened.\n */\nfunction useCloseWhenScrolledOut(\n open: boolean,\n setOpen: (open: boolean) => void,\n triggerRef: RefObject<HTMLElement | null>,\n bandRef: RefObject<HTMLDivElement | null>,\n) {\n useEffect(() => {\n const band = bandRef.current\n if (!open || !band) return\n const onScroll = () => {\n const trigger = triggerRef.current\n if (!trigger) return\n const anchor = trigger.getBoundingClientRect()\n const box = band.getBoundingClientRect()\n const centre = anchor.left + anchor.width / 2\n if (centre < box.left || centre > box.right) setOpen(false)\n }\n band.addEventListener('scroll', onScroll)\n return () => band.removeEventListener('scroll', onScroll)\n }, [bandRef, open, setOpen, triggerRef])\n}\n\n/** One renderable value: the wire value, untouched, and how it reads. */\nexport interface OptionChoice {\n value: ModelOptionValue\n label: string\n}\n\n/**\n * A value pill and the menu it opens.\n *\n * The pill shows the VALUE and carries the parameter name in `title` and in its\n * accessible name — a band of eight labelled pills does not fit a chat card,\n * and \"5s\" alone tells a screen-reader user nothing about which parameter it\n * belongs to.\n */\nexport function OptionPill({\n label,\n value,\n choices,\n onSelect,\n bandRef,\n custom,\n}: {\n label: string\n value: ModelOptionValue | undefined\n choices: readonly OptionChoice[]\n onSelect: (value: ModelOptionValue) => void\n bandRef: RefObject<HTMLDivElement | null>\n /** A trailing row that swaps the menu to a form (the gpt-image-2 custom\n * size). Absent for every parameter whose values are an enum. */\n custom?: {\n label: string\n render: (args: { close: () => void }) => ReactNode\n }\n}) {\n const [open, setOpen] = useState(false)\n const [customOpen, setCustomOpen] = useState(false)\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const panelId = useId()\n useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef)\n\n const selected = choices.find((choice) => choice.value === value)\n const close = () => {\n setOpen(false)\n setCustomOpen(false)\n }\n\n return (\n <div ref={containerRef} className=\"relative inline-flex flex-none\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-controls={open ? panelId : undefined}\n title={label}\n aria-label={`${label}: ${selected?.label ?? 'not set'}`}\n onClick={() => {\n setCustomOpen(false)\n setOpen(!open)\n }}\n className={PILL}\n >\n <span>{selected?.label ?? '—'}</span>\n <ChevronDown className=\"h-3 w-3 shrink-0 text-muted-foreground\" />\n </button>\n\n <PopoverSurface\n open={open}\n id={panelId}\n role={customOpen ? undefined : 'menu'}\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={MENU_PANEL}\n >\n <div className={MENU_HEADER}>{label}</div>\n {customOpen && custom\n ? custom.render({ close })\n : (\n <>\n {choices.map((choice) => (\n <button\n key={String(choice.value)}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={choice.value === value}\n onClick={() => {\n onSelect(choice.value)\n close()\n }}\n className={menuRowClass(choice.value === value)}\n >\n <span className=\"truncate\">{choice.label}</span>\n {choice.value === value && (\n <CheckGlyph className=\"ml-auto h-3.5 w-3.5 shrink-0 text-primary\" />\n )}\n </button>\n ))}\n {custom && (\n <button\n type=\"button\"\n onClick={() => setCustomOpen(true)}\n className={`${menuRowClass(false)} text-muted-foreground`}\n >\n {custom.label}\n </button>\n )}\n </>\n )}\n </PopoverSurface>\n </div>\n )\n}\n\n/**\n * The custom-size form the Size menu swaps to.\n *\n * The reason a size is refused renders next to the fields that are wrong — a\n * toast would be gone by the time the user looks back at the inputs — and Apply\n * refuses rather than disables, so the reason is always reachable.\n */\nexport function CustomSizeForm({\n initial,\n onApply,\n onCancel,\n}: {\n initial?: string\n onApply: (size: string) => void\n onCancel: () => void\n}) {\n const parsed = /^(\\d+)x(\\d+)$/.exec(initial ?? '')\n const [width, setWidth] = useState(parsed?.[1] ?? '')\n const [height, setHeight] = useState(parsed?.[2] ?? '')\n const [error, setError] = useState<string | null>(null)\n const fieldClass = 'h-8 w-[74px] rounded-md border border-input bg-background px-2 text-[13px] tabular-nums'\n\n function apply() {\n const verdict = validateCustomImageSize(Number(width), Number(height))\n if (!verdict.ok) {\n setError(verdict.reason)\n return\n }\n onApply(`${Number(width)}x${Number(height)}`)\n }\n\n return (\n <div className=\"flex w-[228px] flex-col gap-2 p-1.5\">\n <div className=\"flex items-center gap-2\">\n <input\n type=\"number\"\n value={width}\n aria-label=\"Custom width\"\n onChange={(event) => {\n setWidth(event.target.value)\n setError(null)\n }}\n className={fieldClass}\n />\n <span aria-hidden className=\"text-muted-foreground\">×</span>\n <input\n type=\"number\"\n value={height}\n aria-label=\"Custom height\"\n onChange={(event) => {\n setHeight(event.target.value)\n setError(null)\n }}\n className={fieldClass}\n />\n </div>\n {error\n ? <p className=\"text-[12px] text-destructive\">{error}</p>\n : <p className=\"text-[12px] text-muted-foreground\">Multiples of 16, long edge up to 3840, ratio between 1:3 and 3:1.</p>}\n <div className=\"flex items-center justify-end gap-1.5\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"rounded-md px-2.5 py-1 text-[12.5px] font-medium text-muted-foreground transition hover:bg-accent hover:text-foreground\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={apply}\n className=\"rounded-md bg-primary px-2.5 py-1 text-[12.5px] font-medium text-primary-foreground transition hover:opacity-90\"\n >\n Apply\n </button>\n </div>\n </div>\n )\n}\n\n/**\n * The audio switch. It is a toggle rather than a menu because the parameter has\n * exactly two states, and a two-row menu to say \"on\" is a menu that costs a\n * click for nothing.\n */\nexport function AudioTogglePill({ on, onToggle }: { on: boolean; onToggle: (on: boolean) => void }) {\n return (\n <button\n type=\"button\"\n aria-pressed={on}\n title=\"Audio\"\n onClick={() => onToggle(!on)}\n className={`${PILL} ${on ? '' : 'text-muted-foreground'}`}\n >\n {on\n ? <Volume2 className=\"h-4 w-4 shrink-0\" strokeWidth={1.5} />\n : <VolumeX className=\"h-4 w-4 shrink-0\" strokeWidth={1.5} />}\n <span>{on ? 'Audio on' : 'Audio off'}</span>\n </button>\n )\n}\n\n/**\n * The reference-image pill: the only control that changes which MODEL runs, so\n * the swap to the image-to-video sibling is the caller's (`onAttach`/`onRemove`)\n * and the pill only reports what is attached.\n *\n * With no `pick` seam it opens a URL form instead of a file dialog — a URL is\n * something a host with no upload endpoint can still supply, and refusing\n * anything that is not `http(s)` keeps a pasted local path from reaching the\n * provider as an unfetchable reference.\n */\nexport function ReferencePill({\n url,\n onAttach,\n onRemove,\n pick,\n bandRef,\n}: {\n url: string | null\n onAttach: (url: string) => void\n onRemove: () => void\n pick?: () => Promise<string | null>\n bandRef: RefObject<HTMLDivElement | null>\n}) {\n const [open, setOpen] = useState(false)\n const [draft, setDraft] = useState('')\n const [error, setError] = useState<string | null>(null)\n const { containerRef, triggerRef, panelRef } = usePopover(open, setOpen)\n const panelId = useId()\n useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef)\n\n if (url) {\n return (\n <div className={`${PILL} border-primary bg-primary/10 pr-1.5 text-primary hover:bg-primary/10`}>\n <img src={url} alt=\"\" className=\"h-[18px] w-[18px] shrink-0 rounded-[5px] object-cover\" />\n <span>Reference</span>\n <button\n type=\"button\"\n aria-label=\"Remove reference image\"\n onClick={onRemove}\n className=\"ml-0.5 inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full transition hover:bg-primary/20\"\n >\n <X className=\"h-3 w-3\" strokeWidth={2} />\n </button>\n </div>\n )\n }\n\n function attach() {\n const value = draft.trim()\n if (!/^https?:\\/\\//i.test(value)) {\n setError('Enter a URL starting with http:// or https://')\n return\n }\n onAttach(value)\n setDraft('')\n setError(null)\n setOpen(false)\n }\n\n return (\n <div ref={containerRef} className=\"relative inline-flex flex-none\">\n <button\n type=\"button\"\n ref={triggerRef}\n // With a `pick` seam the button opens the host's own picker, not a\n // popover — claiming `aria-haspopup` there announces a menu that never\n // appears.\n {...(pick ? {} : { 'aria-haspopup': true as const, 'aria-expanded': open, 'aria-controls': open ? panelId : undefined })}\n title=\"Reference image\"\n onClick={() => {\n if (!pick) {\n setOpen(!open)\n return\n }\n void pick().then((picked) => {\n if (picked) onAttach(picked)\n })\n }}\n className={PILL}\n >\n <ImagePlus className=\"h-4 w-4 shrink-0 text-muted-foreground\" strokeWidth={1.5} />\n <span>Reference image</span>\n </button>\n\n {!pick && (\n <PopoverSurface\n open={open}\n id={panelId}\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={MENU_PANEL}\n >\n <div className={MENU_HEADER}>Reference image</div>\n <div className=\"flex w-[260px] flex-col gap-2 p-1.5\">\n <input\n type=\"url\"\n value={draft}\n aria-label=\"Reference image URL\"\n placeholder=\"https://…\"\n onChange={(event) => {\n setDraft(event.target.value)\n setError(null)\n }}\n className=\"h-8 w-full rounded-md border border-input bg-background px-2 text-[13px]\"\n />\n {error && <p className=\"text-[12px] text-destructive\">{error}</p>}\n <div className=\"flex items-center justify-end gap-1.5\">\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n className=\"rounded-md px-2.5 py-1 text-[12.5px] font-medium text-muted-foreground transition hover:bg-accent hover:text-foreground\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={attach}\n className=\"rounded-md bg-primary px-2.5 py-1 text-[12.5px] font-medium text-primary-foreground transition hover:opacity-90\"\n >\n Attach\n </button>\n </div>\n </div>\n </PopoverSurface>\n )}\n </div>\n )\n}\n\n/**\n * The model pill — first in the band on every lane, and the only pill that is\n * always there. A model with no published option metadata renders this and\n * nothing else, which is the honest reading of \"we do not know what this model\n * takes\".\n */\nexport function ModelPill({\n models,\n value,\n displayName,\n provider,\n onSelect,\n bandRef,\n}: {\n models: readonly MediaModelOption[]\n value: string\n /** What the pill reads. Falls back to the id — including for a model the\n * catalog does not list, such as an image-to-video sibling. */\n displayName: string\n provider?: string\n onSelect: (id: string) => void\n bandRef: RefObject<HTMLDivElement | null>\n}) {\n const [open, setOpen] = useState(false)\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const panelId = useId()\n useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef)\n\n return (\n <div ref={containerRef} className=\"relative inline-flex flex-none\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-controls={open ? panelId : undefined}\n title=\"Model\"\n aria-label={`Model: ${displayName}`}\n onClick={() => setOpen(!open)}\n className={PILL}\n >\n {provider && <ProviderLogo provider={provider} size={14} />}\n <span className=\"max-w-[168px] truncate\">{displayName}</span>\n <ChevronDown className=\"h-3 w-3 shrink-0 text-muted-foreground\" />\n </button>\n\n <PopoverSurface\n open={open}\n id={panelId}\n role=\"menu\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={`${MENU_PANEL} max-w-[320px]`}\n >\n <div className={MENU_HEADER}>Model</div>\n {models.length === 0 && (\n <p className=\"px-2.5 py-2 text-[13px] text-muted-foreground\">No models are available for this media type.</p>\n )}\n {models.map((model) => (\n <button\n key={model.id}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={model.id === value}\n disabled={model.status === 'unavailable'}\n onClick={() => {\n onSelect(model.id)\n setOpen(false)\n }}\n className={`${menuRowClass(model.id === value)} disabled:opacity-50`}\n >\n {model.provider && <ProviderLogo provider={model.provider} size={14} />}\n <span className=\"truncate\">{model.name || model.id}</span>\n {model.status !== 'available' && (\n <span className=\"ml-auto shrink-0 text-[11px] capitalize text-muted-foreground\">{model.status}</span>\n )}\n {model.id === value && (\n <CheckGlyph className={`${model.status === 'available' ? 'ml-auto ' : ''}h-3.5 w-3.5 shrink-0 text-primary`} />\n )}\n </button>\n ))}\n </PopoverSurface>\n </div>\n )\n}\n\n/**\n * How a wire value reads, with the wire value itself untouched underneath.\n *\n * Keyed on the PARAMETER because the same scalar means different things across\n * them: `5` is \"5s\" of video and `2` is \"×2\" images. Anything this table has no\n * rule for reads as itself with a capital letter — never as an invented word,\n * so `std` renders \"Std\" and not a \"Standard\" nobody published.\n */\nexport function optionValueLabel(param: string, value: ModelOptionValue): string {\n if (typeof value === 'boolean') return value ? 'On' : 'Off'\n const text = String(value)\n if (param === 'n') return `×${text}`\n if (param === 'speed') return `${text}×`\n if (text === 'auto') return 'Auto'\n if (param === 'duration') return /^\\d+(\\.\\d+)?$/.test(text) ? `${text}s` : text\n if (param === 'size') {\n const size = /^(\\d+)x(\\d+)$/.exec(text)\n return size ? `${size[1]}×${size[2]}` : text\n }\n if (param === 'resolution' || param === 'aspect_ratio') return text\n return text.charAt(0).toUpperCase() + text.slice(1)\n}\n","import { Button } from '@tangle-network/sandbox-ui/primitives'\nimport { Images } from 'lucide-react'\n\nexport function StudioHeader({\n count,\n onOpenLibrary,\n canGenerate,\n}: {\n count: number\n onOpenLibrary: () => void\n canGenerate: boolean\n}) {\n return (\n <header className=\"sticky top-0 z-20 border-b border-border bg-card\">\n <div className=\"mx-auto flex w-full max-w-3xl items-center justify-between gap-3 px-4 py-3 sm:px-6\">\n <div className=\"min-w-0\">\n <h1 className=\"text-lg font-semibold tracking-tight text-foreground\">Studio</h1>\n <p className=\"truncate text-xs text-muted-foreground\">\n {canGenerate\n ? 'Generate images, video, voice, avatars, and transcripts.'\n : \"Browse your team's generations.\"}\n </p>\n </div>\n <Button size=\"sm\" variant=\"outline\" onClick={onOpenLibrary} className=\"shrink-0\">\n <Images className=\"mr-1.5 h-4 w-4\" />\n Library\n {count > 0 && (\n <span className=\"ml-2 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary/15 px-1.5 text-xs font-semibold text-primary\">\n {count}\n </span>\n )}\n </Button>\n </div>\n </header>\n )\n}\n","import { Badge } from '@tangle-network/sandbox-ui/primitives'\nimport { ArrowRight, Sparkles } from 'lucide-react'\nimport {\n type Generation,\n generationError,\n generationStatus,\n} from '../studio'\nimport { typeConfigFor } from './type-config'\n\nexport function ResultCanvas({\n batch,\n onOpenLibrary,\n onSelect,\n}: {\n batch: Generation[]\n onOpenLibrary: () => void\n onSelect: (generation: Generation) => void\n}) {\n const cardClass = 'rounded-xl border border-border bg-card shadow-sm'\n\n if (batch.length === 0) {\n return (\n <section className={`${cardClass} flex min-h-[180px] flex-col items-center justify-center gap-2 p-8 text-center`}>\n <div className=\"flex h-12 w-12 items-center justify-center rounded-full bg-muted\">\n <Sparkles className=\"h-6 w-6 text-muted-foreground/40\" />\n </div>\n <p className=\"text-sm font-medium text-foreground\">Your creations appear here</p>\n <p className=\"max-w-xs text-xs text-muted-foreground\">\n Generate above — results appear here and are saved to your library.\n </p>\n </section>\n )\n }\n\n const cfg = typeConfigFor(batch[0]?.type ?? 'image')\n const Icon = cfg.icon\n const statuses = batch.map(generationStatus)\n const runLabel = statuses.some((s) => s === 'pending' || s === 'running')\n ? 'Generating…'\n : statuses.every((s) => s === 'failed')\n ? 'Last run failed'\n : 'Latest creation'\n const isWorking = statuses.some((s) => s === 'pending' || s === 'running')\n\n return (\n <section className={`${cardClass} p-5`}>\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"outline\" className={`${cfg.color} gap-1 text-xs`}>\n <Icon className=\"h-3 w-3\" />\n {cfg.label}\n </Badge>\n <span className=\"text-sm font-medium text-foreground\">{runLabel}</span>\n {isWorking && <span className=\"h-2 w-2 animate-pulse rounded-full bg-primary\" />}\n </div>\n <button\n type=\"button\"\n onClick={onOpenLibrary}\n className=\"flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:text-primary/80\"\n >\n View in Library\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n\n <div className={`grid gap-3 ${batch.length === 1 ? 'grid-cols-1' : 'grid-cols-2'}`}>\n {batch.map((generation) => (\n <button\n key={generation.id}\n type=\"button\"\n onClick={() => onSelect(generation)}\n className=\"studio-bloom group relative aspect-video overflow-hidden rounded-lg border border-border bg-secondary transition-colors hover:border-primary/40\"\n >\n <CanvasTile generation={generation} />\n </button>\n ))}\n </div>\n\n {batch.length > 1 && !isWorking && (\n <p className=\"mt-2 text-xs text-muted-foreground\">{batch.length} results from this run</p>\n )}\n </section>\n )\n}\n\nfunction CanvasTile({ generation }: { generation: Generation }) {\n const cfg = typeConfigFor(generation.type)\n const Icon = cfg.icon\n const status = generationStatus(generation)\n\n if (status === 'pending' || status === 'running') {\n return <div className=\"studio-shimmer absolute inset-0 h-full w-full\" />\n }\n if (status === 'failed') {\n return (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center gap-2 p-4 text-center\">\n <Icon className=\"h-6 w-6 text-destructive/50\" />\n <p className=\"line-clamp-3 text-xs text-destructive\">{generationError(generation) ?? 'Generation failed'}</p>\n </div>\n )\n }\n if (generation.type === 'image' && generation.result) {\n return <img src={generation.result} alt={generation.prompt} className=\"absolute inset-0 h-full w-full object-contain\" />\n }\n if ((generation.type === 'video' || generation.type === 'avatar') && generation.result) {\n return <video src={generation.result} controls className=\"absolute inset-0 h-full w-full object-contain\" />\n }\n if (generation.type === 'speech' && generation.result) {\n return (\n <div className=\"absolute inset-0 flex items-center justify-center p-4\">\n <audio src={generation.result} controls className=\"w-full\" />\n </div>\n )\n }\n if (generation.type === 'transcription' && generation.result) {\n return (\n <div className=\"absolute inset-0 overflow-y-auto p-4\">\n <p className=\"whitespace-pre-wrap text-xs text-foreground\">{generation.result}</p>\n </div>\n )\n }\n return (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <Icon className=\"h-8 w-8 text-muted-foreground/20\" />\n </div>\n )\n}\n","import { FileText, Image, Mic, User, Video } from 'lucide-react'\n\n/** Define configuration options for a type including label, icon, and color properties */\nexport interface TypeConfig {\n label: string\n icon: typeof Image\n color: string\n}\n\n/* Type badges sit on top of media thumbnails, where a per-type hue is\n illegible and off-token — every type gets the same neutral scrim chip\n (no backdrop-blur, so the thumbnail doesn't smear through). The type\n icon inside the badge carries the identity instead of a color. */\nconst MEDIA_SCRIM = 'bg-black/60 text-white border-white/10'\n\nconst IMAGE: TypeConfig = { label: 'Image', icon: Image, color: MEDIA_SCRIM }\n\n// string-keyed so list cards can index by Generation.type\n/** Map type keys to their corresponding configuration objects including labels, icons, and colors */\nexport const TYPE_CONFIG: Record<string, TypeConfig> = {\n image: IMAGE,\n video: { label: 'Video', icon: Video, color: MEDIA_SCRIM },\n avatar: { label: 'Avatar', icon: User, color: MEDIA_SCRIM },\n speech: { label: 'Audio', icon: Mic, color: MEDIA_SCRIM },\n transcription: { label: 'Transcript', icon: FileText, color: MEDIA_SCRIM },\n}\n\n// Safe lookup for an arbitrary `Generation.type` — always defined (the table is\n// declared `Record<string, …>`, so a raw index is `T | undefined`). Falls back\n// to the image config, matching the prior `?? TYPE_CONFIG.image` call sites.\n/** Resolve the configuration object for a given type or return the default image configuration */\nexport function typeConfigFor(type: string): TypeConfig {\n return TYPE_CONFIG[type] ?? IMAGE\n}\n","import { useMemo } from 'react'\nimport { Link } from 'react-router'\nimport {\n Button,\n Tabs, TabsList, TabsTrigger,\n} from '@tangle-network/sandbox-ui/primitives'\nimport { ArrowLeft, DollarSign, FolderOpen, X } from 'lucide-react'\nimport { type Generation } from '../studio'\nimport { TYPE_CONFIG } from './type-config'\nimport { StudioSheet } from './studio-sheet'\nimport { GenerationGrid, filterGenerations } from './generation-grid'\nimport { GenerationDetail } from './generation-detail'\n\nexport function LibraryDrawer({\n open,\n onOpenChange,\n generations,\n totalCost,\n typeFilter,\n onFilterChange,\n vaultHref,\n selected,\n onSelect,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n generations: Generation[]\n totalCost: number\n typeFilter: string | null\n onFilterChange: (type: string | null) => void\n vaultHref?: (filePath?: string | null) => string\n selected: Generation | null\n onSelect: (generation: Generation | null) => void\n}) {\n const visible = useMemo(() => filterGenerations(generations, typeFilter), [generations, typeFilter])\n\n return (\n <StudioSheet open={open} onOpenChange={onOpenChange} title=\"Asset library\">\n <header className=\"flex shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-3\">\n {selected ? (\n <button\n type=\"button\"\n onClick={() => onSelect(null)}\n className=\"flex items-center gap-1.5 text-sm font-medium text-foreground transition-colors hover:text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back to library\n </button>\n ) : (\n <h2 className=\"text-sm font-semibold text-foreground\">Asset library</h2>\n )}\n <button\n type=\"button\"\n aria-label=\"Close library\"\n onClick={() => onOpenChange(false)}\n className=\"rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </header>\n\n {selected ? (\n <div className=\"flex-1 overflow-y-auto p-4\">\n <GenerationDetail\n generation={selected}\n vaultHref={vaultHref}\n onNavigate={() => onOpenChange(false)}\n />\n </div>\n ) : (\n <>\n <div className=\"shrink-0 border-b border-border px-4 pt-3\">\n <div className=\"mb-2 flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground\">\n <span>{visible.length} generation{visible.length !== 1 ? 's' : ''}</span>\n <span className=\"flex items-center gap-0.5\">\n <DollarSign className=\"h-3 w-3\" />\n {totalCost.toFixed(2)} spent\n </span>\n </div>\n {vaultHref && (\n <Link to={vaultHref()} prefetch=\"intent\" onClick={() => onOpenChange(false)}>\n <Button size=\"sm\" variant=\"outline\">\n <FolderOpen className=\"mr-1.5 h-3.5 w-3.5\" />\n Vault\n </Button>\n </Link>\n )}\n </div>\n <Tabs value={typeFilter ?? 'all'} onValueChange={(v) => onFilterChange(v === 'all' ? null : v)}>\n <TabsList className=\"h-9 w-full justify-start gap-1 overflow-x-auto bg-transparent\">\n <TabsTrigger value=\"all\" className=\"text-xs\">All</TabsTrigger>\n {Object.entries(TYPE_CONFIG).map(([key, cfg]) => (\n <TabsTrigger key={key} value={key} className=\"text-xs\">{cfg.label}</TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n </div>\n\n <div className=\"flex-1 overflow-y-auto p-4\">\n <GenerationGrid generations={visible} typeFilter={typeFilter} onSelect={onSelect} />\n </div>\n </>\n )}\n </StudioSheet>\n )\n}\n","import type { ReactNode } from 'react'\nimport * as Dialog from '@radix-ui/react-dialog'\n\n/**\n * Right-side overlay sheet built on Radix Dialog — gives focus-trap, scroll-lock,\n * and Escape-to-close for free. Slide/fade come from the studio-sheet-* classes in\n * ./studio.css (driven by Radix data-state), not tailwindcss-animate.\n */\nexport function StudioSheet({\n open,\n onOpenChange,\n title,\n children,\n}: {\n open: boolean\n onOpenChange: (open: boolean) => void\n title: string\n children: ReactNode\n}) {\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Portal>\n <Dialog.Overlay className=\"studio-sheet-overlay fixed inset-0 z-50 bg-black/50\" />\n <Dialog.Content\n className=\"studio-sheet-content fixed inset-y-0 right-0 z-50 flex w-[min(92vw,30rem)] flex-col border-l border-card-edge bg-popover shadow-[var(--shadow-overlay)] focus:outline-none\"\n aria-describedby={undefined}\n >\n <Dialog.Title className=\"sr-only\">{title}</Dialog.Title>\n {children}\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n )\n}\n","import { EmptyState } from '@tangle-network/sandbox-ui/primitives'\nimport { Film, Sparkles } from 'lucide-react'\nimport { type Generation, isGenerationType } from '../studio'\nimport { TYPE_CONFIG } from './type-config'\nimport { GenerationCard } from './generation-card'\n\n/** The visible set for the active type tab (all generations when unfiltered). */\nexport function filterGenerations(generations: Generation[], typeFilter: string | null): Generation[] {\n if (!typeFilter || !isGenerationType(typeFilter)) return generations\n return generations.filter((generation) => generation.type === typeFilter)\n}\n\n/**\n * Chrome-less asset grid — the `GenerationCard` grid plus its empty state, with\n * no surrounding tabs/stats/sheet chrome. Composed by both the inline\n * `LibraryPanel` and the `LibraryDrawer`'s list view so the grid and its\n * empty-state copy stay in one place. `generations` is the already-filtered\n * visible set; `typeFilter` only tunes the empty-state message.\n */\nexport function GenerationGrid({\n generations,\n typeFilter,\n onSelect,\n}: {\n generations: Generation[]\n typeFilter: string | null\n onSelect: (generation: Generation) => void\n}) {\n if (generations.length === 0) {\n return (\n <EmptyState\n icon={typeFilter\n ? <Film className=\"h-8 w-8 text-muted-foreground/30\" />\n : <Sparkles className=\"h-10 w-10 text-muted-foreground/30\" />}\n title={typeFilter\n ? `No ${TYPE_CONFIG[typeFilter]?.label ?? typeFilter} generations`\n : 'No generations yet'}\n description={typeFilter\n ? 'Try a different filter or change the type in the composer.'\n : 'Everything you and the agent create lives here.'}\n className=\"py-16\"\n />\n )\n }\n return (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {generations.map((generation) => (\n <GenerationCard key={generation.id} generation={generation} onSelect={onSelect} />\n ))}\n </div>\n )\n}\n","import { Card, CardContent, Badge } from '@tangle-network/sandbox-ui/primitives'\nimport {\n type Generation,\n generationError,\n generationStatus,\n relativeTime,\n} from '../studio'\nimport { typeConfigFor } from './type-config'\n\nexport function GenerationCard({\n generation,\n onSelect,\n}: {\n generation: Generation\n onSelect: (generation: Generation) => void\n}) {\n const cfg = typeConfigFor(generation.type)\n const Icon = cfg.icon\n const status = generationStatus(generation)\n return (\n <button type=\"button\" onClick={() => onSelect(generation)} className=\"group text-left animate-row-in\">\n <Card className=\"overflow-hidden transition-all group-hover:border-primary/50 group-hover:shadow-md\">\n <div className=\"relative aspect-video bg-secondary\">\n {generation.type === 'image' && generation.result ? (\n <img src={generation.result} alt={generation.prompt} className=\"h-full w-full object-cover\" />\n ) : (generation.type === 'video' || generation.type === 'avatar') && generation.result ? (\n <video src={generation.result} className=\"h-full w-full object-cover\" muted />\n ) : generation.type === 'speech' && generation.result ? (\n <div className=\"flex h-full items-center justify-center\">\n <Icon className=\"h-6 w-6 text-muted-foreground/40\" />\n </div>\n ) : generation.type === 'transcription' && generation.result ? (\n <div className=\"flex h-full items-center justify-center p-4\">\n <p className=\"line-clamp-5 text-xs text-muted-foreground\">{generation.result}</p>\n </div>\n ) : status === 'pending' || status === 'running' ? (\n <div className=\"shimmer h-full w-full\" />\n ) : (\n <div className=\"flex h-full items-center justify-center\">\n <Icon className=\"h-8 w-8 text-muted-foreground/20\" />\n </div>\n )}\n <div className=\"absolute left-2 top-2\">\n <Badge variant=\"outline\" className={`${cfg.color} gap-1 text-xs`}>\n <Icon className=\"h-3 w-3\" />\n {cfg.label}\n </Badge>\n </div>\n <GenerationStatusBadge generation={generation} />\n </div>\n <CardContent className=\"p-3\">\n <p className=\"mb-2 line-clamp-2 text-sm font-medium text-foreground\">{generation.prompt}</p>\n {status === 'failed' && (\n <p className=\"mb-2 line-clamp-2 text-xs text-destructive\">{generationError(generation)}</p>\n )}\n <div className=\"flex items-center justify-between\">\n {generation.model && (\n <span className=\"truncate text-xs text-muted-foreground\">{generation.model}</span>\n )}\n <div className=\"flex shrink-0 items-center gap-2\">\n {generation.cost != null && (\n <span className=\"text-xs text-muted-foreground\">${generation.cost.toFixed(3)}</span>\n )}\n <span className=\"text-xs text-muted-foreground\">{relativeTime(generation.createdAt)}</span>\n </div>\n </div>\n </CardContent>\n </Card>\n </button>\n )\n}\n\nexport function GenerationStatusBadge({\n generation,\n inline = false,\n}: {\n generation: Generation\n inline?: boolean\n}) {\n const status = generationStatus(generation)\n if (status === 'succeeded') return null\n\n const label = status === 'failed' ? 'Failed' : status === 'running' ? 'Running' : 'Pending'\n // Solid tint mixed onto the card surface — the badge floats over media, so an\n // alpha fill would let the thumbnail bleed through.\n const className = status === 'failed'\n ? 'border-destructive/30 bg-[color-mix(in_srgb,hsl(var(--destructive))_10%,hsl(var(--card)))] text-destructive'\n : 'border-warning/30 bg-[color-mix(in_srgb,hsl(var(--warning))_10%,hsl(var(--card)))] text-warning'\n\n return (\n <div className={inline ? '' : 'absolute bottom-2 left-2'}>\n <Badge variant=\"outline\" className={`${className} text-xs`}>\n {label}\n </Badge>\n </div>\n )\n}\n","import { Link } from 'react-router'\nimport { Badge, Button } from '@tangle-network/sandbox-ui/primitives'\nimport { FolderOpen } from 'lucide-react'\nimport {\n type Generation,\n generationError,\n generationVaultPath,\n} from '../studio'\nimport { typeConfigFor } from './type-config'\nimport { GenerationStatusBadge } from './generation-card'\n\nexport function GenerationDetail({\n generation,\n vaultHref,\n onNavigate,\n}: {\n generation: Generation\n vaultHref?: (filePath?: string | null) => string\n onNavigate?: () => void\n}) {\n const cfg = typeConfigFor(generation.type)\n const Icon = cfg.icon\n // Only offer the vault link when the generation actually persisted a file.\n // generationVaultPath is null for failed/pending/running and storage-failed\n // generations; without this guard vaultHref(null) falls back to the vault\n // root, rendering a button that opens nothing relevant.\n const vaultPath = generationVaultPath(generation)\n const href = vaultHref && vaultPath ? vaultHref(vaultPath) : null\n\n return (\n <div className=\"space-y-4\">\n <div className=\"overflow-hidden rounded-lg bg-secondary\">\n {generation.type === 'image' && generation.result ? (\n <img src={generation.result} alt={generation.prompt} className=\"max-h-[420px] w-full object-contain\" />\n ) : (generation.type === 'video' || generation.type === 'avatar') && generation.result ? (\n <video src={generation.result} controls className=\"max-h-[420px] w-full\" />\n ) : generation.type === 'speech' && generation.result ? (\n <div className=\"p-6\">\n <audio src={generation.result} controls className=\"w-full\" />\n </div>\n ) : (\n <div className=\"flex h-32 items-center justify-center\">\n <Icon className=\"h-8 w-8 text-muted-foreground/20\" />\n </div>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className={`${cfg.color} gap-1`}>\n <Icon className=\"h-3 w-3\" />\n {cfg.label}\n </Badge>\n <GenerationStatusBadge generation={generation} inline />\n {generationError(generation) && (\n <span className=\"text-xs text-destructive\">{generationError(generation)}</span>\n )}\n </div>\n\n <div>\n <span className=\"mb-1 block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground\">Prompt</span>\n <p className=\"text-sm text-foreground\">{generation.prompt}</p>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-4\">\n {generation.model && (\n <div>\n <span className=\"block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground\">Model</span>\n <span className=\"text-xs text-foreground\">{generation.model}</span>\n </div>\n )}\n {generation.cost != null && (\n <div>\n <span className=\"block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground\">Cost</span>\n <span className=\"text-xs text-foreground\">${generation.cost.toFixed(3)}</span>\n </div>\n )}\n {generation.createdAt && (\n <div>\n <span className=\"block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground\">Created</span>\n <span className=\"text-xs text-foreground\">\n {new Date(generation.createdAt).toLocaleString('en-US', { dateStyle: 'medium', timeStyle: 'short' })}\n </span>\n </div>\n )}\n </div>\n\n {generation.type === 'transcription' && generation.result && (\n <div>\n <span className=\"mb-1 block text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground\">Transcription</span>\n <pre className=\"max-h-64 overflow-y-auto whitespace-pre-wrap rounded-lg bg-secondary p-4 font-mono text-[13px] tabular-nums text-foreground\">\n {generation.result}\n </pre>\n </div>\n )}\n\n {href && (\n <div className=\"border-t border-border pt-2\">\n <Link to={href} prefetch=\"intent\" onClick={onNavigate}>\n <Button size=\"sm\" variant=\"outline\">\n <FolderOpen className=\"mr-1.5 h-3.5 w-3.5\" />\n Open in Vault\n </Button>\n </Link>\n </div>\n )}\n </div>\n )\n}\n","import { Info } from 'lucide-react'\n\n/**\n * The one thing a chat-shaped generator has to say about itself: each prompt is\n * a fresh generation, and nothing carries over from the last one.\n *\n * A chat card sets the expectation that the thing on the other side remembers\n * the conversation. This one does not, and the cost of learning that by\n * experiment is a wasted generation. So the notice sits above the composer as a\n * statement of fact rather than a warning — `text-foreground`, not muted, since\n * a rule the reader must know before typing cannot be the quietest text on the\n * page.\n *\n * The icon is INLINE in the text flow rather than a flex sibling, so a narrow\n * viewport wraps it with the sentence instead of stranding a lone glyph beside\n * a two-line block.\n */\nexport function GenerationNoticeChip({ className }: { className?: string }) {\n return (\n <p\n className={`inline-block rounded-full border px-3.5 py-1.5 text-center text-[12.5px] font-medium leading-relaxed text-foreground shadow-sm ${className ?? ''}`}\n style={{ background: 'var(--studio-notice-bg)', borderColor: 'var(--studio-notice-border)' }}\n >\n <Info className=\"mr-1.5 inline-block h-[14px] w-[14px] align-[-2px] text-primary\" strokeWidth={1.5} aria-hidden />\n Each prompt starts a new generation — this chat does not remember the last one.\n </p>\n )\n}\n","import { useMemo } from 'react'\nimport {\n Tabs, TabsList, TabsTrigger,\n} from '@tangle-network/sandbox-ui/primitives'\nimport { DollarSign } from 'lucide-react'\nimport { type Generation } from '../studio'\nimport { TYPE_CONFIG } from './type-config'\nimport { GenerationGrid, filterGenerations } from './generation-grid'\n\n/**\n * Inline asset gallery — the same filter + stats + card grid as the library\n * drawer's list view, but with no sheet chrome so it can sit on the page beside\n * the composer. In-flight generations need no special handling: they arrive at\n * the front of `generations` (via `useStudioGenerations.mergedGenerations`) and\n * `GenerationCard` renders their `pending`/`running` shimmer.\n */\nexport function LibraryPanel({\n generations,\n totalCost,\n typeFilter,\n onFilterChange,\n onSelect,\n}: {\n generations: Generation[]\n totalCost: number\n typeFilter: string | null\n onFilterChange: (type: string | null) => void\n onSelect: (generation: Generation) => void\n}) {\n const visible = useMemo(() => filterGenerations(generations, typeFilter), [generations, typeFilter])\n\n return (\n <div className=\"flex min-w-0 flex-col\">\n <div className=\"mb-4 flex flex-wrap items-center justify-between gap-3\">\n <Tabs value={typeFilter ?? 'all'} onValueChange={(v) => onFilterChange(v === 'all' ? null : v)}>\n <TabsList className=\"h-9 justify-start gap-1 overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-background p-1\">\n <TabsTrigger\n value=\"all\"\n className=\"rounded-md px-3 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground data-[state=active]:bg-primary/10 data-[state=active]:font-semibold data-[state=active]:text-primary data-[state=active]:shadow-none\"\n >\n All\n </TabsTrigger>\n {Object.entries(TYPE_CONFIG).map(([key, cfg]) => (\n <TabsTrigger\n key={key}\n value={key}\n className=\"rounded-md px-3 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground data-[state=active]:bg-primary/10 data-[state=active]:font-semibold data-[state=active]:text-primary data-[state=active]:shadow-none\"\n >\n {cfg.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n <div className=\"flex items-center gap-3 text-xs text-muted-foreground\">\n <span>{visible.length} generation{visible.length !== 1 ? 's' : ''}</span>\n <span className=\"flex items-center gap-0.5\">\n <DollarSign className=\"h-3 w-3\" />\n {totalCost.toFixed(2)} spent\n </span>\n </div>\n </div>\n\n <GenerationGrid generations={visible} typeFilter={typeFilter} onSelect={onSelect} />\n </div>\n )\n}\n","import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@tangle-network/sandbox-ui/primitives'\nimport { type Generation } from '../studio'\nimport { typeConfigFor } from './type-config'\nimport { GenerationDetail } from './generation-detail'\n\n/**\n * Centered detail view for a single generation. Opens when `generation` is set\n * and reports dismissal through `onClose`. GenerationDetail renders the media,\n * type badge, prompt and metadata; the dialog title carries the a11y label.\n */\nexport function GenerationDetailModal({\n generation,\n vaultHref,\n onClose,\n}: {\n generation: Generation | null\n vaultHref?: (filePath?: string | null) => string\n onClose: () => void\n}) {\n const cfg = generation ? typeConfigFor(generation.type) : null\n return (\n <Dialog open={generation != null} onOpenChange={(open) => { if (!open) onClose() }}>\n <DialogContent className=\"max-h-[85vh] max-w-2xl overflow-y-auto border-card-edge bg-popover shadow-[var(--shadow-overlay)]\">\n <DialogHeader>\n <DialogTitle className=\"text-left text-[15px] font-semibold\">{cfg ? `${cfg.label} generation` : 'Generation'}</DialogTitle>\n </DialogHeader>\n {generation && (\n <GenerationDetail generation={generation} vaultHref={vaultHref} onNavigate={onClose} />\n )}\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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","names":["useRef","useState","Button","Images","useEffect","useMemo","useRef","useState","useCallback","useEffect","useState","useCallback","useEffect","useState","jsx","jsxs","useState","useRef","useEffect","useMemo","jsx","jsxs","Video","jsx","jsxs","useMemo","Link","Button","FolderOpen","X","jsx","jsxs","Sparkles","Badge","jsx","jsxs","Badge","jsx","Sparkles","Badge","Button","jsx","jsxs","Badge","Button","Fragment","jsx","jsxs","useMemo","X","Link","Button","FolderOpen","Fragment","jsx","jsxs","useState","useRef","Images","Button","jsx","jsxs","useMemo","Tabs","TabsList","TabsTrigger","DollarSign","jsx","jsxs","useMemo","Tabs","TabsList","TabsTrigger","DollarSign","Dialog","jsx","jsxs","Dialog"]}
1
+ {"version":3,"sources":["../../src/studio-react/use-studio-generations.ts","../../src/studio-react/studio-composer.tsx","../../src/studio-react/composer-option-controls.tsx","../../src/studio-react/generation-notice.tsx","../../src/studio-react/media-tile.tsx","../../src/studio-react/vault-path-popover.tsx","../../src/studio-react/download-generations.ts","../../src/studio-react/studio-playback.tsx","../../src/studio-react/media-viewer.tsx","../../src/studio-react/studio-home-screen.tsx","../../src/studio-react/studio-confirm.tsx","../../src/studio-react/studio-toasts.tsx","../../src/studio-react/use-batch-navigation.ts","../../src/studio-react/use-deferred-delete.ts","../../src/studio-react/studio-generation-screen.tsx","../../src/studio-react/studio-history-screen.tsx","../../src/studio-react/use-generation-history.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useRevalidator } from 'react-router'\nimport {\n type Generation,\n generationStatus,\n isLocalGeneration,\n latestBatchOf,\n mergeLiveGeneration,\n mergeLoaderAndLive,\n} from '../studio'\n\n/**\n * The generation orchestrator behind a studio surface: it merges the loader's\n * rows with in-flight live generations, computes the latest batch for the\n * canvas, polls running generations until they settle, and revalidates the\n * route loader when a status changes.\n *\n * The merge keeps the canvas, the library, and the polling path looking at the\n * same full list. Polling hits `generationsEndpoint` (default `/api/generations`,\n * the convention both apps already serve); pass an override if a product routes\n * it elsewhere. `onGenerated` is wired to the composer's per-result callback.\n */\nexport function useStudioGenerations(\n loaderGenerations: Generation[],\n options: { workspaceId?: string; generationsEndpoint?: string } = {},\n) {\n const { workspaceId, generationsEndpoint = '/api/generations' } = options\n const revalidator = useRevalidator()\n const [liveGenerations, setLiveGenerations] = useState<Generation[]>([])\n\n const mergedGenerations = useMemo(\n () => mergeLoaderAndLive(loaderGenerations, liveGenerations),\n [loaderGenerations, liveGenerations],\n )\n\n const latestBatch = useMemo(() => latestBatchOf(mergedGenerations), [mergedGenerations])\n\n const runningGenerationIds = useMemo(() => mergedGenerations\n .filter((gen) => {\n const status = generationStatus(gen)\n return status === 'pending' || status === 'running'\n })\n .map((gen) => gen.id)\n .filter((id) => !id.startsWith('local-')), [mergedGenerations])\n\n // The poll loop must not churn the effect. `runningGenerationIds` is a fresh\n // array every render and react-router's `revalidator` identity flips while a\n // revalidation is in flight — depending on either restarts the interval on\n // every tick, collapsing the 4s cadence into a request flood. Key the effect\n // on the STABLE join of the running ids (re-subscribe only when the set\n // actually changes) and reach the live ids / revalidate through refs.\n const runningIdsRef = useRef(runningGenerationIds)\n runningIdsRef.current = runningGenerationIds\n const revalidateRef = useRef(revalidator.revalidate)\n revalidateRef.current = revalidator.revalidate\n const runningKey = runningGenerationIds.join(',')\n\n useEffect(() => {\n if (!workspaceId || !runningKey) return\n let cancelled = false\n const poll = async () => {\n const responses = await Promise.all(runningIdsRef.current.map(async (id) => {\n const res = await fetch(`${generationsEndpoint}?workspaceId=${encodeURIComponent(workspaceId)}&id=${encodeURIComponent(id)}`)\n if (!res.ok) return null\n const data = await res.json() as { generation?: Generation }\n return data.generation ?? null\n }))\n if (cancelled) return\n const refreshed = responses.filter((gen): gen is Generation => Boolean(gen))\n if (refreshed.length > 0) {\n setLiveGenerations((current) => refreshed.reduce(mergeLiveGeneration, current))\n }\n if (refreshed.some((gen) => generationStatus(gen) !== 'running' && generationStatus(gen) !== 'pending')) {\n revalidateRef.current()\n }\n }\n const interval = window.setInterval(() => { void poll() }, 4_000)\n void poll()\n return () => {\n cancelled = true\n window.clearInterval(interval)\n }\n }, [runningKey, workspaceId, generationsEndpoint])\n\n const onGenerated = useCallback((generation: Generation) => {\n setLiveGenerations((current) => mergeLiveGeneration(current, generation))\n if (!isLocalGeneration(generation)) revalidateRef.current()\n }, [])\n\n return { mergedGenerations, latestBatch, onGenerated }\n}\n","/**\n * The studio composer: one chat-shaped card — prompt, media-type switch, and a\n * band of option pills — that replaces the stacked field panel.\n *\n * The whole point of the rework is that the CONTROLS ARE THE MODEL'S OWN. A pill\n * renders only when the selected model publishes that parameter, its choices are\n * the published enum (or the published range), and the value goes onto the wire\n * exactly as published — seedance takes the string `'5'`, veo `'8s'`, kling the\n * number `5`, and coercing any of them is a 400 from the provider. A model that\n * publishes nothing (`ltx-video`) shows the model pill and nothing else: the\n * honest reading of \"we do not know what this takes\" is an absent control, not\n * an invented one and not a disabled one.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { AudioLines, ArrowUp, Image as ImageIcon, Video } from 'lucide-react'\nimport {\n type Generation,\n type MediaModelCatalogResponse,\n type MediaModelOption,\n type ModelOptionMetadata,\n type ModelOptionValue,\n type ModelOptionsMetadata,\n aspectRatioFromOptions,\n buildGenerationRequestBody,\n curateComposerModels,\n failedOptimisticGeneration,\n imageToVideoSibling,\n modelMessage,\n normalizeImageCount,\n optimisticGeneration,\n optionChoices,\n preferredModelId,\n reconcileOptionValues,\n resolveComposerOptions,\n supportsCustomImageSize,\n textToVideoSibling,\n userSafeGenerationMessage,\n} from '../studio'\nimport {\n AudioTogglePill,\n ComposerBand,\n CustomSizeForm,\n MediaTypeSegments,\n ModelPill,\n OptionPill,\n ReferencePill,\n type MediaTypeSegment,\n type OptionChoice,\n optionValueLabel,\n} from './composer-option-controls'\n\n/** The lanes the composer offers. Avatar and transcription stay disabled\n * (#451); \"Audio\" is the word for the `speech` lane everywhere on screen. */\nexport type ComposerType = 'image' | 'video' | 'speech'\n\nconst SEGMENTS: readonly MediaTypeSegment<ComposerType>[] = [\n { type: 'image', label: 'Image', icon: ImageIcon },\n { type: 'video', label: 'Video', icon: Video },\n { type: 'speech', label: 'Audio', icon: AudioLines },\n]\n\n/** Pill order per lane. A parameter absent from the model's metadata, or marked\n * `supported: false`, drops out of the row entirely. */\nconst PILL_ORDER: Record<ComposerType, readonly string[]> = {\n image: ['size', 'quality', 'n'],\n video: ['duration', 'resolution', 'aspect_ratio', 'mode'],\n speech: ['voice', 'speed'],\n}\n\nconst PARAM_LABELS: Record<string, string> = {\n size: 'Size',\n quality: 'Quality',\n n: 'Count',\n duration: 'Duration',\n resolution: 'Resolution',\n aspect_ratio: 'Aspect',\n mode: 'Mode',\n voice: 'Voice',\n speed: 'Speed',\n}\n\n/**\n * Speed is published as a continuous range (0.25–4.0 on OpenAI's endpoint), and\n * a range is not a menu: enumerating it at the step a user would actually pick\n * is 376 rows. These are the presets, filtered against whatever range the model\n * publishes, so a narrower range can never offer a speed it rejects.\n */\nconst SPEED_PRESETS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 4] as const\n\nfunction choicesFor(param: string, meta: ModelOptionMetadata): readonly ModelOptionValue[] {\n const { min, max } = meta\n if (param === 'speed' && !meta.values && min != null && max != null) {\n return SPEED_PRESETS.filter((speed) => speed >= min && speed <= max)\n }\n return optionChoices(meta)\n}\n\n/** The parameters a lane renders, in order, skipping unknown and unsupported\n * ones. `audio` and the reference image are not in here — neither is a value\n * menu. */\nfunction visibleParams(type: ComposerType, options: ModelOptionsMetadata | undefined): string[] {\n if (!options) return []\n return PILL_ORDER[type].filter((param) => {\n const meta = options[param]\n return Boolean(meta) && meta?.supported !== false\n })\n}\n\n/**\n * The lane's model. The catalog's own default wins when it survives curation;\n * otherwise the first curated model does — a default that #449 curation removed\n * (a Sora model, a non-gpt-image-2 image model) must not leave the lane with a\n * selection its own menu does not offer.\n */\nfunction defaultModelId(\n type: ComposerType,\n catalog: MediaModelCatalogResponse | null,\n curated: readonly MediaModelOption[],\n): string {\n const preferred = preferredModelId(type, catalog)\n if (preferred && curated.some((model) => model.id === preferred)) return preferred\n return curated.find((model) => model.status !== 'unavailable')?.id ?? curated[0]?.id ?? ''\n}\n\nexport interface StudioComposerProps {\n workspaceId?: string\n onGenerated: (generation: Generation) => void\n /** `home` (under a page heading) vs `docked` (pinned under a generation).\n * Presentational only — the card itself is identical either way. */\n variant?: 'home' | 'docked'\n /** Pick or upload a reference image, resolving to a URL or `null` on cancel.\n * Without it the Reference pill takes a URL instead, which is the most a host\n * with no upload endpoint can honestly offer. */\n pickReferenceImage?: () => Promise<string | null>\n className?: string\n}\n\n/**\n * The composer card. The host owns its width (the card fills its container) and\n * the heading above it; this owns the prompt, the controls, and the POST.\n */\nexport function StudioComposer({\n workspaceId,\n onGenerated,\n variant = 'home',\n pickReferenceImage,\n className,\n}: StudioComposerProps) {\n const [type, setType] = useState<ComposerType>('image')\n const [prompt, setPrompt] = useState('')\n const [catalog, setCatalog] = useState<MediaModelCatalogResponse | null>(null)\n const [catalogLoading, setCatalogLoading] = useState(false)\n const [catalogError, setCatalogError] = useState<string | null>(null)\n const [selectedModels, setSelectedModels] = useState<Partial<Record<ComposerType, string>>>({})\n const [optionValues, setOptionValues] = useState<Record<ComposerType, Record<string, ModelOptionValue>>>({\n image: {},\n video: {},\n speech: {},\n })\n const [referenceImageUrl, setReferenceImageUrl] = useState<string | null>(null)\n const [isSubmitting, setIsSubmitting] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const submitLockRef = useRef(false)\n const bandRef = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n if (!workspaceId) return\n let cancelled = false\n setCatalogLoading(true)\n setCatalogError(null)\n\n fetch(`/api/media-models?workspaceId=${encodeURIComponent(workspaceId)}`)\n .then(async (res) => {\n const data = await res.json() as MediaModelCatalogResponse\n if (!res.ok) throw new Error(data.error ?? 'Could not load media models')\n return data\n })\n .then((data) => {\n if (cancelled) return\n setCatalog(data)\n })\n .catch(() => {\n if (cancelled) return\n setCatalog(null)\n // Never surface the raw fetch/parse error — a non-JSON 404 page reads\n // as `Unexpected token 'N', \"Not Found\" is not valid JSON` to users.\n setCatalogError('Could not load media models')\n })\n .finally(() => {\n if (!cancelled) setCatalogLoading(false)\n })\n\n return () => {\n cancelled = true\n }\n }, [workspaceId])\n\n const laneModels = useMemo(() => catalog?.models[type] ?? [], [catalog, type])\n const curatedModels = useMemo(() => curateComposerModels(type, laneModels), [laneModels, type])\n // A retained pick survives only while the CURRENT catalog can honour it: with\n // no catalog yet keep it; an intentionally-uncurated i2v sibling stays (it is\n // reached by attaching a reference, never listed); anything else must be\n // present and available in the curated list, or the lane falls back to its\n // default. The deleted ComposerHero defended this via selectedModelsWithDefaults;\n // that helper resolves over the FULL catalog and would resurrect\n // curation-removed defaults, so the guard is re-derived here over curatedModels.\n const retained = selectedModels[type]\n const retainedUsable = retained !== undefined && (\n !catalog\n || Boolean(textToVideoSibling(retained))\n || curatedModels.some((model) => model.id === retained && model.status !== 'unavailable')\n )\n const modelId = retainedUsable ? retained : defaultModelId(type, catalog, curatedModels)\n // The image-to-video sibling is deliberately absent from the curated menu (it\n // is reached by attaching a reference image), so the row backing the pill is\n // looked up in the FULL lane list and may still be missing — a sibling the\n // catalog does not carry is sent by id and displayed as its id.\n const modelOption = laneModels.find((model) => model.id === modelId)\n\n const options = useMemo(\n () => (modelId\n ? resolveComposerOptions({\n type,\n modelId,\n provider: modelOption?.provider,\n catalogOptions: modelOption?.options,\n })\n : undefined),\n [modelId, modelOption, type],\n )\n\n // Re-reconcile whenever the resolved options change: a selection the new model\n // still accepts survives, an illegal one resets to that model's default, and a\n // parameter it does not publish is dropped rather than carried invisibly.\n useEffect(() => {\n setOptionValues((current) => {\n const next = reconcileOptionValues(options, current[type], {\n allowCustomSize: supportsCustomImageSize(modelId),\n })\n const unchanged = Object.keys(next).length === Object.keys(current[type]).length\n && Object.entries(next).every(([key, value]) => current[type][key] === value)\n return unchanged ? current : { ...current, [type]: next }\n })\n }, [modelId, options, type])\n\n const values = optionValues[type]\n const params = visibleParams(type, options)\n const audioMeta = type === 'video' ? options?.audio : undefined\n const audioSupported = Boolean(audioMeta) && audioMeta?.supported !== false\n // A model the catalog does not list is normally \"not ready\" — except for a\n // verified image-to-video sibling, which the composer selected itself by\n // attaching a reference image and must be able to send even when the catalog\n // never listed it.\n const unlistedSibling = !modelOption && Boolean(textToVideoSibling(modelId))\n const referenceSupported = type === 'video'\n && Boolean(imageToVideoSibling(modelId) ?? textToVideoSibling(modelId))\n\n const modelReady = (Boolean(modelOption) || unlistedSibling)\n && modelOption?.status !== 'unavailable'\n && !catalogLoading\n && !catalogError\n const canSubmit = Boolean(workspaceId) && modelReady && Boolean(prompt.trim()) && !isSubmitting\n\n function selectModel(id: string) {\n setSelectedModels((current) => ({ ...current, [type]: id }))\n }\n\n /** The model MENU's handler. While a reference is attached the selection must\n * keep the attachment ⇒ image-to-video invariant: a reference-capable pick is\n * re-mapped to its i2v sibling, and a pick with no sibling drops the\n * attachment explicitly rather than hiding it to reappear later. */\n function chooseModel(id: string) {\n if (!referenceImageUrl || type !== 'video') return selectModel(id)\n const sibling = imageToVideoSibling(id)\n if (sibling) return selectModel(sibling)\n if (!textToVideoSibling(id)) setReferenceImageUrl(null)\n return selectModel(id)\n }\n\n function setOption(param: string, value: ModelOptionValue) {\n setOptionValues((current) => ({ ...current, [type]: { ...current[type], [param]: value } }))\n }\n\n function attachReference(url: string) {\n setReferenceImageUrl(url)\n const sibling = imageToVideoSibling(modelId)\n if (sibling) selectModel(sibling)\n }\n\n function removeReference() {\n setReferenceImageUrl(null)\n const sibling = textToVideoSibling(modelId)\n if (sibling) selectModel(sibling)\n }\n\n async function generate() {\n if (!workspaceId || submitLockRef.current || isSubmitting) return\n const promptText = prompt.trim()\n if (!promptText) return\n if (!modelReady) {\n if (!catalogLoading) setError('Select an available model')\n return\n }\n submitLockRef.current = true\n setIsSubmitting(true)\n setError(null)\n\n const clientRequestId = crypto.randomUUID()\n const imageCount = type === 'image' ? normalizeImageCount(values.n ?? 1) : 1\n const localGenerations = Array.from({ length: imageCount }, (_, outputIndex) => optimisticGeneration(\n {\n type,\n prompt: promptText,\n model: modelId,\n clientRequestId,\n outputIndex: type === 'image' ? outputIndex : undefined,\n outputCount: type === 'image' ? imageCount : undefined,\n },\n aspectRatioFromOptions(type, {\n size: asText(values.size),\n aspectRatio: asText(values.aspect_ratio),\n }),\n ))\n localGenerations.slice().reverse().forEach(onGenerated)\n setPrompt('')\n\n let receivedServerGeneration = false\n try {\n const body = buildGenerationRequestBody({\n workspaceId,\n clientRequestId,\n type,\n model: modelId,\n prompt: promptText,\n // `duration`, `audio` and `speed` go on the wire in the TYPE the model\n // published them in — a `duration` of `'5'` that arrives as `5` is a\n // different request, and the three providers behind this lane disagree\n // about which is right. An absent value is omitted, never defaulted.\n image: {\n size: asText(values.size),\n quality: asText(values.quality),\n count: imageCount,\n },\n video: {\n duration: asWireScalar(values.duration),\n resolution: asText(values.resolution),\n aspectRatio: asText(values.aspect_ratio),\n referenceImageUrl: referenceSupported && referenceImageUrl ? referenceImageUrl : undefined,\n audio: audioSupported && typeof values.audio === 'boolean' ? values.audio : undefined,\n mode: asText(values.mode),\n },\n speech: {\n voice: asText(values.voice),\n speed: typeof values.speed === 'number' ? values.speed : undefined,\n },\n })\n\n const res = await fetch('/api/generate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n const data = await res.json() as { generation?: Generation; generations?: Generation[]; error?: string }\n const serverGenerations = data.generations?.length ? data.generations : data.generation ? [data.generation] : []\n if (serverGenerations.length > 0) {\n receivedServerGeneration = true\n serverGenerations.slice().reverse().forEach(onGenerated)\n }\n if (!res.ok || serverGenerations.length === 0) throw new Error(data.error ?? 'Generation failed')\n } catch (err) {\n if (!receivedServerGeneration) localGenerations.map(failedOptimisticGeneration).forEach(onGenerated)\n setError(err instanceof Error ? userSafeGenerationMessage(err.message) : 'Generation failed')\n } finally {\n submitLockRef.current = false\n setIsSubmitting(false)\n }\n }\n\n const statusMessage = unlistedSibling\n ? null\n : modelMessage(modelOption, catalogLoading, curatedModels.length)\n const notice = catalogError ?? error ?? statusMessage\n const noticeIsProblem = Boolean(catalogError || error || modelOption?.status === 'unavailable')\n\n return (\n <section\n data-variant={variant}\n className={`rounded-[14px] border border-border bg-card px-2.5 pb-[9px] pt-2 shadow-sm transition focus-within:border-primary focus-within:ring-[3px] focus-within:ring-ring/30 ${className ?? ''}`}\n >\n <textarea\n value={prompt}\n onChange={(event) => setPrompt(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== 'Enter' || event.shiftKey) return\n event.preventDefault()\n void generate()\n }}\n rows={3}\n aria-label=\"Prompt\"\n placeholder=\"Describe what you want to generate…\"\n className=\"block min-h-[66px] w-full resize-none border-0 bg-transparent px-1.5 pb-1 pt-1.5 text-[14.5px] leading-relaxed outline-none placeholder:text-muted-foreground\"\n />\n\n <div className=\"flex items-center gap-2\">\n <MediaTypeSegments value={type} segments={SEGMENTS} onChange={setType} />\n\n <ComposerBand bandRef={bandRef} resetKey={type}>\n <ModelPill\n models={curatedModels}\n value={modelId}\n displayName={modelOption?.name || modelId || 'Select a model'}\n provider={modelOption?.provider}\n onSelect={chooseModel}\n bandRef={bandRef}\n />\n\n {params.map((param) => (\n <div key={`${type}-${param}`} className=\"studio-pill-in flex-none\">\n <OptionPill\n label={PARAM_LABELS[param] ?? param}\n value={values[param]}\n choices={choicesForPill(param, options?.[param], values[param])}\n onSelect={(value) => setOption(param, value)}\n bandRef={bandRef}\n custom={param === 'size' && supportsCustomImageSize(modelId)\n ? {\n label: 'Custom…',\n render: ({ close }) => (\n <CustomSizeForm\n initial={asText(values.size)}\n onApply={(size) => {\n setOption('size', size)\n close()\n }}\n onCancel={close}\n />\n ),\n }\n : undefined}\n />\n </div>\n ))}\n\n {audioSupported && (\n <div key={`${type}-audio`} className=\"studio-pill-in flex-none\">\n <AudioTogglePill\n on={values.audio === true}\n onToggle={(on) => setOption('audio', on)}\n />\n </div>\n )}\n\n {referenceSupported && (\n <div key={`${type}-reference`} className=\"studio-pill-in flex-none\">\n <ReferencePill\n url={referenceImageUrl}\n onAttach={attachReference}\n onRemove={removeReference}\n pick={pickReferenceImage}\n bandRef={bandRef}\n />\n </div>\n )}\n </ComposerBand>\n\n <button\n type=\"button\"\n aria-label=\"Generate\"\n title=\"Generate\"\n disabled={!canSubmit}\n onClick={() => void generate()}\n className=\"studio-send ml-auto inline-flex h-8 w-8 flex-none items-center justify-center rounded-full text-white transition disabled:opacity-40\"\n >\n <ArrowUp className=\"h-4 w-4\" strokeWidth={2} />\n </button>\n </div>\n\n {notice && (\n <p className={`px-1.5 pt-1.5 text-[12px] ${noticeIsProblem ? 'text-destructive' : 'text-muted-foreground'}`}>\n {notice}\n </p>\n )}\n </section>\n )\n}\n\n/** A text-typed field's value, or `undefined` so the field is omitted entirely\n * — `String(undefined)` puts the literal `\"undefined\"` on the wire, which the\n * provider will try to honour. These fields are typed `string` by the\n * `/api/generate` contract, so a numeric published value is rendered as its\n * digits; the type-sensitive fields (`duration`, `audio`, `speed`) never pass\n * through here. */\nfunction asText(value: ModelOptionValue | undefined): string | undefined {\n return typeof value === 'string' ? value : typeof value === 'number' ? String(value) : undefined\n}\n\n/** A duration passes through as published: the string `'5'`, the string `'8s'`,\n * or the number `5`. Only a boolean (which no lane publishes here) is refused. */\nfunction asWireScalar(value: ModelOptionValue | undefined): string | number | undefined {\n return typeof value === 'string' || typeof value === 'number' ? value : undefined\n}\n\n/** The pill's rows: the published choices, plus the current value when it is a\n * legal off-enum one (a custom gpt-image-2 size), so the pill reports what is\n * actually selected instead of falling back to a dash. */\nfunction choicesForPill(\n param: string,\n meta: ModelOptionMetadata | undefined,\n value: ModelOptionValue | undefined,\n): OptionChoice[] {\n if (!meta) return []\n const choices = choicesFor(param, meta).map((choice) => ({\n value: choice,\n label: optionValueLabel(param, choice),\n }))\n if (value === undefined || choices.some((choice) => choice.value === value)) return choices\n return [...choices, { value, label: `${optionValueLabel(param, value)} · custom` }]\n}\n","/**\n * The studio composer's control row: the media-type segmented group, the\n * horizontally scrolling option band, and the pills that dock in it.\n *\n * Every pill that opens a panel opens it through `usePopover` + `PopoverSurface`\n * (`../web-react/controls`), never an in-place `absolute` panel: the band IS a\n * scroll container (`overflow-x-auto`), and a scroll container clips every\n * positioned descendant whose containing block sits inside it — the measured\n * failure that left the chat composer's own menus painting zero pixels. The\n * surface portals to `document.body` and re-anchors on capture-phase scroll, so\n * scrolling the band moves an open menu with its pill. The one rule the surface\n * cannot supply is when to give up: a menu whose pill has been scrolled out of\n * the band no longer points at anything, so it closes when the pill's\n * horizontal CENTRE leaves the band's box.\n */\n\nimport {\n useCallback,\n useEffect,\n useId,\n useState,\n type JSX,\n type ReactNode,\n type RefObject,\n} from 'react'\nimport { ImagePlus, Volume2, VolumeX, X, type LucideIcon } from 'lucide-react'\nimport {\n CheckGlyph,\n ChevronDown,\n OVERLAY_SHADOW,\n POPOVER_OPTION_FOCUS,\n PopoverSurface,\n usePopover,\n} from '../web-react/controls'\nimport { ProviderLogo } from '../web-react/provider-logo'\nimport { type MediaModelOption, type ModelOptionValue, validateCustomImageSize } from '../studio'\n\n/** The resting pill: one row height, never wraps, shrink-proof in the band. */\nconst PILL = 'inline-flex h-7 flex-none items-center gap-1.5 whitespace-nowrap rounded-full border border-border bg-card px-2.5 text-[12.5px] font-medium text-foreground transition hover:bg-accent'\nconst MENU_PANEL = `flex min-w-[184px] flex-col overflow-y-auto rounded-xl border border-border bg-popover p-1 text-popover-foreground ${OVERLAY_SHADOW}`\nconst MENU_HEADER = 'px-2.5 pb-1 pt-1.5 text-[10.5px] font-semibold uppercase tracking-[0.08em] text-muted-foreground'\n\nfunction menuRowClass(selected: boolean): string {\n return `flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-[13px] transition ${POPOVER_OPTION_FOCUS} ${\n selected ? 'bg-primary/10 font-medium' : 'hover:bg-accent'\n }`\n}\n\n/** Deepest edge fade, in px. Beyond this the fade stops reading as \"there is\n * more\" and starts reading as a vignette. */\nconst MAX_FADE = 32\n\n/** One media lane's segment: the icon it shows collapsed, the word it shows\n * when active. */\nexport interface MediaTypeSegment<T extends string> {\n type: T\n label: string\n icon: LucideIcon\n}\n\n/**\n * The pinned-left media-type group: a track holding one filled pill (the active\n * lane, icon + word) and icon-only siblings. `aria-pressed` carries the state\n * and `aria-label` carries the word the collapsed items drop.\n */\nexport function MediaTypeSegments<T extends string>({\n value,\n segments,\n onChange,\n}: {\n value: T\n segments: readonly MediaTypeSegment<T>[]\n onChange: (type: T) => void\n}) {\n return (\n <div\n role=\"group\"\n aria-label=\"Media type\"\n className=\"flex flex-none items-center gap-0.5 rounded-full border border-border bg-muted p-[3px]\"\n >\n {segments.map(({ type, label, icon: Icon }) => {\n const active = type === value\n return (\n <button\n key={type}\n type=\"button\"\n aria-pressed={active}\n aria-label={label}\n title={label}\n onClick={() => onChange(type)}\n className={`inline-flex h-7 items-center gap-1.5 whitespace-nowrap rounded-full text-[12.5px] transition ${\n active\n ? 'bg-card px-2.5 font-medium text-foreground shadow-sm'\n : 'px-2 text-muted-foreground hover:text-foreground'\n }`}\n >\n <Icon className=\"h-4 w-4 shrink-0\" strokeWidth={1.5} />\n {active && <span>{label}</span>}\n </button>\n )\n })}\n </div>\n )\n}\n\n/**\n * The middle scroller. It hides its scrollbar and fades whichever edge has\n * content behind it, at the DEPTH of what is actually hidden (capped at\n * {@link MAX_FADE}) rather than a fixed gradient that claims more is off-screen\n * than there is. A media-type switch resets the scroll to the start, because\n * the control set itself changed; every other re-render leaves `scrollLeft`\n * alone, so picking a value cannot jump the band back to the beginning.\n */\nexport function ComposerBand({\n bandRef,\n resetKey,\n children,\n}: {\n bandRef: RefObject<HTMLDivElement | null>\n /** Changing this scrolls the band back to the start. */\n resetKey: string\n children: ReactNode\n}) {\n const sync = useCallback(() => {\n const band = bandRef.current\n if (!band) return\n const hiddenStart = band.scrollLeft\n const hiddenEnd = Math.max(0, band.scrollWidth - band.clientWidth - band.scrollLeft)\n const atStart = hiddenStart > 1\n const atEnd = hiddenEnd > 1\n band.dataset.overflow = atStart && atEnd ? 'both' : atStart ? 'start' : atEnd ? 'end' : 'none'\n band.style.setProperty('--fade-start', `${atStart ? Math.min(MAX_FADE, hiddenStart) : 0}px`)\n band.style.setProperty('--fade-end', `${atEnd ? Math.min(MAX_FADE, hiddenEnd) : 0}px`)\n }, [bandRef])\n\n // After every render: the pill set changes with the model and the lane, so\n // the hidden width this reads is only correct once the new pills are laid out.\n useEffect(sync)\n\n useEffect(() => {\n const band = bandRef.current\n window.addEventListener('resize', sync)\n // jsdom and older browsers have no ResizeObserver; the window listener still\n // covers the case that matters (the viewport getting narrower).\n const observer = typeof ResizeObserver === 'undefined' || !band ? null : new ResizeObserver(sync)\n if (band) observer?.observe(band)\n return () => {\n window.removeEventListener('resize', sync)\n observer?.disconnect()\n }\n }, [bandRef, sync])\n\n useEffect(() => {\n const band = bandRef.current\n if (band) band.scrollLeft = 0\n sync()\n }, [bandRef, resetKey, sync])\n\n return (\n <div\n ref={bandRef}\n data-overflow=\"none\"\n onScroll={sync}\n className=\"studio-band flex min-w-0 flex-1 flex-nowrap items-center gap-2 overflow-x-auto\"\n >\n {children}\n </div>\n )\n}\n\n/**\n * Close an open menu once its pill has been scrolled out of the band.\n *\n * `PopoverSurface` re-anchors on scroll, so a menu follows its pill for free —\n * which is right until the pill leaves the band entirely and the menu is left\n * pointing at a clipped edge. The centre is the test rather than either edge: a\n * pill half-way out is still the pill the user opened.\n */\nfunction useCloseWhenScrolledOut(\n open: boolean,\n setOpen: (open: boolean) => void,\n triggerRef: RefObject<HTMLElement | null>,\n bandRef: RefObject<HTMLDivElement | null>,\n) {\n useEffect(() => {\n const band = bandRef.current\n if (!open || !band) return\n const onScroll = () => {\n const trigger = triggerRef.current\n if (!trigger) return\n const anchor = trigger.getBoundingClientRect()\n const box = band.getBoundingClientRect()\n const centre = anchor.left + anchor.width / 2\n if (centre < box.left || centre > box.right) setOpen(false)\n }\n band.addEventListener('scroll', onScroll)\n return () => band.removeEventListener('scroll', onScroll)\n }, [bandRef, open, setOpen, triggerRef])\n}\n\n/** One renderable value: the wire value, untouched, and how it reads. */\nexport interface OptionChoice {\n value: ModelOptionValue\n label: string\n}\n\nfunction MenuRows<T extends ModelOptionValue>({\n value,\n choices,\n onSelect,\n}: {\n value: T | undefined\n choices: readonly { value: T; label: string }[]\n onSelect: (value: T) => void\n}) {\n return choices.map((choice) => (\n <button\n key={String(choice.value)}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={choice.value === value}\n onClick={() => onSelect(choice.value)}\n className={menuRowClass(choice.value === value)}\n >\n <span className=\"truncate\">{choice.label}</span>\n {choice.value === value && (\n <CheckGlyph className=\"ml-auto h-3.5 w-3.5 shrink-0 text-primary\" />\n )}\n </button>\n ))\n}\n\n/** A standalone enum picker using the studio composer's pill and menu grammar. */\nexport function MenuPill<T extends string>({\n label,\n value,\n choices,\n onSelect,\n className,\n}: {\n label: string\n value: T\n choices: readonly { value: T; label: string }[]\n onSelect: (value: T) => void\n className?: string\n}): JSX.Element {\n const [open, setOpen] = useState(false)\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const panelId = useId()\n const selected = choices.find((choice) => choice.value === value)\n\n return (\n <div ref={containerRef} className=\"relative inline-flex flex-none\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-controls={open ? panelId : undefined}\n title={label}\n aria-label={label}\n onClick={() => setOpen(!open)}\n className={`${PILL} ${className ?? ''}`}\n >\n <span>{selected?.label ?? '—'}</span>\n <ChevronDown className=\"h-3 w-3 shrink-0 text-muted-foreground\" />\n </button>\n\n <PopoverSurface\n open={open}\n id={panelId}\n role=\"menu\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={MENU_PANEL}\n >\n <div className={MENU_HEADER}>{label}</div>\n <MenuRows\n value={value}\n choices={choices}\n onSelect={(next) => {\n onSelect(next)\n setOpen(false)\n }}\n />\n </PopoverSurface>\n </div>\n )\n}\n\n/**\n * A value pill and the menu it opens.\n *\n * The pill shows the VALUE and carries the parameter name in `title` and in its\n * accessible name — a band of eight labelled pills does not fit a chat card,\n * and \"5s\" alone tells a screen-reader user nothing about which parameter it\n * belongs to.\n */\nexport function OptionPill({\n label,\n value,\n choices,\n onSelect,\n bandRef,\n custom,\n}: {\n label: string\n value: ModelOptionValue | undefined\n choices: readonly OptionChoice[]\n onSelect: (value: ModelOptionValue) => void\n bandRef: RefObject<HTMLDivElement | null>\n /** A trailing row that swaps the menu to a form (the gpt-image-2 custom\n * size). Absent for every parameter whose values are an enum. */\n custom?: {\n label: string\n render: (args: { close: () => void }) => ReactNode\n }\n}) {\n const [open, setOpen] = useState(false)\n const [customOpen, setCustomOpen] = useState(false)\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const panelId = useId()\n useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef)\n\n const selected = choices.find((choice) => choice.value === value)\n const close = () => {\n setOpen(false)\n setCustomOpen(false)\n }\n\n return (\n <div ref={containerRef} className=\"relative inline-flex flex-none\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-controls={open ? panelId : undefined}\n title={label}\n aria-label={`${label}: ${selected?.label ?? 'not set'}`}\n onClick={() => {\n setCustomOpen(false)\n setOpen(!open)\n }}\n className={PILL}\n >\n <span>{selected?.label ?? '—'}</span>\n <ChevronDown className=\"h-3 w-3 shrink-0 text-muted-foreground\" />\n </button>\n\n <PopoverSurface\n open={open}\n id={panelId}\n role={customOpen ? undefined : 'menu'}\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={MENU_PANEL}\n >\n <div className={MENU_HEADER}>{label}</div>\n {customOpen && custom\n ? custom.render({ close })\n : (\n <>\n <MenuRows\n value={value}\n choices={choices}\n onSelect={(next) => {\n onSelect(next)\n close()\n }}\n />\n {custom && (\n <button\n type=\"button\"\n onClick={() => setCustomOpen(true)}\n className={`${menuRowClass(false)} text-muted-foreground`}\n >\n {custom.label}\n </button>\n )}\n </>\n )}\n </PopoverSurface>\n </div>\n )\n}\n\n/**\n * The custom-size form the Size menu swaps to.\n *\n * The reason a size is refused renders next to the fields that are wrong — a\n * toast would be gone by the time the user looks back at the inputs — and Apply\n * refuses rather than disables, so the reason is always reachable.\n */\nexport function CustomSizeForm({\n initial,\n onApply,\n onCancel,\n}: {\n initial?: string\n onApply: (size: string) => void\n onCancel: () => void\n}) {\n const parsed = /^(\\d+)x(\\d+)$/.exec(initial ?? '')\n const [width, setWidth] = useState(parsed?.[1] ?? '')\n const [height, setHeight] = useState(parsed?.[2] ?? '')\n const [error, setError] = useState<string | null>(null)\n const fieldClass = 'h-8 w-[74px] rounded-md border border-input bg-background px-2 text-[13px] tabular-nums'\n\n function apply() {\n const verdict = validateCustomImageSize(Number(width), Number(height))\n if (!verdict.ok) {\n setError(verdict.reason)\n return\n }\n onApply(`${Number(width)}x${Number(height)}`)\n }\n\n return (\n <div className=\"flex w-[228px] flex-col gap-2 p-1.5\">\n <div className=\"flex items-center gap-2\">\n <input\n type=\"number\"\n value={width}\n aria-label=\"Custom width\"\n onChange={(event) => {\n setWidth(event.target.value)\n setError(null)\n }}\n className={fieldClass}\n />\n <span aria-hidden className=\"text-muted-foreground\">×</span>\n <input\n type=\"number\"\n value={height}\n aria-label=\"Custom height\"\n onChange={(event) => {\n setHeight(event.target.value)\n setError(null)\n }}\n className={fieldClass}\n />\n </div>\n {error\n ? <p className=\"text-[12px] text-destructive\">{error}</p>\n : <p className=\"text-[12px] text-muted-foreground\">Multiples of 16, long edge up to 3840, ratio between 1:3 and 3:1.</p>}\n <div className=\"flex items-center justify-end gap-1.5\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"rounded-md px-2.5 py-1 text-[12.5px] font-medium text-muted-foreground transition hover:bg-accent hover:text-foreground\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={apply}\n className=\"rounded-md bg-primary px-2.5 py-1 text-[12.5px] font-medium text-primary-foreground transition hover:opacity-90\"\n >\n Apply\n </button>\n </div>\n </div>\n )\n}\n\n/**\n * The audio switch. It is a toggle rather than a menu because the parameter has\n * exactly two states, and a two-row menu to say \"on\" is a menu that costs a\n * click for nothing.\n */\nexport function AudioTogglePill({ on, onToggle }: { on: boolean; onToggle: (on: boolean) => void }) {\n return (\n <button\n type=\"button\"\n aria-pressed={on}\n title=\"Audio\"\n onClick={() => onToggle(!on)}\n className={`${PILL} ${on ? '' : 'text-muted-foreground'}`}\n >\n {on\n ? <Volume2 className=\"h-4 w-4 shrink-0\" strokeWidth={1.5} />\n : <VolumeX className=\"h-4 w-4 shrink-0\" strokeWidth={1.5} />}\n <span>{on ? 'Audio on' : 'Audio off'}</span>\n </button>\n )\n}\n\n/**\n * The reference-image pill: the only control that changes which MODEL runs, so\n * the swap to the image-to-video sibling is the caller's (`onAttach`/`onRemove`)\n * and the pill only reports what is attached.\n *\n * With no `pick` seam it opens a URL form instead of a file dialog — a URL is\n * something a host with no upload endpoint can still supply, and refusing\n * anything that is not `http(s)` keeps a pasted local path from reaching the\n * provider as an unfetchable reference.\n */\nexport function ReferencePill({\n url,\n onAttach,\n onRemove,\n pick,\n bandRef,\n}: {\n url: string | null\n onAttach: (url: string) => void\n onRemove: () => void\n pick?: () => Promise<string | null>\n bandRef: RefObject<HTMLDivElement | null>\n}) {\n const [open, setOpen] = useState(false)\n const [draft, setDraft] = useState('')\n const [error, setError] = useState<string | null>(null)\n const { containerRef, triggerRef, panelRef } = usePopover(open, setOpen)\n const panelId = useId()\n useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef)\n\n if (url) {\n return (\n <div className={`${PILL} border-primary bg-primary/10 pr-1.5 text-primary hover:bg-primary/10`}>\n <img src={url} alt=\"\" className=\"h-[18px] w-[18px] shrink-0 rounded-[5px] object-cover\" />\n <span>Reference</span>\n <button\n type=\"button\"\n aria-label=\"Remove reference image\"\n onClick={onRemove}\n className=\"ml-0.5 inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full transition hover:bg-primary/20\"\n >\n <X className=\"h-3 w-3\" strokeWidth={2} />\n </button>\n </div>\n )\n }\n\n function attach() {\n const value = draft.trim()\n if (!/^https?:\\/\\//i.test(value)) {\n setError('Enter a URL starting with http:// or https://')\n return\n }\n onAttach(value)\n setDraft('')\n setError(null)\n setOpen(false)\n }\n\n return (\n <div ref={containerRef} className=\"relative inline-flex flex-none\">\n <button\n type=\"button\"\n ref={triggerRef}\n // With a `pick` seam the button opens the host's own picker, not a\n // popover — claiming `aria-haspopup` there announces a menu that never\n // appears.\n {...(pick ? {} : { 'aria-haspopup': true as const, 'aria-expanded': open, 'aria-controls': open ? panelId : undefined })}\n title=\"Reference image\"\n onClick={() => {\n if (!pick) {\n setOpen(!open)\n return\n }\n void pick().then((picked) => {\n if (picked) onAttach(picked)\n })\n }}\n className={PILL}\n >\n <ImagePlus className=\"h-4 w-4 shrink-0 text-muted-foreground\" strokeWidth={1.5} />\n <span>Reference image</span>\n </button>\n\n {!pick && (\n <PopoverSurface\n open={open}\n id={panelId}\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={MENU_PANEL}\n >\n <div className={MENU_HEADER}>Reference image</div>\n <div className=\"flex w-[260px] flex-col gap-2 p-1.5\">\n <input\n type=\"url\"\n value={draft}\n aria-label=\"Reference image URL\"\n placeholder=\"https://…\"\n onChange={(event) => {\n setDraft(event.target.value)\n setError(null)\n }}\n className=\"h-8 w-full rounded-md border border-input bg-background px-2 text-[13px]\"\n />\n {error && <p className=\"text-[12px] text-destructive\">{error}</p>}\n <div className=\"flex items-center justify-end gap-1.5\">\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n className=\"rounded-md px-2.5 py-1 text-[12.5px] font-medium text-muted-foreground transition hover:bg-accent hover:text-foreground\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={attach}\n className=\"rounded-md bg-primary px-2.5 py-1 text-[12.5px] font-medium text-primary-foreground transition hover:opacity-90\"\n >\n Attach\n </button>\n </div>\n </div>\n </PopoverSurface>\n )}\n </div>\n )\n}\n\n/**\n * The model pill — first in the band on every lane, and the only pill that is\n * always there. A model with no published option metadata renders this and\n * nothing else, which is the honest reading of \"we do not know what this model\n * takes\".\n */\nexport function ModelPill({\n models,\n value,\n displayName,\n provider,\n onSelect,\n bandRef,\n}: {\n models: readonly MediaModelOption[]\n value: string\n /** What the pill reads. Falls back to the id — including for a model the\n * catalog does not list, such as an image-to-video sibling. */\n displayName: string\n provider?: string\n onSelect: (id: string) => void\n bandRef: RefObject<HTMLDivElement | null>\n}) {\n const [open, setOpen] = useState(false)\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const panelId = useId()\n useCloseWhenScrolledOut(open, setOpen, triggerRef, bandRef)\n\n return (\n <div ref={containerRef} className=\"relative inline-flex flex-none\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-controls={open ? panelId : undefined}\n title=\"Model\"\n aria-label={`Model: ${displayName}`}\n onClick={() => setOpen(!open)}\n className={PILL}\n >\n {provider && <ProviderLogo provider={provider} size={14} />}\n <span className=\"max-w-[168px] truncate\">{displayName}</span>\n <ChevronDown className=\"h-3 w-3 shrink-0 text-muted-foreground\" />\n </button>\n\n <PopoverSurface\n open={open}\n id={panelId}\n role=\"menu\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={`${MENU_PANEL} max-w-[320px]`}\n >\n <div className={MENU_HEADER}>Model</div>\n {models.length === 0 && (\n <p className=\"px-2.5 py-2 text-[13px] text-muted-foreground\">No models are available for this media type.</p>\n )}\n {models.map((model) => (\n <button\n key={model.id}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={model.id === value}\n disabled={model.status === 'unavailable'}\n onClick={() => {\n onSelect(model.id)\n setOpen(false)\n }}\n className={`${menuRowClass(model.id === value)} disabled:opacity-50`}\n >\n {model.provider && <ProviderLogo provider={model.provider} size={14} />}\n <span className=\"truncate\">{model.name || model.id}</span>\n {model.status !== 'available' && (\n <span className=\"ml-auto shrink-0 text-[11px] capitalize text-muted-foreground\">{model.status}</span>\n )}\n {model.id === value && (\n <CheckGlyph className={`${model.status === 'available' ? 'ml-auto ' : ''}h-3.5 w-3.5 shrink-0 text-primary`} />\n )}\n </button>\n ))}\n </PopoverSurface>\n </div>\n )\n}\n\n/**\n * How a wire value reads, with the wire value itself untouched underneath.\n *\n * Keyed on the PARAMETER because the same scalar means different things across\n * them: `5` is \"5s\" of video and `2` is \"×2\" images. Anything this table has no\n * rule for reads as itself with a capital letter — never as an invented word,\n * so `std` renders \"Std\" and not a \"Standard\" nobody published.\n */\nexport function optionValueLabel(param: string, value: ModelOptionValue): string {\n if (typeof value === 'boolean') return value ? 'On' : 'Off'\n const text = String(value)\n if (param === 'n') return `×${text}`\n if (param === 'speed') return `${text}×`\n if (text === 'auto') return 'Auto'\n if (param === 'duration') return /^\\d+(\\.\\d+)?$/.test(text) ? `${text}s` : text\n if (param === 'size') {\n const size = /^(\\d+)x(\\d+)$/.exec(text)\n return size ? `${size[1]}×${size[2]}` : text\n }\n if (param === 'resolution' || param === 'aspect_ratio') return text\n return text.charAt(0).toUpperCase() + text.slice(1)\n}\n","import { Info } from 'lucide-react'\n\n/**\n * The one thing a chat-shaped generator has to say about itself: each prompt is\n * a fresh generation, and nothing carries over from the last one.\n *\n * A chat card sets the expectation that the thing on the other side remembers\n * the conversation. This one does not, and the cost of learning that by\n * experiment is a wasted generation. So the notice sits above the composer as a\n * statement of fact rather than a warning — `text-foreground`, not muted, since\n * a rule the reader must know before typing cannot be the quietest text on the\n * page.\n *\n * The icon is INLINE in the text flow rather than a flex sibling, so a narrow\n * viewport wraps it with the sentence instead of stranding a lone glyph beside\n * a two-line block.\n */\nexport function GenerationNoticeChip({ className }: { className?: string }) {\n return (\n <p\n className={`inline-block rounded-full border px-3.5 py-1.5 text-center text-[12.5px] font-medium leading-relaxed text-foreground shadow-sm ${className ?? ''}`}\n style={{ background: 'var(--studio-notice-bg)', borderColor: 'var(--studio-notice-border)' }}\n >\n <Info className=\"mr-1.5 inline-block h-[14px] w-[14px] align-[-2px] text-primary\" strokeWidth={1.5} aria-hidden />\n Each prompt starts a new generation — this chat does not remember the last one.\n </p>\n )\n}\n","import {\n Check,\n Download,\n FileText,\n FolderOpen,\n FolderPlus,\n Pause,\n Play,\n Trash2,\n} from 'lucide-react'\nimport {\n type CSSProperties,\n type JSX,\n type KeyboardEvent,\n type MouseEvent,\n useEffect,\n useState,\n} from 'react'\n\nimport { hashSeed, previewWaveformBars } from '../studio/audio-preview'\nimport {\n generationError,\n generationStatus,\n generationVaultPath,\n type Generation,\n} from '../studio/generation'\nimport type { StudioMediaActions, VaultSaveResult } from '../studio/ports'\nimport { usePopover } from '../web-react/controls'\nimport { VaultPathPopover } from './vault-path-popover'\nimport { downloadGenerationsViaAnchor } from './download-generations'\nimport { useStudioPlayback } from './studio-playback'\n\nexport interface MediaTileProps {\n generation: Generation\n context: 'home' | 'generation' | 'history'\n onOpen: (generation: Generation) => void\n actions?: StudioMediaActions\n /** Generation screen passes the batch aspect; omitted → square. */\n aspectRatio?: number\n /** 26 for grids (default), 72 for wide generation/viewer tiles. */\n waveformBars?: number\n selectMode?: boolean\n selected?: boolean\n onToggleSelect?: (id: string) => void\n onRequestDelete?: (generation: Generation) => void\n onSaved?: (results: readonly VaultSaveResult[]) => void\n className?: string\n style?: CSSProperties\n}\n\ntype TileStyle = CSSProperties & { '--r'?: number }\n\nfunction stop(event: MouseEvent<HTMLElement>): void {\n event.stopPropagation()\n}\n\nexport function MediaTile({\n generation,\n context,\n onOpen,\n actions,\n aspectRatio,\n waveformBars = 26,\n selectMode = false,\n selected = false,\n onToggleSelect,\n onRequestDelete,\n onSaved,\n className,\n style,\n}: MediaTileProps): JSX.Element {\n const playback = useStudioPlayback()\n const [waveNode, setWaveNode] = useState<HTMLDivElement | null>(null)\n const [saveOpen, setSaveOpen] = useState(false)\n const [saving, setSaving] = useState(false)\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(saveOpen, setSaveOpen)\n const vaultPath = generationVaultPath(generation)\n const status = generationStatus(generation)\n const isSpeech = generation.type === 'speech'\n const isPlaying = isSpeech && playback.activeId === generation.id && playback.playing\n\n useEffect(() => playback.registerPositionNode(generation.id, waveNode), [\n generation.id,\n playback.registerPositionNode,\n waveNode,\n ])\n\n const classes = [\n 'studio-tile relative isolate m-0 cursor-pointer overflow-hidden rounded-none border-0 bg-accent',\n aspectRatio === undefined ? 'aspect-square' : '',\n 'focus-visible:[outline-offset:-2px]',\n isSpeech ? 'studio-has-player' : '',\n selectMode ? 'studio-selectmode' : '',\n selected ? 'outline outline-2 -outline-offset-2 outline-primary' : '',\n className ?? '',\n ].filter(Boolean).join(' ')\n const tileStyle: TileStyle = aspectRatio === undefined\n ? { ...style }\n : { ...style, aspectRatio, '--r': aspectRatio }\n\n function activate(): void {\n if (selectMode) onToggleSelect?.(generation.id)\n else onOpen(generation)\n }\n\n function onKeyDown(event: KeyboardEvent<HTMLElement>): void {\n if (event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' ')) return\n event.preventDefault()\n activate()\n }\n\n async function save(path: string): Promise<void> {\n if (!actions?.save) return\n setSaving(true)\n try {\n const results = await actions.save({ generations: [generation], path })\n setSaveOpen(false)\n onSaved?.(results)\n } finally {\n setSaving(false)\n }\n }\n\n const bars = isSpeech ? previewWaveformBars(generation.id, waveformBars) : []\n const hue = hashSeed(generation.id) % 360\n const secondHue = (hue + 34) % 360\n\n return (\n <figure\n role=\"button\"\n tabIndex={0}\n aria-label={`${generation.prompt} — open`}\n className={classes}\n style={tileStyle}\n data-selectable={context === 'history'}\n data-selected={selected}\n onClick={activate}\n onKeyDown={onKeyDown}\n >\n <div className=\"absolute inset-0\">\n {status === 'pending' || status === 'running' ? (\n <div className=\"studio-skeleton absolute inset-0\" />\n ) : status === 'failed' ? (\n <div className=\"grid h-full w-full place-items-center bg-accent p-3 text-center text-[12px] text-destructive\">\n {generationError(generation) ?? 'Generation failed'}\n </div>\n ) : generation.result === null ? (\n <div className=\"studio-skeleton absolute inset-0\" />\n ) : generation.type === 'image' ? (\n <img\n src={generation.result}\n alt=\"\"\n className=\"h-full w-full object-cover\"\n loading=\"lazy\"\n draggable={false}\n />\n ) : generation.type === 'video' || generation.type === 'avatar' ? (\n <video\n src={generation.result}\n muted\n playsInline\n preload=\"metadata\"\n className=\"h-full w-full object-cover\"\n />\n ) : generation.type === 'transcription' ? (\n <div className=\"grid h-full w-full place-items-center bg-muted text-muted-foreground\">\n <FileText size={20} strokeWidth={1.5} />\n </div>\n ) : isSpeech ? (\n <div\n className=\"absolute inset-0\"\n style={{ background: `linear-gradient(158deg, hsl(${hue} 38% 24%), hsl(${secondHue} 46% 13%))` }}\n >\n <div\n className=\"studio-wave-layers\"\n ref={setWaveNode}\n style={{ color: `hsl(${hue} 92% 74%)` }}\n >\n <div className=\"studio-wave studio-wave-base\">\n {bars.map((bar, index) => (\n <i key={index} style={{ height: `${bar.heightPct}%`, opacity: bar.opacity }} />\n ))}\n </div>\n <div className=\"studio-wave studio-wave-play\">\n {bars.map((bar, index) => (\n <i key={index} style={{ height: `${bar.heightPct}%`, opacity: bar.opacity }} />\n ))}\n </div>\n </div>\n </div>\n ) : null}\n </div>\n\n <div className=\"studio-tile-scrim absolute inset-0\" />\n\n <div className=\"studio-tile-actions absolute right-2 top-2 flex gap-[5px]\">\n <button\n type=\"button\"\n className=\"studio-ibtn relative grid h-[30px] w-[30px] place-items-center rounded-full\"\n data-tip=\"Download\"\n aria-label=\"Download\"\n onClick={(event) => {\n stop(event)\n void (actions?.download ?? downloadGenerationsViaAnchor)([generation])\n }}\n >\n <Download size={15} strokeWidth={1.5} />\n </button>\n\n {!vaultPath && actions?.save && (\n <div ref={containerRef}>\n <button\n {...triggerProps}\n type=\"button\"\n className=\"studio-ibtn relative grid h-[30px] w-[30px] place-items-center rounded-full\"\n data-tip=\"Save to vault\"\n aria-label=\"Save to vault\"\n onClick={(event) => {\n stop(event)\n setSaveOpen((open) => !open)\n }}\n >\n <FolderPlus size={15} strokeWidth={1.5} />\n </button>\n <VaultPathPopover\n open={saveOpen}\n triggerRef={triggerRef}\n panelRef={panelRef}\n generations={[generation]}\n onSubmit={save}\n onCancel={() => setSaveOpen(false)}\n pending={saving}\n />\n </div>\n )}\n\n {vaultPath && actions?.vaultHref && (\n <a\n href={actions.vaultHref(vaultPath)}\n className=\"studio-ibtn relative grid h-[30px] w-[30px] place-items-center rounded-full\"\n data-tip=\"View in vault\"\n aria-label=\"View in vault\"\n onClick={(event) => {\n stop(event)\n if (actions.onOpenVault) {\n event.preventDefault()\n actions.onOpenVault(generation)\n }\n }}\n >\n <FolderOpen size={15} strokeWidth={1.5} />\n </a>\n )}\n {vaultPath && !actions?.vaultHref && actions?.onOpenVault && (\n <button\n type=\"button\"\n className=\"studio-ibtn relative grid h-[30px] w-[30px] place-items-center rounded-full\"\n data-tip=\"View in vault\"\n aria-label=\"View in vault\"\n onClick={(event) => {\n stop(event)\n actions.onOpenVault?.(generation)\n }}\n >\n <FolderOpen size={15} strokeWidth={1.5} />\n </button>\n )}\n\n {actions?.remove && onRequestDelete && (\n <button\n type=\"button\"\n className=\"studio-ibtn studio-ibtn-danger relative grid h-[30px] w-[30px] place-items-center rounded-full\"\n data-tip=\"Delete\"\n aria-label=\"Delete\"\n onClick={(event) => {\n stop(event)\n onRequestDelete(generation)\n }}\n >\n <Trash2 size={15} strokeWidth={1.5} />\n </button>\n )}\n </div>\n\n <div className=\"studio-tile-badges absolute bottom-2 left-2 flex items-center gap-1.5\">\n {(generation.type === 'video' || generation.type === 'avatar') && (\n <span className=\"studio-chip-dark grid h-[22px] w-[22px] place-items-center rounded-full\" aria-hidden=\"true\">\n <Play size={11} strokeWidth={1.5} fill=\"currentColor\" />\n </span>\n )}\n {isSpeech && (\n <button\n type=\"button\"\n className=\"studio-play-btn studio-chip-dark grid h-[22px] w-[22px] place-items-center rounded-full\"\n aria-label={isPlaying ? `Pause ${generation.prompt}` : `Play ${generation.prompt}`}\n onClick={(event) => {\n stop(event)\n playback.toggle(generation)\n }}\n >\n {isPlaying\n ? <Pause size={11} strokeWidth={1.5} fill=\"currentColor\" />\n : <Play size={11} strokeWidth={1.5} fill=\"currentColor\" />}\n </button>\n )}\n {vaultPath && (\n <span className=\"studio-chip-dark inline-flex h-5 items-center gap-1 rounded-full pl-1.5 pr-2 text-[11px]\">\n <FolderOpen size={12} strokeWidth={1.5} />\n In vault\n </span>\n )}\n </div>\n\n {context === 'history' && (\n <button\n type=\"button\"\n className={`studio-tile-sel studio-chip-dark absolute left-2 top-2 h-6 w-6 place-items-center rounded-full ${selected ? '!bg-primary border-primary' : ''}`}\n aria-pressed={selected}\n aria-label=\"Select this item\"\n onClick={(event) => {\n stop(event)\n onToggleSelect?.(generation.id)\n }}\n >\n <Check\n size={13}\n strokeWidth={1.5}\n className={selected ? 'opacity-100' : 'opacity-0'}\n />\n </button>\n )}\n\n <figcaption className=\"studio-tile-prompt absolute inset-x-[11px] bottom-[9px] m-0 truncate text-[12px] text-white\">\n {generation.prompt}\n </figcaption>\n </figure>\n )\n}\n","import { useEffect, useId, useRef, useState, type FormEvent, type JSX, type RefObject } from 'react'\nimport { defaultVaultPathFor, normalizeVaultPath, type Generation } from '../studio'\nimport { OVERLAY_SHADOW, PopoverSurface } from '../web-react/controls'\n\nexport interface VaultPathPopoverProps {\n open: boolean\n triggerRef: RefObject<HTMLElement | null>\n panelRef: RefObject<HTMLDivElement | null>\n generations: readonly Generation[]\n onSubmit: (path: string) => void | Promise<void>\n onCancel: () => void\n pending?: boolean\n}\n\nexport function VaultPathPopover({\n open,\n triggerRef,\n panelRef,\n generations,\n onSubmit,\n onCancel,\n pending = false,\n}: VaultPathPopoverProps): JSX.Element | null {\n const inputRef = useRef<HTMLInputElement>(null)\n const inputId = useId()\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n if (!open) return\n setError(null)\n inputRef.current?.focus()\n inputRef.current?.select()\n }, [open])\n\n if (!open) return null\n\n async function submit(event: FormEvent) {\n event.preventDefault()\n const normalized = normalizeVaultPath(inputRef.current?.value ?? '')\n if (!normalized) {\n setError('Enter a folder path inside the vault.')\n return\n }\n setError(null)\n try {\n await onSubmit(normalized)\n } catch {\n setError('Could not save to vault. Try again.')\n }\n }\n\n const count = generations.length\n const defaultPath = defaultVaultPathFor(generations)\n return (\n <PopoverSurface\n open={open}\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={`flex w-[262px] flex-col overflow-y-auto rounded-xl border border-border bg-popover p-1.5 text-popover-foreground ${OVERLAY_SHADOW}`}\n >\n <form onSubmit={submit}>\n <label htmlFor={inputId} className=\"block px-0.5 pb-[7px] pt-0.5 text-[12px] text-muted-foreground\">\n Save {count} item{count > 1 ? 's' : ''} to\n </label>\n <input\n key={defaultPath}\n ref={inputRef}\n id={inputId}\n defaultValue={defaultPath}\n spellCheck={false}\n autoComplete=\"off\"\n className=\"h-8 w-full rounded-lg border border-input bg-muted px-2.5 text-[13px] outline-none focus:border-primary focus:ring-[3px] focus:ring-ring/30\"\n />\n {error && <p className=\"pt-1.5 text-[12px] text-destructive\">{error}</p>}\n <p className=\"pt-2 text-[11.5px] text-muted-foreground\">Media stays in Studio until you save it into the vault.</p>\n <div className=\"flex justify-end gap-2 pt-2.5\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"h-[30px] rounded-full border border-border px-3.5 text-[13px] hover:bg-accent\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={pending}\n className=\"h-[30px] rounded-full bg-primary px-3.5 text-[13px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n Save\n </button>\n </div>\n </form>\n </PopoverSurface>\n )\n}\n","import type { DownloadGenerations } from '../studio/ports'\n\n/** Same-origin anchor download, staggered so the browser does not swallow\n * every request after the first. Default when no `download` port is given. */\nexport const downloadGenerationsViaAnchor: DownloadGenerations = (generations) => {\n if (typeof document === 'undefined') return\n\n const downloadable = generations.filter((generation) => generation.result !== null)\n downloadable.forEach((generation, index) => {\n setTimeout(() => {\n if (generation.result === null) return\n const vaultPath = generation.metadata?.vaultPath\n const filename = typeof vaultPath === 'string' && vaultPath.trim()\n ? vaultPath.trim().split('/').filter(Boolean).at(-1) ?? `${generation.type}-${generation.id}`\n : `${generation.type}-${generation.id}`\n const anchor = document.createElement('a')\n anchor.href = generation.result\n anchor.download = filename\n document.body.appendChild(anchor)\n anchor.click()\n anchor.remove()\n }, index * 150)\n })\n}\n","import {\n createContext,\n type JSX,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport type { Generation } from '../studio/generation'\n\nexport interface StudioAudioElementLike {\n src: string\n currentTime: number\n readonly duration: number\n play(): Promise<void> | void\n pause(): void\n addEventListener(type: string, listener: () => void): void\n removeEventListener(type: string, listener: () => void): void\n}\n\nexport interface StudioPlayback {\n activeId: string | null\n playing: boolean\n durationSeconds: number\n play(generation: Generation): void\n pause(): void\n toggle(generation: Generation): void\n stop(): void\n seekTo(generation: Generation, seconds: number): void\n seekBy(seconds: number): void\n getPositionSeconds(): number\n registerPositionNode(id: string, node: HTMLElement | null): () => void\n registerTimeNode(node: HTMLElement | null): () => void\n}\n\nconst StudioPlaybackContext = createContext<StudioPlayback | null>(null)\n\nconst createBrowserAudioElement = (): StudioAudioElementLike => new Audio()\n\nexport function formatClock(seconds: number): string {\n const wholeSeconds = Math.max(0, Math.floor(Number.isFinite(seconds) ? seconds : 0))\n return `${Math.floor(wholeSeconds / 60)}:${String(wholeSeconds % 60).padStart(2, '0')}`\n}\n\nfunction metadataDuration(generation: Generation): number {\n const duration = generation.metadata?.durationSeconds\n return typeof duration === 'number' && Number.isFinite(duration) ? Math.max(0, duration) : 0\n}\n\nfunction clampPosition(seconds: number, duration: number): number {\n const finiteSeconds = Number.isFinite(seconds) ? seconds : 0\n return Math.max(0, duration > 0 ? Math.min(finiteSeconds, duration) : finiteSeconds)\n}\n\nexport function StudioPlaybackProvider(props: {\n children: ReactNode\n createAudioElement?: () => StudioAudioElementLike\n}): JSX.Element {\n const [activeId, setActiveId] = useState<string | null>(null)\n const [playing, setPlaying] = useState(false)\n const [durationSeconds, setDurationSeconds] = useState(0)\n\n const activeIdRef = useRef<string | null>(null)\n const playingRef = useRef(false)\n const durationRef = useRef(0)\n const createAudioElementRef = useRef(props.createAudioElement ?? createBrowserAudioElement)\n createAudioElementRef.current = props.createAudioElement ?? createBrowserAudioElement\n const audioRef = useRef<StudioAudioElementLike | null>(null)\n const audioListenersRef = useRef<{\n loadedmetadata: () => void\n ended: () => void\n } | null>(null)\n const positionNodesRef = useRef(new Map<string, Set<HTMLElement>>())\n const timeNodeRef = useRef<HTMLElement | null>(null)\n const animationFrameRef = useRef<number | null>(null)\n const playAttemptRef = useRef(0)\n\n const setActive = useCallback((id: string | null) => {\n activeIdRef.current = id\n setActiveId(id)\n }, [])\n\n const setIsPlaying = useCallback((value: boolean) => {\n playingRef.current = value\n setPlaying(value)\n }, [])\n\n const setDuration = useCallback((value: number) => {\n const normalized = Number.isFinite(value) ? Math.max(0, value) : 0\n durationRef.current = normalized\n setDurationSeconds(normalized)\n }, [])\n\n const paintAll = useCallback(() => {\n const currentId = activeIdRef.current\n const position = currentId ? Math.max(0, audioRef.current?.currentTime ?? 0) : 0\n const duration = durationRef.current\n const fraction = duration > 0 ? Math.min(1, position / duration) : 0\n\n for (const [id, nodes] of positionNodesRef.current) {\n const isActive = id === currentId && (playingRef.current || position > 0)\n for (const node of nodes) {\n node.style.setProperty('--pos', isActive ? fraction.toFixed(4) : '0')\n if (isActive) node.dataset.active = 'true'\n else delete node.dataset.active\n }\n }\n if (timeNodeRef.current) timeNodeRef.current.textContent = formatClock(position)\n }, [])\n\n const cancelAnimation = useCallback(() => {\n if (animationFrameRef.current !== null && typeof cancelAnimationFrame === 'function') {\n cancelAnimationFrame(animationFrameRef.current)\n }\n animationFrameRef.current = null\n }, [])\n\n const startAnimation = useCallback(() => {\n if (animationFrameRef.current !== null || typeof requestAnimationFrame !== 'function') return\n const frame = () => {\n animationFrameRef.current = null\n paintAll()\n if (playingRef.current) animationFrameRef.current = requestAnimationFrame(frame)\n }\n animationFrameRef.current = requestAnimationFrame(frame)\n }, [paintAll])\n\n const ensureAudio = useCallback(() => {\n if (audioRef.current) return audioRef.current\n const audio = createAudioElementRef.current()\n const onLoadedMetadata = () => {\n if (Number.isFinite(audio.duration)) setDuration(audio.duration)\n paintAll()\n }\n const onEnded = () => {\n playAttemptRef.current += 1\n audio.currentTime = 0\n setIsPlaying(false)\n cancelAnimation()\n paintAll()\n }\n audio.addEventListener('loadedmetadata', onLoadedMetadata)\n audio.addEventListener('ended', onEnded)\n audioListenersRef.current = { loadedmetadata: onLoadedMetadata, ended: onEnded }\n audioRef.current = audio\n return audio\n }, [cancelAnimation, paintAll, setDuration, setIsPlaying])\n\n const adopt = useCallback((generation: Generation) => {\n const audio = ensureAudio()\n if (activeIdRef.current !== generation.id) {\n audio.src = generation.result ?? ''\n audio.currentTime = 0\n setDuration(metadataDuration(generation))\n setActive(generation.id)\n }\n return audio\n }, [ensureAudio, setActive, setDuration])\n\n const play = useCallback((generation: Generation) => {\n if (!generation.result) return\n const audio = adopt(generation)\n const attempt = ++playAttemptRef.current\n setIsPlaying(false)\n cancelAnimation()\n paintAll()\n\n try {\n const result = audio.play()\n if (result && typeof result.then === 'function') {\n void result.then(() => {\n if (playAttemptRef.current !== attempt || activeIdRef.current !== generation.id) return\n setIsPlaying(true)\n paintAll()\n startAnimation()\n }, () => {\n if (playAttemptRef.current !== attempt) return\n setIsPlaying(false)\n cancelAnimation()\n paintAll()\n })\n } else {\n setIsPlaying(true)\n paintAll()\n startAnimation()\n }\n } catch {\n if (playAttemptRef.current === attempt) {\n setIsPlaying(false)\n cancelAnimation()\n paintAll()\n }\n }\n }, [adopt, cancelAnimation, paintAll, setIsPlaying, startAnimation])\n\n const pause = useCallback(() => {\n playAttemptRef.current += 1\n audioRef.current?.pause()\n setIsPlaying(false)\n cancelAnimation()\n paintAll()\n }, [cancelAnimation, paintAll, setIsPlaying])\n\n const stop = useCallback(() => {\n playAttemptRef.current += 1\n audioRef.current?.pause()\n if (audioRef.current) audioRef.current.currentTime = 0\n setIsPlaying(false)\n setActive(null)\n cancelAnimation()\n paintAll()\n }, [cancelAnimation, paintAll, setActive, setIsPlaying])\n\n const toggle = useCallback((generation: Generation) => {\n if (activeIdRef.current === generation.id && playingRef.current) pause()\n else play(generation)\n }, [pause, play])\n\n const seekTo = useCallback((generation: Generation, seconds: number) => {\n if (!generation.result) return\n const changedTrack = activeIdRef.current !== generation.id\n const audio = adopt(generation)\n if (changedTrack) {\n playAttemptRef.current += 1\n setIsPlaying(false)\n cancelAnimation()\n }\n audio.currentTime = clampPosition(seconds, durationRef.current)\n paintAll()\n }, [adopt, cancelAnimation, paintAll, setIsPlaying])\n\n const seekBy = useCallback((seconds: number) => {\n if (!activeIdRef.current || !audioRef.current) return\n audioRef.current.currentTime = clampPosition(audioRef.current.currentTime + seconds, durationRef.current)\n paintAll()\n }, [paintAll])\n\n const getPositionSeconds = useCallback(() => audioRef.current?.currentTime ?? 0, [])\n\n const registerPositionNode = useCallback((id: string, node: HTMLElement | null) => {\n if (!node) {\n const nodes = positionNodesRef.current.get(id)\n if (nodes) {\n for (const registered of nodes) {\n registered.style.setProperty('--pos', '0')\n delete registered.dataset.active\n }\n }\n positionNodesRef.current.delete(id)\n return () => {}\n }\n const nodes = positionNodesRef.current.get(id) ?? new Set<HTMLElement>()\n nodes.add(node)\n positionNodesRef.current.set(id, nodes)\n paintAll()\n return () => {\n nodes.delete(node)\n if (nodes.size === 0) positionNodesRef.current.delete(id)\n }\n }, [paintAll])\n\n const registerTimeNode = useCallback((node: HTMLElement | null) => {\n timeNodeRef.current = node\n paintAll()\n return () => {\n if (timeNodeRef.current === node) timeNodeRef.current = null\n }\n }, [paintAll])\n\n useEffect(() => {\n paintAll()\n }, [activeId, durationSeconds, paintAll, playing])\n\n useEffect(() => () => {\n playAttemptRef.current += 1\n cancelAnimation()\n const audio = audioRef.current\n if (!audio) return\n audio.pause()\n const listeners = audioListenersRef.current\n if (listeners) {\n audio.removeEventListener('loadedmetadata', listeners.loadedmetadata)\n audio.removeEventListener('ended', listeners.ended)\n }\n }, [cancelAnimation])\n\n const value = useMemo<StudioPlayback>(() => ({\n activeId,\n playing,\n durationSeconds,\n play,\n pause,\n toggle,\n stop,\n seekTo,\n seekBy,\n getPositionSeconds,\n registerPositionNode,\n registerTimeNode,\n }), [\n activeId,\n durationSeconds,\n getPositionSeconds,\n pause,\n play,\n playing,\n registerPositionNode,\n registerTimeNode,\n seekBy,\n seekTo,\n stop,\n toggle,\n ])\n\n return <StudioPlaybackContext.Provider value={value}>{props.children}</StudioPlaybackContext.Provider>\n}\n\nexport function useStudioPlayback(): StudioPlayback {\n const playback = useContext(StudioPlaybackContext)\n if (!playback) throw new Error('useStudioPlayback must be used within a StudioPlaybackProvider')\n return playback\n}\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type JSX,\n type KeyboardEvent,\n type MouseEvent,\n type PointerEvent,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { Download, FolderOpen, FolderPlus, Pause, Play, Trash2, X } from 'lucide-react'\n\nimport {\n generationAspectRatio,\n generationError,\n generationSpecSegments,\n generationStatus,\n generationVaultPath,\n hashSeed,\n previewWaveformBars,\n relativeTime,\n WIDE_WAVEFORM_BARS,\n type Generation,\n type StudioMediaActions,\n type VaultSaveResult,\n} from '../studio'\nimport { OVERLAY_SHADOW, POPOVER_SURFACE_ATTR, usePopover } from '../web-react/controls'\nimport { downloadGenerationsViaAnchor } from './download-generations'\nimport { formatClock, useStudioPlayback } from './studio-playback'\nimport { VaultPathPopover } from './vault-path-popover'\n\nexport interface MediaViewerModalProps {\n generation: Generation | null\n onClose: () => void\n actions?: StudioMediaActions\n /** Screens own the delete confirm; absent hides the Delete button. */\n onRequestDelete?: (generation: Generation) => void\n /** After a successful save from the footer popover. */\n onSaved?: (results: readonly VaultSaveResult[]) => void\n}\n\nconst TYPE_LABELS: Record<string, string> = {\n image: 'Image',\n video: 'Video',\n speech: 'Audio',\n avatar: 'Avatar',\n transcription: 'Transcript',\n}\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'textarea:not([disabled])',\n 'select:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(', ')\n\nconst footerButtonClass =\n 'inline-flex h-8 items-center gap-1.5 rounded-full border border-border px-3 text-[12.5px] font-medium hover:bg-accent'\n\nfunction hasOpenPopover(): boolean {\n return typeof document !== 'undefined' && document.querySelector(`[${POPOVER_SURFACE_ATTR}]`) !== null\n}\n\nfunction Waveform({ generation }: { generation: Generation }): JSX.Element {\n const { registerPositionNode } = useStudioPlayback()\n const layersRef = useRef<HTMLDivElement>(null)\n const bars = useMemo(\n () => previewWaveformBars(generation.id, WIDE_WAVEFORM_BARS),\n [generation.id],\n )\n const hue = useMemo(() => hashSeed(generation.id) % 360, [generation.id])\n\n useEffect(() => registerPositionNode(generation.id, layersRef.current), [generation.id, registerPositionNode])\n\n const renderBars = () => bars.map((bar, index) => (\n <i key={index} style={{ height: `${bar.heightPct}%`, opacity: bar.opacity }} />\n ))\n return (\n <div\n className=\"absolute inset-0\"\n style={{ background: `linear-gradient(158deg, hsl(${hue} 38% 24%), hsl(${(hue + 34) % 360} 46% 13%))` }}\n >\n <div ref={layersRef} className=\"studio-wave-layers\" style={{ color: `hsl(${hue} 92% 74%)` }}>\n <div className=\"studio-wave studio-wave-base\">{renderBars()}</div>\n <div className=\"studio-wave studio-wave-play\">{renderBars()}</div>\n </div>\n </div>\n )\n}\n\nfunction AudioTransport({ generation }: { generation: Generation }): JSX.Element {\n const playback = useStudioPlayback()\n const elapsedRef = useRef<HTMLSpanElement>(null)\n const seekRef = useRef<HTMLDivElement>(null)\n const trackRef = useRef<HTMLDivElement>(null)\n const pointerIdRef = useRef<number | null>(null)\n const [ariaPosition, setAriaPosition] = useState(0)\n const isPlaying = playback.activeId === generation.id && playback.playing\n const duration = playback.activeId === generation.id ? playback.durationSeconds : 0\n\n useEffect(() => playback.registerTimeNode(elapsedRef.current), [playback.registerTimeNode])\n useEffect(\n () => playback.registerPositionNode(generation.id, seekRef.current),\n [generation.id, playback.registerPositionNode],\n )\n useEffect(() => {\n setAriaPosition(playback.activeId === generation.id ? playback.getPositionSeconds() : 0)\n }, [generation.id, playback.activeId, playback.durationSeconds, playback.getPositionSeconds, playback.playing])\n\n const syncAriaPosition = useCallback(() => {\n setAriaPosition(playback.activeId === generation.id ? playback.getPositionSeconds() : 0)\n }, [generation.id, playback])\n\n function toggle() {\n playback.toggle(generation)\n syncAriaPosition()\n }\n\n function seekFromClientX(clientX: number) {\n const rect = trackRef.current?.getBoundingClientRect()\n if (!rect || rect.width <= 0 || duration <= 0) return\n const fraction = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width))\n playback.seekTo(generation, fraction * duration)\n setAriaPosition(fraction * duration)\n }\n\n function onPointerDown(event: PointerEvent<HTMLDivElement>) {\n event.currentTarget.focus()\n pointerIdRef.current = event.pointerId\n event.currentTarget.setPointerCapture?.(event.pointerId)\n seekFromClientX(event.clientX)\n }\n\n function onPointerMove(event: PointerEvent<HTMLDivElement>) {\n if (pointerIdRef.current !== event.pointerId) return\n seekFromClientX(event.clientX)\n }\n\n function endPointer(event: PointerEvent<HTMLDivElement>) {\n if (pointerIdRef.current !== event.pointerId) return\n event.currentTarget.releasePointerCapture?.(event.pointerId)\n pointerIdRef.current = null\n syncAriaPosition()\n }\n\n function onKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n event.preventDefault()\n playback.seekBy(event.key === 'ArrowLeft' ? -5 : 5)\n syncAriaPosition()\n } else if (event.key === ' ' || event.code === 'Space') {\n event.preventDefault()\n toggle()\n }\n }\n\n const position = Math.max(0, duration > 0 ? Math.min(ariaPosition, duration) : ariaPosition)\n return (\n <div className=\"mt-3.5 flex items-center gap-3 px-0.5\">\n <button\n type=\"button\"\n aria-label={isPlaying ? 'Pause' : 'Play'}\n onClick={toggle}\n className=\"grid h-[38px] w-[38px] shrink-0 place-items-center rounded-full bg-primary text-primary-foreground shadow-raised hover:brightness-110\"\n >\n {isPlaying\n ? <Pause size={15} strokeWidth={1.5} fill=\"currentColor\" />\n : <Play size={15} strokeWidth={1.5} fill=\"currentColor\" />}\n </button>\n <span ref={elapsedRef} className=\"min-w-8 text-[12px] text-muted-foreground tabular-nums\">\n {duration > 0 ? '0:00' : '--:--'}\n </span>\n <div\n ref={seekRef}\n className=\"studio-seek relative flex h-5 min-w-0 flex-1 cursor-pointer items-center\"\n role=\"slider\"\n tabIndex={0}\n aria-label=\"Seek\"\n aria-valuemin={0}\n aria-valuemax={duration}\n aria-valuenow={position}\n aria-valuetext={duration > 0 ? formatClock(position) : '--:--'}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endPointer}\n onPointerCancel={endPointer}\n onKeyDown={onKeyDown}\n >\n <div ref={trackRef} className=\"relative h-1 w-full overflow-hidden rounded-full bg-accent\">\n <div className=\"studio-seek-fill absolute inset-y-0 left-0 bg-primary\" />\n </div>\n <div className=\"studio-seek-thumb absolute h-[13px] w-[13px] -translate-x-1/2 rounded-full bg-primary ring-2 ring-card\" />\n </div>\n <span className=\"text-[12px] text-muted-foreground tabular-nums\">\n {duration > 0 ? formatClock(duration) : '--:--'}\n </span>\n </div>\n )\n}\n\nexport function MediaViewerModal({\n generation,\n onClose,\n actions,\n onRequestDelete,\n onSaved,\n}: MediaViewerModalProps): JSX.Element | null {\n const playback = useStudioPlayback()\n const stopPlayback = playback.stop\n const panelRef = useRef<HTMLDivElement>(null)\n const closeRef = useRef<HTMLButtonElement>(null)\n const priorFocusRef = useRef<HTMLElement | null>(null)\n const openSessionRef = useRef(false)\n const [saveOpen, setSaveOpen] = useState(false)\n const [savePending, setSavePending] = useState(false)\n const popover = usePopover(saveOpen, setSaveOpen)\n const open = generation !== null\n\n const stopAndRestore = useCallback(() => {\n if (!openSessionRef.current) return\n openSessionRef.current = false\n stopPlayback()\n const priorFocus = priorFocusRef.current\n if (priorFocus?.isConnected) priorFocus.focus()\n }, [stopPlayback])\n\n const close = useCallback(() => {\n setSaveOpen(false)\n stopAndRestore()\n onClose()\n }, [onClose, stopAndRestore])\n\n useEffect(() => {\n if (!open) return\n priorFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null\n openSessionRef.current = true\n closeRef.current?.focus()\n return stopAndRestore\n }, [open, stopAndRestore])\n\n useEffect(() => {\n if (!open) return\n function onDocumentKeyDown(event: globalThis.KeyboardEvent) {\n if (event.key !== 'Escape' || event.defaultPrevented || hasOpenPopover()) return\n event.preventDefault()\n close()\n }\n document.addEventListener('keydown', onDocumentKeyDown)\n return () => document.removeEventListener('keydown', onDocumentKeyDown)\n }, [close, open])\n\n if (!generation || typeof document === 'undefined') return null\n\n const ratio = generationAspectRatio(generation)\n const status = generationStatus(generation)\n const vaultPath = generationVaultPath(generation)\n const metaSegments = [\n TYPE_LABELS[generation.type] ?? generation.type,\n generation.model,\n ...generationSpecSegments(generation),\n generation.createdAt ? relativeTime(generation.createdAt) : null,\n vaultPath,\n ].filter((segment): segment is string => typeof segment === 'string' && segment.length > 0)\n\n function onPanelKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n if (event.key !== 'Tab' || hasOpenPopover()) return\n const focusable = panelRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n if (!focusable || focusable.length === 0) return\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = document.activeElement\n if (event.shiftKey && active === first) {\n event.preventDefault()\n last?.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first?.focus()\n }\n }\n\n async function save(path: string) {\n if (!actions?.save || !generation) return\n setSavePending(true)\n try {\n const results = await actions.save({ generations: [generation], path })\n setSaveOpen(false)\n onSaved?.(results)\n } finally {\n setSavePending(false)\n }\n }\n\n function viewVault(event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) {\n if (!actions?.onOpenVault || !generation) return\n event.preventDefault()\n actions.onOpenVault(generation)\n }\n\n const mediaStyle: CSSProperties = generation.type === 'transcription'\n ? { width: '100%' }\n : {\n width: `min(100%, calc(70vh * ${ratio}))`,\n aspectRatio: ratio,\n }\n const vaultHref = vaultPath && actions?.vaultHref ? actions.vaultHref(vaultPath) : null\n const viewControl = vaultPath && (vaultHref || actions?.onOpenVault)\n const modal = (\n <div\n className=\"studio-layer-viewer studio-backdrop fixed inset-0 grid place-items-center p-6\"\n onMouseDown={(event) => { if (event.target === event.currentTarget) close() }}\n >\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Media detail\"\n onKeyDown={onPanelKeyDown}\n className={`studio-rise relative max-h-[92vh] w-[min(780px,100%)] overflow-auto rounded-[14px] border border-border bg-card p-3.5 ${OVERLAY_SHADOW}`}\n >\n <button\n ref={closeRef}\n type=\"button\"\n aria-label=\"Close\"\n onClick={close}\n className=\"absolute right-3.5 top-3.5 z-10 grid h-8 w-8 place-items-center rounded-full border border-border bg-card hover:bg-accent\"\n >\n <X size={15} strokeWidth={1.5} />\n </button>\n\n <div className=\"flex justify-center\">\n <div className=\"relative overflow-hidden rounded-xl border border-border bg-accent\" style={mediaStyle}>\n {status === 'pending' || status === 'running' ? (\n <div className=\"studio-skeleton absolute inset-0\" />\n ) : status === 'failed' ? (\n <div className=\"grid h-full w-full place-items-center bg-accent p-3 text-center text-[12px] text-destructive\">\n {generationError(generation) ?? 'Generation failed'}\n </div>\n ) : generation.result === null ? (\n <div className=\"studio-skeleton absolute inset-0\" />\n ) : generation.type === 'image' ? (\n <img src={generation.result} className=\"h-full w-full object-cover\" alt={generation.prompt} />\n ) : generation.type === 'video' || generation.type === 'avatar' ? (\n <video src={generation.result} controls playsInline className=\"h-full w-full object-cover\" />\n ) : generation.type === 'transcription' ? (\n <div className=\"max-h-[60vh] overflow-auto whitespace-pre-wrap p-4 text-[13.5px] leading-relaxed\">\n {generation.result ?? ''}\n </div>\n ) : generation.type === 'speech' ? (\n <Waveform generation={generation} />\n ) : null}\n </div>\n </div>\n\n {status !== 'failed' && generation.type === 'speech' && <AudioTransport generation={generation} />}\n\n <p className=\"mx-0.5 mb-1.5 mt-4 max-w-[62ch] text-[15px] leading-normal\">{generation.prompt}</p>\n <div className=\"mx-0.5 flex flex-wrap items-center gap-1.5 text-[12.5px] text-muted-foreground\">\n {metaSegments.join(' · ')}\n </div>\n\n <div className=\"mt-3.5 flex flex-wrap gap-2 border-t border-border pt-4\">\n <button\n type=\"button\"\n onClick={() => { void (actions?.download ?? downloadGenerationsViaAnchor)([generation]) }}\n className={footerButtonClass}\n >\n <Download size={15} strokeWidth={1.5} /> Download\n </button>\n\n {!vaultPath && actions?.save && (\n <div ref={popover.containerRef}>\n <button\n {...popover.triggerProps}\n type=\"button\"\n onClick={() => setSaveOpen((value) => !value)}\n className={footerButtonClass}\n >\n <FolderPlus size={15} strokeWidth={1.5} /> Save to vault\n </button>\n <VaultPathPopover\n open={saveOpen}\n triggerRef={popover.triggerRef}\n panelRef={popover.panelRef}\n generations={[generation]}\n onSubmit={save}\n onCancel={() => setSaveOpen(false)}\n pending={savePending}\n />\n </div>\n )}\n\n {viewControl && (vaultHref ? (\n <a href={vaultHref} onClick={viewVault} className={footerButtonClass}>\n <FolderOpen size={15} strokeWidth={1.5} /> View in vault\n </a>\n ) : (\n <button type=\"button\" onClick={viewVault} className={footerButtonClass}>\n <FolderOpen size={15} strokeWidth={1.5} /> View in vault\n </button>\n ))}\n\n {onRequestDelete && (\n <button\n type=\"button\"\n onClick={() => onRequestDelete(generation)}\n className={`${footerButtonClass} ml-auto text-destructive hover:border-destructive hover:bg-destructive/10`}\n >\n <Trash2 size={15} strokeWidth={1.5} /> Delete\n </button>\n )}\n </div>\n </div>\n </div>\n )\n\n return createPortal(modal, document.body)\n}\n","import {\n type JSX,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react'\n\nimport type { Generation } from '../studio/generation'\nimport type { StudioMediaActions, VaultSaveResult } from '../studio/ports'\nimport { MediaTile } from './media-tile'\nimport { MediaViewerModal } from './media-viewer'\nimport { StudioComposer } from './studio-composer'\nimport { StudioConfirmDialog } from './studio-confirm'\nimport { useStudioPlayback } from './studio-playback'\nimport { useStudioToast } from './studio-toasts'\nimport { useBatchNavigation } from './use-batch-navigation'\nimport { useDeferredDelete } from './use-deferred-delete'\n\nexport interface StudioHomeScreenProps {\n /** Newest first, from the host loader (host runs useStudioGenerations). */\n generations: Generation[]\n onGenerated: (generation: Generation) => void\n onOpenGeneration: (batchKey: string, first: Generation) => void\n onOpenHistory: () => void\n workspaceId?: string\n pickReferenceImage?: () => Promise<string | null>\n actions?: StudioMediaActions\n recentLimit?: number\n className?: string\n}\n\n/**\n * Studio's create-and-recent-media landing screen. The host must render this\n * inside both `StudioToastProvider` and `StudioPlaybackProvider`.\n */\nexport function StudioHomeScreen({\n generations,\n onGenerated,\n onOpenGeneration,\n onOpenHistory,\n workspaceId,\n pickReferenceImage,\n actions,\n recentLimit = 20,\n className,\n}: StudioHomeScreenProps): JSX.Element {\n const [viewer, setViewer] = useState<Generation | null>(null)\n const [confirmTargets, setConfirmTargets] = useState<Generation[] | null>(null)\n const playback = useStudioPlayback()\n const { toast } = useStudioToast()\n const deferredDelete = useDeferredDelete({\n remove: actions?.remove ?? (async () => {}),\n })\n const navigateNewBatch = useBatchNavigation({ seed: generations, onOpenGeneration })\n\n const wrappedOnGenerated = useCallback((generation: Generation) => {\n onGenerated(generation)\n navigateNewBatch(generation)\n }, [navigateNewBatch, onGenerated])\n\n const visible = useMemo(\n () => generations.filter((generation) => !deferredDelete.pendingIds.has(generation.id)),\n [deferredDelete.pendingIds, generations],\n )\n const renderedViewer = viewer\n ? generations.find((generation) => generation.id === viewer.id) ?? viewer\n : null\n const requestDelete = actions?.remove\n ? (generation: Generation) => setConfirmTargets([generation])\n : undefined\n\n const onSaved = useCallback((results: readonly VaultSaveResult[]) => {\n const first = results[0]\n if (first) toast({ message: `Saved to vault · ${first.vaultPath}` })\n }, [toast])\n\n useEffect(() => playback.stop, [playback.stop])\n\n function confirmDelete(): void {\n const targets = confirmTargets\n setConfirmTargets(null)\n if (!targets?.length) return\n if (viewer && targets.some((target) => target.id === viewer.id)) setViewer(null)\n deferredDelete.request(targets)\n }\n\n return (\n <main className={className}>\n <div className=\"studio-home-top mx-auto w-full max-w-[820px] px-6 pt-[clamp(24px,12vh,124px)] max-[900px]:px-4\">\n <h1 className=\"mb-5 text-center text-[30px] font-semibold leading-[1.2] tracking-[-0.02em] [text-wrap:balance] max-[640px]:text-[25px]\">\n What do you want to create?\n </h1>\n <StudioComposer\n variant=\"home\"\n workspaceId={workspaceId}\n pickReferenceImage={pickReferenceImage}\n onGenerated={wrappedOnGenerated}\n />\n </div>\n\n <section className=\"studio-home-recent pb-[72px]\">\n <div className=\"mb-3 mt-[34px] flex items-center justify-between gap-3 px-6 max-[900px]:px-4\">\n <h2 className=\"text-[13px] font-medium tracking-[0.02em] text-muted-foreground\">Recent media</h2>\n <button\n type=\"button\"\n onClick={onOpenHistory}\n className=\"h-[30px] whitespace-nowrap rounded-full border border-border bg-card px-3 text-[13px] shadow-sm hover:bg-accent\"\n >\n View history\n </button>\n </div>\n\n <div className=\"studio-grid studio-grid-library\">\n {visible.length === 0 ? (\n <div className=\"col-span-full flex flex-col items-center py-16 text-center\">\n <p className=\"text-[14px] text-foreground\">Nothing generated yet.</p>\n <p className=\"max-w-[380px] text-[13px] text-muted-foreground\">\n Whatever you make lands here first — it only reaches the vault when you save it.\n </p>\n </div>\n ) : visible.slice(0, recentLimit).map((generation) => (\n <MediaTile\n key={generation.id}\n generation={generation}\n context=\"home\"\n onOpen={setViewer}\n actions={actions}\n onRequestDelete={requestDelete}\n onSaved={onSaved}\n />\n ))}\n </div>\n </section>\n\n <MediaViewerModal\n generation={renderedViewer}\n onClose={() => setViewer(null)}\n actions={actions}\n onRequestDelete={requestDelete}\n onSaved={onSaved}\n />\n <StudioConfirmDialog\n open={confirmTargets !== null}\n count={confirmTargets?.length ?? 0}\n onConfirm={confirmDelete}\n onCancel={() => setConfirmTargets(null)}\n />\n </main>\n )\n}\n","import { useEffect, useId, useRef, type JSX } from 'react'\nimport { createPortal } from 'react-dom'\nimport { OVERLAY_SHADOW } from '../web-react/controls'\n\nexport interface StudioConfirmDialogProps {\n open: boolean\n count: number\n onConfirm: () => void\n onCancel: () => void\n}\n\nexport function StudioConfirmDialog({\n open,\n count,\n onConfirm,\n onCancel,\n}: StudioConfirmDialogProps): JSX.Element | null {\n const panelRef = useRef<HTMLDivElement>(null)\n const cancelRef = useRef<HTMLButtonElement>(null)\n const previousFocusRef = useRef<HTMLElement | null>(null)\n const titleId = useId()\n const descriptionId = useId()\n\n useEffect(() => {\n if (!open) return\n previousFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null\n cancelRef.current?.focus()\n return () => {\n const previous = previousFocusRef.current\n if (previous && document.contains(previous)) previous.focus()\n previousFocusRef.current = null\n }\n }, [open])\n\n if (!open || typeof document === 'undefined') return null\n\n function onKeyDown(event: React.KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n return\n }\n if (event.key !== 'Tab') return\n const focusable = panelRef.current?.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n )\n if (!focusable || focusable.length === 0) return\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = document.activeElement as HTMLElement | null\n if (event.shiftKey && active === first) {\n event.preventDefault()\n last?.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first?.focus()\n }\n }\n\n return createPortal(\n <div\n className=\"studio-layer-confirm studio-backdrop fixed inset-0 grid place-items-center p-6\"\n onMouseDown={(event) => { if (event.target === event.currentTarget) onCancel() }}\n >\n <div\n ref={panelRef}\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={descriptionId}\n onKeyDown={onKeyDown}\n className={`w-[min(360px,100%)] rounded-[14px] border border-border bg-card px-[18px] pb-4 pt-[18px] ${OVERLAY_SHADOW}`}\n >\n <h2 id={titleId} className=\"text-[15.5px] font-semibold tracking-[-0.01em]\">\n Delete {count} item{count > 1 ? 's' : ''}?\n </h2>\n <p id={descriptionId} className=\"mt-[7px] text-[13px] text-muted-foreground\">This cannot be undone.</p>\n <div className=\"flex justify-end gap-2 pt-[18px]\">\n <button\n ref={cancelRef}\n type=\"button\"\n onClick={onCancel}\n className=\"h-[30px] rounded-full border border-border px-3.5 text-[13px] hover:bg-accent\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={onConfirm}\n className=\"h-[30px] rounded-full bg-destructive px-3.5 text-[13px] font-medium text-destructive-foreground hover:brightness-110\"\n >\n Delete\n </button>\n </div>\n </div>\n </div>,\n document.body,\n )\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type JSX,\n type ReactNode,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { X } from 'lucide-react'\nimport { OVERLAY_SHADOW } from '../web-react/controls'\n\nexport interface StudioToastInput {\n message: string\n action?: { label: string; run: () => void }\n durationMs?: number\n /** Fires exactly once when the toast leaves for ANY reason. */\n onDismiss?: (reason: 'timeout' | 'dismissed' | 'action') => void\n}\n\ntype DismissReason = 'timeout' | 'dismissed' | 'action'\ntype ToastRecord = StudioToastInput & { id: string; leaving: boolean }\n\ninterface StudioToastContextValue {\n toast: (input: StudioToastInput) => string\n dismiss: (id: string) => void\n setDockLift: (px: number | null) => void\n}\n\nconst StudioToastContext = createContext<StudioToastContextValue | null>(null)\n\nfunction StudioToast({ record, leave }: { record: ToastRecord; leave: (id: string, reason: DismissReason) => void }) {\n useEffect(() => {\n if (record.leaving) return\n const duration = record.durationMs ?? 3500\n if (duration === 0) return\n const timer = window.setTimeout(() => leave(record.id, 'timeout'), duration)\n return () => window.clearTimeout(timer)\n }, [leave, record.durationMs, record.id, record.leaving])\n\n return (\n <div\n role=\"status\"\n className={`pointer-events-auto flex max-w-[min(94vw,480px)] items-center gap-2.5 rounded-[10px] border border-border bg-card py-2 pl-[13px] pr-2 text-[13px] ${OVERLAY_SHADOW} ${record.leaving ? 'studio-toast-out' : 'studio-toast-in'}`}\n >\n <span className=\"min-w-0 truncate\">{record.message}</span>\n {record.action && (\n <button\n type=\"button\"\n onClick={() => {\n record.action?.run()\n leave(record.id, 'action')\n }}\n className=\"rounded-lg border-0 px-2 py-1 text-[13px] font-semibold text-primary hover:bg-primary/10\"\n >\n {record.action.label}\n </button>\n )}\n <button\n type=\"button\"\n aria-label=\"Dismiss\"\n onClick={() => leave(record.id, 'dismissed')}\n className=\"inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-muted-foreground hover:bg-accent hover:text-foreground\"\n >\n <X className=\"h-3.5 w-3.5\" aria-hidden />\n </button>\n </div>\n )\n}\n\nexport function StudioToastProvider({ children }: { children: ReactNode }): JSX.Element {\n const [toasts, setToasts] = useState<ToastRecord[]>([])\n const [dockLift, setDockLift] = useState<number | null>(null)\n const sequence = useRef(0)\n const dismissed = useRef(new Set<string>())\n const records = useRef(new Map<string, ToastRecord>())\n\n const leave = useCallback((id: string, reason: DismissReason) => {\n if (dismissed.current.has(id)) return\n const record = records.current.get(id)\n if (!record) return\n dismissed.current.add(id)\n record.onDismiss?.(reason)\n setToasts((current) => current.map((toast) => toast.id === id ? { ...toast, leaving: true } : toast))\n window.setTimeout(() => {\n records.current.delete(id)\n setToasts((current) => current.filter((toast) => toast.id !== id))\n }, 180)\n }, [])\n\n const toast = useCallback((input: StudioToastInput) => {\n const id = `studio-toast-${++sequence.current}`\n const record: ToastRecord = { ...input, id, leaving: false }\n records.current.set(id, record)\n setToasts((current) => [...current, record])\n return id\n }, [])\n\n const dismiss = useCallback((id: string) => leave(id, 'dismissed'), [leave])\n const value = useMemo(() => ({ toast, dismiss, setDockLift }), [dismiss, toast])\n\n return (\n <StudioToastContext.Provider value={value}>\n {children}\n {typeof document !== 'undefined' && createPortal(\n <div\n role=\"region\"\n aria-label=\"Notifications\"\n style={{ bottom: dockLift ? dockLift + 10 : 22 }}\n className=\"studio-layer-toasts pointer-events-none fixed left-1/2 flex -translate-x-1/2 flex-col items-center gap-2\"\n >\n {toasts.map((record) => <StudioToast key={record.id} record={record} leave={leave} />)}\n </div>,\n document.body,\n )}\n </StudioToastContext.Provider>\n )\n}\n\nexport function useStudioToast(): StudioToastContextValue {\n const context = useContext(StudioToastContext)\n if (!context) throw new Error('useStudioToast must be used within a StudioToastProvider')\n return context\n}\n","import { useCallback, useRef } from 'react'\nimport { generationBatchKey, type Generation } from '../studio/generation'\n\nexport interface UseBatchNavigationOptions {\n /** Rows whose batch keys are treated as already seen. Read once, at mount. */\n seed: readonly Generation[]\n /**\n * A batch key that never navigates, read on every call — the generation\n * screen passes its own batch so rows landing for the batch already on\n * screen stay put while a NEW dock submit navigates.\n */\n currentBatchKey?: string\n onOpenGeneration: (batchKey: string, first: Generation) => void\n}\n\n/**\n * Returns a callback the screens wrap around `onGenerated`: the first row of a\n * batch not seen before navigates via `onOpenGeneration`, exactly once per\n * batch — a four-image batch arrives as four rows sharing one batch key and\n * must produce one navigation, and rows merely refreshed from the loader\n * (seeded at mount) must produce none.\n */\nexport function useBatchNavigation({\n seed,\n currentBatchKey,\n onOpenGeneration,\n}: UseBatchNavigationOptions): (generation: Generation) => void {\n const seenKeys = useRef<Set<string> | null>(null)\n seenKeys.current ??= new Set(seed.map(generationBatchKey))\n const currentRef = useRef(currentBatchKey)\n currentRef.current = currentBatchKey\n\n return useCallback((generation: Generation) => {\n const key = generationBatchKey(generation)\n const seen = seenKeys.current\n if (key === currentRef.current || !seen || seen.has(key)) return\n seen.add(key)\n onOpenGeneration(key, generation)\n }, [onOpenGeneration])\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { Generation } from '../studio/generation'\nimport type { DeleteGenerations } from '../studio/ports'\nimport { useStudioPlayback } from './studio-playback'\nimport { useStudioToast } from './studio-toasts'\n\nexport interface UseDeferredDeleteOptions {\n remove: DeleteGenerations\n /** Undo window AND toast lifetime. Default 3500. */\n undoWindowMs?: number\n /** After the server call succeeds for a batch. */\n onCommitted?: (ids: readonly string[]) => void\n /** After a failed server call restored the rows (screens may refetch). */\n onRestoreFailed?: (ids: readonly string[]) => void\n}\n\nexport interface DeferredDelete {\n /** Rows the screens must filter OUT of every render. Survives refetches. */\n pendingIds: ReadonlySet<string>\n request: (generations: readonly Generation[]) => void\n /** Commit every outstanding batch NOW (unmount/navigation). */\n flush: () => void\n}\n\ninterface DeleteBatch {\n ids: readonly string[]\n toastId: string\n status: 'pending' | 'committing'\n}\n\nconst itemLabel = (count: number) => `item${count === 1 ? '' : 's'}`\n\nexport function useDeferredDelete(options: UseDeferredDeleteOptions): DeferredDelete {\n const { activeId, stop } = useStudioPlayback()\n const { dismiss, toast } = useStudioToast()\n const [pendingIds, setPendingIds] = useState<ReadonlySet<string>>(() => new Set())\n const batchesRef = useRef(new Map<string, DeleteBatch>())\n const committedIdsRef = useRef(new Set<string>())\n const sequenceRef = useRef(0)\n const optionsRef = useRef(options)\n const dismissRef = useRef(dismiss)\n optionsRef.current = options\n dismissRef.current = dismiss\n\n const visiblePendingIds = useCallback(() => {\n const ids = new Set(committedIdsRef.current)\n for (const batch of batchesRef.current.values()) {\n for (const id of batch.ids) ids.add(id)\n }\n return ids\n }, [])\n\n const commit = useCallback((batchKey: string) => {\n const batch = batchesRef.current.get(batchKey)\n if (!batch || batch.status !== 'pending') return\n batch.status = 'committing'\n\n void optionsRef.current.remove(batch.ids).then(() => {\n if (batchesRef.current.get(batchKey) !== batch) return\n batchesRef.current.delete(batchKey)\n for (const id of batch.ids) committedIdsRef.current.add(id)\n optionsRef.current.onCommitted?.(batch.ids)\n }, () => {\n if (batchesRef.current.get(batchKey) !== batch) return\n batchesRef.current.delete(batchKey)\n setPendingIds(visiblePendingIds())\n toast({ message: `Could not delete ${batch.ids.length} ${itemLabel(batch.ids.length)}` })\n optionsRef.current.onRestoreFailed?.(batch.ids)\n })\n }, [toast, visiblePendingIds])\n\n const request = useCallback((generations: readonly Generation[]) => {\n if (generations.length === 0) return\n const ids = [...new Set(generations.map((generation) => generation.id))]\n const batchKey = `deferred-delete-${++sequenceRef.current}`\n\n setPendingIds((current) => {\n const next = new Set(current)\n for (const id of ids) next.add(id)\n return next\n })\n if (activeId && ids.includes(activeId)) stop()\n\n const undo = () => {\n const batch = batchesRef.current.get(batchKey)\n if (!batch || batch.status !== 'pending') return\n batchesRef.current.delete(batchKey)\n setPendingIds(visiblePendingIds())\n toast({ message: `Restored ${ids.length} ${itemLabel(ids.length)}` })\n }\n\n const toastId = toast({\n message: `Deleted ${ids.length} ${itemLabel(ids.length)}`,\n action: { label: 'Undo', run: undo },\n durationMs: options.undoWindowMs ?? 3500,\n onDismiss: (reason) => {\n if (reason !== 'action') commit(batchKey)\n },\n })\n batchesRef.current.set(batchKey, { ids, toastId, status: 'pending' })\n }, [activeId, commit, options.undoWindowMs, stop, toast, visiblePendingIds])\n\n const flush = useCallback(() => {\n for (const [batchKey, batch] of batchesRef.current) {\n if (batch.status !== 'pending') continue\n batch.status = 'committing'\n batchesRef.current.delete(batchKey)\n dismissRef.current(batch.toastId)\n void optionsRef.current.remove(batch.ids).catch(() => {})\n }\n }, [])\n\n useEffect(() => {\n window.addEventListener('pagehide', flush)\n return () => {\n window.removeEventListener('pagehide', flush)\n flush()\n }\n }, [flush])\n\n return { pendingIds, request, flush }\n}\n","/**\n * The generation screen: ONE batch's results, with the composer docked under\n * them.\n *\n * The screen is a flex column that fills its host — results scroll, the dock\n * does not. The dock band is deliberately OPAQUE (`bg-background`): it sits\n * above the scrolling results and a translucent band would let a tile read\n * through the composer's own card. The 28px fade above it comes from\n * `.studio-dock::before` in `./studio.css`.\n *\n * The dock's measured height is published two ways, because two different\n * consumers need it: as the `--studio-dock-h` custom property on the screen\n * root (host CSS, and anything nested that must clear the band) and through\n * `useStudioToast().setDockLift`, which lifts the toast stack off the composer.\n * The body's own bottom padding is applied from the measured value directly\n * rather than read back through that property — a CSS custom property a\n * component READS must be one `tokens.css` defines (the theme contract), and a\n * dock height is a layout measurement, not a theme token.\n *\n * A prompt sent from the DOCK starts a NEW batch, which is a different screen.\n * This one reports it (`onOpenGeneration`) exactly once per new batch key and\n * lets the host navigate; every row of that batch still flows through\n * `onGenerated` so the host's list stays whole.\n *\n * Assumes a `StudioToastProvider` and a `StudioPlaybackProvider` above it.\n */\n\nimport { ArrowLeft } from 'lucide-react'\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type JSX,\n} from 'react'\n\nimport {\n generationAspectRatio,\n generationError,\n generationStatus,\n generationsInBatch,\n WIDE_WAVEFORM_BARS,\n type DeleteGenerations,\n type Generation,\n type StudioMediaActions,\n type VaultSaveResult,\n} from '../studio'\nimport { GenerationNoticeChip } from './generation-notice'\nimport { MediaTile } from './media-tile'\nimport { MediaViewerModal } from './media-viewer'\nimport { StudioComposer } from './studio-composer'\nimport { StudioConfirmDialog } from './studio-confirm'\nimport { useStudioPlayback } from './studio-playback'\nimport { useStudioToast } from './studio-toasts'\nimport { useBatchNavigation } from './use-batch-navigation'\nimport { useDeferredDelete } from './use-deferred-delete'\n\nexport interface StudioGenerationScreenProps {\n /** Full merged list from the host (`useStudioGenerations`). */\n generations: Generation[]\n batchKey: string\n onGenerated: (generation: Generation) => void\n /** A dock submit that starts a NEW batch navigates via the host. */\n onOpenGeneration: (batchKey: string, first: Generation) => void\n onBack: () => void\n workspaceId?: string\n pickReferenceImage?: () => Promise<string | null>\n actions?: StudioMediaActions\n className?: string\n}\n\ntype SkeletonStyle = CSSProperties & { '--r'?: number }\n\n/** What the body clears before the dock has ever been measured. */\nconst FALLBACK_DOCK_HEIGHT = 190\n\n/** `useDeferredDelete` needs a remover unconditionally; with no `remove` seam\n * no delete control is rendered, so this is never reached. */\nconst noRemove: DeleteGenerations = async () => {}\n\nfunction isRunning(generation: Generation): boolean {\n const status = generationStatus(generation)\n return status === 'pending' || status === 'running'\n}\n\nexport function StudioGenerationScreen({\n generations,\n batchKey,\n onGenerated,\n onOpenGeneration,\n onBack,\n workspaceId,\n pickReferenceImage,\n actions,\n className,\n}: StudioGenerationScreenProps): JSX.Element {\n const rootRef = useRef<HTMLDivElement>(null)\n const dockRef = useRef<HTMLDivElement>(null)\n const { setDockLift, toast } = useStudioToast()\n const { stop } = useStudioPlayback()\n const stopRef = useRef(stop)\n stopRef.current = stop\n\n const [dockHeight, setDockHeight] = useState(FALLBACK_DOCK_HEIGHT)\n const [viewerId, setViewerId] = useState<string | null>(null)\n const [deleteTarget, setDeleteTarget] = useState<Generation | null>(null)\n\n const remove = actions?.remove\n const { pendingIds, request } = useDeferredDelete({ remove: remove ?? noRemove })\n\n const rows = useMemo(\n () => generationsInBatch(generations, batchKey).filter((row) => !pendingIds.has(row.id)),\n [batchKey, generations, pendingIds],\n )\n const first = rows[0]\n const prompt = first?.prompt ?? ''\n const ratio = first ? generationAspectRatio(first) : 1.5\n const runningCount = rows.filter(isRunning).length\n\n // The rise stagger counts SETTLED rows only, so a batch whose slots finish out\n // of order still lands 60ms apart instead of inheriting a skeleton's slot.\n const settledOrder = new Map<string, number>()\n for (const row of rows) if (!isRunning(row)) settledOrder.set(row.id, settledOrder.size)\n\n const columns = rows.length === 4 ? 2 : Math.min(rows.length, 4) || 1\n const maxWidth = rows.length > 1 || first?.type === 'speech'\n ? 820\n : ratio < 1 ? 380 : 640\n\n const viewerGeneration = viewerId ? rows.find((row) => row.id === viewerId) ?? null : null\n\n const onSaved = useCallback((results: readonly VaultSaveResult[]) => {\n const saved = results[0]\n if (saved) toast({ message: `Saved to vault · ${saved.vaultPath}` })\n }, [toast])\n\n const navigateNewBatch = useBatchNavigation({ seed: [], currentBatchKey: batchKey, onOpenGeneration })\n const handleGenerated = useCallback((generation: Generation) => {\n onGenerated(generation)\n navigateNewBatch(generation)\n }, [navigateNewBatch, onGenerated])\n\n useEffect(() => {\n const dock = dockRef.current\n if (!dock) return\n const apply = (height: number) => {\n // A zero reading means \"not laid out yet\" (jsdom, a hidden host); publishing\n // it would collapse the body's clearance onto a dock that is really there.\n if (height <= 0) return\n rootRef.current?.style.setProperty('--studio-dock-h', `${height}px`)\n setDockHeight(height)\n setDockLift(height)\n }\n const measure = () => apply(dock.getBoundingClientRect().height)\n measure()\n // jsdom and older browsers have no ResizeObserver; the window listener still\n // covers the case that matters (the viewport getting narrower).\n const observer = typeof ResizeObserver === 'undefined'\n ? null\n : new ResizeObserver((entries) => apply(entries[0]?.contentRect.height ?? dock.getBoundingClientRect().height))\n observer?.observe(dock)\n window.addEventListener('resize', measure)\n return () => {\n observer?.disconnect()\n window.removeEventListener('resize', measure)\n setDockLift(null)\n }\n }, [setDockLift])\n\n // Leaving the screen stops whatever it was playing — the audio element lives\n // in the provider ABOVE this component and would otherwise keep going.\n useEffect(() => () => stopRef.current(), [])\n\n function confirmDelete(): void {\n if (!deleteTarget) return\n request([deleteTarget])\n if (viewerId === deleteTarget.id) setViewerId(null)\n setDeleteTarget(null)\n }\n\n return (\n <div ref={rootRef} className={`flex min-h-full flex-col ${className ?? ''}`}>\n <header className=\"studio-gen-head mx-auto flex w-full max-w-[868px] items-center gap-2.5 px-6 pb-4 pt-1 max-[900px]:px-4\">\n <button\n type=\"button\"\n aria-label=\"Back to Studio\"\n onClick={onBack}\n className=\"grid h-8 w-8 flex-none place-items-center rounded-full border border-border bg-card shadow-sm hover:bg-accent\"\n >\n <ArrowLeft size={16} strokeWidth={1.5} />\n </button>\n <p\n title={prompt}\n className=\"min-w-0 max-w-full truncate rounded-full border border-border bg-card px-4 py-2 text-[14px] shadow-sm\"\n >\n {prompt}\n </p>\n </header>\n\n <div\n className=\"studio-gen-body flex-1 px-6 max-[900px]:px-4\"\n style={{ paddingBottom: `${dockHeight + 16}px` }}\n >\n {rows.length === 0 ? (\n <div className=\"mx-auto flex max-w-[520px] flex-col items-center gap-1 py-16 text-center\">\n <p className=\"text-[14px]\">Nothing left from this generation.</p>\n <p className=\"text-[13px] text-muted-foreground\">Send another prompt below to start a new one.</p>\n </div>\n ) : (\n <>\n <div\n className=\"mx-auto grid gap-[3px]\"\n aria-busy={runningCount > 0}\n style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, maxWidth }}\n >\n {rows.map((row) => {\n if (isRunning(row)) {\n return <div key={row.id} className=\"studio-skeleton\" style={{ '--r': ratio } as SkeletonStyle} />\n }\n // A failed row is NOT a tile: `MediaTile` reads a null result as\n // \"still running\" and draws a skeleton, which would leave a dead\n // slot sweeping for ever. The cell states the reason instead.\n if (generationStatus(row) === 'failed') {\n return (\n <div\n key={row.id}\n className=\"grid place-items-center bg-accent p-3 text-center text-[12px] text-destructive\"\n style={{ aspectRatio: ratio }}\n >\n {generationError(row) ?? 'Generation failed'}\n </div>\n )\n }\n return (\n <div\n key={row.id}\n className=\"studio-rise\"\n style={{ animationDelay: `${(settledOrder.get(row.id) ?? 0) * 60}ms` }}\n >\n <MediaTile\n generation={row}\n context=\"generation\"\n aspectRatio={ratio}\n waveformBars={WIDE_WAVEFORM_BARS}\n actions={actions}\n onOpen={(generation) => setViewerId(generation.id)}\n onRequestDelete={remove ? setDeleteTarget : undefined}\n onSaved={onSaved}\n />\n </div>\n )\n })}\n </div>\n {runningCount > 0 && (\n <p className=\"sr-only\" aria-live=\"polite\">\n Generating {runningCount} result{runningCount > 1 ? 's' : ''}…\n </p>\n )}\n </>\n )}\n </div>\n\n <div\n ref={dockRef}\n className=\"studio-dock sticky bottom-0 z-[5] bg-background px-6 pb-[18px] pt-4 max-[900px]:px-4\"\n >\n <div className=\"mx-auto w-full max-w-[820px]\">\n <div className=\"mb-2.5 flex justify-center\">\n <GenerationNoticeChip />\n </div>\n <StudioComposer\n variant=\"docked\"\n workspaceId={workspaceId}\n pickReferenceImage={pickReferenceImage}\n onGenerated={handleGenerated}\n />\n </div>\n </div>\n\n <MediaViewerModal\n generation={viewerGeneration}\n onClose={() => setViewerId(null)}\n actions={actions}\n onRequestDelete={remove ? setDeleteTarget : undefined}\n onSaved={onSaved}\n />\n <StudioConfirmDialog\n open={deleteTarget !== null}\n count={1}\n onConfirm={confirmDelete}\n onCancel={() => setDeleteTarget(null)}\n />\n </div>\n )\n}\n","/**\n * The studio history screen — the full media library behind the home screen's\n * \"View history\" button: search, a media-type filter, cursor-paged infinite\n * scroll, and the multi-select bar that runs download / save-to-vault / delete\n * over a whole selection at once.\n *\n * It ASSUMES a `StudioToastProvider` and a `StudioPlaybackProvider` above it —\n * the deferred delete it drives lives in both (undo toast, stopping audio for a\n * row that is going away), and every screen in this surface makes the same\n * assumption so a route layout can mount the two providers once.\n *\n * The screen never fetches: `fetchPage` is the product's own paged endpoint and\n * `actions` are its media seams. An ABSENT action hides its control rather than\n * rendering a batch button that does nothing.\n */\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useState,\n type CSSProperties,\n type JSX,\n} from 'react'\nimport { ArrowLeft, Download, FolderPlus, Search, Trash2, X } from 'lucide-react'\n\nimport type { Generation } from '../studio/generation'\nimport {\n MEDIA_TYPE_FILTERS,\n type FetchGenerationsPage,\n type GenerationPage,\n type MediaTypeFilter,\n type StudioMediaActions,\n type VaultSaveResult,\n} from '../studio/ports'\nimport { POPOVER_SURFACE_ATTR, usePopover } from '../web-react/controls'\nimport { useInfiniteScroll } from '../web-react/session-history'\nimport { MenuPill } from './composer-option-controls'\nimport { downloadGenerationsViaAnchor } from './download-generations'\nimport { MediaTile } from './media-tile'\nimport { MediaViewerModal } from './media-viewer'\nimport { StudioConfirmDialog } from './studio-confirm'\nimport { useStudioPlayback } from './studio-playback'\nimport { useStudioToast } from './studio-toasts'\nimport { useDeferredDelete } from './use-deferred-delete'\nimport { useGenerationHistory } from './use-generation-history'\nimport { VaultPathPopover } from './vault-path-popover'\n\nexport interface StudioHistoryScreenProps {\n fetchPage: FetchGenerationsPage\n /** SSR/loader page 1 for the DEFAULT view (no search, no type filter). */\n initialPage?: GenerationPage\n onBack: () => void\n actions?: StudioMediaActions\n /** Trailing debounce before a keystroke becomes a fetch. */\n searchDebounceMs?: number\n className?: string\n}\n\n/** How many cells the first load and each subsequent page stand in for. */\nconst FIRST_LOAD_SKELETONS = 8\nconst MORE_SKELETONS = 4\n\nconst BAR = 'flex min-h-[44px] flex-wrap items-center justify-between gap-3 px-6 pb-[18px] pt-0.5 max-[900px]:px-4'\nconst SEARCH_INPUT = 'h-8 w-[260px] max-w-[52vw] rounded-full border border-border bg-card pl-8 pr-3 text-[13px] text-foreground outline-none placeholder:text-muted-foreground focus:border-primary focus:ring-[3px] focus:ring-ring/30 max-[640px]:w-full max-[640px]:max-w-none'\nconst OUTLINE_PILL = 'inline-flex h-8 items-center gap-1.5 rounded-full border border-border px-3 text-[12.5px] font-medium hover:bg-accent disabled:cursor-not-allowed disabled:opacity-40'\n\n/** The empty-state plural for a type filter, in the filter's own words. */\nconst TYPE_NOUNS: Record<Exclude<MediaTypeFilter, 'all'>, string> = {\n image: 'images',\n video: 'videos',\n speech: 'audio',\n}\n\ntype SkeletonStyle = CSSProperties & { '--r'?: number }\n/** `.studio-skeleton` defaults to the 3:2 generation ratio; the library grid is\n * square, and the ratio is a custom property rather than a utility because a\n * Tailwind `aspect-*` class ties with the stylesheet rule on specificity. */\nconst SQUARE_CELL: SkeletonStyle = { '--r': 1 }\n\nfunction skeletonCells(count: number, keyPrefix: string) {\n return Array.from({ length: count }, (_, index) => (\n <div key={`${keyPrefix}-${index}`} className=\"studio-skeleton\" style={SQUARE_CELL} aria-hidden />\n ))\n}\n\nfunction hasOpenPopover(): boolean {\n return typeof document !== 'undefined' && document.querySelector(`[${POPOVER_SURFACE_ATTR}]`) !== null\n}\n\nexport function StudioHistoryScreen({\n fetchPage,\n initialPage,\n onBack,\n actions,\n searchDebounceMs = 250,\n className,\n}: StudioHistoryScreenProps): JSX.Element {\n const playback = useStudioPlayback()\n const { stop } = playback\n const { toast } = useStudioToast()\n\n const [query, setQuery] = useState('')\n const [debouncedQuery, setDebouncedQuery] = useState('')\n const [type, setType] = useState<MediaTypeFilter>('all')\n const [selectMode, setSelectMode] = useState(false)\n const [selected, setSelected] = useState<ReadonlySet<string>>(() => new Set())\n const [viewer, setViewer] = useState<Generation | null>(null)\n const [confirmTargets, setConfirmTargets] = useState<Generation[] | null>(null)\n const [saveOpen, setSaveOpen] = useState(false)\n const [savePending, setSavePending] = useState(false)\n const savePopover = usePopover(saveOpen, setSaveOpen)\n\n // Trailing debounce. Clearing the search sets both halves at once, and the\n // equality guard is what keeps that from scheduling a redundant timer.\n useEffect(() => {\n if (query === debouncedQuery) return\n const timer = window.setTimeout(() => setDebouncedQuery(query), searchDebounceMs)\n return () => window.clearTimeout(timer)\n }, [debouncedQuery, query, searchDebounceMs])\n\n const history = useGenerationHistory({ fetchPage, q: debouncedQuery, type, initialPage })\n const deferredDelete = useDeferredDelete({\n remove: actions?.remove ?? (async () => {}),\n // Nothing to reload: `pendingIds` already hides the rows and the server has\n // dropped them. A refetch here would only re-request the page we can see.\n })\n const { pendingIds } = deferredDelete\n const rows = useMemo(\n () => history.items.filter((item) => !pendingIds.has(item.id)),\n [history.items, pendingIds],\n )\n const selectedRows = useMemo(() => rows.filter((row) => selected.has(row.id)), [rows, selected])\n\n useEffect(() => {\n if (history.isLoadingFirst || history.isLoadingMore || !playback.activeId) return\n if (rows.some((row) => row.id === playback.activeId) || viewer?.id === playback.activeId) return\n playback.stop()\n }, [history.isLoadingFirst, history.isLoadingMore, playback, rows, viewer])\n\n const sentinelRef = useInfiniteScroll(history.loadMore, {\n enabled: history.hasMore && !history.isLoadingMore && !history.isLoadingFirst && !history.isError,\n })\n\n const exitSelectMode = useCallback(() => {\n setSelectMode(false)\n setSelected(new Set())\n setSaveOpen(false)\n }, [])\n\n const toggleSelect = useCallback((id: string) => {\n if (!selectMode) {\n // Entering select mode hides every tile's play button, so a clip playing\n // underneath would have no control left to stop it.\n stop()\n setSelectMode(true)\n setSelected(new Set([id]))\n return\n }\n setSelected((current) => {\n const next = new Set(current)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }, [selectMode, stop])\n\n // The residual Escape: the confirm dialog, the viewer and every popover close\n // themselves, so this handler exists only for the case none of them owns —\n // leaving select mode. It checks for those overlays rather than assuming an\n // order, because two document listeners see the same keydown.\n useEffect(() => {\n if (!selectMode) return\n function onKeyDown(event: globalThis.KeyboardEvent) {\n if (event.key !== 'Escape') return\n if (confirmTargets || viewer || hasOpenPopover()) return\n event.preventDefault()\n exitSelectMode()\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n }, [confirmTargets, exitSelectMode, selectMode, viewer])\n\n useEffect(() => () => stop(), [stop])\n\n const clearFilters = useCallback(() => {\n setQuery('')\n setDebouncedQuery('')\n setType('all')\n }, [])\n\n const onSaved = useCallback((results: readonly VaultSaveResult[]) => {\n const path = results[0]?.vaultPath\n if (!path) return\n toast({\n message: results.length > 1\n ? `Saved ${results.length} items to vault · ${path}`\n : `Saved to vault · ${path}`,\n })\n }, [toast])\n\n function batchDownload() {\n void (actions?.download ?? downloadGenerationsViaAnchor)(selectedRows)\n exitSelectMode()\n }\n\n async function batchSave(path: string) {\n if (!actions?.save) return\n setSavePending(true)\n try {\n const results = await actions.save({ generations: selectedRows, path })\n setSaveOpen(false)\n onSaved(results)\n exitSelectMode()\n } finally {\n setSavePending(false)\n }\n }\n\n function confirmDelete() {\n const targets = confirmTargets ?? []\n setConfirmTargets(null)\n if (targets.length === 0) return\n if (viewer && targets.some((target) => target.id === viewer.id)) setViewer(null)\n deferredDelete.request(targets)\n exitSelectMode()\n }\n\n const viewerRow = viewer ? rows.find((row) => row.id === viewer.id) ?? viewer : null\n const searchTerm = debouncedQuery.trim()\n const isFiltered = query !== '' || debouncedQuery !== '' || type !== 'all'\n const emptyCopy = searchTerm\n ? {\n title: `No media matches “${searchTerm}”.`,\n body: 'Try a shorter word, or drop the type filter.',\n }\n : type !== 'all'\n ? {\n title: `No ${TYPE_NOUNS[type]} yet.`,\n body: 'Generate one from the Studio composer and it will show up here.',\n }\n : {\n title: 'Your history is empty.',\n body: 'Everything you generate is kept here until you delete it.',\n }\n\n return (\n <div className={`studio-hist-wrap pb-[72px] ${className ?? ''}`}>\n {selectMode ? (\n <div className={BAR}>\n <div className=\"flex items-center gap-2 text-[13.5px] font-medium\">\n <button\n type=\"button\"\n aria-label=\"Exit select mode\"\n onClick={exitSelectMode}\n className=\"grid h-[26px] w-[26px] place-items-center rounded-full hover:bg-accent\"\n >\n <X size={15} strokeWidth={1.5} />\n </button>\n {selected.size} selected\n </div>\n\n <div role=\"toolbar\" aria-label=\"Selection actions\" className=\"ml-auto flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n disabled={selected.size === 0}\n onClick={batchDownload}\n className={OUTLINE_PILL}\n >\n <Download size={16} strokeWidth={1.5} /> Download\n </button>\n\n {actions?.save && (\n <div ref={savePopover.containerRef} className=\"inline-flex\">\n <button\n {...savePopover.triggerProps}\n type=\"button\"\n disabled={selected.size === 0}\n onClick={() => setSaveOpen(!saveOpen)}\n className={OUTLINE_PILL}\n >\n <FolderPlus size={16} strokeWidth={1.5} /> Save to vault\n </button>\n <VaultPathPopover\n open={saveOpen}\n triggerRef={savePopover.triggerRef}\n panelRef={savePopover.panelRef}\n generations={selectedRows}\n onSubmit={batchSave}\n onCancel={() => setSaveOpen(false)}\n pending={savePending}\n />\n </div>\n )}\n\n {actions?.remove && (\n <button\n type=\"button\"\n disabled={selected.size === 0}\n onClick={() => setConfirmTargets(selectedRows)}\n className={`${OUTLINE_PILL} text-destructive hover:border-destructive hover:bg-destructive/10`}\n >\n <Trash2 size={16} strokeWidth={1.5} /> Delete\n </button>\n )}\n </div>\n </div>\n ) : (\n <div className={BAR}>\n <button\n type=\"button\"\n aria-label=\"Back to Studio\"\n onClick={onBack}\n className=\"grid h-8 w-8 flex-none place-items-center rounded-full border border-border bg-card shadow-sm hover:bg-accent\"\n >\n <ArrowLeft size={16} strokeWidth={1.5} />\n </button>\n\n <div className=\"ml-auto flex flex-wrap items-center gap-2 max-[640px]:w-full\">\n <div className=\"relative inline-flex items-center max-[640px]:flex-1 max-[640px]:basis-[160px]\">\n <Search\n size={15}\n strokeWidth={1.5}\n aria-hidden\n className=\"pointer-events-none absolute left-[11px] text-muted-foreground\"\n />\n <input\n type=\"text\"\n value={query}\n placeholder=\"Search\"\n aria-label=\"Search prompts\"\n onChange={(event) => setQuery(event.target.value)}\n className={SEARCH_INPUT}\n />\n </div>\n\n <MenuPill\n label=\"Filter by media type\"\n value={type}\n choices={MEDIA_TYPE_FILTERS}\n onSelect={setType}\n />\n </div>\n </div>\n )}\n\n {history.isError && rows.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 px-6 py-16 text-center max-[900px]:px-4\">\n <p className=\"text-[14px] text-foreground\">Could not load media.</p>\n <button type=\"button\" onClick={history.retry} className={OUTLINE_PILL}>Retry</button>\n </div>\n ) : history.isLoadingFirst && rows.length === 0 ? (\n <div className=\"studio-grid studio-grid-library\">\n {skeletonCells(FIRST_LOAD_SKELETONS, 'first')}\n </div>\n ) : rows.length === 0 ? (\n <div className=\"flex flex-col items-center gap-1.5 px-6 py-16 text-center max-[900px]:px-4\">\n <p className=\"text-[14px] text-foreground\">{emptyCopy.title}</p>\n <p className=\"max-w-[380px] text-[13px] text-muted-foreground\">{emptyCopy.body}</p>\n {isFiltered && (\n <button type=\"button\" onClick={clearFilters} className={`${OUTLINE_PILL} mt-2`}>Clear</button>\n )}\n </div>\n ) : (\n <div className={selectMode ? 'studio-selectmode' : undefined}>\n <div className=\"studio-grid studio-grid-library\">\n {rows.map((row) => (\n <MediaTile\n key={row.id}\n generation={row}\n context=\"history\"\n onOpen={setViewer}\n actions={actions}\n selectMode={selectMode}\n selected={selected.has(row.id)}\n onToggleSelect={toggleSelect}\n onRequestDelete={actions?.remove ? (generation) => setConfirmTargets([generation]) : undefined}\n onSaved={onSaved}\n />\n ))}\n {history.isLoadingMore && skeletonCells(MORE_SKELETONS, 'more')}\n </div>\n </div>\n )}\n\n {history.isError && rows.length > 0 && (\n <div className=\"flex items-center justify-center gap-3 px-6 py-4 text-[13px] text-muted-foreground max-[900px]:px-4\">\n <span>Could not load more media.</span>\n <button type=\"button\" onClick={history.retry} className={OUTLINE_PILL}>Retry</button>\n </div>\n )}\n\n <div ref={sentinelRef} aria-hidden />\n\n <MediaViewerModal\n generation={viewerRow}\n onClose={() => setViewer(null)}\n actions={actions}\n onRequestDelete={actions?.remove ? (generation) => setConfirmTargets([generation]) : undefined}\n onSaved={onSaved}\n />\n\n <StudioConfirmDialog\n open={confirmTargets !== null}\n count={confirmTargets?.length ?? 0}\n onConfirm={confirmDelete}\n onCancel={() => setConfirmTargets(null)}\n />\n </div>\n )\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { FetchGenerationsPage, GenerationPage, MediaTypeFilter } from '../studio/ports'\nimport { mergeGenerationPages, type Generation } from '../studio/generation'\n\nexport interface UseGenerationHistoryOptions {\n fetchPage: FetchGenerationsPage\n /** ALREADY debounced by the caller. */\n q: string\n type: MediaTypeFilter\n /** SSR/loader page 1 for the DEFAULT view only (q === '' && type === 'all'). */\n initialPage?: GenerationPage\n}\n\nexport interface GenerationHistoryState {\n items: Generation[]\n hasMore: boolean\n isLoadingFirst: boolean\n isLoadingMore: boolean\n isError: boolean\n loadMore: () => void\n retry: () => void\n reload: () => void\n}\n\n/** Content identity keeps inline-equivalent loader pages from reseeding. */\nfunction seedSignature(page: GenerationPage | undefined): string {\n if (!page) return ''\n return `${page.nextCursor ?? ''}|${page.items.map((item) => item.id).join(',')}`\n}\n\n/** Cursor-paged generation history over the product-supplied data port. */\nexport function useGenerationHistory({\n fetchPage,\n q,\n type,\n initialPage,\n}: UseGenerationHistoryOptions): GenerationHistoryState {\n const startsFromSeed = q === '' && type === 'all' && initialPage !== undefined\n const [items, setItems] = useState<Generation[]>(startsFromSeed ? initialPage.items : [])\n const [nextCursor, setNextCursor] = useState<string | null>(\n startsFromSeed ? initialPage.nextCursor ?? null : null,\n )\n const [phase, setPhase] = useState<'idle' | 'loadingFirst' | 'loadingMore' | 'error'>(\n startsFromSeed ? 'idle' : 'loadingFirst',\n )\n const [reloadKey, setReloadKey] = useState(0)\n\n const seqRef = useRef(0)\n const resetAbortRef = useRef<AbortController | null>(null)\n const loadMoreAbortRef = useRef<AbortController | null>(null)\n const loadingFirstRef = useRef(!startsFromSeed)\n const loadingMoreRef = useRef(false)\n const lastOpRef = useRef<'first' | 'more'>('first')\n const seedEligibleRef = useRef(true)\n\n const nextCursorRef = useRef(nextCursor)\n nextCursorRef.current = nextCursor\n const viewRef = useRef({ q, type, fetchPage })\n viewRef.current = { q, type, fetchPage }\n const seedRef = useRef(initialPage)\n seedRef.current = initialPage\n\n const isDefaultView = q === '' && type === 'all'\n const seedKey = useMemo(() => seedSignature(initialPage), [initialPage])\n\n useEffect(() => {\n resetAbortRef.current?.abort()\n loadMoreAbortRef.current?.abort()\n loadingFirstRef.current = false\n loadingMoreRef.current = false\n const seq = ++seqRef.current\n\n if (!isDefaultView) seedEligibleRef.current = false\n const seed = seedRef.current\n if (isDefaultView && seedEligibleRef.current && reloadKey === 0 && seed) {\n setItems(seed.items)\n setNextCursor(seed.nextCursor ?? null)\n setPhase('idle')\n return\n }\n\n const controller = new AbortController()\n resetAbortRef.current = controller\n loadingFirstRef.current = true\n lastOpRef.current = 'first'\n setItems([])\n setNextCursor(null)\n setPhase('loadingFirst')\n\n void (async () => {\n try {\n const current = viewRef.current\n const page = await current.fetchPage({\n q: current.q.trim(),\n type: current.type,\n cursor: null,\n signal: controller.signal,\n })\n if (seq !== seqRef.current) return\n setItems(page.items)\n setNextCursor(page.nextCursor ?? null)\n setPhase('idle')\n } catch {\n if (controller.signal.aborted || seq !== seqRef.current) return\n setPhase('error')\n } finally {\n if (seq === seqRef.current) loadingFirstRef.current = false\n }\n })()\n\n return () => controller.abort()\n }, [q, type, seedKey, isDefaultView, reloadKey])\n\n const loadMore = useCallback(() => {\n const cursor = nextCursorRef.current\n if (!cursor || loadingFirstRef.current || loadingMoreRef.current) return\n\n const current = viewRef.current\n const seq = seqRef.current\n loadMoreAbortRef.current?.abort()\n const controller = new AbortController()\n loadMoreAbortRef.current = controller\n loadingMoreRef.current = true\n lastOpRef.current = 'more'\n setPhase('loadingMore')\n\n void (async () => {\n try {\n const page = await current.fetchPage({\n q: current.q.trim(),\n type: current.type,\n cursor,\n signal: controller.signal,\n })\n if (seq !== seqRef.current) return\n setItems((previous) => mergeGenerationPages(previous, page.items))\n setNextCursor(page.nextCursor ?? null)\n setPhase('idle')\n } catch {\n if (controller.signal.aborted || seq !== seqRef.current) return\n setPhase('error')\n } finally {\n if (seq === seqRef.current) loadingMoreRef.current = false\n }\n })()\n }, [])\n\n const retry = useCallback(() => {\n if (lastOpRef.current === 'more') loadMore()\n else setReloadKey((key) => key + 1)\n }, [loadMore])\n\n const reload = useCallback(() => {\n setReloadKey((key) => key + 1)\n }, [])\n\n useEffect(\n () => () => {\n resetAbortRef.current?.abort()\n loadMoreAbortRef.current?.abort()\n },\n [],\n )\n\n return {\n items,\n hasMore: nextCursor !== null,\n isLoadingFirst: phase === 'loadingFirst',\n isLoadingMore: phase === 'loadingMore',\n isError: phase === 'error',\n loadMore,\n retry,\n reload,\n }\n}\n"],"mappings":"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","names":["useEffect","useMemo","useRef","useState","useCallback","useEffect","useState","useCallback","useEffect","useState","jsx","jsxs","useState","useRef","useEffect","useMemo","jsx","jsxs","useEffect","useState","useEffect","useId","useRef","useState","jsx","jsxs","useRef","useId","useState","useEffect","useCallback","useEffect","useMemo","useRef","useState","jsx","value","stop","nodes","jsx","jsxs","useState","useEffect","useCallback","useEffect","useMemo","useRef","useState","Download","FolderOpen","FolderPlus","Pause","Play","Trash2","X","jsx","jsxs","useRef","useMemo","useEffect","useState","useCallback","Pause","Play","X","Download","FolderPlus","FolderOpen","Trash2","useCallback","useEffect","useMemo","useState","useEffect","useId","useRef","createPortal","jsx","jsxs","useRef","useId","useEffect","createPortal","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","createPortal","X","jsx","jsxs","createContext","useEffect","X","useState","useRef","useCallback","toast","useMemo","createPortal","useContext","useCallback","useRef","useRef","useCallback","useCallback","useEffect","useRef","useState","stop","useState","useRef","useCallback","useEffect","jsx","jsxs","useState","useCallback","useMemo","useEffect","useCallback","useEffect","useMemo","useRef","useState","Fragment","jsx","jsxs","useRef","stop","useState","useMemo","useCallback","useEffect","useCallback","useEffect","useMemo","useState","ArrowLeft","Download","FolderPlus","Trash2","X","useCallback","useEffect","useMemo","useRef","useState","useState","useRef","useMemo","useEffect","useCallback","jsx","jsxs","hasOpenPopover","stop","useState","useEffect","useMemo","useCallback","X","Download","FolderPlus","Trash2","ArrowLeft"]}