diff --git a/packages/ui/src/components/chat/ChatContainer.tsx b/packages/ui/src/components/chat/ChatContainer.tsx index ff93e579..5bc617cd 100644 --- a/packages/ui/src/components/chat/ChatContainer.tsx +++ b/packages/ui/src/components/chat/ChatContainer.tsx @@ -25,8 +25,6 @@ export const ChatContainer: React.FC = () => { loadMessages, loadMoreMessages, updateViewportAnchor, - updateActiveTurnAnchor, - getActiveTurnAnchor, sessionMemoryState, openNewSessionDraft, isSyncing, @@ -75,26 +73,22 @@ export const ChatContainer: React.FC = () => { const { scrollRef, handleMessageContentChange, - getAnimationHandlers, - showScrollButton, - scrollToBottom, - spacerHeight, - pendingAnchorId, - hasActiveAnchor, + getAnimationHandlers, + showScrollButton, + scrollToBottom, + scrollToPosition, + isPinned, } = useChatScrollManager({ currentSessionId, sessionMessages, streamingMessageId, sessionMemoryState, updateViewportAnchor, - updateActiveTurnAnchor, - getActiveTurnAnchor, isSyncing, isMobile, messageStreamStates, sessionPermissions: sessionBlockingCards, trimToViewportWindow, - sessionActivityPhase, }); const memoryState = React.useMemo(() => { @@ -123,12 +117,12 @@ export const ChatContainer: React.FC = () => { await loadMoreMessages(currentSessionId, 'up'); if (container && prevHeight !== null && prevTop !== null) { const heightDiff = container.scrollHeight - prevHeight; - container.scrollTop = prevTop + heightDiff; + scrollToPosition(prevTop + heightDiff, { instant: true }); } } finally { setIsLoadingOlder(false); } - }, [currentSessionId, isLoadingOlder, loadMoreMessages, scrollRef]); + }, [currentSessionId, isLoadingOlder, loadMoreMessages, scrollRef, scrollToPosition]); // Scroll to a specific message by ID (for timeline dialog) const scrollToMessage = React.useCallback((messageId: string) => { @@ -169,7 +163,8 @@ export const ChatContainer: React.FC = () => { } finally { const currentPhase = sessionActivityPhase?.get(currentSessionId) ?? 'idle'; const isActivePhase = currentPhase === 'busy' || currentPhase === 'cooldown'; - const shouldSkipScroll = isActivePhase && hasActiveAnchor; + // When pinned and active, scroll is already maintained automatically + const shouldSkipScroll = isActivePhase && isPinned; if (!shouldSkipScroll) { if (typeof window === 'undefined') { @@ -184,7 +179,7 @@ export const ChatContainer: React.FC = () => { }; void load(); - }, [currentSessionId, hasActiveAnchor, loadMessages, messages, scrollToBottom, sessionActivityPhase]); + }, [currentSessionId, isPinned, loadMessages, messages, scrollToBottom, sessionActivityPhase]); if (!currentSessionId && !draftOpen) { return ( @@ -277,7 +272,6 @@ export const ChatContainer: React.FC = () => { }} data-scroll-shadow="true" data-scrollbar="chat" - hideBottomShadow={!!pendingAnchorId} >