> 执行状态:本文是 10-review 阶段的树域判断记录,不再作为待办清单直接执行。Resource Tree / File Tree / Page Tree 真源合同已由 `/mnt/Data1T/mnote/design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md` 承接;主编辑区 Object Tab、mindmap object editor、`index.md` 隔离已由 `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-12-main-editor-object-tab-resource-alignment-checklist-v1.md` 承接。后续只需按 `06-execution-checklist-and-acceptance.md` 保留 smoke 防回归。 结论 我基本同意你的方向,但要把“文件树为根源”说得更精确:不应让“文件树 UI 组件”成为真源,而应让 Rust kernel 中的 workspace resource tree / file tree projection 背后 的资源层级 成为页面、附件、mindmap、OnlyOffice 等对象的组织根源。页面树则是从这棵资源/对象树派生出的导航投影,类似快捷方式、收藏视图或文档视图。 对当前 bug 来说,最核心的规则应固定为: 1. index.md 只代表页面正文,即 Page Aggregate body。 2. mindmap 不是主编辑区正文替身,而是页面内某个 block 关联的 object / asset editor。 3. 文件树点击 mindmap 应打开 mindmap object editor,不能被 index.md 吞掉,也不能用临时 mindmap-only bootstrap 覆盖真实页面正文。 4. 页面树不应再持有独立结构真相,只显示“文档导航关系”;文件树显示“资源组织关系”。 主编辑区与插件 主编辑区应该是一个工作区 tab host,而不是只等于文档正文编辑器。index.md 是其中一种 tab,mindmap、OnlyOffice、附件预览、代码附件都是其他 object tab/model。 Siyuan 这里值得借鉴:doc 和 asset 是不同 model/tab,asset 打开不伪装成 doc 正文;插件、widgets、assets、.sy 文档也在存储层分开。mnote 也应采用类似原则:插件/附 件/mindmap 可以嵌入页面,但它们的持久化真源不能变成页面正文本身,只能通过 block-asset relation 被 Page Aggregate 引用。 文件树与页面树 我建议改成三层表述: - Resource Tree:长期 canonical 对象组织树,Rust kernel 持有语义,Convex 只是底层存储。 - File Tree:Resource Tree 的主组织投影,展示页面文件夹、index.md、附件、mindmap、OnlyOffice、代码附件等。 - Page Tree:面向阅读/导航的快捷投影,只显示页面关系,不拥有排序、父子、附件归属的最终真相。 这样你的“文件树为根源”可以落地,但不会和现有 tree-first graph kernel 冲突。更准确地说:文件树背后的 resource hierarchy 是根源,页面树是它的页面导航视图。 建议路线 短期先修当前 bug:保留 /mindmap/{doc}/{asset} 作为 object editor,但彻底隔离草稿 key、保存 command 和 Page Aggregate body,smoke 必须真实网页验证“点击 mindmap、 编辑长中文、保存、切回 index.md、再打开 mindmap 内容仍在”。 中期做主编辑区 asset tab:index.md | mindmap.json | xxx.docx,每个 tab 有自己的 object identity 和保存命令。 长期补 resource projection / block-asset relation:文件树 row、页面内 mindmap block、mindmap projection 必须来自同一关系索引,而不是前端临时按路由拼。 推荐路线 我建议采用“分阶段收口 Resource Tree”的方案,不做一次性大重构。 可选方案有三种: - 只修 mindmap bug:最快,但会继续留下页面树/文件树真源分裂。 - 分阶段建立 Resource Tree -> File Tree/Page Tree projection:最稳,能解决当前 bug,也能承接附件、OnlyOffice、插件对象。 - 全量重写 tree/page/resource 模型:理论干净,但当前工作区和主链都很活跃,风险太高。 我推荐第二种。 阶段 0:冻结合同 先把 design/10-review/05-tree.md 升级为正式设计合同,建议后续移动或补一份到: - design/04-tree-domain/process/4-24-resource-tree-filetree-pagetree-source-contract-v1.md - 或 design/05-editor-mainline/process/5-11-main-editor-object-tab-resource-tree-alignment-v1.md 合同里固定四句话: - Resource Tree 是组织真源。 - File Tree 是资源组织主投影。 - Page Tree 是页面导航投影/快捷视图。 - index.md、mindmap、OnlyOffice、附件是不同 object model,不能互相伪装。 阶段 1:先关闭当前 mindmap bug 目标:文件树 mindmap 仍可编辑保存,但不污染 index.md。 重点文件: - rust/crates/mnote-web/src/ssr/pages/layout.rs - rust/crates/mnote-web/src/routes/mindmap_shell.rs - rust/spikes/leptos-tiptap-spike/src/lib.rs - scripts/task169-mindmap-realtime-smoke.js 验收必须是真实浏览器链路: 1. 从文件树点击 mindmap asset。 2. 进入明确的 mindmap object editor。 3. 输入长中文并保存。 4. 切回同页 index.md,正文仍是 Page Aggregate body。 5. 再打开 mindmap,刚才内容仍在。 6. 确认没有用真实 documentId 写 standalone 草稿 key。 阶段 2:补 Resource Projection 协议 在 Rust 协议层明确资源节点和 block-asset 关系。 建议新增或扩展: - ResourceNode - ResourceKind: page | index | mindmap | attachment | onlyoffice | code - ObjectIdentity: { objectKind, documentId, blockId?, assetId? } - BlockAssetRelation: { documentId, blockId, assetId, assetKind } 落点优先看: - rust/crates/core-protocol/ - rust/crates/bridge-runtime/ - rust/crates/mnote-web/src/tree_shell/ 这个阶段不要求立刻迁移所有数据,只要协议和 projection 能表达清楚。 阶段 3:让 File Tree 从 Resource Tree 派生 当前 file tree 已有 resourceMeta、assetKind、file_tree projection 基础。下一步是收口为: - 页面节点下面固定有 index.md - mindmap/附件/OnlyOffice 都是同一 Resource Tree 下的 child resource - 文件树 row 不再临时从多份前端数据拼出第二真相 同时保留 tree.asset.open,但它必须只表达 object open intent,不直接决定“把谁当正文”。 阶段 4:Page Tree 降级为导航投影 Page Tree 不再拥有资源归属、附件归属、mindmap 归属。它只显示页面导航关系: - 页面标题 - 页面层级 - 快捷入口/收藏/最近打开这类导航语义 如果页面树需要显示某个资源状态,也只读 Resource Projection,不自己维护。 阶段 5:主编辑区变成 Object Tab Host 这是中期关键体验: - index.md tab:加载 Page Aggregate body。 - mindmap.json tab:加载 mindmap projection/command。 - xxx.docx tab:打开 OnlyOffice object editor。 - attachment tab:预览/代码编辑/外部打开。 这样 mindmap 不会被 index.md 吞掉,也不会伪装成正文。 阶段 6:命令面统一 后续命令应逐步收口到: - tree.asset.attach - tree.asset.detach - tree.resource.rename - tree.resource.move - page.body.save - mindmap.command.apply 关键规则:改资源关系走 tree/resource command,改页面正文走 page command,改导图内容走 mindmap command。 落地入口 以上判断已落实到两个主线 done checklist: - /mnt/Data1T/mnote/design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md 用于冻结 Resource Tree / File Tree / Page Tree 的真源合同和 projection / command 边界。 - /mnt/Data1T/mnote/design/05-editor-mainline/done/5-12-main-editor-object-tab-resource-alignment-checklist-v1.md 用于落实主编辑区 Object Tab、mindmap object editor、草稿隔离和真实浏览器 smoke 验收。 执行顺序固定为: 1. 先完成 4-24:协议、projection、command 边界必须能表达 `index.md`、mindmap、OnlyOffice、附件、代码附件的不同 object identity,并确认 Page Tree 只是页面导航投影。 2. 再完成 5-12:主编辑区只消费 4-24 输出的 object identity / resourceMeta / open intent,先关闭 mindmap 污染 `index.md` 的 P0 bug,再推进 Object Tab Host。 3. 验收以真实浏览器 smoke 为准,尤其是 `scripts/task169-mindmap-realtime-smoke.js` 覆盖“打开 mindmap、长中文编辑、保存、切页、回 `index.md`、再开 mindmap”的链路。