From fadbcc8259dcbadd5618461c006e3c32ee484857 Mon Sep 17 00:00:00 2001 From: Haileyesus Date: Wed, 11 Feb 2026 17:37:51 +0300 Subject: [PATCH] fix(chat): stabilize long-history scroll-up pagination behavior - fix top-pagination lockups by only locking when older messages are actually fetched - make fetched older messages visible immediately by increasing `visibleMessageCount` on prepend - prevent unintended auto-scroll-to-bottom during older-message loading and scroll restore - replace state-based pagination offset with a ref to avoid stale offset/reload side effects - ensure initial auto-scroll runs only after initial session load completes - reset top-load lock/restore state and visible window when switching sessions - loosen top-lock release near the top to avoid requiring a full down/up cycle --- src/hooks/chat/useChatSessionState.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/hooks/chat/useChatSessionState.ts b/src/hooks/chat/useChatSessionState.ts index 6e2f542..7e42c03 100644 --- a/src/hooks/chat/useChatSessionState.ts +++ b/src/hooks/chat/useChatSessionState.ts @@ -208,13 +208,17 @@ export function useChatSessionState({ sessionProvider, ); - if (moreMessages.length > 0) { - pendingScrollRestoreRef.current = { - height: previousScrollHeight, - top: previousScrollTop, - }; - setSessionMessages((previous) => [...moreMessages, ...previous]); + if (moreMessages.length === 0) { + return false; } + + pendingScrollRestoreRef.current = { + height: previousScrollHeight, + top: previousScrollTop, + }; + setSessionMessages((previous) => [...moreMessages, ...previous]); + // Keep the rendered window in sync with top-pagination so newly loaded history becomes visible. + setVisibleMessageCount((previousCount) => previousCount + moreMessages.length); return true; } finally { isLoadingMoreRef.current = false; @@ -239,6 +243,10 @@ export function useChatSessionState({ } if (topLoadLockRef.current) { + // After a top-load restore, release the lock once user has moved away from absolute top. + if (container.scrollTop > 20) { + topLoadLockRef.current = false; + } return; } @@ -263,6 +271,9 @@ export function useChatSessionState({ useEffect(() => { pendingInitialScrollRef.current = true; + topLoadLockRef.current = false; + pendingScrollRestoreRef.current = null; + setVisibleMessageCount(INITIAL_VISIBLE_MESSAGES); setIsUserScrolledUp(false); }, [selectedProject?.name, selectedSession?.id]);