- 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
24 KiB
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 已经做对的部分
当前代码已经有一些方向是正确的:
kernelSidebarProjectionkernelSidebarTreeSidebar主树开始以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 前端框架
优先关注:
LeptosDioxusYew
本文的建议顺序是:
LeptosDioxusYew
原因不是抽象喜好,而是贴合度:
- 你们已经在走 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
只承载:
nodeedgesubtreeaudit
2. Tree Projection Layer
专门输出:
sidebar_treepage_treefile_tree
固定输出应包括:
projection_idroot_node_iditemsedgessort keyexpand hintcapability 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 页面树和文件树的正确关系
在新架构里,这两者不该是并列的两套不同系统,而应是:
页面树
只关心:
workspacefolderpagesection
文件树
在页面树骨架上再纳入:
assetmindmaptablebookpdf- 未来的
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 组合方式collapsible、scroll area、menu、overlay、可访问性细节- 树域壳层需要的基础交互组件
限制:
- 它不是完整树组件
- 不能直接替代页面树 / 文件树的 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用来补树域壳层 primitivesheadless-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_idnode_idparent_node_idnode_typeprojection_kinddepthpositiontitleicon_hintexpandableexpanded_by_defaultcapabilitiesresource_meta
其中:
- 页面树主要消费
page/folder/section - 文件树再多消费
asset/mindmap/table/book/pdf
10.2 Tree Command Protocol
建议统一成:
tree.node.createtree.subtree.movetree.node.renametree.node.archivetree.node.restoretree.asset.attachtree.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_idnode_idparent_node_idnode_typeprojection_kinddepthpositiontitlecapabilitiesresource_meta
- 树域主路径已统一到
kernelSidebarTree -> page_tree projection -> visible rows这一协议家族 - 把
sidebar_tree、page_tree、file_tree的共用字段与差异字段正式写成共享类型/文档,不再只散落在前端映射代码里 - 定义并冻结
file_tree扩展字段:resource_kindasset_kindicon_hintexpandableexpanded_by_default
- 第一批树域命令已经收口到共享 command client:
documents.createdocuments.title.updatedocuments.movedocuments.deletedocuments.restoredocuments.purgedocuments.embeddocuments.copy_tree
- 冻结树域 command protocol 的长期命名面,并补齐
documents.* -> tree.*的兼容映射说明 - 明确哪些状态属于 projection,哪些状态只能留在 UI 本地,并形成 Rust/前端共识文档:
expandedselectedhoverfocusdraggingdrop 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 B:Rust route 与 projection 输出
这一阶段的目标是让 mnote-web 成为树域 projection 与 command 的正式出口,而不是继续让前端自己拼树。
当前状态:COMPLETED(Rust route、projection mapper、契约测试与兼容边界已进入正式主线)
完成 checklist
mnote-web已具备树域相关 route:kernel projection routekernel subtree routetree command routetree shell route
page_tree已经是当前页面树 / 文件树 / picker 的共同协议骨架/api/tree/commands已能承接第一批树域命令并映射到 Rust/Convex bridge 主链- route 已带 request/trace 上下文与基础测试,不再只是占位骨架
- 把树域读取出口补成更明确的正式 projection route 族:
sidebar_treepage_treefile_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 中剥离为独立、可验证、可持续替换的正式执行面。
当前状态:COMPLETED(Leptos 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_tree与file_tree两种渲染模式共用同一 renderer/surface 家族 - 支持 picker 场景复用同一 tree shell 的轻量模式
- 保持正式 UI 壳不持有结构真相,只持有局部交互状态
- 去掉主路径对
iframe + postMessage的依赖,把它降回纯兼容/调试用途
11.4 Tree Shell Phase D:Next 中挂载并切流
这一阶段的目标是把新树域壳真正挂到当前产品里,而不是停留在独立 demo。
当前状态:COMPLETED(Sidebar / 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 状态
长期优化建议
- 继续把
Leptosscaffold 深化为更完整的 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。