feat: 收口 mindmap Phase 6 Leptos UI shell

This commit is contained in:
lix-2026
2026-05-11 12:27:40 +08:00
parent b7dddd2a66
commit b5eb27fabc
51 changed files with 8669 additions and 1313 deletions
@@ -1,400 +1,312 @@
# Mindmap Phase 6 Projection Editor v1 Implementation Plan
# Mindmap Phase 6 leptos-mindmap 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.
> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` where independent code slices exist, or `superpowers:executing-plans` when running this checklist task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
>
> 旧 Rust-native 计划已归档:
> `/mnt/Data1T/mnote/recycle/design/superpowers-plans/2026-05-10-mindmap-phase6-projection-editor-checklist.rust-native.md`
**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
**Goal:** 交付 `leptos-mindmap`:以 Rust kernel 作为导图事实源与命令面,以 `simple-mind-map` / KMind-like runtime 作为 `leptos-tiptap` NodeView 中的显示与交互 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 存在,但不再把它当长期对象真相
**Architecture:** `kernel truth -> mindmap.kernel_projection.v1 -> mindmap.simple_mind_map_scene.v1 -> leptos-mindmap island -> simple-mind-map runtime -> adapter diff / command bridge -> kernel command`
**Tech Stack:** Next.js / React、Convex、Rust `core-protocol`、Rust `bridge-runtime`、Rust `mnote-web``simple-mind-map`
**Tech Stack:** Rust `core-protocol`、Rust `bridge-runtime`、Rust `mnote-web``leptos-tiptap` island、`simple-mind-map`、KMind/lx-doc reference code、Playwright smoke、Convex compat substrate。
---
## 方向确认
## 口径修正状态
- [ ] 当前 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` 仍是本轮主验收入口
- [x] 已确认旧 `Rust / Leptos 原生 Projection Editor` 口径过时。
- [x] 已将旧设计稿归档到 `recycle/design/06-mindmap/process/`
- [x] 已将旧 checklist 归档到 `recycle/design/superpowers-plans/`
- [x] 已把当前执行口径修正为 `leptos-mindmap`
- [x] 后续每完成一个实现节点,必须在本 checklist 中勾选对应项。
- [x] 2026-05-10 现实复核:当前已勾选的 Task 6/7/8/退出标准只代表“projection / command 最小闭环 + 手写伪画布 smoke”通过,不代表真实 `simple-mind-map` runtime 已接入。最终 UI/runtime 完成判定以后续 `Phase 6.1` 为准。
## 范围冻结
### 本计划明确要做
- [ ] 明确按 `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 真相”
- [ ] 定义 kernel projection 与 `simple-mind-map` adapter projection 的边界。
- [ ] `leptos-tiptap` NodeView 中挂载 `leptos-mindmap`
- [ ] 使用 `simple-mind-map` runtime 渲染真实导图画布。
- [ ] 参考 `lx-doc/mind-map` 和 KMind 插件复刻第一阶段 toolbar/sidebar/bottom bar。
- [ ] 通过 adapter command bridge 把用户编辑写回 Rust kernel。
- [ ] `data_change` 这类粗粒度事件建立 diff 到 kernel command 的转换。
- [ ] 保留 `compat_payload` 承接暂未语义化的高级字段。
- [ ] 更新 smoke,验证真实节点、连线、编辑、reload 和截图证据。
### 本计划明确不做
- [ ]要求先完成 `Page Aggregate` 全闭环
- [ ]要求先完成 `tree realtime` 全域 live cache 统一
- [ ]重写 `MindmapBlock.tsx` 的整套工具栏、动画、导入导出、图片工具
- [ ]把 Convex `mindmaps` blob 存储在本轮强行改成 kernel node / edge 持久化
- [ ]把所有 `simple-mind-map execCommand` 一次性收口到 Rust
- [ ]为了浏览器 smoke 或截图留档而保守维持旧的 blob-first 主路径
- [ ]`simple-mind-map` runtime data 作为 canonical truth。
- [ ]直接嵌入完整 Vue2 `lx-doc/mind-map` 应用并保存 blob。
- [ ]恢复旧 React `MindmapBlock.tsx` 作为 3000 文档页默认主链。
- [ ]要求本轮完成 Rust-native renderer。
- [ ]在本轮完整复刻 KMind 多根、MOC、镜像块、思源块预览、PDF 标注跳转。
- [ ]立即移除 Convex `mindmaps` blob;它只作为 compat substrate / import-export / fallback snapshot。
### 开工判定
## 文件职责图
- [ ] 确认当前主线口径仍是 `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`
- [ ] 参考只用于体验与结构借鉴,不要求机械复刻外部产品
- `design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md`
- 当前 `leptos-mindmap` 设计口径。
- `rust/crates/core-protocol/src/mindmap.rs`
- kernel projection、adapter projection、command DTO。
- `rust/crates/bridge-runtime/src/lib.rs`
- projection builder、adapter scene builder、command apply。
- `rust/crates/mnote-web/`
- projection / command route 与 3000 文档页运行时分发。
- `rust/spikes/leptos-tiptap-spike/src/lib.rs`
- mindmap NodeView 与 island 挂载。
- `wolai-frontend/src/lib/mindmap/*`
- adapter、diff、simple-mind-map bridge、legacy compat。
- `wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`
- legacy React/simple-mind-map 参考实现,不作为默认主链。
- `design/05-editor-mainline/reference-code/lx-doc/mind-map`
- UI、命令、插件和主题参考。
- `design/05-editor-mainline/reference-code/kmind-plugin`
- KMind 打包实现和视觉行为参考。
- `scripts/task166-mindmap-phase6-block-smoke.js`
- Phase 6 浏览器 smoke。
---
### Task 1: 冻结 Phase 6 执行边界
### Task 1: 冻结 leptos-mindmap Contract
**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`
- Modify: `rust/crates/core-protocol/src/mindmap.rs`
- Modify: `rust/crates/core-protocol/src/lib.rs`
- Test: `rust/crates/core-protocol/src/lib.rs`
- [ ] 在设计稿里补一段“本轮执行边界”,明确:
- 方向是把 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` 全完成
- [x] 定义 `MindmapKernelProjection`,包含 `schema``documentId``mindmapId``rootNodeId``revision``nodes``edges``summaries``associativeLines``layout``theme``view``capabilities``source`
- [x] 定义 `MindmapAdapterProjection`,包含 `schema``runtime="simple-mind-map"``root``layout``theme``themeConfig``view``config``compatPayload``kernelRevision`
- [x] 定义最小 command DTO`updateText``insertChild``insertSiblingAfter``deleteNode``moveNode``patchView``setLayout``setTheme`
- [x] 标记 `MindmapTreeNode` / `MindmapOp` 为 compat DTO。
- [x] 补 Rust 单测:kernel projection 与 adapter projection 都能序列化为 camelCase JSON。
- [x] 验证命令:`cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol mindmap`
---
### Task 2: 建立 Mindmap Projection 真实合同
### Task 2: 建立 Adapter Projection Builder
**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`
- [x] 增加 `mindmap.kernel_projection.get` 查询。
- [x] 增加 `mindmap.simple_mind_map_scene.get` 查询。
- [x] 从 kernel truth 或 compat substrate 构建 `simple-mind-map` 可消费的 `root/layout/theme/view/config`
- [x] projection 返回明确 `source`,如 `kernel``compat-blob``adapter-cache`
- [x] 默认骨架包含 `KMIND``二级节点`、两个 `分支主题``概要`
- [x] 补 Rust 单测:同一 mindmapId 能输出 kernel projection 和 adapter projection,节点数量一致。
- [x] 验证命令:`cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime mindmap_projection`
---
### Task 4: 建立共享 Entry Contract 与 Renderer Adapter 基础
### Task 3: 审计 lx-doc / KMind Runtime 能力
**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`
- Inspect: `design/05-editor-mainline/reference-code/lx-doc/mind-map/src/pages/Edit/components/Edit.vue`
- Inspect: `design/05-editor-mainline/reference-code/lx-doc/mind-map/src/pages/Edit/components/ToolbarNodeBtnList.vue`
- Inspect: `design/05-editor-mainline/reference-code/lx-doc/mind-map/src/pages/Edit/components/SidebarTrigger.vue`
- Inspect: `design/05-editor-mainline/reference-code/kmind-plugin/app/app.js`
- Output Optional: `design/06-mindmap/process/6-mindmap-leptos-adapter-reference-notes-v1.md`
- [ ] 把“系统真相字段”和“画布运行时字段”分成三层:
- 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`
- [x] 列出本轮需要注册的 `simple-mind-map` 插件。
- [x] 列出 toolbar 命令到 kernel command 的映射。
- [x] 列出右侧栏第一阶段保留项:节点样式、导图样式、主题、结构、大纲。
- [x] 列出底部工具第一阶段保留项:统计、搜索、缩放、回根节点、全屏或只读。
- [x] 列出必须进入 `compat_payload` 的字段。
- [x] 明确哪些 KMind 专有能力延期。
---
### Task 4: 实现 simple-mind-map Bridge
### Task 5: 收口文档内 mindmap block 的主读写真相链
**Files:**
- Create/Modify: `wolai-frontend/src/lib/mindmap/simple-mind-map-bridge.ts`
- Create/Modify: `wolai-frontend/src/lib/mindmap/leptos-mindmap-adapter.ts`
- Create/Modify: `wolai-frontend/src/lib/mindmap/mindmap-command-diff.ts`
- [x] 动态加载 `simple-mind-map`,避免 SSR 阶段访问 `window/document`
- [x] 注册本轮必需插件:Drag、KeyboardNavigation、Export、Select、RichText、AssociativeLine、Search、Scrollbar、OuterFrame 等按审计结果取舍。
- [x] 支持从 `MindmapAdapterProjection` 初始化 runtime。
- [x] 监听 `data_change``view_data_change``node_active``back_forward``scale``translate`
- [x] 暴露 `execCommand` 安全包装,不让 UI 任意写 blob。
- [x] 销毁时清理 runtime、事件监听和定时器。
### Task 5: 建立 Command Bridge 与 Diff
**Files:**
- Modify: `wolai-frontend/src/lib/mindmap/mindmap-command-diff.ts`
- Modify: `rust/crates/bridge-runtime/src/lib.rs`
- Test: relevant Rust and TS tests
- [x] 直接命令优先:toolbar 明确动作直接发 kernel command。
- [x] `data_change` diff 能识别文本更新、插入子节点、插入同级节点、删除节点、移动节点。
- [x] `view_data_change` 转成 `mindmap.view.patch`
- [x] 主题与结构变化转成 `mindmap.theme.set` / `mindmap.layout.set`
- [x] 暂不可识别字段进入 `compat_payload.patch`,并记录字段路径。
- [x] command 成功后刷新 projection revision。
- [x] command 失败时 UI 显示可定位错误,不伪装保存成功。
### Task 6: 嵌入 Leptos Tiptap Mindmap NodeView
**Files:**
- Modify: `rust/spikes/leptos-tiptap-spike/src/lib.rs`
- Modify: relevant generated island runtime
- Inspect/Modify: `design/05-editor-mainline/reference-code/leptos-tiptap/tiptap/src/extensions/tiptap_paragraph.ts`
- [x] `/` 命令插入 mindmap block 时,Tiptap 节点只保存 `mnoteBlockType="mindmap"``mindmapId``rootNodeId``projectionVersion`
- [x] NodeView 创建 `data-testid="mnote-mindmap-editor-root"`
- [x] NodeView 挂载 `leptos-mindmap` island。
- [x] island 初始化时请求 `mindmap.simple_mind_map_scene.get`
- [x] adapter 初始化失败时显示 `adapter_init_failed`
- [x] projection 获取失败时显示 `projection_load_failed`
- [x] 不创建 `.mnote-mindmap-react-mount` 作为主链合同。
### Task 7: 复刻第一阶段 KMind-like UI
**Files:**
- Create/Modify: relevant leptos-mindmap UI files
- Reference: `lx-doc/mind-map/src/pages/Edit/components/Toolbar.vue`
- Reference: `lx-doc/mind-map/src/pages/Edit/components/SidebarTrigger.vue`
- Reference: `lx-doc/mind-map/src/pages/Edit/components/NavigatorToolbar.vue`
- [x] 顶部工具栏包含撤销、重做、编辑节点、同级节点、子节点、删除节点、标签、超链接、备注、图片、图标、概要、关联线、公式、格式刷、导入、导出。
- [x] 工具栏按钮禁用状态跟随 active node。
- [x] 右侧栏包含节点样式、导图样式、主题、结构、大纲。
- [x] 底部栏包含字数/节点数、回根节点、搜索、缩放百分比、全屏或只读入口。
- [x] 默认主题对齐 KMind 截图:根节点红色、二级节点蓝色、分支蓝色文本、概要括号线。
- [x] 移动端或窄宽度下工具栏能折叠为更多菜单。
### Task 8: 更新 Browser Smoke
**Files:**
- Modify: `scripts/task166-mindmap-phase6-block-smoke.js`
- Reference: `scripts/TESTING_REFERENCE.md`
- Output: `tmp/task166-mindmap-phase6-block-smoke/*.png`
- Output: `tmp/task166-mindmap-phase6-block-smoke/result.json`
- [x] smoke 启动前检查 3000 / 8000 端口占用,并输出 PID、进程名、命令行。
- [x] 使用真实测试账号登录。
- [x] 新建或打开临时文档,通过 `/` 命令插入 mindmap block。
- [x] 断言 `data-testid="mnote-mindmap-editor-root"` 存在。
- [x] 断言 `simple-mind-map` runtime 画布存在且 bounding box 非零。
- [x] 断言至少 4 个可见节点和 3 条可见连线。
- [x] 截图保存插入后、编辑后、reload 后三张图。
- [x] 编辑一个节点文本,等待 kernel command 成功,再 reload,断言新文本仍存在。
- [x] 结构化输出 JSON,包含 `ok``task``baseUrl``documentId``mindmapId``nodeCount``edgeCount``kernelRevision``adapterSource``screenshots``resultPath`
- [x] 失败时错误信息必须指出阶段,例如 `projection_load_failed``adapter_init_failed``runtime_render_failed``command_failed``reload_mismatch`
### Task 9: 调整 Legacy React / Blob 边界
**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`
- Modify: `wolai-frontend/src/lib/mindmap/mindmap-renderer-adapter.ts`(当前文件已不存在,边界由 `leptos-mindmap-adapter.ts` / `simple-mind-map-bridge.ts` 与静态断言覆盖)
- [ ] 让文档内 `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`
- [x] 给旧 React mindmap 文件增加注释:仅为 legacy/compat/reference,不是 3000 文档页默认主链。
- [x] 禁止新增代码把 `.mnote-mindmap-react-mount` 作为 Phase 6 主验收合同。
- [x] 禁止新增代码把 `window.__mindmapInstance` 作为 smoke 成功条件。
- [x] 保留旧 blob 导入能力,但明确它是 import/compat,不是 canonical edit path。
- [x]静态断言或 smoke:默认文档页 host 为 `leptos_tiptap_island` 时不加载旧 React mindmap 主链。
---
### 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
### Task 10: 独立导图页接同一 Adapter Contract
**Files:**
- Modify: `rust/crates/mnote-web/src/routes/mindmap_shell.rs`
- Modify: `rust/crates/mnote-web/src/ssr/pages/mindmap.rs`
- 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`
- [x] 独立导图页读取同一 `mindmap.simple_mind_map_scene.get`
- [x] 独立页和文档 block 共用 command bridge。
- [x] 独立页可以有更完整 chrome,但 projection、command、revision contract 必须一致。
- [x] compat fallback 必须带 `source="compat-blob"`UI 或日志可见。
- [x] 补 smoke 或 route 单测:同一个 `mindmapId` 在 block 和独立页返回相同 root、node count、revision。
---
### Task 8: 标记并隔离仍未切掉的 Compat 区域
### Task 11: 验证与复核
**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`
- Test: Rust protocol/runtime tests
- Test: leptos-tiptap island build
- Test: `scripts/task166-mindmap-phase6-block-smoke.js`
- [ ] 给仍保留 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 持久化,不是长期对象语义
- [ ] 验证:新执行者一眼能区分哪条是正式路径,哪条只是过渡保底
- [x] 运行 `cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol mindmap`
- [x] 运行 `cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime mindmap`
- [x] 重新构建 leptos-tiptap island。
- [x] 启动本地服务,确认 3000 主链来自 `mnote-web` 文档页。
- [x] 运行 `node scripts/task166-mindmap-phase6-block-smoke.js`
- [x] 人工查看 smoke 输出截图,确认不是标题占位、不是空容器、不是旧 React mount。
- [x] 每完成一个 checklist 项,必须基于代码或验证结果再勾选。
---
### Task 9: 补齐最小回归与浏览器验收
### Task 12: Phase 6.1 现实复核与真实 Runtime Cutover
**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`
- Modify: `design/05-editor-mainline/reference-code/leptos-tiptap/tiptap/src/extensions/tiptap_paragraph.ts`
- Modify: `rust/spikes/leptos-tiptap-spike/src/lib.rs`
- Modify: `rust/spikes/leptos-tiptap-spike/generated/island/snippets/leptos-tiptap-c355e6ec24c3df4c/src/js/generated/tiptap_paragraph.js`
- Reuse/Modify: `wolai-frontend/src/lib/mindmap/simple-mind-map-bridge.ts`
- Reuse/Modify: `wolai-frontend/src/lib/mindmap/leptos-mindmap-adapter.ts`
- Reuse/Modify: `wolai-frontend/src/lib/mindmap/mindmap-command-diff.ts`
- Modify: `scripts/task166-mindmap-phase6-block-smoke.js`
- Output: `tmp/task166-mindmap-phase6-block-smoke/*.png`
- Output: `tmp/task166-mindmap-phase6-block-smoke/result.json`
- [ ] 单测覆盖:
- 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` 风格新增或复用
- [x] 确认当前 `tiptap_paragraph.ts` 的 NodeView 是手写 DOM/SVG`renderScene()` 拼接 toolbar、`<button data-testid="mindmap-node">``<path data-testid="mindmap-edge">`
- [x] 确认当前截图 `/mnt/Data1T/mnote/tmp/task166-mindmap-phase6-block-smoke/03-after-reload.png``image copy 70.png``image copy 60.png` 的真实工作台形态差距明显。
- [x] 确认当前 `wolai-frontend/src/lib/mindmap/simple-mind-map-bridge.ts` 已有真实 `new MindMap(...)` bridge,但没有接入 leptos-tiptap NodeView 主链。
- [x]`tiptap_paragraph.ts` 中的 `renderScene()` 手写节点/连线逻辑改为只创建 runtime mount element、状态层和错误层。
- [x] 建立 leptos-tiptap 可加载的 browser-side adapter 入口,复用 `createLeptosMindmapAdapter()`,避免在 generated bridge 中复制第二套 simple-mind-map 初始化逻辑。
- [x] adapter 初始化时请求 `mindmap.simple_mind_map_scene.get`,把 projection 的 `root/layout/theme/themeConfig/view/config` 传给真实 `simple-mind-map`
- [x] adapter 内部真实调用 `new MindMap({ el, data, layout, theme, themeConfig, viewData, config })`
- [x] NodeView 销毁时调用 bridge `destroy()`,清理 runtime、监听器和 DOM。
- [ ] `data_change` / `view_data_change` 通过 `mindmap-command-diff.ts` 转换为 kernel command 或 `compatPayload.patch`,再调用 `mindmap.command.apply`
- [x] command 成功后刷新 adapter projection 或同步 runtime revision;失败时显示 `command_failed`,不得伪装保存成功。
- [ ] toolbar 的“子节点 / 同级节点 / 删除 / 回根 / 缩放”优先调用 runtime command,再经 command bridge 回写 kernel。
- [x] 右侧栏和底部栏只保留第一阶段 chrome,不再占据画布主要宽度;视觉对齐 `image copy 60.png` 的内嵌 KMind 工作台。
- [x] 删除或降级旧 `.mnote-mindmap-node`、手写 `mindmap-edge`、固定 `svg viewBox` 成为 fallback/debug,不作为默认主链。
- [x] 更新 smoke:禁止以手写 `.mnote-mindmap-node``path[data-testid="mindmap-edge"]` 作为成功条件。
- [x] 更新 smoke:断言真实 runtime mount 内存在 simple-mind-map 生成的 SVG/HTML 层、节点布局非零、连线来自 runtime 渲染层。
- [x] 更新 smoke:保存插入后、编辑后、reload 后截图,并人工对比 `image copy 60.png` / `image copy 70.png` 的工作台结构。
- [x] 运行 `cd /mnt/Data1T/mnote/wolai-frontend && pnpm test -- mindmap` 或等效 TS 单测,验证 adapter/diff 合同。
- [x] 重新构建 leptos-tiptap island。
- [x] 运行 `node scripts/task166-mindmap-phase6-block-smoke.js`
- [x] 只有 smoke 结果 JSON、截图和代码检查都证明真实 `simple-mind-map` runtime 已接入后,才允许勾选本 Task 完成。
备注:2026-05-10 首轮真实 runtime cutover 已通过;仍未勾选的 `view_data_change` 持久化与 toolbar 缩放绑定属于后续交互完善项,不能反向否定真实 runtime 已接入。
### Task 13: UI Shell Reuse 后续收口
## 依赖关系
**Current Checklist:**
- `design/06-mindmap/process/6-mindmap-phase6-leptos-ui-shell-reuse-checklist-v1.md`
- [ ] 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 形成最小闭环后收尾
- [x] 2026-05-11 已将后续 UI 可用度收口迁入独立 checklist,目标是 `leptos-mindmap + simple-mind-map runtime + Leptos/Rust floating overlay UI shell`
- [x] 新 checklist 已完成 Task 5A/5B/6/7/8/9/10/11/12/13 的核心验收:floating overlay、Leptos/Rust shell、toolbar/sidebar/navigator/context menu、command bridge、smoke 截图与旧 React 边界。
- [x] 后续 KMind/lx-doc 视觉细化、图标体系、主题细节和高级面板内容按新 checklist 继续,不再把本旧计划的手写伪画布阶段作为 UI 完成判定。
## 退出标准
- [ ] 独立导图页主读链已不是 `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,而不是只换了命名
> 2026-05-10 现实复核后,本段退出标准重新打开。旧勾选只代表伪画布阶段的最小闭环,不能作为最终完成依据。
- [x] 文档页 `/` 插入 mindmap block 后,默认主链是 `leptos-tiptap` NodeView + 真实 `leptos-mindmap` adapter
- [x] 插入后截图可见 KMind-like 真实根节点、分支节点、概要和连线,且节点/连线由 `simple-mind-map` runtime 渲染。
- [x] `simple-mind-map` runtime 只作为 editor adapter,不是 canonical truth。
- [x] 最小节点编辑动作通过 Rust kernel command 回写,reload 后仍存在。
- [x] smoke 输出结构化 JSON 和截图证据,失败信息能定位到 projection、adapter、runtime、command 或 reload 阶段。
- [x] 旧 React/simple-mind-map block 不在 Phase 6 默认文档页主链内。
- [x] 默认 smoke 不再把手写 `.mnote-mindmap-node` / `path[data-testid="mindmap-edge"]` 当作完成条件。
## 暂不作为阻塞的尾项
- [ ] `Page Aggregate` 写后完整回灌统一 projection
- [ ] page subtree 与正文本地态完全同源
- [ ] tree realtime 全域单 live cache
- [ ] 导图 blob 存储彻底移除
- [ ] 全量画布命令 Rust 化
- [ ] Rust-native mindmap renderer。
- [ ] 完整复杂布局算法 Rust 化。
- [ ] KMind 多根节点。
- [ ] MOC / 文档树导图。
- [ ] 思源块悬浮预览与镜像块。
- [ ] PDF 标注跳转。
- [ ] 导图 blob 存储彻底移除。
- [ ] Page Aggregate 写后完整回灌统一 projection。
- [ ] tree realtime 全域单 live cache。
## 建议执行顺序
- [ ] 第 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 + 设计稿回填
- [x] 第 1 天:Task 1 + Task 2,冻结 projection 和 adapter scene contract。
- [x] 第 2 天:Task 3 + Task 4,审计参考实现并建立 simple-mind-map bridge。
- [x] 第 3 天:Task 5,打通 command bridge 和 adapter diff。
- [x] 第 4 天:Task 6 + Task 7 + Task 8,完成的是手写伪画布最小闭环,不是最终真实 runtime。
- [x] 下一步Task 12,真实 `simple-mind-map` runtime cutover,替换手写 DOM/SVG 画布。
- [x] Task 12 通过后:复核 Task 9 + Task 10 + Task 11,确认 legacy、独立页和验证口径没有回到伪画布。
- 2026-05-11 复核:新 UI shell checklist 已完成旧 React 边界注释、`editor-host-config` 静态断言和 `task166` smoke;默认成功条件为 `mnote-mindmap-editor-root``simple-mind-map-runtime``mindmap-rust-shell`