diff --git a/apps/web/src/components/timeline/TimelineView.tsx b/apps/web/src/components/timeline/TimelineView.tsx index ec4cc2d..3e5a399 100644 --- a/apps/web/src/components/timeline/TimelineView.tsx +++ b/apps/web/src/components/timeline/TimelineView.tsx @@ -689,15 +689,25 @@ function TimelineViewContent({ const pendingLeftCompensationPx = useRef(0); // Flag: scroll viewport to today after the next viewStart-driven re-layout. const pendingScrollToTodayRef = useRef(false); - - // Scroll to today on mount so the viewport opens with today at the left edge. - // Empty deps: intentionally runs once (and twice in React Strict Mode dev, both correct). - + // Guard reset on every real unmount (including Strict Mode fake-unmount) so the + // scroll-to-today fires correctly on remount. + const hasScrolledToTodayOnLoad = useRef(false); useLayoutEffect(() => { + return () => { + hasScrolledToTodayOnLoad.current = false; + }; + }, []); + + // Scroll to today the first time the canvas has its full width (after initial data load). + // Depends on totalCanvasWidth so it fires after isInitialLoading → false renders the canvas. + useLayoutEffect(() => { + if (totalCanvasWidth === 0) return; + if (hasScrolledToTodayOnLoad.current) return; const el = scrollContainerRef.current; if (!el) return; el.scrollLeft = toLeft(today); - }, []); + hasScrolledToTodayOnLoad.current = true; + }, [totalCanvasWidth, toLeft, today]); // Apply scroll compensation synchronously after the canvas grows (left-extend or Today button). useLayoutEffect(() => {