"use client"; import { useEffect, useMemo, useState } from "react"; import { type EditPlan, planTimeline, SECOND } from "@/lib/video-canvas/smart-edit/contract"; import { EditPreview } from "./EditPreview"; /** Preview the selected edit boundary with the same timing/compositing used by the editor. */ export function TransitionPreview({ plan, index, cover = false, }: { plan: EditPlan; index: number; cover?: boolean; }) { const preview = useMemo( () => ({ ...plan, subtitles: false, music: undefined, audioClips: [], clips: plan.clips .slice(index - 1, index + 1) .map((c) => ({ ...c, gain: 0, audioDetached: true })), }), [plan, index], ); const timeline = useMemo(() => planTimeline(preview), [preview]); const boundary = timeline.clips[1]; const start = Math.max(0, boundary.transitionStart / SECOND - 0.25); const end = Math.min( timeline.duration / SECOND, (boundary.transitionStart + boundary.overlap) / SECOND + 0.25, ); const [time, setTime] = useState(start), [error, setError] = useState(""), [visible, setVisible] = useState(true); useEffect(() => { const change = () => setVisible(!document.hidden); document.addEventListener("visibilitychange", change); return () => document.removeEventListener("visibilitychange", change); }, []); useEffect(() => { let request = 0, epoch = 0; setError(""); setTime(start); const tick = (now: number) => { if (document.hidden) epoch = 0; else { if (!epoch) epoch = now; setTime(start + (((now - epoch) / 1000) % (end - start))); } request = requestAnimationFrame(tick); }; request = requestAnimationFrame(tick); return () => cancelAnimationFrame(request); }, [start, end, preview]); return (