diff --git a/surfsense_web/components/new-chat/source-detail-panel.tsx b/surfsense_web/components/new-chat/source-detail-panel.tsx index ed6c7e4ac..dbafd98f1 100644 --- a/surfsense_web/components/new-chat/source-detail-panel.tsx +++ b/surfsense_web/components/new-chat/source-detail-panel.tsx @@ -130,6 +130,7 @@ export function SourceDetailPanel({ const t = useTranslations("dashboard"); const scrollAreaRef = useRef(null); const hasScrolledRef = useRef(false); // Use ref to avoid stale closures + const scrollTimersRef = useRef[]>([]); const [activeChunkIndex, setActiveChunkIndex] = useState(null); const [mounted, setMounted] = useState(false); const [_hasScrolledToCited, setHasScrolledToCited] = useState(false); @@ -314,18 +315,22 @@ export function SourceDetailPanel({ const scrollAttempts = [50, 150, 300, 600, 1000]; scrollAttempts.forEach((delay) => { - setTimeout(() => { - scrollToCitedChunk(); - }, delay); + scrollTimersRef.current.push( + setTimeout(() => { + scrollToCitedChunk(); + }, delay) + ); }); // After final attempt, mark state as scrolled - setTimeout( - () => { - setHasScrolledToCited(true); - setActiveChunkIndex(citedChunkIndex); - }, - scrollAttempts[scrollAttempts.length - 1] + 50 + scrollTimersRef.current.push( + setTimeout( + () => { + setHasScrolledToCited(true); + setActiveChunkIndex(citedChunkIndex); + }, + scrollAttempts[scrollAttempts.length - 1] + 50 + ) ); } }, @@ -335,10 +340,16 @@ export function SourceDetailPanel({ // Reset scroll state when panel closes useEffect(() => { if (!open) { + scrollTimersRef.current.forEach(clearTimeout); + scrollTimersRef.current = []; hasScrolledRef.current = false; setHasScrolledToCited(false); setActiveChunkIndex(null); } + return () => { + scrollTimersRef.current.forEach(clearTimeout); + scrollTimersRef.current = []; + }; }, [open]); // Handle escape key