feat: 优化对话历史与会商引导体验
列表引导改为手动触发且不跳转创建页;历史 Tab 展示会商步骤/链条与深度研究模板;Step2 增加暂停与内容高度跟随滚动,并修复旧草稿恢复兜底。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
832e180c52
commit
4ac7bc21cd
18 files changed
+184
-88
No files matched your search
@@ -615,7 +615,11 @@ export function AgentGallery() {
|
||||
</TabsContent>
|
||||
)}
|
||||
</Tabs>
|
||||
<AgentGuide stage="list" eligible={isAgentGuideEnvironment()} />
|
||||
<AgentGuide
|
||||
stage="list"
|
||||
eligible={isAgentGuideEnvironment()}
|
||||
autoStart={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Guide, type GuideStep } from "tdesign-react";
|
||||
|
||||
import { env } from "@/env";
|
||||
@@ -13,7 +12,8 @@ import { isWujieEnv } from "@/core/embed/wujie";
|
||||
* 「浏览管理 → 创建与配置 → 发起问答」流程:
|
||||
*
|
||||
* - list :智能体列表页(/page/workspace/agents)——分区、搜索排序、卡片操作、
|
||||
* 创建入口;最后一步「去创建智能体 →」直接跳到创建页衔接下一段。
|
||||
* 创建入口;完成 / 跳过就地结束,不跳转创建页(创建页有自己的一段
|
||||
* 引导,点「创建智能体」进入时自动演示)。
|
||||
* - create :创建页(/page/workspace/agents/new)——对话式创建、基本信息、模型、
|
||||
* 知识空间、SOUL.md、技能、保存。
|
||||
* - chat :智能体对话页(/page/workspace/agents/:id/chats/...)——侧栏切换与
|
||||
@@ -23,6 +23,10 @@ import { isWujieEnv } from "@/core/embed/wujie";
|
||||
* localStorage 标记,**只有用户显式点「完成」或「跳过」才写入**;中途刷新/离开
|
||||
* 不记忆,下次进入该页面还会自动演示。各页头部的「使用引导」按钮可随时强制重放。
|
||||
*
|
||||
* 自动演示范围:只有 create 段(进入新建页)与 chat 段自动弹;list 段挂在
|
||||
* 智能体列表页但不自动演示(`autoStart={false}`,避免一进列表页就弹引导),
|
||||
* 仅由头部「使用引导」按钮手动触发。
|
||||
*
|
||||
* 高亮目标通过 `data-agent-guide` 标记在各组件上;启动时按目标是否存在过滤步骤
|
||||
* (如新用户无智能体卡片、无预设常用问题时自动剔除该步)——Guide 对找不到的
|
||||
* 目标元素会抛错,不能盲传。
|
||||
@@ -121,7 +125,7 @@ const LIST_STEPS: StepSpec[] = [
|
||||
mode: "popup",
|
||||
placement: "bottom-left",
|
||||
title: "创建智能体",
|
||||
body: "准备好了就点这里,开始创建你的专属智能体。点「去创建智能体」直接进入下一站。",
|
||||
body: "准备好了就点这里,开始创建你的专属智能体;进入创建页后还会有一段配置引导。",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -260,17 +264,19 @@ function buildSteps(stage: AgentGuideStage): GuideStep[] {
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能体三段引导的运行组件:`stage` 决定步骤内容,`eligible` 为false 时完全
|
||||
* 静默(不监听、不自动弹)。挂在三个页面各自的作用域内。
|
||||
* 智能体三段引导的运行组件:`stage` 决定步骤内容,`eligible` 为 false 时完全
|
||||
* 静默(不监听、不自动弹)。挂在三个页面各自的作用域内;`autoStart` 为 false
|
||||
* 时仅响应手动触发(列表页不自动弹,进新建页才自动演示)。
|
||||
*/
|
||||
export function AgentGuide({
|
||||
stage,
|
||||
eligible,
|
||||
autoStart = true,
|
||||
}: {
|
||||
stage: AgentGuideStage;
|
||||
eligible: boolean;
|
||||
autoStart?: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [current, setCurrent] = useState(-1);
|
||||
const stepsRef = useRef<GuideStep[]>([]);
|
||||
|
||||
@@ -285,7 +291,7 @@ export function AgentGuide({
|
||||
// 「已看过」只由 finish 回调(完成/跳过)写入。
|
||||
const autoTriedRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!eligible || autoTriedRef.current) return;
|
||||
if (!eligible || !autoStart || autoTriedRef.current) return;
|
||||
autoTriedRef.current = true;
|
||||
if (isStageFinished(stage)) return;
|
||||
const timer = setTimeout(start, START_DELAY_MS);
|
||||
@@ -294,7 +300,7 @@ export function AgentGuide({
|
||||
// 组件在计时窗口内卸载(用户迅速离开页面)则下次进入再演示。
|
||||
autoTriedRef.current = false;
|
||||
};
|
||||
}, [eligible, stage, start]);
|
||||
}, [eligible, autoStart, stage, start]);
|
||||
|
||||
// 手动触发:页面头部「使用引导」按钮广播事件,忽略已看过记忆强制重放。
|
||||
useEffect(() => {
|
||||
@@ -305,15 +311,11 @@ export function AgentGuide({
|
||||
window.removeEventListener(startEventName(stage), handler);
|
||||
}, [eligible, stage, start]);
|
||||
|
||||
// 完成 / 跳过是用户显式结束引导 —— 只有这两种情况才记忆「已看过」。
|
||||
// list 阶段的最后一步按钮文案是「去创建智能体 →」,完成即跳转衔接下一段。
|
||||
// 完成 / 跳过都是用户显式结束引导,就地关闭并记忆「已看过」,不跳转任何页面。
|
||||
const finish = useCallback(() => {
|
||||
markStageFinished(stage);
|
||||
setCurrent(-1);
|
||||
if (stage === "list") {
|
||||
router.push("/page/workspace/agents/new");
|
||||
}
|
||||
}, [stage, router]);
|
||||
}, [stage]);
|
||||
|
||||
if (!eligible) return null;
|
||||
|
||||
@@ -324,9 +326,6 @@ export function AgentGuide({
|
||||
onChange={setCurrent}
|
||||
onFinish={finish}
|
||||
onSkip={finish}
|
||||
finishButtonProps={
|
||||
stage === "list" ? { content: "去创建智能体 →" } : undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -19,17 +19,16 @@ const TOOLBOX_ICON_TONES = [
|
||||
* 简洁模式右侧工具箱。
|
||||
*
|
||||
* 菜单来源与左侧简洁菜单、设置和更多完全一致,由管理员在菜单管理中把项目
|
||||
* 放到 ``toolbox`` 泳道。点击右侧竖排按钮后展开单列竖向抽屉。
|
||||
* 放到 ``toolbox`` 泳道。默认展开常显(不再藏进右侧按钮后面);点 X 或 Esc
|
||||
* 收起后右侧出现竖排按钮,可再次展开。
|
||||
*/
|
||||
export function CompactToolbox() {
|
||||
const { toolboxItems } = useWorkspaceCompactNavItems();
|
||||
const { pathname } = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [open, setOpen] = useState(true);
|
||||
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||
const closeRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => setOpen(false), [pathname]);
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
|
||||
@@ -41,7 +40,6 @@ export function CompactToolbox() {
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
closeRef.current?.focus();
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown);
|
||||
};
|
||||
@@ -73,22 +71,8 @@ export function CompactToolbox() {
|
||||
<span className="text-xs font-semibold leading-4">工具箱</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
tabIndex={-1}
|
||||
aria-label="关闭工具箱抽屉"
|
||||
aria-hidden={!open}
|
||||
onClick={closeDrawer}
|
||||
className={cn(
|
||||
"fixed inset-0 z-[90] bg-black/20 transition-opacity duration-200",
|
||||
open ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0",
|
||||
)}
|
||||
/>
|
||||
|
||||
<aside
|
||||
id="compact-toolbox-panel"
|
||||
role="dialog"
|
||||
aria-modal={open}
|
||||
aria-label="工具箱"
|
||||
aria-hidden={!open}
|
||||
inert={!open}
|
||||
@@ -102,10 +86,9 @@ export function CompactToolbox() {
|
||||
<header className="relative flex h-12 shrink-0 items-center justify-center px-2">
|
||||
<h2 className="text-sm font-semibold">工具箱</h2>
|
||||
<button
|
||||
ref={closeRef}
|
||||
type="button"
|
||||
aria-label="关闭工具箱"
|
||||
title="关闭"
|
||||
aria-label="收起工具箱"
|
||||
title="收起"
|
||||
onClick={closeDrawer}
|
||||
className="absolute right-2 flex size-7 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
>
|
||||
@@ -131,10 +114,7 @@ export function CompactToolbox() {
|
||||
? "bg-primary/12 text-primary ring-1 ring-inset ring-primary/25"
|
||||
: "bg-muted/55 text-card-foreground hover:bg-primary/8 hover:text-primary",
|
||||
)}
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
navigate(item.path ?? "/page/workspace/chats/new");
|
||||
}}
|
||||
onClick={() => navigate(item.path ?? "/page/workspace/chats/new")}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
|
||||
Reference in new issue
Block a user