feat: persist scroll position per session when switching
This commit is contained in:
@@ -103,18 +103,46 @@ export function Chat({ messages, isGenerating, isLoadingHistory, status, session
|
|||||||
return () => el.removeEventListener('scroll', handler);
|
return () => el.removeEventListener('scroll', handler);
|
||||||
}, [checkIfNearBottom]);
|
}, [checkIfNearBottom]);
|
||||||
|
|
||||||
// Reset state on session switch
|
// Persist scroll position per session
|
||||||
|
const scrollPositionsRef = useRef<Map<string, { top: number; height: number }>>(new Map());
|
||||||
const prevSessionKeyRef = useRef(sessionKey);
|
const prevSessionKeyRef = useRef(sessionKey);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sessionKey !== prevSessionKeyRef.current) {
|
if (sessionKey !== prevSessionKeyRef.current) {
|
||||||
|
// Save scroll position of previous session
|
||||||
|
const prevKey = prevSessionKeyRef.current;
|
||||||
|
const el = scrollContainerRef.current;
|
||||||
|
if (prevKey && el) {
|
||||||
|
const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
||||||
|
scrollPositionsRef.current.set(prevKey, { top: el.scrollTop, height: el.scrollHeight });
|
||||||
|
// If user was near bottom, don't save (will auto-scroll to bottom on restore)
|
||||||
|
if (distFromBottom <= SCROLL_THRESHOLD) {
|
||||||
|
scrollPositionsRef.current.delete(prevKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
prevSessionKeyRef.current = sessionKey;
|
prevSessionKeyRef.current = sessionKey;
|
||||||
prevMessageCountRef.current = messages.length;
|
prevMessageCountRef.current = messages.length;
|
||||||
setNewMessageCount(0); // eslint-disable-line react-hooks/set-state-in-effect -- intentional: reset on session switch
|
setNewMessageCount(0); // eslint-disable-line react-hooks/set-state-in-effect -- intentional: reset on session switch
|
||||||
|
|
||||||
|
// Restore scroll position for new session
|
||||||
|
const saved = sessionKey ? scrollPositionsRef.current.get(sessionKey) : undefined;
|
||||||
|
if (saved) {
|
||||||
|
isNearBottomRef.current = false;
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const container = scrollContainerRef.current;
|
||||||
|
if (container) {
|
||||||
|
// Adjust for content height changes since last visit
|
||||||
|
const heightDelta = container.scrollHeight - saved.height;
|
||||||
|
container.scrollTop = saved.top + Math.max(0, heightDelta);
|
||||||
|
checkIfNearBottom();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
isNearBottomRef.current = true;
|
isNearBottomRef.current = true;
|
||||||
// Scroll to bottom on session switch
|
|
||||||
requestAnimationFrame(() => scrollToBottom('instant'));
|
requestAnimationFrame(() => scrollToBottom('instant'));
|
||||||
}
|
}
|
||||||
}, [sessionKey, messages.length, scrollToBottom]);
|
}
|
||||||
|
}, [sessionKey, messages.length, scrollToBottom, checkIfNearBottom]);
|
||||||
|
|
||||||
// Auto-scroll when messages change, but only if user is near bottom or just sent a message
|
// Auto-scroll when messages change, but only if user is near bottom or just sent a message
|
||||||
const wasLoadingHistoryRef = useRef(isLoadingHistory);
|
const wasLoadingHistoryRef = useRef(isLoadingHistory);
|
||||||
|
|||||||
Reference in New Issue
Block a user