From 7e824ca5dc7fbf619bc02ed5765cf216c8b9689b Mon Sep 17 00:00:00 2001 From: "sp.wack" <83104063+amanape@users.noreply.github.com> Date: Wed, 5 Nov 2025 18:23:10 +0400 Subject: [PATCH] fix(frontend): V1 Loading UI (#11630) --- .../features/chat/chat-interface.tsx | 29 +++++++++++-------- frontend/src/hooks/use-scroll-to-bottom.ts | 22 +++++++++----- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/features/chat/chat-interface.tsx b/frontend/src/components/features/chat/chat-interface.tsx index 91f07fc611..cabf087689 100644 --- a/frontend/src/components/features/chat/chat-interface.tsx +++ b/frontend/src/components/features/chat/chat-interface.tsx @@ -97,24 +97,29 @@ export function ChatInterface() { const isV1Conversation = conversation?.conversation_version === "V1"; - // Instantly scroll to bottom when history loading completes - const prevLoadingHistoryRef = React.useRef( + // Track when we should show V1 messages (after DOM has rendered) + const [showV1Messages, setShowV1Messages] = React.useState(false); + const prevV1LoadingRef = React.useRef( conversationWebSocket?.isLoadingHistory, ); + + // Wait for DOM to render before showing V1 messages React.useEffect(() => { - const wasLoading = prevLoadingHistoryRef.current; + const wasLoading = prevV1LoadingRef.current; const isLoading = conversationWebSocket?.isLoadingHistory; - // When history loading transitions from true to false, instantly scroll to bottom - if (wasLoading && !isLoading && scrollRef.current) { - scrollRef.current.scrollTo({ - top: scrollRef.current.scrollHeight, - behavior: "instant", + if (wasLoading && !isLoading) { + // Loading just finished - wait for next frame to ensure DOM is ready + requestAnimationFrame(() => { + setShowV1Messages(true); }); + } else if (isLoading) { + // Reset when loading starts + setShowV1Messages(false); } - prevLoadingHistoryRef.current = isLoading; - }, [conversationWebSocket?.isLoadingHistory, scrollRef]); + prevV1LoadingRef.current = isLoading; + }, [conversationWebSocket?.isLoadingHistory]); // Filter V0 events const v0Events = storeEvents @@ -252,7 +257,7 @@ export function ChatInterface() { )} - {conversationWebSocket?.isLoadingHistory && + {(conversationWebSocket?.isLoadingHistory || !showV1Messages) && isV1Conversation && !isTask && (
@@ -269,7 +274,7 @@ export function ChatInterface() { /> )} - {!conversationWebSocket?.isLoadingHistory && v1UserEventsExist && ( + {showV1Messages && v1UserEventsExist && ( )}
diff --git a/frontend/src/hooks/use-scroll-to-bottom.ts b/frontend/src/hooks/use-scroll-to-bottom.ts index ac868f49e8..18516785fa 100644 --- a/frontend/src/hooks/use-scroll-to-bottom.ts +++ b/frontend/src/hooks/use-scroll-to-bottom.ts @@ -1,4 +1,10 @@ -import { RefObject, useEffect, useState, useCallback, useRef } from "react"; +import { + RefObject, + useState, + useCallback, + useRef, + useLayoutEffect, +} from "react"; export function useScrollToBottom(scrollRef: RefObject) { // Track whether we should auto-scroll to the bottom when content changes @@ -65,20 +71,20 @@ export function useScrollToBottom(scrollRef: RefObject) { }, [scrollRef]); // Auto-scroll effect that runs when content changes - useEffect(() => { + // Use useLayoutEffect to scroll after DOM updates but before paint + useLayoutEffect(() => { // Only auto-scroll if autoscroll is enabled if (autoscroll) { const dom = scrollRef.current; if (dom) { - requestAnimationFrame(() => { - dom.scrollTo({ - top: dom.scrollHeight, - behavior: "smooth", - }); + // Scroll to bottom - this will trigger on any DOM change + dom.scrollTo({ + top: dom.scrollHeight, + behavior: "smooth", }); } } - }); + }); // No dependency array - runs after every render to follow new content return { scrollRef,