Files
mnote/design/04-tree-domain/done/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md
T
lix-2026 5f97800489 chore: align local-first control plane and editor fixes
- wire SQLite control-plane access/session paths into Rust web local-folder routes

- preserve local Markdown attachment semantics across upload, reload, and secondary-pane resource tabs

- refresh design governance docs, Reasonix task templates, and bug records

- retire root .mcp.json local MCP config
2026-05-23 23:38:42 +08:00

24 KiB
Raw Blame History

4 [done] Sidebar / 页面树 / 文件树 Rust Web 重构方案 v1

更新时间:2026-04-22

当前优先级入口:

  • /mnt/Data1T/mnote/design/01-05-current-priority-overview.md

关联文档:

  • /mnt/Data1T/mnote/design/01-tree-first-graph-kernel/reference/1-tree-first-graph-kernel-v1.md
  • /mnt/Data1T/mnote/design/01-tree-first-graph-kernel/reference/1-1-tree-first-graph-kernel-checklist-v2.md
  • /mnt/Data1T/mnote/design/03-rust-web/done/3-2-tree-first-graph-kernel-phase3-task-breakdown-v1.md
  • /mnt/Data1T/mnote/design/90-reference/90-2-yemianshu.md
  • /mnt/Data1T/mnote/design/90-reference/90-1-filetree.md
  • /mnt/Data1T/mnote/design/old/04-tree-domain/process/4-1-sidebar-pagetree-filetree-product-gap-analysis-v1.md

1. 文档目的

这份文档回答的问题不是:

  • “当前 Sidebar 再怎么局部优化一下”

而是:

tree-first graph kernel 前提下,是否应该把 Sidebar / 页面树 / 文件树直接重构为一个独立的 Rust Web 子系统。

本文的结论是:

可以,而且长期上这是正确方向;但重构对象不是“一个更快的树组件”,而是“一个直接消费 kernel projection 的独立树域执行面”。

也就是说,目标不是把当前 React 树组件换个语言重写,而是:

  • 用 Rust 主导 tree projection
  • 用 Rust Web 主导 tree query / command
  • 让页面树 / 文件树只作为 kernel 的树投影
  • 再决定 UI 壳是否也迁到 Rust 家族

2. 必须遵守的前提:树不是 UI 数据,而是 kernel 投影

这份方案必须完全服从:

里面已经固定的几条原则。

2.1 树是主骨架

当前长期架构已经冻结为:

  • 树是主骨架
  • 图是横向扩展
  • Sidebar / 页面树 / 文件树 / 阅读流 / Mindmap 都只是 projection

所以这里的页面树 / 文件树不能再被定义为:

  • 前端自己拼出来的导航数据

它们必须被定义为:

  • tree-first graph kernel 的树投影

2.2 页面树和文件树不是两套真相

在新架构里:

  • 页面树不是独立系统
  • 文件树也不是独立系统

两者都来自同一个 kernel,只是投影范围不同:

  • page_tree
    • page / section / 页面层级为主
  • file_tree
    • 在页面层级基础上,把 asset / mindmap / table / 未来 book / pdf 一起投影出来

2.3 Sidebar 是壳,不是事实源

Sidebar 长期不应再被理解为:

  • “一个左侧导航 React 组件”

而应理解为:

  • “tree projection 的承载壳”

固定边界应是:

  • kernel 持有真相
  • projection 输出树
  • Sidebar 只负责显示和交互

3. 当前现状

3.1 已经做对的部分

当前代码已经有一些方向是正确的:

  • kernelSidebarProjection
  • kernelSidebarTree
  • Sidebar 主树开始以 kernelSidebarTree 为来源
  • Rust runtime 和 mnote-web 已开始承接 Sidebar 相关 projection 主链

对应代码包括:

3.2 还没做完的部分

当前真正的问题是:

  • 主 Sidebar 仍是超大客户端组件
  • 文件树仍然主要在前端继续加工 row model
  • move-embed picker 等兼容域仍保留旧 buildDocumentTree(...)
  • 页面树和文件树还没有彻底统一为稳定的 kernel projection family

这说明:

现在的瓶颈不只是“UI 重”,而是“树域仍然没有形成独立、稳定、可替换的执行边界”。


