"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { Pause, Play, RotateCcw, SkipBack, SkipForward } from "lucide-react"; import type { AnimationSettings, Caption, CaptionLayout, CaptionStyle, VideoMeta, } from "@/types"; import { Button } from "@/components/ui/button"; import { CaptionWord } from "@/components/caption-word"; import { cn } from "@/lib/utils"; interface VideoPreviewProps { videoUrl: string; audioUrl?: string; captions: Caption[]; style: CaptionStyle; layout: CaptionLayout; animation: AnimationSettings; videoMeta: VideoMeta; } export function VideoPreview({ videoUrl, audioUrl, captions, style, layout, animation, videoMeta, }: VideoPreviewProps) { const videoRef = useRef(null); const [currentMs, setCurrentMs] = useState(0); const [playing, setPlaying] = useState(false); useEffect(() => { const video = videoRef.current; if (!video) return; const onTimeUpdate = () => setCurrentMs(video.currentTime * 1000); video.addEventListener("timeupdate", onTimeUpdate); return () => video.removeEventListener("timeupdate", onTimeUpdate); }, []); useEffect(() => { const video = videoRef.current; if (!video || !audioUrl) return; video.muted = true; }, [audioUrl]); const togglePlay = useCallback(() => { const video = videoRef.current; if (!video) return; if (video.paused) { void video.play(); setPlaying(true); } else { video.pause(); setPlaying(false); } }, []); const seekBy = (delta: number) => { const video = videoRef.current; if (!video) return; video.currentTime = Math.max(0, Math.min(video.duration, video.currentTime + delta)); }; const restart = () => { const video = videoRef.current; if (!video) return; video.currentTime = 0; setCurrentMs(0); }; // ponytail: strict window — the +200ms grace stacked the outgoing phrase under the new one const visibleCaptions = captions.filter( (c) => c.startMs <= currentMs && currentMs < c.endMs, ); const positionClass = layout.position === "top" ? "top-[15%]" : layout.position === "center" ? "top-1/2 -translate-y-1/2" : "bottom-[15%]"; return (
{/* ponytail: container-query context so captions scale with the preview instead of using raw export px, which looked ~4x too big in a 292px-wide frame */}

{videoMeta.width}×{videoMeta.height} • {videoMeta.fps} fps • {Math.round(videoMeta.duration)}s

); }