Files
mnote/bugs/05-editor-mainline/done/5-10-mindmap-filetree-index-single-truth-split-v1.md
T
2026-05-13 22:43:16 +08:00

11 KiB
Raw Blame History

5-10 [done][bug] 文件树思维导图打开污染 index.md 单一真源 v1

更新时间:2026-05-13

分类归属:

  • 05-editor-mainline/done
  • 涉及边界:04-tree-domain/file_tree asset intent03-rust-web/mindmap standalone shell06-mindmap/projection editor

关联文档:

  • /mnt/Data1T/mnote/design/10-review/README.md
  • /mnt/Data1T/mnote/design/10-review/02-frontend-editor-tree-review.md
  • /mnt/Data1T/mnote/design/10-review/04-secondary-domains-and-design-governance-review.md
  • /mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md
  • /mnt/Data1T/mnote/design/04-tree-domain/done/4-2-sidebar-pagetree-filetree-product-interaction-contract-v1.md
  • /mnt/Data1T/mnote/design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md

1. 问题定义

当前文件树中的思维导图文件点击打开后,会进入 standalone /mindmap/{documentId}/{mindmapId} 壳。这个壳会构造一份只包含单个 mindmap block 的临时 editor bootstrap,而不是读取该页面真实的 index.md / Page Aggregate body。

结果是:用户从文件树打开思维导图、编辑、切换页面后,再点击同一页面的 index.md,可能看到只剩思维导图默认内容的页面体验。这里的核心问题不是单次保存失败,而是 index.md 页面正文与文件树里的 mindmap asset 没有明确收口到同一份页面真源。

2. 归类理由

本问题归到 05-editor-mainline,而不是只归到 06-mindmap,理由如下:

  • 用户可见故障发生在文档页壳、文件树点击打开、主编辑区内容切换体验上。
  • 受影响对象是 index.md 页面正文与主编辑区当前内容,而不只是 mindmap runtime 内部编辑能力。
  • design/10-review 已明确当前 Page Aggregate 仍是“主链已切、单一真源收口未完”的状态,本问题正是页面正文、asset view 与 standalone shell 边界未闭合的具体缺陷。
  • 04-tree-domain 需要定义 filetree asset row 的 open intent,但真正承载用户体验和页面真源一致性的 owner 仍是编辑主线。

3. 真实现象

用户复现结论:

  1. 打开文件树中的思维导图文件,编辑后可以正常保存。
  2. 切换页面后,文件树中的思维导图仍能保存。
  3. 切换页面后点击文件树中的 index.md,主编辑区只显示思维导图默认内容,而不是该页面真实正文。

用户判断的根因方向:

  1. 之前对文件树中的思维导图文件点击操作会对主页面产生干扰,应取消该干扰。
  2. 思维导图文件的点击打开应该在主编辑区弹出新页面或新 tab,类似 VS Code,而不是污染 index.md
  3. 当前文件树 index.md 与其中的思维导图没有只持有一份真源,需要收口成单一来源。

4. 证据

设计审查证据:

  • design/10-review/README.md 明确当前最需要收口的是 Page Aggregate 单一真源边界、tree realtime/live cache、side effect 一致性,以及 legacy/compat/fallback 退场边界。
  • design/10-review/02-frontend-editor-tree-review.md 的 F-01 指出 Page Aggregate 读链已 Rust-first,但客户端仍有本地 aggregate reducer 持有标题、正文、设置、子树快照等临时真相,页面域单一真源尚未闭环。
  • design/10-review/04-secondary-domains-and-design-governance-review.md 的 Mindmap 段落指出 standalone mindmap shell 已存在,但 compat 数据层仍承载导图数据,不应被描述为长期 canonical truth。

代码锚点:

  • rust/crates/mnote-web/src/ssr/pages/layout.rs:1100 定义 buildMindmapOpenPath(documentId, assetId),直接生成 /mindmap/{documentId}/{assetId}
  • rust/crates/mnote-web/src/ssr/pages/layout.rs:1128:1134openConvexAssetFromFileTree 在识别到 mindmap asset 后直接 window.location.assign(buildMindmapOpenPath(documentId, assetId))
  • rust/crates/mnote-web/src/ssr/pages/layout.rs:1180:1182tree.asset.open 统一交给 openConvexAssetFromFileTree
  • rust/crates/mnote-web/src/ssr/pages/layout.rs:3551:3555 文件树点击 index/document/markdown 时导航到文档页,但非文档 row 且有 assetId 时 dispatch tree.asset.openmindmap asset 因而进入 standalone mindmap 路由。
  • rust/crates/mnote-web/src/routes/mindmap_shell.rs:75:104 standalone mindmap shell 构造临时 editor_bootstrap.content,内容只有一个 mnoteBlockType: "mindmap" 的 paragraph block。
  • rust/crates/mnote-web/src/routes/mindmap_shell.rs:105:123 standalone contract 标记为 mnote.mindmap_shell.v1projection source 仍是 compat-blob
  • wolai-frontend/src/components/sidebar/sidebar-navigation.ts:21:33 的 React 侧 buildSidebarMindmapOpenTargetmain 模式下返回 /documents/{documentId},只有 sidebar 模式才打开 /mindmap/{documentId}/{mindmapId};这说明至少已有一条路径表达了“主编辑区不应直接进入 standalone mindmap shell”的倾向。

5. 当前判断

当前更像是三条路径混在一起:

  1. index.md 应代表页面正文,并由 Page Aggregate body / page command 持有。
  2. 文件树中的 mindmap asset 应代表页面内某个 mindmap block 关联的资源视图或编辑入口。
  3. standalone /mindmap/{doc}/{mindmap} 是过渡兼容壳,应作为独立编辑页或调试/弹出入口,不应替代主编辑区的 index.md 真源。