4. 对参考资料的判断

4.1 /design/cankao/yemianshu.md/design/cankao/filetree.md 能参考什么

这两份参考有价值,但要分层使用。

适合借鉴的部分:

  • 树形系统的分层
  • VS Code / Notion 风格交互
  • 折叠、展开、拖拽、懒加载、多选、右键菜单

不适合直接拿来落当前 Web 主线的部分:

  • Ratatui / Cursive / TUI 组件
  • egui / iced / Fyrox / GPUI 这类桌面 GUI 组件

原因很简单:

  • 这些更适合终端或原生桌面
  • 当前 mnote 的主线是 Web + Rust Web + kernel projection

所以它们更适合做:

  • 交互语义参考

而不适合做:

  • 当前 Web 主线的直接实现模板

4.2 更适合作为直接参考的方向

如果这次真要把 Sidebar / 页面树 / 文件树往 Rust 家族重构,应该看两类参考:

A. Rust Web 前端框架

优先关注:

  • Leptos
  • Dioxus
  • Yew

本文的建议顺序是:

  1. Leptos
  2. Dioxus
  3. Yew

原因不是抽象喜好,而是贴合度:

  • 你们已经在走 Rust kernel + Rust Web + server-first
  • 这时最有价值的是“Rust Web 组件 + server integration + 渐进切流”
  • 不是终端树,也不是桌面树

B. 成熟 Web Tree 的行为模型

即使最终决定用 Rust 家族重写,交互模型也应该优先参考成熟 Web Tree 的做法:

  • headless tree 思路
  • VS Code Explorer 的 row model
  • 大树虚拟化
  • DnD 状态机
  • selection / focus / keyboard 模型

这里学的是:

  • 行为模型

不是:

  • 必须沿用 React

5. 结论:可以直接重构,但应定义成独立大任务

我的明确结论是:

可以直接把 Sidebar / 页面树 / 文件树作为独立大任务重构,而且长期上应该这样做。

但这个重构不能被理解为:

  • sidebar.tsx 翻译成 Rust

而应被理解为:

  • 把树域从旧前端壳里剥离出来
  • 形成一个独立的 Rust Web tree shell

也就是:

  • 独立 route / shell
  • 独立 projection protocol
  • 独立 command protocol
  • 独立 UI state 边界

这个任务应该单独成立,而不是继续藏在 Kernel Phase 4 的一句话里。


6. 目标架构

6.1 新的树域分层

长期建议把树域拆成五层:

1. Kernel Truth

只承载:

  • node
  • edge
  • subtree
  • audit

2. Tree Projection Layer

专门输出:

  • sidebar_tree
  • page_tree
  • file_tree

固定输出应包括:

  • projection_id
  • root_node_id
  • items
  • edges
  • sort key
  • expand hint
  • capability flags

3. Tree Command Layer

只处理树域命令:

  • create page
  • move subtree
  • attach asset
  • reorder sibling
  • archive / restore
  • open node

4. Tree Shell

树域的独立承载壳,只负责:

  • 拉 projection
  • 发 command
  • 维护局部 UI 状态

5. Tree Renderer

最终的可视组件,只负责:

  • row 渲染
  • 虚拟化
  • 选中
  • 展开
  • 右键菜单
  • DnD feedback

6.2 页面树和文件树的正确关系

在新架构里,这两者不该是并列的两套不同系统,而应是:

页面树

只关心:

  • workspace
  • folder
  • page
  • section

文件树

在页面树骨架上再纳入:

  • asset
  • mindmap
  • table
  • book
  • pdf
  • 未来的 index_node

也就是说:

文件树不是“另建一棵树”,而是“同一棵树的更宽对象投影”。

这非常符合 tree-first graph kernel 的定义。


7. 为什么建议用 Rust Web 子系统,而不是继续堆在当前 Sidebar 里

7.1 当前 Sidebar 太大

现在的 Sidebar 不只是树:

  • 搜索入口
  • 成员
  • 分享
  • 回收站
  • 资源操作
  • 页面树
  • 文件树
  • 各种本地对话框

这会导致:

  • 状态膨胀
  • 切页参与重渲染
  • 树逻辑和业务逻辑缠在一起

