diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx index 1153ddf33..f5b59ab38 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx @@ -5,8 +5,10 @@ import type { FormEventHandler, RefObject, SetStateAction, + UIEvent, UIEventHandler, } from 'react'; +import { useLayoutEffect, useRef } from 'react'; import type { AgentRuntimeState, @@ -33,6 +35,8 @@ type RuntimeControlProps = ComponentProps< typeof ProjectSupervisorRuntimeControls >; +const CHAT_SCROLL_BOTTOM_THRESHOLD = 24; + type SupervisorChatOnlyViewProps = { chatAgentBusy: boolean; chatInput: string; @@ -95,6 +99,7 @@ export function SupervisorChatOnlyView({ visibleMessages, workspaceStatus, }: SupervisorChatOnlyViewProps) { + const shouldFollowLatestRef = useRef(true); const running = Boolean( chatAgentBusy || (runtime && !isAgentRuntimeTerminalState(runtime)), ); @@ -107,6 +112,34 @@ export function SupervisorChatOnlyView({ : runtime ? projectSupervisorChatRuntimeStatus(runtime) : projectWorkspaceStatusForDisplay(workspaceStatus); + useLayoutEffect(() => { + if (!running) { + return; + } + const messageList = messagesRef.current; + if (!messageList) { + return; + } + if (shouldFollowLatestRef.current) { + messageList.scrollTop = messageList.scrollHeight; + } + }, [ + messagesRef, + running, + runtime?.updatedAt, + transientReply, + visibleMessages.length, + ]); + function handleScroll(event: UIEvent) { + const messageList = event.currentTarget; + const distanceFromBottom = + messageList.scrollHeight - + messageList.scrollTop - + messageList.clientHeight; + shouldFollowLatestRef.current = + distanceFromBottom <= CHAT_SCROLL_BOTTOM_THRESHOLD; + onScroll(event); + } const conversationLabel = directCodex ? '陶泥儿项目对话' : '项目总控 Agent 纯聊天'; @@ -139,7 +172,7 @@ export function SupervisorChatOnlyView({ ref={messagesRef} className="message-list supervisor-chat-only-message-list" aria-label={directCodex ? '陶泥儿消息' : '项目总控消息'} - onScroll={onScroll} + onScroll={handleScroll} > {hiddenConversationCount > 0 ? (