69 KiB
前端开发文档(frontend-web)
本文档面向参与
frontend-web/开发的工程师,覆盖工程结构、技术栈、环境与启动、路由与页面、状态体系、与后端 LangGraph 的实时流式交互、上传/产物(Artifacts)、模型与设置、鉴权、主题与国际化、构建与发布等所有关键开发点,力求从零到一帮助新成员快速上手。
1. 概览
- 项目名:
deer-flow-frontend-vite(package.json) - 类型:单页应用(SPA),React 19 + TypeScript 5 + Vite 6
- 包管理器:pnpm(
packageManager: pnpm@10.26.2) - 路由:
react-router-domv7(采用 HashRouter) - 状态:TanStack Query(服务端状态)+ React Context(流式会话/Artifacts/Subtasks/PromptInput)+
localStorage/sessionStorage(用户偏好与流式恢复) - UI 体系:TailwindCSS v4 + Radix UI 原语 + shadcn 风格自封装(
src/components/ui/)+ TDesign(少量页面)+ Lucide 图标 - LLM 流式交互:
@langchain/langgraph-sdk,与后端 Gateway 上的 LangGraph 运行时(默认lead_agent)建立 SSE 连接 - 渲染:
react-markdown+streamdown+rehype-*+shiki/react-syntax-highlighter+katex - 富文本:
@tiptap/*(用于 Open Canvas 写作) - 编辑器:
@uiw/react-codemirror(代码片段编辑) - 流程图:
@xyflow/react(节点连线类组件)
这是一个由 Next.js 历史代码迁移而来的 Vite 工程:保留
next/link、next/navigation、use client等遗留写法,并通过 src/shims/ 提供运行时兼容。新开发中可以继续按 Next.js 习惯写useRouter()/useParams()/usePathname(),由 shim 转译为 react-router 实现。
2. 目录结构
frontend-web/
├─ index.html 入口模板(含黑/白主题快速注入脚本)
├─ vite.config.ts Vite 配置(alias + sourceIdentifier 插件)
├─ tsconfig.json TS 编译选项
├─ postcss.config.js PostCSS + tailwindcss v4
├─ .env / .env.development / .env.production 后端地址等环境变量
├─ docs/ 前端文档(本目录)
└─ src/
├─ main.tsx React 入口(StrictMode + 全局样式)
├─ App.tsx 根组件:HashRouter + 顶层 Provider 嵌套
├─ env.ts 读 import.meta.env(兼容旧 NEXT_PUBLIC_ 命名)
├─ styles/globals.css 全局 + Tailwind 入口
├─ shims/ next/link、next/navigation 的 react-router 适配
├─ pages/ 路由页面(HashRouter 下的所有 Route 元素)
│ ├─ PageRoutes.tsx /page/* 一级布局:左侧 PageSidebar + Header
│ ├─ WorkspaceRoutes.tsx /page/workspace/* 二级路由(聊天/Agents/Skills/Memory/Admin…)
│ ├─ StrategyRoutes.tsx /page/strategy/* 策略页
│ ├─ RoundtableRoutes.tsx /page/roundtable/*
│ ├─ ChatPage.tsx 主聊天页(绑定 lead_agent)
│ ├─ AgentChatPage.tsx 基于自定义 Agent 的会话
│ ├─ ScheduledTasksPage.tsx 定时任务
│ ├─ ScheduledTaskRunDetailPage.tsx
│ ├─ PublicSharePage.tsx 免登录分享只读
│ ├─ PublicScheduledTaskPage.tsx
│ ├─ EmbedChatPage.tsx iframe 嵌入问答(见 docs/embed-iframe-chat.md)
│ ├─ LoginPage.tsx 用户名直登(开发/内网用)
│ └─ ...
├─ core/ 「领域核心」: API 客户端、领域 hooks、类型
│ ├─ api/ langgraph-sdk client + 鉴权 fetch
│ ├─ auth/ 访问令牌存取(localStorage)
│ ├─ config/ 后端/LangGraph URL 解析
│ ├─ threads/ 会话流(核心 useThreadStream)
│ ├─ uploads/ 线程上传/校验/解析
│ ├─ settings/ 本地偏好与线程级模型覆盖
│ ├─ tasks/ 子任务 SubtaskContext
│ ├─ models/ 可用模型 + 用量开关
│ ├─ agents/ 自定义 Agent CRUD + 选择器
│ ├─ skills/、memory/、tags/ 其他领域
│ ├─ scheduled-tasks/ 定时任务
│ ├─ artifacts/ 产物(代码/文档)读取
│ ├─ recommended-questions/、notifications/、admin/、curator/、tool-metrics/、llm-metrics/
│ ├─ note/ 轻应用(笔记)API(独立后端 VITE_NOTE_API_BASE_URL)
│ ├─ i18n/ 语言上下文、词条、检测
│ ├─ rehype/、streamdown/、tools/、messages/、page-layout/、route-base.tsx
│ └─ utils/ uuid、json、files、markdown、datetime
├─ contexts/ 全局 React 上下文(Chat/Theme)
├─ components/
│ ├─ ui/ shadcn 风格基础组件(button、dialog、sidebar、resizable…)
│ ├─ ai-elements/ AI 业务组件(prompt-input、conversation、message、plan、reasoning…)
│ ├─ workspace/ 工作台模块(chats、messages、artifacts、settings、admin、agents…)
│ ├─ landing/ 营销页(Landing)
│ ├─ query-client-provider.tsx TanStack Query 容器
│ └─ theme-provider.tsx next-themes 容器
├─ open-canvas/ 开放画布/AI 写作(独立路由:/page/canvas/*)
├─ roundtable-planning/ 圆桌规划(独立路由:/page/roundtable/*)
├─ strategy-components/ 策略相关组件 + Header
├─ hooks/ 通用工具 hooks(debounce、async、localStorage…)
└─ lib/ 通用工具:cn、desensitize、ime
3. 启动、构建、脚本
3.1 NPM Scripts(package.json)
| 脚本 | 命令 | 说明 |
|---|---|---|
pnpm dev |
vite |
启动开发服务器(默认端口 5173;项目实际通过仓库根脚本统一为 5174) |
pnpm build |
node --max-old-space-size=8192 vite build |
生产构建(提升 Node 内存阈值,避免大型依赖 OOM) |
pnpm preview |
vite preview |
本地预览 dist 构建产物 |
pnpm typecheck |
tsc --noEmit |
仅做 TS 类型检查,不输出文件 |
3.2 仓库根脚本
scripts/start-frontend.sh(与后端联动)会自动注入 VITE_BACKEND_BASE_URL=http://127.0.0.1:8001 与 VITE_LANGGRAPH_BASE_URL=http://127.0.0.1:8001/api,并将日志/PID 写入 .runtime/。详细命令见根目录 CLAUDE.md:
./scripts/start-all.sh # 同时启动 backend + frontend
./scripts/stop-all.sh
./scripts/restart-all.sh
./scripts/status.sh
3.3 Vite 别名(vite.config.ts)
| 别名 | 真实路径 |
|---|---|
@/ |
src/ |
@vite/ |
src/ |
@/env |
src/env.ts |
next/link |
src/shims/next-link.tsx |
next/navigation |
src/shims/next-navigation.ts |
工程同时配置了 vite-plugin-source-identifier:构建产物会带源文件标记,便于线上排错对应到源文件。
base: "./" 表示打包后使用相对路径,方便部署到任意子路径或本地 file:// 调试。
3.4 浏览器与构建目标
- TS
target: ES2022、module: ESNext、moduleResolution: Bundler useDefineForClassFields: true、isolatedModules: true、jsx: react-jsx- 严格模式开启,但
noImplicitAny: false
4. 环境变量
入口在 src/env.ts,所有公共变量使用 VITE_ 前缀,运行时通过 import.meta.env 读取。出于兼容旧 Next.js 代码,导出的对象保留 NEXT_PUBLIC_ 命名:
env.NEXT_PUBLIC_BACKEND_BASE_URL // VITE_BACKEND_BASE_URL
env.NEXT_PUBLIC_LANGGRAPH_BASE_URL // VITE_LANGGRAPH_BASE_URL
env.NEXT_PUBLIC_CANVAS_LANGGRAPH_BASE_URL // 画布独立 LangGraph(可选,默认回退主地址)
env.NEXT_PUBLIC_NOTE_API_BASE_URL // 轻应用「笔记」API
env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY // 静态演示模式(禁用上传/发送)
env.GITHUB_OAUTH_TOKEN // 备用:landing 中拉取仓库信息
.env 文件加载优先级(Vite 标准):
.env.development.local > .env.development > .env (开发)
.env.production.local > .env.production > .env (生产)
个人本机覆盖请使用 .env.development.local(已 gitignore)。
getBackendBaseURL() / getLangGraphBaseURL() / getCanvasLangGraphBaseURL() 均在 src/core/config/index.ts 中,会基于当前 window.location.origin 兜底为同源 /api/langgraph。
5. 渲染入口与 Provider 体系
5.1 启动栈
ReactDOM.createRoot(#root)
└─ <React.StrictMode>
└─ <App>
└─ <HashRouter>
└─ <AppProviders>
├─ <NextThemesProvider> // next-themes,attribute=class,存储键 "strategy-theme"
│ ├─ <I18nProvider> // 语言上下文(locale)
│ │ └─ <ThemeProvider> // 自定义主题(与 next-themes 协作)
│ │ └─ <ChatProvider> // QA 历史(localStorage: qa-chat-history)
│ │ └─ <QueryClientProvider> // TanStack Query
│ │ └─ <Routes>...
index.html 头部内联了一段脚本,会在 React 启动前根据 localStorage["strategy-theme"] 决定是否给 <html> 添加 dark 类,避免暗色主题闪烁。
5.2 路由
外层(App.tsx):
/ -> Redirect to /login
/login LoginPage
/login/:username LoginPage(按用户名直登)
/landing LandingPage
/share/:shareCode PublicSharePage(免登录只读)
/embed/chats/:thread_id EmbedChatPage(iframe 嵌入问答,见 docs/embed-iframe-chat.md)
/public/scheduled-tasks/:taskId PublicScheduledTaskPage(免登录)
/page/* PageRoutes(含侧边栏与 Header)
* NotFoundPage
/page/* 内(PageRoutes.tsx):
/page/workspace/* -> WorkspaceRoutes(默认)
/page/strategy/* -> StrategyRoutes
/page/canvas/* -> open-canvas/routes/OpenCanvasRoutes
/page/roundtable/* -> RoundtableRoutes
/page/workspace/*(WorkspaceRoutes.tsx):
chats -> Navigate to chats/new
chats/:thread_id -> <WorkspaceLayout><ChatRuntime><ChatPage>
agents -> AgentsPage
agents/new -> NewAgentPage
agents/:agent_id/chats/:thread_id -> AgentChatPage
skills -> SkillsPage
skills/evolution -> SkillEvolutionPage
memory -> MemoryPage
ai-writing -> AIWritingPage(来自 open-canvas)
roundtable/planning -> RoundtablePlanningPage
scheduled/tasks -> ScheduledTasksPage
scheduled/runs/:run_id -> ScheduledTaskRunDetailPage
admin -> AdminUserHubPage (system_role=admin)
admin/scheduled-tasks -> AdminScheduledTasksPage (admin)
admin/tags -> TagsPage (admin)
settings/recommended-questions (admin)
settings/prompt-prefix (admin)
settings/llm-metrics (admin)
WorkspaceRoutes 通过 <RouteBaseProvider value="/page/workspace"> 提供基址上下文,让兼容自 Next.js 的 useRouter().push("/page/workspace/...") 在不同的二级路由下都能映射到正确的 base。
重要约定:使用 HashRouter,URL 形如
/#/page/workspace/chats/new。这是为了便于在静态文件部署(包括打包后file://)下使用。新建一条聊天时,前端会用history.replaceState(...)直接改写 hash,而不让 router 重渲染整棵子树 —— 这一点在 ChatPage.tsx:75-79 中体现,依赖了useThreadChat()的特殊兜底逻辑(避免 useParams 把new误判为真实 thread_id)。
5.3 Workspace 双层布局
<WorkspaceLayout>提供<SidebarProvider>+<WorkspaceSidebar>+<SidebarInset>+<CommandPalette>+<Toaster>,并通过 CSS 变量--page-sidebar-width把内层侧栏紧贴外层 PageSidebar。- 对于「聊天类」路由 (
chats/:thread_id、agents/.../chats/:thread_id),额外包一层<ChatRuntime>:
<SubtasksProvider>
<ArtifactsProvider>
<PromptInputProvider>
{children}
</PromptInputProvider>
</ArtifactsProvider>
</SubtasksProvider>
这三个上下文只在聊天会话内生效,避免污染列表/管理类页面。
6. 鉴权
6.1 登录流程
src/pages/LoginPage.tsx 通过 loginByUsername(username) 直接向后端发起登录(默认 guest),返回结构存在 src/core/auth/index.ts:
interface LoginByUsernameResponse {
access_token: string;
token_type: string; // 默认 "Bearer"
expires_in: number;
user_id: string;
email: string;
system_role: string; // "admin" 控制管理员菜单
needs_setup: boolean;
created: boolean;
}
存储键:localStorage["deerflow.auth"]。
工具方法:
getStoredAuth()/setStoredAuth(auth)/clearStoredAuth()getAccessToken()—— 返回 token 字符串getAuthorizationHeaderValue()—— 返回"Bearer xxx",缺失时 undefined
6.2 鉴权注入
所有出站请求都通过两条路径之一注入 Token:
- 裸 fetch —— 使用 src/core/api/fetch-client.ts 提供的
apiFetch(input, init),自动在 headers 上加Authorization。 - LangGraph SDK —— src/core/api/api-client.ts 在
new LangGraphClient的onRequest钩子里塞Authorization,并对runs.stream / runs.joinStream做sanitizeRunStreamOptions包装(见core/api/stream-mode.ts),确保流式参数合法。
务必使用
apiFetch而不是裸fetch,否则鉴权头不会带上,私有接口会 401。
6.3 路由级守护
WorkspaceRoutes 内的 /admin/* 与 /settings/* 路由在元素层面做了判定:
getStoredAuth()?.system_role === "admin"
? <WorkspaceLayout>...</WorkspaceLayout>
: <Navigate to="/page/workspace/chats/new" replace />
非管理员被静默重定向到聊天页。
7. 与后端的通信
7.1 三种入口
| 通道 | 函数/客户端 | 用途 |
|---|---|---|
| LangGraph SDK | getAPIClient(isMock?) |
LangGraph runtime:threads.search/get/update/delete,runs.stream/joinStream |
| HTTP fetch | apiFetch(url, init) |
Gateway 业务接口(Agents/Memory/Skills/Tags/Uploads/Scheduled-tasks…) |
| 独立服务 | getNoteApiBaseURL() |
轻应用「笔记」服务 |
7.2 LangGraph 客户端缓存
getAPIClient(isMock?) 通过模块级 Map 进行客户端实例缓存(default / mock),避免重复构造。
7.3 流式聊天的核心:useThreadStream
定义于 src/core/threads/hooks.ts。这是「整套前端的心脏」,所有聊天页都基于它:
const [thread, sendMessage, isUploading] = useThreadStream({
threadId,
context, // LocalSettings["context"]
isMock,
assistantId: "lead_agent",
onStart(threadId) { /* 第一次创建会话时回调,用于改写 URL */ },
onFinish(state) { /* 流式收束,浏览器后台时弹通知 */ },
onToolEnd(evt) { /* 工具调用结束钩子 */ },
});
关键能力:
- 断线重连:通过
sessionStorage["lg:stream:<threadId>"]存储正在进行的 run_id,刷新页面后 SDK 自动joinStream续接。normalizeStoredRunId()容错处理历史 URL/带 query 的情况。 - 乐观更新:发送即在本地 messages 后追加临时
human消息;当文件正在上传时还追加占位ai消息("uploadingFiles");服务端有响应(messages 数量增加)后自动清空。 - 首条消息特例:仅当线程已持久化时立即触发
onStart;新会话需要等到 SDKonCreated(meta.thread_id),确保 URL 同步用的是真正的服务端 thread_id。 - Context 装配:每次提交时把
thinking_enabled / is_plan_mode / subagent_enabled / reasoning_effort / memory_injection_enabled / thread_id等基于「四种 mode」推导:
| mode | thinking_enabled | is_plan_mode | subagent_enabled | reasoning_effort |
|---|---|---|---|---|
flash |
false | false | false | undefined |
thinking |
true | false | false | low |
pro |
true | true | false | medium |
ultra |
true | true | true | high |
- 附件上传:先
uploadFiles(threadId, files)上传到 Gateway/api/threads/:threadId/uploads,然后将path/filename/size写入messages[0].additional_kwargs.files,与 lead_agent 后端约定一致。 - TanStack Query 失效:流式完成或新建会话时调用
queryClient.invalidateQueries({ queryKey: ["threads", "search"] }),让侧边栏「最近会话」自动刷新。 - 错误降级:
onError弹 sonner toast;getStreamErrorMessage(error)兜底解析对象/嵌套字段。
7.4 重要回调事件
| SDK 事件 | 处理 |
|---|---|
onCreated(meta) |
新线程创建:触发 onStart、把 agent_id 写入 thread metadata(非 mock) |
onLangChainEvent |
监听 on_tool_end 并外抛 onToolEnd |
onUpdateEvent |
若 update.title 出现,则更新 TanStack Query 缓存里的 thread 标题 |
onCustomEvent |
task_running:更新 SubtaskContext;llm_retry:弹 toast 提示 |
onError |
清空乐观消息,弹 toast |
onFinish |
触发外部 onFinish + 刷新 threads.search |
7.5 其他领域 hook 约定
所有领域目录(agents/、skills/、memory/、tags/、scheduled-tasks/、notifications/、admin/、curator/、llm-metrics/、tool-metrics/、system-settings/、thread-shares/、recommended-questions/)遵循统一模板:
core/<domain>/
├─ api.ts 底层调用(fetch + apiFetch)
├─ hooks.ts useQuery/useMutation 包装
├─ types.ts 接口类型
└─ index.ts re-export
写新领域时也按此模板维持一致。
8. 全局状态与本地存储
8.1 React Context(运行时状态)
| Context | 文件 | 作用域 | 状态 |
|---|---|---|---|
ChatContext |
contexts/ChatContext.tsx | App 顶层 | 通用 QA 消息历史(localStorage: qa-chat-history) |
ThemeContext |
contexts/ThemeContext.tsx |
App 顶层 | 主题,与 next-themes 协作 |
I18nContext |
core/i18n/context.tsx | App 顶层 | 当前语言(Cookie: locale) |
SubtaskContext |
core/tasks/context.tsx | ChatRuntime | 子任务最新 message |
ArtifactsContext |
components/workspace/artifacts/context.tsx | ChatRuntime | 当前会话产物列表与选中 |
PromptInputContext |
components/ai-elements/prompt-input.tsx | ChatRuntime | 输入框附件/状态 |
RouteBaseContext |
core/route-base.tsx | Workspace/Strategy/Roundtable | 给 useRouter shim 提供 base prefix |
ThreadContext |
components/workspace/messages/context.ts |
每个聊天页内 | 共享 thread 与 isMock 给 MessageList 等子组件 |
8.2 LocalSettings(用户偏好)
core/settings/local.ts + core/settings/store.ts + core/settings/hooks.ts:
LocalSettings = {
notification: { enabled: boolean },
display: { streamSpeedMeterEnabled: boolean },
context: {
model_name?: string,
mode?: "flash" | "thinking" | "pro" | "ultra",
reasoning_effort?: "minimal"|"low"|"medium"|"high",
writing_mode?: boolean,
memory_injection_enabled?: boolean,
agent_id?: string,
}
}
- 持久化:
localStorage["deerflow.local-settings"] - 线程级模型覆盖:
localStorage["deerflow.thread-model.<threadId>"],通过useThreadSettings(threadId)自动合并 - 实现:自定义的「订阅 + storage 事件」store,借助
useSyncExternalStore让多标签页/多组件同步
8.3 sessionStorage
| Key | 写入位置 | 说明 |
|---|---|---|
lg:stream:<threadId> |
useThreadStream(SDK 内部) | 当前 run_id,用于 reconnect |
pendingAgentMessage |
ChatPage 提交时(含 agentId) | 跳转 AgentChatPage 后续发首条消息 |
9. 组件层次
9.1 src/components/ui/
shadcn 风格的「无业务」基础组件:button、badge、avatar、card、carousel、command、dialog、dropdown-menu、hover-card、input、input-group、item、progress、resizable、scroll-area、select、separator、sidebar、skeleton、sonner、switch、tabs、textarea、toggle、toggle-group、tooltip 等。
特色组件:
aurora-text/flickering-grid/magic-bento/spotlight-card/shine-border/confetti-button—— Landing 视觉效果terminal—— 终端样式number-ticker/word-rotate—— 动画文本(基于motion)
9.2 src/components/ai-elements/
AI 业务可复用组件,是聊天页的「乐高」:
| 组件 | 用途 |
|---|---|
prompt-input |
输入区域容器、附件、提交按钮、模式选择 |
conversation |
消息流容器(自动滚动到底,依赖 use-stick-to-bottom) |
message / message-content |
消息气泡 |
reasoning |
折叠的「思考」区段 |
plan |
Plan-Mode 任务计划 |
chain-of-thought |
步骤链路 |
task |
单步任务展示 |
checkpoint |
流程节点 |
sources |
来源引用列表 |
suggestion |
建议追问/快捷动作 |
code-block |
代码块(shiki/syntax highlighter) |
model-selector |
模型下拉 |
web-preview / image |
网页/图片预览 |
canvas / node / edge / controls |
xyflow 流程图原语 |
loader / shimmer / toolbar / panel / queue |
辅助 UI |
open-in-chat |
「在聊天中打开」入口 |
9.3 src/components/workspace/
工作台模块,承载 90% 业务页面:
workspace/
├─ workspace-sidebar.tsx 右栏导航
├─ workspace-header.tsx 顶部
├─ workspace-nav-menu.tsx
├─ workspace-nav-chat-list.tsx 最近会话
├─ workspace-container.tsx 业务包裹器
├─ command-palette.tsx ⌘K
├─ thread-title.tsx
├─ welcome.tsx / agent-welcome.tsx
├─ todo-list.tsx
├─ token-usage-indicator.tsx
├─ streaming-indicator.tsx
├─ input-box.tsx 基于 PromptInput 的实际输入框(模式、Agent、附件)
├─ export-trigger.tsx 导出会话 docx/md
├─ thread-share-dialogs.tsx 创建/管理只读分享链接
├─ recent-chat-list.tsx / recent-writing-list.tsx
├─ chats/
│ ├─ chat-box.tsx 左右双栏 (聊天 + Artifacts),react-resizable-panels
│ ├─ use-thread-chat.ts 解析 :thread_id(new -> uuid)
│ └─ use-chat-mode.ts 特定 mode hook
├─ messages/
│ ├─ message-list.tsx 渲染所有消息、groupMessages
│ ├─ message-group.tsx
│ ├─ message-list-item.tsx
│ ├─ markdown-content.tsx react-markdown + rehype-katex + rehype-raw + remark-gfm + remark-math
│ ├─ subtask-card.tsx
│ ├─ message-token-usage.tsx
│ ├─ use-message-metrics.ts
│ ├─ download-word-button.tsx
│ ├─ context.ts ThreadContext
│ └─ skeleton.tsx
├─ artifacts/
│ ├─ context.tsx ArtifactsContext + Provider
│ ├─ artifact-file-list.tsx
│ ├─ artifact-file-detail.tsx
│ └─ artifact-trigger.tsx 顶部「打开 Artifacts」按钮
├─ citations/ 引用链接渲染
├─ settings/ 设置弹窗与子页(appearance/account/skill/memory/tool/about)
├─ admin/ 管理员页(user-management / tag-management / admin-user-hub / tool-metrics / memory)
├─ agents/ Agent gallery、selector、manage dialog
├─ skills/ skill-detail-dialog、skill-evolution-panel
├─ scheduled-tasks/ schedule-builder
├─ tags/ tag-picker-dialog、tag-chip、tag-search-bar、tag-colors
└─ ...
9.4 src/open-canvas/
AI 写作 / 画布编辑器(基于 @tiptap/*):
routes/OpenCanvasRoutes.tsx二级路由pages/:CanvasHomePage / CanvasThreadPage / AIWritingPage / ArticleTypesConfigPageeditor/TiptapEditor.tsx+TiptapExtensions.ts+AiHighlightExtension.tscontexts/:Canvas 自有的 Stream/Thread/Graph/UI Context(与主聊天页解耦)hooks/:useCanvasStream、useCanvasArtifact、useCanvasThread、useWritingRewrite、useDocumentOutline等api/:自己封装/canvas/runs/canvas/threads/rewrite/writing-export/ai-writing-sessions- 独立 LangGraph 服务地址通过
VITE_CANVAS_LANGGRAPH_BASE_URL配置;未配置则与主聊天同源(仅 assistantId 不同)
9.5 src/roundtable-planning/
圆桌规划页(多 Agent 协同生成方案):
pages/RoundtablePlanningPage.tsx主页components/:Step1/Step2/Step3 面板、MessageBubble、PersonaDrawer、HighFidelityReport、RecommendAgentsDialoghooks/:useStep1Intent、useStep2Orchestration、useAutoScroll、useDraftPersistenceapi/:intent / multi-agent / recommendlib/:clarification 规则、reasoning 工具、step-display- 自带
styles/roundtable-planning.css
9.6 src/strategy-components/
策略类页面(含 QA 中心 / Agent QA / Collaborative QA / MopeChat),其中部分仍保留 .jsx 旧文件。新增功能优先迁移到 .tsx。
10. 样式与主题
10.1 TailwindCSS v4
postcss.config.js引入@tailwindcss/postcss- 入口样式:src/styles/globals.css
- 配合
tw-animate-css提供常用动效 cn(...)工具(src/lib/utils.ts)=twMerge(clsx(...)),是合并 className 的「事实标准」
10.2 主题
- 顶层用 next-themes,
attribute="class",storageKey="strategy-theme",默认light,/landing强制 light - 自定义
ThemeProvider(contexts/ThemeContext.tsx)暴露theme给少量组件做白底 sidebar 等微调(见 workspace-sidebar.tsx:27) index.html早期注入脚本读取 storage,避免 FOUC
10.3 暗色样式
.dark类位于<html>,Tailwind v4 通过darkMode: class(约定)匹配
11. 国际化(i18n)
locale.ts提供detectLocale(),按 navigator 推断translations.ts/locales/index.ts汇总词条context.tsx暴露I18nProvider+useI18nContext()hooks.ts提供useI18n(),返回{ t, locale, setLocale }- 切换语言写 Cookie(
locale=xxx),下次访问加载对应词条
业务里固定写法:
const { t } = useI18n();
return <span>{t.common.loading}</span>;
12. 路由层的 Next.js 兼容
src/shims/next-navigation.ts 通过 react-router-dom 实现了:
useRouter():push/replace/back/forward/refresh/prefetch(其中 push/replace 会将旧 base/page/workspace替换成RouteBaseContext值)usePathname():useLocation().pathnameuseParams<T>():原样转发useSearchParams():只返回searchParams(注意:不是 tuple,与 Next.js 习惯一致)
src/shims/next-link.tsx 把 <Link href=...> 桥接到 <a>/<NavLink>。
新代码可继续使用
next/link、next/navigation,但请避免依赖 SSR/数据获取等 Next.js 高阶特性。
13. 渲染与 Markdown
src/components/workspace/messages/markdown-content.tsx 使用 react-markdown + 以下插件:
remark-gfm—— GFM 表格、删除线remark-math+rehype-katex—— 数学公式rehype-raw—— 渲染内嵌 HTML- core/rehype 中的
useRehypeSplitWordsIntoSpans():流式追加时按词包<span>,配合 CSS 渐入动画 - core/streamdown —— 流式 Markdown 解析(
streamdown包) - 代码块用
shiki(高亮)+react-syntax-highlighter(兜底)
引用渲染:components/workspace/citations/citation-link.tsx 和 artifact-link.tsx 将 [[uuid]] 风格引用转成可悬浮的链接卡片。
14. 上传与产物(Artifacts)
14.1 上传
uploadFiles(threadId, files):POST 到/api/threads/:threadId/uploads(FormData,files字段重复多份)- 大小/类型/数量校验:core/uploads/file-validation.ts
- PromptInput 附件解析:core/uploads/prompt-input-files.ts 提供
promptInputFilePartToFile(),把组件 UI Part 转为File - 失败容错:
useThreadStream中若有任意 file 转换失败,会抛出 toast 并清掉乐观消息
14.2 产物
- 服务端在
thread.values.artifacts: string[]里报路径 ArtifactsProvider(components/workspace/artifacts/context.tsx)做三层状态:所有产物、选中产物、面板开关ChatBox用react-resizable-panels拉出右侧 40% 面板,展示ArtifactFileList/ArtifactFileDetail- 选中 Markdown 产物时,「写作模式」会把
writing_artifact_path注入 context(ChatPage.tsx:157-159),后端 PromptPrefix/Writing 中间件会接管
15. 模型/模式/Token 用量
15.1 useModels
const { models, tokenUsageEnabled, isLoading } = useModels();
loadModels()调 Gateway/api/models(具体见core/models/api.ts)tokenUsageEnabled控制顶部<TokenUsageIndicator>是否显示
15.2 模式 (mode)
flash | thinking | pro | ultra 四档(见 §7.3 表),由 <InputBox> 中的 mode 切换器修改 LocalSettings.context.mode。ModeHoverGuide 提供悬浮说明。
15.3 Token 用量
- 顶部全局:components/workspace/token-usage-indicator.tsx 累计当前线程
- 单条消息:components/workspace/messages/message-token-usage.tsx +
use-message-metrics.ts,依赖tokenlens包
16. 通知、快捷键、命令面板
- 浏览器通知:core/notification/hooks.ts 提供
useNotification().showNotification(title, opts),在document.hidden || !hasFocus()时调用 - 全局快捷键:hooks/use-global-shortcuts.ts,统一注册到
window keydown,过滤掉 input/textarea/contentEditable,但⌘K例外(用于唤起命令面板) - 命令面板:components/workspace/command-palette.tsx(基于
cmdk)
17. 定时任务、分享
17.1 定时任务
- core/scheduled-tasks/ 提供 cron / one-shot 任务的 CRUD 与运行历史
- components/workspace/scheduled-tasks/schedule-builder.tsx 是创建/编辑 UI
- 详情页
ScheduledTaskRunDetailPage复用 MessageList 渲染历史一次运行 markdown-docx.ts用docx包导出 Word
17.2 只读分享
- 创建分享:components/workspace/thread-share-dialogs.tsx +
core/thread-shares/ - 只读页
PublicSharePage(路由/share/:shareCode)/PublicScheduledTaskPage(/public/scheduled-tasks/:taskId),不需要登录 - iframe 嵌入问答
EmbedChatPage(/embed/chats/:thread_id):第三方用普通 iframe 嵌套,父页面用sessionId轮询公开 API 判断问答是否结束,详见 embed-iframe-chat.md - 后端会用 share token 做权限校验,前端通过
apiFetch自动带
18. 调试与测试
18.1 类型检查
pnpm typecheck
工程没有内置 ESLint/Prettier 配置(依赖 IDE/CI),但有少量 // eslint-disable-next-line react-hooks/exhaustive-deps 注释提示 dependencies 故意省略。
18.2 Mock 模式
URL 加 ?mock=true 时,useThreadChat() 设 isMock=true,getAPIClient(true) 使用 mock 客户端,getLangGraphBaseURL(true) 回退到 ${origin}/mock/api,便于离线复现 UI(需要后端或本地 mock server 配合)。
18.3 演示模式
VITE_STATIC_WEBSITE_ONLY=true 时:
- 输入框 disabled,提示「演示模式不可用」
- 自动展开 Artifacts 面板并选中第一个产物
- 用于把构建产物嵌入营销页 / 静态主页
18.4 在浏览器调试 SSE
LangGraph SDK 走标准 SSE。开发期通过 Chrome DevTools Network -> EventSource 可以直接查看 frames。失败原因(如 422)可在 sonner toast 看到 getStreamErrorMessage() 的解析结果。
19. 常见开发任务指南
19.1 新增一个聊天页内的子组件
- 在
src/components/workspace/下建目录与index.ts - 通过
useThread()(来自messages/context.ts)拿到当前thread - 业务展示用
<MessageList>/<MarkdownContent>现有组件,避免自行 Markdown 解析 - 若依赖产物,使用
useArtifacts()
19.2 新增一个 LangGraph Assistant(自定义 Agent)
- 后端注册新的 graph
- 前端
core/agents/api.ts增 CRUD 接口;hooks.ts增useAgents/useAgent/useCreateAgent - 路由:复用
AgentChatPage,URL/page/workspace/agents/:agent_id/chats/:thread_id - 提交时只需把
assistantId传给useThreadStream(参考 AgentChatPage 中传agent_id给后端 graph 的方式)
19.3 新增一个领域 API
- 在
core/<domain>/建api.ts/hooks.ts/types.ts/index.ts api.ts函数全部使用apiFetch(${getBackendBaseURL()}/api/...)hooks.ts用useQuery({ queryKey: ["<domain>", ...] })/useMutation({ onSuccess: () => invalidateQueries })- 在使用方 import
useXxx即可
19.4 新增一个全局快捷键
useGlobalShortcuts([
{ key: "k", meta: true, action: openCommandPalette },
{ key: "/", meta: false, shift: true, action: showHelp },
]);
19.5 修改本地偏好
const [settings, setSettings] = useLocalSettings();
setSettings("display", { streamSpeedMeterEnabled: false });
线程级:
const [settings, setSettings] = useThreadSettings(threadId);
setSettings("context", { model_name: "gpt-5" }); // 仅当前线程
19.6 新增 i18n 词条
- 在 core/i18n/translations.ts 与
locales/下补充 useI18n().t.xxx.yyy使用- 不要硬编码中英文文案到组件里
20. 性能与稳定性约定
- TanStack Query:键统一以「领域 + 操作 + params」组合(如
["threads", "search", params]、["agents", id]);mutate 之后 invalidate;写时支持setQueriesData立即生效更新缓存(参考useDeleteThread/useRenameThread) - 流式断线续连:保持
useThreadStream的reconnectOnMount启用(已默认);切换线程时会 resetrunMetadataStorageRef以确保不串 - 乐观更新:发送 message 立即出现 human 气泡,避免空白等待;附件场景额外提供 uploadingFiles 占位
- 避免重复发送:
sendInFlightRef在useThreadStream里做单飞锁 - 不阻塞主线程:长文 markdown 拆词、渲染时分批;
use-stick-to-bottom自动暂停跟随 - 大依赖懒加载:建议对低频路由(如
Open Canvas、RoundtablePlanning)使用React.lazy+Suspense(当前已大致按模块切分,文件级可继续优化) - 构建内存:
pnpm build已--max-old-space-size=8192,新增大依赖请关注 dist 体积与构建时长
21. 部署
- 默认构建产物
dist/,因base: "./"可直接置于任何静态文件路径 - HashRouter 不需要后端 fallback,但建议把所有未匹配请求回退到
index.html,便于直接访问深链 - 后端地址通过环境变量在 build-time 注入(Vite 把
VITE_*内联到 bundle 中),不同环境请构建不同产物或使用窗口注入的方式 - 内网部署时常配合 Nginx 反代
/api -> 后端 Gateway、/api/langgraph -> LangGraph Server,让前端使用同源相对路径无需额外 CORS
22. 已知历史包袱与注意事项
- HashRouter + replaceState:新建线程时
history.replaceState直接改写 hash,不再触发 router 渲染。若以后改用 BrowserRouter,需要重写此处(参考 ChatPage.tsx:75-79 与 chats/use-thread-chat.ts 的兜底) "use client"指令:迁移自 Next.js,在 Vite 中无意义但保留可读性,新文件可省略- Next.js shim:尽量使用
useRouteNavigate()(core/route-base.tsx)代替原始的useRouter().push("/page/workspace/..."),因为后者依赖 base 替换,与你所在的路由模块强相关 - 混合 jsx/tsx:
strategy-components/**内仍有.jsx,新增请直接用.tsx并补全类型 - TDesign:仅在少量页面被使用,避免与 shadcn UI 风格混用;优先复用
components/ui/ - Open Canvas / Roundtable:各自有独立的 Stream/Thread Context 与 API;不要直接在它们里复用主聊天的
useThreadStream,反之亦然 - 生产模式默认是 light,但
index.html早期脚本默认 dark:如果改默认主题,要同时改 App.tsx:29 的defaultTheme和 index.html 的脚本
23. 速查表
| 我想… | 去哪里看 / 怎么写 |
|---|---|
| 改后端地址 | .env.development[.local] 中的 VITE_BACKEND_BASE_URL / VITE_LANGGRAPH_BASE_URL |
| 新增路由 | pages/WorkspaceRoutes.tsx(工作台)或 pages/PageRoutes.tsx(一级) |
| 发起 GET/POST | apiFetch(${getBackendBaseURL()}/api/..., { method, body }) |
| 发起 SSE 聊天 | useThreadStream({ threadId, context, ... }) |
| 跨标签同步偏好 | useLocalSettings() / useThreadSettings(threadId) |
| 弹 toast | import { toast } from "sonner"; toast.success("done") |
| 弹浏览器通知 | useNotification().showNotification(title, { body }) |
| 国际化 | const { t } = useI18n(); t.common.loading |
| 合并 className | cn("base", cond && "x-y") |
| 获取登录态 | getStoredAuth() / getAccessToken() |
| 命令面板 | ⌘K 唤起 <CommandPalette/> |
24. 后续建议
- 统一目录命名:
workspace/messages/、workspace/artifacts/、workspace/chats/现都用 kebab-case 文件夹 + kebab-case 文件名;建议在新增文件时延续此约定 - 抽离配置常量:与服务端契约相关的硬编码(如
assistantId = "lead_agent")建议集中到core/config/ - 测试:当前缺单测;优先为
core/threads/hooks.ts的normalizeStoredRunId等纯函数补 vitest - 包体积:landing 视觉特效(
ogl、gsap、canvas-confetti、embla-carousel)较重,可考虑路由级 code-split - lint/format:建议引入 ESLint + Prettier + lint-staged,统一团队风格
25. 页面清单(Page Catalog)
本节按一级路径分组,列出 src/pages/、src/open-canvas/pages/ 与 src/roundtable-planning/pages/ 下的每一个页面:做什么 + 挂载在哪里 + 关键依赖与组件。
25.1 顶层路由(无 /page 前缀)
LandingPage —— /landing
- 文件:LandingPage.tsx
- 作用:营销/介绍主页,深色背景;与登录后的工作台无关
- 强制
light主题被 App.tsx:23 关掉,单独使用bg-[#0a0a0a] - 包含组件:
- Header、Footer
- Hero —— 首屏 Slogan + CTA
- CaseStudySection —— 案例
- SkillsSection + ProgressiveSkillsAnimation
- SandboxSection + Terminal
- WhatsNewSection + PostList
- CommunitySection
- 视觉特效依赖:
aurora-text、flickering-grid、magic-bento、spotlight-card、shine-border、confetti-button、number-ticker、word-rotate、gsap、ogl、canvas-confetti、embla-carousel
LoginPage —— /login、/login/:username
- 文件:LoginPage.tsx
- 作用:以用户名直登(默认
guest),从 query 解析pageSidebarEnabled偏好,登录成功后写入localStorage["deerflow.auth"]并跳到/page/workspace/chats/new - 关键依赖:loginByUsername、setStoredAuth、persistSidebarEnabled
- UI:极简卡片(Tailwind),仅在
error时显示失败原因
PublicSharePage —— /share/:shareCode
- 文件:PublicSharePage.tsx
- 作用:通过分享码免登录查看一段会话快照(含产物面板)
- 数据:getPublicShareSnapshot
- 内部布局:自建左右双栏(不依赖 WorkspaceLayout)
- 左:MessageList
- 右:ArtifactFileDetail(点击文件后才显示)
- 自带
<ArtifactsProvider>+<SubtasksProvider>+<ThreadContext.Provider>,用快照模拟BaseStream<AgentThreadState>
PublicScheduledTaskPage —— /public/scheduled-tasks/:taskId
- 文件:PublicScheduledTaskPage.tsx
- 作用:免登录查看「已发布」的定时任务详情、最近运行结果、可下载文件
- 包含组件:
Card、Tabs、Dialog、Tooltip、Badge、MarkdownContent、downloadMarkdownAsDocx
NotFoundPage —— *
- 文件:NotFoundPage.tsx
- 作用:404 兜底,给一个 "Open workspace" 链接回到
/page/workspace/chats/new
25.2 /page/workspace/*(工作台主区)
所有页面外层套
<WorkspaceLayout>(侧栏 + 内容区)。聊天类页面再多一层<ChatRuntime>(SubtasksProvider/ArtifactsProvider/PromptInputProvider)。
ChatsPage —— /page/workspace/chats
- 文件:ChatsPage.tsx
- 作用:会话列表与搜索;可触发「导入聊天记录」对话框
- 数据:useThreads() —— 过滤掉
thread_type === "scheduler" - 包含组件:
- WorkspaceContainer/Header/Body
Input(搜索)、Button、ScrollArea、Link- ImportConversationsDialog
- 工具:
titleOfThread、usePathOfThread、formatTimeAgo、desensitize
ChatPage —— /page/workspace/chats/:thread_id
- 文件:ChatPage.tsx
- 作用:主聊天页(绑定
assistantId="lead_agent"),支持新会话与已有会话;首条消息时若选中了 Agent,会跳转到 AgentChatPage - Hook:useThreadStream、useThreadChat、useThreadSettings、useModels、useArtifacts、usePromptPrefixSettings
- 主要组件:
- ChatBox(左聊天 / 右产物 双栏)
- MessageList
- InputBox + AgentSelector
- ThreadTitle
- TodoList
- TokenUsageIndicator
- ExportTrigger、ArtifactTrigger
- Welcome(落地态欢迎)
AgentChatPage —— /page/workspace/agents/:agent_id/chats/:thread_id
- 文件:AgentChatPage.tsx
- 作用:与自定义 Agent 对话。
assistantId=agent_id,context 注入agent_id。会消费sessionStorage["pendingAgentMessage"]来自动发送 ChatPage 转交的首条消息 - Hook:与 ChatPage 类似,但额外
useAgent(agent_id) - 关键组件:与 ChatPage 同一套;顶栏额外渲染 Agent 名称 Badge;落地态使用 AgentWelcome
AgentsPage —— /page/workspace/agents
- 文件:AgentsPage.tsx(薄壳,直接渲染 AgentGallery)
- 作用:Agent 画廊;含分类、标签、特色、搜索、最近使用
- 子组件:AgentCard、AgentManageDialog、TagSearchBar、TagPickerDialog、TagChipList
NewAgentPage —— /page/workspace/agents/new
- 文件:NewAgentPage.tsx
- 作用:以「对话」的方式引导用户创建 Agent —— 复用 lead_agent 的
is_bootstrap=true模式 +SetupAgentApprovalMiddleware中拦截的setup_agent工具调用,让 LLM 给出 setup_agent 调用并由用户点「批准」最终落库 - Hook:useThreadStream + useSkills + useModels + checkAgentName/createAgent/updateAgent
- 组件:
- 左:表单(名称/描述/灵魂 prompt/模型/技能多选)
- 右:
<PromptInput>+<MessageList>(带 SetupAgentApprovalPayload 审批 UI) - SkillDetailDialog
- 包了
<ArtifactsProvider>+<ThreadContext.Provider>与 ChatPage 同源
ScheduledTasksPage —— /page/workspace/scheduled/tasks
- 文件:ScheduledTasksPage.tsx
- 作用:当前用户的定时任务列表 + Cron 配置 + 已发布任务/可订阅、运行历史快览
- Hook:useScheduledTasks、
useScheduledTaskRuns、useScheduledTaskActions、usePublishedScheduledTasks、useScheduledTaskDeliveryProfile、useAgents - 关键组件:
- WorkspaceContainer/Header/Body
- ScheduleBuilder +
buildCron/parseCron - TagChipList + TagPickerDialog + TagSearchBar
Card / Dialog / Tabs / Select / Switch / Textarea / Badge / Button
ScheduledTaskRunDetailPage —— /page/workspace/scheduled/runs/:run_id
- 文件:ScheduledTaskRunDetailPage.tsx
- 作用:单次定时任务运行的详情;展示消息时间线、产物文件、状态、可重命名/删除/导出 docx
- Hook:useScheduledTaskRun、
useScheduledTaskRunMessages、useScheduledTaskActions、getScheduledTaskRunFileContent、updateScheduledTaskRunFile、downloadScheduledTaskRunFile - 组件:
Tabs、Card、MarkdownContent、Textarea(编辑结果文件)
ScheduledChatPage(未在路由中显式注册,但留作入口跳转)
- 文件:ScheduledChatPage.tsx
- 作用:从
useSchedulerThread()拿到「调度器助手」线程 id 后立即Navigate到/page/workspace/chats/<id>,相当于一个智能跳板 - 组件:WorkspaceContainer/Body + Loading 提示文案
SkillsPage —— /page/workspace/skills
- 文件:SkillsPage.tsx(薄壳)→ SkillSettingsPage
- 作用:技能开关 + MCP Server 编辑入口
- 子组件:McpServerEditDialog、SkillStateSheet
SkillEvolutionPage —— /page/workspace/skills/evolution
- 文件:SkillEvolutionPage.tsx(薄壳)→ SkillEvolutionPanel
- 作用:技能演化(实验/进化曲线)展示
MemoryPage —— /page/workspace/memory
- 文件:MemoryPage.tsx(薄壳)→ MemorySettingsPage
- 作用:当前用户的记忆桶(事实/偏好/项目笔记)管理
- 依赖 core/memory/api.ts +
hooks.ts
AIWritingPage —— /page/workspace/ai-writing
- 文件:open-canvas/pages/AIWritingPage.tsx(带
hideAdvancedFields) - 作用:AI 写作主界面(左草稿/中聊天/右时间线),WorkspaceLayout 内嵌入
- 见 §25.4 详解
RoundtablePlanningPage —— /page/workspace/roundtable/planning
- 文件:roundtable-planning/pages/RoundtablePlanningPage.tsx
- 作用:圆桌规划三步流程(任务理解 → 编排 → 高保真报告)
- 见 §25.5 详解
25.3 管理员页(/page/workspace/admin/* 与 settings/*)
路由元素层做了
getStoredAuth()?.system_role === "admin"判断,非管理员被重定向到chats/new。
AdminUserHubPage —— /page/workspace/admin
- 文件:AdminUserHubPage.tsx(薄壳)→ AdminUserHub
- 作用:统一的「用户中心」聚合页(用户列表、活动、记忆查看),替代旧的多分支管理路由
- 旧路径
admin/users/admin/user-activity/admin/memory均Navigate到此页
AdminScheduledTasksPage —— /page/workspace/admin/scheduled-tasks
- 文件:AdminScheduledTasksPage.tsx
- 作用:全平台定时任务总览、暂停/恢复/编辑/删除/导出
- Hook:useAllScheduledTasks、
useAdminScheduledTaskActions、useAdminScheduledTaskRuns - 组件:WorkspaceContainer/Header/Body、ScheduleBuilder、
Card、Dialog、Badge、Input、Textarea
AdminUserActivityPage(路由已并入 AdminUserHub,但文件保留)
- 文件:AdminUserActivityPage.tsx
- 作用:按用户钻取其线程与消息(独立窗口模式)
- Hook:useAllUsers、useUserMemory / useUserThreadMessages / useUserThreads
- 组件:
Tabs、Input、ScrollArea、Badge、左右两栏布局
AdminMemoryPage(已并入 Hub)
UsersPage(已并入 Hub)
- 文件:UsersPage.tsx(薄壳)→ UserManagementPage
- 作用:账号管理(系统角色变更、密码重置等)
TagsPage —— /page/workspace/admin/tags
- 文件:TagsPage.tsx(薄壳)→ TagManagementPage
- 作用:全局标签 CRUD(颜色、可见性、排序)
LlmMetricsPage —— /page/workspace/settings/llm-metrics
- 文件:LlmMetricsPage.tsx
- 作用:LLM 调用计量与工具计量
- Hook:useLlmMetrics、
downloadLlmMetricsExcel - 组件:
Tabs、Input、Select、Badge、ToolMetricsPanel
RecommendedQuestionsPage —— /page/workspace/settings/recommended-questions
- 文件:RecommendedQuestionsPage.tsx
- 作用:管理欢迎页/新会话落地的「推荐问题」(增删改、拖拽排序)
- Hook:useRecommendedQuestions / useCreateRecommendedQuestion / useUpdateRecommendedQuestion / useDeleteRecommendedQuestion / useReorderRecommendedQuestions
- 组件:
Dialog、Input、Textarea、Button+ 自定义拖拽(HTML5 DnD)
PromptPrefixSettingsPage —— /page/workspace/settings/prompt-prefix
- 文件:PromptPrefixSettingsPage.tsx
- 作用:全局「前缀模式」开关、开关名称、prompt 内容;用户在新会话首轮可勾选附加该 prefix
- Hook:usePromptPrefixSettings / useUpdatePromptPrefixSettings
- 组件:
Switch、Input、Textarea、Button
25.4 /page/canvas/*(开放画布 / AI 写作)
由 OpenCanvasRoutes.tsx 注册,不在 WorkspaceLayout 内(直接挂在 PageRoutes 内容区)。
CanvasHomePage —— /page/canvas
- 文件:open-canvas/pages/CanvasHomePage.tsx
- 作用:画布首页,选择「新建文本文档」或「新建代码」→ 调 createCanvasThread → 跳转到
:thread_id - 组件:
Button+ Lucide 图标(FileText/Code2/Sparkles/PenLine)
CanvasThreadPage —— /page/canvas/:thread_id
- 文件:open-canvas/pages/CanvasThreadPage.tsx
- 作用:画布工作区,左右分栏:聊天 + 富文本/代码编辑
- Provider 栈:CanvasThreadProvider → CanvasGraphProvider → CanvasUIProvider
- 子组件:CanvasShell
- Hook:useCanvasStream、
useCanvasArtifact、useCanvasThread、useCanvasQuickActions、useCanvasSelection、useWritingEditor、useDocumentOutline、useWritingRewrite - 与主聊天的隔离:使用 Canvas 自有的 LangGraph base URL(可选)、独立 thread/run API、独立编辑器扩展(AiHighlightExtension)
AIWritingPage —— /page/canvas/ai-writing 与 /page/workspace/ai-writing
- 文件:open-canvas/pages/AIWritingPage.tsx
- 作用:受控的「AI 写作流程」 —— 启动写作 → SSE 进度 → 用户干预(intervention)→ 继续 → 评审 → 导出 docx
- 子组件:
- AIWritingPageHeader
- AIWritingForm
- AIWritingDraftPanel + AIWritingPanel
- AIWritingTimeline + TimelineStep
- AIWritingHistoryDropdown + AIWritingHistoryView
- IntentStreamingView / IntentResultCard
- OutlineStreamingView / OutlineCard / OutlineSection
- DraftStreamingView
- ReviewStreamingView / ReviewCard / ScoreBar / IssueList
- InterventionCard / CompletedInterventionCard / RevisionSummaryCard
- MaterialsToolbar / MaterialsCard / MaterialItem
- DraftOutlineSidebar / WritingSkeletonCard / CollapseTransition
- Hook:useAIWritingSessions、useAIWritingStream、useArticleTypes、useWritingRewrite、useDocumentOutline
- API:ai-writing-sessions.ts、rewrite.ts、writing-export.ts、ai-writing-article-types.ts
- 文件头有大段 Docker 内网部署排错指南(transcript 500 / resume 400 / 会话丢失),新成员部署前请先读
ArticleTypesConfigPage —— /page/canvas/article-types
- 文件:open-canvas/pages/ArticleTypesConfigPage.tsx
- 作用:AI 写作「文章类型」配置(标题/描述/默认 prompt 等)
- Hook:useArticleTypes
- 组件:
Button、Separator、Dialog+ 自定义 input/textarea 样式常量
25.5 /page/roundtable/*(圆桌规划)
RoundtablePlanningPage —— /page/roundtable/planning
- 文件:roundtable-planning/pages/RoundtablePlanningPage.tsx
- 作用:三步式多 Agent 圆桌规划(任务理解 → 编排 → 高保真报告)
- Hook:
- useStep1Intent —— 任务理解、INTENT_READY、澄清卡
- useStep2Orchestration —— 编排循环、对话流、Persona 覆盖、澄清回复
- useDraftPersistence —— 本地草稿(自动保存/手动/加载/重命名/删除)
- useAutoScroll
- 组件:
- Step1Panel / Step2Panel / Step3Panel
- PersonaDrawer
- Avatars(含 BUILTIN_AGENT_META)
- MessageBubble / MessageStepsCard
- IntentClarificationCard
- RecommendAgentsDialog
- HighFidelityReport —— Step3 报告呈现
- ArtifactPreviewModal
- ScrollToBottomButton
- API:intent.ts / multi-agent.ts / recommend.ts
- 工具:clarification.ts、step-display.tsx、reasoning.ts、roundtable-constants.ts、drafts.ts
- 注意(来自文件头注释):
- special 顺序派活,不要改回
Promise.all step1InitGenRef / step2InitRef闸门用于规避 StrictMode 双 mount 副作用- 离开 Step 2 不销毁 threadIds,允许返回继续看
- 草稿自动保存仅在 1→2、2→3 前进时触发
- Persona 抽屉改的 model 不立即重启编排,下一轮才生效
- 默认
availableModels[0].name,Persona 覆盖更高优先级
- special 顺序派活,不要改回
25.6 /page/strategy/*(策略路由)
StrategyRoutes.tsx 复用了 WorkspaceRoutes 内大多数页面,不带 WorkspaceLayout/PageSidebar,由 SidebarProvider 单独维持。
<RouteBaseProvider value="/page/strategy">让useRouter跳转保持在策略前缀下。
qa/general→ GeneralQA(综合问答中心)light-app/new-note→ LightAppNewNotePage.tsx- 作用:轻应用「新建笔记」入口;调用独立的笔记服务
VITE_NOTE_API_BASE_URL,使用 useNoteAuth 完成笔记服务自有的鉴权 - API:createSearchSpace
- 作用:轻应用「新建笔记」入口;调用独立的笔记服务
light-app/notes→ LightAppNoteListPage.tsx- 作用:笔记列表,支持网格/列表视图切换
- API:listSearchSpaces
light-app/note-qa→ LightAppNoteQAPage.tsx- 作用:基于笔记的提问入口(UI 雏形,详细问答尚在开发)
- 其余路径(chats / agents / skills / memory / scheduled / settings / admin)直接复用 WorkspaceRoutes 中的页面文件,仅 base 不同
25.7 路由与页面对照速查
| 一级路径 | 进入路由文件 | 主要页面文件 |
|---|---|---|
/login、/login/:username |
App.tsx | LoginPage.tsx |
/landing |
App.tsx | LandingPage.tsx |
/share/:shareCode |
App.tsx | PublicSharePage.tsx |
/public/scheduled-tasks/:taskId |
App.tsx | PublicScheduledTaskPage.tsx |
/embed/chats/:thread_id |
App.tsx | EmbedChatPage.tsx(embed-iframe-chat.md) |
/page/workspace/* |
PageRoutes.tsx → WorkspaceRoutes.tsx | ChatsPage / ChatPage / AgentsPage / NewAgentPage / AgentChatPage / SkillsPage / SkillEvolutionPage / MemoryPage / ScheduledTasksPage / ScheduledTaskRunDetailPage / AdminUserHubPage / AdminScheduledTasksPage / TagsPage / RecommendedQuestionsPage / PromptPrefixSettingsPage / LlmMetricsPage |
/page/strategy/* |
PageRoutes.tsx → StrategyRoutes.tsx | GeneralQA + LightApp* + 复用 Workspace 页面 |
/page/canvas/* |
PageRoutes.tsx → open-canvas/routes/OpenCanvasRoutes.tsx | CanvasHomePage / CanvasThreadPage / AIWritingPage / ArticleTypesConfigPage |
/page/roundtable/* |
PageRoutes.tsx → RoundtableRoutes.tsx | RoundtablePlanningPage |
* |
App.tsx | NotFoundPage |
25.8 "薄壳页面"与"重逻辑页面"
| 页面 | 类型 | 真正实现位置 |
|---|---|---|
| AgentsPage | 薄壳 | components/workspace/agents/agent-gallery.tsx |
| AdminUserHubPage | 薄壳 | components/workspace/admin/admin-user-hub.tsx |
| AdminMemoryPage | 薄壳 | components/workspace/admin/admin-memory-page.tsx |
| UsersPage | 薄壳 | components/workspace/admin/user-management-page.tsx |
| TagsPage | 薄壳 | components/workspace/admin/tag-management-page.tsx |
| SkillsPage | 薄壳 | components/workspace/settings/skill-settings-page.tsx |
| SkillEvolutionPage | 薄壳 | components/workspace/skills/skill-evolution-panel.tsx |
| MemoryPage | 薄壳 | components/workspace/settings/memory-settings-page.tsx |
| 其余 | 重逻辑 | 直接在 pages/ 内实现 |
维护建议:薄壳页面只负责 layout/insets,业务逻辑请始终下沉到
components/workspace/<domain>/下方便复用与测试。
本文档与代码同步演进。若发现内容与实现不符,请优先以代码为准并更新本文。