7.2 树域已经足够大,可以独立成系统

页面树 / 文件树本身已经有:

  • 自己的数据协议
  • 自己的 row model
  • 自己的拖拽系统
  • 自己的选择模型
  • 自己的上下文菜单
  • 自己的资源挂载逻辑

这已经不是一个小组件,而是一个完整子系统。

7.3 独立之后更符合后续迁移

如果现在就把它切成独立树域子系统,后续:

  • Mindmap
  • 阅读页结构树
  • 搜索结构结果
  • Book / PDF 子树

都可以复用同一套 projection / renderer 协议。


8. 技术路线选择

8.1 方案对比

方案 A:继续 React,只换数据层

优点:

  • 风险最低
  • 最快收口旧 helper

缺点:

  • 树域仍留在旧前端壳内
  • 不能完成“Rust 主执行面”这一步

方案 B:独立 Rust Web tree shell,当前主站只挂载它

优点:

  • 可以保留整体产品双栈过渡
  • 树域先行 Rust 化
  • tree-first graph kernel 最一致

缺点:

  • 需要额外处理嵌入、路由、样式、事件桥接

方案 C:直接整站前端重写

优点:

  • 理论上最终最纯

缺点:

  • 范围失控
  • 风险过高
  • 与当前阶段目标不匹配

8.2 当前建议

本文明确建议:

选方案 B:把 Sidebar / 页面树 / 文件树做成独立 Rust Web tree shell。


8.3 Rust Web 框架建议

当前优先建议:

第一选择:Leptos

原因:

  • 更贴近 Rust 全栈 / server-first
  • 更适合和 axum / mnote-web 的方向合并
  • 适合做“树域先行”的渐进式替换

第二选择:Dioxus

原因:

  • 跨 Web / Desktop 能力强
  • 如果未来想把树域同时复用到桌面壳,会有价值

第三选择:Yew

原因:

  • 能做,但相对不如前两者贴合当前迁移方向

所以这份方案的推荐结论是:

树域独立重构时,优先按 mnote-web + Leptos 设计。


8.4 GitHub 参考池

这里不再按“有没有现成 Rust Notion 成品”来选参考,而是按三个层级来选:

  • Rust Web 承载框架
  • 树域 UI primitives
  • 树行为模型与产品结构参考

A. 直接可参考:Rust Web 主路线

leptos-rs/leptos

GitHub

适合借鉴:

  • Rust + SSR + islands 的 Web 承载方式
  • axum 风格服务端组合
  • 渐进式切流,而不是一次性整站替换

为什么适合当前方案:

  • 当前 mnote-web 已经是 Rust Web 接入点
  • 树域后续如果独立成 shell,最需要的是“Rust Web 承载能力”,不是单独一个树控件

结论:

  • 这是树域 Rust Web 重构的第一参考。

DioxusLabs/dioxus

GitHub

适合借鉴:

  • Rust 组件化 UI
  • Web / Desktop 共享思路

限制:

  • 更偏多端壳能力
  • 与当前 mnote-web + axum 路线的贴合度仍低于 Leptos

结论:

  • 可作为备选路线参考,但不是当前首选。

yewstack/yew

GitHub

适合借鉴:

  • Rust Web 组件化基本能力

限制:

  • 能做,但对你们当前 server-first 与渐进切流路线支持感不如 Leptos

结论:

  • 保留为第三选择,不作为当前主实现模板。

B. 直接可参考:树域 UI primitives / 组件层

cloud-shuttle/radix-leptos

GitHub

适合借鉴:

  • Leptos 生态下的 UI primitives 组合方式
  • collapsiblescroll areamenuoverlay、可访问性细节
  • 树域壳层需要的基础交互组件

限制:

  • 它不是完整树组件
  • 不能直接替代页面树 / 文件树的 row model 与状态机

结论:

  • 适合作为树域 shell 的基础件参考。

thaw-ui/thaw

GitHub

适合借鉴:

  • Leptos 组件组织方式
  • 通用面板、按钮、菜单等基础 UI

限制:

  • 更像通用组件库
  • 对树域协议、树行为模型帮助有限

结论:

  • 适合作为辅助 UI 库参考,不是树域核心参考。

