@@ -714,7 +714,10 @@ function VideoPlayer({
714714
715715 const finalizeTrim = async ( ) => {
716716 // Wait one tick so React commits the src removal before finalize starts.
717+ // Check cancelled here too so the StrictMode double-invocation doesn't
718+ // fire two simultaneous trim-finalize requests and cause a race condition.
717719 await new Promise ( ( resolve ) => setTimeout ( resolve , 0 ) )
720+ if ( cancelled ) return
718721
719722 try {
720723 const finalRes = await apiPost ( '/api/clips/trim-finalize' , { source_path : trimFinalizePath } )
@@ -760,6 +763,20 @@ function VideoPlayer({
760763 videoRef . current . load ( )
761764 } , [ suppressVideoSrc , videoReloadToken ] )
762765
766+ // When virtual trim bounds are applied, snap cursor into the trim region if it's outside
767+ useEffect ( ( ) => {
768+ if ( virtualTrimStart === null || virtualTrimEnd === null ) return
769+ if ( ! videoRef . current ) return
770+ const t = videoRef . current . currentTime
771+ if ( t < virtualTrimStart ) {
772+ videoRef . current . currentTime = virtualTrimStart
773+ setCurrentTime ( virtualTrimStart )
774+ } else if ( t > virtualTrimEnd ) {
775+ videoRef . current . currentTime = virtualTrimEnd
776+ setCurrentTime ( virtualTrimEnd )
777+ }
778+ } , [ virtualTrimStart , virtualTrimEnd ] )
779+
763780 // Subscribe to per-stage progress events from the backend
764781 useEffect ( ( ) => {
765782 const unsub = api . onOrganizeProgress ?. ( ( p ) => setOrganizeProgress ( p ) )
@@ -807,6 +824,8 @@ function VideoPlayer({
807824
808825 // Share state: { phase: 'compressing'|'uploading'|'done'|'error', url, error, percent } or null
809826 const [ shareModal , setShareModal ] = useState ( persistedShareState )
827+ const shareModalRef = useRef ( shareModal )
828+ shareModalRef . current = shareModal
810829 const [ shareCopied , setShareCopied ] = useState ( false )
811830 const isSharing = shareModal ?. phase === 'uploading' || shareModal ?. phase === 'compressing'
812831
@@ -824,12 +843,11 @@ function VideoPlayer({
824843 useEffect ( ( ) => {
825844 if ( ! api . onShareProgress ) return
826845 const unsub = api . onShareProgress ( ( data ) => {
827- setShareModal ( ( prev ) => {
828- if ( ! prev || prev . phase === 'done' || prev . phase === 'error' ) return prev
829- const next = { ...prev , phase : data . phase , percent : data . percent }
830- onShareStateChange ?. ( media ?. path , next )
831- return next
832- } )
846+ const prev = shareModalRef . current
847+ if ( ! prev || prev . phase === 'done' || prev . phase === 'error' ) return
848+ const next = { ...prev , phase : data . phase , percent : data . percent }
849+ setShareModal ( next )
850+ onShareStateChange ?. ( media ?. path , next )
833851 } )
834852 return ( ) => unsub ( )
835853 // eslint-disable-next-line react-hooks/exhaustive-deps
0 commit comments