feat: 聊天增加回到最新悬浮钮,智能体面板生成即收起
- 复用 ConversationScrollButton 接入 MessageList,滚离底部时显示一键回到最新问答的悬浮按钮;新增 scrollButtonClassName 以适配各页输入框高度 - 智能体详情面板改用 open+selectedArtifact 判定收起,流式写文件开始即收起让位预览,不再等生成结束 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
d82b5ed884
commit
4f0494a97a
4 files changed
+33
-8
No files matched your search
@@ -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<string | null>(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(
|
||||
|
||||
@@ -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<AgentThreadState>;
|
||||
paddingBottom?: number;
|
||||
tokenUsageEnabled?: boolean;
|
||||
/** 额外定位「回到最新」悬浮按钮——不同页面输入框高度不同,需上移让位。 */
|
||||
scrollButtonClassName?: string;
|
||||
extraRehypePlugins?: ReturnType<typeof useRehypeSplitWordsIntoSpans>;
|
||||
onClarificationSubmit?: (answer: string) => void | Promise<void>;
|
||||
onEditResubmit?: (text: string) => void;
|
||||
@@ -543,6 +547,16 @@ export function MessageList({
|
||||
<div style={{ height: `${paddingBottom}px` }} />
|
||||
</MessageMetricsProvider>
|
||||
</ConversationContent>
|
||||
{/* ChatGPT 式「回到最新」悬浮钮:仅在向上滚离底部时出现,点击平滑回到
|
||||
最新问答。默认贴底,调用方可用 scrollButtonClassName 上移避开输入框。 */}
|
||||
<ConversationScrollButton
|
||||
className={cn(
|
||||
"pointer-events-auto z-20 size-9 shadow-md",
|
||||
scrollButtonClassName,
|
||||
)}
|
||||
aria-label="回到最新"
|
||||
title="回到最新"
|
||||
/>
|
||||
</Conversation>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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: [] })}
|
||||
/>
|
||||
|
||||
@@ -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: [] })
|
||||
|
||||
Reference in new issue
Block a user