KoVal177/leptos-column-browser

GitHub

适合借鉴:

  • Rust Web 下的层级导航
  • 异步懒加载子节点
  • 多层树浏览器的交互拆分

限制:

  • 项目较新、体量小
  • 更接近 column browser,不是当前 Sidebar 单栏树的完整模板

结论:

  • 适合借鉴 provider / async loading / column navigation 思路,不适合直接照搬。

C. 高价值参考:树行为模型与产品结构

lukasbach/headless-tree

GitHub

适合借鉴:

  • row model
  • selection / focus / keyboard 模型
  • DnD 状态机
  • 虚拟化树行为拆分

为什么值得看:

  • 你们后续真正难的不是“画一棵树”,而是把树行为从具体 UI 框架中抽出来
  • 这正对应 tree-first graph kernel -> projection -> shell -> renderer 的分层思想

限制:

  • 不是 Rust
  • 不能直接进入实现层

结论:

  • 非常适合作为树行为模型参考。

AppFlowy-IO/AppFlowy

GitHub

适合借鉴:

  • “工作区 / 页面树 / 文档”这类产品结构
  • Rust 统一管理部分内核能力的思路
  • Notion 类产品如何收敛页面树语义

限制:

  • 主前端不是你们要走的 Leptos Web 路线
  • 不能作为当前树域 Web 重构的直接模板

结论:

  • 适合作为产品结构与边界参考,不适合作为实现模板。

toeverything/AFFiNE

GitHub

适合借鉴:

  • Notion / knowledge base 类产品的页面树体验
  • 页面、知识库、画布等多视图并存时的交互组织

限制:

  • 技术栈不是 Rust
  • 更适合借鉴交互与产品层结构

结论:

  • 适合作为页面树 / 知识库产品交互参考。

8.5 外部参考的使用原则

为了避免“看了很多仓库,但最后没有真正推进”,这里固定使用原则:

  • Leptos 用来确定树域 Rust Web shell 的主承载路线
  • radix-leptos / thaw 用来补树域壳层 primitives
  • headless-tree 用来借 row model、selection、keyboard、DnD 的行为模型
  • AppFlowy / AFFiNE 用来参考产品层交互与树域边界
  • 不引入终端树、桌面树、TUI/GUI 框架作为当前 Web 主线实现模板

也就是说,后续不是“找一个仓库直接替换 Sidebar”,而是:

把外部参考拆成承载层、基础件层、行为模型层、产品结构层,分别吸收。


9. 重构范围

9.1 本次应纳入的范围

  • Sidebar 主树域
  • 页面树
  • 文件树
  • move / embed picker 的树域部分
  • 树域 command
  • 树域 projection route

9.2 本次不纳入的范围

  • 搜索主面板
  • AI 主面板
  • Mindmap 主画布
  • BlockNote 编辑器
  • 旧前端壳整体删除

原因:

  • 这些属于后续 phase
  • 本次只做树域切换,保持边界清晰

10. 新协议定义建议

10.1 Tree Projection Protocol

建议统一成一套 tree row 协议,而不是 page tree、file tree 各自手写结构。

最小字段建议:

  • row_id
  • node_id
  • parent_node_id
  • node_type
  • projection_kind
  • depth
  • position
  • title
  • icon_hint
  • expandable
  • expanded_by_default
  • capabilities
  • resource_meta

其中:

  • 页面树主要消费 page/folder/section
  • 文件树再多消费 asset/mindmap/table/book/pdf

10.2 Tree Command Protocol

建议统一成:

  • tree.node.create
  • tree.subtree.move
  • tree.node.rename
  • tree.node.archive
  • tree.node.restore
  • tree.asset.attach
  • tree.asset.detach

这些 command 最终应映射到 kernel command,而不是直接绑在前端 UI 行为上。


11. 分阶段实施建议

下面这组 phase 不再按“最初方案假设”维护,而按 2026-04-18 当前仓库代码状态 重写。

这意味着:

  • 已经落地的部分要明确勾掉
  • 还没真正开始的部分不能因为存在实验壳就误写成已完成
  • 如果长期目标已经固定为“主执行面最终也迁到 Rust 家族”,那么当前主线应直接推进 Phase C + Phase D

