用户引导

This commit is contained in:
董宏凯 committed 2026-08-26 09:41:53 +08:00
1 parent 62a3faa7c8
commit c6fcf51e0c
12 files changed
+785 -77

No files matched your search

+62 -19
View File
@@ -1,6 +1,6 @@
"use client";
import { BotIcon, PlusSquare } from "lucide-react";
import { BotIcon, Compass, PlusSquare } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useLocation } from "react-router-dom";
import { useRouteBase } from "@/core/route-base";
@@ -11,6 +11,11 @@ import { Button } from "@/components/ui/button";
import { AgentChatSidebar } from "@/components/workspace/agents/agent-chat-sidebar";
import { AgentDetailPanel } from "@/components/workspace/agents/agent-detail-panel";
import { AgentQuestionChips } from "@/components/workspace/agents/agent-question-chips";
import {
AgentGuide,
isAgentGuideEnvironment,
startAgentGuide,
} from "@/components/workspace/agents/agent-guide";
import { AgentWelcome } from "@/components/workspace/agent-welcome";
import { ArtifactTrigger } from "@/components/workspace/artifacts";
import { ChatBox, useThreadChat } from "@/components/workspace/chats";
@@ -559,6 +564,10 @@ export default function AgentChatPage({
// 3qfx/xdfx(taskBind)不展示闸门,新建对话进问答页后由右上角「开始回答」按钮手动发出开场信息。
const showTaskGate = !!taskGate && !!taskId && isNewThread && !hasStartedConversation;
const isDemoMode = env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true";
// 智能体使用引导(chat 阶段):仅普通智能体对话页 —— 排除通用问答原地嵌入
// (embeddedAgentId)、任务深链工作区(taskGate/taskBind)等复用本页的场景。
const agentGuideEligible =
!!showAgentList && !isEmbedded && !taskCtxOn && isAgentGuideEnvironment();
const status = thread.error
? "error"
@@ -575,7 +584,14 @@ export default function AgentChatPage({
panel stays rightmost. */}
<div className="flex size-full min-h-0 overflow-hidden workspace-chat-bg">
{/* 最左:智能体列表栏(仅独立整页问答开启)。顶部下拉切换 我的/广场。 */}
{showAgentList && <AgentChatSidebar activeAgentId={agent_id} />}
{showAgentList && (
<div
data-agent-guide="chat-sidebar"
className="flex h-full shrink-0"
>
<AgentChatSidebar activeAgentId={agent_id} />
</div>
)}
<div className="min-h-0 min-w-0 flex-1">
<ChatBox threadId={threadId}>
{/* Main conversation column. `relative` hosts the floating
@@ -598,6 +614,20 @@ export default function AgentChatPage({
</div>
<div className="flex items-center">
{/* 智能体使用引导(chat 阶段)手动重放入口。 */}
{agentGuideEligible && (
<Tooltip content="重新播放智能体使用引导">
<Button
size="sm"
variant="ghost"
className="text-muted-foreground hover:text-foreground mr-1"
onClick={() => startAgentGuide("chat")}
>
<Compass className="size-4" />
使用引导
</Button>
</Tooltip>
)}
{/* 「模板数据转换器」专属:选页面模板 + 渲染预览(调试该智能体效果)。 */}
{isTemplateBuilder && (
<div className="mr-2 flex items-center gap-1.5">
@@ -688,13 +718,17 @@ export default function AgentChatPage({
className="mb-7"
/>
<div className="w-full max-w-(--container-width-md)">
<AgentQuestionChips
className="mb-3"
questions={commonQuestions}
disabled={isDemoMode}
onAsk={(text) => handleSubmit({ text, files: [] })}
/>
<div className="min-w-0 flex-1">
<div data-agent-guide="chat-questions" className="mb-3">
<AgentQuestionChips
questions={commonQuestions}
disabled={isDemoMode}
onAsk={(text) => handleSubmit({ text, files: [] })}
/>
</div>
<div
className="min-w-0 flex-1"
data-agent-guide="chat-input"
>
<InputBox
className="w-full"
isNewThread
@@ -756,7 +790,10 @@ export default function AgentChatPage({
/>
<div className="from-background pointer-events-none shrink-0 bg-gradient-to-t to-transparent px-4 pb-4 pt-2">
<div className="pointer-events-auto mx-auto w-full max-w-(--container-width-lg)">
<div
className="pointer-events-auto mx-auto w-full max-w-(--container-width-lg)"
data-agent-guide="chat-input"
>
<TodoList
className="bg-background/5 mb-2"
todos={thread.values.todos ?? []}
@@ -805,16 +842,22 @@ export default function AgentChatPage({
</div>
{/* Right: agent detail panel — always the rightmost column. */}
<AgentDetailPanel
agent={agent}
agentName={agentDisplayName}
disabled={thread.isLoading || isDemoMode}
onAskQuestion={(text) => handleSubmit({ text, files: [] })}
defaultCollapsed={taskCtxOn}
showRecentChats={!taskCtxOn}
compactAppearanceActions={compactAppearanceActions}
/>
<div
data-agent-guide="chat-detail"
className="flex h-full shrink-0"
>
<AgentDetailPanel
agent={agent}
agentName={agentDisplayName}
disabled={thread.isLoading || isDemoMode}
onAskQuestion={(text) => handleSubmit({ text, files: [] })}
defaultCollapsed={taskCtxOn}
showRecentChats={!taskCtxOn}
compactAppearanceActions={compactAppearanceActions}
/>
</div>
</div>
<AgentGuide stage="chat" eligible={agentGuideEligible} />
{/* 「模板数据转换器」调试预览弹窗(仅此 agent)。 */}
{isTemplateBuilder && (
+22 -2
View File
@@ -34,11 +34,13 @@ import {
} from "@/components/workspace/appearance-controls";
import { TokenUsageIndicator } from "@/components/workspace/token-usage-indicator";
import { UserIdentityBadge } from "@/components/workspace/user-identity-badge";
import { CompactModeGuide } from "@/components/workspace/compact-mode-guide";
import { Welcome } from "@/components/workspace/welcome";
import { CompactWelcome } from "@/components/workspace/compact-welcome";
import { readSidebarEnabled } from "@/core/page-layout/sidebar-enabled";
import { isChatsIframeWorkspacePath } from "@/core/page-layout/chats-iframe-route";
import { isHideChromeActive } from "@/core/embed/embed-session";
import { isEmbedActive, isHideChromeActive } from "@/core/embed/embed-session";
import { isWujieEnv } from "@/core/embed/wujie";
import { useSidebar } from "@/components/ui/sidebar";
import { Button } from "@/components/ui/button";
import { PanelLeftOpen } from "lucide-react";
@@ -665,6 +667,17 @@ export default function ChatPage() {
chromeByPreference && !isChatsIframeWorkspacePath(pathname);
const compactLanding = isLandingState && compactChrome;
const compactActionsHidden = isHideChromeActive();
// 简洁模式新手引导:仅面向「无全局导航栏/侧边栏」的正常简洁布局。iframe 嵌入
// (embed=1)、去 chrome(hideChrome=1)、强制收起侧栏(无界部署)等环境同样没有
// 全局 chrome,但不该在外部系统里弹引导。
const compactGuideEligible =
!compactChrome &&
!fullChatMode &&
!isChatsIframeWorkspacePath(pathname) &&
!compactActionsHidden &&
!env.FORCE_COLLAPSED_SIDEBAR &&
!isEmbedActive() &&
!isWujieEnv();
return (
<ThreadContext.Provider value={{ thread: displayThread, isMock }}>
@@ -801,7 +814,10 @@ export default function ChatPage() {
/>
))}
{mounted ? (
<div className="w-full -translate-y-4">
<div
className="w-full -translate-y-4"
data-compact-guide="prompt-input"
>
<InputBox
className="w-full"
isNewThread={isLandingState}
@@ -886,6 +902,10 @@ export default function ChatPage() {
className="top-16 right-0 bottom-48 left-0"
/>
)}
<CompactModeGuide
eligible={compactGuideEligible}
autoStart={mounted && isLandingState}
/>
</div>
</ChatBox>
</ThreadContext.Provider>
+70 -41
View File
@@ -3,6 +3,7 @@
import {
ArrowLeftIcon,
CheckCircleIcon,
Compass,
SaveIcon,
} from "lucide-react";
import { useParams, useRouter } from "next/navigation";
@@ -39,6 +40,11 @@ import {
import { ThreadContext } from "@/components/workspace/messages/context";
import { SkillTransferSelector } from "@/components/workspace/agents/skill-transfer-selector";
import { SkillDetailDialog } from "@/components/workspace/skills/skill-detail-dialog";
import {
AgentGuide,
isAgentGuideEnvironment,
startAgentGuide,
} from "@/components/workspace/agents/agent-guide";
import { getAPIClient } from "@/core/api";
import type { Agent } from "@/core/agents";
import {
@@ -407,16 +413,28 @@ export default function NewAgentPage() {
</Button>
<h1 className="text-sm font-semibold">创建智能体</h1>
</div>
{agent ? (
<div className="flex items-center gap-2">
<Button
variant="outline"
variant="ghost"
size="sm"
onClick={() => router.push(`/page/workspace/agents/${agent.id}/chats/new`)}
className="text-muted-foreground hover:text-foreground"
title="重新播放创建引导"
onClick={() => startAgentGuide("create")}
>
<CheckCircleIcon className="h-4 w-4" />
开始对话
<Compass className="mr-1.5 h-4 w-4" />
使用引导
</Button>
) : null}
{agent ? (
<Button
variant="outline"
size="sm"
onClick={() => router.push(`/page/workspace/agents/${agent.id}/chats/new`)}
>
<CheckCircleIcon className="h-4 w-4" />
开始对话
</Button>
) : null}
</div>
</header>
<ResizablePanelGroup
@@ -442,7 +460,10 @@ export default function NewAgentPage() {
onSetupAgentReject={handleRejectSetupAgent}
/>
</div>
<div className="bg-background shrink-0 space-y-2 border-t px-4 py-4">
<div
className="bg-background shrink-0 space-y-2 border-t px-4 py-4"
data-agent-guide="create-chat-input"
>
<div className="flex items-center gap-2">
<span className="text-muted-foreground text-xs">
当前对话模型
@@ -525,32 +546,35 @@ export default function NewAgentPage() {
</p>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">名称</label>
<Input
placeholder="例如:合同审查智能体"
value={nameInput}
onChange={(event) => {
setNameInput(event.currentTarget.value);
setFormError("");
}}
className={cn(formError && "border-destructive")}
/>
<div className="space-y-5" data-agent-guide="create-basic">
<div className="space-y-2">
<label className="text-sm font-medium">名称</label>
<Input
placeholder="例如:合同审查智能体"
value={nameInput}
onChange={(event) => {
setNameInput(event.currentTarget.value);
setFormError("");
}}
className={cn(formError && "border-destructive")}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">描述</label>
<Textarea
placeholder="简要说明这个智能体擅长什么"
value={descriptionInput}
onChange={(event) =>
setDescriptionInput(event.currentTarget.value)
}
className="min-h-24 resize-none"
/>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">描述</label>
<Textarea
placeholder="简要说明这个智能体擅长什么"
value={descriptionInput}
onChange={(event) =>
setDescriptionInput(event.currentTarget.value)
}
className="min-h-24 resize-none"
/>
</div>
<div className="space-y-2">
<div className="space-y-5" data-agent-guide="create-model">
<div className="space-y-2">
<label className="text-sm font-medium">模型</label>
{isModelsLoading || models.length === 0 ? (
<div className="border-input bg-background text-muted-foreground flex h-9 w-full items-center rounded-md border px-3 text-sm shadow-xs dark:border-[#1B477E] dark:bg-[#022248]">
@@ -590,9 +614,9 @@ export default function NewAgentPage() {
<p className="text-muted-foreground text-xs">
该智能体被使用时调用的模型。
</p>
</div>
</div>
<div className="space-y-2">
<div className="space-y-2">
<label className="text-sm font-medium">入库模板名</label>
<Input
placeholder="例如:临时策略库(留空表示不支持入库)"
@@ -605,16 +629,19 @@ export default function NewAgentPage() {
配置后,该智能体对话中生成的 Markdown
文件右上角会出现"发布入库"按钮,可一键发送到知识入库接口。
</p>
</div>
</div>
<LlmWikiKnowledgeSelector
source="all"
value={selectedKnowledgeBaseIds}
onChange={setSelectedKnowledgeBaseIds}
variant="panel"
/>
<div data-agent-guide="create-knowledge">
<LlmWikiKnowledgeSelector
source="all"
value={selectedKnowledgeBaseIds}
onChange={setSelectedKnowledgeBaseIds}
variant="panel"
/>
</div>
<div className="space-y-2">
<div className="space-y-2" data-agent-guide="create-soul">
<label className="text-sm font-medium">SOUL.md</label>
<Textarea
placeholder="写下智能体的人设、边界、工作方式和输出要求"
@@ -624,7 +651,7 @@ export default function NewAgentPage() {
/>
</div>
<div className="space-y-2">
<div className="space-y-2" data-agent-guide="create-skills">
<div className="flex items-center justify-between">
<label className="text-sm font-medium">技能</label>
<span className="text-muted-foreground text-xs">
@@ -675,6 +702,7 @@ export default function NewAgentPage() {
className="w-full"
onClick={() => void handleSaveAgent()}
disabled={busy || isSkillsLoading}
data-agent-guide="create-save-btn"
>
<SaveIcon className="h-4 w-4" />
{agentId ? "保存智能体" : "创建智能体"}
@@ -683,6 +711,7 @@ export default function NewAgentPage() {
</aside>
</ResizablePanel>
</ResizablePanelGroup>
<AgentGuide stage="create" eligible={isAgentGuideEnvironment()} />
</div>
</ArtifactsProvider>
</ThreadContext.Provider>