diff --git a/src/components/chat/hooks/useChatSessionState.ts b/src/components/chat/hooks/useChatSessionState.ts index 7ac53e87..c81954a5 100644 --- a/src/components/chat/hooks/useChatSessionState.ts +++ b/src/components/chat/hooks/useChatSessionState.ts @@ -93,6 +93,7 @@ export function useChatSessionState({ const scrollPositionRef = useRef({ height: 0, top: 0 }); const loadAllFinishedTimerRef = useRef | null>(null); const loadAllOverlayTimerRef = useRef | null>(null); + const lastLoadedSessionKeyRef = useRef(null); const createDiff = useMemo(() => createCachedDiffCalculator(), []); @@ -297,10 +298,15 @@ export function useChatSessionState({ pendingScrollRestoreRef.current = null; }, [chatMessages.length]); + const prevSessionMessagesLengthRef = useRef(0); + const isInitialLoadRef = useRef(true); + useEffect(() => { pendingInitialScrollRef.current = true; topLoadLockRef.current = false; pendingScrollRestoreRef.current = null; + prevSessionMessagesLengthRef.current = 0; + isInitialLoadRef.current = true; setVisibleMessageCount(INITIAL_VISIBLE_MESSAGES); setIsUserScrolledUp(false); }, [selectedProject?.name, selectedSession?.id]); @@ -373,6 +379,15 @@ export function useChatSessionState({ } } + // Skip loading if session+project+provider hasn't changed + const sessionKey = `${selectedSession.id}:${selectedProject.name}:${provider}`; + if (lastLoadedSessionKeyRef.current === sessionKey) { + setTimeout(() => { + isLoadingSessionRef.current = false; + }, 250); + return; + } + if (provider === 'cursor') { setCurrentSessionId(selectedSession.id); sessionStorage.setItem('cursorSessionId', selectedSession.id); @@ -400,6 +415,9 @@ export function useChatSessionState({ setIsSystemSessionChange(false); } } + + // Update the last loaded session key + lastLoadedSessionKeyRef.current = sessionKey; } else { if (!isSystemSessionChange) { resetStreamingState(); @@ -417,6 +435,7 @@ export function useChatSessionState({ setHasMoreMessages(false); setTotalMessages(0); setTokenBudget(null); + lastLoadedSessionKeyRef.current = null; } setTimeout(() => { @@ -433,7 +452,7 @@ export function useChatSessionState({ pendingViewSessionRef, resetStreamingState, selectedProject, - selectedSession, + selectedSession?.id, // Only depend on session ID, not the entire object sendMessage, ws, ]); @@ -490,11 +509,24 @@ export function useChatSessionState({ } }, [pendingViewSessionRef, selectedSession?.id]); + useEffect(() => { - if (sessionMessages.length > 0) { - setChatMessages(convertedMessages); + // Only sync sessionMessages to chatMessages when: + // 1. Not currently loading (to avoid overwriting user's just-sent message) + // 2. SessionMessages actually changed (including from non-empty to empty) + // 3. Either it's initial load OR sessionMessages increased (new messages from server) + if ( + sessionMessages.length !== prevSessionMessagesLengthRef.current && + !isLoading + ) { + // Only update if this is initial load, sessionMessages grew, or was cleared to empty + if (isInitialLoadRef.current || sessionMessages.length === 0 || sessionMessages.length > prevSessionMessagesLengthRef.current) { + setChatMessages(convertedMessages); + isInitialLoadRef.current = false; + } + prevSessionMessagesLengthRef.current = sessionMessages.length; } - }, [convertedMessages, sessionMessages.length]); + }, [convertedMessages, sessionMessages.length, isLoading, setChatMessages]); useEffect(() => { if (selectedProject && chatMessages.length > 0) {