11.1 Tree Shell Phase A:协议冻结

这一阶段的目标不是开始写 UI,而是把树域协议冻结到后续不会反复返工。

当前状态:COMPLETED(协议、共享类型、状态边界与 contract tests 已完成封板)

完成 checklist

  • page_tree 的现有字段提升为当前协议基线:
    • row_id
    • node_id
    • parent_node_id
    • node_type
    • projection_kind
    • depth
    • position
    • title
    • capabilities
    • resource_meta
  • 树域主路径已统一到 kernelSidebarTree -> page_tree projection -> visible rows 这一协议家族
  • sidebar_treepage_treefile_tree 的共用字段与差异字段正式写成共享类型/文档,不再只散落在前端映射代码里
  • 定义并冻结 file_tree 扩展字段:
    • resource_kind
    • asset_kind
    • icon_hint
    • expandable
    • expanded_by_default
  • 第一批树域命令已经收口到共享 command client
    • documents.create
    • documents.title.update
    • documents.move
    • documents.delete
    • documents.restore
    • documents.purge
    • documents.embed
    • documents.copy_tree
  • 冻结树域 command protocol 的长期命名面,并补齐 documents.* -> tree.* 的兼容映射说明
  • 明确哪些状态属于 projection,哪些状态只能留在 UI 本地,并形成 Rust/前端共识文档:
    • expanded
    • selected
    • hover
    • focus
    • dragging
    • drop target
  • 已有 projection / rows / sidebar-data / tree-stream 基础测试,避免主路径再次回到本地 synthetic projection
  • 给协议补一组更明确的 fixture / contract tests,覆盖 sidebar_tree / page_tree / file_tree / command protocol

11.2 Tree Shell Phase BRust route 与 projection 输出

这一阶段的目标是让 mnote-web 成为树域 projection 与 command 的正式出口,而不是继续让前端自己拼树。

当前状态:COMPLETEDRust route、projection mapper、契约测试与兼容边界已进入正式主线)

完成 checklist

  • mnote-web 已具备树域相关 route
    • kernel projection route
    • kernel subtree route
    • tree command route
    • tree shell route
  • page_tree 已经是当前页面树 / 文件树 / picker 的共同协议骨架
  • /api/tree/commands 已能承接第一批树域命令并映射到 Rust/Convex bridge 主链
  • route 已带 request/trace 上下文与基础测试,不再只是占位骨架
  • 把树域读取出口补成更明确的正式 projection route 族:
    • sidebar_tree
    • page_tree
    • file_tree
  • file_tree 从“前端 adapter 拼装”继续下沉为 Rust 侧直接输出的 projection
  • 在 Rust 侧补齐 asset / mindmap / table / book / pdf 的 projection 映射层
  • 明确树域 command route 的长期协议面,避免一直停留在 documents.* 兼容命名
  • 把鉴权、错误码、兼容 fallback、trace、workspace 解析补成完整 route 契约
  • sidebar_tree / page_tree / file_tree / commands 补齐 route tests、fixture tests、兼容入口 tests
  • 让 Next 侧进一步只保留 transport / compat,不再残留结构真相拼装逻辑

11.3 Tree Shell Phase C:正式树域壳

这一阶段的目标是把树域从旧 Sidebar 中剥离为独立、可验证、可持续替换的正式执行面。

当前状态:COMPLETEDLeptos scaffold、Rust tree_shell 模块、统一 surface 与 iframe 主路径下线已完成)

完成 checklist

  • 已经证明“树域可以从旧 sidebar.tsx 中剥离成独立 Rust Web 壳”,而不是只能留在 React 组件里
  • Leptos 搭建最小正式 tree shell
    • tree loader
    • command dispatcher
    • local UI state store
  • 接入可验证的最小 renderer 主干与稳定 data-testid
  • 接入展开 / 折叠状态
  • 接入选中 / focus / keyboard 导航
  • 接入 DnD 状态机
  • 接入右键菜单与基础上下文动作
  • 支持 page_treefile_tree 两种渲染模式共用同一 renderer/surface 家族
  • 支持 picker 场景复用同一 tree shell 的轻量模式
  • 保持正式 UI 壳不持有结构真相,只持有局部交互状态
  • 去掉主路径对 iframe + postMessage 的依赖,把它降回纯兼容/调试用途

