Files
mnote/docs/superpowers/plans/2026-05-10-mindmap-phase6-projection-editor-checklist.md
T

401 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mindmap Phase 6 Projection Editor v1 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 在不等待 `design/01-05` 全量收口的前提下,按 `design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md` 的方向推进 `Mindmap Phase 6` 主线重构:让 Mindmap 从“独立对象中心 + blob 真相 + 前端重壳”降级成“kernel subtree / graph 的一种 projection 与 editor”,并适配当前已有的 Rust 主链;本轮优先收口文档内 `/` 命令插入的 `mindmap block` 最小读写真相链,再把独立导图页挂到同一套 truth / adapter 上。
**Architecture:** 这一版不重写画布,不替换 `simple-mind-map`,也不要求 page aggregate / tree realtime 彻底闭环。先建立 block 与独立页共用的 Rust projection + 最小 command adapter,让 `simple-mind-map` 降级成 renderer / editor adapter,再把 `/` 插入示例骨架、默认隐藏 chrome、hover 后显示工具栏 / 右侧面板 / 节点操作这些产品体验挂到这条新主链上。测试和截图验收只负责证明主线成立,不反向限制重构范围;本轮允许 blob 存储暂时作为 compat substrate 存在,但不再把它当长期对象真相。
**Tech Stack:** Next.js / React、Convex、Rust `core-protocol`、Rust `bridge-runtime`、Rust `mnote-web``simple-mind-map`
---
## 方向确认
- [ ] 当前 Phase 6 的方向,明确是按 `design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md` 推进
- [ ] 目标明确是让 Mindmap 从“独立对象中心 + blob 真相 + 前端重壳”降级成“kernel subtree / graph 的一种 projection 与 editor”
- [ ] `design/01-05` 仍然是当前仓库的上位主线与优先级依据,但不是这轮 Mindmap Phase 6 的硬前置阻塞
- [ ] 这轮只在不破坏 `Page Aggregate / tree command / tree realtime` 主线口径的前提下,收口 Mindmap 自身的 truth / projection / command 边界
- [ ] 测试验收、截图留档、浏览器 smoke 只从属于主线重构,不反向决定主线方案
- [ ] 独立导图页必须适配同一套 truth / adapter,但文档内 `/` 命令插入的 `mindmap block` 仍是本轮主验收入口
## 范围冻结
### 本计划明确要做
- [ ] 明确按 `6-mindmap-kernel-phase6-projection-editor-v1.md` 的口径推进,不把 Mindmap 再当独立对象真相层
- [ ] 让 Mindmap 降级为 kernel subtree / graph 的一种 projection 与 editor,并接入当前 Rust 主链
- [ ] 文档内 `/` 命令插入的 `mindmap block` 是本轮主验收面
- [ ] 先收口 block 与独立页共用的最小读写真相链,再叠加产品体验
- [ ] 独立导图页读链优先消费 Rust `mindmap.projection.get`
- [ ] 前端导图 projection 类型与 Rust projection contract 对齐
- [ ] 为最小可用动作集建立 Rust command / op 适配层
- [ ] 保留 `simple-mind-map` 作为 renderer / editor adapter
- [ ] 为独立页与内嵌块建立同一套“projection truth + adapter runtime”口径
- [ ] `/` 插入后默认生成示例骨架:根节点 + 二级节点 + 两个分支主题
- [ ] 默认只显示导图本体;hover block 后显示工具栏 / 右侧面板 / 节点 hover 操作
- [ ] 补齐最小测试,确保 Phase 6 不回退成“前端摘要 + blob 真相”
### 本计划明确不做
- [ ] 不要求先完成 `Page Aggregate` 全闭环
- [ ] 不要求先完成 `tree realtime` 全域 live cache 统一
- [ ] 不重写 `MindmapBlock.tsx` 的整套工具栏、动画、导入导出、图片工具
- [ ] 不把 Convex `mindmaps` blob 存储在本轮强行改成 kernel node / edge 持久化
- [ ] 不把所有 `simple-mind-map execCommand` 一次性收口到 Rust
- [ ] 不为了浏览器 smoke 或截图留档而保守维持旧的 blob-first 主路径
### 开工判定
- [ ] 确认当前主线口径仍是 `tree-first graph kernel`
- [ ] 确认当前方向是“Mindmap 降级成 kernel projection/editor 并适配 Rust 主链”,而不是继续补一套独立 blob 系统
- [ ] 确认 `Page Aggregate` / `tree.*` / `/api/tree/events` 已是正式主链,而不是待验证方向
- [ ] 确认本轮目标是 `projection/editor v1`,不是 `mindmap full kernel cutover`
- [ ] 确认测试验收从属于重构主线,而不是主线为测试妥协
- [ ] 若任务被重新扩大到“顺手收口 page aggregate / realtime 尾项”,暂停并拆分
## 产品体验目标
- [ ] 主验收入口:文档页正文内通过 `/` 命令插入 `mindmap block`
- [ ] 插入后立即看到示例骨架,而不是空白画布或只有一个根节点
- [ ] 默认只显示画布与导图本体,不常驻显示顶部工具栏、右侧面板、节点操作
- [ ] 鼠标移入 block 区域后,显示顶部工具栏与右侧面板
- [ ] 鼠标移入节点后,显示节点级 hover 操作
- [ ] 独立页与 block 共享同一套 truth / adapter,只允许 chrome 壳层不同
## 参考对标
- [ ] 体验参考:`/mnt/Data1T/mnote/tmp/image copy 59.png`
- [ ] 更接近现有实现的参考:`/mnt/Data1T/mnote/tmp/image copy 60.png`
- [ ] 默认隐藏 chrome 的参考:`/mnt/Data1T/mnote/tmp/image copy 61.png`
- [ ] 外部参考实现:
- `https://github.com/suka233/siyuan-kmind-plugin`
- `https://github.com/wanglin2/mind-map`
- `https://github.com/wanglin2/mind-map-mcp`
- `https://github.com/wanglin2/lx-doc`
- [ ] 参考只用于体验与结构借鉴,不要求机械复刻外部产品
---
### Task 1: 冻结 Phase 6 执行边界
**Files:**
- Modify: `design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md`
- Reference: `design/01-05-current-priority-overview.md`
- Reference: `ARCHITECTURE.md`
- [ ] 在设计稿里补一段“本轮执行边界”,明确:
- 方向是把 Mindmap 降级成 kernel subtree / graph projection + editor
- 继续适配并复用当前已有的 Rust 主链
- 先切 block / page 共用的最小读写真相链
- 再把 `/` 插入、示例骨架、hover chrome 挂到新主链
- 继续保留 `simple-mind-map` renderer
- 不把 `01-05` 尾项作为硬前置
- [ ] 在设计稿里补一段“非目标说明”,明确:
- 本轮不是继续把 mindmap blob 打磨成长期对象真相
- 本轮不是先做前端重壳再以后接 Rust
- 本轮不是一次性完成最终 kernel-native graph storage cutover
- [ ] 在设计稿里补一段“主验收面”,明确:
- 主验收入口是文档内 `/` 命令插入的 `mindmap block`
- 独立页必须共用同一真相链,但不是产品验收主画面
- [ ] 在设计稿里补一段“测试从属关系”,明确:
- smoke / 截图用于证明主线成立
- 不允许为了更好测而维持旧主路径
- [ ] 在设计稿里补一段“阻塞条件”,明确只有以下情况才暂停:
- Rust projection contract 不可用
- 现有 bridge 无法表达最小 MindmapOp
- 独立页与内嵌块入口无法共享 projection contract
- [ ] 在设计稿里补一段“延期项”,把以下内容显式标为后续阶段:
- blob 持久化移除
- page aggregate 深度对齐
- realtime delta 深度接入
- 全量画布命令收口
- [ ] 验证:设计稿读起来不会再误导执行者去先重写画布或等待 `01-05` 全完成
---
### Task 2: 建立 Mindmap Projection 真实合同
**Files:**
- Inspect/Modify: `rust/crates/core-protocol/src/mindmap.rs`
- Inspect/Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Inspect/Modify: `rust/crates/mnote-web/src/routes/mindmap_shell.rs`
- Inspect/Modify: `wolai-frontend/src/lib/mindmap/mindmap-projection.ts`
- Test: `rust/crates/bridge-runtime/src/lib.rs`
- Test: `wolai-frontend/src/lib/mindmap/*.test.ts`
- [ ] 盘点 Rust 侧现有 `MindmapProjection` 字段,列出前端真正需要消费的最小字段:
- `documentId`
- `mindmapId`
- `rootNodeId`
- `title`
- `nodeCount`
- `nodes`
- `raw/tree payload`
- `owner/source/meta`
- [ ] 判断前端 `MindmapProjection` 与 Rust contract 的差异,特别检查:
- 前端是否仍把 `projection: "mindmap_subtree"` 当私有摘要
- 是否缺少 source / owner / version 语义
- 是否把 `data` 原树和 projection 摘要混成一层
- [ ] 冻结一份统一口径:
- Rust 输出什么
- 前端只做什么适配
- 哪些字段是 renderer 所需,哪些字段是系统真相
- [ ] 在 contract 里显式区分三层:
- projection truth
- renderer input
- chrome / hover UI state
- [ ] 确认 block 和独立页都消费同一份 projection truth,不允许各自再拼一份摘要对象
- [ ] 补测试,至少覆盖:
- Rust `mindmap.projection.get` 返回稳定 schema
- 前端 adapter 能解析 Rust projection
- 当原始 blob 字段缺失时,adapter 仍只做渲染兜底,不篡改 projection 语义
- [ ] 验证命令:
- `cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime mindmap`
- `cd /mnt/Data1T/mnote/wolai-frontend && pnpm test -- mindmap-projection`
---
### Task 3: 建立最小 Command Adapter,只接本轮必须动作
**Files:**
- Inspect/Modify: `rust/crates/core-protocol/src/mindmap.rs`
- Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Modify: `wolai-frontend/src/app/api/mindmap/[docId]/[mindmapId]/route.ts`
- Create/Modify: `wolai-frontend/src/lib/mindmap/mindmap-command-client.ts`
- Test: `rust/crates/bridge-runtime/src/lib.rs`
- Test: `wolai-frontend/src/lib/mindmap/*.test.ts`
- [ ] 冻结 v1 支持的最小动作集,只选:
- `rename/updateText`
- `addChild`
- `deleteNode`
- `addSiblingAfter`
- 节点 hover 操作所需的最小命令
- `/` 插入后的示例骨架创建
- [ ] `setRefs``move` 不作为本轮 block 主验收前置,除非实现时已在同一 adapter 中顺手闭环
- [ ] 为这些动作建立前端 command client,不再默认整棵 `POST data blob`
- [ ] Rust bridge 明确区分:
- projection query
- command apply
- compat `mindmaps.put`
- [ ] 只有确实无法表达的动作,才回退到整棵 blob 保存;回退只作为 compat 行为存在,不在这里展开做标签治理
- [ ] 补测试,至少覆盖:
- 每个最小动作都能转成 `MindmapOp``MindmapCommand`
- 非支持动作不会偷偷走“无差别全量 blob 覆盖”
- compat 回退有显式标记
- [ ] 验证命令:
- `cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime mindmap_apply_ops`
- `cd /mnt/Data1T/mnote/wolai-frontend && pnpm test -- mindmap-command-client`
---
### Task 4: 建立共享 Entry Contract 与 Renderer Adapter 基础
**Files:**
- Modify: `wolai-frontend/src/lib/mindmap/mindmap-projection.ts`
- Create/Modify: `wolai-frontend/src/lib/mindmap/mindmap-entry-contract.ts`
- Create/Modify: `wolai-frontend/src/lib/mindmap/mindmap-renderer-adapter.ts`
- Modify: `wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`
- Modify: `wolai-frontend/src/app/mindmap/[docId]/[mindmapId]/page.tsx`
- Test: `wolai-frontend/src/lib/mindmap/*.test.ts`
- Test: `wolai-frontend/src/components/editor/blocks/*.test.tsx`
- Test: `wolai-frontend/src/app/mindmap/[docId]/[mindmapId]/*.test.tsx`
- [ ] 把“系统真相字段”和“画布运行时字段”分成三层:
- projection truth
- renderer input
- chrome / hover UI state
- [ ] 建立 `mindmap-entry-contract`,统一 block 与独立页入口的最小约定:
- 如何拿 projection
- 如何构建 renderer input
- 如何声明 UI 壳层差异
- [ ] 提取 `mindmap-renderer-adapter`,专门负责:
- projection -> `simple-mind-map` 可渲染数据
- renderer 事件 -> 规范化编辑意图
- [ ] 避免继续在 `MindmapBlock.tsx` 或独立页内直接把 `buildMindmapProjection(blob)` 当正式读链
- [ ] 补测试,至少覆盖:
- adapter 不改写 projection 语义
- adapter 的兜底只作用于 renderer
- block 与独立页能共用同一 entry contract / renderer adapter
- [ ] 验证命令:
- `cd /mnt/Data1T/mnote/wolai-frontend && pnpm test -- mindmap-projection mindmap-renderer-adapter src/app/mindmap src/components/editor/blocks`
---
### Task 5: 收口文档内 mindmap block 的主读写真相链
**Files:**
- Modify: `wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`
- Modify: `wolai-frontend/src/lib/mindmap/mindmap-projection.ts`
- Modify: `wolai-frontend/src/lib/mindmap/mindmap-renderer-adapter.ts`
- Modify: `wolai-frontend/src/lib/mindmap/mindmap-command-client.ts`
- Inspect/Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Test: `wolai-frontend/src/components/editor/blocks/*.test.tsx`
- Test: `wolai-frontend/src/lib/mindmap/*.test.ts`
- Test: `rust/crates/bridge-runtime/src/lib.rs`
- [ ] 让文档内 `mindmap block` 改为消费统一 projection truth,而不是 `buildMindmapProjection(blob)` 私有摘要
- [ ] 让 block 的最小编辑动作统一走 Task 3 的 command adapter
- [ ] 把节点 hover 操作建立在这条最小写入链上,不再默认走全量 blob 覆盖
- [ ] 确认 `MindmapBlock.tsx` 不再同时扮演 truth / adapter / chrome 三层
- [ ] 补测试,至少覆盖:
- block 主读链优先消费 projection truth
- block 最小动作集能转成 `MindmapOp` / command
- 非支持动作不会偷偷回退成无差别整棵覆盖
- [ ] 验证命令:
- `cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime mindmap`
- `cd /mnt/Data1T/mnote/wolai-frontend && pnpm test -- MindmapBlock mindmap-command-client`
---
### Task 6: 把 `/` 插入示例骨架与 Hover Chrome 体验挂到新主链
**Files:**
- Modify: `wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`
- Inspect/Modify: `wolai-frontend/src/store/editor-bridge.ts`
- Inspect/Modify: `wolai-frontend/src/components/editor/*`
- Inspect/Modify: `wolai-frontend/src/app/(app)/documents/[id]/*`
- Test: `wolai-frontend/src/components/editor/blocks/*.test.tsx`
- [ ] 找到文档页 `/` 命令插入 `mindmap block` 的真实入口,并把它改为直接创建示例骨架 truth
- [ ] 示例骨架固定为:
- 根节点
- 1 个二级节点
- 2 个分支主题
- [ ] `mindmap block` 默认只显示画布与导图本体
- [ ] 鼠标移入 block 区域后,显示顶部工具栏与右侧面板
- [ ] 鼠标移入节点后,显示节点级 hover 操作
- [ ] chrome 显隐只依赖新 truth / adapter 主链,不在旧 blob 路径上额外补逻辑
- [ ] 补测试,至少覆盖:
- `/` 插入后不是空白壳
- 默认不常显 chrome
- hover block 后 chrome 出现
- hover 节点后节点操作出现
---
### Task 7: 独立导图页同步挂到同一套 Truth
**Files:**
- Modify: `wolai-frontend/src/app/mindmap/[docId]/[mindmapId]/page.tsx`
- Modify: `wolai-frontend/src/app/api/mindmap/[docId]/[mindmapId]/route.ts`
- Inspect/Modify: `wolai-frontend/src/lib/documents/rust-runtime.ts`
- Inspect/Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Inspect/Modify: `rust/crates/mnote-web/src/routes/mindmap_shell.rs`
- Modify: `wolai-frontend/src/lib/mindmap/mindmap-entry-contract.ts`
- Test: `wolai-frontend/src/app/mindmap/[docId]/[mindmapId]/*.test.tsx`
- Test: `wolai-frontend/src/app/api/mindmap/[docId]/[mindmapId]/*.test.ts`
- [ ] 让独立页 SSR 读链优先请求 Rust `mindmap.projection.get`
- [ ] 让独立页与 block 共用 Task 3/4 产出的 entry contract / renderer adapter / command adapter
- [ ] 如果仍需兼容 blob 返回,必须把它降级为 fallback,并在代码里标出 compat 边界
- [ ] 保证 route 返回的主体语义是 projection,而不是“顺便附带 data blob 的摘要”
- [ ] 校验 `initialProjection` 命名与真实内容一致,不再出现“其实是本地摘要却叫 projection”的假象
- [ ] 明确哪些行为允许不同:
- block 以文档内 hover chrome 为主
- 独立页可保留更完整的工作区 chrome
- [ ] 补测试,至少覆盖:
- projection 正常返回时,页面首屏直接消费 Rust projection
- projection 失败时,compat fallback 行为可见且受控
- 独立页与 block 读取的是同一 contract,不再分叉
- [ ] 验证命令:
- `cd /mnt/Data1T/mnote/wolai-frontend && pnpm test -- src/app/mindmap src/app/api/mindmap`
---
### Task 8: 标记并隔离仍未切掉的 Compat 区域
**Files:**
- Modify: `wolai-frontend/src/app/api/mindmap/[docId]/[mindmapId]/route.ts`
- Modify: `wolai-frontend/src/lib/documents/rust-runtime.ts`
- Modify: `wolai-frontend/src/lib/mindmap/mindmapLocalStore.ts`
- Modify: `wolai-frontend/convex/mindmaps.ts`
- Optional Doc: `design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md`
- [ ] 给仍保留 blob 真相的路径打清楚标签:
- `compat_blob_read`
- `compat_blob_write`
- `renderer_only_fallback`
- [ ] 只在 Task 5 / Task 7 主链稳定后,集中补 compat 标记;不和 Task 3 的命令适配职责混写
- [ ] 避免继续在新代码里把 compat 路径写成正式主路径
- [ ] 明确 localStorage / 附件广播 / `mindmap-<id>.json` 属于过渡资产,不属于长期真相合同
- [ ] 给 Convex `mindmaps` 的整棵 blob 存储加一句清晰注释:当前仅是 substrate / compat 持久化,不是长期对象语义
- [ ] 验证:新执行者一眼能区分哪条是正式路径,哪条只是过渡保底
---
### Task 9: 补齐最小回归与浏览器验收
**Files:**
- Test: `wolai-frontend/src/app/mindmap/[docId]/[mindmapId]/*.test.tsx`
- Test: `wolai-frontend/src/components/editor/blocks/*.test.tsx`
- Test: `wolai-frontend/src/lib/mindmap/*.test.ts`
- Test: `rust/crates/bridge-runtime/src/lib.rs`
- Optional Script: `scripts/task*-mindmap-smoke.js`
- [ ] 单测覆盖:
- projection 主读链
- compat fallback
- renderer adapter
- 最小 command adapter
- block / 独立页共享 truth
- [ ] 补一个浏览器 smoke,至少验证:
- 新建任意页面后,可通过 `/` 命令插入 `mindmap block`
- 插入后立即出现示例骨架,而不是空白壳
- 默认不常显 chrome
- hover block 后顶部工具栏和右侧面板出现
- hover 节点后节点级操作出现
- 至少一个最小编辑动作能成功回显
- [ ] 独立页 smoke 只作为同链补充验证,不再是本轮产品主验收面
- [ ] 补一个“禁止回退”断言:
- 当 projection 可用时,不允许默认走 `buildMindmapProjection(blob)` 主路径
- [ ] 验证命令:
- `cd /mnt/Data1T/mnote/wolai-frontend && pnpm test`
- `cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime`
- 浏览器 smoke 使用仓库现有 `scripts/task*-smoke.js` 风格新增或复用
## 依赖关系
- [ ] Task 1 完成后,才能开始代码实现,避免目标漂移
- [ ] Task 2 是 Task 3 和 Task 4 的共同前置
- [ ] Task 3 必须先于 Task 4、Task 5、Task 6、Task 7,否则 entry contract 和 block/page 主链没有稳定命令面
- [ ] Task 4 必须先于 Task 5 和 Task 7,否则 block / page 没有共享 contract 与 renderer adapter
- [ ] Task 5 是 block 主验收面的第一落点,必须先于 `/` 插入和 hover 体验
- [ ] Task 6 依赖 Task 5,不能先在旧主路径上做 UI 伪收口
- [ ] Task 7 依赖 Task 3 和 Task 4;建议在 Task 5 之后执行,确保 page 跟随 block 主链接入
- [ ] Task 8 不再与 Task 3 重复承担命令适配职责,只负责集中标记和隔离仍残留的 compat 区域
- [ ] Task 9 只在 Task 5/6/7/8 形成最小闭环后收尾
## 退出标准
- [ ] 独立导图页主读链已不是 `mindmaps.get + 前端摘要`
- [ ] 文档内 `/` 插入的 `mindmap block` 已成为本轮主验收面,并挂在新 truth / adapter 主链上
- [ ] `simple-mind-map` 被明确降级为 renderer/editor adapter
- [ ] 至少一组最小编辑动作已走 Rust `MindmapOp` / command,而不是默认全量 blob 覆盖
- [ ] `/` 插入后默认生成示例骨架,且不是空白壳
- [ ] 默认隐藏 chromehover block / 节点后再显示相应操作
- [ ] 独立页与内嵌块共享同一套 projection truth 口径
- [ ] 仍未移除的 blob / localStorage / 附件路径都被清楚标成 compat
- [ ] 测试和 smoke 能证明 Phase 6 已经开始脱离 blob-first,而不是只换了命名
## 暂不作为阻塞的尾项
- [ ] `Page Aggregate` 写后完整回灌统一 projection
- [ ] page subtree 与正文本地态完全同源
- [ ] tree realtime 全域单 live cache
- [ ] 导图 blob 存储彻底移除
- [ ] 全量画布命令 Rust 化
## 建议执行顺序
- [ ] 第 1 天:Task 1 + Task 2
- [ ] 第 2 天:Task 3
- [ ] 第 3 天:Task 4
- [ ] 第 4 天:Task 5
- [ ] 第 5 天:Task 6
- [ ] 第 6 天:Task 7 + Task 8
- [ ] 第 7 天:Task 9 + 设计稿回填