Files
deerflow-code/frontend-web/docs/openCanvas-progress.md
T
2026-05-15 23:27:34 +08:00

9.1 KiB
Raw Blame History

Open Canvas 集成进度文档

更新时间:2026-05-16 当前分支:dhk-1


一、整体进度概览

阶段 名称 状态
前端阶段一 路由与目录骨架 ✅ 已完成
前端阶段二 Canvas 双栏布局 ✅ 已完成
前端阶段三 类型与状态协议 ✅ 已完成(随阶段一一并完成)
后端阶段一 ThreadState 扩展 ✅ 已完成
后端阶段二 canvas_agent 注册 + 最小节点闭环 ✅ 已完成
前端阶段四 API 适配层 ✅ 已完成
前端阶段五 线程与流式状态接入 ✅ 已完成
前端阶段六 Artifact 渲染器增强(CodeMirror / Markdown) ✅ 已完成
前后端阶段七 局部选区改写 ✅ 已完成
前后端阶段八 快捷动作系统 ✅ 已完成
前后端阶段九 本地模型支持完善 ✅ 已完成

当前状态:全部 9 个阶段已完成 🎉


二、已完成工作详情

2.1 前端路由接入

修改了 frontend-web/src/pages/PageRoutes.tsx:

import OpenCanvasRoutes from "@/open-canvas/routes/OpenCanvasRoutes";
// 在 PageLayout 内部添加:
<Route path="canvas/*" element={<OpenCanvasRoutes />} />

子路由:

  • /page/canvas → CanvasHomePage(欢迎页 + 快速开始)
  • /page/canvas/:thread_id → CanvasThreadPage(主工作区)

2.2 前端目录结构(已建立,26 个文件)

frontend-web/src/open-canvas/
  api/
    client.ts          # stub,Phase 5 实现
    models.ts          # stub,Phase 5 实现
    threads.ts         # stub,Phase 5 实现
    runs.ts            # stub,Phase 5 实现
  components/
    layout/
      CanvasShell.tsx              # 顶层布局壳,flex-col
      CanvasHeader.tsx             # 顶部栏:返回键、折叠按钮、标题、流式指示
      CanvasResizableLayout.tsx    # 左右可拖拽双栏(react-resizable-panels)
    chat/
      CanvasChatPanel.tsx          # 左栏:欢迎状态 + 消息列表 + 禁用输入框
    artifact/
      CanvasArtifactPanel.tsx      # 右栏:空状态或渲染器
      CanvasArtifactHeader.tsx     # 右栏顶部:图标、标题、语言 Badge、版本导航
      CanvasArtifactVersionNav.tsx # 版本 prev/next 导航
      CanvasEmptyState.tsx         # 空状态:5 种快速开始模板
      TextArtifactRenderer.tsx     # 文本渲染(Phase 6 增强为 Markdown)
      CodeArtifactRenderer.tsx     # 代码渲染(Phase 6 增强为 CodeMirror)
  contexts/
    CanvasThreadContext.tsx        # threadId、modelName
    CanvasGraphContext.tsx         # messages、artifact、isStreaming
    CanvasUIContext.tsx            # isChatCollapsed、isEditing、selection 等 UI 状态
  hooks/
    useCanvasStream.ts             # stub,Phase 5 实现
    useCanvasArtifact.ts           # stub,Phase 6 实现
    useCanvasSelection.ts          # stub,Phase 7 实现
    useCanvasQuickActions.ts       # stub,Phase 8 实现
  pages/
    CanvasHomePage.tsx             # 欢迎页 + 快速开始入口
    CanvasThreadPage.tsx           # 三层 Provider 装配 + CanvasShell
  routes/
    OpenCanvasRoutes.tsx           # React Router 子路由
  types/
    artifact.ts                    # CanvasArtifact、CanvasArtifactText、CanvasArtifactCode
    thread.ts                      # CanvasMessage、CanvasThreadValues、CanvasThreadMetadata
    model.ts                       # CanvasModel(含 supports_canvas_* 能力字段)
    selection.ts                   # CanvasTextSelection、CanvasCodeSelection
    quick-action.ts                # QuickAction、QuickActionId
  utils/
    artifact.ts                    # getCurrentArtifactContent、appendArtifactVersion 等工具
    selection.ts                   # stub
    thread.ts                      # stub
    stream.ts                      # stub
    quick-actions.ts               # stub
  constants/
    index.ts                       # CANVAS_AGENT_ID、PROGRAMMING_LANGUAGES、QUICK_ACTIONS_*

2.3 核心类型定义

// types/artifact.ts
export type CanvasArtifact = {
  currentIndex: number;                          // 从 1 开始
  contents: CanvasArtifactContent[];
};

export type CanvasArtifactText = {
  index: number; type: "text"; title: string; fullMarkdown: string;
};

export type CanvasArtifactCode = {
  index: number; type: "code"; title: string;
  language: ProgrammingLanguageOptions; code: string;
};

