@tangle-network/agent-app 0.35.0 → 0.37.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/studio-react/studio-workspace.tsx","../../src/studio-react/use-studio-generations.ts","../../src/studio-react/composer-hero.tsx","../../src/studio-react/type-config.ts","../../src/studio-react/composer-shell.tsx","../../src/studio-react/image-composer.tsx","../../src/studio-react/video-composer.tsx","../../src/studio-react/speech-composer.tsx","../../src/studio-react/avatar-composer.tsx","../../src/studio-react/transcription-composer.tsx","../../src/studio-react/publish-package-composer.tsx","../../src/studio-react/studio-header.tsx","../../src/studio-react/result-canvas.tsx","../../src/studio-react/library-drawer.tsx","../../src/studio-react/studio-sheet.tsx","../../src/studio-react/generation-card.tsx","../../src/studio-react/generation-detail.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 { ComposerHero } from './composer-hero'\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 /** Integrations page href for the publish-package \"Connect\" link. Defaults to `/app/<workspaceId>/integrations`. */\n integrationsHref?: 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) and the integration-management affordances.\n */\nexport function StudioWorkspace({\n generations,\n totalCost,\n workspaceId,\n role,\n generationsEndpoint,\n vaultHref,\n integrationsHref,\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 const canManageIntegrations = role === 'owner' || role === 'admin'\n // Default the navigation hrefs to the conventional /app/<workspaceId>/… routes;\n // a host on a different route scheme passes its own builders.\n const resolvedVaultHref = vaultHref ?? (workspaceId\n ? (filePath?: string | null) => (filePath\n ? `/app/${workspaceId}/vault?file=${encodeURIComponent(filePath)}`\n : `/app/${workspaceId}/vault`)\n : undefined)\n const resolvedIntegrationsHref = integrationsHref\n ?? (workspaceId ? `/app/${workspaceId}/integrations` : 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 <ComposerHero\n workspaceId={workspaceId}\n integrationsHref={resolvedIntegrationsHref}\n canManageIntegrations={canManageIntegrations}\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 When workspace members generate images, video, voice, avatars, or transcripts, they 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","import { useEffect, useMemo, useRef, useState } from 'react'\nimport {\n Badge, Button, Input, Label, Textarea,\n Select, SelectContent, SelectItem, SelectTrigger, SelectValue,\n} from '@tangle-network/sandbox-ui/primitives'\nimport { useIntegrations } from '@tangle-network/sandbox-ui/integrations'\nimport { Sparkles } from 'lucide-react'\nimport {\n type Generation,\n type GenerationType,\n type MediaModelCatalogResponse,\n CADENCES,\n DESTINATIONS,\n buildGenerationRequestBody,\n buildPublishPackage,\n failedOptimisticGeneration,\n isDestinationConnected,\n modelMessage,\n normalizeImageCount,\n optimisticGeneration,\n outputPathFor,\n preferredModelId,\n selectedModelsWithDefaults,\n userSafeGenerationMessage,\n} from '../studio'\nimport { TYPE_CONFIG, typeConfigFor } from './type-config'\nimport { ComposerDisclosure, Field } from './composer-shell'\nimport { ImageComposer } from './image-composer'\nimport { VideoComposer } from './video-composer'\nimport { SpeechComposer } from './speech-composer'\nimport { AvatarComposer } from './avatar-composer'\nimport { TranscriptionComposer, TranscriptionOptions } from './transcription-composer'\nimport { PublishPackageComposer } from './publish-package-composer'\n\nconst HUB_BASE_URL = '/api/integrations/hub'\n\nconst SUGGESTIONS: Array<{ label: string; prompt: string; type: GenerationType }> = [\n {\n label: 'Storyboard frame',\n type: 'image',\n prompt: 'A vertical storyboard frame for a product launch teaser — dark cinematic lighting, sleek studio aesthetic, 9:16 composition.',\n },\n {\n label: 'Launch teaser clip',\n type: 'video',\n prompt: 'A 6-second vertical launch teaser: animated product reveal, dynamic camera move, hero music swell.',\n },\n {\n label: 'Scratch voiceover',\n type: 'speech',\n prompt: 'A confident 12-second scratch voiceover for a product launch teaser: warm tone, conversational pace.',\n },\n {\n label: 'Transcribe dailies',\n type: 'transcription',\n prompt: 'Transcribe the latest dailies recording with timestamps, speaker labels, and key story beats highlighted.',\n },\n]\n\nexport function ComposerHero({\n workspaceId,\n integrationsHref,\n canManageIntegrations,\n onGenerated,\n}: {\n workspaceId?: string\n integrationsHref?: string\n canManageIntegrations: boolean\n onGenerated: (generation: Generation) => void\n}) {\n const [type, setType] = useState<GenerationType>('image')\n const [prompt, setPrompt] = useState('')\n const [negativePrompt, setNegativePrompt] = useState('')\n const [outputPath, setOutputPath] = useState(outputPathFor('image'))\n const [caption, setCaption] = useState('')\n const [postDescription, setPostDescription] = useState('')\n const [mentions, setMentions] = useState('')\n const [cadence, setCadence] = useState(CADENCES[0] ?? 'Manual approval')\n const [selectedDestinations, setSelectedDestinations] = useState<string[]>([])\n const [size, setSize] = useState('1536x1024')\n const [quality, setQuality] = useState('high')\n const [imageCount, setImageCount] = useState(1)\n const [duration, setDuration] = useState('6')\n const [resolution, setResolution] = useState('720p')\n const [aspectRatio, setAspectRatio] = useState('16:9')\n const [referenceImageUrl, setReferenceImageUrl] = useState('')\n const [voice, setVoice] = useState('alloy')\n const [avatarAudioUrl, setAvatarAudioUrl] = useState('')\n const [avatarImageUrl, setAvatarImageUrl] = useState('')\n const [avatarId, setAvatarId] = useState('')\n const [transcriptionAudioUrl, setTranscriptionAudioUrl] = useState('')\n const [transcriptionLanguage, setTranscriptionLanguage] = useState('')\n const [transcriptionResponseFormat, setTranscriptionResponseFormat] = useState('json')\n const [transcriptionTemperature, setTranscriptionTemperature] = useState('0')\n const [mediaModels, setMediaModels] = useState<MediaModelCatalogResponse | null>(null)\n const [mediaModelsLoading, setMediaModelsLoading] = useState(false)\n const [mediaModelsError, setMediaModelsError] = useState<string | null>(null)\n const [selectedModels, setSelectedModels] = useState<Partial<Record<GenerationType, string>>>({})\n const submitLockRef = useRef(false)\n const [isSubmitting, setIsSubmitting] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const modelsForType = mediaModels?.models[type] ?? []\n const selectedModel = selectedModels[type] ?? preferredModelId(type, mediaModels) ?? ''\n const selectedModelOption = modelsForType.find((model) => model.id === selectedModel)\n const modelReady = Boolean(selectedModelOption)\n && selectedModelOption?.status !== 'unavailable'\n && !mediaModelsLoading\n && !mediaModelsError\n const hasRequiredInput = type === 'transcription'\n ? Boolean(transcriptionAudioUrl.trim())\n : type === 'avatar'\n ? Boolean(avatarAudioUrl.trim())\n : Boolean(prompt.trim())\n const canSubmit = Boolean(workspaceId)\n && modelReady\n && hasRequiredInput\n && !isSubmitting\n const integrations = useIntegrations({ apiBaseUrl: HUB_BASE_URL })\n const selectedConnectedDestinations = useMemo(() => selectedDestinations.filter((destinationId) => {\n const destination = DESTINATIONS.find((item) => item.id === destinationId)\n return Boolean(destination && isDestinationConnected(destination, integrations.connections))\n }), [integrations.connections, selectedDestinations])\n\n useEffect(() => {\n if (!workspaceId) return\n let cancelled = false\n setMediaModelsLoading(true)\n setMediaModelsError(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 setMediaModels(data)\n setSelectedModels((current) => selectedModelsWithDefaults(current, data))\n })\n .catch((err) => {\n if (cancelled) return\n setMediaModels(null)\n setMediaModelsError(err instanceof Error ? err.message : 'Could not load media models')\n })\n .finally(() => {\n if (!cancelled) setMediaModelsLoading(false)\n })\n\n return () => {\n cancelled = true\n }\n }, [workspaceId])\n\n function changeType(next: GenerationType) {\n setType(next)\n setOutputPath(outputPathFor(next))\n }\n\n async function generate() {\n if (!workspaceId || submitLockRef.current) return\n const promptText = prompt.trim()\n const avatarAudioUrlText = avatarAudioUrl.trim()\n const transcriptionAudioUrlText = transcriptionAudioUrl.trim()\n if (type !== 'transcription' && type !== 'avatar' && !promptText) {\n setError('prompt is required')\n return\n }\n if (type === 'avatar' && !avatarAudioUrlText) {\n setError('audioUrl is required')\n return\n }\n if (type === 'transcription' && !transcriptionAudioUrlText) {\n setError('audioUrl is required')\n return\n }\n if (!selectedModelOption || selectedModelOption.status === 'unavailable') {\n setError(`Select an available ${typeConfigFor(type).label} model`)\n return\n }\n if (cadence === 'Publish now' && selectedConnectedDestinations.length === 0) {\n setError('Select a connected destination to publish now')\n return\n }\n submitLockRef.current = true\n setIsSubmitting(true)\n setError(null)\n const clientRequestId = crypto.randomUUID()\n const requestedImageCount = type === 'image' ? normalizeImageCount(imageCount) : 1\n if (type === 'image' && requestedImageCount !== imageCount) setImageCount(requestedImageCount)\n const localGenerations = Array.from({ length: requestedImageCount }, (_, outputIndex) => optimisticGeneration({\n type,\n // avatar hides the prompt field (promptText is stale); transcription's is\n // an optional vocab hint. Never surface the source audio URL as the prompt.\n prompt: type === 'avatar' ? '' : promptText,\n model: selectedModel,\n clientRequestId,\n outputIndex: type === 'image' ? outputIndex : undefined,\n outputCount: type === 'image' ? requestedImageCount : undefined,\n }))\n localGenerations.slice().reverse().forEach(onGenerated)\n setPrompt('')\n let receivedServerGeneration = false\n try {\n const body = buildGenerationRequestBody({\n workspaceId,\n clientRequestId,\n type,\n model: selectedModel,\n prompt,\n negativePrompt,\n outputPath,\n publishPackage: buildPublishPackage({\n caption,\n postDescription,\n mentions,\n cadence,\n destinations: selectedConnectedDestinations,\n }),\n image: { size, quality, count: requestedImageCount },\n video: { duration, resolution, aspectRatio, referenceImageUrl },\n speech: { voice },\n avatar: { audioUrl: avatarAudioUrl, imageUrl: avatarImageUrl, avatarId },\n transcription: {\n audioUrl: transcriptionAudioUrl,\n language: transcriptionLanguage,\n responseFormat: transcriptionResponseFormat,\n temperature: transcriptionTemperature,\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 mediaTypes = Object.entries(TYPE_CONFIG) as Array<[GenerationType, typeof TYPE_CONFIG[string]]>\n const hasInlineParams = type === 'image' || type === 'video' || type === 'speech'\n\n return (\n <section className=\"rounded-xl border border-border bg-card p-5 shadow-sm\">\n <Field label=\"Media type\" className=\"space-y-1.5\">\n <div\n role=\"tablist\"\n aria-label=\"Generation type\"\n className=\"grid grid-cols-5 gap-1 rounded-lg border border-border bg-muted/40 p-1\"\n >\n {mediaTypes.map(([key, cfg]) => {\n const Icon = cfg.icon\n const active = type === key\n return (\n <button\n key={key}\n type=\"button\"\n role=\"tab\"\n aria-selected={active}\n onClick={() => changeType(key)}\n className={`flex items-center justify-center gap-1.5 rounded-md px-2 py-2 text-xs font-medium transition-all ${\n active\n ? 'bg-primary/15 text-primary shadow-sm ring-1 ring-inset ring-primary/30'\n : 'text-muted-foreground hover:bg-foreground/[0.04] hover:text-foreground'\n }`}\n >\n <Icon className=\"h-4 w-4 shrink-0\" />\n <span className=\"truncate\">{cfg.label}</span>\n </button>\n )\n })}\n </div>\n </Field>\n\n {type !== 'avatar' && (\n <div className=\"mt-4 space-y-2\">\n <Label htmlFor=\"studio-prompt\" className=\"block text-sm font-medium\">\n What do you want to create?\n </Label>\n <Textarea\n id=\"studio-prompt\"\n value={prompt}\n onChange={(event) => setPrompt(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n void generate()\n }\n }}\n rows={4}\n placeholder={type === 'transcription'\n ? 'Optional vocabulary, speaker names, timestamp style, or context...'\n : 'A vertical hero frame for a product launch teaser, dark cinematic lighting...'}\n className=\"resize-none text-sm\"\n />\n <div className=\"flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground\">\n <span>Try:</span>\n {SUGGESTIONS.map((suggestion) => (\n <button\n key={suggestion.label}\n type=\"button\"\n onClick={() => {\n setPrompt(suggestion.prompt)\n changeType(suggestion.type)\n }}\n className=\"rounded-full border border-border bg-background px-2.5 py-1 text-xs text-foreground/80 transition-colors hover:border-primary/40 hover:bg-muted\"\n >\n {suggestion.label}\n </button>\n ))}\n </div>\n {type === 'transcription' && (\n <p className=\"text-xs text-muted-foreground\">\n Optional — biases the transcript's spelling, vocabulary, and speaker names. It isn't an instruction for the model to follow.\n </p>\n )}\n </div>\n )}\n\n {type === 'avatar' && (\n <AvatarComposer\n audioUrl={avatarAudioUrl}\n imageUrl={avatarImageUrl}\n avatarId={avatarId}\n onAudioUrlChange={setAvatarAudioUrl}\n onImageUrlChange={setAvatarImageUrl}\n onAvatarIdChange={setAvatarId}\n />\n )}\n {type === 'transcription' && (\n <TranscriptionComposer\n audioUrl={transcriptionAudioUrl}\n language={transcriptionLanguage}\n onAudioUrlChange={setTranscriptionAudioUrl}\n onLanguageChange={setTranscriptionLanguage}\n />\n )}\n\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <Field\n label=\"Model\"\n htmlFor=\"studio-media-model\"\n className={`space-y-1.5 ${hasInlineParams ? '' : 'sm:col-span-2'}`}\n >\n <Select\n value={selectedModel || undefined}\n onValueChange={(value) => setSelectedModels((current) => ({ ...current, [type]: value }))}\n disabled={mediaModelsLoading || Boolean(mediaModelsError) || modelsForType.length === 0}\n >\n <SelectTrigger id=\"studio-media-model\" className=\"h-9 w-full\">\n <SelectValue placeholder={mediaModelsLoading ? 'Loading models…' : 'Select a model'} />\n </SelectTrigger>\n <SelectContent>\n {modelsForType.map((model) => (\n <SelectItem key={model.id} value={model.id} disabled={model.status === 'unavailable'}>\n <span className=\"flex w-full items-center justify-between gap-3\">\n <span className=\"truncate\">\n {model.name || model.id}{model.provider ? ` · ${model.provider}` : ''}\n </span>\n {model.status !== 'available' && (\n <span className=\"shrink-0 text-[10px] capitalize text-muted-foreground\">{model.status}</span>\n )}\n </span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </Field>\n {type === 'image' && (\n <ImageComposer\n size={size}\n quality={quality}\n imageCount={imageCount}\n onSizeChange={setSize}\n onQualityChange={setQuality}\n onImageCountChange={setImageCount}\n />\n )}\n {type === 'video' && (\n <VideoComposer\n duration={duration}\n resolution={resolution}\n aspectRatio={aspectRatio}\n referenceImageUrl={referenceImageUrl}\n onDurationChange={setDuration}\n onResolutionChange={setResolution}\n onAspectRatioChange={setAspectRatio}\n onReferenceImageUrlChange={setReferenceImageUrl}\n />\n )}\n {type === 'speech' && (\n <SpeechComposer voice={voice} onVoiceChange={setVoice} />\n )}\n </div>\n\n {(mediaModelsError || modelMessage(selectedModelOption, mediaModelsLoading, modelsForType.length)) && (\n <p className={`mt-2 text-xs ${mediaModelsError || selectedModelOption?.status === 'unavailable' ? 'text-destructive' : 'text-muted-foreground'}`}>\n {mediaModelsError ?? modelMessage(selectedModelOption, mediaModelsLoading, modelsForType.length)}\n </p>\n )}\n\n {error && (\n <div className=\"mt-3 rounded-md border border-destructive/20 bg-destructive/10 p-3 text-sm text-destructive\">\n {error}\n </div>\n )}\n\n <div className=\"mt-4 space-y-2\">\n <ComposerDisclosure summary=\"Advanced options\">\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <Field label=\"Negative prompt\">\n <Textarea\n value={negativePrompt}\n onChange={(event) => setNegativePrompt(event.target.value)}\n rows={2}\n placeholder=\"Avoid artifacts, off-style composition...\"\n />\n </Field>\n <Field label=\"Save to\">\n <Input value={outputPath} onChange={(event) => setOutputPath(event.target.value)} />\n </Field>\n {type === 'transcription' && (\n <TranscriptionOptions\n responseFormat={transcriptionResponseFormat}\n temperature={transcriptionTemperature}\n onResponseFormatChange={setTranscriptionResponseFormat}\n onTemperatureChange={setTranscriptionTemperature}\n />\n )}\n </div>\n </ComposerDisclosure>\n\n <ComposerDisclosure\n summary={(\n <>\n Schedule a post\n {selectedConnectedDestinations.length > 0 && (\n <Badge variant=\"outline\" className=\"ml-1 text-[10px]\">\n {selectedConnectedDestinations.length}\n </Badge>\n )}\n </>\n )}\n >\n <PublishPackageComposer\n caption={caption}\n postDescription={postDescription}\n mentions={mentions}\n cadence={cadence}\n selectedDestinations={selectedDestinations}\n connections={integrations.connections}\n connectionError={integrations.error}\n connectionsLoading={integrations.isLoading}\n integrationsHref={integrationsHref}\n canManageIntegrations={canManageIntegrations}\n onCaptionChange={setCaption}\n onDescriptionChange={setPostDescription}\n onMentionsChange={setMentions}\n onCadenceChange={setCadence}\n onDestinationToggle={(destination) => {\n const destinationConfig = DESTINATIONS.find((item) => item.id === destination)\n if (!destinationConfig || !isDestinationConnected(destinationConfig, integrations.connections)) return\n setSelectedDestinations((current) => current.includes(destination)\n ? current.filter((item) => item !== destination)\n : [...current, destination])\n }}\n />\n </ComposerDisclosure>\n </div>\n\n <Button onClick={generate} disabled={!canSubmit} size=\"lg\" className=\"mt-4 w-full\">\n <Sparkles className=\"mr-2 h-4 w-4\" />\n Generate\n <span className=\"ml-2 text-[10px] opacity-60\">⌘↵</span>\n </Button>\n </section>\n )\n}\n","import { Film, FileText, Image, Mic, Video } from 'lucide-react'\n\nexport interface TypeConfig {\n label: string\n icon: typeof Image\n color: string\n}\n\nconst IMAGE: TypeConfig = { label: 'Image', icon: Image, color: 'bg-blue-500/10 text-blue-600 border-blue-500/20' }\n\n// string-keyed so list cards can index by Generation.type\nexport const TYPE_CONFIG: Record<string, TypeConfig> = {\n image: IMAGE,\n video: { label: 'Video', icon: Video, color: 'bg-red-500/10 text-red-600 border-red-500/20' },\n avatar: { label: 'Avatar', icon: Film, color: 'bg-purple-500/10 text-purple-600 border-purple-500/20' },\n speech: { label: 'Audio', icon: Mic, color: 'bg-orange-500/10 text-orange-600 border-orange-500/20' },\n transcription: { label: 'Transcript', icon: FileText, color: 'bg-emerald-500/10 text-emerald-600 border-emerald-500/20' },\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.\nexport function typeConfigFor(type: string): TypeConfig {\n return TYPE_CONFIG[type] ?? IMAGE\n}\n","import type { ReactNode, SelectHTMLAttributes } from 'react'\nimport { Label } from '@tangle-network/sandbox-ui/primitives'\nimport { ChevronRight } from 'lucide-react'\n\nexport function Field({\n label,\n htmlFor,\n className = 'space-y-1.5',\n children,\n}: {\n label: string\n htmlFor?: string\n className?: string\n children: ReactNode\n}) {\n return (\n <div className={className}>\n <Label htmlFor={htmlFor} className=\"text-sm\">{label}</Label>\n {children}\n </div>\n )\n}\n\nexport function ComposerDisclosure({ summary, children }: { summary: ReactNode; children: ReactNode }) {\n return (\n <details className=\"group rounded-lg border border-border bg-muted/30 transition-colors open:bg-muted/40\">\n <summary className=\"flex cursor-pointer list-none items-center gap-1.5 px-3 py-2.5 text-xs font-medium text-foreground/80 transition-colors hover:text-foreground [&::-webkit-details-marker]:hidden\">\n <ChevronRight className=\"h-3.5 w-3.5 text-muted-foreground transition-transform group-open:rotate-90\" />\n {summary}\n </summary>\n <div className=\"border-t border-border px-3 py-3\">{children}</div>\n </details>\n )\n}\n\nexport function NativeSelect(props: SelectHTMLAttributes<HTMLSelectElement>) {\n return (\n <select {...props} className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\" />\n )\n}\n","import { Input } from '@tangle-network/sandbox-ui/primitives'\nimport { MIN_IMAGE_COUNT, MAX_IMAGE_COUNT, normalizeImageCount } from '../studio'\nimport { Field } from './composer-shell'\n\nexport function ImageComposer({\n size,\n quality,\n imageCount,\n onSizeChange,\n onQualityChange,\n onImageCountChange,\n}: {\n size: string\n quality: string\n imageCount: number\n onSizeChange: (value: string) => void\n onQualityChange: (value: string) => void\n onImageCountChange: (value: number) => void\n}) {\n return (\n <>\n <Field label=\"Size\"><Input value={size} onChange={(event) => onSizeChange(event.target.value)} /></Field>\n <Field label=\"Quality\"><Input value={quality} onChange={(event) => onQualityChange(event.target.value)} /></Field>\n <Field label=\"Images\"><Input type=\"number\" min={MIN_IMAGE_COUNT} max={MAX_IMAGE_COUNT} value={imageCount} onChange={(event) => onImageCountChange(normalizeImageCount(event.target.value))} /></Field>\n </>\n )\n}\n","import { Input } from '@tangle-network/sandbox-ui/primitives'\nimport { Field } from './composer-shell'\n\nexport function VideoComposer({\n duration,\n resolution,\n aspectRatio,\n referenceImageUrl,\n onDurationChange,\n onResolutionChange,\n onAspectRatioChange,\n onReferenceImageUrlChange,\n}: {\n duration: string\n resolution: string\n aspectRatio: string\n referenceImageUrl: string\n onDurationChange: (value: string) => void\n onResolutionChange: (value: string) => void\n onAspectRatioChange: (value: string) => void\n onReferenceImageUrlChange: (value: string) => void\n}) {\n return (\n <>\n <Field label=\"Duration (s)\"><Input value={duration} onChange={(event) => onDurationChange(event.target.value)} /></Field>\n <Field label=\"Resolution\"><Input value={resolution} onChange={(event) => onResolutionChange(event.target.value)} /></Field>\n <Field label=\"Aspect ratio\"><Input value={aspectRatio} onChange={(event) => onAspectRatioChange(event.target.value)} /></Field>\n <Field label=\"Reference image URL\"><Input value={referenceImageUrl} onChange={(event) => onReferenceImageUrlChange(event.target.value)} /></Field>\n </>\n )\n}\n","import { Input } from '@tangle-network/sandbox-ui/primitives'\nimport { Field } from './composer-shell'\n\nexport function SpeechComposer({\n voice,\n onVoiceChange,\n}: {\n voice: string\n onVoiceChange: (value: string) => void\n}) {\n return (\n <Field label=\"Voice\"><Input value={voice} onChange={(event) => onVoiceChange(event.target.value)} /></Field>\n )\n}\n","import { Input } from '@tangle-network/sandbox-ui/primitives'\nimport { Field } from './composer-shell'\n\nexport function AvatarComposer({\n audioUrl,\n imageUrl,\n avatarId,\n onAudioUrlChange,\n onImageUrlChange,\n onAvatarIdChange,\n}: {\n audioUrl: string\n imageUrl: string\n avatarId: string\n onAudioUrlChange: (value: string) => void\n onImageUrlChange: (value: string) => void\n onAvatarIdChange: (value: string) => void\n}) {\n return (\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <Field label=\"Audio URL\" htmlFor=\"studio-audio-url\">\n <Input\n id=\"studio-audio-url\"\n value={audioUrl}\n onChange={(event) => onAudioUrlChange(event.target.value)}\n placeholder=\"https://cdn.example.com/source-audio.mp3\"\n />\n </Field>\n <Field label=\"Image URL\" htmlFor=\"studio-avatar-image-url\">\n <Input\n id=\"studio-avatar-image-url\"\n value={imageUrl}\n onChange={(event) => onImageUrlChange(event.target.value)}\n placeholder=\"https://cdn.example.com/portrait.png\"\n />\n </Field>\n <Field label=\"Avatar ID\" htmlFor=\"studio-avatar-id\">\n <Input\n id=\"studio-avatar-id\"\n value={avatarId}\n onChange={(event) => onAvatarIdChange(event.target.value)}\n placeholder=\"Optional provider avatar id\"\n />\n </Field>\n </div>\n )\n}\n","import { Input } from '@tangle-network/sandbox-ui/primitives'\nimport { Field, NativeSelect } from './composer-shell'\n\nexport function TranscriptionComposer({\n audioUrl,\n language,\n onAudioUrlChange,\n onLanguageChange,\n}: {\n audioUrl: string\n language: string\n onAudioUrlChange: (value: string) => void\n onLanguageChange: (value: string) => void\n}) {\n return (\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2\">\n <Field label=\"Audio URL\" htmlFor=\"studio-audio-url\">\n <Input\n id=\"studio-audio-url\"\n value={audioUrl}\n onChange={(event) => onAudioUrlChange(event.target.value)}\n placeholder=\"https://cdn.example.com/source-audio.mp3\"\n />\n </Field>\n <Field label=\"Language\" htmlFor=\"studio-transcription-language\">\n <Input\n id=\"studio-transcription-language\"\n value={language}\n onChange={(event) => onLanguageChange(event.target.value)}\n placeholder=\"en\"\n />\n </Field>\n </div>\n )\n}\n\nexport function TranscriptionOptions({\n responseFormat,\n temperature,\n onResponseFormatChange,\n onTemperatureChange,\n}: {\n responseFormat: string\n temperature: string\n onResponseFormatChange: (value: string) => void\n onTemperatureChange: (value: string) => void\n}) {\n return (\n <>\n <Field label=\"Response format\">\n <NativeSelect value={responseFormat} onChange={(event) => onResponseFormatChange(event.target.value)}>\n <option value=\"json\">JSON</option>\n <option value=\"text\">Text</option>\n <option value=\"srt\">SRT</option>\n <option value=\"verbose_json\">Verbose JSON</option>\n <option value=\"vtt\">VTT</option>\n </NativeSelect>\n </Field>\n <Field label=\"Temperature\"><Input type=\"number\" min=\"0\" max=\"1\" step=\"0.1\" value={temperature} onChange={(event) => onTemperatureChange(event.target.value)} /></Field>\n </>\n )\n}\n","import { Link } from 'react-router'\nimport { Badge, Button, Input, Textarea } from '@tangle-network/sandbox-ui/primitives'\nimport type { IntegrationConnection } from '@tangle-network/sandbox-ui/integrations'\nimport { AlertTriangle, CalendarClock, Check } from 'lucide-react'\nimport { CADENCES, DESTINATIONS, isDestinationConnected } from '../studio'\nimport { Field, NativeSelect } from './composer-shell'\n\nexport function PublishPackageComposer({\n caption,\n postDescription,\n mentions,\n cadence,\n selectedDestinations,\n connections,\n connectionError,\n connectionsLoading,\n integrationsHref,\n canManageIntegrations,\n onCaptionChange,\n onDescriptionChange,\n onMentionsChange,\n onCadenceChange,\n onDestinationToggle,\n}: {\n caption: string\n postDescription: string\n mentions: string\n cadence: string\n selectedDestinations: string[]\n connections: IntegrationConnection[]\n connectionError: Error | null\n connectionsLoading: boolean\n integrationsHref?: string\n canManageIntegrations: boolean\n onCaptionChange: (value: string) => void\n onDescriptionChange: (value: string) => void\n onMentionsChange: (value: string) => void\n onCadenceChange: (value: string) => void\n onDestinationToggle: (destination: string) => void\n}) {\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n {connectionError ? (\n <div className=\"flex items-center gap-2 rounded-md border border-warning/20 bg-warning/5 px-2.5 py-1.5 text-xs text-warning\">\n <AlertTriangle className=\"h-3.5 w-3.5 shrink-0\" />\n Couldn't load connected apps. Check Integrations.\n </div>\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {connectionsLoading\n ? 'Checking connected apps…'\n : 'Stage captions, destinations, cadence. Saved with the generated asset.'}\n </p>\n )}\n {integrationsHref && canManageIntegrations && (\n <Link to={integrationsHref} prefetch=\"intent\" className=\"shrink-0\">\n <Button variant=\"outline\" size=\"sm\">Connect</Button>\n </Link>\n )}\n </div>\n <div className=\"grid gap-2\">\n {DESTINATIONS.map((destination) => {\n const connected = isDestinationConnected(destination, connections)\n const active = connected && selectedDestinations.includes(destination.id)\n return (\n <button\n key={destination.id}\n type=\"button\"\n disabled={!connected}\n aria-pressed={active}\n onClick={() => onDestinationToggle(destination.id)}\n className={`rounded-md border p-2 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60 ${\n active\n ? 'border-primary bg-primary/5 ring-1 ring-primary/20'\n : 'border-border hover:bg-muted/50'\n }`}\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"flex items-center gap-1.5 text-xs font-medium text-foreground\">\n {active && <Check className=\"h-3.5 w-3.5 text-primary\" />}\n {destination.label}\n </span>\n <Badge variant={active || connected ? 'default' : 'outline'}>{active ? 'Selected' : connected ? 'Ready' : 'Not connected'}</Badge>\n </div>\n <p className=\"mt-1 text-xs text-muted-foreground\">{destination.fields}</p>\n </button>\n )\n })}\n </div>\n <Field label=\"Caption\" className=\"space-y-2\">\n <Textarea value={caption} onChange={(event) => onCaptionChange(event.target.value)} rows={3} placeholder=\"Write or generate the caption for selected destinations...\" />\n </Field>\n <Field label=\"Description / CTA\" className=\"space-y-2\">\n <Textarea value={postDescription} onChange={(event) => onDescriptionChange(event.target.value)} rows={2} placeholder=\"Release note, product context, link, or approval instruction...\" />\n </Field>\n <div className=\"grid grid-cols-2 gap-3\">\n <Field label=\"Mentions\" className=\"space-y-2\">\n <Input value={mentions} onChange={(event) => onMentionsChange(event.target.value)} placeholder=\"@creator, @partner\" />\n </Field>\n <Field label=\"Cadence\" className=\"space-y-2\">\n <NativeSelect value={cadence} onChange={(event) => onCadenceChange(event.target.value)}>\n {CADENCES.map((option) => <option key={option} value={option}>{option}</option>)}\n </NativeSelect>\n </Field>\n </div>\n <div className=\"flex items-center gap-2 rounded-md bg-muted/40 p-2 text-xs text-muted-foreground\">\n <CalendarClock className=\"h-3.5 w-3.5\" />\n Publish packages stay attached to generated media and can run through connected GTM apps.\n </div>\n </div>\n )\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-background/80 backdrop-blur-md\">\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 images, video, voice, avatars, and transcripts your team has created.'}\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-[10px] 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 Write a prompt above and hit Generate — your latest result blooms here, and everything lives in your library.\n </p>\n </section>\n )\n }\n\n const cfg = typeConfigFor(batch[0]?.type ?? 'image')\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} text-[10px]`}>{cfg.label}</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-muted/40 transition-colors hover:border-primary/40\"\n >\n <CanvasTile generation={generation} />\n </button>\n ))}\n </div>\n\n {batch.length > 1 && (\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 { useMemo } from 'react'\nimport { Link } from 'react-router'\nimport {\n Button, EmptyState,\n Tabs, TabsList, TabsTrigger,\n} from '@tangle-network/sandbox-ui/primitives'\nimport { ArrowLeft, DollarSign, Film, FolderOpen, Sparkles, X } from 'lucide-react'\nimport {\n type Generation,\n isGenerationType,\n} from '../studio'\nimport { TYPE_CONFIG } from './type-config'\nimport { StudioSheet } from './studio-sheet'\nimport { GenerationCard } from './generation-card'\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(() => {\n if (!typeFilter || !isGenerationType(typeFilter)) return generations\n return generations.filter((generation) => generation.type === typeFilter)\n }, [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 {visible.length === 0 ? (\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 <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {visible.map((generation) => (\n <GenerationCard key={generation.id} generation={generation} onSelect={onSelect} />\n ))}\n </div>\n )}\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 backdrop-blur-sm\" />\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-border bg-card shadow-lg 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 { Card, CardContent, Badge } from '@tangle-network/sandbox-ui/primitives'\nimport { Send } from 'lucide-react'\nimport {\n type Generation,\n generationError,\n generationStatus,\n isPublishPackage,\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 const publishPackage = generation.metadata?.publishPackage\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-muted/50\">\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 <div className=\"flex items-center gap-2\">\n <Icon className=\"h-6 w-6 text-muted-foreground/40\" />\n <span className=\"text-xs text-muted-foreground\">Audio</span>\n </div>\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} text-[10px] backdrop-blur-sm`}>\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-xs text-foreground\">{generation.prompt}</p>\n {status === 'failed' && (\n <p className=\"mb-2 line-clamp-2 text-[10px] text-destructive\">{generationError(generation)}</p>\n )}\n <div className=\"flex items-center justify-between\">\n {generation.model && (\n <span className=\"truncate text-[10px] 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-[10px] text-muted-foreground\">${generation.cost.toFixed(3)}</span>\n )}\n <span className=\"text-[10px] text-muted-foreground\">{relativeTime(generation.createdAt)}</span>\n </div>\n </div>\n {isPublishPackage(publishPackage) && (\n <div className=\"mt-3 rounded-md border border-border bg-muted/30 p-2\">\n <div className=\"mb-1 flex items-center gap-1.5 text-[10px] font-medium text-foreground\">\n <Send className=\"h-3 w-3\" />\n {(publishPackage.destinations ?? []).join(', ') || 'Publish package'}\n </div>\n <p className=\"line-clamp-2 text-[10px] text-muted-foreground\">\n {publishPackage.caption || 'Caption pending'}\n </p>\n </div>\n )}\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 const className = status === 'failed'\n ? 'border-destructive/25 bg-destructive/10 text-destructive'\n : 'border-warning/25 bg-warning/10 text-warning'\n\n return (\n <div className={inline ? '' : 'absolute bottom-2 left-2'}>\n <Badge variant=\"outline\" className={`${className} text-[10px] backdrop-blur-sm`}>\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-muted/50\">\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}>{cfg.label}</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-medium 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-[10px] font-medium 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-[10px] font-medium text-muted-foreground\">Cost</span>\n <span className=\"text-xs text-foreground\">${generation.cost.toFixed(4)}</span>\n </div>\n )}\n {generation.createdAt && (\n <div>\n <span className=\"block text-[10px] font-medium text-muted-foreground\">Created</span>\n <span className=\"text-xs text-foreground\">{new Date(generation.createdAt).toLocaleString()}</span>\n </div>\n )}\n </div>\n\n {generation.type === 'transcription' && generation.result && (\n <div>\n <span className=\"mb-1 block text-xs font-medium text-muted-foreground\">Transcription</span>\n <pre className=\"max-h-64 overflow-y-auto whitespace-pre-wrap rounded-lg bg-muted/50 p-4 text-sm 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"],"mappings":"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","names":["useRef","useState","Button","Images","useEffect","useMemo","useRef","useState","Badge","Button","Input","Label","Textarea","jsx","jsxs","Input","Fragment","jsx","jsxs","Input","Input","jsx","Input","Input","jsx","jsxs","Input","Input","Fragment","jsx","jsxs","Input","Input","jsx","jsxs","Input","Fragment","jsx","jsxs","useState","useRef","useMemo","useEffect","Label","Textarea","Input","Badge","Button","Button","jsx","jsxs","Badge","Sparkles","jsx","jsxs","Sparkles","Badge","useMemo","Link","Button","Film","FolderOpen","Sparkles","jsx","jsxs","Badge","jsx","jsxs","Badge","Link","Badge","Button","jsx","jsxs","Badge","Link","Button","Fragment","jsx","jsxs","useMemo","Link","Button","FolderOpen","Film","Sparkles","Fragment","jsx","jsxs","useState","useRef","Images","Button"]}
@@ -0,0 +1,73 @@
1
+ /*
2
+ * Studio animations: the right-side library sheet slide/fade, the result-canvas
3
+ * bloom-in, and the neutral skeleton shimmer. Ships as a raw stylesheet (the
4
+ * `./styles` precedent) so apps that don't already define these classes can
5
+ * `import '@tangle-network/agent-app/studio-react/styles'` once. Token-driven
6
+ * (`--muted`, `--muted-foreground`) so it inherits the consuming app's theme.
7
+ */
8
+
9
+ /* ── Studio: library sheet + result canvas ── */
10
+ @keyframes studio-sheet-in {
11
+ from { transform: translateX(100%); }
12
+ to { transform: translateX(0); }
13
+ }
14
+ @keyframes studio-sheet-out {
15
+ from { transform: translateX(0); }
16
+ to { transform: translateX(100%); }
17
+ }
18
+ @keyframes studio-overlay-fade {
19
+ from { opacity: 0; }
20
+ to { opacity: 1; }
21
+ }
22
+ @keyframes studio-bloom {
23
+ from { opacity: 0; transform: translateY(6px) scale(0.99); }
24
+ to { opacity: 1; transform: translateY(0) scale(1); }
25
+ }
26
+
27
+ /* Dedicated classes (not tailwindcss-animate .animate-in) so the right-side
28
+ sheet is unaffected by a conflicting .animate-in rule. Driven by Radix's
29
+ data-state on the dialog parts. */
30
+ .studio-sheet-content[data-state='open'] { animation: studio-sheet-in 0.28s cubic-bezier(0.32, 0.72, 0, 1); }
31
+ .studio-sheet-content[data-state='closed'] { animation: studio-sheet-out 0.2s cubic-bezier(0.4, 0, 1, 1); }
32
+ .studio-sheet-overlay[data-state='open'] { animation: studio-overlay-fade 0.28s ease-out; }
33
+ .studio-sheet-overlay[data-state='closed'] { animation: studio-overlay-fade 0.2s ease-in reverse; }
34
+ .studio-bloom { animation: studio-bloom 0.3s cubic-bezier(0.22, 1, 0.36, 1); }
35
+
36
+ /* Neutral skeleton shimmer — a soft grey band sweeps the muted surface.
37
+ `.shimmer` is an alias so the generation card and result canvas share one
38
+ skeleton treatment. */
39
+ @keyframes studio-shimmer {
40
+ 0% { background-position: 200% 0; }
41
+ 100% { background-position: -200% 0; }
42
+ }
43
+ .studio-shimmer,
44
+ .shimmer {
45
+ background-color: hsl(var(--muted));
46
+ background-image: linear-gradient(
47
+ 90deg,
48
+ transparent 0%,
49
+ hsl(var(--muted-foreground) / 0.22) 50%,
50
+ transparent 100%
51
+ );
52
+ background-size: 200% 100%;
53
+ background-repeat: no-repeat;
54
+ animation: studio-shimmer 1.7s ease-in-out infinite;
55
+ }
56
+
57
+ /* Library card entrance. */
58
+ @keyframes studio-row-in {
59
+ from { opacity: 0; transform: translateY(4px); }
60
+ to { opacity: 1; transform: translateY(0); }
61
+ }
62
+ .animate-row-in { animation: studio-row-in 0.25s ease-out both; }
63
+
64
+ @media (prefers-reduced-motion: reduce) {
65
+ .studio-sheet-content[data-state='open'],
66
+ .studio-sheet-content[data-state='closed'],
67
+ .studio-sheet-overlay[data-state='open'],
68
+ .studio-sheet-overlay[data-state='closed'],
69
+ .studio-bloom,
70
+ .studio-shimmer,
71
+ .shimmer,
72
+ .animate-row-in { animation: none; }
73
+ }
@@ -6,7 +6,7 @@ import {
6
6
  restrictTaxonomy,
7
7
  verifyCapabilityToken,
8
8
  verifyExpiringCapabilityToken
9
- } from "../chunk-MH6AVXQ7.js";
9
+ } from "../chunk-VT5DI6GL.js";
10
10
  import {
11
11
  DEFAULT_APP_TOOL_PATHS,
12
12
  DEFAULT_HEADER_NAMES,
@@ -7,7 +7,7 @@ import {
7
7
  } from "../chunk-5VXPDXZJ.js";
8
8
 
9
9
  // src/web-react/index.tsx
10
- import { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef3, useState as useState6, memo } from "react";
10
+ import { useEffect as useEffect5, useMemo as useMemo3, useRef as useRef4, useState as useState6, memo } from "react";
11
11
 
12
12
  // src/web-react/smooth-text.ts
13
13
  import { useEffect, useRef, useState } from "react";
@@ -740,7 +740,7 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
740
740
  }
741
741
 
742
742
  // src/web-react/sandbox-terminal.ts
743
- import { useCallback as useCallback2, useEffect as useEffect4, useState as useState4 } from "react";
743
+ import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
744
744
  var DEFAULT_PROVISION_POLL_INTERVAL_MS = 2e3;
745
745
  var DEFAULT_PROVISION_POLL_TIMEOUT_MS = 9e4;
746
746
  var DEFAULT_TOKEN_REFRESH_SKEW_MS = 12e4;
@@ -755,6 +755,8 @@ var EMPTY_CONNECTION = {
755
755
  };
756
756
  function useSandboxTerminalConnection(opts) {
757
757
  const [conn, setConn] = useState4(EMPTY_CONNECTION);
758
+ const mountedRef = useRef3(false);
759
+ const generationRef = useRef3(0);
758
760
  const fetcher = opts.fetcher ?? fetch;
759
761
  const pollIntervalMs = opts.provisionPollIntervalMs ?? DEFAULT_PROVISION_POLL_INTERVAL_MS;
760
762
  const pollTimeoutMs = opts.provisionPollTimeoutMs ?? DEFAULT_PROVISION_POLL_TIMEOUT_MS;
@@ -764,17 +766,25 @@ function useSandboxTerminalConnection(opts) {
764
766
  return opts.connectionUrl ?? `/api/workspaces/${encodeURIComponent(opts.workspaceId)}/sandbox/connection`;
765
767
  }, [opts.connectionUrl, opts.workspaceId]);
766
768
  const connect = useCallback2(async () => {
767
- setConn((current) => ({ ...current, loading: true, error: null }));
769
+ const generation = generationRef.current + 1;
770
+ generationRef.current = generation;
771
+ const isCurrent = () => mountedRef.current && generationRef.current === generation;
772
+ const setCurrentConn = (value) => {
773
+ if (!isCurrent()) return;
774
+ setConn(value);
775
+ };
776
+ setCurrentConn((current) => ({ ...current, loading: true, error: null }));
768
777
  const deadline = Date.now() + pollTimeoutMs;
769
- while (true) {
778
+ while (isCurrent()) {
770
779
  try {
771
780
  const res = await fetcher(connectionUrl());
772
781
  const data = await res.json();
782
+ if (!isCurrent()) return;
773
783
  const runtimeUrl = data.runtimeUrl ?? data.sidecarUrl;
774
784
  if (res.ok && runtimeUrl && data.token && data.expiresAt) {
775
- setConn({
785
+ setCurrentConn({
776
786
  runtimeUrl,
777
- sidecarUrl: runtimeUrl,
787
+ sidecarUrl: data.sidecarUrl ?? runtimeUrl,
778
788
  token: data.token,
779
789
  expiresAt: data.expiresAt,
780
790
  status: data.status ?? "running",
@@ -785,7 +795,7 @@ function useSandboxTerminalConnection(opts) {
785
795
  return;
786
796
  }
787
797
  if (res.ok) {
788
- setConn({
798
+ setCurrentConn({
789
799
  runtimeUrl: null,
790
800
  sidecarUrl: null,
791
801
  token: null,
@@ -798,7 +808,7 @@ function useSandboxTerminalConnection(opts) {
798
808
  return;
799
809
  }
800
810
  if (res.status === 503 && Date.now() < deadline) {
801
- setConn((current) => ({
811
+ setCurrentConn((current) => ({
802
812
  ...current,
803
813
  loading: true,
804
814
  status: data.status ?? "provisioning",
@@ -807,7 +817,7 @@ function useSandboxTerminalConnection(opts) {
807
817
  await sleep(pollIntervalMs);
808
818
  continue;
809
819
  }
810
- setConn((current) => ({
820
+ setCurrentConn((current) => ({
811
821
  ...current,
812
822
  runtimeUrl: null,
813
823
  sidecarUrl: null,
@@ -819,11 +829,12 @@ function useSandboxTerminalConnection(opts) {
819
829
  }));
820
830
  return;
821
831
  } catch (err) {
832
+ if (!isCurrent()) return;
822
833
  if (Date.now() < deadline) {
823
834
  await sleep(pollIntervalMs);
824
835
  continue;
825
836
  }
826
- setConn((current) => ({
837
+ setCurrentConn((current) => ({
827
838
  ...current,
828
839
  runtimeUrl: null,
829
840
  sidecarUrl: null,
@@ -836,6 +847,13 @@ function useSandboxTerminalConnection(opts) {
836
847
  }
837
848
  }
838
849
  }, [connectionUrl, fetcher, pollIntervalMs, pollTimeoutMs]);
850
+ useEffect4(() => {
851
+ mountedRef.current = true;
852
+ return () => {
853
+ mountedRef.current = false;
854
+ generationRef.current += 1;
855
+ };
856
+ }, []);
839
857
  useEffect4(() => {
840
858
  void connect();
841
859
  }, [connect]);
@@ -1338,9 +1356,9 @@ function AssistantMessageImpl({
1338
1356
  const content = useSmoothText(msg.content, streaming);
1339
1357
  const reasoning = useSmoothText(msg.reasoning ?? "", streaming);
1340
1358
  const body = useMemo3(() => renderBody(content), [renderBody, content]);
1341
- const reasoningScrollRef = useRef3(null);
1342
- const thinkStartRef = useRef3(null);
1343
- const thinkMsRef = useRef3(null);
1359
+ const reasoningScrollRef = useRef4(null);
1360
+ const thinkStartRef = useRef4(null);
1361
+ const thinkMsRef = useRef4(null);
1344
1362
  if (streaming && reasoning && !content && thinkStartRef.current === null) {
1345
1363
  thinkStartRef.current = performance.now();
1346
1364
  }