diff --git a/frontend-web/src/components/workspace/agents/agent-detail-panel.tsx b/frontend-web/src/components/workspace/agents/agent-detail-panel.tsx index cedeb6c8..f9a3292e 100644 --- a/frontend-web/src/components/workspace/agents/agent-detail-panel.tsx +++ b/frontend-web/src/components/workspace/agents/agent-detail-panel.tsx @@ -187,23 +187,30 @@ export function AgentDetailPanel({ const isSaving = updateAgent.isPending || setFeatured.isPending; // 对话生成文件(md/txt 等 artifact)并在右侧展开预览时,自动收起智能体 - // 信息面板给预览让位;新文件落地时同样收起。编辑中不打断用户,用户随时 + // 信息面板给预览让位;切换到新文件时同样收起。编辑中不打断用户,用户随时 // 可手动重新展开。 + // + // 关键:判定用 open + selectedArtifact,而非 artifacts.length。流式写文件时 + // message-group 一开始就调用 select()+setOpen(true) 打开预览并选中该文件,但 + // 该文件要等生成结束、artifact 入库后才会进 thread.values.artifacts。若按 + // 数量判定就只能等生成结束才收起——正是之前「生成完才收起」的 bug 根因。 const artifactsCtx = useArtifactsOptional(); const artifactCount = artifactsCtx?.artifacts.length ?? 0; - const artifactPanelVisible = !!artifactsCtx?.open && artifactCount > 0; + const selectedArtifact = artifactsCtx?.selectedArtifact ?? null; + const artifactPanelVisible = + !!artifactsCtx?.open && (!!selectedArtifact || artifactCount > 0); const prevArtifactPanelVisible = useRef(false); - const prevArtifactCount = useRef(0); + const prevSelectedArtifact = useRef(null); useEffect(() => { const opened = artifactPanelVisible && !prevArtifactPanelVisible.current; - const grew = - artifactPanelVisible && artifactCount > prevArtifactCount.current; + const switched = + artifactPanelVisible && selectedArtifact !== prevSelectedArtifact.current; prevArtifactPanelVisible.current = artifactPanelVisible; - prevArtifactCount.current = artifactCount; - if ((opened || grew) && !editing) { + prevSelectedArtifact.current = selectedArtifact; + if ((opened || switched) && !editing) { setCollapsed(true); } - }, [artifactPanelVisible, artifactCount, editing]); + }, [artifactPanelVisible, selectedArtifact, editing]); const displayName = desensitize(agent?.name ?? agentName); const questions = useMemo( diff --git a/frontend-web/src/components/workspace/messages/message-list.tsx b/frontend-web/src/components/workspace/messages/message-list.tsx index 9b7badbf..c3d0cd58 100644 --- a/frontend-web/src/components/workspace/messages/message-list.tsx +++ b/frontend-web/src/components/workspace/messages/message-list.tsx @@ -6,6 +6,7 @@ import { useCallback, useContext, useEffect, useMemo, useState } from "react"; import { Conversation, ConversationContent, + ConversationScrollButton, } from "@/components/ai-elements/conversation"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; @@ -61,6 +62,7 @@ export function MessageList({ paddingBottom = MESSAGE_LIST_DEFAULT_PADDING_BOTTOM, tokenUsageEnabled = false, extraRehypePlugins, + scrollButtonClassName, onClarificationSubmit, onEditResubmit, onSetupAgentApprove, @@ -75,6 +77,8 @@ export function MessageList({ thread: BaseStream; paddingBottom?: number; tokenUsageEnabled?: boolean; + /** 额外定位「回到最新」悬浮按钮——不同页面输入框高度不同,需上移让位。 */ + scrollButtonClassName?: string; extraRehypePlugins?: ReturnType; onClarificationSubmit?: (answer: string) => void | Promise; onEditResubmit?: (text: string) => void; @@ -543,6 +547,16 @@ export function MessageList({
+ {/* ChatGPT 式「回到最新」悬浮钮:仅在向上滚离底部时出现,点击平滑回到 + 最新问答。默认贴底,调用方可用 scrollButtonClassName 上移避开输入框。 */} + ); } diff --git a/frontend-web/src/pages/AgentChatPage.tsx b/frontend-web/src/pages/AgentChatPage.tsx index 33626595..55b726a8 100644 --- a/frontend-web/src/pages/AgentChatPage.tsx +++ b/frontend-web/src/pages/AgentChatPage.tsx @@ -285,6 +285,8 @@ export default function AgentChatPage() { thread={thread} paddingBottom={showFollowups ? 96 : 24} tokenUsageEnabled={tokenUsageEnabled} + // 输入框在流内(MessageList 下方),按钮贴对话区底部即可。 + scrollButtonClassName="bottom-2" onClarificationSubmit={handleClarificationSubmit} onEditResubmit={(text) => handleSubmit({ text, files: [] })} /> diff --git a/frontend-web/src/pages/ChatPage.tsx b/frontend-web/src/pages/ChatPage.tsx index 930dac06..b39d4363 100644 --- a/frontend-web/src/pages/ChatPage.tsx +++ b/frontend-web/src/pages/ChatPage.tsx @@ -493,6 +493,8 @@ export default function ChatPage() { thread={thread} paddingBottom={messageListPaddingBottom} tokenUsageEnabled={tokenUsageEnabled} + // 输入框为底部绝对浮层(约 160px 高),按钮上移到其上方居中。 + scrollButtonClassName="bottom-40" onClarificationSubmit={handleClarificationSubmit} onEditResubmit={(text) => handleSubmit({ text, files: [] })