From 6e910f05934f68c8375c32f054eb9493f7fbe80c Mon Sep 17 00:00:00 2001 From: Mahdy Arief Date: Fri, 27 Mar 2026 23:39:43 +0700 Subject: [PATCH] feat: implement core VideoEditor component for video editing functionality. --- src/components/video-editor/VideoEditor.tsx | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 530e5fdb..cb7fee01 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -1961,6 +1961,9 @@ export default function VideoEditor() { if (id) { setSelectedTrimId(null); setSelectedAudioId(null); + setSelectedSpeedId(null); + setSelectedAnnotationId(null); + setSelectedCaptionId(null); } }, []); @@ -1970,6 +1973,8 @@ export default function VideoEditor() { setSelectedZoomId(null); setSelectedAnnotationId(null); setSelectedAudioId(null); + setSelectedSpeedId(null); + setSelectedCaptionId(null); } }, []); @@ -1979,6 +1984,8 @@ export default function VideoEditor() { setSelectedZoomId(null); setSelectedTrimId(null); setSelectedAudioId(null); + setSelectedSpeedId(null); + setSelectedCaptionId(null); } }, []); @@ -1995,6 +2002,7 @@ export default function VideoEditor() { setSelectedZoomId(id); setSelectedTrimId(null); setSelectedAnnotationId(null); + setSelectedCaptionId(null); }, []); const handleZoomSuggested = useCallback((span: Span, focus: ZoomFocus) => { @@ -2010,6 +2018,7 @@ export default function VideoEditor() { setSelectedZoomId(id); setSelectedTrimId(null); setSelectedAnnotationId(null); + setSelectedCaptionId(null); }, []); const handleTrimAdded = useCallback((span: Span) => { @@ -2023,6 +2032,7 @@ export default function VideoEditor() { setSelectedTrimId(id); setSelectedZoomId(null); setSelectedAnnotationId(null); + setSelectedCaptionId(null); }, []); const handleZoomSpanChange = useCallback((id: string, span: Span) => { @@ -2111,6 +2121,7 @@ export default function VideoEditor() { setSelectedTrimId(null); setSelectedAnnotationId(null); setSelectedAudioId(null); + setSelectedCaptionId(null); } }, []); @@ -2127,6 +2138,7 @@ export default function VideoEditor() { setSelectedZoomId(null); setSelectedTrimId(null); setSelectedAnnotationId(null); + setSelectedCaptionId(null); }, []); const handleSpeedSpanChange = useCallback((id: string, span: Span) => { @@ -2160,6 +2172,7 @@ export default function VideoEditor() { setSelectedTrimId(null); setSelectedAnnotationId(null); setSelectedSpeedId(null); + setSelectedCaptionId(null); } }, []); @@ -2178,6 +2191,7 @@ export default function VideoEditor() { setSelectedTrimId(null); setSelectedAnnotationId(null); setSelectedSpeedId(null); + setSelectedCaptionId(null); }, []); const handleAudioSpanChange = useCallback((id: string, span: Span) => { @@ -2204,6 +2218,31 @@ export default function VideoEditor() { [selectedAudioId], ); + const handleCaptionSpanChange = useCallback((id: string, span: Span) => { + setAutoCaptions((prev) => + prev.map((caption) => + caption.id === id + ? { + ...caption, + startMs: Math.round(span.start), + endMs: Math.round(span.end), + } + : caption, + ), + ); + }, []); + + const handleSelectCaption = useCallback((id: string | null) => { + setSelectedCaptionId(id); + if (id) { + setSelectedZoomId(null); + setSelectedTrimId(null); + setSelectedAnnotationId(null); + setSelectedSpeedId(null); + setSelectedAudioId(null); + } + }, []); + const handleSpeedChange = useCallback( (speed: PlaybackSpeed) => { if (!selectedSpeedId) return; @@ -2443,6 +2482,12 @@ export default function VideoEditor() { } }, [selectedAudioId, audioRegions]); + useEffect(() => { + if (selectedCaptionId && !autoCaptions.some((cue) => cue.id === selectedCaptionId)) { + setSelectedCaptionId(null); + } + }, [selectedCaptionId, autoCaptions]); + // Audio playback sync: manage Audio elements that play in sync with video const audioElementsRef = useRef>(new Map());