11.4 Tree Shell Phase DNext 中挂载并切流

这一阶段的目标是把新树域壳真正挂到当前产品里,而不是停留在独立 demo。

当前状态:COMPLETEDSidebar / filetree / picker 默认切流、快速回退与网页 smoke 已完成)

完成 checklist

  • 在当前主站中预留 tree shell 挂载位
  • 用 feature flag 控制新旧树域切换
  • 已经为 Sidebar / 文件树 / picker 提供实验壳挂载接缝
  • 保留快速回退到旧树域实现的开关
  • 已验证“3104 不可用时主站仍可进入页面”,避免实验壳阻塞首屏
  • 让 Sidebar 主树默认进入正式新 shell
  • 再让页面树 / 文件树默认进入正式新 shell
  • 再让 move/embed picker 切到正式新 shell 的轻量模式
  • 补齐切页、展开、拖拽、右键菜单、搜索跳转等高频路径的正式切流回归
  • 记录真实用户流量下的性能指标:
    • 首包
    • 首次可交互
    • 切页延迟
    • 大树展开延迟

11.5 Tree Shell Phase E:收敛旧 helper

这一阶段的目标是收掉旧树域真相层残留,避免双轨长期并存。

当前状态:COMPLETED(主路径统一到同一套 surface/adapter 家族,旧实验壳退出主路径)

完成 checklist

  • 删除 buildDocumentTree(...) 在树域中的最后运行时入口
  • 主路径 consumer 已统一改读 projection protocol,而不是旧对象数组拼树
  • 清理了只服务于旧树域主路径的一批测试、fixture、兼容代码
  • 删除剩余的 page tree / file tree 主路径特殊拼装逻辑
  • 清理旧 Sidebar 超大组件中的树域状态与 helper,把非树逻辑和树逻辑继续拆开
  • 把 picker / 文件树 / 页面树统一到同一套 renderer 或 row adapter 家族
  • 在正式新壳稳定后,下线 iframe + postMessage 的实验树壳主路径职责
  • 更新架构文档、checklist、harness 状态

长期优化建议

  • 继续把 Leptos scaffold 深化为更完整的 Rust-first renderer,但这不再阻塞当前 Sidebar / page tree / filetree 重建完成判定。
  • 持续采集生产环境下的树规模、切页延迟与 fallback 触发频率,把本轮开发机 smoke 指标升级为长期趋势指标。
  • 对 move/embed picker 的搜索结果路径补异步索引可见性指标,但这属于搜索链路优化,不再作为当前树域切流 gate。

12. 验收标准

只有同时满足下面几条,才建议把这次树域 Rust Web 重构视为成立:

  • 页面树与文件树都直接消费 kernel projection
  • Sidebar 不再自己定义树结构真相
  • 树域已有独立 Rust Web shell
  • 至少一条真实用户流量默认进入新 tree shell
  • buildDocumentTree(...) 不再处于主路径
  • move/embed picker 等兼容树域也已切到统一 projection

13. 风险与约束

风险 1

如果在 projection 协议未冻结前就开始重写 UI,容易重写两遍。

风险 2

如果把搜索、AI、Mindmap 一起塞进树域重构,范围会立即失控。

风险 3

如果只重写 UI,不改 projection / command / shell 分层,最终只是“换皮”,不是根治。


14. 最终结论

这次页面树 / 文件树的长期正确方向,不是:

  • 再修补当前 sidebar.tsx
  • 或者简单找一个 Rust 树控件来替换

而是:

tree-first graph kernel 前提下,把树域独立成一个 Rust Web 子系统。

这个子系统应当满足:

  • 树是 kernel projection
  • Sidebar 只是树域壳
  • 页面树和文件树来自同一 truth,不再是两套系统
  • Rust 主导 query / command / projection
  • UI 壳可以逐步迁到 Leptos 一类 Rust Web 前端框架

所以,这次不是“参考某个树组件”,而是:

参考成熟树域行为模型,结合 tree-first graph kernel,把 Sidebar / 页面树 / 文件树整体提升为独立的 Rust Web tree shell。