因此,即使 mindmap asset 自己可以保存,也不能说明 index.md 与 mindmap block 已经同源。当前故障的关键是:打开 asset 的动作把用户带进了会伪造临时正文的 shell,导致页面主编辑体验看起来像 index.md 被替换成默认 mindmap。

6. 建议修复方向

阶段一:立即止血

  1. 取消文件树 mindmap asset 点击直接 window.location.assign('/mindmap/{doc}/{asset}') 的主路径行为。
  2. 点击 index.md 必须始终打开真实 /documents/{documentId}?treeView=filetree,并读取真实 Page Aggregate body。
  3. mindmap asset 点击只能发出明确的 asset-open intent,不应改写当前页面正文、不应伪造 index.md 内容、不应让主编辑区误认为当前正文就是 standalone bootstrap。
  4. 保留 /mindmap/{doc}/{asset} 作为显式独立页面入口时,应只用于新窗口、新 tab、调试入口或明确的 asset editor,不作为普通文件树主点击默认行为。

阶段二:主编辑区 asset tab

建议把文件树里的 mindmap asset 打开为主编辑区内的 asset tab / preview,而不是替换 index.md

  • 标签形态类似 index.md | mindmap.json
  • index.md tab 只显示真实页面正文。
  • mindmap.json 或 mindmap asset tab 使用 { documentId, mindmapId } 加载 mindmap projection。
  • asset tab 保存只写 mindmap command / projection 对应资源,不创建第二份页面正文。
  • 关闭 asset tab 后回到 index.md,正文内容应保持不变。

阶段三:Page Aggregate 与 block-asset 单一真源

长期应把 mindmap asset 与页面正文 block 关系收口到 Page Aggregate / kernel projection

  1. 页面正文中的 mindmap block attrs 持有稳定 mindmapId
  2. 文件树 mindmap asset row 来自同一份 page aggregate/resource projection,而不是另起一套对象真相。
  3. mindmap asset 的删除、恢复、移动、重命名应同步维护 page body block 与 resource projection 的关系。
  4. standalone shell 如果继续存在,也必须声明自己是 asset editor,不再构造会被误认为 index.md 的页面正文 bootstrap。

7. 建议 smoke 验证

修复完成后至少补一条文件树 mindmap 回归 smoke

  1. 登录真实测试账号。
  2. 新建测试页面,插入或生成一个 mindmap block,记录 documentIdmindmapId
  3. 从文件树点击该页面下的 mindmap asset。
  4. 断言主编辑区没有导航到会污染 index.md 的 standalone bootstrap;如果打开 asset tab,则 tab 标识与 index.md 分离。
  5. 在 mindmap 中输入一段较长中文内容并保存。
  6. 切换到其它页面,再从文件树点击原页面 index.md
  7. 断言 index.md 仍显示真实页面正文,且页面内 mindmap block 仍引用同一个 mindmapId
  8. 再打开 mindmap asset,断言刚才输入的长中文内容仍存在。

需要保留一条负向断言:

  • 普通文件树主点击 mindmap asset 不应触发 window.location.assign('/mindmap/{doc}/{asset}') 并替换当前文档页正文。

8. 修复证据

本问题已按 4-245-12 收口:

  • rust/crates/core-protocol/src/kernel.rs 增加 KernelObjectIdentity / KernelBlockAssetRelation
  • rust/crates/bridge-runtime/src/lib.rs 的 file tree projection 为 index.md、mindmap、OnlyOffice、代码附件、普通附件输出不同 resourceMeta.objectIdentity
  • rust/crates/mnote-web/src/tree_shell/filetree_renderer.rsrust/crates/mnote-web/src/routes/tree.rsrust/crates/mnote-web/src/ssr/pages/layout.rsobjectIdentity 下发到文件树 DOM 与 tree.asset.open intent。
  • /mindmap/{documentId}/{mindmapId}rust/crates/mnote-web/src/ssr/pages/mindmap.rs 明确标记为 data-mnote-object-editor="mindmap"data-mnote-object-identity="resource:mindmap:{documentId}:{mindmapId}"
  • rust/crates/mnote-web/src/routes/mindmap_shell.rs 的 standalone bootstrap 使用 __mindmap_object__:{documentId}:{mindmapId},不复用真实页面正文草稿身份。

验证命令:

cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol --test resource_tree_contract -- --nocapture
cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime file_tree_projection -- --nocapture
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web file_tree_projection -- --nocapture
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web mindmap -- --nocapture
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web documents_save_route_executes_page_body_save_command -- --nocapture
cd /mnt/Data1T/mnote/rust/spikes/leptos-tiptap-spike && cargo test standalone_mindmap_object_uses_isolated_draft_identity -- --nocapture
node scripts/task112-tree-rust-family-regression-smoke.js
node scripts/task169-mindmap-realtime-smoke.js

task169 已覆盖:文件树 mindmap asset 打开、长中文编辑、保存、切页、从文件树回 index.md、再次打开 mindmap,且未触发 page.body.save 污染链。

9. 流转条件

当前状态:done

只有在以下条件都满足后,本文才能移动到 bugs/05-editor-mainline/done/

  1. 文件树 mindmap asset 普通点击不再污染 index.md 主编辑区。
  2. index.md 与页面内 mindmap block 的关系有明确单一真源,至少不再出现切页后只剩默认导图的现象。
  3. /mindmap/{doc}/{asset} 的产品定位被明确为独立页、新 tab、debug 或 asset editor,不再被文件树主路径误用。
  4. smoke 覆盖长中文 mindmap 编辑、保存、切页、回到 index.md、再次打开 asset 的完整链路。
  5. 浏览器复测确认 mindmap 编辑可用性优先,不因实时刷新或路由切换导致闪烁、跑位或内容错乱。