From f66c47c812cf4f95218925a9e1fb6f5c6371a2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 18 May 2026 16:35:59 -0400 Subject: [PATCH] fix(studio): derive effective duration in PlayerControls instead of useEffect sync Replace the useEffect that pushed effectiveTimelineDuration into the player store with an inline derived selector in PlayerControls. The selector computes Math.max(duration, maxElementEnd) directly from store state, avoiding the effect-based sync anti-pattern entirely. --- packages/studio/src/App.tsx | 4 ---- packages/studio/src/player/components/PlayerControls.tsx | 6 +++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/studio/src/App.tsx b/packages/studio/src/App.tsx index ced474638..1c6fd8e79 100644 --- a/packages/studio/src/App.tsx +++ b/packages/studio/src/App.tsx @@ -84,10 +84,6 @@ export function StudioApp() { : 0; return Math.max(timelineDuration, maxEnd); }, [timelineDuration, timelineElements]); - useEffect(() => { - if (effectiveTimelineDuration !== usePlayerStore.getState().duration) - usePlayerStore.getState().setDuration(effectiveTimelineDuration); - }, [effectiveTimelineDuration]); const refreshPreviewDocumentVersion = useCallback(() => { setPreviewDocumentVersion((v) => v + 1); window.setTimeout(() => setPreviewDocumentVersion((v) => v + 1), 80); diff --git a/packages/studio/src/player/components/PlayerControls.tsx b/packages/studio/src/player/components/PlayerControls.tsx index c528cffb8..ef57ca457 100644 --- a/packages/studio/src/player/components/PlayerControls.tsx +++ b/packages/studio/src/player/components/PlayerControls.tsx @@ -55,7 +55,11 @@ export const PlayerControls = memo(function PlayerControls({ }: PlayerControlsProps) { // Subscribe to only the fields we render — each selector prevents cascading re-renders const isPlaying = usePlayerStore((s) => s.isPlaying); - const duration = usePlayerStore((s) => s.duration); + const duration = usePlayerStore((s) => { + if (s.elements.length === 0) return s.duration; + const maxEnd = Math.max(...s.elements.map((el) => el.start + el.duration)); + return Math.max(s.duration, maxEnd); + }); const timelineReady = usePlayerStore((s) => s.timelineReady); const playbackRate = usePlayerStore((s) => s.playbackRate); const audioMuted = usePlayerStore((s) => s.audioMuted);