2.4 Context 结构

// CanvasThreadPage.tsx 的装配顺序
<CanvasThreadProvider initialThreadId={thread_id}>
  <CanvasGraphProvider>
    <CanvasUIProvider>
      <CanvasShell />
    </CanvasUIProvider>
  </CanvasGraphProvider>
</CanvasThreadProvider>

三、已解决的技术难题

3.1 react-resizable-panels v4.6.2 API 差异

此版本与旧版 API 不同,必须使用:

// ✅ 正确写法
import type { GroupImperativeHandle } from "react-resizable-panels";
const layoutRef = useRef<GroupImperativeHandle>(null);

<ResizablePanelGroup
  orientation="horizontal"      // ← 不是 direction
  groupRef={layoutRef}          // ← 不是 ref
>
  <ResizablePanel id="chat" ... />      // ← 必须有 id
  <ResizablePanel id="artifact" ... />  // ← 必须有 id
</ResizablePanelGroup>

// 切换折叠
layoutRef.current?.setLayout({ chat: 0, artifact: 100 });  // ← 对象形式,非数组

3.2 Flex 高度链(防止底部内容被裁剪)

PageLayout(h-screen)
  └── main(flex-1)
        └── CanvasShell div(h-full flex flex-col)
              └── CanvasResizableLayout wrapper(flex-1 min-h-0 overflow-hidden)← 关键
                    └── ResizablePanelGroup(h-full)
                          └── 面板内容(h-full)

min-h-0 是必须的,否则 flex 子项不会收缩,底部内容被推出屏幕外。

3.3 折叠/展开按钮

CanvasHeader.tsx 中通过 useCanvasUIContext() 控制 isChatCollapsed, CanvasResizableLayout.tsx 中用 useEffect 监听并调用 setLayout:

useEffect(() => {
  layoutRef.current?.setLayout(
    isChatCollapsed ? { chat: 0, artifact: 100 } : { chat: 35, artifact: 65 }
  );
}, [isChatCollapsed]);

四、下一步:后端 ThreadState 扩展

目标文件:offline-backend-20260512/backend/packages/harness/deerflow/agents/thread_state.py

任务:在现有 ThreadState 中新增以下 Canvas 专用字段,不影响 artifacts: list[str](文件产物):

# 新增 TypedDict(建议单独定义)
class CanvasArtifactText(TypedDict):
    index: int
    type: Literal["text"]
    title: str
    fullMarkdown: str

class CanvasArtifactCode(TypedDict):
    index: int
    type: Literal["code"]
    title: str
    language: str
    code: str

class CanvasArtifact(TypedDict):
    currentIndex: int
    contents: list[CanvasArtifactText | CanvasArtifactCode]

# 在 ThreadState 中新增字段
artifact: Optional[CanvasArtifact]          # Canvas 主文档(区别于 artifacts 文件列表)
highlighted_text: Optional[str]             # 用户选中的文本片段
highlighted_code: Optional[str]             # 用户选中的代码片段
canvas_mode: Optional[Literal["text", "code"]]
quick_action_id: Optional[str]             # 快捷动作 id
web_search_enabled: bool                   # 默认 False
web_search_results: Optional[list[dict]]

验收标准:

  • 旧线程无 artifact 字段时不报错
  • pnpm typecheck 无新增错误(前端字段已对齐)
  • make test 全量回归通过

之后紧接:canvas_agent 注册 + factory.py 修改 + 最小节点闭环


五、关键文件位置

文件 用途
frontend-web/docs/openCanvas.md 主需求文档
frontend-web/docs/openCanvas-frontend-checklist.md 前端详细清单
frontend-web/docs/openCanvas-backend-checklist.md 后端详细清单
frontend-web/docs/openCanvas-progress.md 本进度文档
F:\react01\openCanvas open-canvas 源码参考(本地克隆)
offline-backend-20260512/backend/packages/harness/deerflow/agents/thread_state.py 下一步主改文件
offline-backend-20260512/backend/packages/harness/deerflow/agents/factory.py canvas_agent 注册位置

六、架构决策记录

决策 内容
canvas_agent 方案 新增独立 canvas_agent,不改 lead_agent(方案 A)
路由形式 /page/canvas/*,挂在 PageLayout 内
artifact 字段共存 artifact(Canvas 文档)与 artifacts: list[str](文件列表)独立共存
模型配置真源 后端 config.yaml,前端动态加载 /api/models
前端 artifact 索引 从 1 开始(与 open-canvas 对齐)
流式首版策略 节点完成后输出完整 artifact 对象,不做 token 级局部流

七、开始新对话的推荐提示词

我们在集成 open-canvas 到 DeerFlow 项目。
参考进度文档:frontend-web/docs/openCanvas-progress.md
目前前端阶段一/二/三已完成,下一步是后端 ThreadState 扩展。
请从第四节"下一步:后端 ThreadState 扩展"开始继续。