/* Stage viewport — render preview, HUD, title-safe guides, transport. */ import { useEffect, useRef, useState } from "react"; import { Icon } from "./Icon"; import { cap, fmt } from "../state/util"; import type { EpisodeDocument, Shot, ViewMode } from "../state/types"; function RenderOverlay({ pct }: { pct: number }) { const r = 32; const c = 2 * Math.PI * r; const indeterminate = pct < 0; return (
{indeterminate ? "···" : Math.round(pct) + "%"}
Rendering preview
{indeterminate ? "aura · working…" : pct < 40 ? "aura · rigging cast…" : pct < 75 ? "aura · simulating lighting…" : "aura · compositing frames…"}
); } export interface StageProps { data: EpisodeDocument; shot: Shot | null; time: number; playing: boolean; onPlay: () => void; onScrub: (t: number) => void; onStep: (dir: number) => void; viewMode: ViewMode; rendering: boolean; renderPct: number; /** Most recent REAL render output (served under /preview/*), or null before first render. */ renderVideo?: string | null; renderPoster?: string | null; /** No real working document exists yet — render the empty "describe a scene" state. */ empty?: boolean; } export function Stage({ data, shot, time, playing, onPlay, onScrub, onStep, viewMode, rendering, renderPct, renderVideo, renderPoster, empty }: StageProps) { const [guides, setGuides] = useState(false); const [cc, setCc] = useState(true); const [isFullscreen, setIsFullscreen] = useState(false); const stageRef = useRef(null); const DUR = data.DUR; useEffect(() => { const onFsChange = () => setIsFullscreen(document.fullscreenElement === stageRef.current); document.addEventListener("fullscreenchange", onFsChange); return () => document.removeEventListener("fullscreenchange", onFsChange); }, []); const toggleFullscreen = () => { if (document.fullscreenElement === stageRef.current) { void document.exitFullscreen(); } else { void stageRef.current?.requestFullscreen(); } }; // Drive the actual