export const PREVIEW_PORTAL_SURFACES = [ 'edit', 'review', 'client-review', 'preview', 'compare', 'index', // The live run dashboard: per-generation status badges + contract/diff cards + // playable in-progress clips + an event log, auto-refreshing against on-disk // state. Driven by show-bible (Show › Episode › Generation), scene-candidates // (status), run-contract (the diff-vs-contract alarm baseline), and // events.jsonl (the log). 'run', ] as const; export const PREVIEW_PORTAL_STATUSES = [ 'draft', 'editor-review', 'changes-requested', 'client-review', 'client-declined', 'client-approved', 'final', 'published', 'archived', ] as const; export const PREVIEW_PORTAL_TEMPLATES = [ 'music-video', 'story-film', 'documentary', 'product-ad', 'sports-recap', 'generic-video', ] as const; export type PreviewPortalSurface = typeof PREVIEW_PORTAL_SURFACES[number]; export type PreviewPortalStatus = typeof PREVIEW_PORTAL_STATUSES[number]; export type PreviewPortalTemplateId = typeof PREVIEW_PORTAL_TEMPLATES[number]; export interface PreviewPortalTemplate { id: PreviewPortalTemplateId; name: string; heroLabel: string; primaryAssetLabel: string; sectionOrder: string[]; sectionLabels: Record; } export interface PreviewPortalAsset { id: string; path: string; kind: 'image' | 'video' | 'audio' | 'html' | 'json' | 'text' | 'other'; label: string; section: string; sizeBytes?: number; exists: boolean; /** ISO timestamp of when the file was created/downloaded on disk (file mtime). * Rendered on each card so the operator sees when an asset landed. */ createdAt?: string; /** Scene index for keyframe/generation-input assets, used to pair them with * their storyboard beat. Carried through from the asset-manifest. */ sceneIndex?: number; /** Real pixel dimensions probed at discovery (ffprobe for video, header parse * for stills). Drive PER-ASSET card sizing: a 9:16 deliverable must render in * a portrait card even inside a 16:9 project. Unset when probing fails. */ width?: number; height?: number; orientation?: 'portrait' | 'square' | 'landscape'; } export interface PreviewPortalBriefMetadata { platform?: string; executionProfile?: { aspectRatio?: string; quality?: string; resolution?: string; generateAudio?: boolean; outputCount?: number; }; [key: string]: unknown; } /** Brief artifact subset rendered in the preview header. */ export interface PreviewPortalBrief { title: string; intent: string; metadata?: PreviewPortalBriefMetadata; } export interface PreviewPortalScene { sceneIndex: number; description: string; characters?: string[]; scenePrompt?: { imagePrompt?: string; animationPrompt?: string; cameraMove?: string; styleFooter?: string; }; /** * The exact provider submit text for this scene — the resolved * `filmmaking-prompts.json` seedance packet (`seedancePackets[].promptText`, * the 10-block Joey master prompt) matched by sceneIndex. This is the * "what the model actually receives" contract; when present it is the * authoritative prompt rendered in the scene card, above the lighter * storyboard `scenePrompt` fields. */ renderPrompt?: string; /** * The reference slots this scene will submit (`seedancePackets[].references`), * each with its role, ready/pending status, and — for character-sheet slots — * the bound `Asset://` URI resolved from `seedance-assets.json`. Surfacing this * lets the operator confirm identity-lock (which avatar locks which scene, and * whether every required reference is ready) before any render spend. */ referenceSlots?: PreviewPortalReferenceSlot[]; /** * Optional narrative color-language state for this scene (the grade id, e.g. * `cool-steel` / `crimson-threat` / `electric-blue`). Rendered as a swatched * chip in the contract card so the operator sees the intended look per scene, * and applied as the per-segment grade at assemble time. */ colorState?: string; dialogue?: string; durationSeconds?: number; /** * Project-relative path to this scene's rendered clip * (`outputs/scene-.mp4`), present only when the file exists on disk. * When set, the storyboard card renders an inline `