Merge remote-tracking branch 'origin/master' into master

This commit is contained in:
吉鑫升 committed 2026-08-31 18:06:10 +08:00
commit 90e94994f5
16 files changed
+922 -179

No files matched your search

@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { injectInPageAnchorHandler } from "@/core/utils/markdown";
import { prepareGeneratedHtml } from "@/core/utils/legacy-html-preview";
/**
* Sandboxed preview of a generated HTML document.
@@ -26,7 +26,7 @@ export function HtmlPreviewFrame({
const [url, setUrl] = useState<string>();
useEffect(() => {
const blob = new Blob([injectInPageAnchorHandler(html ?? "")], {
const blob = new Blob([prepareGeneratedHtml(html ?? "")], {
type: "text/html;charset=utf-8",
});
const objectUrl = URL.createObjectURL(blob);
@@ -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
}
/>
);
}
@@ -97,9 +97,9 @@ import {
openMermaidSvgInNewTab,
renderMermaidToSvg,
} from "@/core/utils/mermaid";
import { prepareGeneratedHtml } from "@/core/utils/legacy-html-preview";
import {
createMarkdownAnchorResolver,
injectInPageAnchorHandler,
isInPageAnchor,
scrollToInPageAnchor,
} from "@/core/utils/markdown";
@@ -1422,7 +1422,9 @@ export function ArtifactFileDetail({
toast.error("文件内容为空,暂时无法在新窗口打开");
return;
}
const blob = new Blob([source], { type: "text/html;charset=utf-8" });
const blob = new Blob([prepareGeneratedHtml(source)], {
type: "text/html;charset=utf-8",
});
const url = URL.createObjectURL(blob);
window.open(url, "_blank", "noopener,noreferrer");
window.setTimeout(() => URL.revokeObjectURL(url), 60_000);
@@ -1460,7 +1462,7 @@ export function ArtifactFileDetail({
return (
<Artifact className={cn(className)}>
<ArtifactHeader className="relative flex flex-wrap items-center gap-3 px-4 py-3 pr-14">
<ArtifactHeader className="relative flex flex-wrap items-center gap-3 px-4 py-3 pr-14">
<div className="flex min-w-0 items-center gap-2">
<div
className={cn(
@@ -1554,8 +1556,8 @@ export function ArtifactFileDetail({
</div>
)}
</div>
<div className="flex min-w-0 w-full items-center justify-end gap-2 md:ml-auto md:w-auto">
<ArtifactActions className="bg-muted/45 min-w-0 max-w-full flex-wrap justify-end rounded-3xl border border-black/6 p-1 dark:border-white/8">
<div className="flex min-w-0 w-full items-center justify-end gap-2 md:ml-auto md:w-auto">
<ArtifactActions className="bg-muted/45 min-w-0 max-w-full flex-wrap justify-end rounded-3xl border border-black/6 p-1 dark:border-white/8">
{canPublishToKnowledge && (
<ArtifactAction
icon={isPublishingToKnowledge ? LoaderIcon : UploadCloudIcon}
@@ -1683,18 +1685,18 @@ export function ArtifactFileDetail({
}
}}
/>
</ArtifactActions>
</div>
{/* 文件目录会压缩详情区宽度。关闭入口固定在右上角,不再跟随可换行的
动作组,避免工具较多或面板较窄时最右侧按钮被裁掉。 */}
<ArtifactAction
className="bg-background/90 hover:bg-accent absolute top-3 right-3 z-10 border shadow-sm"
icon={XIcon}
label="收起文件面板"
onClick={() => (onClose ? onClose() : setOpen(false))}
tooltip="收起文件面板"
/>
</ArtifactHeader>
</ArtifactActions>
</div>
{/* 文件目录会压缩详情区宽度。关闭入口固定在右上角,不再跟随可换行的
动作组,避免工具较多或面板较窄时最右侧按钮被裁掉。 */}
<ArtifactAction
className="bg-background/90 hover:bg-accent absolute top-3 right-3 z-10 border shadow-sm"
icon={XIcon}
label="收起文件面板"
onClick={() => (onClose ? onClose() : setOpen(false))}
tooltip="收起文件面板"
/>
</ArtifactHeader>
<ArtifactContent
ref={artifactContentRef}
className="relative p-0"
@@ -2084,7 +2086,7 @@ export function ArtifactFilePreview({
// <meta charset> 就会中文乱码。与下方下载/新窗口打开的 blobType 保持一致。
// Intercept in-page anchor clicks so they scroll within the iframe instead
// of navigating the sandboxed blob: document (which blanks the frame).
const blob = new Blob([injectInPageAnchorHandler(content ?? "")], {
const blob = new Blob([prepareGeneratedHtml(content ?? "")], {
type: "text/html;charset=utf-8",
});
const url = URL.createObjectURL(blob);
@@ -143,6 +143,7 @@ export function MarkdownContent({
className={className}
remarkPlugins={remarkPlugins}
rehypePlugins={rehypePlugins}
mermaidConfig={streamdownPlugins.mermaidConfig}
components={components}
>
{content}