diff --git a/site/src/pages/AgentsPage/AgentDetail.tsx b/site/src/pages/AgentsPage/AgentDetail.tsx index 839e42cf08..d8d976f1fe 100644 --- a/site/src/pages/AgentsPage/AgentDetail.tsx +++ b/site/src/pages/AgentsPage/AgentDetail.tsx @@ -884,15 +884,28 @@ const AgentDetail: FC = () => { requestUnarchiveAgent(agentId); }; - // Signal the parent layout that messages have loaded. + // Signal ready only after the store has synced fetched messages, + // so the DOM actually contains them when the parent scrolls. const chatReadyFiredRef = useRef(null); + const storeMessageCount = useChatSelector(store, (s) => s.messagesByID.size); + const fetchedMessageCount = chatMessagesList?.length ?? 0; useEffect(() => { - if (chatReadyFiredRef.current === agentId || !chatMessagesQuery.isSuccess) { + if ( + chatReadyFiredRef.current === agentId || + !chatMessagesQuery.isSuccess || + storeMessageCount < fetchedMessageCount + ) { return; } chatReadyFiredRef.current = agentId ?? null; onChatReady(); - }, [onChatReady, chatMessagesQuery.isSuccess, agentId]); + }, [ + onChatReady, + storeMessageCount, + fetchedMessageCount, + chatMessagesQuery.isSuccess, + agentId, + ]); const handleRegenerateTitle = () => { if (!agentId || isRegenerateTitleDisabled || !onRegenerateTitle) { diff --git a/site/src/pages/AgentsPage/AgentEmbedPage.tsx b/site/src/pages/AgentsPage/AgentEmbedPage.tsx index f66082a93e..d93410c2a6 100644 --- a/site/src/pages/AgentsPage/AgentEmbedPage.tsx +++ b/site/src/pages/AgentsPage/AgentEmbedPage.tsx @@ -194,8 +194,7 @@ const AgentEmbedPage: FC = () => { // instead of creating its own. const scrollContainerRef = useRef(null); - // Listen for parent frame commands: theme changes and - // scroll-to-bottom requests. + // Listen for parent frame commands (e.g. theme changes). useEffect(() => { const parentWindow = window.parent; const handler = (event: MessageEvent) => { @@ -205,14 +204,6 @@ const AgentEmbedPage: FC = () => { const theme = getThemeFromMessage(event.data); if (theme) { applyEmbedTheme(theme); - return; - } - if (event.data?.type === "coder:scroll-to-bottom") { - // Normal flow: scroll to the bottom of the transcript. - if (scrollContainerRef.current) { - scrollContainerRef.current.scrollTop = - scrollContainerRef.current.scrollHeight; - } } }; diff --git a/site/src/pages/AgentsPage/components/AgentDetailView.tsx b/site/src/pages/AgentsPage/components/AgentDetailView.tsx index 7e605d2add..aa80523d3f 100644 --- a/site/src/pages/AgentsPage/components/AgentDetailView.tsx +++ b/site/src/pages/AgentsPage/components/AgentDetailView.tsx @@ -842,6 +842,11 @@ const ScrollAnchoredContainer: FC<{ return; } + if (autoScrollRef.current) { + scheduleBottomPin(); + return; + } + // Skip compensation during reflow. Width changes indicate the // height delta is distributed through the transcript rather than // appended at the bottom, so applying the full delta would @@ -850,11 +855,6 @@ const ScrollAnchoredContainer: FC<{ return; } - if (autoScrollRef.current) { - scheduleBottomPin(); - return; - } - // In normal flow, appends grow below the viewport, so users reading // history do not need scroll compensation. });