# DeerFlow 前端开发文档 ## 目录 1. [整体目录结构](#1-整体目录结构) 2. [路由结构](#2-路由结构) 3. [核心数据流与状态管理](#3-核心数据流与状态管理) 4. [主要页面组件](#4-主要页面组件) 5. [核心业务组件](#5-核心业务组件) 6. [核心 Hooks 与工具函数](#6-核心-hooks-与工具函数) 7. [API 调用层](#7-api-调用层) 8. [国际化机制](#8-国际化机制) 9. [主题与样式体系](#9-主题与样式体系) 10. [关键业务流程](#10-关键业务流程) 11. [项目配置与依赖](#11-项目配置与依赖) 12. [常见开发模式](#12-常见开发模式) --- ## 1. 整体目录结构 ``` frontend-web/src/ ├── App.tsx # 应用入口,顶层路由 + Provider ├── env.ts # 环境变量类型定义 ├── pages/ # 路由级页面组件 │ ├── PageRoutes.tsx # /page/* 根路由容器(含外部系统预留位) │ ├── WorkspaceRoutes.tsx # /page/workspace/* 工作区路由 │ ├── LoginPage.tsx # 登录页 │ ├── LandingPage.tsx # 营销落地页(强制 dark 主题) │ ├── ChatsPage.tsx # 对话列表 │ ├── ChatPage.tsx # 单条对话(核心页面) │ ├── AgentsPage.tsx # 智能体库 │ ├── AgentChatPage.tsx # 与智能体对话 │ ├── NewAgentPage.tsx # 创建智能体(两步流程) │ ├── ScheduledTasksPage.tsx # 定时任务管理 │ ├── ScheduledTaskRunDetailPage.tsx # 定时任务执行详情 │ ├── ScheduledChatPage.tsx # 定时任务对话跳转页 │ └── NotFoundPage.tsx # 404 页面 ├── components/ # 业务组件库 │ ├── ui/ # shadcn/ui 基础组件(Button、Card、Dialog 等 40+) │ ├── ai-elements/ # AI 交互可视化组件 │ │ ├── prompt-input/ # 输入框 Context + 子组件系统 │ │ ├── artifact.tsx # 制品展示 │ │ ├── canvas.tsx │ │ ├── conversation.tsx # 虚拟滚动对话容器 │ │ ├── message.tsx │ │ ├── model-selector.tsx │ │ ├── code-block.tsx │ │ └── shimmer.tsx │ ├── workspace/ # 工作区业务组件(详见第 5 节) │ └── landing/ # 落地页组件 ├── core/ # 核心业务逻辑层(非 UI) │ ├── api/ # API 客户端(LangGraph SDK + Fetch) │ ├── auth/ # 认证管理 │ ├── config/ # 环境配置 & Base URL │ ├── i18n/ # 国际化(上下文、翻译文件、语言检测) │ ├── threads/ # 对话线程管理(最核心) │ ├── agents/ # 智能体 CRUD │ ├── models/ # 模型列表 │ ├── settings/ # 本地设置(LocalStorage 发布订阅) │ ├── uploads/ # 文件上传 │ ├── artifacts/ # 制品管理 │ ├── memory/ # 用户记忆 │ ├── skills/ # 技能管理 │ ├── scheduled-tasks/ # 定时任务 │ ├── messages/ # 消息处理工具函数 │ ├── tasks/ # 子任务 Context │ ├── notification/ # 系统通知 │ └── utils/ # 通用工具(文件下载、日期格式化等) └── hooks/ # 全局自定义 hooks ``` --- ## 2. 路由结构 > 项目使用 **HashRouter**,所有路由访问形如 `http://host/#/xxx` ### 2.1 App.tsx 顶层路由 | 路径 | 组件 | 说明 | |------|------|------| | `/` | Navigate | 重定向到 `/login` | | `/login` | LoginPage | 登录(自动登录,无表单) | | `/login/:username` | LoginPage | 带用户名登录 | | `/landing` | LandingPage | 营销落地页(固定 dark 主题) | | `/page/*` | PageRoutes | 应用内容根容器 | | `*` | NotFoundPage | 404 | ### 2.2 PageRoutes.tsx(`/page/*`) ``` /page/ ├── index → Navigate to "workspace" ├── workspace/* → WorkspaceRoutes └── [TODO 预留] → 外部系统路由挂载位置 ``` ### 2.3 WorkspaceRoutes.tsx(`/page/workspace/*`) | 路径 | 页面组件 | 布局包裹 | |------|---------|--------| | `index` | Navigate → `chats/new` | - | | `chats` | ChatsPage | WorkspaceLayout | | `chats/:thread_id` | ChatPage | WorkspaceLayout + ChatRuntime | | `agents` | AgentsPage | WorkspaceLayout | | `agents/new` | NewAgentPage | WorkspaceLayout | | `agents/:agent_id/chats` | Navigate → `/page/workspace/agents` | - | | `agents/:agent_id/chats/:thread_id` | AgentChatPage | WorkspaceLayout + ChatRuntime | | `scheduled` | Navigate → `.../scheduled/tasks` | - | | `scheduled/tasks` | ScheduledTasksPage | WorkspaceLayout | | `scheduled/runs/:run_id` | ScheduledTaskRunDetailPage | WorkspaceLayout | ### 2.4 布局 Provider 层级结构 ``` WorkspaceLayout ├── SidebarProvider(侧边栏展开/收起状态) │ ├── WorkspaceSidebar │ │ ├── WorkspaceHeader(Logo、新建按钮) │ │ ├── WorkspaceNavChatList(Chats / Agents / Scheduled 导航) │ │ ├── RecentChatList(最近会话列表) │ │ └── WorkspaceNavMenu(Settings / About / GitHub) │ ├── SidebarInset(主内容区) │ │ └── [Page Content] │ ├── CommandPalette(快捷命令面板) │ └── Toaster(全局通知) ChatRuntime(仅 ChatPage / AgentChatPage 使用) ├── SubtasksProvider(子任务执行状态) ├── ArtifactsProvider(制品选中/打开状态) └── PromptInputProvider(输入框内容/附件状态) ``` --- ## 3. 核心数据流与状态管理 ### 3.1 全局 Provider 架构 ``` App └── HashRouter └── AppProviders(useLocation 驱动主题判断) ├── NextThemesProvider │ └── attribute="class",/landing 强制 forcedTheme="dark" ├── I18nProvider │ └── initialLocale = detectLocale()(从 cookie 或浏览器语言) └── QueryClientProvider(TanStack React Query 全局缓存) ``` ### 3.2 全局 Context 列表 | Context | 文件 | 职责 | 消费 Hook | |---------|------|------|-----------| | `I18nContext` | `core/i18n/context.tsx` | 当前语言 + 翻译对象 | `useI18n()` | | `SubtaskContext` | `core/tasks/context.tsx` | 实时子任务执行状态 | `useSubtaskContext()` | | `ArtifactsContext` | `components/workspace/artifacts/context.tsx` | 制品选中/面板开关 | `useArtifacts()` | | `PromptInputContext` | `components/ai-elements/prompt-input/` | 输入框内容、附件列表 | `usePromptInputController()` | | `ThreadContext` | `components/workspace/messages/context.tsx` | 当前 thread 对象下传 | 直接读 context.thread | ### 3.3 LocalStorage 数据 **操作层:** `core/settings/store.ts`(发布-订阅,`useSyncExternalStore` 驱动响应式) | Key | 内容 | |-----|------| | `deerflow.auth` | `{ access_token, token_type, user_id, email }` | | `deerflow.local-settings` | `{ notification, context: { model_name, mode, reasoning_effort } }` | | `deerflow.thread-model.{threadId}` | 每条线程的模型覆盖 | ### 3.4 SessionStorage 数据 | Key | 内容 | |-----|------| | `deerflow.run-id.{threadId}` | WebSocket run_id,用于断线重连 | ### 3.5 React Query 缓存 Key | Query Key | 数据 | |-----------|------| | `["threads"]` | 线程列表 | | `["thread", threadId]` | 单条线程 | | `["agents"]` | 智能体列表 | | `["models"]` | 模型列表 | | `["skills"]` | 技能列表 | | `["memory"]` | 用户记忆 | | `["scheduled-tasks"]` | 定时任务列表 | | `["scheduled-runs", taskId]` | 任务执行记录 | --- ## 4. 主要页面组件 ### 4.1 ChatPage.tsx — 单条对话页面 **职责:** 管理单条聊天线程的完整交互,包括消息流、输入、工具调用展示、制品等。 **关键状态:** ```typescript const [threadId, setThreadId] = useState(routeThreadId || uuid()); const [isNewThread, setIsNewThread] = useState(!routeThreadId || routeThreadId === "new"); const [hasStartedConversation, setHasStartedConversation] = useState(false); const [showFollowups, setShowFollowups] = useState(false); const [settings, setSettings] = useThreadSettings(threadId); ``` **核心 Hook:** ```typescript const [thread, sendMessage, isUploading] = useThreadStream({ threadId: isNewThread ? undefined : threadId, context: settings.context, onStart: (createdThreadId) => { setThreadId(createdThreadId); setIsNewThread(false); // 不刷新页面,直接更新 URL hash history.replaceState(null, "", `...#/page/workspace/chats/${createdThreadId}`); }, onFinish: (state) => { // 后台通知 showNotification(state.title, { body: lastMessageText }); } }); ``` **主要子组件:** | 组件 | 职责 | |------|------| | `` | 消息渲染(含工具调用、子任务、制品) | | `` | 新对话欢迎屏 | | `` | 输入框(模式选择、附件、发送) | | `` | 线程标题(自动生成后同步更新) | | `` | 导出对话按钮 | | `` | 制品面板开关 | | `` | Token 用量展示(可选) | | `` | 任务清单 | | `` | 追问建议 | **数据流:** ``` 用户输入 (InputBox) ↓ handleSubmit(message: PromptInputMessage) ↓ sendMessage(threadId, message) ↓ [内部] 上传文件 → 提交到 LangGraph API ↓ WebSocket 流返回消息 ↓ thread.messages 更新 → MessageList 重渲染 ``` --- ### 4.2 AgentChatPage.tsx — 与智能体对话 与 ChatPage 结构基本一致,差异点: - 从路由获取 `agent_id`,传入 `useThreadStream({ assistantId: agent_id })` - Header 展示智能体名称 Badge - "新建对话"跳转到 `/page/workspace/agents/${agent_id}/chats/new` - URL 更新为 `#/page/workspace/agents/${agent_id}/chats/${threadId}` --- ### 4.3 NewAgentPage.tsx — 创建智能体(两步流程) **Step 1:name(命名 + 选技能)** ```typescript // 自动预选前 3 个启用的技能 useEffect(() => { if (!isSkillSelectionTouched) { setSelectedSkills(enabledSkills.slice(0, 3).map(s => s.name)); } }, [enabledSkills]); // 提交:验证名称 → 创建智能体 await checkAgentName(name); // 检查重名 await createAgent(request); // 创建,返回 agent.id setStep("chat"); // 进入 Step 2 ``` **Step 2:chat(初始化对话)** ```typescript // 发送 bootstrap 消息触发智能体初始化 sendMessage(threadId, bootstrapMessage, { agent_id: agentId }); // 监听 setup_agent 工具完成事件 onToolEnd: (event) => { if (event.name === "setup_agent") { // 带重试轮询加载 agent 最新信息 retryLoadAgent(agentId); } } ``` --- ### 4.4 ChatsPage.tsx — 对话列表 ```typescript const { data: threads } = useThreads({ limit: 50, sortBy: "updated_at" }); // 客户端过滤:排除定时任务线程 + 搜索关键词 const filteredThreads = useMemo(() => threads?.filter(t => t.metadata?.thread_type !== "scheduler" && titleOfThread(t).toLowerCase().includes(search) ), [threads, search] ); ``` --- ### 4.5 LoginPage.tsx — 登录 ```typescript // 自动登录,无需用户操作 useEffect(() => { loginByUsername(username || "guest") .then(auth => { setStoredAuth(auth); router.replace("/page/workspace/chats/new"); }) .catch(err => setStatus("error")); }, [username]); ``` --- ### 4.6 ScheduledTasksPage.tsx — 定时任务 **功能:** 创建/编辑/发布/暂停/恢复定时任务,查看执行历史。 **Cron 表达式构建:** ```typescript type ScheduleFreq = "hourly" | "daily" | "weekly" | "monthly"; function buildCron(config): string { // hourly → "15 * * * *" // daily → "0 9 * * *" // weekly → "0 9 * * 1" // monthly → "0 9 1 * *" } ``` **核心操作:** ```typescript const actions = useScheduledTaskActions(); actions.create.mutateAsync(payload); actions.publish.mutateAsync(taskId); actions.pause.mutateAsync(taskId); actions.trigger.mutateAsync(taskId); // 立即执行一次 actions.subscribe.mutateAsync(params); // 订阅推送 ``` --- ## 5. 核心业务组件 ### 5.1 消息系统(`components/workspace/messages/`) | 文件 | 职责 | |------|------| | `message-list.tsx` | 消息列表容器,调用 `groupMessages` 分组后渲染 | | `message-list-item.tsx` | 单条消息渲染(含 role 判断) | | `message-group.tsx` | 消息组(human / assistant / processing 等) | | `markdown-content.tsx` | Markdown 渲染(rehype 系列插件) | | `subtask-card.tsx` | 子任务状态卡片 | | `message-token-usage.tsx` | 单条消息 Token 用量 | | `skeleton.tsx` | 流式加载骨架屏 | | `context.tsx` | ThreadContext,向下传递 thread 对象 | **消息分组类型(`groupMessages`):** ```typescript type MessageGroup = | "human" // 用户输入 | "assistant:processing" // 推理/工具调用中 | "assistant" // 最终响应 | "assistant:clarification" // 澄清请求 | "assistant:present-files" // 文件呈现 | "assistant:subagent"; // 子智能体消息 ``` --- ### 5.2 输入框(`components/workspace/input-box.tsx`) **子组件层级:** ``` InputBox └── PromptInput(Context Provider) ├── PromptInputBody │ ├── PromptInputTextarea(文本输入) │ ├── PromptInputAttachments(附件列表) │ └── PromptInputTools(工具栏:附件上传、模式选择) ├── PromptInputFooter │ ├── PromptInputSubmit(发送/停止按钮) │ └── Suggestions(追问建议列表) └── ModelSelector(模型下拉选择) ``` **四种会话模式:** | Mode | 说明 | |------|------| | `flash` | 快速模式,无推理步骤 | | `thinking` | 推理模式,开启深度思考 | | `pro` | 专业模式,多模态 + 规划 | | `ultra` | 超级模式,启用子智能体 | **推理努力等级(mode ≠ flash 时可选):** `minimal` / `low` / `medium` / `high` --- ### 5.3 侧边栏(`components/workspace/workspace-sidebar.tsx`) ``` WorkspaceSidebar ├── SidebarHeader → WorkspaceHeader │ ├── Logo(可点击,跳转 /page/workspace/chats/new) │ └── 新建对话按钮 ├── SidebarContent │ ├── WorkspaceNavChatList │ │ ├── 新建对话(/page/workspace/chats/new) │ │ ├── 所有对话(/page/workspace/chats) │ │ ├── 智能体(/page/workspace/agents) │ │ └── 任务管理(/page/workspace/scheduled/tasks) │ └── RecentChatList(最近会话,含搜索) └── SidebarFooter → WorkspaceNavMenu ├── Settings(打开设置 Dialog) ├── About └── GitHub ``` --- ### 5.4 制品管理(`components/workspace/artifacts/`) ```typescript interface ArtifactsContextType { artifacts: string[]; // artifact ID 列表 selectedArtifact: string | null; autoSelect: boolean; // 是否自动选中最新制品 open: boolean; // 面板是否展开 autoOpen: boolean; select(artifact: string, autoSelect?: boolean): void; deselect(): void; setOpen(open: boolean): void; } ``` **文件:** | 文件 | 职责 | |------|------| | `context.tsx` | ArtifactsContext + Provider | | `artifact-trigger.tsx` | 面板展开/收起按钮 | | `artifact-file-list.tsx` | 制品文件列表 | | `artifact-file-detail.tsx` | 单个制品详情(含代码编辑器) | --- ### 5.5 设置面板(`components/workspace/settings/`) | 页面文件 | 内容 | |---------|------| | `appearance-settings-page.tsx` | 主题切换(light/dark/system)、语言切换 | | `memory-settings-page.tsx` | 用户记忆(事实列表、总结管理) | | `notification-settings-page.tsx` | 桌面通知开关 | | `skill-settings-page.tsx` | 技能启用/禁用 | | `tool-settings-page.tsx` | 工具配置 | | `about-settings-page.tsx` | 版本号、仓库链接 | --- ### 5.6 命令面板(`components/workspace/command-palette.tsx`) 通过 `Cmd/Ctrl + K` 触发,提供快速导航和操作入口。 ```typescript const handleNewChat = useCallback(() => { router.push("/page/workspace/chats/new"); setOpen(false); }, [router]); ``` --- ## 6. 核心 Hooks 与工具函数 ### 6.1 useThreadStream(最核心 Hook) **位置:** `core/threads/hooks.ts` 管理 LangGraph WebSocket 连接、消息流、乐观更新。 **签名:** ```typescript const [thread, sendMessage, isUploading] = useThreadStream({ threadId?: string, // 有值则连接现有线程,无值表示新建 context: LocalSettings["context"], // 模型、模式等 isMock?: boolean, assistantId?: string, // 默认 "lead_agent" onStart?: (threadId: string) => void, onFinish?: (state: AgentThreadState) => void, onToolEnd?: (event: ToolEndEvent) => void, }); ``` **thread 对象关键属性:** ```typescript thread: { messages: Message[], // 含乐观消息的完整列表 isLoading: boolean, // 流是否进行中 isThreadLoading: boolean, // 初始线程数据是否加载中 values: AgentThreadState, // 线程最终状态 error?: Error, stop: async () => void, // 终止当前流 } ``` **sendMessage 签名:** ```typescript sendMessage( threadId: string | undefined, message: PromptInputMessage, extraContext?: Partial, options?: { isMock?: boolean } ): Promise ``` **内部机制:** - **乐观更新:** 用户发送后立即本地展示消息,不等服务端响应 - **文件上传:** 自动调用 `uploadFiles()` 后再提交 - **run_id 持久化:** sessionStorage 保存,支持页面刷新后断线重连 - **消息同步:** 流结束后通过 `queryClient.invalidateQueries(["threads"])` 刷新列表 --- ### 6.2 useThreadSettings **位置:** `core/settings/hooks.ts` ```typescript const [settings, setSettings] = useThreadSettings(threadId); // settings 结构 { notification: { enabled: boolean }, context: { model_name?: string, mode: "flash" | "thinking" | "pro" | "ultra", reasoning_effort?: "minimal" | "low" | "medium" | "high" } } // 修改(合并更新) setSettings("context", { mode: "pro" }); setSettings("notification", { enabled: true }); ``` **工作原理:** `useSyncExternalStore` 监听 localStorage 变化,跨标签页同步。 --- ### 6.3 useI18n **位置:** `core/i18n/hooks.ts` ```typescript const { t, locale, changeLocale } = useI18n(); t.sidebar.newChat // "New Chat" t.agents.createPageTitle // "Create Agent" t.toolCalls.moreSteps(3) // "Show 3 more steps" changeLocale("zh-CN"); // 切换语言,自动存 cookie ``` --- ### 6.4 useScheduledTaskActions **位置:** `core/scheduled-tasks/hooks.ts` ```typescript const actions = useScheduledTaskActions(); // 所有返回值均为 TanStack Query Mutation actions.create // 创建定时任务 actions.update // 更新任务配置 actions.remove // 删除任务 actions.removeRun // 删除执行记录 actions.pause // 暂停 actions.resume // 恢复 actions.trigger // 立即触发一次 actions.publish // 发布(激活) actions.unpublish // 取消发布 actions.subscribe // 订阅推送 actions.updateSubscription actions.unsubscribe ``` --- ### 6.5 工具函数 **消息处理(`core/messages/utils.ts`):** ```typescript groupMessages(messages, mapper) // 按类型分组消息列表 extractTextFromMessage(message) // 提取纯文本 extractContentFromMessage(message) // 提取 Markdown/图像内容 extractReasoningContentFromMessage(msg) // 提取推理过程 hasToolCalls(message) // 是否含工具调用 hasPresentFiles(message) // 是否包含文件呈现 extractPresentFilesFromMessage(message) // 获取文件列表 isClarificationToolMessage(message) // 是否为澄清请求 ``` **线程路由(`core/threads/utils.ts`):** ```typescript pathOfThread(thread, context?) // → "/page/workspace/agents/{agentRef}/chats/{threadId}" // 或 "/page/workspace/chats/{threadId}" titleOfThread(thread) // → thread.values?.title ?? "Untitled" textOfMessage(message) // → 消息文本内容(string 或 content 数组中第一个 text) ``` **其他工具:** ```typescript // core/utils/files.tsx downloadFile(blob: Blob, filename: string) // core/utils/datetime.ts formatTimeAgo(dateString: string) // → "2 小时前" / "3 days ago" ``` --- ## 7. API 调用层 ### 7.1 API 客户端架构 **两层设计:** **第一层:LangGraph SDK Client**(`core/api/api-client.ts`) ```typescript const client = new LangGraphClient({ apiUrl: getLangGraphBaseURL(isMock), onRequest(url, init) { const headers = new Headers(init.headers ?? {}); headers.set("Authorization", getAuthorizationHeaderValue()); return { ...init, headers }; } }); export function getAPIClient(isMock?: boolean): LangGraphClient ``` **第二层:Fetch Client**(`core/api/fetch-client.ts`) ```typescript function apiFetch(input: RequestInfo, init?: RequestInit): Promise { return fetch(input, { ...init, headers: withAuthHeaders(init?.headers) }); } ``` ### 7.2 主要 API 模块 | 模块 | 文件 | 关键函数 | |------|------|---------| | **Auth** | `core/auth/api.ts` | `loginByUsername(username)` → `LoginByUsernameResponse` | | **Agents** | `core/agents/api.ts` | `createAgent()`, `updateAgent()`, `listAgents()`, `deleteAgent()`, `checkAgentName()` | | **Models** | `core/models/api.ts` | `loadModels()` → `Model[]` | | **Threads** | (via LangGraph SDK) | `useThreadStream()`, `useThreads()`, `useDeleteThread()`, `useRenameThread()` | | **Skills** | `core/skills/api.ts` | `loadSkills()`, `enableSkill(name, enabled)` | | **Uploads** | `core/uploads/api.ts` | `uploadFiles(threadId, files[])`, `listUploadedFiles()`, `deleteUploadedFile()` | | **Memory** | `core/memory/api.ts` | `loadMemory()`, `createMemoryFact()`, `updateMemoryFact()`, `deleteMemoryFact()` | | **Scheduled Tasks** | `core/scheduled-tasks/api.ts` | CRUD、publish、pause、resume、trigger、subscribe | ### 7.3 Base URL 配置 **文件:** `core/config/index.ts` ```typescript export function getBackendBaseURL(): string { // env.NEXT_PUBLIC_BACKEND_BASE_URL // 默认:""(当前域名根路径) } export function getLangGraphBaseURL(isMock?: boolean): string { // mock 模式:${origin}/mock/api // 正常:${origin}/api/langgraph // 或读取 env.NEXT_PUBLIC_LANGGRAPH_BASE_URL } ``` ### 7.4 认证流程 ``` LoginPage 调用 loginByUsername(username) ↓ POST /api/auth/login ↓ 返回 { access_token, token_type, user_id, email } ↓ setStoredAuth() 存入 localStorage["deerflow.auth"] ↓ 后续所有 API 请求 ↓ getAuthorizationHeaderValue() ↓ → "Bearer {access_token}" ↓ 自动添加到 Authorization header ``` --- ## 8. 国际化机制 ### 8.1 文件结构 ``` core/i18n/ ├── context.tsx # I18nProvider(包裹 App) ├── hooks.ts # useI18n() ├── locale.ts # detectLocale(),Locale = "en-US" | "zh-CN" ├── cookies.ts # cookie 读写(持久化语言选择) ├── translations.ts # 翻译映射表入口 └── locales/ ├── types.ts # Translations 接口定义(所有翻译 key 的类型) ├── en-US.ts # 英文翻译 └── zh-CN.ts # 简体中文翻译 ``` ### 8.2 Translations 接口分类 ```typescript interface Translations { locale: { localName: string } // 语言自身名称 common: { ... } // 通用词汇(确认、取消等) welcome: { ... } // 欢迎屏 inputBox: { ... } // 输入框(模式、占位符) sidebar: { ... } // 侧边栏导航 agents: { ... } // 智能体相关 chats: { ... } // 对话相关 workspace: { ... } // 工作区通用 settings: { ... } // 设置面板 toolCalls: { ... } // 工具调用展示 uploads: { ... } // 文件上传 subtasks: { ... } // 子任务 tokenUsage: { ... } // Token 用量 shortcuts: { ... } // 快捷键说明 } ``` ### 8.3 语言检测优先级 1. 读取 cookie `deerflow.locale` 2. 读取浏览器 `navigator.language` 3. 回退到 `en-US` --- ## 9. 主题与样式体系 ### 9.1 主题系统 **库:** `next-themes` ```typescript // App.tsx 应用主题 enableSystem // 跟随系统偏好 disableTransitionOnChange // 切换时禁用过渡动画 forcedTheme={pathname === "/landing" ? "dark" : undefined} > ``` **可选主题值:** `light` / `dark` / `system` ### 9.2 样式方案 **基础:** Tailwind CSS **组件库:** shadcn/ui(`components/ui/` 目录,40+ 基础组件) **主要 CSS 自定义属性:** ```css --container-width-sm /* 小容器宽度 */ --container-width-md /* 中等容器宽度(对话主区域) */ --primary /* 主色 */ --background /* 页面背景 */ --foreground /* 主文本色 */ --muted-foreground /* 次要文本色 */ --border /* 边框色 */ --sidebar-foreground /* 侧边栏文本 */ ``` ### 9.3 样式使用规范 - 工具类优先(Tailwind) - 动态样式用 `cn()` 工具函数合并(基于 `clsx` + `tailwind-merge`) - 主题感知样式使用 CSS 变量(如 `text-muted-foreground`) --- ## 10. 关键业务流程 ### 10.1 新建对话流程 ``` 1. 点击"新建对话" → navigate("/page/workspace/chats/new") 2. ChatPage 挂载,threadId = uuid(),isNewThread = true 3. 用户输入消息,点击发送 4. sendMessage(undefined, message) 调用 5. useThreadStream 内部: a. 上传文件(若有) b. 创建 LangGraph 线程,后端返回 threadId c. onStart(threadId) 回调: - setIsNewThread(false) - history.replaceState 更新 URL hash 6. WebSocket 流开始接收 AI 消息 7. MessageList 实时渲染消息 8. 流结束,onFinish 回调,可能触发桌面通知 ``` ### 10.2 创建智能体流程 ``` Step 1(name 步骤) ↓ 用户输入名称 ↓ checkAgentName(name) → 验证是否重名 ↓ createAgent({ name, skills: selectedSkills }) ↓ 后端返回 agent.id,setStep("chat") Step 2(chat 步骤) ↓ 生成临时 threadId ↓ useThreadStream({ assistantId: agent.id }) ↓ 发送 bootstrap 消息(触发智能体自我设置) ↓ onToolEnd 监听:event.name === "setup_agent" ↓ 指数退避重试加载 agent 信息,确认已保存 ↓ 显示"Agent Created"完成界面 ↓ 用户可点击"开始对话"或"返回列表" ``` ### 10.3 文件上传流程 ``` 1. 用户在 InputBox 选择/粘贴文件 2. PromptInputAttachments 展示预览列表 3. 用户点击发送 4. sendMessage 内部调用 uploadFiles(threadId, files) 5. POST /api/threads/{threadId}/upload 6. 后端返回 { files: [{ filename, size, virtual_path }] } 7. 构建 Message additional_kwargs.files 8. 完整 Message 提交到 LangGraph ``` --- ## 11. 项目配置与依赖 ### 11.1 主要依赖 | 包 | 版本 | 用途 | |----|------|------| | `react` | ^18 | 核心框架 | | `react-router-dom` | ^6 | 客户端路由(HashRouter) | | `@tanstack/react-query` | - | 服务端状态缓存 | | `@langchain/langgraph-sdk` | - | LangGraph WebSocket 连接 | | `next-themes` | - | 主题管理 | | `sonner` | - | Toast 通知 | | `lucide-react` | - | 图标库 | | `tailwindcss` | - | CSS 框架 | | `rehype` 系列 | - | Markdown 渲染 | | `uuid` | - | 生成临时 thread ID | ### 11.2 环境变量 | 变量 | 说明 | 默认值 | |------|------|--------| | `NEXT_PUBLIC_BACKEND_BASE_URL` | 后端接口基础 URL | `""` (当前域名) | | `NEXT_PUBLIC_LANGGRAPH_BASE_URL` | LangGraph API URL | `/api/langgraph` | | `NEXT_PUBLIC_STATIC_WEBSITE_ONLY` | 纯静态演示模式 | `"false"` | --- ## 12. 常见开发模式 ### 12.1 添加新工作区页面 ``` 1. 创建 src/pages/MyPage.tsx 2. 在 WorkspaceRoutes.tsx 中添加路由: } /> 3. 在 workspace-nav-chat-list.tsx 中添加导航入口(如需要) 4. 更新相关的跳转路径 ``` ### 12.2 添加新 API 接口 ``` 1. 在 core/xxx/api.ts 中定义 API 函数(使用 apiFetch 或 getAPIClient()) 2. 在 core/xxx/hooks.ts 中封装 useQuery / useMutation 3. 在页面或组件中调用 hook ``` ### 12.3 添加新 Context ``` 1. 创建 core/xxx/context.tsx:定义 Context + Provider + 默认值 2. 创建消费 hook(如 useXxx()) 3. 在合适的组件树根部插入 Provider(WorkspaceRoutes 或 ChatRuntime) ``` ### 12.4 添加国际化文本 ``` 1. 在 core/i18n/locales/types.ts 的 Translations 接口中添加字段 2. 在 core/i18n/locales/en-US.ts 添加英文内容 3. 在 core/i18n/locales/zh-CN.ts 添加中文内容 4. 组件中通过 useI18n() 访问:const { t } = useI18n(); t.xxx.yyy ``` ### 12.5 引入外部系统路由 在 `src/pages/PageRoutes.tsx` 中找到 TODO 注释位置,填入: ```typescript import AnotherSystemRoutes from "外部系统路径"; // 在 Routes 中添加: } /> ``` 如外部系统有独立 Provider 依赖,在此文件或 App.tsx 中包裹。 --- ## 附:关键文件速查 | 需求 | 关键文件 | |------|---------| | 修改路由结构 | `App.tsx`, `PageRoutes.tsx`, `WorkspaceRoutes.tsx` | | 修改侧边栏导航 | `components/workspace/workspace-nav-chat-list.tsx` | | 修改对话逻辑 | `pages/ChatPage.tsx`, `core/threads/hooks.ts` | | 修改输入框 | `components/workspace/input-box.tsx`, `components/ai-elements/prompt-input/` | | 修改消息渲染 | `components/workspace/messages/` | | 添加 API 接口 | `core/xxx/api.ts` + `core/xxx/hooks.ts` | | 修改全局设置 | `core/settings/store.ts`, `core/settings/hooks.ts` | | 修改翻译文本 | `core/i18n/locales/en-US.ts`, `core/i18n/locales/zh-CN.ts` | | 修改主题 | `App.tsx` (NextThemesProvider) | | 修改认证逻辑 | `core/auth/api.ts`, `pages/LoginPage.tsx` | | 定时任务 | `pages/ScheduledTasksPage.tsx`, `core/scheduled-tasks/` | | 智能体管理 | `pages/NewAgentPage.tsx`, `core/agents/` |