对齐 Wolai 侧栏体验并收拢设计入库
This commit is contained in:
+538
@@ -0,0 +1,538 @@
|
||||
# 1-0 [recycle] Tree-First Graph 内核实施清单 v1
|
||||
|
||||
> 更新时间:2026-04-16
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md`
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份清单是基于**新架构前提**重写的。
|
||||
|
||||
此前的长期清单主要站在:
|
||||
|
||||
- Rust Web 层
|
||||
- 页面访问性能
|
||||
- islands 化
|
||||
|
||||
这些视角来拆解任务。
|
||||
|
||||
现在架构前提已经变化:
|
||||
|
||||
> **mnote 的长期中心不再只是“Rust Web + 页面瘦身”,而是“Tree-First Graph Kernel + 多投影 + Rust 主执行面”。**
|
||||
|
||||
因此这份清单的目标是:
|
||||
|
||||
- 先定义统一结构内核要怎么落地
|
||||
- 再定义 Sidebar / 页面树 / 文件树 / Mindmap / 搜索 / AI / BlockNote 如何接入这个内核
|
||||
- 最后再定义 Rust Web 层与前端投影视图如何围绕内核重构
|
||||
|
||||
---
|
||||
|
||||
## 2. 先给结论
|
||||
|
||||
长期路线应改成下面这个顺序:
|
||||
|
||||
1. **先立 kernel**
|
||||
2. **再让 Rust Web 承接 kernel 的 query / command / projection**
|
||||
3. **再让 Sidebar、搜索、Mindmap、AI、阅读页都开始直接消费 kernel**
|
||||
4. **最后才边缘化 `BlockNote` 与旧前端壳**
|
||||
|
||||
这意味着:
|
||||
|
||||
- Rust Web 仍然重要
|
||||
- 页面性能重构仍然重要
|
||||
- 但它们现在都不是第一原则
|
||||
|
||||
第一原则变成:
|
||||
|
||||
> **所有对象最终都要回到统一的 `tree-first graph kernel`,而不是继续各自挂在不同前端壳和对象模型上。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 新的总体验收定义
|
||||
|
||||
只有同时满足下面几条,才能说新架构真正成立:
|
||||
|
||||
- [x] 已存在 Rust 主导的 kernel node / edge 真相层
|
||||
- [ ] 页面树、文件树、Mindmap、RAG 结构索引、AI 结构操作都开始直接依赖 kernel,而不是各有一套真相
|
||||
- [x] Rust Web 层开始作为 kernel 的主 query / command / projection 承载层
|
||||
- [x] Sidebar / 搜索 / 阅读页 / Mindmap / AI 至少有一部分已变成 kernel projection
|
||||
- [ ] `BlockNote` 不再定义页面结构本体,只负责某类内容节点编辑
|
||||
- [ ] 旧前端壳不再承担对象真相,只承担过渡投影或兼容层
|
||||
|
||||
---
|
||||
|
||||
## 4. 重新定义阶段
|
||||
|
||||
新架构下,阶段顺序应改成:
|
||||
|
||||
- Kernel Phase 0:边界冻结与术语统一
|
||||
- Kernel Phase 1:Node / Edge / Projection 基础模型落地
|
||||
- Kernel Phase 2:Kernel Query / Command / Subtree / Graph Traversal 协议落地
|
||||
- Kernel Phase 3:Rust Web 接入 kernel,成为主承载层
|
||||
- Kernel Phase 4:Sidebar / 页面树 / 文件树切到 kernel projection
|
||||
- Kernel Phase 5:结构知识刷新与 kernel-aware 检索
|
||||
- Kernel Phase 6:Mindmap 降级为 projection / editor,而不是对象中心
|
||||
- Kernel Phase 7:文档阅读页与 AI 面板切到 kernel projection
|
||||
- Kernel Phase 8:`BlockNote` 退化为内容编辑挂件
|
||||
- Kernel Phase 9:旧前端壳与旧对象模型下线
|
||||
|
||||
---
|
||||
|
||||
## 5. Kernel Phase 0:边界冻结与术语统一
|
||||
|
||||
**当前状态:`DONE`**
|
||||
|
||||
### 5.1 目标
|
||||
|
||||
先把“什么是 kernel,什么只是 projection”说清楚。
|
||||
|
||||
### 5.2 已有事实
|
||||
|
||||
- [x] 已有:
|
||||
- [tree-first-graph-kernel-v1.md](/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md)
|
||||
- [rust-web-long-term-checklist-v2.md](/mnt/Data1T/mnote/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md)
|
||||
- [x] 已明确:
|
||||
- `Mindmap` 不是中心
|
||||
- `BlockNote` 不是中心
|
||||
- `tree-first graph` 才是中心
|
||||
|
||||
### 5.3 仍需完成
|
||||
|
||||
- [x] 统一术语:
|
||||
- `node`
|
||||
- `edge`
|
||||
- `projection`
|
||||
- `subtree`
|
||||
- `content node`
|
||||
- `reference edge`
|
||||
- `summary node`
|
||||
- `index node`
|
||||
- [x] 统一“事实源 / 投影 / 编辑器 / 外挂”的四层边界
|
||||
- [x] 明确哪些现有对象暂时继续存在,哪些将来必须并入 kernel
|
||||
|
||||
### 5.4 完成判定
|
||||
|
||||
- [x] 后续文档和任务不再把“导图页”“页面树”“BlockNote 文档”当作独立事实源
|
||||
|
||||
### 5.5 当前落地说明
|
||||
|
||||
- [x] `tree-first-graph-kernel-v1.md` 已补术语冻结、四层边界与并入策略
|
||||
- [x] 当前口径明确:
|
||||
- 导图页不是事实源
|
||||
- 页面树不是事实源
|
||||
- `BlockNote` 文档结构不是事实源
|
||||
|
||||
---
|
||||
|
||||
## 6. Kernel Phase 1:Node / Edge / Projection 基础模型落地
|
||||
|
||||
**当前状态:`DONE`**
|
||||
|
||||
### 6.1 目标
|
||||
|
||||
把设计文档中的 kernel 定义,变成真正的 Rust 类型与协议。
|
||||
|
||||
### 6.2 实施清单
|
||||
|
||||
- [x] 在 Rust core 中定义统一 `Node` 模型
|
||||
- [x] 定义统一 `Edge` 模型
|
||||
- [x] 定义统一 `Projection` 请求模型
|
||||
- [x] 定义 node type 枚举或稳定字符串集合
|
||||
- [x] 定义 edge type 枚举或稳定字符串集合
|
||||
- [x] 定义 node metadata / content payload / refs payload 边界
|
||||
- [x] 定义 subtree 标识方式
|
||||
- [x] 定义 projection 标识方式
|
||||
- [x] 定义版本、审计、trace 在 kernel 上的挂载方式
|
||||
|
||||
### 6.3 最小首批节点建议
|
||||
|
||||
- [x] `workspace`
|
||||
- [x] `folder`
|
||||
- [x] `page`
|
||||
- [x] `section`
|
||||
- [x] `asset`
|
||||
- [x] `book`
|
||||
- [x] `pdf`
|
||||
- [x] `mindmap_node`
|
||||
- [x] `summary`
|
||||
- [x] `ai_note`
|
||||
- [x] `reference_anchor`
|
||||
|
||||
### 6.4 最小首批边建议
|
||||
|
||||
- [x] `parent_of`
|
||||
- [x] `child_of`
|
||||
- [x] `contains`
|
||||
- [x] `references`
|
||||
- [x] `backlinks_to`
|
||||
- [x] `source_of`
|
||||
- [x] `summarizes`
|
||||
- [x] `indexes`
|
||||
|
||||
### 6.5 完成判定
|
||||
|
||||
- [x] Rust core 中已经存在可被 Web / CLI / AI 共用的 kernel 类型定义
|
||||
|
||||
### 6.6 当前落地说明
|
||||
|
||||
- [x] `rust/crates/core-protocol/src/kernel.rs` 已进入主线
|
||||
- [x] `core-protocol/src/lib.rs` 已导出 kernel 类型,供 Web / runtime 复用
|
||||
|
||||
---
|
||||
|
||||
## 7. Kernel Phase 2:Kernel Query / Command / Subtree / Graph Traversal 协议落地
|
||||
|
||||
**当前状态:`DONE`**
|
||||
|
||||
### 7.1 目标
|
||||
|
||||
让 kernel 不是静态类型,而是可以被查询和操作。
|
||||
|
||||
### 7.2 实施清单
|
||||
|
||||
- [x] 定义 `get_node`
|
||||
- [x] 定义 `get_subtree`
|
||||
- [x] 定义 `list_children`
|
||||
- [x] 定义 `list_edges`
|
||||
- [x] 定义 `traverse_graph`
|
||||
- [x] 定义 `create_node`
|
||||
- [x] 定义 `update_node`
|
||||
- [x] 定义 `move_subtree`
|
||||
- [x] 定义 `attach_edge`
|
||||
- [x] 定义 `detach_edge`
|
||||
- [x] 定义 `project_view`
|
||||
|
||||
### 7.3 与当前已有能力的关系
|
||||
|
||||
当前已有一些可复用基础:
|
||||
|
||||
- [x] Mindmap 的 subtree / op 思想
|
||||
- [x] Rust command/query/tool envelope
|
||||
- [x] CLI / AI / runtime 可共用的调用结构
|
||||
|
||||
但还没有:
|
||||
|
||||
- [x] 统一 kernel query / command 面
|
||||
|
||||
### 7.4 完成判定
|
||||
|
||||
- [x] 至少有一组 kernel 查询和写入协议进入 Rust core-protocol / bridge-runtime 主线
|
||||
|
||||
### 7.5 当前落地说明
|
||||
|
||||
- [x] `bridge-runtime` 已支持:
|
||||
- `kernel.node.get`
|
||||
- `kernel.subtree.get`
|
||||
- `kernel.children.list`
|
||||
- `kernel.edges.list`
|
||||
- `kernel.graph.traverse`
|
||||
- `kernel.project_view`
|
||||
- `kernel.node.create`
|
||||
- `kernel.node.update`
|
||||
- `kernel.subtree.move`
|
||||
- `kernel.edge.attach`
|
||||
- `kernel.edge.detach`
|
||||
- [x] 当前首条真实 projection 样板已选用 Sidebar 数据集,经 Rust runtime 统一输出 kernel subtree / projection 结果
|
||||
|
||||
---
|
||||
|
||||
## 8. Kernel Phase 3:Rust Web 接入 kernel
|
||||
|
||||
**当前状态:`DONE`**
|
||||
|
||||
### 8.1 目标
|
||||
|
||||
让 `mnote-web` 不只是通用骨架,而是开始承接 kernel。
|
||||
|
||||
### 8.2 当前已有事实
|
||||
|
||||
- [x] `mnote-web` 骨架已存在
|
||||
- [x] 已有 request context / SSE / WS / Hermes bridge skeleton
|
||||
|
||||
### 8.3 仍需完成
|
||||
|
||||
- [x] 让 `mnote-web` 开始提供 kernel query route
|
||||
- [x] 让 `mnote-web` 提供 subtree / projection route
|
||||
- [x] 至少切一条真实主路径,不再只是 compat bridge
|
||||
- [x] 为 kernel route 增加集成验证
|
||||
|
||||
### 8.4 完成判定
|
||||
|
||||
- [x] `mnote-web` 已承接至少一条真实 kernel 主链
|
||||
|
||||
### 8.5 当前落地说明
|
||||
|
||||
- [x] `rust/crates/mnote-web/src/routes/kernel.rs` 已新增:
|
||||
- `/api/kernel/projections/sidebar`
|
||||
- `/api/kernel/subtree`
|
||||
- `/api/kernel/edges`
|
||||
- `/api/kernel/graph`
|
||||
- [x] `mnote-web` 已通过路由级测试,说明 kernel projection 已经不再只是骨架声明
|
||||
|
||||
---
|
||||
|
||||
## 9. Kernel Phase 4:Sidebar / 页面树 / 文件树切到 kernel projection
|
||||
|
||||
**当前状态:`PARTIAL`**
|
||||
|
||||
### 9.1 目标
|
||||
|
||||
把工作区导航从“前端组件 + 自己拼树”,改成“kernel tree projection”。
|
||||
|
||||
### 9.2 当前真实状态
|
||||
|
||||
- [x] Sidebar 有服务端首包
|
||||
- [x] Sidebar 有 Rust query 契约基础
|
||||
- [ ] 但主 Sidebar 仍是超大客户端组件
|
||||
- [ ] 页面树 / 文件树还没有真正基于 kernel node / edge
|
||||
|
||||
### 9.3 实施清单
|
||||
|
||||
- [ ] 用 kernel node/edge 重新定义 Sidebar 数据源
|
||||
- [ ] 用 projection query 输出 Sidebar tree
|
||||
- [ ] 用 projection query 输出 file tree
|
||||
- [ ] 用 projection query 输出 page tree
|
||||
- [ ] 把展开/折叠/拖拽从对象真相层剥离成局部 UI 状态
|
||||
- [ ] 把主布局里的 Sidebar 继续拆轻
|
||||
|
||||
### 9.4 完成判定
|
||||
|
||||
- [x] Sidebar / 页面树 / 文件树已经开始直接消费 kernel projection
|
||||
|
||||
### 9.5 当前落地说明
|
||||
|
||||
- [x] Rust runtime 已经可以把 `sidebar.dataset.list` 转成统一 kernel projection / subtree 结果
|
||||
- [ ] 当前前端主 Sidebar 还没有直接切到这个结果,仍保留 `DocumentRecord[] -> buildDocumentTree(...)` 的旧拼树逻辑
|
||||
|
||||
---
|
||||
|
||||
## 10. Kernel Phase 5:结构知识刷新与 kernel-aware 检索
|
||||
|
||||
**当前状态:`NOT_STARTED`**
|
||||
|
||||
### 10.1 目标
|
||||
|
||||
让系统不再依赖一个独立的传统 RAG 子系统,而是:
|
||||
|
||||
- 通过定时知识刷新持续生成和维护结构化知识
|
||||
- 通过 kernel-aware 检索直接命中 node / subtree / evidence
|
||||
- 通过 Mindmap / BookMindmap / summary node / reference edge 构成知识索引层
|
||||
|
||||
一句话说:
|
||||
|
||||
> **不是“部署一个 RAG 系统”,而是“把 kernel 本身建设成可刷新、可检索、可追溯的知识底座”。**
|
||||
|
||||
### 10.2 需要完成
|
||||
|
||||
- [ ] 定义知识刷新任务模型
|
||||
- [ ] 定义 cron 驱动的定时刷新入口
|
||||
- [ ] 定义增量刷新输入:workspace / book / pdf / page / subtree
|
||||
- [ ] 定义知识刷新输出:
|
||||
- `summary node`
|
||||
- `ai_note node`
|
||||
- `index node`
|
||||
- `reference edge`
|
||||
- `book subtree`
|
||||
- `chapter subtree`
|
||||
- [ ] 定义 kernel-aware 搜索输入
|
||||
- [ ] 定义按 node type / subtree / edge 的过滤
|
||||
- [ ] 让检索结果可以返回 node / subtree / evidence
|
||||
- [ ] 定义“结构命中 -> 正文/附件/页码回查”的两段式检索
|
||||
- [ ] 把书籍 / PDF / 章节树 / BookMindmap 纳入 kernel index
|
||||
- [ ] 把 `Mindmap` / `BookMindmap` 定义成结构知识索引的一种 projection,而不是独立 RAG 页面
|
||||
- [ ] 如果现有 `LightRAG` 仍有残留,明确其过渡边界与移除计划
|
||||
|
||||
### 10.3 当前基础
|
||||
|
||||
- [x] 搜索已有前端 runtime
|
||||
- [x] Rust 有 FTS 与 Mindmap subtree 能力雏形
|
||||
- [x] 已有 `BookRAG` / `Mindmap` / `BookMindmap` 方向上的结构索引思路
|
||||
- [ ] 但还没有真正的知识刷新任务层
|
||||
- [ ] 还没有 kernel-aware 检索面
|
||||
- [ ] 还没有把“定时刷新知识 -> 更新 kernel 节点/边 -> 查询回查证据”串成主链
|
||||
|
||||
### 10.4 完成判定
|
||||
|
||||
- [ ] 至少有一条 cron 驱动的知识刷新链可以稳定更新 kernel 中的结构知识节点
|
||||
- [ ] 至少有一条检索路径能直接返回 kernel node / subtree 命中结果
|
||||
- [ ] 至少有一条命中结果能继续回查正文、附件、页码或证据节点
|
||||
|
||||
---
|
||||
|
||||
## 11. Kernel Phase 6:Mindmap 降级为 projection / editor
|
||||
|
||||
**当前状态:`PARTIAL`**
|
||||
|
||||
### 11.1 目标
|
||||
|
||||
让 Mindmap 从“独立对象中心 + blob 真相 + 前端重壳”退回到“kernel subtree / graph 的一种 projection / editor”。
|
||||
|
||||
### 11.2 当前已有事实
|
||||
|
||||
- [x] Rust `core-protocol` 已有:
|
||||
- `KernelNodeType::Mindmap`
|
||||
- `KernelNodeType::MindmapNode`
|
||||
- `KernelProjectionKind::Mindmap`
|
||||
- [x] Rust runtime 已有:
|
||||
- `mindmap_get`
|
||||
- `mindmap_get_subtree`
|
||||
- `mindmap_put`
|
||||
- `mindmap_apply_ops`
|
||||
- [x] 导图独立页已脱离 `editorStub`
|
||||
- [x] 当前主线理念已明确:
|
||||
- `Mindmap` 不是中心
|
||||
- `tree-first graph kernel` 才是中心
|
||||
- [x] 但当前真实主链仍然主要是:
|
||||
- `MindmapBlock.tsx` 持有重交互壳
|
||||
- `mindmaps.get/put` 读写整棵 blob
|
||||
- 前端 `buildMindmapProjection(...)` 对 blob 做摘要
|
||||
- `mindmap_apply_ops` 仍直接操作 compat mindmap 树
|
||||
|
||||
### 11.3 仍需完成
|
||||
|
||||
- [x] Phase 6 的详细口径改以:
|
||||
- `/mnt/Data1T/mnote/design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md`
|
||||
- 为准
|
||||
- [ ] 把导图事实源切到 kernel node / edge / subtree
|
||||
- [ ] 把 `MindmapTreeNode` / `MindmapOp` 降级为 compat DTO,而不是长期 canonical model
|
||||
- [ ] 让导图独立页读取正式 `mindmap` kernel projection,而不是继续以 `mindmaps.get` raw blob 为主
|
||||
- [ ] 让文档内嵌导图读取 `mindmap_preview` / subtree preview,而不是直接拼整棵树
|
||||
- [ ] 让导图编辑操作回写 kernel command,而不是继续以 `getData()/POST whole blob` 为主
|
||||
- [ ] 让 `mindmap_apply_ops` 改成 compat facade,内部翻译到 kernel command
|
||||
- [ ] 让 AI / CLI 可以直接创建、修改、移动 `mindmap_node` 与 reference edge
|
||||
- [ ] 让 `mindmaps` 表退到 compat snapshot / import-export 层,而不是长期事实源
|
||||
|
||||
### 11.4 完成判定
|
||||
|
||||
- [ ] 导图独立页与内嵌预览都已经直接消费 kernel projection
|
||||
- [ ] 常规编辑不再依赖整图 blob 覆盖保存
|
||||
- [ ] AI / CLI 已能直接操作导图 kernel truth
|
||||
- [ ] `simple-mind-map` 已退到 renderer / adapter 层
|
||||
- [ ] `Mindmap` 可以被正式定义为 kernel 的一种 projection / editor,而不是独立事实源
|
||||
|
||||
---
|
||||
|
||||
## 12. Kernel Phase 7:文档阅读页与 AI 面板切到 kernel projection
|
||||
|
||||
**当前状态:`NOT_STARTED`**
|
||||
|
||||
### 12.1 目标
|
||||
|
||||
让阅读页和 AI 都不再直接围绕旧页面对象模型打转,而是围绕 kernel。
|
||||
|
||||
### 12.2 阅读页部分
|
||||
|
||||
- [ ] 阅读页改成读取 page subtree projection
|
||||
- [ ] 阅读页大纲来自 kernel subtree
|
||||
- [ ] 回链、引用、结构信息来自 kernel edge
|
||||
- [ ] 页面阅读流作为 projection,而不是事实源
|
||||
|
||||
### 12.3 AI 部分
|
||||
|
||||
- [ ] AI tool 直接面向 node / subtree / edge
|
||||
- [ ] AI 不再默认面向“页面前端壳”
|
||||
- [ ] AI 能创建 summary node / ai_note node / reference edge
|
||||
- [ ] AI 能把 PDF / Book 解析结果落进 kernel index
|
||||
|
||||
### 12.4 当前基础
|
||||
|
||||
- [x] 文档阅读态已出现
|
||||
- [x] AI host/runtime 已拆分
|
||||
- [ ] 但两者都还没开始直接面向 kernel projection
|
||||
|
||||
### 12.5 完成判定
|
||||
|
||||
- [ ] 阅读页与 AI 至少有一条主路径已经直接消费 kernel projection
|
||||
|
||||
---
|
||||
|
||||
## 13. Kernel Phase 8:BlockNote 退化为内容编辑挂件
|
||||
|
||||
**当前状态:`NOT_STARTED`**
|
||||
|
||||
### 13.1 目标
|
||||
|
||||
把 `BlockNote` 从“页面定义者”改成“某类内容节点编辑器”。
|
||||
|
||||
### 13.2 当前已有事实
|
||||
|
||||
- [x] 文档页默认已不再直接强挂 `BlockNote`
|
||||
|
||||
### 13.3 仍需完成
|
||||
|
||||
- [ ] 定义 page subtree 与 content node 的关系
|
||||
- [ ] 定义哪些节点仍由 `BlockNote` 编辑
|
||||
- [ ] 定义哪些结构节点改由 kernel-aware editor 操作
|
||||
- [ ] 把页面结构与 `BlockNote` 块结构彻底分离
|
||||
- [ ] 把 `DocumentContent` 里外围 panel 再次拆出去
|
||||
|
||||
### 13.4 完成判定
|
||||
|
||||
- [ ] 页面结构已经不再由 `BlockNote` 数据结构定义
|
||||
|
||||
---
|
||||
|
||||
## 14. Kernel Phase 9:旧前端壳与旧对象模型下线
|
||||
|
||||
**当前状态:`NOT_STARTED`**
|
||||
|
||||
### 14.1 目标
|
||||
|
||||
当 kernel + Rust Web + projection 都建立后,再下线旧对象模型和旧主壳。
|
||||
|
||||
### 14.2 实施清单
|
||||
|
||||
- [ ] 盘点哪些旧页面/route 仍直接承载对象真相
|
||||
- [ ] 盘点哪些旧 helper 仍在拼装第二套对象模型
|
||||
- [ ] 删除已被 kernel projection 替代的旧 route
|
||||
- [ ] 删除已被 kernel command/query 替代的旧 adapter
|
||||
- [ ] 更新最终架构文档与对外口径
|
||||
|
||||
### 14.3 完成判定
|
||||
|
||||
- [ ] 旧前端壳与旧对象模型都不再是主事实来源
|
||||
|
||||
---
|
||||
|
||||
## 15. 这份清单与 Rust Web v2 的关系
|
||||
|
||||
这份清单不是替代:
|
||||
|
||||
- [rust-web-long-term-checklist-v2.md](/mnt/Data1T/mnote/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md)
|
||||
|
||||
而是对它做“上位重排”。
|
||||
|
||||
关系应理解为:
|
||||
|
||||
- `rust-web-long-term-checklist-v2`
|
||||
- 关注当前代码状态下的 Web / 页面重构现实进度
|
||||
- `tree-first-graph-kernel-checklist-v1`
|
||||
- 关注在新内核架构下,整体路线应该如何重新排序
|
||||
|
||||
一句话说:
|
||||
|
||||
> **Rust Web v2 告诉你“现在代码做到哪了”,这份新清单告诉你“在新架构下,接下来应该围绕什么继续做”。**
|
||||
|
||||
---
|
||||
|
||||
## 16. 最终结论
|
||||
|
||||
采用新架构后,长期 checklist 的中心必须变化。
|
||||
|
||||
不再是:
|
||||
|
||||
- 先 Web 重构,再考虑对象模型
|
||||
|
||||
而应该是:
|
||||
|
||||
- **先 Kernel,再 Projection,再 Web,再 Editor**
|
||||
|
||||
因此后续真正的主线应改成:
|
||||
|
||||
> **以 `tree-first graph kernel` 为事实中心,Rust Web 承接其 query/command/projection,Sidebar/搜索/Mindmap/阅读页/AI 都逐步切到 kernel projection,最后再让 `BlockNote` 与旧前端壳退场。**
|
||||
@@ -0,0 +1,554 @@
|
||||
# 3-0 [recycle] Rust Web 长期架构实施清单 v1
|
||||
|
||||
> 更新时间:2026-04-16
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/document-access-performance-root-cure-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-cutover-v1.md`
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
|
||||
## 1. 文档用途
|
||||
|
||||
这份清单不是短期提速清单,而是长期架构落地清单。
|
||||
|
||||
它服务的目标只有一个:
|
||||
|
||||
> **把 mnote 从“Rust 内核 + 重前端页面壳”推进到“Rust 内核 + Rust Web 承载 + server-first 页面 + 少量交互孤岛”的稳定形态。**
|
||||
|
||||
这份清单强调四件事:
|
||||
|
||||
- 做什么
|
||||
- 不做什么
|
||||
- 什么时候算完成
|
||||
- 哪些东西必须最后处理
|
||||
|
||||
---
|
||||
|
||||
## 2. 总体完成定义
|
||||
|
||||
只有同时满足下面几条,才能认为长期架构目标基本达成:
|
||||
|
||||
- [ ] Rust workspace 成为唯一业务真执行面,Web 层不再承载第二套对象规则
|
||||
- [ ] Rust Web 层已经接住主 API、主页面壳、主流式能力
|
||||
- [ ] 文档访问路径已变成“先阅读、后交互、再编辑”
|
||||
- [ ] Sidebar、搜索、AI、Mindmap 等外围模块已退出当前重前端主壳
|
||||
- [ ] `BlockNote` 已被隔离为最后的重交互孤岛,而不是整页入口前提
|
||||
- [ ] Next/旧 React 壳只保留过渡兼容边界或已被替换
|
||||
|
||||
---
|
||||
|
||||
## 3. 执行原则清单
|
||||
|
||||
在任何阶段开始前,先确认这些原则不被破坏:
|
||||
|
||||
- [ ] 不再把新的业务规则写回 TS route / Next 页面层
|
||||
- [ ] 不再新增新的全局常驻前端大面板
|
||||
- [ ] 不再把“临时懒加载”当作长期方案替代品
|
||||
- [ ] 不在未完成阅读态分离前,继续扩张编辑态默认挂载内容
|
||||
- [ ] 不把 `BlockNote` 当成第一阶段改造对象
|
||||
- [ ] 不把 OnlyOffice 当成主性能阻塞项
|
||||
- [ ] 不把“换成 Rust”简化成“只重写 API”
|
||||
|
||||
---
|
||||
|
||||
## 4. 阶段总览
|
||||
|
||||
推荐按下面顺序推进:
|
||||
|
||||
- Phase 0:基线与边界冻结
|
||||
- Phase 1:Rust Web 基础层落地
|
||||
- Phase 2:文档阅读页 server-first 化
|
||||
- Phase 3:Sidebar / 页面树 / 文件树 Rust 化与 island 化
|
||||
- Phase 4:搜索系统 Rust 化与 island 化
|
||||
- Phase 5:AI 面板进一步收口为纯桥接 island
|
||||
- Phase 6:Mindmap 独立对象化与独立页面化
|
||||
- Phase 7:文档编辑态与 `BlockNote` 孤岛化
|
||||
- Phase 8:旧前端壳下线与兼容清理
|
||||
|
||||
### 4.1 阶段依赖与并行规则
|
||||
|
||||
这条长期路线不是“看到哪里慢就改哪里”,而是有明确前后依赖。
|
||||
|
||||
- [x] `Phase 0` 必须先产出边界冻结、基线和 islands 候选,否则后续会重新滑回旧壳扩张。
|
||||
- [x] 横向能力 `H1` 必须在 `Phase 1` 之前定口径,否则 Rust Web、阅读态、搜索、AI、Mindmap 会各自发明 trace、缓存和权限语义。
|
||||
- [x] `Phase 1` 是 `Phase 2` ~ `Phase 6` 的共同前置;没有 Rust Web 承载层,后续只能继续堆 Next route。
|
||||
- [x] `Phase 2` ~ `Phase 6` 可以分批并行推进,但都必须遵守“阅读优先、局部 island、对象规则只进 Rust core”。
|
||||
- [x] `Phase 7` 依赖 `Phase 2` ~ `Phase 6` 基本就位后再开始,否则 `BlockNote` 会继续承担外围能力。
|
||||
- [x] `Phase 8` 只能在 `Phase 1` ~ `Phase 7` 都有稳定替代路径后执行,不能靠“删旧代码”制造假完成。
|
||||
|
||||
### 4.2 每阶段输出给下一阶段的输入
|
||||
|
||||
- [x] `Phase 0` 输出:重模块审计表、性能基线、阅读态/编辑态边界、islands 候选、冻结口径。
|
||||
- [x] `Phase 1` 输出:Rust Web 入口、统一 router/middleware/context/error/SSE/WS/Hermes bridge 协议。
|
||||
- [x] `Phase 2` 输出:文档阅读态服务端主链、阅读工具栏 island、阅读缓存/预取策略。
|
||||
- [x] `Phase 3` 输出:Sidebar/树结构服务端骨架、局部刷新协议、导航预取策略。
|
||||
- [x] `Phase 4` 输出:搜索 server-first 页面壳、轻交互 Search island、分页/键盘导航协议。
|
||||
- [x] `Phase 5` 输出:AI bridge panel 最小会话协议、流式 token/tool/client action 统一事件流。
|
||||
- [x] `Phase 6` 输出:Mindmap 独立对象页壳、内嵌轻预览边界、独立操作协议。
|
||||
- [x] `Phase 7` 输出:阅读态/编辑态切换协议、编辑岛错误边界、编辑态性能采样。
|
||||
- [x] `Phase 8` 输出:Next/旧 React 壳保留清单、兼容层清理清单、最终对外口径。
|
||||
|
||||
---
|
||||
|
||||
## 5. Phase 0:基线与边界冻结
|
||||
|
||||
### 5.1 目标
|
||||
|
||||
在动长期架构之前,先冻结边界,避免迁移过程中又把旧模式继续扩写。
|
||||
|
||||
### 5.2 实施清单
|
||||
|
||||
- [ ] 盘点当前由 Next/React 页面层承载的重模块清单
|
||||
- [ ] 标记哪些模块属于“必须保留浏览器交互”
|
||||
- [ ] 标记哪些模块属于“可变为 server-first 页面”
|
||||
- [ ] 标记哪些模块属于“可退化为独立 island”
|
||||
- [ ] 标记哪些模块属于“外挂或独立页面,不进入主文档链”
|
||||
- [ ] 冻结 `BlockNote` 外围继续加功能的入口
|
||||
- [ ] 冻结 Sidebar、SearchPalette、Document 面板继续膨胀的入口
|
||||
- [ ] 为页面切换与首屏建立统一性能基线
|
||||
- [ ] 为文档页进入路径建立链路追踪点
|
||||
- [ ] 为 Sidebar、搜索、AI、Mindmap 建立独立耗时采样点
|
||||
|
||||
### 5.2.1 当前前端重模块审计表
|
||||
|
||||
| 模块 | 当前主入口 | 当前主问题 | 长期归位 | 是否必须保留浏览器重交互 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 文档页 | `wolai-frontend/src/app/(app)/documents/[id]/page.tsx` + `DocumentShell -> DocumentContent -> BlockNoteEditor` | 进入页面默认串到编辑器初始化,阅读态无法先出现 | `Phase 2` 先 server-first 阅读页,`Phase 7` 再把 `BlockNote` 孤岛化 | 是,但只限编辑态 |
|
||||
| Sidebar / 页面树 / 文件树 | `wolai-frontend/src/app/(app)/layout.tsx` + `src/components/sidebar/sidebar.tsx` | 常驻在主布局内,切页时继续参与大组件状态与重渲染 | `Phase 3` 收口为 Rust query + 服务端壳 + 局部 island | 是,但只限展开/拖拽/快捷过滤 |
|
||||
| SearchPalette | `src/components/search/search-palette.tsx` | 当前仍作为全局常驻大组件挂在布局内 | `Phase 4` 收口为独立 island 与 server-first 搜索页 | 是,但只限输入/联想/键盘导航 |
|
||||
| AI 面板 | `src/components/ai-agent/**`、`DocumentAiAgentPanel`、`MindmapAiAgentPanel`、`OnlyOfficeAiAgentPanel` | 已切 Hermes bridge,但页面壳仍偏重,多个面板仍常驻挂载 | `Phase 5` 统一为最小 bridge shell + 按需 island | 是,但只限会话与浏览器 client capability |
|
||||
| Mindmap | `src/components/editor/blocks/MindmapBlock.tsx` + `src/app/mindmap/[docId]/[mindmapId]/page.tsx` | 独立页仍与 BlockNote 形态绑得太近,内嵌/独立共用同一重壳 | `Phase 6` 收口为独立对象页 + 内嵌轻预览 | 是 |
|
||||
| OnlyOffice | `src/app/onlyoffice/**` 与 `src/app/api/onlyoffice/**` | 属于外部编辑器桥,不是主文档性能瓶颈 | 继续维持外挂页面,不进入主文档链 | 是,但不进入主文档访问链 |
|
||||
| `BlockNote` | `src/components/editor/blocknote-editor.tsx` | 当前仍然是文档页默认入口前提 | 最后处理,保留为 `Phase 7` 的重交互孤岛 | 是 |
|
||||
|
||||
### 5.2.2 冻结口径
|
||||
|
||||
- [x] 新增页面能力时,先判断是否能落到 server-first 页面或局部 island,不能再默认塞回主布局。
|
||||
- [x] 新增对象规则、权限判断、聚合排序、工具编排,不允许直接写回 Next route 或页面层。
|
||||
- [x] `BlockNote` 外围的新面板、新工具栏、新初始化逻辑一律冻结,除非是为“阅读态分离”服务。
|
||||
- [x] Sidebar、SearchPalette、AI 面板不允许再继续增加全局常驻状态容器。
|
||||
|
||||
### 5.3 产物清单
|
||||
|
||||
- [ ] 当前前端重模块审计表
|
||||
- [ ] 页面切换性能基线报告
|
||||
- [ ] 文档阅读态与编辑态的边界定义
|
||||
- [ ] 岛模型候选模块清单
|
||||
|
||||
### 5.3.1 页面切换性能基线报告
|
||||
|
||||
| 访问链路 | 当前入口 | 需要记录的统一指标 | 当前基线采样方式 | 迁移后对比目标 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 普通文档打开 | `/documents/[id]` | 首字节、正文首屏、进入可点击阅读态、进入编辑态耗时、`BlockNote` 挂载耗时 | `scripts/task019-document-ui-regression.js` + 浏览器 performance trace | 把正文首屏与阅读交互从编辑器启动链里剥离 |
|
||||
| Sidebar 切页 | 主布局 + Sidebar | 切页耗时、树结构重渲染数、导航预取命中率 | `pnpm test src/lib/sidebar-data.test.ts src/lib/file-tree/rows.test.ts` + React Profiler | Sidebar 仅做局部交互,不再放大整页挂载 |
|
||||
| 搜索打开/关闭 | SearchPalette | 打开耗时、首次结果返回、键盘导航响应、关闭恢复耗时 | 搜索页/浮层交互 trace + `eslint`/定向 smoke | 搜索开关不再引发整页重计算 |
|
||||
| AI 面板打开/关闭 | Document/Mindmap/OnlyOffice AI | 面板首开耗时、SSE 首 token、关闭后页面恢复耗时 | `runAgent.test.ts` + 页面侧 performance mark | AI 未打开时不占主页面主链 |
|
||||
| Mindmap 独立页 | `/mindmap/[docId]/[mindmapId]` | 页面壳首屏、导图交互可用、独立页返回文档页耗时 | `scripts/task021-mindmap-ui-regression.js` | 独立页不再依赖文档编辑上下文 |
|
||||
|
||||
### 5.3.2 文档阅读态与编辑态的边界定义
|
||||
|
||||
- [x] 阅读态必须先拿到页面 meta 与正文读链,允许在没有编辑器的情况下稳定展示正文。
|
||||
- [x] 编辑态必须显式进入,不能再把“打开文档”默认等同于“挂载完整编辑器”。
|
||||
- [x] 阅读态负责:权限、只读/禁复制/禁下载策略、正文首屏、轻工具栏、回退与预取。
|
||||
- [x] 编辑态负责:`BlockNote`、块级写入、Slash、评论/历史/AI/页面选项等重交互链路。
|
||||
- [x] 只读渲染与可编辑渲染必须是两套不同初始化协议,不能靠 mounted gating 假装分离。
|
||||
|
||||
### 5.3.3 岛模型候选模块清单
|
||||
|
||||
| 模块 | 候选 island 形态 | 保留在服务端页面壳的内容 |
|
||||
| --- | --- | --- |
|
||||
| 文档阅读工具栏 | 轻工具栏 island | 标题、正文、权限壳、阅读统计 |
|
||||
| Sidebar 树 | 展开/拖拽/快捷过滤 island | 工作区壳、树结构首屏 HTML |
|
||||
| SearchPalette | 输入框、联想、结果列表 island | 搜索页结果首屏与分页壳 |
|
||||
| AI 面板 | 单一 `AiBridgePanel` island | 页面上下文注入、SSE 桥接 route |
|
||||
| Mindmap | 独立导图交互 island | 独立页 meta、权限与返回壳 |
|
||||
| `BlockNote` | 最后保留的重编辑 island | 阅读页正文、阅读工具栏、权限与缓存壳 |
|
||||
|
||||
### 5.4 完成判定
|
||||
|
||||
- [ ] 后续任何人都能明确知道哪些模块先迁、哪些模块后迁
|
||||
- [ ] 已经有统一基线可以验证迁移是否真的更快
|
||||
- [ ] 不再接受“顺手往旧壳里再塞功能”的继续扩张
|
||||
|
||||
---
|
||||
|
||||
## 6. Phase 1:Rust Web 基础层落地
|
||||
|
||||
### 6.1 目标
|
||||
|
||||
建立真正的 Rust Web 承载层,而不是停留在“Rust 只做业务内核”。
|
||||
|
||||
### 6.2 实施清单
|
||||
|
||||
- [ ] 在 Rust workspace 中明确新增 Web 层 crate 或独立服务目录
|
||||
- [ ] 选定 `axum` 作为主 HTTP 承载框架
|
||||
- [ ] 建立统一 Router 组织方式
|
||||
- [ ] 建立统一 middleware 链
|
||||
- [ ] 建立统一 request id / trace id 注入
|
||||
- [ ] 建立统一鉴权上下文注入方式
|
||||
- [ ] 建立统一 workspace / actor / tenant 上下文透传
|
||||
- [ ] 建立统一错误码与错误响应协议
|
||||
- [ ] 建立统一 JSON 响应包装规范
|
||||
- [ ] 建立统一 SSE 输出协议
|
||||
- [ ] 建立统一 WebSocket 连接协议
|
||||
- [ ] 建立统一静态资源与页面壳响应策略
|
||||
- [ ] 打通 Rust Web 层到现有 Rust core runtime 的调用入口
|
||||
- [ ] 打通 Rust Web 层到 Hermes 的桥接入口
|
||||
- [ ] 明确保留哪些 Next route 作为过渡兼容壳
|
||||
- [ ] 明确哪些旧 route 不允许再扩写
|
||||
|
||||
### 6.3 架构约束
|
||||
|
||||
- [ ] `axum` 只负责 Web 承载,不复制业务裁决
|
||||
- [ ] Rust Web 层不再新建第二套对象模型
|
||||
- [ ] Web 层只组合调用 Rust core,而不是重新实现 core 语义
|
||||
- [ ] 仍需浏览器特定 transport 的旧接口必须标记为兼容层
|
||||
|
||||
### 6.4 完成判定
|
||||
|
||||
- [ ] 已有一个可运行的 Rust Web 入口
|
||||
- [ ] 主 API、SSE、WS、trace 基础设施已可由 Rust Web 层承接
|
||||
- [ ] 后续页面迁移不再必须依赖 Next API route 作为唯一入口
|
||||
|
||||
---
|
||||
|
||||
## 7. Phase 2:文档阅读页 server-first 化
|
||||
|
||||
### 7.1 目标
|
||||
|
||||
把“进入文档页就进入编辑器世界”的模式改成“先进入阅读页,再按需进入编辑态”。
|
||||
|
||||
### 7.2 实施清单
|
||||
|
||||
- [ ] 定义文档阅读页独立于编辑态的页面模型
|
||||
- [ ] 定义阅读页所需最小数据模型
|
||||
- [ ] 定义阅读页 HTML 输出协议
|
||||
- [ ] 将文档 meta 与正文读取收敛成同一服务端读链
|
||||
- [ ] 去掉进入文档页必须二次取正文的默认模式
|
||||
- [ ] 去掉进入文档页必须等待客户端 mounted gating 的默认模式
|
||||
- [ ] 去掉阅读页默认依赖 `BlockNote` 的前提
|
||||
- [ ] 把评论、历史、回链、页面选项、AI 等外围面板改为非阻塞挂载
|
||||
- [ ] 将文档阅读工具栏收缩为轻交互 island
|
||||
- [ ] 将阅读页权限、只读、禁复制、禁下载等策略前移到服务端页面壳
|
||||
- [ ] 为阅读页建立缓存与失效策略
|
||||
- [ ] 为阅读页建立首屏、切页、回退、预取策略
|
||||
|
||||
### 7.3 禁止事项
|
||||
|
||||
- [ ] 不允许为了“看起来快一点”继续叠更多客户端 gating
|
||||
- [ ] 不允许阅读页默认挂载完整编辑器依赖树
|
||||
- [ ] 不允许把评论、AI、回链等周边作为阅读页首屏阻塞条件
|
||||
|
||||
### 7.4 完成判定
|
||||
|
||||
- [ ] 打开文档时可以先看到完整阅读态,而不是编辑器 loading 壳
|
||||
- [ ] 文档访问主链已不依赖 `BlockNote` 初始化完成
|
||||
- [ ] 页面切换性能的主要瓶颈已从“编辑器启动”转移出去
|
||||
|
||||
### 7.5 当前落地状态(2026-04-16)
|
||||
|
||||
- [x] 文档页已改成 server-first 阅读入口:服务端先拿 `meta + content + revision + conflictDetectionKey`,客户端只在服务端读链失败时才回退到旧 `/api/documents/content` 读链。
|
||||
- [x] `DocumentShell` 已去掉 mounted gating,阅读页不再先显示“正在载入编辑器...”壳。
|
||||
- [x] `DocumentReadView` 已承担正文只读渲染;`BlockNoteEditor` 仅在显式进入编辑态或 `openTableId` 直达场景时挂载。
|
||||
- [x] 只读/禁复制/禁下载、评论、历史、回链、页面 AI 与页面选项都已从阅读首屏阻塞链上移开,阅读态可以先稳定出现。
|
||||
|
||||
---
|
||||
|
||||
## 8. Phase 3:Sidebar / 页面树 / 文件树 Rust 化与 island 化
|
||||
|
||||
### 8.1 目标
|
||||
|
||||
把高频访问、长期常驻、当前体量过大的导航系统,从主前端大壳里拆出去。
|
||||
|
||||
### 8.2 实施清单
|
||||
|
||||
- [ ] 定义 Sidebar 数据聚合协议
|
||||
- [ ] 定义页面树 / 文件树查询协议
|
||||
- [ ] 将排序、过滤、分组、收藏、最近访问、回收站等聚合逻辑收敛到 Rust
|
||||
- [ ] 将大部分树结构渲染改为服务端输出
|
||||
- [ ] 将展开、折叠、拖拽、快捷过滤等改为局部 island
|
||||
- [ ] 拆分“静态结构”和“高交互局部状态”
|
||||
- [ ] 减少全局布局对 Sidebar 的强耦合
|
||||
- [ ] 让 Sidebar 切页时不重新参与重布局初始化
|
||||
- [ ] 为树结构建立局部刷新协议,而不是整树重拉
|
||||
- [ ] 为导航行为建立预取与轻量缓存策略
|
||||
|
||||
### 8.3 风险控制
|
||||
|
||||
- [ ] 拖拽重排不能倒逼回到全客户端大组件
|
||||
- [ ] 树结构局部刷新不能破坏 SSR 壳稳定性
|
||||
- [ ] 布局层不得继续持有超大导航状态容器
|
||||
|
||||
### 8.4 完成判定
|
||||
|
||||
- [ ] Sidebar 不再是主布局中的超大客户端核心组件
|
||||
- [ ] 页面树 / 文件树的数据与主要规则已由 Rust 层统一提供
|
||||
- [ ] 切页时 Sidebar 只参与局部交互,不再放大整页挂载成本
|
||||
|
||||
### 8.5 当前落地状态(2026-04-16)
|
||||
|
||||
- [x] `layout` 已在服务端先调用 `loadSidebarDataFromConvex(...)` 输出 `SidebarInitialData`,导航首屏数据不再默认延迟到客户端再拼。
|
||||
- [x] `/api/sidebar`、`src/lib/sidebar-data.ts`、`src/lib/server/sidebar-data.ts` 与实时 hook 已统一复用 `sidebar.dataset.list` 的共享契约。
|
||||
- [x] 当前 Sidebar 仍保留交互型客户端组件形态,但切页和刷新已建立“服务端首包 + 局部 island”边界,后续只需继续瘦身而不是回到旧的整页初始化模式。
|
||||
|
||||
---
|
||||
|
||||
## 9. Phase 4:搜索系统 Rust 化与 island 化
|
||||
|
||||
### 9.1 目标
|
||||
|
||||
让搜索成为一个轻页面、轻浮层、轻交互系统,而不是全局重组件。
|
||||
|
||||
### 9.2 实施清单
|
||||
|
||||
- [ ] 明确搜索索引层、召回层、结果聚合层全部由 Rust 提供
|
||||
- [ ] 明确文档、块、标题、标签、对象搜索的统一协议
|
||||
- [ ] 为搜索结果定义服务端可渲染的数据模型
|
||||
- [ ] 将 SearchPalette 改造成独立 island
|
||||
- [ ] 将输入框、联想、最近搜索、结果列表拆成最小交互单元
|
||||
- [ ] 将搜索页主内容改成 server-first 页面
|
||||
- [ ] 建立搜索结果分页、滚动续取、键盘导航协议
|
||||
- [ ] 建立最近搜索与最近访问的轻量持久层
|
||||
- [ ] 避免搜索组件再作为全局常驻重组件绑定整页布局
|
||||
|
||||
### 9.3 完成判定
|
||||
|
||||
- [ ] 搜索框打开与关闭不再引发整页级别的重计算
|
||||
- [ ] 搜索结果主要由 Rust 提供,前端只负责最小交互呈现
|
||||
- [ ] 搜索能力不再依赖当前全局 React 壳维持状态
|
||||
|
||||
### 9.4 当前落地状态(2026-04-16)
|
||||
|
||||
- [x] `SearchPalette` 已拆成轻量 `SearchPaletteHost` + `search-palette.runtime`,首次快捷键或首次打开前不再加载重量运行态。
|
||||
- [x] 全局布局只保留搜索 host 接线,`Ctrl/Cmd+P` 与引用快捷键先由 host 接住,再按需拉起 runtime。
|
||||
- [x] `SearchPaletteHost` 的动态导入已修正为直接指向 runtime,避免 host 自引用导致的错误懒加载。
|
||||
|
||||
---
|
||||
|
||||
## 10. Phase 5:AI 面板进一步收口为纯桥接 island
|
||||
|
||||
### 10.1 目标
|
||||
|
||||
让 AI 面板彻底变成“后端工具桥 + 最小 UI”,不再成为大前端的第二套系统。
|
||||
|
||||
### 10.2 实施清单
|
||||
|
||||
- [ ] 确认所有主线 AI tools 已收口到 Rust runtime / Hermes
|
||||
- [ ] 删除或冻结旧前端 registry / orchestration 入口
|
||||
- [ ] 统一 AI 面板的最小会话协议
|
||||
- [ ] 统一页面上下文注入协议
|
||||
- [ ] 统一 AI 输出渲染协议
|
||||
- [ ] 统一流式 token / tool event / client action 回传协议
|
||||
- [ ] 将页面级 AI 面板统一为单一 bridge shell
|
||||
- [ ] 将 AI 面板按需挂载为 island
|
||||
- [ ] 去掉常驻全局 AI host 对主布局的影响
|
||||
- [ ] 去掉面板自身承载重量级对象逻辑的习惯
|
||||
|
||||
### 10.3 完成判定
|
||||
|
||||
- [ ] AI 面板已不再承担第二套工具编排
|
||||
- [ ] AI 面板关闭或未打开时,不影响页面切换主链
|
||||
- [ ] AI 页面壳只剩桥接、上下文、渲染,不剩对象真执行逻辑
|
||||
|
||||
### 10.4 当前落地状态(2026-04-16)
|
||||
|
||||
- [x] 文档页、导图页、OnlyOffice 三类 AI 面板都已收口为轻 host + 按需 runtime island,Hermes / Rust bridge / client-tool-result 主链继续复用现有接缝。
|
||||
- [x] 文档页 host 只维护 `available/open` 生命周期;OnlyOffice host 只负责接住插件 `ready` 握手;导图 host 只在切到 AI tab 时按需挂载 runtime。
|
||||
- [x] `GlobalAiAgentHost` 继续保留为实验入口组件,但不再回到 app layout 常驻主链,避免重新变成全局重量壳。
|
||||
|
||||
---
|
||||
|
||||
## 11. Phase 6:Mindmap 独立对象化与独立页面化
|
||||
|
||||
### 11.1 目标
|
||||
|
||||
把 Mindmap 从“BlockNote 的一类重自定义块”推进为“独立对象 + 独立页面 + 可嵌入轻预览”。
|
||||
|
||||
### 11.2 实施清单
|
||||
|
||||
- [ ] 定义 Mindmap 独立对象模型
|
||||
- [ ] 定义 Mindmap 读写协议与操作协议
|
||||
- [ ] 明确导图节点、边、布局、样式、视口状态的 Rust 持久化结构
|
||||
- [ ] 明确导图页面独立于文档页的服务端壳
|
||||
- [ ] 将独立导图页优先迁到 Rust Web 层
|
||||
- [ ] 将文档内嵌导图降级为轻预览或轻交互卡片
|
||||
- [ ] 把导图工具栏、侧栏、缩略图等拆成局部 island
|
||||
- [ ] 去掉对 BlockNote editor context 的强依赖
|
||||
- [ ] 去掉“文档内嵌导图”和“独立导图页”共用同一重前端壳的绑定方式
|
||||
|
||||
### 11.3 完成判定
|
||||
|
||||
- [ ] Mindmap 已可以在不依赖 BlockNote 主运行时的情况下独立打开
|
||||
- [ ] 文档页不再因为导图组件而被迫挂载整套导图交互壳
|
||||
- [ ] 导图对象已进入 Rust 主线,而不是继续主要依赖前端组件状态
|
||||
|
||||
### 11.4 当前落地状态(2026-04-16)
|
||||
|
||||
- [x] 独立导图页已切到 `StandaloneMindmapView`,不再依赖 fake `editorStub` 或 BlockNote editor context。
|
||||
- [x] 文档内嵌导图已降级为轻预览 / 轻交互入口,文档主链不再因为独立导图壳而被迫进入重运行态。
|
||||
- [x] Mindmap AI 与导图独立页都已按对象级接缝继续走 Rust / bridge 主线,而不是继续把主逻辑绑在文档编辑器里。
|
||||
|
||||
---
|
||||
|
||||
## 12. Phase 7:文档编辑态与 `BlockNote` 孤岛化
|
||||
|
||||
### 12.1 目标
|
||||
|
||||
最后再处理真正难替代的 `BlockNote`,把它从“默认页面入口前提”改成“按需挂载的重编辑岛”。
|
||||
|
||||
### 12.2 实施清单
|
||||
|
||||
- [ ] 明确阅读态与编辑态的切换协议
|
||||
- [ ] 明确进入编辑态时的最小初始化协议
|
||||
- [ ] 将 `BlockNote` 依赖的外围功能继续向外剥离
|
||||
- [ ] 将评论、回链、历史、AI、页面选项等从编辑器默认初始化链上移走
|
||||
- [ ] 将自定义 block 的读取协议改成可按需注入
|
||||
- [ ] 将只读渲染与可编辑渲染彻底拆开
|
||||
- [ ] 将文档打开路径改成“阅读态常驻,编辑态进入时挂编辑岛”
|
||||
- [ ] 为编辑岛建立独立错误边界
|
||||
- [ ] 为编辑岛建立独立性能采样
|
||||
- [ ] 为编辑岛建立独立恢复机制
|
||||
|
||||
### 12.3 禁止事项
|
||||
|
||||
- [ ] 不允许在这一步之前就试图整体替换全部编辑器能力
|
||||
- [ ] 不允许让阅读页重新回退为“先挂编辑器再展示内容”
|
||||
- [ ] 不允许把新的外围面板重新绑回 `BlockNote` 初始化链
|
||||
|
||||
### 12.4 完成判定
|
||||
|
||||
- [ ] 绝大多数页面访问不需要等待 `BlockNote`
|
||||
- [ ] 编辑器只在真正进入编辑态时才挂载
|
||||
- [ ] `BlockNote` 已成为孤岛,而不是整个页面系统的基础前提
|
||||
|
||||
### 12.5 当前落地状态(2026-04-16)
|
||||
|
||||
- [x] 阅读态与编辑态的切换协议已经落地:默认阅读、显式进入编辑、`openTableId` 强制编辑、退出编辑后保留短暂 grace period 再卸载编辑岛。
|
||||
- [x] 评论、历史、回链、页面 AI、页面选项等外围能力都已从 `BlockNote` 默认初始化链外移,阅读态可以单独存在。
|
||||
- [x] `DocumentReadView` 与 `BlockNoteEditor` 已形成两套不同初始化协议,`BlockNote` 现在是按需进入的重编辑 island,而不是整页入口前提。
|
||||
|
||||
---
|
||||
|
||||
## 13. Phase 8:旧前端壳下线与兼容清理
|
||||
|
||||
### 13.1 目标
|
||||
|
||||
在新结构稳定后,清理旧壳,避免双栈长期共存。
|
||||
|
||||
### 13.2 实施清单
|
||||
|
||||
- [ ] 盘点仍保留的 Next 页面壳与 API 壳
|
||||
- [ ] 标记哪些属于长期兼容层
|
||||
- [ ] 标记哪些属于可删除过渡层
|
||||
- [ ] 删除已被 Rust Web 层替代的 route
|
||||
- [ ] 删除已被 Rust islands 替代的全局客户端面板
|
||||
- [ ] 删除失效的桥接 helper、旧 registry、旧 adapter
|
||||
- [ ] 清理不再使用的布局状态容器
|
||||
- [ ] 清理不再使用的动态导入链
|
||||
- [ ] 更新运行文档与架构文档
|
||||
- [ ] 更新开发约束,禁止回流到旧模式
|
||||
|
||||
### 13.3 完成判定
|
||||
|
||||
- [ ] 旧前端壳已不再是主路径
|
||||
- [ ] 双栈只是短期兼容,而不是长期事实
|
||||
- [ ] 维护成本已经从“双系统并行”回到“单主线演进”
|
||||
|
||||
### 13.4 当前收缩口径(2026-04-16)
|
||||
|
||||
- [x] 已明确当前仍保留的 Next/React 页面壳:文档页、Sidebar、搜索、AI host 与 OnlyOffice 页面仍在兼容层内,但重运行态已被拆到按需 island。
|
||||
- [x] 已完成这一轮最小清理:搜索与各类 AI 面板的重量 runtime 不再默认跟随主布局常驻,`GlobalAiAgentHost` 继续停留在实验入口而不是 app layout 主链。
|
||||
- [x] 已把长期清单、长期架构方案与 harness 状态统一回写,后续 Phase 8 继续以“删旧壳前先写清保留边界”为准,而不是靠误删制造假完成。
|
||||
|
||||
---
|
||||
|
||||
## 14. 横向能力清单
|
||||
|
||||
这些事情不属于单一阶段,但必须贯穿全部阶段推进。
|
||||
|
||||
### 14.1 观测与性能
|
||||
|
||||
- [ ] 建立统一性能指标:首字节、首屏、交互可用、切页耗时、编辑器挂载耗时
|
||||
- [ ] 建立统一错误指标:页面壳错误、island 错误、编辑器错误、桥接错误
|
||||
- [ ] 建立统一 trace 关联:页面请求、Rust query、Rust command、Hermes tool、前端 island
|
||||
- [ ] 建立按页面类型的性能对比面板
|
||||
|
||||
最低交付物:
|
||||
|
||||
- [x] 每个页面壳请求都必须生成 `request_id` / `trace_id`,并能串到 Rust query/command 与 Hermes tool。
|
||||
- [x] 文档页、Sidebar、搜索、AI、Mindmap、编辑岛都要有统一的 performance mark 命名规则。
|
||||
- [x] 回归脚本必须至少覆盖:文档页、Mindmap、OnlyOffice、AI tool runtime、Sidebar/文件树定向测试。
|
||||
|
||||
### 14.2 缓存与预取
|
||||
|
||||
- [ ] 建立阅读页缓存策略
|
||||
- [ ] 建立树结构局部缓存策略
|
||||
- [ ] 建立搜索结果缓存策略
|
||||
- [ ] 建立文档邻近页面预取策略
|
||||
- [ ] 建立失败回退与缓存失效策略
|
||||
|
||||
最低交付物:
|
||||
|
||||
- [x] 阅读页采用“meta + content”同一读链缓存键,不能再拆成首屏后二次正文请求作为默认主路径。
|
||||
- [x] 树结构、搜索结果、邻近页面预取都必须显式声明失效来源,避免隐式常驻缓存。
|
||||
- [x] 失败回退策略必须写明是回源、回旧壳还是退到只读页面。
|
||||
|
||||
### 14.3 安全与权限
|
||||
|
||||
- [ ] 统一页面壳权限校验
|
||||
- [ ] 统一对象级读写鉴权
|
||||
- [ ] 统一浏览器 island 的最小权限输入
|
||||
- [ ] 统一 AI / Hermes 的工具权限边界
|
||||
|
||||
最低交付物:
|
||||
|
||||
- [x] 页面壳、对象查询、对象写入、AI tool 调用必须复用同一 workspace / actor / tenant 上下文。
|
||||
- [x] browser island 只能收到最小权限输入,不能自己推导更高权限。
|
||||
- [x] Hermes 与 mnote Rust 之间的 bridge 必须只暴露最小业务能力面,不复制前端编排逻辑。
|
||||
|
||||
### 14.4 开发规范
|
||||
|
||||
- [ ] 新功能默认先判断是否属于 server-first 页面
|
||||
- [ ] 新交互默认先判断是否可以做成局部 island
|
||||
- [ ] 新业务规则默认只进 Rust core
|
||||
- [ ] 新页面默认不能直接复制旧重前端壳模式
|
||||
|
||||
最低交付物:
|
||||
|
||||
- [x] 提交新页面时必须回答四个问题:是否 server-first、是否 island、对象规则是否进 Rust、是否引入新的常驻壳。
|
||||
- [x] 没有 trace / 缓存 / 权限口径的新页面或新 island,不允许直接并入长期主线。
|
||||
|
||||
---
|
||||
|
||||
## 15. 最终验收清单
|
||||
|
||||
只有下面这些问题都能回答“是”,这条长期路线才算真正跑通。
|
||||
|
||||
- [ ] 打开一个普通文档时,是否已经可以先稳定进入阅读态
|
||||
- [ ] 切换页面时,是否已经不再默认触发重编辑器初始化
|
||||
- [ ] Sidebar 是否已经不再是主布局里的超大客户端组件
|
||||
- [ ] 搜索是否已经不再依赖全局大组件常驻
|
||||
- [ ] AI 面板是否已经变成纯桥接 island
|
||||
- [ ] Mindmap 是否已经能独立于 `BlockNote` 主运行时工作
|
||||
- [ ] Rust Web 层是否已经能承接主 API、主页面壳、主流式链路
|
||||
- [ ] Rust core 是否已经成为唯一业务真执行面
|
||||
- [ ] `BlockNote` 是否已经被延后为最后处理的孤岛,而不是继续绑住整页架构
|
||||
- [ ] 旧前端壳是否已经开始实质性退场,而不是继续成为默认主路径
|
||||
|
||||
当前可回答“是”的项(2026-04-16):
|
||||
|
||||
- [x] 打开一个普通文档时,已经可以先稳定进入阅读态。
|
||||
- [x] 切换页面时,已经不再默认触发重编辑器初始化。
|
||||
- [x] 搜索已经不再依赖全局大组件常驻。
|
||||
- [x] AI 面板已经收口为纯桥接 island。
|
||||
- [x] Mindmap 已能独立于 `BlockNote` 主运行时工作。
|
||||
- [x] `BlockNote` 已被延后为最后处理的孤岛,而不是继续绑住整页架构。
|
||||
|
||||
---
|
||||
|
||||
## 16. 一句话执行顺序
|
||||
|
||||
如果后续需要不断回看,这条路线可以压缩成一句话:
|
||||
|
||||
> **先冻结旧壳扩张,再立 Rust Web 层;先迁阅读页、Sidebar、搜索、AI、Mindmap,最后才处理 `BlockNote`;稳定后再清旧壳,而不是反过来。**
|
||||
+526
@@ -0,0 +1,526 @@
|
||||
# 4-1 [recycle] Sidebar / 页面树 / 文件树 产品级差距分析 v1
|
||||
|
||||
> 更新时间:2026-04-17
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/process/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-1-tree-first-graph-kernel-checklist-v2.md`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份文档不回答:
|
||||
|
||||
- “当前新树能不能先凑合继续用”
|
||||
- “再补一点样式是不是就够了”
|
||||
|
||||
这份文档回答的是:
|
||||
|
||||
> **当前 git 中已经接入的 Rust Web tree shell,距离“文件树对标 VS Code、页面树对标 Wolai / Notion”的产品目标还差什么,以及下一步应该如何推进。**
|
||||
|
||||
结论先固定:
|
||||
|
||||
- 当前方向没有跑偏
|
||||
- 当前实现仍然只是过渡 shell,不是产品级树控件
|
||||
- 下一步重点不是继续修饰过渡 shell,而是进入“产品级树域控件重建”
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前实现所处阶段
|
||||
|
||||
根据当前 git 中未提交改动,现状应被定义为:
|
||||
|
||||
- 已完成 tree shell 挂载位
|
||||
- 已完成 `mnote-web /tree` 路由与基本 command 回写
|
||||
- 已完成 Sidebar / 文件树 / picker 的渐进切流入口
|
||||
- 尚未完成产品级树交互能力重建
|
||||
|
||||
也就是说,当前完成的是:
|
||||
|
||||
- **协议验证**
|
||||
- **切流验证**
|
||||
- **最小可运行壳验证**
|
||||
|
||||
而不是:
|
||||
|
||||
- **VS Code 级文件树**
|
||||
- **Wolai / Notion 级页面树**
|
||||
|
||||
### 2.1 当前代码中已经成立的部分
|
||||
|
||||
- `wolai-frontend` 已经可以把树域挂到 `mnote-web tree shell`
|
||||
- `mnote-web` 已经可以输出基础 tree projection 并承接 create / rename / move
|
||||
- 新旧树之间已经有 feature flag 与 fallback
|
||||
|
||||
对应实现可参考:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/MnoteWebTreeShell.tsx`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/tree.rs`
|
||||
|
||||
### 2.2 当前实现不能被误判为“已完成重构”的原因
|
||||
|
||||
当前 `mnote-web tree shell` 仍然具有明显过渡壳特征:
|
||||
|
||||
- 通过 `iframe + postMessage + fallback` 接入主站
|
||||
- 壳内 UI 仍是手写 HTML / CSS / DOM 逻辑
|
||||
- 交互动作仍大量依赖 `prompt` / `alert`
|
||||
- 没有完成稳定的 row model / focus model / selection model / keyboard model / DnD state machine
|
||||
|
||||
这类实现适合做:
|
||||
|
||||
- 协议对齐
|
||||
- route 验证
|
||||
- 真机切流
|
||||
|
||||
不适合直接作为:
|
||||
|
||||
- 最终产品树控件
|
||||
|
||||
---
|
||||
|
||||
## 3. 现阶段的关键判断
|
||||
|
||||
### 3.1 方向没有错
|
||||
|
||||
当前路线与长期架构是一致的:
|
||||
|
||||
- 树真相继续下沉到 kernel / projection
|
||||
- 页面树与文件树继续作为 projection family
|
||||
- Sidebar 继续降级为树域承载壳
|
||||
|
||||
所以,“把树域逐步从旧 React 组件中剥离出来”这个方向是对的。
|
||||
|
||||
### 3.2 体验回退是阶段性真实现象
|
||||
|
||||
用户现在觉得新树在操作和界面上与目标产品差距很大,这个判断是正确的。
|
||||
|
||||
原因不是:
|
||||
|
||||
- 目标错了
|
||||
|
||||
而是:
|
||||
|
||||
- 当前切进去的是过渡 shell
|
||||
- 旧 React 树实际上已经沉淀了一部分成熟交互
|
||||
- 新 Rust Web 壳还没有把这些产品交互能力重新建起来
|
||||
|
||||
### 3.3 当前最危险的误区
|
||||
|
||||
当前最应该避免的,不是“改慢一点”,而是下面两个误区:
|
||||
|
||||
- 误区 A:把过渡 shell 继续当最终产品打磨
|
||||
- 误区 B:看到效果差,就退回“继续长期维持旧树 + 新协议双轨”
|
||||
|
||||
正确做法是:
|
||||
|
||||
- 承认当前只是过渡壳
|
||||
- 用它验证协议与切流
|
||||
- 然后进入产品级树控件重建
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前实现与目标产品的差距矩阵
|
||||
|
||||
## 4.1 文件树:目标应对标 VS Code Explorer
|
||||
|
||||
这里的“对标”不是抄界面,而是对标:
|
||||
|
||||
- 行为模型
|
||||
- 信息密度
|
||||
- 交互反馈
|
||||
- 结构表达
|
||||
|
||||
### 当前已经具备的部分
|
||||
|
||||
- 已有文件树入口
|
||||
- 已有页面与附件的基础层级表达
|
||||
- 已有打开页面 / 打开附件的最小动作链路
|
||||
- 主站旧文件树中已沉淀部分 VS Code 风格拖放语义
|
||||
|
||||
### 当前缺失的关键能力
|
||||
|
||||
- 缺少稳定的文件树专用 row model
|
||||
- 缺少真实的 `filetree` 模式闭环
|
||||
- 缺少多选与连续选择
|
||||
- 缺少键盘导航
|
||||
- 缺少目录型拖放状态机
|
||||
- 缺少重命名内联编辑
|
||||
- 缺少右键菜单体系
|
||||
- 缺少 hover 工具动作
|
||||
- 缺少大树虚拟化与增量展开策略
|
||||
- 缺少图标语义与资源类型区分
|
||||
|
||||
### 当前与 VS Code 的本质差距
|
||||
|
||||
当前新壳更像:
|
||||
|
||||
- “能渲染树结构的调试页”
|
||||
|
||||
而 VS Code Explorer 是:
|
||||
|
||||
- “高密度、可键盘驱动、可多选、可拖放、可重命名、可上下文操作的文件资源管理器”
|
||||
|
||||
所以文件树下一阶段不应再以“补几个按钮”为目标,而应以“重建 Explorer 行为模型”为目标。
|
||||
|
||||
## 4.2 页面树:目标应对标 Wolai / Notion
|
||||
|
||||
这里的“对标”不是纯视觉复刻,而是对标:
|
||||
|
||||
- 页面层级表达方式
|
||||
- hover 操作节奏
|
||||
- 新建 / 展开 / 拖拽 / 上下文动作的一致性
|
||||
- 页面树作为知识库导航入口的轻量感
|
||||
|
||||
### 当前已经具备的部分
|
||||
|
||||
- 已有页面树 projection 主链
|
||||
- 已有基础展开 / 新建 / 重命名 / 上下移动作
|
||||
- 已有主站切流挂载位
|
||||
|
||||
### 当前缺失的关键能力
|
||||
|
||||
- 缺少 hover 暴露的轻量动作区
|
||||
- 缺少更细的页面类型 / 状态表达
|
||||
- 缺少更自然的层级拖拽交互
|
||||
- 缺少键盘导航与 focus 管理
|
||||
- 缺少右键菜单与上下文操作体系
|
||||
- 缺少行级局部状态管理
|
||||
- 缺少高密度列表的稳定渲染与滚动体验
|
||||
- 缺少与搜索、跳转、最近访问状态的联动边界
|
||||
|
||||
### 当前与 Wolai / Notion 的本质差距
|
||||
|
||||
当前新壳更像:
|
||||
|
||||
- “展示树数据并支持几个命令”
|
||||
|
||||
而 Wolai / Notion 页面树更接近:
|
||||
|
||||
- “低干扰导航器 + 轻量页面管理器”
|
||||
|
||||
所以页面树下一阶段的重心不是“增加更多按钮”,而是:
|
||||
|
||||
- 让操作默认隐藏、按需显现
|
||||
- 让层级结构更轻
|
||||
- 让拖拽、展开、新建、上下文菜单进入一致的节奏
|
||||
|
||||
## 4.3 Sidebar:目标不是单独对标某产品,而是成为稳定壳
|
||||
|
||||
在长期架构中,Sidebar 的任务不是持有树真相,而是:
|
||||
|
||||
- 承载 projection
|
||||
- 承载搜索入口
|
||||
- 承载快速切换
|
||||
- 承载少量工作区级操作
|
||||
|
||||
所以 Sidebar 的核心问题不是“左栏长什么样”,而是:
|
||||
|
||||
- 树域壳与主站其它能力之间的耦合是否被切干净
|
||||
|
||||
当前 Sidebar 仍然偏重,说明下一阶段除了树控件本身,还要继续做:
|
||||
|
||||
- Sidebar 壳职责收敛
|
||||
- 树域与其它面板解耦
|
||||
- 搜索 / AI / 树域三者的边界整理
|
||||
|
||||
---
|
||||
|
||||
## 5. 当前代码中的具体偏差
|
||||
|
||||
## 5.1 `filetree` 模式闭环仍不完整
|
||||
|
||||
当前主站已向新壳传入 `mode=\"filetree\"`,但 Rust route 与壳内脚本对模式的识别还没有完整闭环。
|
||||
|
||||
这意味着当前“文件树已切到新壳”不能简单视为已经完成。
|
||||
|
||||
这一点必须优先修正,因为它会直接影响:
|
||||
|
||||
- 文件树功能判断
|
||||
- 测试结果判断
|
||||
- 后续重构任务拆分
|
||||
|
||||
## 5.2 新壳仍是协议验证页,不是产品树控件
|
||||
|
||||
当前树壳使用:
|
||||
|
||||
- 手写 HTML 模板
|
||||
- 手写 DOM 生成树节点
|
||||
- 手写按钮动作
|
||||
|
||||
这在协议验证阶段是合理的,但不应继续长期积累。
|
||||
|
||||
如果继续在这一层追加:
|
||||
|
||||
- hover 细节
|
||||
- 菜单
|
||||
- 多选
|
||||
- DnD
|
||||
- keyboard
|
||||
|
||||
最终只会把过渡壳演化成难维护的第二套前端。
|
||||
|
||||
## 5.3 旧树的成熟交互能力尚未被系统迁移
|
||||
|
||||
旧文件树与旧页面树中,已经沉淀出一部分成熟能力:
|
||||
|
||||
- 文件树的拖放反馈
|
||||
- 文件树的多选与内部拖放
|
||||
- 页面树的虚拟化
|
||||
- 页面树的拖拽排序
|
||||
|
||||
这些能力现在还没有以“协议化行为模型”的方式迁移进新树域,而是仍然留在旧 React 实现里。
|
||||
|
||||
这意味着下一阶段不能只盯新壳,还要做一件关键工作:
|
||||
|
||||
- 把旧树中已经证明有效的交互经验抽象成正式产品合同
|
||||
|
||||
---
|
||||
|
||||
## 6. 下一阶段应如何指导修改
|
||||
|
||||
## 6.1 原则一:停止把过渡 shell 当最终实现打磨
|
||||
|
||||
接下来不应继续以如下方式推进:
|
||||
|
||||
- “再补几个按钮”
|
||||
- “再修一版样式”
|
||||
- “再把这个 iframe 页面做像一点”
|
||||
|
||||
这些动作只能缓解表面问题,不能得到产品级树控件。
|
||||
|
||||
正确方式是:
|
||||
|
||||
- 把当前壳明确标记为过渡验证层
|
||||
- 只修协议、切流、阻塞性错误
|
||||
- 不在这里继续堆复杂交互
|
||||
|
||||
## 6.2 原则二:先冻结产品交互合同,再进入 Rust Web 正式实现
|
||||
|
||||
下一阶段首先要做的,不是直接写更多 UI,而是冻结两份合同:
|
||||
|
||||
- 文件树产品交互合同
|
||||
- 页面树产品交互合同
|
||||
|
||||
这两份合同至少应明确:
|
||||
|
||||
- 行模型
|
||||
- 层级缩进规则
|
||||
- active / selected / focused / dragging 的区别
|
||||
- hover 暴露策略
|
||||
- 右键菜单入口
|
||||
- 键盘导航规则
|
||||
- 多选规则
|
||||
- 拖放语义
|
||||
- 内联重命名规则
|
||||
- 空白区与容器区行为
|
||||
|
||||
## 6.3 原则三:旧树不是要照抄,而是要提炼成熟行为
|
||||
|
||||
当前仓库里旧树实现仍然有直接参考价值:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/file-tree.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/private-tree.tsx`
|
||||
|
||||
它们的价值不在于:
|
||||
|
||||
- 继续长期保留 React 版本
|
||||
|
||||
而在于:
|
||||
|
||||
- 可以作为“已在当前产品中验证过的交互基线”
|
||||
|
||||
下一阶段应从它们中提炼:
|
||||
|
||||
- 哪些交互是必须保留的
|
||||
- 哪些只是过渡做法
|
||||
- 哪些要升级成正式协议字段或 UI 状态机
|
||||
|
||||
## 6.4 原则四:文件树与页面树应共用一套树行为骨架
|
||||
|
||||
下一阶段不要再走两套完全独立实现。
|
||||
|
||||
应采用:
|
||||
|
||||
- 同一套 tree row model
|
||||
- 同一套 selection / focus / keyboard / drag state
|
||||
- 同一套 command dispatch
|
||||
|
||||
然后在 projection 层区分:
|
||||
|
||||
- `page_tree`
|
||||
- `file_tree`
|
||||
|
||||
这样才能保持:
|
||||
|
||||
- 文件树不是孤立系统
|
||||
- 页面树不是孤立系统
|
||||
- 两者继续服从 `tree-first graph kernel`
|
||||
|
||||
## 6.5 原则五:最终目标应是产品级 Rust Web tree shell,而不是长期 iframe 壳
|
||||
|
||||
长期上,目标应当是:
|
||||
|
||||
- Rust Web 正式树控件
|
||||
- 直接消费 projection protocol
|
||||
- 直接发 command protocol
|
||||
- 主站以内嵌挂载或原生整合方式承载
|
||||
|
||||
而不是:
|
||||
|
||||
- 长期保留 `iframe + postMessage + fallback` 作为正式方案
|
||||
|
||||
`iframe` 在当前阶段的价值是:
|
||||
|
||||
- 降风险切流
|
||||
- 降低对主站的侵入
|
||||
|
||||
它不应成为最终交付形态。
|
||||
|
||||
---
|
||||
|
||||
## 7. 建议采用的参考体系
|
||||
|
||||
## 7.1 最值得参考:行为模型参考
|
||||
|
||||
优先级最高的不是某个“现成树组件”,而是行为模型参考。
|
||||
|
||||
推荐优先参考:
|
||||
|
||||
- `headless-tree`
|
||||
|
||||
主要借鉴内容:
|
||||
|
||||
- row model
|
||||
- selection
|
||||
- focus
|
||||
- keyboard
|
||||
- drag and drop 状态拆分
|
||||
|
||||
这类参考最适合解决:
|
||||
|
||||
- 为什么树一旦复杂就开始失控
|
||||
- 为什么文件树与页面树经常需要重复写交互
|
||||
|
||||
## 7.2 产品结构参考
|
||||
|
||||
推荐参考:
|
||||
|
||||
- `AppFlowy`
|
||||
- `AFFiNE`
|
||||
|
||||
主要借鉴内容:
|
||||
|
||||
- Sidebar 与页面树的产品层边界
|
||||
- 工作区 / 页面 / 文档之间的协作关系
|
||||
- 页面树如何作为知识产品的导航层
|
||||
|
||||
不建议直接借:
|
||||
|
||||
- 大量样式实现细节
|
||||
- 与当前架构不一致的事实源模型
|
||||
|
||||
## 7.3 Rust Web UI 参考
|
||||
|
||||
推荐参考:
|
||||
|
||||
- `Leptos`
|
||||
- `radix-leptos`
|
||||
- `thaw`
|
||||
|
||||
主要用途:
|
||||
|
||||
- 实现正式 Rust Web tree shell
|
||||
- 补齐 collapsible / menu / scroll area / overlay 等 primitives
|
||||
|
||||
## 7.4 不建议作为主导参考的对象
|
||||
|
||||
以下可以看,但不应成为主导路线:
|
||||
|
||||
- 终端树组件
|
||||
- 桌面 GUI 树组件
|
||||
- 纯列浏览器方案
|
||||
|
||||
原因是它们无法直接回答当前最核心的问题:
|
||||
|
||||
- 如何在 Web 主站中实现产品级页面树 / 文件树
|
||||
|
||||
---
|
||||
|
||||
## 8. 下一阶段的任务拆分建议
|
||||
|
||||
## 8.1 阶段 A:修正当前过渡壳中的协议闭环问题
|
||||
|
||||
只处理阻塞项:
|
||||
|
||||
- 修正 `filetree` 模式闭环
|
||||
- 校正 projection / mode / consumer 对应关系
|
||||
- 补齐最小测试
|
||||
|
||||
这一阶段不做:
|
||||
|
||||
- 大规模 UI 打磨
|
||||
- 新交互堆叠
|
||||
|
||||
## 8.2 阶段 B:产出页面树 / 文件树产品交互合同
|
||||
|
||||
必须单独落文档,至少包含:
|
||||
|
||||
- 文件树对标 VS Code 的功能矩阵
|
||||
- 页面树对标 Wolai / Notion 的功能矩阵
|
||||
- 当前已实现 / 未实现 / 不做 的判定
|
||||
- 对 projection 和 command 的新增要求
|
||||
|
||||
## 8.3 阶段 C:实现正式 tree row model 与状态骨架
|
||||
|
||||
这一阶段要优先完成:
|
||||
|
||||
- row model
|
||||
- selection model
|
||||
- focus model
|
||||
- keyboard model
|
||||
- drag state
|
||||
- context menu entry model
|
||||
|
||||
这一层最好先独立,再挂 UI。
|
||||
|
||||
## 8.4 阶段 D:实现产品级 Rust Web tree shell
|
||||
|
||||
这一阶段才进入正式 UI:
|
||||
|
||||
- 页面树 renderer
|
||||
- 文件树 renderer
|
||||
- picker renderer
|
||||
- 统一 shell 挂载
|
||||
|
||||
## 8.5 阶段 E:收敛旧树实现
|
||||
|
||||
最后再做:
|
||||
|
||||
- 旧文件树 helper 清理
|
||||
- 旧页面树 helper 清理
|
||||
- 旧 Sidebar 树域状态清理
|
||||
- 旧 consumer 收口
|
||||
|
||||
---
|
||||
|
||||
## 9. 最终结论
|
||||
|
||||
当前实现与设计之间,不存在“方向性错误”,但存在明显的“阶段性落差”。
|
||||
|
||||
这个落差的本质不是:
|
||||
|
||||
- 少几个按钮
|
||||
- 样式还不够像
|
||||
|
||||
而是:
|
||||
|
||||
- 当前完成的是树域过渡 shell
|
||||
- 目标要求的是产品级树控件系统
|
||||
|
||||
因此,接下来应明确口径:
|
||||
|
||||
- 当前 git 中的新树实现,定义为**过渡验证层**
|
||||
- 下一阶段任务,定义为**产品级树域控件重建**
|
||||
|
||||
只有这样,团队后续的修改方向才不会继续发散。
|
||||
@@ -0,0 +1,658 @@
|
||||
# 4-10 [recycle] 树域 Rust 家族化执行清单 v1
|
||||
|
||||
> 更新时间:2026-04-26
|
||||
>
|
||||
> 回收说明(2026-04-28):本文是早期总执行清单,未完成项已被 `4-11`、`4-16`、`4-18` 拆分并覆盖。本文保留为历史执行口径,不再作为当前 `process` 入口;后续以 `design/04-tree-domain/process/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md` 及 `done/4-11`、`done/4-18` 为准。
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/04-tree-domain/process/4-9-tree-rust-family-cutover-remaining-architecture-and-capability-preservation-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-2-sidebar-pagetree-filetree-product-interaction-contract-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-11-tree-rust-family-final-renderer-and-host-thinning-checklist-v1.md`
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份清单只服务一件事:
|
||||
|
||||
> **把树域从“Rust 持有语义、React 持有主执行面”的过渡态,推进到“Rust 家族同时持有语义与主执行面”的下一阶段。**
|
||||
|
||||
本文不是上位方案稿,不重复讨论:
|
||||
|
||||
- 为什么要继续 Rust 家族化
|
||||
- 为什么 `Convex` 要保留
|
||||
- 为什么 `page tree / file tree` 不能退化
|
||||
|
||||
这些已经在 `4-9` 与关联文档中固定。
|
||||
|
||||
本文只给:
|
||||
|
||||
- 分阶段执行顺序
|
||||
- 每阶段 checklist
|
||||
- 每阶段阻塞项
|
||||
- 每阶段验收口径
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前基线
|
||||
|
||||
当前默认真实状态(2026-04-25):
|
||||
|
||||
- 树域 truth 已收口到 `tree-first graph kernel`
|
||||
- `Convex` 继续提供存储、同步、内容与实时底座
|
||||
- `mnote-web` 已具备 projection / command / transport 基础出口
|
||||
- `page tree / file tree / picker` 已开始消费 projection family
|
||||
- 主站默认 `treeRendererFamily` 已切到 `rust_family`
|
||||
- `page tree / file tree / picker` 默认主路径已进入 same-origin iframe compat host
|
||||
- 但当前切主的是 `compat host`,不是 final Rust renderer;宿主与 fallback 仍保留
|
||||
|
||||
因此本清单的目标不是“再做一轮 projection 收口”,而是:
|
||||
|
||||
- 补齐 Rust 侧剩余契约
|
||||
- 迁掉主路径 React 树 renderer
|
||||
- 保住 `page tree / file tree` 现有能力
|
||||
|
||||
---
|
||||
|
||||
## 3. 总体执行顺序
|
||||
|
||||
建议固定为下面五段:
|
||||
|
||||
1. `Phase A`:补齐 `file_tree` Rust 直接输出
|
||||
2. `Phase B`:完成 `tree.*` 命令主路径切换
|
||||
3. `Phase C`:补齐树域正式 realtime contract
|
||||
4. `Phase D`:迁移 `page tree` Rust 家族 renderer
|
||||
5. `Phase E`:迁移 `file tree / picker` Rust 家族 renderer,并收薄宿主
|
||||
|
||||
原因:
|
||||
|
||||
- 先补 `file_tree` 输出,避免 renderer 迁移时继续背前端 adapter。
|
||||
- 先收命令与 realtime,避免 UI 壳迁完后还卡在旧协议。
|
||||
- `page tree` 先迁,能先验证主行 renderer 与交互骨架。
|
||||
- `file tree` 后迁,避免最复杂对象面成为第一块落地风险点。
|
||||
|
||||
---
|
||||
|
||||
## 4. Phase A:补齐 `file_tree` Rust 直接输出
|
||||
|
||||
### 当前进度(2026-04-25)
|
||||
|
||||
- [x] 前端 `file tree` 主路径已优先消费 Rust `kernelFileTreeProjection.items`
|
||||
- [x] `/api/sidebar`、SSR loader、`/api/mnote-web/stream` 已补 `kernelFileTreeProjection`
|
||||
- [x] 非过滤主路径不再默认依赖 `pageRows + assetsByDoc` 拼装
|
||||
- [x] `file tree` 同源 host 的 inline override 已收口到 `kernel file_tree items`
|
||||
- `inlineFileTreeRows` 旧注入边界已移除
|
||||
- `TreeShellIframeHost` 不再接受 `FileTreeRow[]` 作为正式 inline renderer 输入
|
||||
- [x] 搜索过滤态不再回退 `pageRows + assets` 二次重建
|
||||
- `buildVisibleRows` 缺少 `kernel file_tree items` 时稳定返回空列表
|
||||
- 旧 `filterKernelFileTreeProjectionItems` 宿主裁剪 helper 已移除
|
||||
- 正式 Rust 搜索 projection 已在 `4-11 Phase H` 接入 3000 同源主链
|
||||
|
||||
### 目标
|
||||
|
||||
让 `file_tree` 不再依赖当前前端在 `pageRows + assets` 上继续拼主路径可见行语义。
|
||||
|
||||
### checklist
|
||||
|
||||
- [x] 盘点当前 `file_tree` 仍由前端 adapter 补齐的对象语义:
|
||||
- `index.md`
|
||||
- 附件
|
||||
- `mindmap` 文件夹
|
||||
- 导图子附件
|
||||
- 表格 / 书籍 / PDF 等扩展对象
|
||||
- [x] 在 Rust 侧明确 `file_tree` projection 的正式输出字段:
|
||||
- `resource_kind`
|
||||
- `asset_kind`
|
||||
- `icon_hint`
|
||||
- `expandable`
|
||||
- `expanded_by_default`
|
||||
- [x] 为 `index / asset / asset_folder / mindmap / table / book / pdf` 补 fixture
|
||||
- `tree-delta.test.ts` 已覆盖 delta 重建后的 `mindmap / pdf / book / table`
|
||||
- `route.test.ts` 已覆盖 snapshot route 中的 `index / asset / asset-folder / mindmap / table / book / pdf`
|
||||
- [x] 为 `file_tree` route 增加契约测试
|
||||
- `/api/mnote-web/stream` route snapshot 已断言 `file_tree` item contract 与 `iconHint`
|
||||
- [x] 把前端 `file tree` 的主路径输入改成“消费 Rust 直接输出的 item 列表”,而不是继续拼对象真相
|
||||
- `sidebar` 主链默认输入已收口到 `kernelFileTreeProjection.items`
|
||||
- `rust_family` host 已不再接受 `inlineFileTreeRows` 旧 rows 注入
|
||||
|
||||
### 阻塞项
|
||||
|
||||
- `file_tree` canonical 基础对象语义已由 projection contract 覆盖
|
||||
- 搜索态已接入 Rust 搜索 projection 主链;更完整的搜索输入扩展与 final renderer 收口已转入 `4-11 Phase H/F`
|
||||
|
||||
### 验收口径
|
||||
|
||||
- `file_tree` 主路径不再依赖临时 adapter 维持对象层级
|
||||
- Rust route 输出足以直接驱动 renderer
|
||||
- `index.md / asset-folder / asset` 行语义在 fixture 与 route test 中稳定存在
|
||||
|
||||
---
|
||||
|
||||
## 5. Phase B:完成 `tree.*` 命令主路径切换
|
||||
|
||||
### 当前进度(2026-04-25)
|
||||
|
||||
- [x] 已确认正式命令名:
|
||||
- `create -> tree.node.create`
|
||||
- `rename -> tree.node.rename`
|
||||
- `move -> tree.subtree.move`
|
||||
- [x] 已新增前端 `/api/tree/commands` route,主路径可直接发正式 tree action
|
||||
- [x] `createDocumentCommand` 已切到 `/api/tree/commands`
|
||||
- [x] `renameDocumentCommand` 已切到 `/api/tree/commands`
|
||||
- [x] `moveDocumentCommand` 已切到 `/api/tree/commands`
|
||||
- [x] `delete / restore / purge / embed / copy-tree` 已切到 `/api/tree/commands`
|
||||
- [x] `create` 保留本地 `ensureDocumentScaffold` 副作用,不因切流丢失
|
||||
- [x] `/api/documents/create` 已收薄为 compat alias,转发 `/api/tree/commands`
|
||||
- [x] `/api/documents/title` 已按命令语义分流:
|
||||
- 树重命名兼容请求 -> `/api/tree/commands`
|
||||
- `page.head.updateTitle` -> `page write`
|
||||
- [x] `/api/documents/move` 已收薄为 compat alias,转发 `/api/tree/commands`
|
||||
- [x] `/api/documents/delete / restore / purge / embed / copy-tree` 已收薄为 compat alias,转发 `/api/tree/commands`
|
||||
- [x] `bridge-runtime` 已补 `tree.node.archive / restore / purge / embed` 与 `tree.subtree.copy` 别名执行计划
|
||||
- [x] `tree.*` 主路径已补路由 / shared client / compat alias / runtime 回归测试
|
||||
- [x] `move` 的 target-parent legality、self / descendant 拦截、canonical `parent_id / sort_order / workspace_id / updated_at` 已下沉到 Convex `documents.move`
|
||||
- [x] `move legality` 已继续离开 Next route 结果拼装,开始转到 Rust bridge-runtime 计算
|
||||
- `/api/tree/commands` 不再自己构造 `movePreflight`
|
||||
- Next route 现在只把 `documents` 快照作为 `preflightData` 交给 Rust
|
||||
- Rust bridge-runtime 已可从快照推导 target parent / ancestor chain 并拒绝 descendant / missing-parent
|
||||
- [ ] `move legality` 与更完整排序规则仍未完全进入最终 Rust kernel 主链;当前实际写入仍停留在 Convex compat mutation
|
||||
- Rust `bridge-runtime` 已新增 canonical move order plan,并在 command plan 中输出 `normalizedMove`
|
||||
- Convex `documents.move` 已增加 `normalizedMove` 可选校验,执行前确认当前 patch plan 与 Rust plan 一致
|
||||
- 最终 Rust 写路径执行下沉仍转入 `4-11 Phase I`
|
||||
|
||||
### 目标
|
||||
|
||||
让树域主路径默认走正式 `tree.*` 命令面,`documents.*` 退到 compat。
|
||||
|
||||
### checklist
|
||||
|
||||
- [x] 盘点当前主路径仍在调用的 `documents.*` 入口:
|
||||
- create
|
||||
- rename
|
||||
- move
|
||||
- archive
|
||||
- restore
|
||||
- purge
|
||||
- embed
|
||||
- copy-tree
|
||||
- [x] 明确每条命令对应的 `tree.*` 正式名
|
||||
- [x] 前端 shared command client 默认发 `tree.*`
|
||||
- [x] `documents.*` 仅保留 alias / compat
|
||||
- [ ] 把 move legality、target parent、position normalize 继续从 Convex compat mutation 回收给 Rust kernel
|
||||
- legality 与 normalized order plan 已在 Rust 可测试;TS transport 已透传 `normalizedMove`;Convex 已可选校验 Rust plan;最终写入仍待迁移
|
||||
- [x] 为 `tree.*` 路径补命令回归测试
|
||||
|
||||
### 阻塞项
|
||||
|
||||
- `embed / copy-tree` 仍有较强前端语义残留
|
||||
- move legality 与排序规则已离开 Next route 拼装;排序计划已进入 Rust 可观测 plan 并由 Convex 可选校验,但实际执行仍待下沉
|
||||
|
||||
### 验收口径
|
||||
|
||||
- 新增主路径不再继续扩写 `documents.*`
|
||||
- 主调用路径默认发 `tree.*`
|
||||
- 组件层不再承担长期树命令语义解释
|
||||
|
||||
---
|
||||
|
||||
## 6. Phase C:补齐树域正式 realtime contract
|
||||
|
||||
### 当前进度(2026-04-24)
|
||||
|
||||
- [x] `tree stream` 前端主链已从“freshness 仲裁”收口到“stream live 优先,query/fallback 兜底”
|
||||
- [x] `usePreferredSidebarSnapshot` 已按 `treeStream.status` 做主链选择:
|
||||
- `live / connecting / idle` 有 stream 数据时优先 stream
|
||||
- `fallback` 时回退 query
|
||||
- [x] `useSidebarData` 手动 `refetch` 已收窄为临时 snapshot:
|
||||
- 仅在当前 live 基线未变化时暂时覆盖
|
||||
- live 基线变化后回到新的 live snapshot
|
||||
- [x] `/api/mnote-web/stream` 已升级为同源长连接 SSE:
|
||||
- 首帧固定发 `snapshot`
|
||||
- 后续按最新 `command_log` cursor 轮询检测变化
|
||||
- 当前已支持最小 `delta/noop/resync` 分流,不伪造不稳定的复杂树增量
|
||||
- [x] stream cursor 已从“复用 overview 分页 cursor”改成“stream 自己的 latest command-log cursor”
|
||||
- [x] stream cursor 已补 `domain_events` 感知
|
||||
- 当 `command_logs` 未推进、但 `domain_events` 新增时,不再静默停在旧 cursor
|
||||
- 当前最小口径先触发 `resync`,避免 workspace realtime 漏感知事件侧推进
|
||||
- [x] `workspace / subtree` 双 scope 已在 route + test 形成正式 envelope 边界:
|
||||
- `workspace -> sidebar_tree`
|
||||
- `subtree(rootNodeId) -> page_tree`
|
||||
- [x] `/api/tree/commands` 主路径已开始写入 `bridgeLogs`:
|
||||
- create / rename / archive / purge / embed / copy / move / restore 都会推进 stream 可见 cursor
|
||||
- failed mutation 也会写 failure artifact,但 stream 主查询已收窄为只看 `succeeded`
|
||||
- [x] stream 当前已能消费显式 `streamDelta`:
|
||||
- `create -> upsert_document`
|
||||
- `rename -> upsert_document(局部 patch)`
|
||||
- `page.head.updateTitle -> upsert_document(局部 patch)`
|
||||
- `archive / purge -> remove_document`
|
||||
- `embed -> noop`
|
||||
- `move -> move_document(细粒度 delta)`
|
||||
- `restore -> upsert_document(细粒度 delta)`
|
||||
- `copy -> upsert_documents(细粒度 delta)`
|
||||
- `documents` 列表不可用时,仍保留 `replace_sidebar / replace_documents` 作为兼容兜底
|
||||
- [x] `move / restore / copy` 已从“只能纯 `resync`”推进到显式 delta
|
||||
- `move` 已推进到 `move_document`
|
||||
- `restore` 已推进到 `upsert_document`
|
||||
- `copy` 已推进到 `upsert_documents`
|
||||
- Rust `bridge-runtime` 已开始输出 `streamDeltaHint`,Next route 仅按 Rust plan hint 与 mutation result 物化 delta
|
||||
- [x] 已新增树域 renderer 统一 delta 应用边界:
|
||||
- `tree-delta.ts` 已补 `applyTreeStreamDeltaToProjectionState`
|
||||
- `sidebar_tree / page_tree / file_tree` 已可从同一 delta 边界派生各自输入
|
||||
- [x] `replace_documents` delta 重建时已保留 `mindmapAssetChildren`
|
||||
- `file_tree` 不再把 `asset-folder` 在 delta 重建时退化成普通 `asset`
|
||||
- [x] Rust / 3000 同源 stream 已能从单条新 `domain_event.payload.streamDelta` 发出 `delta`
|
||||
- [x] Rust / 3000 同源 stream 已能对同一 `command_id` 的 command log + domain event 双写做 delta 去重
|
||||
- `streamDelta` 一致时发一次 `delta`
|
||||
- 不一致或多条混杂时继续保守 `resync`
|
||||
- [x] Rust command 侧已开始生成正式 tree delta / event hint:
|
||||
- `streamDeltaHint` 覆盖 create / rename / archive / restore / purge / embed / move / copy
|
||||
- `domainEventHint` 覆盖 `tree.node.created / renamed / archived / restored / purged / embedded / duplicated` 与 `tree.subtree.moved / copied`
|
||||
- Next route 不再按 action 手写主 delta,而是按 Rust plan hint + mutation result 物化 `streamDelta`
|
||||
- [x] resource command 侧已开始进入正式 delta / event hint:
|
||||
- `tree.resource.copy / move / upload` 已输出 `asset_result -> upsert_assets`
|
||||
- `domainEventHint` 覆盖 `tree.resource.copied / moved / uploaded`
|
||||
- 资源 copy/move/upload 的 artifact 已由 3000 Rust artifact writer 持久化
|
||||
- [ ] Rust 写路径仍未覆盖所有主写入口的完整 domain event 持久化
|
||||
- `mnote-web /api/tree/commands` 已可由 Rust transport 直接生成并持久化 `command_logs / domain_events`
|
||||
- 3000 tree/media 主写入口与文档类 Rust transport adapter 已改为 Rust artifact writer:
|
||||
- `/api/tree/commands`
|
||||
- `/api/media/batch`
|
||||
- `/api/media/upload`
|
||||
- `page-command-adapter`
|
||||
- `page-lifecycle-command-adapter` 的 `create / move / delete / restore / duplicate / copy_tree`
|
||||
- `save-command-adapter`
|
||||
- `metadata-command-adapter` 的 `title / stats / options`
|
||||
- `lib/documents/block-command-adapter`
|
||||
- `page-write-command-adapter` 的 `page.head.updateTitle / page.layout.updateOptions / page.body.save`
|
||||
- 当前显式 adapter 层已不再直接调用 TS `recordBridgeCommandArtifacts(...)`
|
||||
- [ ] `3000` 仍是浏览器入口,但 polling transport 主体已在 Rust SSE;Next route 仍保留薄代理与过渡职责
|
||||
|
||||
### 目标
|
||||
|
||||
让树域从“可用 stream + fallback”进入正式 `snapshot + delta + resync` 合同。
|
||||
|
||||
### checklist
|
||||
|
||||
- [x] 明确 workspace 与 subtree 两类 stream scope
|
||||
- [x] 统一 `snapshot / delta / resync / cursor` 协议
|
||||
- `snapshot / cursor` 已在同源 stream route 固定
|
||||
- `delta` 已支持最小显式 contract,但复杂树变更仍待 Rust 正式事件面补齐
|
||||
- `resync` 仍是复杂树变更与漂移场景的保守回退
|
||||
- 当前勾选口径以 `3000` 同源 Next route contract 为准,`server.ts` / `server.test.ts` / `route.test.ts` 已覆盖;Rust 原生 transport 仍待继续收口
|
||||
- [x] 补 cursor 漂移与 resync 策略
|
||||
- [x] 明确大漂移时的回退口径
|
||||
- 当前保守口径:无法稳定解释的新 cursor 统一退 `resync snapshot`
|
||||
- [x] 为 `page tree / file tree / sidebar_tree` 补事件 fixture
|
||||
- `server.test.ts` 已覆盖 `sidebar_tree` snapshot / delta / resync
|
||||
- `route.test.ts` 已覆盖 `page_tree` subtree snapshot fixture
|
||||
- `route.test.ts` 与 `tree-delta.test.ts` 已覆盖 `file_tree` 的 `document / index / asset-folder / asset` 行语义
|
||||
- [x] 为树域 renderer 定义统一 delta 应用边界
|
||||
- 当前口径是 `documents/sidebar -> projection state` 的统一边界,不等于 final Rust domain-event 细粒度 reducer
|
||||
- [x] 让主路径不再主要依赖 freshness 选择与补偿式 refetch
|
||||
- sidebar stream 主链已变成“长连接 snapshot/delta/resync”
|
||||
- query/fallback 继续保留为护栏
|
||||
|
||||
### 阻塞项
|
||||
|
||||
- Next route 级过渡 transport 仍在承担一部分正式职责
|
||||
- `delta` payload 的对象覆盖面还不够完整
|
||||
- Rust command 已给 `move / restore / copy` 输出 delta/event hint;3000 tree/media 主写入口 artifact 持久化与显式 adapter 层已切到 Rust artifact writer;剩余未完成项转入更正式的 block/save-snapshot domain-event contract 收口
|
||||
- final domain event delta contract 已转入 `4-11 Phase J`
|
||||
|
||||
### 验收口径
|
||||
|
||||
- 树域实时主链存在正式协议说明与测试
|
||||
- 主路径能稳定处理 snapshot、delta、resync
|
||||
- fallback 成为护栏,而不是主同步策略本身
|
||||
|
||||
---
|
||||
|
||||
## 7. Phase D:迁移 `page tree` Rust 家族 renderer
|
||||
|
||||
### 当前进度(2026-04-24)
|
||||
|
||||
- [x] 前端 `page tree / file tree` surface 已显式暴露 `rendererFamily` 边界
|
||||
- [x] `SidebarTreeSurface` 已成为更正式的 renderer host 边界,而不只是隐式 React 直连
|
||||
- [x] 已新增 `TreeShellHost`,`page tree / file tree / picker` 在 `rust_family` 下会进入显式宿主分支,而不再只是透传 `data-renderer-family`
|
||||
- [x] 主站已存在极薄挂载位;当前 `rust_family` 分支先受控包住 React fallback,后续再替换成真正的 Rust family renderer
|
||||
- [x] 已新增 `3000` 同源 `/api/tree/shell` proxy route;浏览器不再直接持有 `mnote-web` base url
|
||||
- [x] 未过滤 `page tree` 在 `rust_family` 下已可切到 same-origin iframe compat host,并 bridge:
|
||||
- `tree.navigate`
|
||||
- `tree.page.context-menu`
|
||||
- `tree.node.created / renamed / moved`
|
||||
- [x] `page tree` 已新增 `tree.page.focus.changed` bridge
|
||||
- 宿主已显式持有 `focusedDocumentId` 本地状态
|
||||
- page focus 不再只停留在 iframe 内部私有状态
|
||||
- iframe host 与 Rust shell contract 已补定向测试
|
||||
- [x] 过滤态 `page tree` 在 `rust_family` 下已可通过 inline override 进入 same-origin iframe compat host
|
||||
- 浏览器端直接注入过滤后的 projection rows
|
||||
- 不再因为 filter 非空而强制回退 React fallback
|
||||
- [x] 过滤态 `page tree` 即使筛到 0 条,也会继续通过 inline override 留在 same-origin iframe compat host
|
||||
- [x] 默认主路径已切到 `rust_family` same-origin compat host
|
||||
- `task112` 主站 smoke 已验证 page tree hostKind 为 `iframe`
|
||||
- 当前切主的是 compat host,不等于 final Rust renderer 已完成
|
||||
- [x] `page tree` 主路径 React renderer 已退出
|
||||
- `TreeShellSurface` 缺少 iframe 宿主时只显示 `page-tree-renderer-removed`
|
||||
- 主路径运行时已不再挂载旧 React `PrivateTree`
|
||||
- [x] `task112` 已补 page tree 的 create / rename / 大树滚动真实 smoke
|
||||
- compat shell hover 动作区已用于实跑新建子页面与重命名
|
||||
- 大树场景下,键盘导航可把目标行稳定带入可视区
|
||||
- [ ] 当前 `page tree` 仍是 `mnote-web tree shell compat host`,不是最终 Rust renderer
|
||||
- [ ] 过滤态 `page tree` 当前仍是 compat host + inline override,不是最终 Rust family renderer
|
||||
|
||||
### 目标
|
||||
|
||||
让 `page tree` 的主渲染与主交互骨架退出当前 React 主壳。
|
||||
|
||||
### checklist
|
||||
|
||||
- [ ] 明确 `page tree` renderer 最小输入:
|
||||
- projection items
|
||||
- local UI state
|
||||
- command dispatcher
|
||||
- [ ] 抽离正式模块边界:
|
||||
- row model
|
||||
- focus model
|
||||
- keyboard model
|
||||
- DnD state machine
|
||||
- context action registry
|
||||
- [ ] 实现 Rust 家族 page tree renderer
|
||||
- [x] 迁移展开/折叠
|
||||
- [x] 迁移当前页高亮与祖先自动展开
|
||||
- [x] 迁移 hover 动作区
|
||||
- [x] 迁移右键菜单入口
|
||||
- [x] 迁移键盘导航
|
||||
- [x] 迁移基础拖拽排序
|
||||
- [x] 在主站预留极薄挂载位
|
||||
- [x] 用 feature flag 控制新旧 page tree 切换
|
||||
|
||||
### page tree 能力保留硬门槛
|
||||
|
||||
- [x] 稳定层级展开/折叠
|
||||
- [x] 当前页高亮
|
||||
- [x] 祖先自动展开
|
||||
- [x] 新建子页面
|
||||
- [x] 重命名
|
||||
- [x] 页面移动
|
||||
- [x] 上下文菜单
|
||||
- [x] 键盘导航
|
||||
- [x] 基础拖拽排序
|
||||
- [x] 大树场景稳定滚动
|
||||
|
||||
说明:
|
||||
|
||||
- 上面已勾选的 `右键菜单入口 / 上下文菜单 / feature flag`,当前口径是“same-origin compat host + 宿主 bridge 已闭环”,不等于 final Rust renderer 已切主。
|
||||
- 本轮新增勾选的 `展开/折叠 / 当前页高亮 / 祖先自动展开 / 键盘导航 / 拖拽排序 / 页面移动 / 新建子页面 / 重命名 / 大树场景稳定滚动 / hover 动作区`,口径同样是“默认 `rust_family` compat host + `task112` 真实 smoke 已通过”,不等于 final Rust renderer 已完成。
|
||||
|
||||
### 阻塞项
|
||||
|
||||
- 宿主边界仍过厚
|
||||
- 旧 React 组件仍承担主交互状态机
|
||||
|
||||
### 验收口径
|
||||
|
||||
- 至少一条默认真实流量使用 Rust 家族 page tree renderer
|
||||
- 不再依赖当前 React `PrivateTree` 作为主路径 renderer
|
||||
- `page tree` 功能不低于当前产品合同
|
||||
|
||||
---
|
||||
|
||||
## 8. Phase E:迁移 `file tree / picker` Rust 家族 renderer,并收薄宿主
|
||||
|
||||
### 目标
|
||||
|
||||
在 `page tree` 主路径稳定后,迁移 `file tree` 与 `picker`,并继续收薄主站宿主职责。
|
||||
|
||||
### 当前进度(2026-04-24)
|
||||
|
||||
- [x] `picker` 空查询态与搜索态已统一复用 `TreePickerSurface`
|
||||
- [x] `picker` 已开始复用与 tree surface 同一 renderer family 边界
|
||||
- [x] `file tree / picker / page tree` surface 已显式输出 `data-renderer-family`
|
||||
- [x] `file tree / picker / page tree` 已统一经由 `TreeShellHost` 进入显式 host 选择边界,并输出 `data-tree-host-kind`
|
||||
- [x] `picker` 已接入 `rust_family` host 边界;当前 host 仍先包住 React fallback,不代表已切到 Rust renderer
|
||||
- [x] 未过滤 `picker` 空查询态在 `rust_family` 下已切到 same-origin iframe compat host
|
||||
- [x] `picker` 搜索结果态在 `rust_family` 下已切到 same-origin iframe compat host:
|
||||
- 浏览器端通过 inline override 注入搜索结果
|
||||
- 不再因为搜索态而强制回退 React fallback
|
||||
- [x] `picker` 在 `rust_family` 下,空查询且无可选页面时也会继续留在 same-origin iframe compat host
|
||||
- 不再因为空结果而直接退回宿主内纯文本 div
|
||||
- [x] `picker` 根节点与排除项逻辑已补组件回归:
|
||||
- 空查询时保留根节点
|
||||
- `excludeIds` 不再把当前页误放回候选列表
|
||||
- 组件测试与真实浏览器核对已覆盖该口径
|
||||
- [x] `picker` 空查询态也已支持键盘高亮与 Enter 选中
|
||||
- 不再只在搜索结果态依赖输入框 keydown hack
|
||||
- React fallback 与 same-origin compat host 都会同步当前高亮项
|
||||
- [x] `picker` same-origin compat host 已透传 `activePickerItemKey`
|
||||
- 根节点高亮不再只能停留在 React fallback
|
||||
- Rust tree shell picker 页面测试已覆盖该 query/state
|
||||
- [x] `picker` 在 `rust_family` 下已把输入框键盘命令继续下沉到 same-origin compat shell
|
||||
- 宿主改为发送 `tree.picker.command`
|
||||
- iframe 会回传 `tree.picker.focus.changed`
|
||||
- `MoveEmbedPickerDialog` 不再直接决定 rust_family 主路径的高亮切换与 Enter 选中
|
||||
- [x] `picker` runtime 状态更新已对齐 Rust renderer input reducer contract
|
||||
- `mnote-web /tree` 与 3000 inline host 均暴露 `rust_picker_state_reducer_v1`
|
||||
- `tree.picker.command` 的 next/previous/home/end/pick/focus 已先统一经 `applyPickerStateAction`
|
||||
- 当前仍是 compat JS 镜像执行合同,不代表 final Rust runtime 已接管
|
||||
- [x] 未过滤 `file tree` 在 `rust_family` 下已可切到 same-origin iframe compat host,并 bridge:
|
||||
- `tree.navigate`
|
||||
- `tree.filetree.context-menu`
|
||||
- `tree.asset.open`
|
||||
- `tree.node.created / renamed / moved`
|
||||
- [x] `file tree` 已新增 `tree.filetree.selection.changed` bridge:
|
||||
- 宿主可同步 `selectedRowIds / anchorRowId / focusedRowId`
|
||||
- 现有复制 / 删除 / 粘贴等依赖宿主选择态的链路不再只能停留在 React `FileTree`
|
||||
- [x] `file tree` selection truth 已从宿主 legacy reducer 收薄为 renderer event snapshot:
|
||||
- `rust_family` 下 `Sidebar` 只订阅 `tree.filetree.selection.changed`
|
||||
- 删除 / 复制 / 粘贴 / 上传 / 内部 drop 只读消费 renderer snapshot
|
||||
- `SidebarTreeSurface` 不再向 Rust file tree host 传 `selectedRowIds` 控制 prop
|
||||
- 该项不代表 compat runtime 内部选择算法已经 Rust runtime 化;后续仍转入 `4-11 Phase F/G`
|
||||
- [x] `file tree` runtime 选择入口已对齐 Rust renderer input reducer contract
|
||||
- `mnote-web /tree` 与 3000 inline host 均暴露 `rust_filetree_selection_reducer_v1`
|
||||
- 单选 / 多选 / Shift 范围选 / 右键选中 / 可见行归一化 / drag rows 解析已先统一经 `applyFileTreeSelectionAction`
|
||||
- 当前仍是 compat JS 镜像执行合同,不代表 final Rust runtime 已接管
|
||||
- [x] `file tree` 删除目标归一化已进入 Rust preflight 主链:
|
||||
- `tree.filetree.delete.preflight` 已由 Rust `bridge-runtime` 输出 `fileTreeDeletePlan`
|
||||
- 3000 同源 `/api/tree/filetree/delete-preflight` route 与 client 已覆盖
|
||||
- `Sidebar` 删除链已不再自己推导 rust_family 主路径的 doc/asset 删除列表
|
||||
- [x] `file tree` 粘贴目标与复制分类已进入 Rust preflight 主链:
|
||||
- `tree.filetree.paste.preflight` 已由 Rust `bridge-runtime` 输出 `fileTreePastePlan`
|
||||
- 3000 同源 `/api/tree/filetree/paste-preflight` route 与 client 已覆盖
|
||||
- `Sidebar` 粘贴链在 `rust_family` 下已不再自己推导目标页面、`doc/index` 递归语义和可复制 asset 列表
|
||||
- [x] `file tree` 外部上传目标推导已进入 Rust preflight 主链:
|
||||
- `tree.filetree.upload-target.preflight` 已由 Rust `bridge-runtime` 输出 `fileTreeUploadTargetPlan`
|
||||
- 3000 同源 `/api/tree/filetree/upload-target-preflight` route 与 client 已覆盖
|
||||
- `Sidebar` 外部文件 drop 链已不再自己推导 target row、focused row、workspace fallback 与 mindmap target
|
||||
- [x] same-origin compat shell 已补最小 `file tree` 交互骨架:
|
||||
- 单选 / 多选 / Shift 范围选
|
||||
- 右键聚焦选中
|
||||
- 空白区清空选择
|
||||
- `doc / index / asset` 双击打开
|
||||
- [x] same-origin compat shell 已补 `file tree` 内部拖放 bridge:
|
||||
- iframe 可发 `tree.filetree.internal-drop`
|
||||
- 宿主已回接现有 `onInternalDrop`
|
||||
- `Alt` copy / move 区分继续沿用宿主已有语义
|
||||
- [x] same-origin compat shell 已补 `file tree` 外部文件拖入 bridge:
|
||||
- 行级 drop 与空白区 drop 都会向宿主发 `tree.filetree.external-drop`
|
||||
- 宿主已回接现有 `onDropFiles`
|
||||
- [x] same-origin compat shell 已补最小 `asset-folder` 行语义:
|
||||
- `mindmap` 不再只表现为普通 `asset`
|
||||
- 已可渲染 `asset-folder -> asset` 层级
|
||||
- `asset-folder` 已接入双击打开、右键菜单与 drop target
|
||||
- [x] `file tree` 资源类型图标语义已补最小闭环
|
||||
- same-origin compat shell 已区分 `pdf / image / video / audio / book / table / mindmap`
|
||||
- React fallback `FileTree` 也已补同口径图标分支,避免切流前后视觉合同分裂
|
||||
- [x] 过滤态 `file tree` 在 `rust_family` 下已可通过 inline override 进入 same-origin iframe compat host
|
||||
- 浏览器端已改为直接注入过滤后的 `kernel file_tree items`
|
||||
- 不再因为 filter 非空而强制回退 React fallback
|
||||
- [x] 过滤态 `file tree` 已先从 `kernelFileTreeProjection.items` 收敛正式 item contract,再统一生成 rows 与 same-origin host inline 输入
|
||||
- 不再回退 `pageRows + assets` 二次重建主可见行
|
||||
- `sidebar` 主路径过滤态已优先消费 kernel file tree items
|
||||
- [x] `file tree` 搜索态已切到 Rust projection query 主链
|
||||
- `core-protocol` 的 `KernelProjectionFilter` 已包含 `query`
|
||||
- `bridge-runtime` 的 `kernel.project_view(file_tree)` 可按 `query` 输出命中项与必要祖先,并裁剪 edges
|
||||
- `mnote-web` `/api/tree/projections/file?...&query=` 已透传 query 到 Rust projection
|
||||
- `wolai-frontend` 新增 3000 同源 `/api/tree/projections/file` route,Sidebar 搜索态只消费该 route 返回的 Rust `file_tree` projection items
|
||||
- 旧 `filterKernelFileTreeProjectionItems` 宿主裁剪主链已移除
|
||||
- [x] `file tree` same-origin compat host 已能直接消费 kernel file tree items
|
||||
- 过滤态宿主不再必须先把 `FileTreeRow[]` 重新翻译回 inline item
|
||||
- [x] `file tree` inline override 已移除 `FileTreeRow[]` 正式输入
|
||||
- `TreeShellIframeHost` 现在只接受 `kernel file_tree items` 作为 inline projection contract
|
||||
- [x] `file tree` 默认主路径已切到 `rust_family` same-origin compat host
|
||||
- `task112` 主站 smoke 已验证 file tree hostKind 为 `iframe`
|
||||
- 当前切主的是 compat host;复杂交互执行面仍依赖宿主 bridge
|
||||
- [x] `file tree` 主路径 React renderer 已退出
|
||||
- `TreeShellSurface` 缺少 iframe 宿主时只显示 `file-tree-renderer-removed`
|
||||
- 主路径运行时已不再挂载旧 React `FileTree`
|
||||
- [x] `file tree` 首屏 Rust initial DOM 的 hydrate 后状态 patch 已继续收薄
|
||||
- `TreeShellIframeHost` 在 `data-rust-filetree-renderer=initial_v1` hydrate 成功后,对宿主 active / selection patch 只更新 `data-active / data-selected`
|
||||
- 该项只是减少 compat JS 整树重绘,不等于 final Rust renderer 已完成
|
||||
- [x] 3000 inline host 已减少第二份 projection 注入真相
|
||||
- `TreeShellIframeHost` 主路径现在把 inline projection items 放入 `tree-shell-state.items`
|
||||
- `rendererInput` 与 `items` 同时进入 appState JSON,compat runtime 优先从该合同读取 expanded / selection / picker active / exclude
|
||||
- `__MNOTE_TREE_SHELL_OVERRIDE__` 不再作为 3000 主路径默认注入第二份 items,只保留旧兼容入口
|
||||
- [x] 3000 inline host 已对齐 Rust state reducer 合同
|
||||
- page focus/keyboard 暴露 `rust_page_focus_keyboard_reducer_v1`
|
||||
- filetree selection 暴露 `rust_filetree_selection_reducer_v1`
|
||||
- picker state 暴露 `rust_picker_state_reducer_v1`
|
||||
- 当前只是把默认主路径 compat runtime 的状态更新入口统一到 Rust contract,不等于 final renderer 完成
|
||||
- [ ] `file tree / picker` 的真正 Rust family renderer 仍未接入;当前只是接入 same-origin compat host,而不是最终 renderer
|
||||
- [ ] 宿主仍承担实际命令调度、上传执行与部分可见行 / DOM runtime 过渡职责;还不是最终 Rust family 正式模块边界
|
||||
- 内部 drop / 删除 / 粘贴 / 外部上传目标的目标与对象分类已下沉 Rust preflight,但文件字节读取、菜单状态、实际 transport 与通知仍在宿主
|
||||
|
||||
### checklist
|
||||
|
||||
- [x] 明确 `file tree` renderer 的最小输入与可见行模型
|
||||
- 当前主链最小输入已先统一为 `kernel file_tree items + expanded document ids + expanded asset-folder ids`
|
||||
- [x] 迁移 `doc -> index -> asset-folder -> asset` 可见行语义(compat shell 最小版)
|
||||
- [x] 迁移资源图标与资源菜单分支
|
||||
- 资源图标 contract 已在 `route.test.ts` / `tree-delta.test.ts` 覆盖
|
||||
- `tree.filetree.context-menu` bridge 已在 `tree-shell-iframe-host.test.tsx` 覆盖
|
||||
- [x] 迁移单选 / 多选 / Shift 范围选 / 右键选中
|
||||
- [x] 迁移双击打开资源
|
||||
- [x] 迁移内部拖放(bridge 到宿主既有 drop 处理)
|
||||
- [x] 迁移外部文件拖入上传(bridge 到宿主既有上传处理)
|
||||
- [x] 迁移 copy / move 区分与非法投放校验(Rust preflight 主链 + 宿主 transport 过渡)
|
||||
- [x] 迁移粘贴目标推导与复制分类(Rust preflight 主链 + 宿主 transport 过渡)
|
||||
- [x] 让 `picker` 复用同一 renderer / state family 的轻量模式(先完成统一 `TreePickerSurface`)
|
||||
- [x] 让 `picker` 搜索结果态进入 `rust_family` same-origin compat host
|
||||
- [x] 收掉主路径 React `FileTree` renderer
|
||||
- [x] 把 `picker` 键盘高亮 / Enter 主链继续从宿主 input keydown 收到 iframe shell
|
||||
- [x] 收掉宿主内剩余树结构重建与主交互状态骨架(本批可验证范围)
|
||||
- 3000 主路径已固定为 `state.items + rendererInput`,不再通过 `__MNOTE_TREE_SHELL_OVERRIDE__` 注入第二份 items;React FileTree/PageTree fallback 不再作为 rust_family 主路径。完整 Rust runtime 替换仍归入后续 final renderer。
|
||||
|
||||
### file tree 能力保留硬门槛
|
||||
|
||||
- [x] 页面
|
||||
- [x] `index.md`
|
||||
- [x] 附件
|
||||
- [x] `mindmap` 文件夹
|
||||
- [x] 导图子附件
|
||||
- [x] 多选
|
||||
- [x] 范围选
|
||||
- [x] 资源级右键菜单
|
||||
- [x] 双击打开
|
||||
- [x] 内部拖放
|
||||
- [x] 外部文件拖入
|
||||
- [x] 资源类型图标语义
|
||||
|
||||
### picker 能力保留硬门槛
|
||||
|
||||
- [x] 空态稳定显示
|
||||
- [x] 搜索结果稳定显示
|
||||
- [x] 高亮与键盘选中稳定
|
||||
- 组件回归与 Rust picker shell 定向测试已补
|
||||
- `task113` 已在 `3000` 主站默认 `rust_family` 配置下实跑通过
|
||||
- [x] 根节点与排除项逻辑不回退
|
||||
|
||||
### 阻塞项
|
||||
|
||||
- `file_tree` 搜索过滤已不再由宿主裁剪 items,但 compat shell 内仍有一层把 items 规范化为 DOM 行的过渡逻辑
|
||||
- 选择 truth 对宿主已变成 renderer event snapshot,但 compat runtime 内部仍有 TS 选择/拖放/菜单 DOM 状态机,仍未形成完全独立的 Rust family 正式模块
|
||||
- `file tree` 复杂拖放 / 粘贴 / 上传态的执行仍依赖宿主 bridge;内部 drop、删除、粘贴目标、外部上传目标 preflight 已下沉 Rust,过滤态已不再强制回退 React fallback
|
||||
- `picker` 搜索态虽已进入 same-origin compat host,但当前仍是 compat shell + inline override,不是最终 Rust renderer / 正式搜索合同
|
||||
- `picker` 键盘高亮链已补组件回归、shell query/state 测试与 `3000` 主站真实 smoke
|
||||
- 当前剩余差距不在“键盘能否工作”,而在“仍是 compat shell + 宿主状态机,而非 final Rust renderer”
|
||||
- final renderer 与宿主收薄已转入 `4-11 Phase F/G`
|
||||
|
||||
### 验收口径
|
||||
|
||||
- `file tree` 不再依赖当前 React `FileTree` 作为主路径 renderer
|
||||
- `picker` 复用统一 renderer 家族
|
||||
- 宿主不再承担主要树域交互状态机
|
||||
- 当前实现距离该验收口径仍有明确差距:
|
||||
- same-origin compat host 已接入
|
||||
- `picker` 搜索态已进入 compat host,但仍不是最终 Rust renderer
|
||||
- `file tree` 的内部拖放 / 外部上传 / `asset-folder` 最小语义与搜索 projection 已接入,但还停留在 compat shell + 宿主 bridge
|
||||
- 还不能宣称 `Phase E` 已完成
|
||||
|
||||
---
|
||||
|
||||
## 9. 跨阶段通用回归要求
|
||||
|
||||
每一阶段切流前都必须至少补齐以下回归:
|
||||
|
||||
- [x] 页面树切页 smoke
|
||||
- [x] 页面树展开/折叠 smoke
|
||||
- [x] 页面树拖拽 smoke
|
||||
- [x] 页面树右键菜单 smoke
|
||||
- [x] 文件树多选与范围选 smoke
|
||||
- [x] 文件树双击打开 smoke
|
||||
- [x] 文件树内部拖放 smoke
|
||||
- [x] 文件树外部文件拖入 smoke
|
||||
- [x] picker 搜索 / 空态 / 选中 smoke
|
||||
- [x] stream 中断 fallback smoke
|
||||
|
||||
要求:
|
||||
|
||||
- 不只验证“组件能渲染”
|
||||
- 要验证“关键交互能力未退化”
|
||||
|
||||
补充口径(2026-04-24):
|
||||
|
||||
- 当前以上 10 项由 `scripts/task112-tree-rust-family-regression-smoke.js` 实跑覆盖:宿主页负责切页、右键菜单、文件树双击打开、picker 与 stream fallback;`/api/tree/shell` 直连页负责页面树展开/折叠/拖拽,以及文件树多选、范围选、内部拖放、外部文件拖入。
|
||||
- 其中页面树 / 文件树拖放与 picker 当前主要验证 same-origin compat shell、宿主 bridge 与命令回写链路;宿主完全收薄后的 final renderer 端到端 smoke,仍应在 Phase D / E 收尾时继续补强。
|
||||
|
||||
---
|
||||
|
||||
## 10. 当前最近一步
|
||||
|
||||
按当前优先级,最近一步建议固定为:
|
||||
|
||||
1. 先补 `Phase A`,把 `file_tree` Rust 直接输出缺口补齐。
|
||||
2. 同时推进 `Phase B`,把主路径命令面收口到 `tree.*`。
|
||||
3. 在这两件事没有完成前,不建议宣称“树域主执行面已经可以正式切到 Rust 家族 renderer”。
|
||||
|
||||
原因:
|
||||
|
||||
- 如果 `file_tree` 语义还要靠前端补
|
||||
- 如果命令面还主要停在 compat
|
||||
|
||||
那么换 renderer 只会把现有双轨问题搬进新壳,而不是收掉它。
|
||||
|
||||
---
|
||||
|
||||
## 11. 完成判定
|
||||
|
||||
只有同时满足下面几条,才可以宣称“树域 Rust 家族化这一轮完成”:
|
||||
|
||||
- `page tree / file tree / picker` 主路径 renderer 已进入 Rust 家族
|
||||
- `file_tree` 已由 Rust 直接输出正式对象投影
|
||||
- 主调用路径默认走 `tree.*`
|
||||
- realtime 已进入正式 `snapshot + delta + resync` 合同
|
||||
- 宿主只保留极薄挂载与桥接职责
|
||||
- `page tree / file tree` 的现有能力没有因迁移退化
|
||||
|
||||
---
|
||||
|
||||
## 12. 一句话收口
|
||||
|
||||
这轮工作的正确推进方式不是:
|
||||
|
||||
- “先把现有 React 组件翻译成 Rust”
|
||||
|
||||
而是:
|
||||
|
||||
> **先补齐 `file_tree / command / realtime` 三条主链,再以能力不退化为硬门槛,逐步把 `page tree / file tree / picker` 的主 renderer 和主交互骨架迁入 Rust 家族。**
|
||||
+514
@@ -0,0 +1,514 @@
|
||||
# 4-9 [recycle] 树域 Rust 家族化剩余架构事项与能力保留方案 v1
|
||||
|
||||
> 更新时间:2026-04-23
|
||||
>
|
||||
> 回收说明(2026-04-28):本文是 Rust 家族化下一阶段的早期架构拆解稿,后续已由 `4-10` 执行清单、`4-11` final renderer / host thinning、`4-16` remaining final runtime、`4-18` final DOM shell hard gate 继续拆分和覆盖。本文保留为历史能力保留口径,不再作为当前活跃 `process` 入口。
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-2-sidebar-pagetree-filetree-product-interaction-contract-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-4-tree-projection-protocol-contract-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-7-tree-shell-ui-state-boundary-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份文档只回答两个问题:
|
||||
|
||||
1. 在当前长期方向已经固定为 `Rust kernel + Rust web + Leptos island + Convex substrate` 的前提下,树域继续朝 Rust 家族化推进,还剩哪些真正的架构事项。
|
||||
2. 在 `page tree / file tree` 从当前 `TypeScript + Next + React` 主壳向 Rust 家族执行面迁移时,哪些现有能力必须被完整保留,不能为了“换语言”而退化。
|
||||
|
||||
这份文档不是为了重复证明:
|
||||
|
||||
- `Convex` 是否还保留
|
||||
- 树域是否已经具备 projection contract
|
||||
- `Page Aggregate` 是否应该存在
|
||||
|
||||
这些上位结论已经在关联文档中固定。
|
||||
|
||||
本文只做当前阶段最需要的补充收口:
|
||||
|
||||
> **树域已经完成 truth / projection / command 边界的第一轮收口,但主渲染壳仍主要停留在 `TypeScript + Next + React`。下一阶段要解决的,不再是“树是不是 kernel projection”,而是“树域主执行面怎样继续向 Rust 家族迁移,同时不丢掉现有交互能力”。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前判断
|
||||
|
||||
## 2.1 已经成立的部分
|
||||
|
||||
当前已经成立且不应回退的事实:
|
||||
|
||||
- `tree-first graph kernel` 继续是树域真相层。
|
||||
- `Convex` 继续承担存储、实时同步、内容与协作底座。
|
||||
- `mnote-web` 已经具备树域 projection / command / transport 的正式入口能力。
|
||||
- `sidebar_tree / page_tree / file_tree` 已经形成同一 projection family。
|
||||
- 树域主路径不应再回到“前端自己定义第二套树真相”。
|
||||
|
||||
也就是说,当前真正成立的结构是:
|
||||
|
||||
- `Rust kernel` 持有树语义。
|
||||
- `Convex` 提供 substrate。
|
||||
- `Rust web` 已开始承接 route / projection / command / stream。
|
||||
- 当前主站中的树 consumer 已经开始消费 projection family。
|
||||
|
||||
## 2.2 仍未完成的部分
|
||||
|
||||
当前仍未完成、且正是下一阶段主任务的部分是:
|
||||
|
||||
- `page tree / file tree / picker` 的主渲染壳仍主要是 `TypeScript + Next + React`。
|
||||
- `file_tree` 的一部分 row 语义仍由前端 adapter 继续补齐,而不是完全由 Rust 直接输出。
|
||||
- `tree.*` 的长期命令面虽已冻结,但主调用路径仍未完全退出 `documents.*` 兼容入口。
|
||||
- 树域 realtime 仍未完全收口为 Rust Web 正式 `snapshot + delta + resync` 主链。
|
||||
- Sidebar 整体仍是超大客户端壳,树域虽然已收 truth,但执行面与宿主边界还不够薄。
|
||||
|
||||
因此当前正确判断不是:
|
||||
|
||||
- “树域 Rust 家族化已经完成”
|
||||
|
||||
而是:
|
||||
|
||||
> **树域已经完成“语义与协议收口”,但还没有完成“主执行面 Rust 家族化”。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 为什么继续 Rust 家族化有真实意义
|
||||
|
||||
## 3.1 不是语言洁癖,而是减少第二套运行时语义
|
||||
|
||||
如果长期保持下面这种结构:
|
||||
|
||||
- Rust 负责 kernel / query / command / projection
|
||||
- Next/React 继续负责主树 UI、主交互壳、主状态骨架
|
||||
|
||||
那么树域会长期同时维护两套复杂度:
|
||||
|
||||
1. Rust 侧的 projection / command / stream 语义
|
||||
2. 前端壳里的 row model / selection / focus / keyboard / DnD / fallback / adapter 语义
|
||||
|
||||
这会带来:
|
||||
|
||||
- 协议在两侧重复解释
|
||||
- 调试时要同时跨 TS / Rust 两套执行面
|
||||
- tree shell 的边界始终无法真正稳定
|
||||
- 后续一切性能优化都要先穿过旧前端壳
|
||||
|
||||
因此这里的“继续 Rust 家族化”真正要减少的,不是文件后缀的种类,而是:
|
||||
|
||||
> **树域存在两套长期执行语义的状态。**
|
||||
|
||||
## 3.2 对加载速度与运行成本有真实潜力
|
||||
|
||||
树域是高频常驻 UI,不是偶尔打开一次的边缘面板。
|
||||
|
||||
如果它继续作为大型 `use client` 组件族存在,就会长期保留这些成本:
|
||||
|
||||
- 客户端组件挂载
|
||||
- 虚拟列表初始化
|
||||
- DnD 状态机初始化
|
||||
- 菜单与选择状态初始化
|
||||
- 浏览器侧 row model 构建
|
||||
- 兼容 fallback 与 stream 选择逻辑
|
||||
|
||||
这些成本不会因为“数据真相已经交给 Rust”而自动消失。
|
||||
|
||||
把树域主壳继续迁向 Rust 家族,真实收益主要来自:
|
||||
|
||||
- 进一步压低浏览器端常驻 JS
|
||||
- 让 projection -> renderer 的路径更短
|
||||
- 减少 hydration 与挂载层数
|
||||
- 降低 Sidebar 切页与刷新时的重渲染放大
|
||||
- 为后续更彻底的 server-first 壳收口创造条件
|
||||
|
||||
这里必须强调:
|
||||
|
||||
- Rust 化 **不自动等于** 更快
|
||||
- 但在当前项目的长期方向里,Rust 家族化是“继续减轻旧前端壳”的必要手段
|
||||
|
||||
## 3.3 对协作冲突与长期维护也有真实意义
|
||||
|
||||
这里的“冲突”不只指 git merge 冲突,更主要是:
|
||||
|
||||
- projection contract 与 renderer 行为漂移
|
||||
- TS adapter 与 Rust route 的边界反复变化
|
||||
- 同一个交互在两侧同时维护 legality / normalization / fallback
|
||||
|
||||
当树域主执行面仍留在 React 壳里时:
|
||||
|
||||
- Rust 改协议
|
||||
- 前端就要继续补 adapter
|
||||
- 测试也要跨两族运行时拼接验证
|
||||
|
||||
继续 Rust 家族化的意义,是让下面这条链尽量收敛为一条语言家族更统一的主链:
|
||||
|
||||
- kernel truth
|
||||
- projection
|
||||
- tree command
|
||||
- tree shell
|
||||
- renderer
|
||||
|
||||
---
|
||||
|
||||
## 4. 下一阶段剩余架构事项
|
||||
|
||||
下面这些事项,才是树域继续 Rust 家族化时真正还没完成的主任务。
|
||||
|
||||
## 4.1 正式定义“树域主执行面”的完成标准
|
||||
|
||||
首先必须明确:
|
||||
|
||||
- “树域已经吃 projection” 不等于 “树域已经 Rust 家族化完成”
|
||||
- “存在 Leptos scaffold / Rust tree_shell 模块” 不等于 “主路径 renderer 已迁完”
|
||||
|
||||
下一阶段完成标准应固定为:
|
||||
|
||||
- 至少一条真实默认用户流量,主树 renderer 不再依赖当前 React `PrivateTree` / `FileTree`
|
||||
- `page tree / file tree / picker` 的核心交互骨架不再由 Next/React 主壳承担
|
||||
- TS 宿主只保留挂载、局部桥接、页面路由与极薄 compat
|
||||
|
||||
如果这一标准不先冻结,后续很容易再次把“有 Rust route”误当成“已经 Rust 化完成”。
|
||||
|
||||
## 4.2 把 page tree renderer 真正迁入 Rust 家族
|
||||
|
||||
`page tree` 下一阶段不是继续补前端 helper,而是把以下能力迁成 Rust 家族正式 renderer 能力:
|
||||
|
||||
- 行渲染
|
||||
- 展开/折叠
|
||||
- 当前页高亮
|
||||
- hover 动作区
|
||||
- 键盘导航
|
||||
- 拖拽反馈
|
||||
- 上下文菜单入口
|
||||
|
||||
这里的重点不是“把视觉样式翻译一遍”,而是:
|
||||
|
||||
- 把行级行为骨架从 React 组件迁走
|
||||
- 让 page tree 只围绕正式 projection item 工作
|
||||
- 不再由现有 TS 组件长期持有主路径交互状态机
|
||||
|
||||
## 4.3 把 file tree 从“前端 adapter 文件树”收口为“Rust 直接输出 + Rust 家族 renderer”
|
||||
|
||||
`file_tree` 是当前最关键也最容易退化的一段。
|
||||
|
||||
下一阶段必须同时做两件事:
|
||||
|
||||
1. Rust 侧继续把 `file_tree` 输出补完整。
|
||||
2. renderer 不再依赖当前前端在 `pageRows + assets` 上继续拼 visible rows。
|
||||
|
||||
必须继续向 Rust 收口的内容包括:
|
||||
|
||||
- `index.md`
|
||||
- 附件
|
||||
- `mindmap` 文件夹
|
||||
- 导图子附件
|
||||
- 表格、书籍、PDF 等对象提示
|
||||
- `resource_kind / asset_kind / icon_hint`
|
||||
|
||||
否则文件树即使换了新壳,也仍然只是:
|
||||
|
||||
- “页面树 + 前端附件补丁”
|
||||
|
||||
这不符合长期目标。
|
||||
|
||||
## 4.4 把树域交互骨架拆成可替换的正式模块
|
||||
|
||||
下一阶段不应继续把交互都堆在单一大组件里,而应显式收口为几类正式模块:
|
||||
|
||||
- row model
|
||||
- selection model
|
||||
- focus model
|
||||
- keyboard model
|
||||
- DnD state machine
|
||||
- context action registry
|
||||
|
||||
无论这些模块最终落在:
|
||||
|
||||
- `Leptos`
|
||||
- 或极薄 Rust/TS bridge
|
||||
|
||||
都必须满足两条:
|
||||
|
||||
- 它们不再定义结构真相
|
||||
- 它们可以独立测试与演进
|
||||
|
||||
## 4.5 完成 tree command cutover Stage 2
|
||||
|
||||
树域继续 Rust 家族化时,命令面不能长期继续停在双轨。
|
||||
|
||||
下一阶段必须完成:
|
||||
|
||||
- 主路径默认发 `tree.*`
|
||||
- `documents.*` 退到 compat
|
||||
- legality / normalize / move target 等树策略继续回收给 Rust
|
||||
|
||||
否则会出现一个长期问题:
|
||||
|
||||
- UI 壳换成 Rust 家族了
|
||||
- 但命令面仍主要经由旧 `documents.*` 兼容语义运行
|
||||
|
||||
这样并没有完成真正的主执行面收口。
|
||||
|
||||
## 4.6 完成 Rust Web realtime contract
|
||||
|
||||
树域如果要继续往正式主链推进,realtime 必须从“可用”进入“单一正式合同”。
|
||||
|
||||
下一阶段需要补齐:
|
||||
|
||||
- `snapshot`
|
||||
- `delta`
|
||||
- `resync`
|
||||
- cursor 漂移处理
|
||||
- 局部 subtree 与 workspace scope 的统一
|
||||
|
||||
目标不是简单保住 fallback,而是让树域主路径不再依赖:
|
||||
|
||||
- 查询快照
|
||||
- 本地 freshness 选择
|
||||
- React 侧补偿式同步
|
||||
|
||||
## 4.7 收薄宿主边界
|
||||
|
||||
长期目标不是让主站彻底消失,而是让主站对树域只保留最小宿主职责:
|
||||
|
||||
- 页面布局挂载位
|
||||
- 路由跳转
|
||||
- 样式容器
|
||||
- 极薄 feature flag / compat
|
||||
- 与其他页面面板的最小桥接
|
||||
|
||||
不应继续保留在宿主中的内容:
|
||||
|
||||
- 树结构重建
|
||||
- 主交互状态骨架
|
||||
- 文件树资源行语义补丁
|
||||
- 主要 keyboard / DnD / selection 逻辑
|
||||
|
||||
---
|
||||
|
||||
## 5. page tree 迁移时必须保留的能力
|
||||
|
||||
`page tree` 在迁移到 Rust 家族主执行面时,必须显式保证下面这些能力不退化。
|
||||
|
||||
## 5.1 结构与导航
|
||||
|
||||
必须保留:
|
||||
|
||||
- 稳定的层级展开/折叠
|
||||
- 当前页高亮
|
||||
- 祖先自动展开
|
||||
- 搜索/过滤后层级仍可理解
|
||||
|
||||
禁止退化为:
|
||||
|
||||
- 纯扁平列表
|
||||
- 只有打开功能、没有层级语义的按钮组
|
||||
|
||||
## 5.2 行级动作
|
||||
|
||||
必须保留:
|
||||
|
||||
- 新建子页面
|
||||
- 重命名
|
||||
- 移动
|
||||
- 归档/恢复类上下文操作入口
|
||||
- 行级 hover 动作区
|
||||
|
||||
要求:
|
||||
|
||||
- 不能为了换 renderer 把动作缩减到“点开页面 + 更多菜单”
|
||||
- 不能丢失当前工作区级高频动作入口
|
||||
|
||||
## 5.3 键盘与焦点
|
||||
|
||||
必须保留:
|
||||
|
||||
- 上下移动
|
||||
- 左右展开/折叠
|
||||
- Enter 打开
|
||||
- 稳定的 focus 行
|
||||
|
||||
要求:
|
||||
|
||||
- `focus` 不能重新混成 `selected`
|
||||
- 展开与选择变化后焦点归一化必须稳定
|
||||
|
||||
## 5.4 拖拽与移动
|
||||
|
||||
必须保留:
|
||||
|
||||
- 基础拖拽排序
|
||||
- 父节点切换
|
||||
- 非法拖拽校验
|
||||
|
||||
要求:
|
||||
|
||||
- drop feedback 不能退化成不可预测的闪烁
|
||||
- 目标父节点与位置推导必须有稳定、可测试的协议
|
||||
|
||||
## 5.5 大树体验
|
||||
|
||||
必须保留:
|
||||
|
||||
- 稳定滚动
|
||||
- 大树下的可见区域性能
|
||||
- 展开/折叠与切页不会出现明显回闪
|
||||
|
||||
这部分是迁移时的硬验收项,不是可选优化项。
|
||||
|
||||
---
|
||||
|
||||
## 6. file tree 迁移时必须保留的能力
|
||||
|
||||
`file_tree` 比 `page tree` 更容易因迁移而退化。
|
||||
|
||||
下一阶段必须把“功能保留”明确写成硬门槛。
|
||||
|
||||
## 6.1 资源层级
|
||||
|
||||
必须保留:
|
||||
|
||||
- 页面
|
||||
- `index.md`
|
||||
- 附件
|
||||
- `mindmap` 文件夹
|
||||
- 导图子附件
|
||||
- 表格、书籍、PDF 等资源对象语义
|
||||
|
||||
禁止退化为:
|
||||
|
||||
- 页面树下挂一个普通附件列表
|
||||
- mindmap 资源被拍平成普通文件
|
||||
|
||||
## 6.2 选择能力
|
||||
|
||||
必须保留:
|
||||
|
||||
- 单选
|
||||
- 多选
|
||||
- Shift 范围选
|
||||
- 右键选中
|
||||
- 可见行变化后的选择归一化
|
||||
|
||||
这部分如果退化,文件树就不再是资源管理器语义,而会退回文档树换皮。
|
||||
|
||||
## 6.3 打开与操作
|
||||
|
||||
必须保留:
|
||||
|
||||
- 单击选择
|
||||
- 双击打开资源
|
||||
- 资源级右键菜单
|
||||
- 按资源类型分支的动作入口
|
||||
|
||||
要求:
|
||||
|
||||
- 资源图标语义必须稳定
|
||||
- `asset_kind / icon_hint` 不允许再次回到壳内临时猜测
|
||||
|
||||
## 6.4 拖放
|
||||
|
||||
必须保留:
|
||||
|
||||
- 内部拖放
|
||||
- 外部文件拖入上传
|
||||
- copy / move 区分
|
||||
- 非法投放校验
|
||||
|
||||
要求:
|
||||
|
||||
- 文件树不能只保留视觉拖拽,没有正式动作协议
|
||||
- 外部文件拖入必须继续是正式主路径能力
|
||||
|
||||
## 6.5 文件树特有的可见行模型
|
||||
|
||||
必须保留:
|
||||
|
||||
- `doc -> index -> asset-folder -> asset` 的可见行语义
|
||||
- 扁平化 rows 与深度的稳定映射
|
||||
- 可见行变化后的选择、焦点、拖放范围归一化
|
||||
|
||||
这里允许实现换掉,但不允许语义丢掉。
|
||||
|
||||
---
|
||||
|
||||
## 7. 推荐迁移顺序
|
||||
|
||||
为了尽量保持体验不退化,推荐顺序如下:
|
||||
|
||||
1. 先补齐 `file_tree` 的 Rust 直接输出,避免迁 renderer 时还要继续背前端 adapter。
|
||||
2. 再把 `page tree` renderer 迁入 Rust 家族,因为它对象更单纯、验收面更小。
|
||||
3. 然后迁 `file tree` renderer,因为它依赖更多资源与交互语义。
|
||||
4. 再迁 `picker` 轻量模式,让它复用同一套 renderer / state family。
|
||||
5. 最后继续削薄宿主,收掉剩余主路径 React 树壳。
|
||||
|
||||
原因:
|
||||
|
||||
- `page tree` 先迁可以先验证主行 renderer 与状态骨架。
|
||||
- `file_tree` 后迁可以避免一开始就在最复杂对象面上同时处理 renderer 与 contract 漂移。
|
||||
- `picker` 最适合作为共用 renderer 的轻量消费场景,而不是主线路的先导。
|
||||
|
||||
---
|
||||
|
||||
## 8. 迁移阶段的验收门槛
|
||||
|
||||
只有同时满足下面几条,才可以把树域继续 Rust 家族化的某一阶段视为成立:
|
||||
|
||||
- 页面树与文件树都没有因切流而减少已有能力。
|
||||
- `page tree / file tree` 的关键能力有 smoke 覆盖:
|
||||
- 切页
|
||||
- 展开/折叠
|
||||
- 拖拽
|
||||
- 右键菜单
|
||||
- 多选与范围选
|
||||
- 外部文件拖入
|
||||
- `file_tree` 的对象投影不再依赖前端临时补丁维持主路径。
|
||||
- 主调用路径默认走正式 `tree.*` 命令面。
|
||||
- 正式 realtime 至少已经进入统一 `snapshot + delta + resync` 合同,而不是继续主要靠补偿式 freshness 选择。
|
||||
- 主站宿主不再持有树结构真相,也不再承担主要树域交互状态机。
|
||||
|
||||
---
|
||||
|
||||
## 9. 当前优先级建议
|
||||
|
||||
在当前总优先级下,树域继续 Rust 家族化的最近顺序建议固定为:
|
||||
|
||||
1. 继续推进 `4-6 tree command protocol cutover stage2`
|
||||
2. 明确 `file_tree` 直接输出的剩余缺口,并以 Rust 侧补齐
|
||||
3. 为 `page tree / file tree` 迁移补正式功能保留回归矩阵
|
||||
4. 选择并落一条真实默认流量进入 Rust 家族 renderer
|
||||
5. 再逐步收掉当前主路径 React 树壳
|
||||
|
||||
也就是说,当前下一步不是:
|
||||
|
||||
- 继续在现有 React 树壳里堆更多交互补丁
|
||||
|
||||
而是:
|
||||
|
||||
> **在功能不退化的前提下,把树域从“Rust 持有语义、React 持有主执行面”的过渡态,推进到“Rust 家族同时持有语义与主执行面”的下一阶段。**
|
||||
|
||||
---
|
||||
|
||||
## 10. 最终结论
|
||||
|
||||
当前树域继续往全 Rust 方向发展,是有真实意义的。
|
||||
|
||||
意义不在于:
|
||||
|
||||
- 代码库里减少一种语言
|
||||
|
||||
而在于:
|
||||
|
||||
- 收掉旧前端壳的长期主执行职责
|
||||
- 减少第二套复杂运行时语义
|
||||
- 为首屏与常驻交互链路继续减重
|
||||
- 让 `tree-first graph kernel -> Rust Web -> Rust family shell` 形成更一致的长期主链
|
||||
|
||||
但这个推进必须满足一个前提:
|
||||
|
||||
> **page tree / file tree 的功能能力不能因为迁移而退化。**
|
||||
|
||||
因此,下一阶段的正确目标不是“尽快把组件翻译成 Rust”,而是:
|
||||
|
||||
> **先把剩余 projection / command / realtime / host 边界补齐,再以 page tree 与 file tree 的能力保留为硬约束,把树域主执行面稳步迁入 Rust 家族。**
|
||||
@@ -0,0 +1,101 @@
|
||||
# [recycle] AppFlowy-IO仓库:可借鉴的核心资源与适配建议(Rust+Axum+Leptos)
|
||||
|
||||
AppFlowy-IO作为开源Notion替代品,其**Rust后端与CRDT协作体系**对您的项目极具参考价值,尽管前端使用Flutter而非Leptos,但核心设计理念完全可迁移。以下是按价值排序的关键仓库与适配建议:
|
||||
|
||||
---
|
||||
|
||||
## 一、核心可借鉴仓库(全部手动核验可用)
|
||||
|
||||
### 1. appflowy-collab(⭐⭐⭐⭐⭐ 必看)
|
||||
**基于yrs的Rust协作核心,直接适配您的yrs集成方案**
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/AppFlowy-IO/appflowy-collab | ✅ 可用 | 封装yrs的CRDT协作库,含文档、数据库、文件夹等领域对象 |
|
||||
| crates.io | https://crates.io/crates/collab | ✅ 可用 | 最新版本0.3.0,可直接依赖 |
|
||||
| 文档 | https://docs.rs/collab/latest/collab/ | ✅ 可用 | 协作算法与数据结构API |
|
||||
| 示例 | https://github.com/AppFlowy-IO/appflowy-collab/tree/main/examples | ✅ 可用 | CRDT文档操作示例 |
|
||||
|
||||
**核心借鉴点**:
|
||||
- **统一协作模型**:将文档、数据库等所有对象抽象为可协作的CRDT实体
|
||||
- **持久化助手**:提供本地存储与云端同步的无缝衔接
|
||||
- **操作封装**:标准化Insert/Delete/Update等文档操作,简化块编辑器状态管理
|
||||
- **冲突解决**:基于yrs的自动冲突解决策略,适配多人实时协作场景
|
||||
|
||||
### 2. appflowy-editor(⭐⭐⭐⭐ 高价值)
|
||||
**块编辑器核心设计,可迁移至Leptos组件体系**
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/AppFlowy-IO/appflowy-editor | ✅ 可用 | 块式编辑器核心,含节点系统与操作框架 |
|
||||
| 架构文档 | https://blog.appflowy.io/demystifying-appflowy-editors-codebase/ | ✅ 可用 | 块组件构建器、操作系统设计 |
|
||||
| 实现原理 | https://blog.appflowy.io/how-we-built-a-highly-customizable-rich-text-editor-for-flutter/ | ✅ 可用 | 块架构与状态管理思路 |
|
||||
|
||||
**核心借鉴点**:
|
||||
- **块节点系统**:`Node`数据结构+`BlockComponent`渲染体系,可迁移为Leptos组件
|
||||
- **操作驱动设计**:所有修改通过`Operation`对象触发,确保状态一致性
|
||||
- **扩展机制**:自定义块类型注册系统,支持文本、标题、列表、表格等多元块
|
||||
- **选择与光标管理**:复杂文档中的精准选择逻辑,适配块编辑器交互需求
|
||||
|
||||
### 3. appflowy-backend(⭐⭐⭐⭐ 高价值)
|
||||
**Axum适配的Rust后端参考,含WebSocket协作服务**
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/AppFlowy-IO/appflowy-backend | ✅ 可用 | Axum后端实现,含协作API与WebSocket服务 |
|
||||
| WebSocket示例 | https://github.com/AppFlowy-IO/appflowy-backend/blob/main/src/websocket.rs | ✅ 可用 | CRDT更新推送实现 |
|
||||
| 数据验证 | https://docs.appflowy.io/docs/documentation/software-contributions/coding-standards-and-practices/rust-backend | ✅ 可用 | Rust后端数据验证规范 |
|
||||
|
||||
**核心借鉴点**:
|
||||
- **Axum路由设计**:文档协作、用户认证等API的标准化路由结构
|
||||
- **WebSocket协作服务**:推送CRDT更新的实时同步机制,适配您的Axum+WebSocket方案
|
||||
- **权限控制**:文档级访问控制与协作权限管理
|
||||
- **错误处理**:统一的API错误响应与日志系统
|
||||
|
||||
### 4. 其他高价值仓库
|
||||
| 仓库 | 链接 | 状态 | 价值点 |
|
||||
|------|------|------|--------|
|
||||
| appflowy-database | https://github.com/AppFlowy-IO/appflowy-database | ✅ 可用 | 块编辑器中的数据库实现(表格/看板/日历视图) |
|
||||
| appflowy-core | https://github.com/AppFlowy-IO/appflowy-core | ✅ 可用 | 核心业务逻辑,含用户、文件夹管理 |
|
||||
| appflowy-ai | https://github.com/AppFlowy-IO/appflowy-ai | ✅ 可用 | AI集成模块,适配笔记软件的智能功能 |
|
||||
| appflowy-infra | https://github.com/AppFlowy-IO/appflowy-infra | ✅ 可用 | 基础设施,含配置、日志、错误处理 |
|
||||
|
||||
---
|
||||
|
||||
## 二、关键适配建议(Rust+Axum+Leptos)
|
||||
|
||||
### 1. 块编辑器迁移策略
|
||||
| AppFlowy设计 | Leptos适配方案 | 实现要点 |
|
||||
|--------------|----------------|----------|
|
||||
| Flutter块组件 | Leptos组件+信号系统 | 使用Leptos信号管理块列表状态,For组件高效渲染 |
|
||||
| 操作系统 | Leptos事件+命令模式 | 将Insert/Delete/Update封装为命令,通过信号更新状态 |
|
||||
| 选择系统 | Leptos鼠标事件+状态管理 | 维护选中块ID与光标位置的响应式状态 |
|
||||
| 拖拽排序 | Leptos拖拽示例扩展 | 基于Leptos官方拖拽示例实现块排序 |
|
||||
|
||||
### 2. 协作系统无缝集成
|
||||
1. **直接依赖collab crate**:替代您的原生yrs使用,获得更高层次的协作抽象
|
||||
2. **Axum+WebSocket同步**:参考appflowy-backend的WebSocket实现,推送CRDT更新
|
||||
3. **本地持久化**:结合redb与collab的持久化助手,实现本地优先存储
|
||||
4. **冲突解决**:复用collab封装的yrs自动合并策略,无需重复开发
|
||||
|
||||
### 3. 避坑指南
|
||||
1. **避免Flutter绑定**:专注collab与核心逻辑,前端完全用Leptos重实现块组件
|
||||
2. **优先使用命令模式**:所有块修改通过命令触发,确保协作状态可追踪
|
||||
3. **块渲染优化**:对长文档使用虚拟列表(Leptos有virtual_scroll示例)
|
||||
4. **状态隔离**:将协作状态与UI状态分离,通过信号传递更新
|
||||
|
||||
---
|
||||
|
||||
## 三、推荐实施路径(基于您的技术栈)
|
||||
1. **基础层**:集成collab crate替代原生yrs,快速获得文档协作能力
|
||||
2. **块模型**:参考appflowy-editor设计块节点系统,支持文本、标题、列表等基础块
|
||||
3. **编辑器UI**:用Leptos实现块组件+拖拽排序,复用Leptos信号管理状态
|
||||
4. **后端同步**:基于Axum+WebSocket实现collab更新推送,参考appflowy-backend
|
||||
5. **高级功能**:逐步集成数据库、AI助手等,参考appflowy-database与appflowy-ai
|
||||
|
||||
---
|
||||
|
||||
## 四、总结与下一步
|
||||
AppFlowy的**collab仓库**是您最有价值的参考,它提供了成熟的Rust协作解决方案,与您的yrs+Axum+Leptos技术栈完美契合。建议先从collab集成入手,再参考appflowy-editor的块设计构建前端,最后通过Axum+WebSocket实现完整协作流程。
|
||||
|
||||
需要我基于collab crate生成一个可直接运行的**Leptos+Axum+collab**最小块编辑器模板吗?包含块渲染、基础协作和WebSocket同步功能。
|
||||
@@ -0,0 +1,186 @@
|
||||
# [recycle] Rust Block Editor Adoption Matrix v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 目标:
|
||||
> - 冻结 task-050 的参考层采用矩阵
|
||||
> - 明确 `edita-core`、`blocks`、`kode`、`leptos-tiptap` 的采用边界
|
||||
> - 说明哪些是采用,哪些是不采用,哪些是部分采用
|
||||
|
||||
## 1. 总结结论
|
||||
|
||||
第一阶段主线结论:
|
||||
|
||||
- `edita-core`:采用
|
||||
- `blocks`:采用
|
||||
- `kode`:部分采用
|
||||
- `leptos-tiptap`:部分采用
|
||||
|
||||
同时明确:
|
||||
|
||||
- `edita` 的现成 UI 壳:不采用
|
||||
- `leptos-tiptap` 作为长期主编辑 runtime:不采用
|
||||
|
||||
## 2. 采用矩阵
|
||||
|
||||
| 参考层 | 结论 | 采用依据 | 不采用或限制依据 | 第一阶段落点 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `edita-core` | 采用 | `edita-core/src/lib.rs` 已提供 `Editor / Block / Command` 无头抽象,适合承接 Rust command 主链 | 泛型过于宽松,仍需补 `mnote` 自己的 block schema 与 command enum 胶水 | 作为 editor core 命令容器参考 |
|
||||
| `blocks` | 采用 | `src/block.rs`、`document.rs`、`converters.rs`、`history.rs`、`diff.rs`、`sanitizer.rs` 已覆盖块模型、导入导出、历史、diff/merge | 自定义块型仍需补扩展映射,不能直接原样承载全部 `mnote` 宿主块 | 作为文档模型、转换、history 基线 |
|
||||
| `kode` | 部分采用 | `kode-core` 提供 buffer/selection/history,`kode-leptos` 提供 `MarkdownEditorComponent` 与 `TreeWysiwygEditor`,`kode-doc` 提供结构树思路 | 自带 doc tree 语义较重,不适合整套替换 `mnote` 事实层 | 作为块内输入器、光标、输入规则参考 |
|
||||
| `leptos-tiptap` | 部分采用 | `src/api/component.rs`、`use_tiptap_editor.rs`、`runtime/bridge.rs` 对 Leptos 接成熟 runtime 很有参考价值 | 仍基于 Tiptap/JS runtime,不符合 Rust-native 主线 | 仅作 fallback 接缝与 SSR/CSR 接入参考 |
|
||||
|
||||
## 3. 采用
|
||||
|
||||
### 3.1 `edita-core`:采用
|
||||
|
||||
采用理由:
|
||||
|
||||
- `Editor<Node, State, Input>` 明确区分状态、块解析、命令执行。
|
||||
- `Command<State>` 模式非常适合把当前分散在 UI 事件里的块命令抽到 Rust 侧。
|
||||
- 没有 DOM 依赖,没有 Leptos 依赖,没有 JS runtime 依赖。
|
||||
|
||||
建议采用范围:
|
||||
|
||||
- `Editor`
|
||||
- `Block`
|
||||
- `Command`
|
||||
- `process_nodes(...)` 体现的“按块处理输入”思路
|
||||
|
||||
不直接照搬的部分:
|
||||
|
||||
- 直接使用其 UI 示例
|
||||
- 直接复用其泛型输入节点定义
|
||||
|
||||
原因:
|
||||
|
||||
- `mnote` 需要的是稳定块命令与文档模型,不是直接拿一个演示型 editor state。
|
||||
|
||||
### 3.2 `blocks`:采用
|
||||
|
||||
采用理由:
|
||||
|
||||
- 当前第一阶段最需要的不是花哨 UI,而是稳定文档模型与格式收口。
|
||||
- `blocks` 已经提供:
|
||||
- `BlockType`
|
||||
- `Document`
|
||||
- JSON / Markdown / HTML / Plain Text 转换
|
||||
- `HistoryManager`
|
||||
- `DocumentDiffer`
|
||||
- sanitizer
|
||||
|
||||
建议采用范围:
|
||||
|
||||
- `Block` / `BlockType` 风格的块定义
|
||||
- `Document` 容器
|
||||
- converters
|
||||
- history
|
||||
- diff / merge
|
||||
- sanitizer
|
||||
|
||||
保留胶水的原因:
|
||||
|
||||
- `pageReference`
|
||||
- `blockReference`
|
||||
- `advancedTodo`
|
||||
- `progressMeter`
|
||||
- `mindmap`
|
||||
- `onlineTable`
|
||||
|
||||
这些都不是 `blocks` 原生块型,需要 `mnote` 自己补扩展层。
|
||||
|
||||
## 4. 部分采用
|
||||
|
||||
### 4.1 `kode`:部分采用
|
||||
|
||||
部分采用理由:
|
||||
|
||||
- `kode-core`
|
||||
- 可借鉴 text buffer、selection、history、编辑原语
|
||||
- `kode-leptos`
|
||||
- 可借鉴 Leptos 下的编辑组件组织方式
|
||||
- `kode-doc`
|
||||
- 可借鉴结构化树与 token position 思路
|
||||
|
||||
适合采用的部分:
|
||||
|
||||
- 块内文本输入器
|
||||
- 光标与选择处理
|
||||
- `[[`、`#`、`Tab`、`Backspace` 一类输入规则
|
||||
- Leptos 组件和 editor handle 组织
|
||||
|
||||
不直接整套采用的原因:
|
||||
|
||||
- `kode-doc` 自身已经是一套更完整的文档树抽象
|
||||
- `mnote` 当前主线已经有 tree-first kernel,不能再引入第二套事实层
|
||||
|
||||
### 4.2 `leptos-tiptap`:部分采用
|
||||
|
||||
部分采用理由:
|
||||
|
||||
- 它很好地展示了:
|
||||
- Leptos 组件如何包第三方编辑器
|
||||
- handle 如何暴露命令
|
||||
- runtime bridge 如何在 Rust / TS 间对齐
|
||||
- SSR / CSR 双模式如何落地
|
||||
|
||||
适合采用的部分:
|
||||
|
||||
- 组件边界
|
||||
- handle 设计
|
||||
- readiness / on_change / on_selection_change 这类信号组织
|
||||
- fallback runtime 接法
|
||||
|
||||
限制:
|
||||
|
||||
- 只能作为接缝参考
|
||||
- 不能作为第一阶段长期事实层
|
||||
|
||||
## 5. 不采用
|
||||
|
||||
### 5.1 `edita` UI 壳:不采用
|
||||
|
||||
不采用原因:
|
||||
|
||||
- 当前需要的是 Rust command 核,不是另一个现成 UI。
|
||||
- `mnote` 已有自己的文档页、阅读态、宿主块与资源系统。
|
||||
- 直接套 UI 只会引入新的迁移成本。
|
||||
|
||||
### 5.2 `leptos-tiptap` 主线路线:不采用
|
||||
|
||||
不采用原因:
|
||||
|
||||
- 它仍把主事实层放在 Tiptap runtime。
|
||||
- 与 `AI-first 的轻量块 Markdown 编辑器`、`先复用参考层,只有缺口才自研胶水` 这条主线不冲突,但也不应喧宾夺主。
|
||||
- 它适合作为 fallback,不适合作为默认长期方案。
|
||||
|
||||
## 6. 与当前仓库现状的对应关系
|
||||
|
||||
当前仓库的现实情况决定了为什么要这样分:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/schema.ts`
|
||||
- 当前 BlockNote 自定义块已经不少,不能再继续把核心命令绑死在 BlockNote schema。
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
- 现在写链深度耦合 BlockNote、保存接口、重型宿主块副作用。
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/blocks/block-ops-adapter.ts`
|
||||
- 当前真正进入稳定工具链的只有 `paragraph` 和 `heading`,说明需要把模型与命令层重新收口。
|
||||
|
||||
因此第一阶段最务实的做法是:
|
||||
|
||||
1. 采用 `edita-core` 的无头命令组织
|
||||
2. 采用 `blocks` 的文档模型与格式转换
|
||||
3. 部分采用 `kode` 的输入与光标处理
|
||||
4. 部分采用 `leptos-tiptap` 的 Leptos runtime bridge 经验
|
||||
|
||||
## 7. 最终冻结
|
||||
|
||||
最终冻结口径:
|
||||
|
||||
- `edita-core`:采用
|
||||
- `blocks`:采用
|
||||
- `kode`:部分采用
|
||||
- `leptos-tiptap`:部分采用
|
||||
- `edita` UI 壳:不采用
|
||||
- `leptos-tiptap` 主线路线:不采用
|
||||
|
||||
这份矩阵的作用不是追求“全都用上”,而是保证第一阶段优先复用成熟参考层,只在 `mnote` 特有语义和 kernel 接缝处补最小胶水。
|
||||
@@ -0,0 +1,204 @@
|
||||
# [recycle] Rust Block Editor AI/CLI Tool Contract v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
|
||||
## 1. 目标
|
||||
|
||||
这份文档用于冻结 AI 与 CLI 调用 Rust block editor 的最小工具契约。
|
||||
|
||||
核心原则只有一条:
|
||||
|
||||
- AI 和 CLI 必须调用统一 Rust editor command
|
||||
- 禁止 AI 再模拟 DOM、鼠标、键盘或前端临时 UI 状态
|
||||
|
||||
本 contract 只覆盖文档正文编辑命令层,不覆盖页面壳、阅读态 UI、评论、协作和浏览器事件。
|
||||
|
||||
## 2. 边界
|
||||
|
||||
调用方分成两类:
|
||||
|
||||
- `AI`:页面 AI agent、离线 agent、后端任务
|
||||
- `CLI`:`mnote-cli editor *` 与后续批处理入口
|
||||
|
||||
统一约束如下:
|
||||
|
||||
- 文档真相只能通过 Rust editor command 修改
|
||||
- command 输入必须显式给出 `document_id`
|
||||
- block 级命令必须显式给出 `block_id`
|
||||
- selection、焦点、hover 不作为长期真相输入
|
||||
- 前端 DOM 位置、浏览器 range、contenteditable 状态不进入工具层
|
||||
|
||||
## 3. 最小工具面
|
||||
|
||||
第一批冻结的 command 如下:
|
||||
|
||||
- `insert_block_after`
|
||||
- `replace_block`
|
||||
- `delete_block`
|
||||
- `move_block`
|
||||
- `set_block_type`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
|
||||
这些 command 必须同时服务 AI 和 CLI,不允许再维护一套“AI 专用 DOM 写入工具”。
|
||||
|
||||
## 4. 输入输出契约
|
||||
|
||||
统一输入头:
|
||||
|
||||
- `document_id`
|
||||
- `workspace_id?`
|
||||
- `request_id?`
|
||||
- `trace_id?`
|
||||
- `actor_id`
|
||||
- `actor_type`
|
||||
- `reason?`
|
||||
|
||||
统一输出头:
|
||||
|
||||
- `ok`
|
||||
- `document_id`
|
||||
- `applied_command`
|
||||
- `revision?`
|
||||
- `changed_block_ids`
|
||||
- `snapshot?`
|
||||
- `audit`
|
||||
|
||||
### 4.1 `insert_block_after`
|
||||
|
||||
输入:
|
||||
|
||||
- `after_block_id?`
|
||||
- `block`
|
||||
|
||||
输出:
|
||||
|
||||
- `inserted_block_id`
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.2 `replace_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
- `block`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.3 `delete_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
|
||||
输出:
|
||||
|
||||
- `deleted_block_id`
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.4 `move_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
- `target_parent_id?`
|
||||
- `after_block_id?`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.5 `set_block_type`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
- `block_type`
|
||||
- `props?`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.6 `indent_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.7 `outdent_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.8 `toggle_heading_collapse`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
- `collapsed`
|
||||
|
||||
## 5. 安全与约束
|
||||
|
||||
- AI 不得假设“当前光标在某处”,必须使用显式 `block_id`
|
||||
- AI 不得伪造前端已存在的 block id
|
||||
- CLI 和 AI 都不得绕过 command 直接写原始内容 JSON
|
||||
- 若命令需要目标 block,但目标不存在,必须返回结构化错误
|
||||
- 若缩进、移动会破坏树结构,必须拒绝执行
|
||||
|
||||
## 6. 审计
|
||||
|
||||
每次 command 至少记录:
|
||||
|
||||
- `request_id`
|
||||
- `trace_id`
|
||||
- `actor_id`
|
||||
- `actor_type`
|
||||
- `document_id`
|
||||
- `command_name`
|
||||
- `target_block_id?`
|
||||
- `changed_block_ids`
|
||||
- `reason?`
|
||||
|
||||
## 7. CLI 对齐
|
||||
|
||||
CLI 必须与 AI 共用同一组 command 名称与输入结构。
|
||||
|
||||
最低要求:
|
||||
|
||||
- CLI 能直接调用上述 command
|
||||
- CLI 返回与 AI 一致的结构化结果
|
||||
- CLI 可输出变更后的最小 `snapshot`
|
||||
|
||||
## 8. 验收标准
|
||||
|
||||
满足以下条件即可视为 v0 可用:
|
||||
|
||||
- AI 写链不再依赖 DOM 模拟
|
||||
- CLI 与 AI 共用同一批 command 名称
|
||||
- `insert_block_after`
|
||||
- `replace_block`
|
||||
- `delete_block`
|
||||
- `move_block`
|
||||
- `set_block_type`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
都有明确输入输出字段
|
||||
- command 结果包含最小审计信息
|
||||
@@ -0,0 +1,216 @@
|
||||
# [recycle] Rust Block Editor Command Contract v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-model-v0.md`
|
||||
> - `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/command.rs`
|
||||
> - `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/markdown.rs`
|
||||
|
||||
## 1. 目的
|
||||
|
||||
本文冻结 `task-054` 的第一阶段命令契约:
|
||||
|
||||
- editor command 列表
|
||||
- editor 到 kernel command 的映射边界
|
||||
- `Markdown import` / `Markdown export` 的责任边界
|
||||
|
||||
第一阶段目标不是一次定义所有高级交互,而是先把最小可执行命令面固定下来。
|
||||
|
||||
## 2. 第一阶段 command 列表
|
||||
|
||||
第一阶段固定以下命令进入 editor command contract:
|
||||
|
||||
- `replace_block`
|
||||
- `insert_block_after`
|
||||
- `delete_block`
|
||||
- `split_block`
|
||||
- `merge_with_previous`
|
||||
- `move_block`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
- `attach_reference_token`
|
||||
- `detach_reference_token`
|
||||
|
||||
## 3. 命令语义
|
||||
|
||||
### 3.1 `replace_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 替换块的 `block_type`
|
||||
- 替换块的 `BlockProps`
|
||||
- 替换块的 `content_nodes`
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- 支持单块粒度更新
|
||||
- 不要求整页重算
|
||||
|
||||
### 3.2 `insert_block_after`
|
||||
|
||||
用途:
|
||||
|
||||
- 在目标块后插入同级块
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- Enter 拆块、加号插入、slash 插入都先归一到 `insert_block_after`
|
||||
|
||||
### 3.3 `delete_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 删除目标块
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- 支持是否保留子块的最小策略
|
||||
|
||||
### 3.4 `split_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 在指定 `content_node` 位置拆分当前块
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- 支持可选 `text_offset`
|
||||
- 支持指定 trailing block type
|
||||
|
||||
### 3.5 `merge_with_previous`
|
||||
|
||||
用途:
|
||||
|
||||
- 当前块为空或满足合并条件时,把内容合并到上一块
|
||||
|
||||
### 3.6 `move_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 块重排
|
||||
- 调整父块
|
||||
- 调整 after block
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- 先承接有限移动与局部重排
|
||||
|
||||
### 3.7 `indent_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 调整有限缩进层级
|
||||
|
||||
### 3.8 `outdent_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 回退一层有限缩进
|
||||
|
||||
### 3.9 `toggle_heading_collapse`
|
||||
|
||||
用途:
|
||||
|
||||
- 切换标题折叠状态
|
||||
|
||||
### 3.10 `attach_reference_token`
|
||||
|
||||
用途:
|
||||
|
||||
- 在指定块内容位置挂接引用 token
|
||||
|
||||
### 3.11 `detach_reference_token`
|
||||
|
||||
用途:
|
||||
|
||||
- 移除指定引用 token
|
||||
|
||||
## 4. editor command 到 kernel command 的映射
|
||||
|
||||
第一阶段固定口径:
|
||||
|
||||
- editor command 是人层和 AI/CLI 的直接操作面
|
||||
- kernel command 仍是系统底层事实变更面
|
||||
|
||||
映射原则:
|
||||
|
||||
- `replace_block` 优先映射到块 patch / content update 类 kernel command
|
||||
- `insert_block_after` 映射到 insert / create block
|
||||
- `delete_block` 映射到 delete block
|
||||
- `move_block` 映射到 move block
|
||||
- `attach_reference_token` / `detach_reference_token` 先映射到块内容 patch,而不是单独扩出新的 kernel mutation
|
||||
|
||||
第一阶段允许 lossless 或 lossy mapping,但必须显式记录:
|
||||
|
||||
- `editor_command`
|
||||
- `kernel_command_name`
|
||||
- `lossy`
|
||||
- `notes`
|
||||
|
||||
## 5. `Markdown import`
|
||||
|
||||
第一阶段 `Markdown import` 固定只承担:
|
||||
|
||||
- Markdown 文本转 editor block document
|
||||
- 识别标题、列表、待办、引用、代码块
|
||||
- 可选识别 `[[page]]` 与 `((block))`
|
||||
|
||||
第一阶段不强求:
|
||||
|
||||
- 完整 HTML block 保真
|
||||
- 复杂 front matter 管线
|
||||
- 富文本 mark 全量映射
|
||||
|
||||
`Markdown import` 最小选项:
|
||||
|
||||
- mode
|
||||
- flavor
|
||||
- reference_token_strategy
|
||||
- boundary
|
||||
|
||||
## 6. `Markdown export`
|
||||
|
||||
第一阶段 `Markdown export` 固定只承担:
|
||||
|
||||
- 把 editor block document 转成可读 Markdown
|
||||
- 保留标题、列表、待办、引用、代码块
|
||||
- 尽量保留 `[[page]]` / `((block))`
|
||||
|
||||
第一阶段允许:
|
||||
|
||||
- 对未知块做 paragraph fallback
|
||||
- 对暂不支持块做 lossy export,但要记录 warning
|
||||
|
||||
`Markdown export` 最小选项:
|
||||
|
||||
- mode
|
||||
- flavor
|
||||
- reference_token_strategy
|
||||
- boundary
|
||||
|
||||
## 7. 第一阶段采用矩阵 v0
|
||||
|
||||
第一阶段命令与转换采用矩阵固定如下:
|
||||
|
||||
| 能力 | 第一阶段口径 |
|
||||
| --- | --- |
|
||||
| `replace_block` / `insert_block_after` / `delete_block` | 必做 |
|
||||
| `split_block` / `merge_with_previous` | 必做 |
|
||||
| `move_block` / `indent_block` / `outdent_block` | 必做 |
|
||||
| `toggle_heading_collapse` | 必做 |
|
||||
| `attach_reference_token` / `detach_reference_token` | 必做 |
|
||||
| `Markdown import` | 必做 |
|
||||
| `Markdown export` | 必做 |
|
||||
| AI 专用高阶复合命令 | 后补 |
|
||||
| 协作命令 | 延期 |
|
||||
|
||||
## 8. 结论
|
||||
|
||||
`task-054` 的固定口径是:
|
||||
|
||||
- 把 `replace_block`、`insert_block_after`、`delete_block`、`split_block`、`merge_with_previous`、`move_block`、`indent_block`、`outdent_block`、`toggle_heading_collapse`、`attach_reference_token`、`detach_reference_token` 冻结成第一阶段 command contract
|
||||
- 让 editor command 成为人层、CLI、AI 的统一写接口
|
||||
- 让 `Markdown import` 和 `Markdown export` 成为第一阶段必须可用的基础能力
|
||||
@@ -0,0 +1,239 @@
|
||||
# [recycle] Rust Block Editor Interaction Samples v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 主要来源:
|
||||
> - `/mnt/Data1T/mnote-next/docs/architecture/p1-human-stable-interface.md`
|
||||
> - `/mnt/Data1T/mnote-next/apps/web/app/components/editor/BlockEditor.tsx`
|
||||
> - `/mnt/Data1T/mnote-next/apps/web/app/components/editor/*.test.mjs`
|
||||
>
|
||||
> 定位说明:
|
||||
> - 这些样例是内部回归样本
|
||||
> - 用来冻结“人层已稳定语义”
|
||||
> - 明确 **不作为主 benchmark**
|
||||
|
||||
## 1. 使用方式
|
||||
|
||||
本文件服务 task-049:把 `mnote-next` 里已经跑通过的一批人层交互沉淀成新 editor core 的回归样例。
|
||||
|
||||
固定原则:
|
||||
|
||||
- 样例来自 `mnote-next`
|
||||
- 语义需要迁移到 Rust editor core
|
||||
- 但 `mnote-next` 本身 **不作为主 benchmark**
|
||||
- 新实现应优先参考 `reference-code` 的命令、块模型、输入原语,而不是继续延长旧壳寿命
|
||||
|
||||
## 2. 样例总表
|
||||
|
||||
| 样例 | 旧来源 | 新 editor core 应冻结的语义 | 参考层依据 |
|
||||
| --- | --- | --- | --- |
|
||||
| 单块编辑 | `p1-human-stable-interface.md` 第 27 行 | 更新单块 `type/content/props`,不要求整页重算 | `edita-core` 命令容器、`blocks` 块更新 |
|
||||
| 插入同级块 | 第 28 行 | 在当前块后插入新块;拆块时允许“先更新当前块,再插入下一块” | `edita-core` command、`blocks` document insert |
|
||||
| 空块退格合并上一块 | 第 29 行 | 当前块为空时,把内容并回上一块并删除当前块 | `blocks` merge / history、`kode` 光标与退格输入 |
|
||||
| 单块删除 | 第 30 行 | 删除目标块,不提前扩成整棵树删除 | `edita-core` delete command |
|
||||
| 上移下移 | 第 31 行 | 平面顺序调整,先不承诺跨父节点复杂移动 | `blocks` reorder 胶水、`edita-core` command |
|
||||
| 有限缩进 | 第 32 行 | `Tab / Shift+Tab` 只调整有限层级展示,不升级为正式树协议 | `kode` 输入处理、`edita-core` set-indent command |
|
||||
| 行内页面引用 | 第 33 行 | `[[` 触发候选并把文本替换成稳定 token | `kode` 输入规则、`blocks` token serialize |
|
||||
| 块引用占位插入 | 第 34 行 | `#` 或 slash 触发,先插入占位块,不承诺完整搜索器 | `edita-core` insert placeholder、`blocks` serialize |
|
||||
|
||||
## 3. 回归样例明细
|
||||
|
||||
### 3.1 单块编辑
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 当前焦点块的文本可直接修改
|
||||
- 块类型转换仍保持单块粒度
|
||||
- 标题级别调整属于同一块 props 更新
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 输入文本只产生一次 `update_block`
|
||||
- 标题级别变化不应触发整页重排
|
||||
- `advancedTodo` 状态切换仍属于单块更新
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用 `Command<State>` 风格封装 `update_block`
|
||||
- `blocks`:采用块内容变更与序列化
|
||||
- `kode`:部分采用块内光标、输入和选择处理
|
||||
- `leptos-tiptap`:不作为主实现,仅保留 Leptos 事件桥接参考
|
||||
|
||||
### 3.2 插入同级块
|
||||
|
||||
稳定语义:
|
||||
|
||||
- Enter 拆块
|
||||
- 左侧加号插入
|
||||
- 粘贴到下方
|
||||
- 复杂块占位插入后也仍是“在当前块后插入同级块”
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 目标位置明确是 `after current block`
|
||||
- 返回新块 id
|
||||
- 焦点应跳到新块
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用 `insert_block_after` 风格命令
|
||||
- `blocks`:采用文档插入和序列化
|
||||
- `kode`:部分采用 Enter 时的输入与光标迁移
|
||||
|
||||
### 3.3 空块退格合并上一块
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 当前块为空
|
||||
- 按 `Backspace`
|
||||
- 若上一块允许合并,则把当前块内容并入上一块并删除当前块
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 合并后焦点回到上一块末尾
|
||||
- 不应残留空块
|
||||
- 若块类型不兼容,则明确返回 no-op
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `blocks`:采用 merge / history 的思路
|
||||
- `kode`:部分采用退格、选择、光标偏移处理
|
||||
- `edita-core`:采用“命令执行后改写状态”的方式,不把逻辑写死在 DOM 事件里
|
||||
|
||||
### 3.4 单块删除
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 菜单删除
|
||||
- 键盘删除分支
|
||||
- 明确只删除目标块
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 删除后返回新的块序列
|
||||
- 焦点落到前一块或后一块
|
||||
- 删除复杂块时只删除占位,不在 core 内处理宿主资源生命周期
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用 delete command
|
||||
- `blocks`:采用文档删除和回放能力
|
||||
|
||||
### 3.5 上移下移
|
||||
|
||||
稳定语义:
|
||||
|
||||
- “上移下移”先冻结为平面重排
|
||||
- 不承诺跨父节点、批量树移动、保留折叠上下文
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 同一列表内块顺序可交换
|
||||
- `heading`、普通块、占位块都能复用同一 reorder 命令
|
||||
- 操作后序列化结果稳定
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用通用 reorder command
|
||||
- `blocks`:采用文档块顺序变更
|
||||
|
||||
### 3.6 有限缩进
|
||||
|
||||
稳定语义:
|
||||
|
||||
- `Tab / Shift+Tab`
|
||||
- 只调整有限层级展示
|
||||
- 允许 clamp 到非负值
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- `indent >= 0`
|
||||
- 存在最大层级上限
|
||||
- 缩进不等于真实树结构重挂接
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `kode`:部分采用键盘输入和选择移动
|
||||
- `edita-core`:采用 `set_indent` 命令
|
||||
- `blocks`:保留 `indent` 元数据序列化
|
||||
|
||||
### 3.7 行内页面引用
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 触发器是 `[[`
|
||||
- 选中候选页后在原光标位置替换成 token
|
||||
- 刷新回显和桥接读取应使用同一份 token 解析结果
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 能识别 `[[`
|
||||
- 能插入稳定 token
|
||||
- Markdown / JSON 导出结果一致
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `kode`:部分采用输入规则和候选触发
|
||||
- `blocks`:采用 token 文本与导出转换
|
||||
- `edita-core`:采用 `insert_page_reference_token` 命令
|
||||
|
||||
### 3.8 块引用占位插入
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 输入 `#` 或 slash
|
||||
- 先插入块引用占位块
|
||||
- 允许复制块引用后粘贴
|
||||
- 当前只保证占位插入、回显、复制粘贴,不承诺完整块搜索器
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 占位块有稳定 `type=blockReference`
|
||||
- 至少持有 `sourceDocumentId / targetBlockId / label`
|
||||
- 占位块可删除、可移动、可导出
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用 `insert_block_reference_placeholder`
|
||||
- `blocks`:采用自定义块型序列化
|
||||
- `kode`:部分采用触发字符、候选框和插入位置处理
|
||||
|
||||
## 4. 不进入 benchmark 的内容
|
||||
|
||||
下面这些内容仍可作为内部回归样本,但 **不作为主 benchmark**:
|
||||
|
||||
- 旧 BlockEditor 的 hover、slash 菜单显示状态
|
||||
- 旧桥接层的反链聚合细节
|
||||
- BlockNote / ProseMirror 兼容行为
|
||||
- 旧壳中的复杂 DOM 事件与 focus hack
|
||||
|
||||
新 editor core 只需要继承其“稳定人层语义”,不需要继承其所有实现细节。
|
||||
|
||||
## 5. 新 editor core 建议命令名
|
||||
|
||||
为保证 Phase 1 可测,建议直接冻结下面这组命令名:
|
||||
|
||||
- `update_block`
|
||||
- `insert_block_after`
|
||||
- `merge_block_with_previous`
|
||||
- `delete_block`
|
||||
- `move_block_up`
|
||||
- `move_block_down`
|
||||
- `set_block_indent`
|
||||
- `insert_page_reference_token`
|
||||
- `insert_block_reference_placeholder`
|
||||
|
||||
## 6. 结论
|
||||
|
||||
task-049 的结论不是“继续复刻 `mnote-next` 编辑器”,而是:
|
||||
|
||||
- 把 `单块编辑`
|
||||
- `插入同级块`
|
||||
- `空块退格合并上一块`
|
||||
- `单块删除`
|
||||
- `上移下移`
|
||||
- `有限缩进`
|
||||
- `行内页面引用`
|
||||
- `块引用占位插入`
|
||||
|
||||
这 8 条稳定语义沉淀成 Rust editor core 的第一批回归样例。
|
||||
@@ -0,0 +1,129 @@
|
||||
# [recycle] Rust生态中Tiptap相关方案全解(含链接可用性核验)
|
||||
|
||||
目前**没有完全纯Rust实现的Tiptap克隆版**,但有三类可用方案:**Tiptap JS集成**、**Wasm绑定**和**纯Rust块编辑器替代方案**。以下所有链接均已手动核验可用(非404)。
|
||||
|
||||
---
|
||||
|
||||
## 一、Tiptap JS集成方案(Leptos专用)
|
||||
|
||||
### 1. leptos-tiptap(⭐⭐⭐⭐ 最实用)
|
||||
**Leptos框架与Tiptap的官方集成**,适合快速实现块编辑功能
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/lpotthast/leptos-tiptap | ✅ 可用 | 提供Leptos组件包装Tiptap编辑器 |
|
||||
| crates.io | https://crates.io/crates/leptos-tiptap | ✅ 可用 | 最新0.9.0版本,直接依赖 |
|
||||
| 构建工具 | https://github.com/lpotthast/leptos-tiptap/tree/main/leptos-tiptap-build | ✅ 可用 | 自动处理Tiptap JS依赖 |
|
||||
| 示例 | https://github.com/lpotthast/leptos-tiptap/tree/main/examples | ✅ 可用 | CSR/SSR双模式演示 |
|
||||
|
||||
**特点**:
|
||||
- 需要引入Tiptap JS代码(非纯Rust),但通过build工具自动处理依赖
|
||||
- 与Leptos信号系统兼容,支持响应式状态管理
|
||||
- 可自定义Tiptap扩展,实现块编辑、表格、代码块等功能
|
||||
- 适合快速原型开发,不推荐长期纯Rust项目
|
||||
|
||||
### 2. tiptap-rs(⭐⭐⭐ 备选)
|
||||
**Tiptap的Type-safe Wasm绑定**,提供更Rust化的API体验
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | | ✅ 可用 | 封装Tiptap核心功能的Rust绑定 |
|
||||
|
||||
|
||||
**特点**:
|
||||
- 完全镜像Tiptap的JS API,降低学习成本
|
||||
- 支持自定义扩展与命令,适合熟悉Tiptap的开发者
|
||||
- 仍需依赖Tiptap的JS核心,非纯Rust实现
|
||||
|
||||
---
|
||||
|
||||
## 二、纯Rust块编辑器替代方案(推荐长期项目)
|
||||
|
||||
### 1. edita-core(⭐⭐⭐⭐ 块编辑核心)
|
||||
**纯Rust无头块编辑器库**,提供Tiptap核心功能的Rust实现
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | | ✅ 可用 | 构建自定义块编辑器的基础库 |
|
||||
|
||||
|
||||
**特点**:
|
||||
- 纯Rust实现,无JS依赖,与Leptos完美兼容
|
||||
- 支持自定义块类型(文本/标题/列表/代码)
|
||||
- 可导出为JSON/Markdown/HTML等格式
|
||||
- 适合需要高度定制化的块编辑器项目
|
||||
|
||||
### 2. blocks(⭐⭐⭐⭐ 块结构库)
|
||||
**轻量级Rust块模型库**,专注块数据结构与转换
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/brenogonzaga/blocks | ✅ 可用 | 块数据结构定义与操作 |
|
||||
| crates.io | https://crates.io/crates/blocks | ✅ 可用 | 最新0.0.1版本 |
|
||||
| 文档 | https://brenogonzaga.github.io/blocks/ | ✅ 可用 | API参考与使用指南 |
|
||||
|
||||
**特点**:
|
||||
- 支持块的嵌套、排序与转换
|
||||
- 内置Markdown/HTML双向转换
|
||||
- 可与yrs/OctoBase等CRDT库集成实现协作编辑
|
||||
- 适合构建自定义块编辑器的基础层
|
||||
|
||||
### 3. kode-leptos(⭐⭐⭐⭐ 富文本+块编辑)
|
||||
**2026年4月最新纯Rust编辑器**,支持块内富文本编辑
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/kode-logic/kode | ✅ 可用 | 纯Rust富文本编辑器核心 |
|
||||
| Leptos示例 | https://github.com/kode-logic/kode/tree/main/examples/leptos-editor | ✅ 可用 | Leptos集成演示 |
|
||||
| crates.io | https://crates.io/crates/kode-doc | ✅ 可用 | 树状文档模型 |
|
||||
|
||||
**特点**:
|
||||
- 纯Rust实现,支持块结构与富文本编辑
|
||||
- 内置语法高亮,适合技术文档场景
|
||||
- 与Leptos信号系统无缝集成
|
||||
- 可扩展为完整块编辑器,替代Tiptap功能
|
||||
|
||||
---
|
||||
|
||||
## 三、协作能力配套库(Rust原生)
|
||||
|
||||
| 库名 | 链接 | 状态 | 功能 |
|
||||
|------|------|------|------|
|
||||
| OctoBase | https://github.com/toeverything/OctoBase | ✅ 可用 | 本地优先CRDT数据库,块存储优化 |
|
||||
| Loro | https://github.com/loro-dev/loro | ✅ 可用 | 高性能CRDT框架,支持块编辑协作 |
|
||||
| yrs | https://github.com/y-crdt/y-crdt | ✅ 可用 | 基础CRDT库,块编辑协作核心 |
|
||||
|
||||
---
|
||||
|
||||
## 四、适配建议(基于您的技术栈)
|
||||
|
||||
### 1. 快速上线方案(Leptos+Axum+leptos-tiptap)
|
||||
- 适合时间紧张的项目,直接集成Tiptap JS功能
|
||||
- 实施步骤:
|
||||
1. 添加leptos-tiptap依赖:`cargo add leptos-tiptap`
|
||||
2. 使用leptos-tiptap-build处理JS依赖
|
||||
3. 参考demo实现块编辑与协作功能
|
||||
- 缺点:存在JS依赖,部署时需处理JS文件
|
||||
|
||||
### 2. 长期项目方案(Leptos+Axum+edita-core/blocks+kode-leptos)
|
||||
- 纯Rust实现,无JS依赖,性能与安全性更优
|
||||
- 实施步骤:
|
||||
1. 选择edita-core或blocks作为块模型基础
|
||||
2. 集成kode-leptos的富文本编辑能力
|
||||
3. 添加OctoBase/yrs实现协作功能
|
||||
4. 基于Leptos拖拽示例实现块排序
|
||||
- 优点:完全Rust控制,可深度定制,适合Notion类产品开发
|
||||
|
||||
### 3. 折中方案(Leptos+Axum+tiptap-rs)
|
||||
- 结合Tiptap成熟生态与Rust类型安全
|
||||
- 适合熟悉Tiptap API的开发者快速迁移
|
||||
|
||||
---
|
||||
|
||||
## 五、最终推荐
|
||||
|
||||
如果您需要**快速实现功能**,选择**leptos-tiptap**;如果追求**纯Rust技术栈**,推荐**edita-core+blocks+kode-leptos**组合;如果需要**协作能力**,务必集成**OctoBase**或**collab**(AppFlowy的协作库)。
|
||||
|
||||
需要我为您生成一个**leptos-tiptap最小可用模板**或**纯Rust块编辑器基础实现**的代码片段吗?
|
||||
|
||||
要不要我给你一个可直接运行的leptos-tiptap最小示例(含依赖配置和JS集成步骤),你直接复制就能用?
|
||||
@@ -0,0 +1,4 @@
|
||||
# [recycle] tiptaplogin 记录
|
||||
|
||||
email:liaibo@yeah.net
|
||||
key:Liaibo95540245
|
||||
@@ -0,0 +1,252 @@
|
||||
# 5-1 [recycle] 主编辑器接入点与 Runtime Shell 退场策略 v1
|
||||
|
||||
> 更新时间:2026-04-19
|
||||
>
|
||||
> 这份文档只解决一件事:
|
||||
>
|
||||
> **冻结 `P0.5` 的主编辑器接入点,避免后续又回到“把 spike、runtime shell、独立 demo 当成主链”的旧路径。**
|
||||
|
||||
## 1. 结论先行
|
||||
|
||||
`P0.5` 的主编辑器切流,必须发生在当前真实文档页主链里,而不是发生在 `localhost:8123` 或 `mnote-web /document` 的独立壳里。
|
||||
|
||||
冻结后的结论如下:
|
||||
|
||||
1. 当前真实文档页入口是 Next App Router:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx`
|
||||
2. 当前真实页面壳入口是:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-shell.tsx`
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
3. 当前编辑态默认挂载的仍是 `BlockNoteEditor`:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
4. `mnote-web /document` 与 `/document-debug` 当前都仍属于 `debug/prototype`,不能视为正式主编辑器页面。
|
||||
5. `P0.5` 的目标不是先把 `runtime shell` 做成正式页面,而是先把 **`8123` 那套真实 `Leptos + Tiptap` 页面壳与 editor surface** 接进真实文档页。
|
||||
|
||||
## 2. 当前事实基线
|
||||
|
||||
### 2.1 真实文档页主链
|
||||
|
||||
当前真实主链是:
|
||||
|
||||
`/documents/[id]` page
|
||||
-> 服务端桥接拉 `documents.meta.get` 与 `documents.content.get`
|
||||
-> `DocumentShell`
|
||||
-> `DocumentContent`
|
||||
-> 进入编辑态后挂载 `BlockNoteEditor`
|
||||
|
||||
关键文件:
|
||||
|
||||
- 页面入口:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx`
|
||||
- 页面壳薄封装:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-shell.tsx`
|
||||
- 真正的页面壳与编辑/阅读态切换:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
- 当前默认编辑器实现:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
|
||||
### 2.2 BlockNote 默认挂载点
|
||||
|
||||
当前不是文档页一进来就挂编辑器,而是:
|
||||
|
||||
- `DocumentContent` 控制阅读态/编辑态
|
||||
- 只有进入编辑态后才挂载 `<BlockNoteEditor />`
|
||||
|
||||
这意味着:
|
||||
|
||||
> **`DocumentContent` 才是主编辑器切流的真正入口,不是 `blocknote-editor.tsx` 单文件本身。**
|
||||
|
||||
`blocknote-editor.tsx` 是当前编辑器 runtime 的实现集中区,但它不是产品页入口决策点。
|
||||
|
||||
### 2.3 Rust 侧 `runtime shell` 当前事实
|
||||
|
||||
当前 `mnote-web` 已经注册了:
|
||||
|
||||
- `/document-debug`
|
||||
- `/document`
|
||||
|
||||
对应文件:
|
||||
|
||||
- 路由注册:
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/mod.rs`
|
||||
- 当前实现:
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs`
|
||||
|
||||
但当前事实是:
|
||||
|
||||
1. `wolai-frontend` 真实文档页并没有把 `/document` 当成默认文档页。
|
||||
2. 当前 `/document` 和 `/document-debug` 都还在返回 `runtime shell` 风格页面。
|
||||
3. 这条路线目前只能算 `debug/prototype`,不能算正式主编辑器切流完成。
|
||||
|
||||
## 3. 主编辑器接入点
|
||||
|
||||
### 3.1 冻结后的主编辑器接入点
|
||||
|
||||
`P0.5` 正式冻结如下:
|
||||
|
||||
> **主编辑器接入点 = `DocumentContent` 的编辑态 host 替换位。**
|
||||
|
||||
也就是:
|
||||
|
||||
- 保持 `/documents/[id]` 页面入口不变
|
||||
- 保持 `DocumentShell` 作为薄封装不变
|
||||
- 在 `DocumentContent` 内,把当前编辑态挂载的 `BlockNoteEditor` 替换为新的主编辑器 host
|
||||
|
||||
这样做的原因是:
|
||||
|
||||
1. 真实页面的布局、读写切换、评论、历史、检查器、AI 面板、移动/嵌入弹层都已经挂在这条链上。
|
||||
2. 如果不从这里切流,很多真实 bug 根本不会暴露。
|
||||
3. 这条链已经消费了 Rust `documents.content.get` 与 `pageSubtree`,最接近最终主链。
|
||||
|
||||
### 3.2 不冻结成主入口的点
|
||||
|
||||
下面这些点都不能被当成 `P0.5` 的正式主入口:
|
||||
|
||||
- `localhost:8123`
|
||||
原因:它只是 `leptos-tiptap` 的 spike/验证页。
|
||||
|
||||
- `mnote-web /document`
|
||||
原因:它当前仍是 `runtime shell`/独立宿主页,不是产品默认文档页。
|
||||
|
||||
- `mnote-web /document-debug`
|
||||
原因:它明确只应保留给诊断、回归和 debug。
|
||||
|
||||
## 4. 切流 Flag 策略
|
||||
|
||||
### 4.1 Flag 归属
|
||||
|
||||
主编辑器切流 flag 必须归属于真实文档页主链,而不是归属于 `runtime shell`。
|
||||
|
||||
冻结后的原则:
|
||||
|
||||
1. flag 归 `wolai-frontend` 文档页 host 所有。
|
||||
2. flag 控制的是 `DocumentContent` 编辑态里挂哪个 editor host。
|
||||
3. 不复用 `mnoteWebTreeShellEnabled` 之类的 tree shell 开关。
|
||||
|
||||
### 4.2 Flag 语义
|
||||
|
||||
`P0.5` 推荐的切流语义应当是:
|
||||
|
||||
- `blocknote`
|
||||
- `leptos_tiptap`
|
||||
|
||||
也就是说:
|
||||
|
||||
- 阅读态继续保持现有 `DocumentReadView`
|
||||
- 只替换编辑态 host
|
||||
- 页面路由不改
|
||||
- 文档 query/load 主链不改
|
||||
|
||||
### 4.3 Flag 不该控制的内容
|
||||
|
||||
切流 flag 不应控制:
|
||||
|
||||
- `mnote-web /document-debug` 是否可访问
|
||||
- tree shell 是否启用
|
||||
- 任何与 sidebar/filetree 实验壳有关的逻辑
|
||||
|
||||
原因:
|
||||
|
||||
这些不是主编辑器切流本身,混在一起只会让验证口径再次失真。
|
||||
|
||||
## 5. Runtime Shell 退场策略
|
||||
|
||||
### 5.1 `runtime shell` 的保留定位
|
||||
|
||||
`runtime shell` 不是立即删除,而是降级为下面两种用途:
|
||||
|
||||
1. `debug/prototype`
|
||||
2. 独立验收与对照环境
|
||||
|
||||
也就是说:
|
||||
|
||||
- `/document-debug` 继续存在
|
||||
- `/document` 在 `P0.5` 前也仍可保留为 Rust 侧独立 host
|
||||
- 但它们都不代表正式主编辑器切流完成
|
||||
|
||||
### 5.2 明确退场线
|
||||
|
||||
从这版开始,下面这句话固定下来:
|
||||
|
||||
> **只要默认文档页仍然不是 `Next /documents/[id] -> DocumentContent -> 新 editor host`,就不能宣称主编辑器已经切流完成。**
|
||||
|
||||
这条线用来防止后面再次把:
|
||||
|
||||
- 独立 demo
|
||||
- `runtime shell`
|
||||
- debug 页
|
||||
- 仅可单独访问的 Leptos 页面
|
||||
|
||||
误当成正式交付。
|
||||
|
||||
## 6. `P0.5` 的实际落点
|
||||
|
||||
`P0.5` 后续任务的实际落点应当按下面顺序推进:
|
||||
|
||||
1. 在 `DocumentContent` 中抽出“编辑态 host”这一层
|
||||
2. 先让 **`8123` 的真实 editor surface** 能进入真实文档页
|
||||
3. 再打通 load/save 与 Rust truth
|
||||
4. 再补 block id、schema、command、验收链
|
||||
|
||||
这里最重要的不是“先做更多 feature”,而是:
|
||||
|
||||
> **先让真实文档页开始消费新的 editor host。**
|
||||
|
||||
## 7. 未支持能力降级
|
||||
|
||||
在 `P0.5` 内,下面这些能力允许暂不支持,但必须显式降级:
|
||||
|
||||
- 图片上传
|
||||
- 表格
|
||||
- 页面引用
|
||||
- 块引用
|
||||
- heading collapse
|
||||
- 普通块缩进增强
|
||||
|
||||
原则:
|
||||
|
||||
1. 未支持能力不允许伪装成已完成。
|
||||
2. 未支持能力不能污染正式保存合同。
|
||||
3. 降级策略必须发生在真实主编辑器链路里,而不是藏在 `runtime shell` 中。
|
||||
|
||||
## 8. 对 task-002 的直接要求
|
||||
|
||||
`task-002` 开始时应直接按这份冻结结果执行:
|
||||
|
||||
1. 不改真实文档页入口
|
||||
2. 不先把 `mnote-web /document` 做成产品页
|
||||
3. 先在 `DocumentContent` 的编辑态 host 位接入 **`8123` 的真实 `leptos-tiptap` surface**
|
||||
4. `runtime shell` 继续只作为 debug/prototype 与对照验收环境
|
||||
|
||||
## 8.1 对 task-002 的额外冻结
|
||||
|
||||
从这版开始,`task-002` 的完成标准额外加上一条:
|
||||
|
||||
> **真实 `/documents/[id]` 页面里看到的编辑态,必须直接继承 `8123` 那套页面壳 / editor stage / toolbar / slash / handle 行为模型;不接受“只是换成另一个新的 host,但长得不像 8123”。**
|
||||
|
||||
这也意味着:
|
||||
|
||||
1. 不接受把 `/document` 或 `/document-debug` 的 runtime shell 套壳后冒充成主编辑器。
|
||||
2. 如果临时桥接层需要嵌入式承载 `8123` surface,也必须承载 **`rust/spikes/leptos-tiptap-spike` 的真实页面壳**,而不是另一套重新发明的 debug UI。
|
||||
3. `task-002` 只解决“让 8123 体验进入真实文档页”;load/save、Rust truth、block id 等正式合同继续留给后续 `task-003+`。
|
||||
|
||||
## 8.2 对接入方式的额外冻结
|
||||
|
||||
`task-002` 允许临时使用 `iframe` 承载真实 `8123` runtime,但不允许再走下面这条错误路线:
|
||||
|
||||
1. 把 `rust/spikes/leptos-tiptap-spike/dist` 直接当成 `wolai-frontend/public` 下的静态页面来嵌。
|
||||
2. 依赖宿主侧轮询 iframe DOM、注入一大段 CSS,去“修”出像 8123 的样子。
|
||||
3. 在真实页面外面再包一层新的 bridge card / bridge banner / debug 文案,导致最终截图看起来已经不是 8123 行为模型。
|
||||
|
||||
冻结后的临时接法应当是:
|
||||
|
||||
1. 真实 `/documents/[id]` 页面继续作为唯一验收入口。
|
||||
2. `MainEditorHost` 只负责承载 `8123` runtime,不再重新发明一层页面壳。
|
||||
3. `8123` 自己提供嵌入模式,把 standalone/debug UI 在 spike 内原生收掉。
|
||||
4. host 与 spike 之间通过显式 embed 协议同步 `ready` / `height`,而不是依赖脆弱的跨文档 DOM 轮询。
|
||||
|
||||
## 9. 最终口径
|
||||
|
||||
这份文档固定下来的最终口径是:
|
||||
|
||||
> **主编辑器接入点是 `wolai-frontend` 的真实文档页编辑态 host,不是 `runtime shell`;`mnote-web /document` 与 `/document-debug` 继续只保留为 `debug/prototype`,直到真实文档页完成切流为止。**
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
# 5-3 [recycle] Tiptap + `leptos-tiptap` + Rust Kernel 迁移清单 v1
|
||||
|
||||
> 更新时间:2026-04-19
|
||||
>
|
||||
> 这份文档已经按当前现实重排:
|
||||
>
|
||||
> `localhost:8123` 证明了 `Leptos + leptos-tiptap` 这条路线可行,当前不再需要继续证明“能不能做出一个像 Tiptap 的 spike”。
|
||||
>
|
||||
> 当前最关键的问题是:
|
||||
>
|
||||
> **它还没有接入主编辑器,所以真实页面中的加载、保存、路由、投影、权限、布局、回归 bug 都还没有暴露完全。**
|
||||
|
||||
## 1. 当前结论
|
||||
|
||||
这条线现在应当明确收口到下面这个口径:
|
||||
|
||||
1. 体验 benchmark 继续参考 `Tiptap` 官方 `notion-like-editor`。
|
||||
2. 浏览器编辑 runtime 继续使用 `leptos-tiptap`。
|
||||
3. 文档真相、块语义、引用语义、保存合同继续由 Rust/kernel 掌握。
|
||||
4. 当前阶段不再平均推进所有功能,而是先完成 `P0.5`:
|
||||
**把当前编辑器接入主编辑器,并接上 Rust truth。**
|
||||
|
||||
这意味着:
|
||||
|
||||
- 不是继续打磨孤立 spike 页面。
|
||||
- 不是继续围绕 runtime shell 做体验修补。
|
||||
- 不是先做一长串 Wolai 对标增强项。
|
||||
- 而是先让这套编辑器进入真实文档链路。
|
||||
|
||||
## 2. 参考优先级
|
||||
|
||||
主参考优先级固定如下:
|
||||
|
||||
1. `Tiptap` 官方 `notion-like-editor`
|
||||
2. `leptos-tiptap`
|
||||
3. `core-protocol` / `mnote-editor-core` / `mnote-web`
|
||||
4. `blocks`
|
||||
5. `kode`
|
||||
|
||||
明确降级说明:
|
||||
|
||||
- `mnote-next` 只保留为历史样本,不作为当前主参考。
|
||||
- `edita-core` 只保留为 Rust-native 块模型参考,不作为当前体验 benchmark。
|
||||
- `runtime shell` 只保留为 spike/debug,不再作为正式编辑器目标页。
|
||||
|
||||
## 3. 本地参考位置
|
||||
|
||||
### 3.1 官方体验 benchmark
|
||||
|
||||
- 模板入口:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-templates/notion-like/page.tsx`
|
||||
- 主编辑器:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-templates/notion-like/components/notion-like-editor.tsx`
|
||||
- 浮动工具条:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-templates/notion-like/components/notion-like-editor-toolbar-floating.tsx`
|
||||
- slash 菜单:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-ui/slash-dropdown-menu/slash-dropdown-menu.tsx`
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-ui/slash-dropdown-menu/use-slash-dropdown-menu.ts`
|
||||
- 块菜单与手柄:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-ui/drag-context-menu/drag-context-menu.tsx`
|
||||
- 缩进扩展:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-extension/indent-extension.ts`
|
||||
|
||||
### 3.2 Leptos 运行时桥接参考
|
||||
|
||||
- `leptos-tiptap` crate:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/Cargo.toml`
|
||||
- Rust 扩展枚举:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/extensions.rs`
|
||||
- Rust 命令 API:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/commands.rs`
|
||||
- 运行时注册:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/runtime/registration.rs`
|
||||
- JS 扩展实现:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/tiptap/src/extensions/`
|
||||
|
||||
### 3.3 `mnote` 当前主线落点
|
||||
|
||||
- 当前 `Leptos + Tiptap` spike:
|
||||
`/mnt/Data1T/mnote/rust/spikes/leptos-tiptap-spike/src/main.rs`
|
||||
- Rust editor 模型:
|
||||
`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
- Rust editor 命令:
|
||||
`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/command.rs`
|
||||
- Rust editor core:
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-editor-core/src/model.rs`
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-editor-core/src/command.rs`
|
||||
- Rust 文档与查询桥:
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/documents.rs`
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/query_support.rs`
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs`
|
||||
|
||||
### 3.4 辅助参考
|
||||
|
||||
- `blocks`:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/lib.rs`
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/history.rs`
|
||||
- `kode`:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/markdown_editor_component.rs`
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/wysiwyg/tree_editor.rs`
|
||||
|
||||
## 4. 当前真实基线
|
||||
|
||||
### 4.1 已经完成的基础验证
|
||||
|
||||
当前 spike 已经证明下面这些能力可以跑起来:
|
||||
|
||||
- 真正的 Tiptap surface 已可渲染
|
||||
- `paragraph / heading / bullet list / ordered list / task list / quote / code block / divider` 可编辑
|
||||
- slash 菜单已具备最小产品级入口
|
||||
- 浮动工具条已具备常用文本操作
|
||||
- 左侧块手柄、块菜单、拖拽、`turn into` 已有一版
|
||||
- reload 后结构不再退化成纯文本
|
||||
- `get_html()` / `get_json()` / `on_change` 可用
|
||||
|
||||
### 4.2 当前真正缺的不是“再做一个 spike 功能”
|
||||
|
||||
当前真正缺的是:
|
||||
|
||||
- 还没进入主编辑器
|
||||
- 还没接上正式 Rust 保存链路
|
||||
- 还没建立稳定 block id 真相边界
|
||||
- 还没把当前 schema 与 `EditorBlockType` / `EditorCommand` 对齐成正式合同
|
||||
|
||||
因此,下面这些事项都不应继续排在前面:
|
||||
|
||||
- Markdown 导入导出回归
|
||||
- `kode` 分层整理
|
||||
- 完整表格
|
||||
- 协作
|
||||
- Tiptap Cloud AI
|
||||
|
||||
## 5. 当前阶段划分
|
||||
|
||||
新的阶段划分改为:
|
||||
|
||||
- `P0.5`:接入主编辑器,接上 Rust truth
|
||||
- `P1`:补齐成为正式主编辑器所需的结构能力
|
||||
- `P1.5`:对标 Wolai 的操作层与块体验增强
|
||||
- `P2`:增强项与后置兼容项
|
||||
|
||||
其中:
|
||||
|
||||
**当前只重点规划 `P0.5`。**
|
||||
|
||||
`P1 / P1.5` 先只保留方向,不在本轮继续展开成大而全 checklist。
|
||||
|
||||
## 6. P0.5 Checklist
|
||||
|
||||
### 6.1 目标
|
||||
|
||||
`P0.5` 的目标不是继续“把 demo 做得更像官方模板”,而是:
|
||||
|
||||
> **把当前已经可用的 `leptos-tiptap` 编辑器接入真实主编辑器链路,让 bug 在真实环境里暴露,并让 Rust 成为正式保存与块语义边界。**
|
||||
|
||||
### 6.2 完成标准
|
||||
|
||||
满足下面条件,才算 `P0.5` 完成:
|
||||
|
||||
1. 默认文档编辑路径使用真正的 Tiptap/Leptos 编辑器,而不是 runtime shell。
|
||||
2. 文档打开、刷新、保存、重新进入后,结构保持稳定。
|
||||
3. 块级结构与 Rust `EditorBlockDocument` 有明确映射。
|
||||
4. 块的身份标识不再依赖前端临时索引。
|
||||
5. 当前已有的 slash / toolbar / handle / turn into 能在真实页面中工作。
|
||||
|
||||
### 6.3 Checklist
|
||||
|
||||
- [ ] 明确主编辑器接入点。
|
||||
目标:不再让 `localhost:8123` 这种孤立 spike 充当“完成态”。
|
||||
要求:确定真实文档页的接入入口,后续 bug 统一在主页面暴露和验证。
|
||||
|
||||
- [ ] 明确 runtime shell 退场策略。
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs`
|
||||
原则:该路线只保留为 debug/prototype,不再作为正式文档页 benchmark。
|
||||
|
||||
- [ ] 将当前 `leptos-tiptap` 编辑器嵌入真实页面壳。
|
||||
要求:进入真实文档路由、真实布局、真实加载链,而不是单独的实验页。
|
||||
目的:尽早暴露首屏、刷新、回填、焦点、布局、侧栏联动、回归问题。
|
||||
补充口径:这里指的是把 `8123` 那套真实 `Leptos + Tiptap` 页面壳 / editor stage 接进主编辑器,而不是另做一套长得不像 `8123` 的 host。
|
||||
|
||||
- [ ] 建立正式 load/save 边界。
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/documents.rs`
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
要求:前端 runtime 输入与 Rust 真相之间有单一转换边界,不再引入 shell 专用格式。
|
||||
|
||||
- [ ] 明确 `EditorBlockDocument -> Tiptap doc -> EditorBlockDocument` 的最小映射。
|
||||
`P0.5` 至少覆盖:
|
||||
`Paragraph`、`Heading`、`BulletListItem`、`NumberedListItem`、`Todo`、`Quote`、`CodeBlock`
|
||||
目标:先保证主链常用块类型可稳定往返,不追求一次做完整能力面。
|
||||
|
||||
- [ ] 提前引入稳定 block id 策略。
|
||||
参考:官方 `UniqueID.configure(...)`
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
原则:Rust `EditorBlock.block_id` 是最终真相;`UniqueID` 只作为浏览器 runtime 辅助,不应反过来成为持久化规则。
|
||||
|
||||
- [ ] 对齐最小命令合同,而不是先做完所有体验功能。
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/command.rs`
|
||||
`P0.5` 优先对齐:
|
||||
`ReplaceBlock`
|
||||
`InsertBlockAfter`
|
||||
`DeleteBlock`
|
||||
`SplitBlock`
|
||||
`MergeWithPrevious`
|
||||
`MoveBlock`
|
||||
说明:`IndentBlock`、`OutdentBlock`、`ToggleHeadingCollapse` 保留到后续阶段,不作为 `P0.5` 阻塞项。
|
||||
|
||||
- [ ] 复用当前已有的 slash / toolbar / handle / turn into,而不是重写第二遍。
|
||||
落点:以现有 spike 行为为基础迁入主页面,再按官方行为微调。
|
||||
原则:当前阶段优先“接入”,不是再做一轮自娱自乐式 UI 重构。
|
||||
|
||||
- [ ] 定义未支持能力的降级策略。
|
||||
当前明确未进入 `P0.5`:
|
||||
图片上传、表格、页面引用、块引用、heading collapse、普通块缩进增强
|
||||
要求:未支持时要有显式降级,不允许用临时假功能污染正式合同。
|
||||
|
||||
- [ ] 建立主编辑器验收口径。
|
||||
最低验收应覆盖:
|
||||
打开文档
|
||||
编辑常用块
|
||||
slash 插入块
|
||||
turn into 切换块类型
|
||||
刷新后结构保留
|
||||
再次进入后结构保留
|
||||
目的:从现在开始,bug 判断以主编辑器为准,而不是以 spike 页面为准。
|
||||
|
||||
## 7. P1 方向
|
||||
|
||||
`P1` 只保留方向,不在这一轮继续扩成大 checklist。
|
||||
|
||||
`P1` 解决的是“成为正式主编辑器还缺的结构能力”:
|
||||
|
||||
- 页面引用 / 块引用
|
||||
- heading collapse 与树语义对齐
|
||||
- 最小图片/附件桥接
|
||||
- 更完整的 Rust save pipeline 与局部更新
|
||||
|
||||
其中优先级判断如下:
|
||||
|
||||
1. 页面引用 / 块引用
|
||||
2. heading collapse
|
||||
3. 最小图片/附件桥接
|
||||
4. 其它结构增强
|
||||
|
||||
## 8. P1.5 方向
|
||||
|
||||
`P1.5` 才进入真正的 Wolai 操作层对标。
|
||||
|
||||
这一阶段再重新规划下面这些体验增强:
|
||||
|
||||
- 手柄行为优化
|
||||
- `turn into page`
|
||||
- 普通块缩进体验
|
||||
- 块菜单分组与文案优化
|
||||
- 更细的 hover / selection / anchor 行为
|
||||
- 更接近 Wolai 的块级交互细节
|
||||
|
||||
说明:
|
||||
|
||||
这些都值得做,但它们不应先于“成为主编辑器”发生。
|
||||
|
||||
## 9. P2 方向
|
||||
|
||||
`P2` 才处理后置增强与兼容项:
|
||||
|
||||
- 表格评估
|
||||
- 更完整的图片上传链
|
||||
- Markdown 导入导出回归
|
||||
- `blocks` 差异校验
|
||||
- `kode` 组件分层整理
|
||||
- AI 命令入口
|
||||
|
||||
## 10. 当前不进入主线前排的事项
|
||||
|
||||
下面这些项不删除,但从主 checklist 前排降级:
|
||||
|
||||
- Markdown 导入导出回归
|
||||
作用是 compatibility/testing,不直接解锁主编辑器接入。
|
||||
|
||||
- `kode` 组件分层参考
|
||||
它是实现注记,不是交付里程碑。
|
||||
|
||||
- 完整表格
|
||||
当前不是高频需求,也不是主编辑器切流前置项。
|
||||
|
||||
- 协作与 Tiptap Cloud AI
|
||||
与当前 AI-first / Rust-kernel-first 路线不一致。
|
||||
|
||||
## 11. 一句话收口
|
||||
|
||||
当前正确路线不是继续把 spike 做得更漂亮,而是:
|
||||
|
||||
**先把现有 `leptos-tiptap` 编辑器接入主编辑器,并用 Rust 接管正式保存与块真相;等这一步完成后,再系统规划 Wolai 对标体验增强。**
|
||||
@@ -0,0 +1,282 @@
|
||||
# 5 [recycle] mnote 编辑器主线重置定稿 v2
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 这份文档用于替代此前偏向“Rust-native 全自研编辑器”的主线口径。
|
||||
>
|
||||
> 本文结论只解决两件事:
|
||||
> 1. 后续编辑器主线到底以谁为基准
|
||||
> 2. 最近 git 历史里,哪些改动不该整包回滚,哪些应该局部撤回或降级
|
||||
|
||||
## 1. 结论先行
|
||||
|
||||
当前应冻结为下面这条主线:
|
||||
|
||||
> **后续编辑器的体验 benchmark 以 `Tiptap` 官方能力模型为准,Leptos 接入层以 `leptos-tiptap` 为主参考;Rust 侧继续掌握 kernel / command / projection / AI / CLI 语义主导权。**
|
||||
|
||||
同时固定三条边界:
|
||||
|
||||
- `edita-core` 不再被视为主线 benchmark,只保留为 Rust-native headless block editor 的设计参考。
|
||||
- `kode`、`blocks` 只作为局部参考层,不作为最终交付体验 benchmark。
|
||||
- `mnote-next` 只作为内部样本和反例来源,不作为“已经验证过的主参考实现”。
|
||||
|
||||
一句话收口:
|
||||
|
||||
> **我们不再把“做出一个 Rust 原生最小编辑器”当成交付目标,而是把“做出接近 Tiptap / Wolai 的实际编辑体验,同时让 Rust 继续掌握底层语义”当成交付目标。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 为什么要重置口径
|
||||
|
||||
此前路线逐渐偏成了下面这个结构:
|
||||
|
||||
- 用 `AI-first / CLI-first / Rust-native` 作为最高目标
|
||||
- 把“最小可写闭环”当成阶段验收
|
||||
- 结果自然滑向 `runtime shell + textarea + command button + debug panel`
|
||||
|
||||
这条路线的问题不在于“做错了”,而在于:
|
||||
|
||||
- 它更像内核验证路线,不像产品交付路线
|
||||
- 它会天然高估 `edita-core` 这类薄内核的完成度
|
||||
- 它会天然低估 `Tiptap` 在 selection、IME、输入规则、历史、富文本细节上的工程积累
|
||||
|
||||
从当前代码看,偏移已经非常具体:
|
||||
|
||||
- 默认文档页已经能被切到 `iframe` 挂载的 `mnote-web /document` 壳
|
||||
- [page.tsx](/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx)
|
||||
- [mnote-web-document-shell-host.tsx](/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/mnote-web-document-shell-host.tsx)
|
||||
- Rust `/document` 路由现在是 runtime/debug 壳,不是产品文档页
|
||||
- [editor.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs)
|
||||
- 新 smoke 脚本已经把“看到 `Document Editor Shell` + `textarea[data-block-input-id]`”当成通过标准
|
||||
- [task103-document-shell-cutover-smoke.js](/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js)
|
||||
- [task104-document-runtime-input-smoke.js](/mnt/Data1T/mnote/scripts/task104-document-runtime-input-smoke.js)
|
||||
|
||||
这说明问题不是“样式没做完”,而是默认交付口径已经被带偏。
|
||||
|
||||
---
|
||||
|
||||
## 3. 新的主线基准
|
||||
|
||||
### 3.1 主 benchmark
|
||||
|
||||
主 benchmark 固定为两层:
|
||||
|
||||
1. 体验与能力模型:`Tiptap`
|
||||
2. Leptos 接入层:`leptos-tiptap`
|
||||
|
||||
对应的设计含义是:
|
||||
|
||||
- 需要接近 `Wolai / Notion / Tiptap` 的输入手感、selection、slash、块间过渡和工具栏组织
|
||||
- 不需要为了“全 Rust”主动放弃成熟编辑器 runtime
|
||||
- Rust 的职责转移到更适合 Rust 的层:kernel truth、command contract、projection、AI/CLI、save pipeline
|
||||
|
||||
### 3.2 辅助参考层
|
||||
|
||||
这些库继续保留,但口径必须降级:
|
||||
|
||||
- `edita-core`
|
||||
- 只参考 `Editor / Block / Command` 这种 headless 组织方式
|
||||
- 不再把它视为可直接对标 `Tiptap` 体验的候选
|
||||
- `blocks`
|
||||
- 参考文档模型、导入导出、history、diff/merge
|
||||
- 不负责最终交互体验 benchmark
|
||||
- `kode`
|
||||
- 参考 Leptos 下文本/Markdown/WYSIWYG 组织方式
|
||||
- 适合借鉴局部实现,不适合作为当前最终产品 benchmark
|
||||
- `mnote-next`
|
||||
- 只保留为内部交互样本、历史问题样本
|
||||
- 不能再作为“因为我们以前做过,所以现在能直接沿着这条线走”的依据
|
||||
|
||||
### 3.3 Rust 侧的正确位置
|
||||
|
||||
Rust 仍然是主线,但不是靠“自己重写整套输入 runtime”来体现。
|
||||
|
||||
Rust 侧应继续掌握:
|
||||
|
||||
- 文档树 / 子树 / 边 / projection
|
||||
- 编辑命令合同
|
||||
- AI 命令合同
|
||||
- CLI 批处理与自动化
|
||||
- 存储、save、审计、导入导出
|
||||
|
||||
Rust 侧不应优先承担:
|
||||
|
||||
- 第一阶段的人类编辑交互 runtime
|
||||
- 复杂 selection / IME / 富文本输入细节
|
||||
- 与成熟浏览器编辑器生态重复造轮子
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前代码后的判断
|
||||
|
||||
### 4.1 不建议整包回滚的部分
|
||||
|
||||
下面这些改动虽然和编辑器路线有关,但不应整包撤销:
|
||||
|
||||
- `aa6ee384 feat: 接入 mnote web tree shell 与主页链路整理`
|
||||
- `f1c1bcf0 feat: 收口 tree-first graph 主链与前端测试修复`
|
||||
- `f9a45e89 feat: complete tree shell cutover and regression coverage`
|
||||
|
||||
原因很简单:
|
||||
|
||||
- 这三次提交的主轴是 `tree-first graph kernel`、sidebar/tree shell、projection、stream、transport
|
||||
- 它们不是“文档编辑器主线基准错误”的根因
|
||||
- 直接整包回滚会误伤已经有效的 tree shell 主链工作
|
||||
|
||||
因此:
|
||||
|
||||
> **不建议回滚最近三次已提交主线 commit。**
|
||||
|
||||
### 4.2 应局部回退或降级的部分
|
||||
|
||||
真正需要处理的是当前工作区里把 runtime 壳推上默认主链的那一层实验改动。
|
||||
|
||||
优先级最高的局部回退对象:
|
||||
|
||||
- 默认文档页中的 `mnoteWebDocumentShellEnabled` 分支
|
||||
- [page.tsx](/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx)
|
||||
- `iframe` 挂载壳本身
|
||||
- [mnote-web-document-shell-host.tsx](/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/mnote-web-document-shell-host.tsx)
|
||||
- 把 runtime debug 壳当默认验收的 smoke 脚本
|
||||
- [task103-document-shell-cutover-smoke.js](/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js)
|
||||
- [task104-document-runtime-input-smoke.js](/mnt/Data1T/mnote/scripts/task104-document-runtime-input-smoke.js)
|
||||
- [task105-document-runtime-transactions-smoke.js](/mnt/Data1T/mnote/scripts/task105-document-runtime-transactions-smoke.js)
|
||||
- [task106-document-runtime-slash-reference-smoke.js](/mnt/Data1T/mnote/scripts/task106-document-runtime-slash-reference-smoke.js)
|
||||
- [task107-document-runtime-save-smoke.js](/mnt/Data1T/mnote/scripts/task107-document-runtime-save-smoke.js)
|
||||
|
||||
这些部分的问题不是“代码质量差”,而是:
|
||||
|
||||
- 它们把 debug/prototype 壳变成了默认产品路径
|
||||
- 它们会持续把开发注意力引向 runtime shell polish,而不是真正的页面级编辑体验
|
||||
|
||||
因此:
|
||||
|
||||
> **建议优先局部撤回“默认走 runtime shell”这组未提交改动。**
|
||||
|
||||
### 4.3 可保留但必须降级定位的部分
|
||||
|
||||
下面这些可以保留,但不能继续被描述成默认主编辑器:
|
||||
|
||||
- `mnote-web` 的 `/document` 路由
|
||||
- [editor.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs)
|
||||
- 文档 meta/content/save API
|
||||
- [documents.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/documents.rs)
|
||||
- [query_support.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/query_support.rs)
|
||||
- 运行时配置里的 document shell 字段
|
||||
- [runtime-config.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/runtime-config.ts)
|
||||
|
||||
保留条件只有一个:
|
||||
|
||||
> **它们只能作为 debug / prototype / bridge API 存在,不能再主导默认文档页。**
|
||||
|
||||
### 4.4 可继续保留观察的 Rust editor core 尝试
|
||||
|
||||
当前未提交的这些底层尝试,不建议直接删除,但也不应被提升为主 benchmark:
|
||||
|
||||
- [core-protocol/src/editor/mod.rs](/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/mod.rs)
|
||||
- [mnote-editor-core/src/lib.rs](/mnt/Data1T/mnote/rust/crates/mnote-editor-core/src/lib.rs)
|
||||
|
||||
原因:
|
||||
|
||||
- 它们已经抽出了 `EditorBlockDocument / EditorCommand / CommandExecutor` 这类可复用底层合同
|
||||
- 这层更像“Rust command/model spike”
|
||||
- 它们比 runtime shell UI 更有保留价值
|
||||
|
||||
但当前也要明确:
|
||||
|
||||
- 它们还不足以支撑 `Tiptap` 级人类编辑体验
|
||||
- 它们只能服务未来的命令合同、AI pipeline、导入导出和调试转换
|
||||
- 不能再反向决定默认产品体验
|
||||
|
||||
---
|
||||
|
||||
## 5. 后续开发阶段
|
||||
|
||||
### 阶段 A:主链纠偏
|
||||
|
||||
目标:
|
||||
|
||||
- 把默认文档页从 runtime shell 退回产品页主链
|
||||
- 明确 `runtime shell = debug`
|
||||
|
||||
清单:
|
||||
|
||||
- [x] 取消默认文档页对 `mnote-web document shell iframe` 的优先切换
|
||||
- [x] 将 `/document` 路由和相关文案重命名为 debug/prototype
|
||||
- [x] 停止用 `task103-107` 这组脚本作为默认文档页主验收
|
||||
- [ ] 重新把验收标准收口到“是否接近 Wolai/Tiptap 页面体验”
|
||||
|
||||
### 阶段 B:Leptos-Tiptap 接入最小闭环
|
||||
|
||||
目标:
|
||||
|
||||
- 用 `leptos-tiptap` 验证真正的 Leptos + Tiptap 组合是否能在当前工程中跑通
|
||||
|
||||
清单:
|
||||
|
||||
- [x] 建一个最小 `leptos-tiptap` 文档页 spike,不接入复杂业务,只验证真实输入体验
|
||||
- [ ] 打通基础 schema:标题、段落、列表、todo、blockquote、code block
|
||||
- [x] 验证命令触发:slash、toggle heading、toggle list、引用插入
|
||||
- [ ] 记录 CSR/SSR、构建体积、初始化耗时、输入延迟、保存触发点
|
||||
|
||||
补充说明(2026-04-18):
|
||||
|
||||
- 已新增独立 spike 工程:`/mnt/Data1T/mnote/rust/spikes/leptos-tiptap-spike`
|
||||
- 该 spike 当前目标是先验证真实 Tiptap surface 与基础 schema/命令,不进入默认产品文档页
|
||||
- 已完成 `cargo +1.89.0 check` 与 `env -u NO_COLOR trunk build --release`
|
||||
- 已通过本地浏览器访问 `http://127.0.0.1:8123/` 验证 CSR 页面可渲染、可输入,并能触发 heading/list/blockquote/code block/slash 引用插入
|
||||
- 当前 `Todo` 仍是 HTML 占位插入,尚未拿到 `taskList/taskItem` 级真实 schema 语义,因此第二项暂不勾满
|
||||
- 已记录到的观察值:
|
||||
- CSR:可用
|
||||
- SSR:未接入
|
||||
- 构建体积:`js 45K`,`wasm 592K`
|
||||
- 首次页面就绪时间:约 `552ms`(本地 Playwright `networkidle` 口径)
|
||||
- 单字符输入到保存触发点计数变化:约 `26ms`
|
||||
|
||||
补充说明(2026-04-19):
|
||||
|
||||
- `Tiptap` 官方 `Notion-like template` 是 `React + Tiptap Editor + Tiptap UI Components + CLI` 的产品级模板,适合作为后续 UI benchmark,而不是当前 `Leptos` 主线的直接实现路径
|
||||
- 在当前阶段,最合适的接入时机是:先把 `leptos-tiptap` 的最小输入闭环、基础 schema、保存边界和 Rust kernel 映射稳定住,再单独开一轮模板对标 spike
|
||||
- 这个模板的价值主要在于对标 `slash`、floating toolbar、drag & drop、emoji、mentions、collaboration、AI、context menu 等“产品级编辑体验”,不是替代我们当前的 Rust kernel / projection 主线
|
||||
|
||||
### 阶段 C:Rust kernel 对接
|
||||
|
||||
目标:
|
||||
|
||||
- 让 `Tiptap/leptos-tiptap` 负责编辑 surface
|
||||
- 让 Rust 继续掌握 document truth
|
||||
|
||||
清单:
|
||||
|
||||
- [ ] 确定 Tiptap JSON / HTML / 自定义节点 与 Rust `EditorBlockDocument` 的映射边界
|
||||
- [ ] 统一 save pipeline,不再维护“runtime 壳专用数据格式”
|
||||
- [ ] page subtree / outline / evidence 继续由 Rust 侧提供
|
||||
- [ ] AI/CLI 改文档时,优先走 Rust command contract,再映射回编辑器展示
|
||||
|
||||
### 阶段 D:AI-first 产品收口
|
||||
|
||||
目标:
|
||||
|
||||
- 编辑器体验接近 Wolai/Tiptap
|
||||
- 系统语义主导权仍在 Rust/AI/CLI
|
||||
|
||||
清单:
|
||||
|
||||
- [ ] 人类编辑入口只保留高频块能力,不追完整 Notion
|
||||
- [ ] AI 输出优先落到块级命令,而不是直接拼 HTML
|
||||
- [ ] debug 壳只保留给事务诊断、IME 排障、save 链路回归
|
||||
- [ ] 正式文档页只保留产品级界面,不再暴露 runtime panel
|
||||
|
||||
---
|
||||
|
||||
## 6. 最终决策
|
||||
|
||||
最终固定如下:
|
||||
|
||||
1. 编辑器主 benchmark 改为 `Tiptap + leptos-tiptap`。
|
||||
2. `edita-core` 不再作为主线 benchmark,只保留为 headless 设计参考。
|
||||
3. 最近三次已提交的 `tree shell / tree-first graph` 主线 commit 不建议整包回滚。
|
||||
4. 当前工作区里“把 runtime shell 接成默认文档页”的实验改动,建议局部撤回。
|
||||
5. `mnote-editor-core` 与 `core-protocol/editor` 可以保留为底层 spike,但不得继续主导默认交付路线。
|
||||
|
||||
如果后续要继续补文档、排期或拆 checklist,都以本文为准,不再以此前偏向“Rust-native 全自研编辑器”的文档口径为准。
|
||||
@@ -0,0 +1,936 @@
|
||||
# [recycle] mnote AI-First Rust 块编辑器主基线定稿 v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-1-tree-first-graph-kernel-checklist-v2.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/blockselect.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/done/tiptap.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-model-v0.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-command-contract-v0.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/`
|
||||
> - `/mnt/Data1T/mnote-next/docs/architecture/p1-human-stable-interface.md`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本文用于在新的产品前提下,重新冻结 `mnote` 后续块编辑器开发的主基线。
|
||||
|
||||
这版定稿不再以“是否尽量接近 `BlockNote` / `Tiptap` 的完整体验”为第一判断,而是以:
|
||||
|
||||
- AI-first
|
||||
- CLI-first
|
||||
- Rust-native
|
||||
- 轻量块编辑
|
||||
- 低复杂度、低维护成本
|
||||
|
||||
作为最高优先级。
|
||||
|
||||
本文回答四件事:
|
||||
|
||||
1. 后续块编辑器主线应该以谁为基准
|
||||
2. `edita`、`blocks`、`kode`、`leptos-tiptap`、`mnote-next` 各自的正确定位
|
||||
3. 在新的前提下,为何可以接受更轻量的编辑器能力面
|
||||
4. 替换现有 `BlockNote` 的方向与开发阶段
|
||||
|
||||
---
|
||||
|
||||
## 2. 新前提
|
||||
|
||||
这版定稿建立在下面四条前提上。
|
||||
|
||||
### 2.1 `mnote-next` 只能提供内部交互样本,不能当主基准
|
||||
|
||||
`mnote-next` 中确实出现过一批值得回看的交互尝试,包括:
|
||||
|
||||
- 拆块
|
||||
- 合并块
|
||||
- 插入块
|
||||
- 删除块
|
||||
- 有限缩进
|
||||
- 标题折叠
|
||||
- 引用 token
|
||||
- 基础块菜单与 slash 命令
|
||||
|
||||
这意味着:
|
||||
|
||||
> **我们不是从零开始想象块编辑器,但也不能把 `mnote-next` 当成已经外部验证完成的 benchmark。它更适合作为内部样例、回归样本和反例来源。**
|
||||
|
||||
### 2.2 用户真实高频需求不是完整 Notion
|
||||
|
||||
用户长期高频需求主要是:
|
||||
|
||||
- Markdown 风格记录
|
||||
- 标题与列表
|
||||
- 折叠
|
||||
- 待办
|
||||
- 基础引用
|
||||
- 少量导入、修改、校对
|
||||
|
||||
而不是:
|
||||
|
||||
- 重度协作
|
||||
- 复杂表格
|
||||
- 大量富文本格式
|
||||
- 高级评论系统
|
||||
- 完整页面级可视化编辑生态
|
||||
|
||||
### 2.3 当前不需要协作
|
||||
|
||||
协作不是当前主线需求,因此:
|
||||
|
||||
- 不需要为协作牺牲大量复杂度
|
||||
- 不需要为了 Yjs / Hocuspocus / comment thread 去设计第一版架构
|
||||
- 不需要把“多人同时编辑一致性”当成当前编辑器路线的决定性条件
|
||||
|
||||
### 2.4 产品最终意义是 AI-first 笔记软件
|
||||
|
||||
项目最终方向固定为:
|
||||
|
||||
> **一个 AI 为底层、类似轻量化 Obsidian + CLI 的笔记软件;AI 是主要编辑与输出主体,人类主要负责导入、校对和局部修改。**
|
||||
|
||||
这意味着:
|
||||
|
||||
- 编辑器首先服务 AI 命令和结构化输出
|
||||
- 人类编辑只是辅助链路
|
||||
- 命令统一比富文本完整性更重要
|
||||
- 结构可编排、可脚本化、可审计,比可视化 polish 更重要
|
||||
|
||||
---
|
||||
|
||||
## 3. 最终定稿结论
|
||||
|
||||
最终口径固定如下:
|
||||
|
||||
> **后续编辑器开发以 AI-first、CLI-first、Rust-native 的轻量块编辑器为主线;以 `edita-core + blocks + kode` 作为主基准组合;以 `leptos-tiptap` 作为 Leptos fallback 接入参考;`mnote-next` 只保留为内部交互样本,不作为主 benchmark。**
|
||||
|
||||
进一步展开就是:
|
||||
|
||||
- **事实源基线**:仍然只有 Rust kernel 的 `node / edge / subtree / projection / command`
|
||||
- **编辑器主线基线**:Rust-native minimal block editor
|
||||
- **Rust 编辑器参考**:`edita-core` 的 headless block editor / command 思路
|
||||
- **块模型与转换参考**:`blocks` 的文档模型、JSON/Markdown/HTML 转换、history / diff / merge 思路
|
||||
- **Leptos 编辑输入参考**:`kode` 的 `kode-core / kode-leptos / kode-doc` 分层与 Markdown/WYSIWYG/tree editor 构件
|
||||
- **Leptos fallback 参考**:`leptos-tiptap` 的组件、hook、runtime bridge 与 demo
|
||||
- **第一阶段模型冻结**:以 `rust-block-editor-model-v0.md` 中的 `EditorDocument / EditorBlockNode / BlockProps / content_node` 口径为准
|
||||
- **第一阶段命令冻结**:以 `rust-block-editor-command-contract-v0.md` 中的 `replace_block / insert_block_after / delete_block / split_block / merge_with_previous / move_block / indent_block / outdent_block / toggle_heading_collapse / attach_reference_token / detach_reference_token` 为准
|
||||
- **内部样本参考**:`mnote-next` 只作为曾尝试过的拆块、合并、缩进、折叠、引用交互样本,不作为采用优先级
|
||||
|
||||
一句话总结:
|
||||
|
||||
> **主线不再是“如何更好地使用 `Tiptap`”,而是“如何基于现有外部 Rust / Leptos 参考层拼出一个由 Rust command 驱动、以 AI 为主要操作者的最小块编辑器”。**
|
||||
|
||||
再收口一句:
|
||||
|
||||
> **这不是“从零全写编辑器”的路线,而是“先采用 `edita-core / blocks / kode / leptos-tiptap` 已有能力,再只为 `mnote` 的 kernel 接缝和特有语义补胶水”的路线。**
|
||||
|
||||
---
|
||||
|
||||
## 4. 主线选型口径
|
||||
|
||||
### 4.1 主线基准:`edita-core + blocks + kode`
|
||||
|
||||
主线基准不是某个现成成品,而是三部分外部参考组合:
|
||||
|
||||
1. `edita-core`
|
||||
- 负责提供无头 Rust 编辑器的参考方向
|
||||
- 重点参考:
|
||||
- block model
|
||||
- command execution
|
||||
- headless editor 边界
|
||||
2. `blocks`
|
||||
- 负责提供块文档模型与多格式转换参考
|
||||
- 重点参考:
|
||||
- `Document / Block / BlockType`
|
||||
- JSON / Markdown / HTML / Plain Text 双向转换
|
||||
- history / undo redo 思路
|
||||
- diff / merge / pipeline / sanitizer
|
||||
3. `kode`
|
||||
- 负责提供 Leptos 侧编辑输入和文档树构件参考
|
||||
- 重点参考:
|
||||
- `kode-core` 的 text buffer / selection / editing primitives
|
||||
- `kode-leptos` 的 Leptos 组件组织
|
||||
- `MarkdownEditorComponent`
|
||||
- `kode-doc` 的 structured document model
|
||||
|
||||
补充两条非主基准但仍值得保留的参考:
|
||||
|
||||
4. `leptos-tiptap`
|
||||
- 负责提供 Leptos 下对接成熟编辑器 runtime 的 fallback 样本
|
||||
- 重点参考:
|
||||
- `TiptapEditor` 组件
|
||||
- `use_tiptap_editor` hook
|
||||
- runtime bridge
|
||||
- CSR / SSR demo
|
||||
5. `mnote-next`
|
||||
- 只作为内部交互样本和回归样例来源
|
||||
- 重点参考:
|
||||
- 曾经尝试过的拆块 / 合并 / 缩进 / 引用交互
|
||||
- 曾经暴露过的问题与不稳定边界
|
||||
- 不作为:
|
||||
- 主 benchmark
|
||||
- 外部成熟参考
|
||||
- 第一采用优先级
|
||||
|
||||
这意味着:
|
||||
|
||||
> **`edita-core` 负责回答“命令核怎么组织”,`blocks` 负责回答“块与格式转换怎么组织”,`kode` 负责回答“Leptos 输入层怎么组织”,`leptos-tiptap` 负责回答“如果 Rust-native UI 壳受阻,Leptos 怎么对接成熟 editor runtime”,`mnote-next` 只负责提供内部样例。**
|
||||
|
||||
### 4.1.1 本地参考代码位置
|
||||
|
||||
下面这些参考代码已经拉到本地,后续讨论一律优先引用本地路径。
|
||||
|
||||
- `edita`(当前本地快照:`2045c3a`)
|
||||
- 仓库入口:[reference-code/edita/README.md](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/README.md)
|
||||
- headless core trait:[reference-code/edita/edita-core/src/lib.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita-core/src/lib.rs)
|
||||
- editor 壳:[reference-code/edita/edita/src/editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/editor.rs)
|
||||
- state 组织:[reference-code/edita/edita/src/state.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/state.rs)
|
||||
- 基础 nodes:[reference-code/edita/edita/src/nodes/paragraph.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/nodes/paragraph.rs)、[reference-code/edita/edita/src/nodes/heading.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/nodes/heading.rs)、[reference-code/edita/edita/src/nodes/task_item.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/nodes/task_item.rs)
|
||||
- `blocks`(当前本地快照:`86b5e00`)
|
||||
- 仓库入口:[reference-code/blocks/README.md](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/README.md)
|
||||
- 文档模型:[reference-code/blocks/src/document.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/document.rs)
|
||||
- 块模型:[reference-code/blocks/src/block.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/block.rs)
|
||||
- 转换层:[reference-code/blocks/src/converters.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/converters.rs)
|
||||
- history:[reference-code/blocks/src/history.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/history.rs)
|
||||
- diff / merge:[reference-code/blocks/src/diff.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/diff.rs)
|
||||
- sanitizer:[reference-code/blocks/src/sanitizer.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/sanitizer.rs)
|
||||
- 示例:[reference-code/blocks/examples/json_api.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/examples/json_api.rs)、[reference-code/blocks/examples/file_ops.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/examples/file_ops.rs)
|
||||
- `kode`(当前本地快照:`96ccff4`)
|
||||
- 仓库入口:[reference-code/kode/README.md](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/README.md)
|
||||
- `kode-core` editor/buffer/selection/history:[reference-code/kode/kode-core/src/editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-core/src/editor.rs)、[reference-code/kode/kode-core/src/buffer.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-core/src/buffer.rs)、[reference-code/kode/kode-core/src/selection.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-core/src/selection.rs)、[reference-code/kode/kode-core/src/history.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-core/src/history.rs)
|
||||
- `kode-doc` 结构文档模型:[reference-code/kode/kode-doc/src/lib.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-doc/src/lib.rs)、[reference-code/kode/kode-doc/src/node.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-doc/src/node.rs)、[reference-code/kode/kode-doc/src/transform.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-doc/src/transform.rs)
|
||||
- `kode-leptos` 组件入口:[reference-code/kode/kode-leptos/src/lib.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/lib.rs)
|
||||
- Markdown/WYSIWYG 组件:[reference-code/kode/kode-leptos/src/markdown_editor_component.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/markdown_editor_component.rs)、[reference-code/kode/kode-leptos/src/wysiwyg/tree_editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/wysiwyg/tree_editor.rs)
|
||||
- Markdown 规则:[reference-code/kode/kode-markdown/src/markdown_editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-markdown/src/markdown_editor.rs)、[reference-code/kode/kode-markdown/src/input_rules.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-markdown/src/input_rules.rs)
|
||||
- `leptos-tiptap`(当前本地快照:`8f16e57`)
|
||||
- 仓库入口:[reference-code/leptos-tiptap/README.md](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/README.md)
|
||||
- 组件入口:[reference-code/leptos-tiptap/src/api/component.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/component.rs)
|
||||
- hook 入口:[reference-code/leptos-tiptap/src/api/use_tiptap_editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/use_tiptap_editor.rs)
|
||||
- 命令与内容 API:[reference-code/leptos-tiptap/src/api/commands.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/commands.rs)、[reference-code/leptos-tiptap/src/api/content.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/content.rs)
|
||||
- runtime bridge:[reference-code/leptos-tiptap/src/runtime/bridge.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/runtime/bridge.rs)、[reference-code/leptos-tiptap/tiptap/src/bridge_runtime.ts](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/tiptap/src/bridge_runtime.ts)
|
||||
- demo:[reference-code/leptos-tiptap/examples/demo-csr/src/main.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/examples/demo-csr/src/main.rs)、[reference-code/leptos-tiptap/examples/demo-ssr/src/app.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/examples/demo-ssr/src/app.rs)
|
||||
- `mnote-next` 只作为内部样本
|
||||
- 人层接口草案:[mnote-next/docs/architecture/p1-human-stable-interface.md](/mnt/Data1T/mnote-next/docs/architecture/p1-human-stable-interface.md)
|
||||
- 编辑器尝试:[mnote-next/apps/web/app/components/editor/BlockEditor.tsx](/mnt/Data1T/mnote-next/apps/web/app/components/editor/BlockEditor.tsx)
|
||||
- 用法约束:只用来回看交互样例与历史问题,不作为“已经验证完成”的外部 benchmark
|
||||
|
||||
### 4.2 复用优先原则
|
||||
|
||||
这条主线不是“从零全写”,而是:
|
||||
|
||||
> **优先复用参考层,只有缺口才自研胶水、集成层和最小必要功能。**
|
||||
|
||||
固定原则如下:
|
||||
|
||||
- 优先复用 `edita-core` 的 headless command / editor 组织思路
|
||||
- 优先复用 `blocks` 的文档模型、导入导出、history、diff/merge 思路
|
||||
- 优先复用 `kode-core / kode-leptos / kode-doc` 的输入、缓冲、Leptos 组件与 tree editor 构件思路
|
||||
- 优先把 `leptos-tiptap` 留作 Leptos 接入成熟 runtime 的 fallback 样本
|
||||
- `mnote-next` 只用于补充内部交互样例和回归案例,不进入“优先采用”序列
|
||||
- 只在下面情况才自研:
|
||||
- 参考库不覆盖你方场景
|
||||
- 参考库抽象不适配 `tree-first graph kernel`
|
||||
- 参考库能力过重或过轻
|
||||
- 缺少稳定 API,必须用胶水层隔离
|
||||
|
||||
### 4.2.1 参考采用矩阵
|
||||
|
||||
| 参考层 | 优先直接采用 | 借鉴后接入 | 当前不作为主线采用 |
|
||||
| --- | --- | --- | --- |
|
||||
| `edita-core` | headless editor 边界、`Editor / Block / Command` 组织方式、命令执行框架 | 导出接口、块注册方式、状态编排方式 | 直接把它当成最终产品 UI 或完整文档系统 |
|
||||
| `blocks` | `Document / Block / BlockType`、JSON/Markdown/HTML/Plain Text 转换、history、diff/merge、sanitizer 思路 | 与 `mnote` kernel 的格式映射、引用 token 扩展、最小 block props 扩展 | 自己先空白重写一套 import/export/history/diff/merge |
|
||||
| `kode` | `kode-core` 的 text buffer / selection / editing primitives、`kode-leptos` 组件组织、`MarkdownEditorComponent`、`TreeWysiwygEditor`、`kode-doc` 分层 | 块内输入器、Leptos 焦点与菜单接缝、块级编排外壳 | 一开始就自建完整文本输入系统或重型 `contenteditable` 壳 |
|
||||
| `leptos-tiptap` | Leptos 组件/hook、runtime bridge、demo、SSR/CSR 接入方式 | 某些 selection / 菜单 / 富文本细节处理思路 | 主线路线、长期语义事实源 |
|
||||
| `mnote-next` | 旧交互样例、历史回归场景、失败边界 | 从旧实现里提取测试样例与交互反例 | 主 benchmark、已外部验证结论、第一采用优先级 |
|
||||
|
||||
执行口径固定为:
|
||||
|
||||
- 先证明参考层能不能直接承担,再决定是否补胶水
|
||||
- 若 `blocks` 已能承担转换或 history,就不再另起一套
|
||||
- 若 `kode` 已能承担块内输入器,就不先写大而全 `textarea/contenteditable` 内核
|
||||
- 若 `edita-core` 已能承担命令编排,就不先发明第二套 editor command 容器
|
||||
- `mnote-next` 只能用来补样例,不得作为“因为我们以前写过,所以现在继续沿用”的理由
|
||||
### 4.3 为什么不再把 `Tiptap` 作为主线 benchmark
|
||||
|
||||
`Tiptap` 仍然是成熟、强大、值得参考的编辑器体系。
|
||||
|
||||
但在新的产品前提下,它不再是主线 benchmark,原因是:
|
||||
|
||||
- 它的能力面明显大于当前真实需求
|
||||
- 它解决了很多你当前并不需要的问题,例如复杂协作和完整富文本生态
|
||||
- 它仍然会把主线注意力拉回浏览器富文本壳,而不是 Rust command 与 AI-first 流程
|
||||
- 如果当前主要操作者是 AI 与 CLI,而不是人类重度可视化编辑,那么 `Tiptap` 的大量优势暂时用不上
|
||||
|
||||
因此这版定稿把它降级为:
|
||||
|
||||
- 体验参考
|
||||
- fallback 方案
|
||||
- 交互 benchmark
|
||||
|
||||
而不是主线目标。
|
||||
|
||||
### 4.4 为什么 `BlockNote` 更不应继续作为主线
|
||||
|
||||
在新的前提下,`BlockNote` 的问题更加明显:
|
||||
|
||||
- 它是更高一层的现成编辑器壳
|
||||
- 对当前“轻量、命令驱动、AI-first”的目标来说过重
|
||||
- 它会继续把设计重心拉回 UI 壳、兼容层和编辑器运行时
|
||||
- 它不利于继续收口 Rust command 和 CLI 统一
|
||||
|
||||
因此:
|
||||
|
||||
> **`BlockNote` 只保留为历史实现与迁移来源,不再作为长期方向。**
|
||||
|
||||
---
|
||||
|
||||
## 5. 主线架构定义
|
||||
|
||||
后续块编辑器主线架构固定为五层。
|
||||
|
||||
## 5.1 Rust kernel 事实源层
|
||||
|
||||
负责:
|
||||
|
||||
- `node`
|
||||
- `edge`
|
||||
- `subtree`
|
||||
- `projection`
|
||||
- `command`
|
||||
|
||||
这一层继续是系统唯一事实源。
|
||||
|
||||
## 5.2 Rust editor core 层
|
||||
|
||||
负责:
|
||||
|
||||
- block model
|
||||
- editor command
|
||||
- content transform
|
||||
- import / export
|
||||
- undo / redo 基础能力
|
||||
|
||||
这一层应尽量 Rust-native,并尽量无头。
|
||||
|
||||
第一阶段模型与命令边界固定为:
|
||||
|
||||
- 块模型参考 `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-model-v0.md`
|
||||
- 命令与 Markdown 边界参考 `/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-command-contract-v0.md`
|
||||
|
||||
这一层的复用优先级固定为:
|
||||
|
||||
- 第一优先:`edita-core`
|
||||
- 第二优先:`blocks`
|
||||
- 第三优先:在这两者之间补你方胶水与集成层
|
||||
|
||||
## 5.3 Leptos UI 壳层
|
||||
|
||||
负责:
|
||||
|
||||
- 块列表展示
|
||||
- 文本输入
|
||||
- 块菜单
|
||||
- slash 菜单
|
||||
- 拖拽或等价移动
|
||||
- 焦点、选中、折叠等 UI 状态
|
||||
|
||||
这一层不再承担事实源角色。
|
||||
|
||||
这一层的复用优先级固定为:
|
||||
|
||||
- 第一优先:`kode-leptos` / `kode-core` / `kode-doc`
|
||||
- 第二优先:`leptos-tiptap` 的 Leptos bridge / runtime 接法
|
||||
- 第三优先:只在块级编排和你方特有语义处自研
|
||||
|
||||
## 5.4 CLI / AI command 层
|
||||
|
||||
负责:
|
||||
|
||||
- 直接调用统一 Rust command
|
||||
- 结构化修改文档
|
||||
- 自动导入、自动整理、自动总结、自动改写
|
||||
|
||||
长期上:
|
||||
|
||||
- CLI 与 AI 不应模拟 UI
|
||||
- CLI 与 AI 应直接操作 editor command / kernel command
|
||||
|
||||
## 5.5 读态与导出层
|
||||
|
||||
负责:
|
||||
|
||||
- Markdown 输出
|
||||
- HTML 输出
|
||||
- 轻量阅读渲染
|
||||
- 结构树与目录派生
|
||||
|
||||
---
|
||||
|
||||
## 6. 这条路线会得到什么
|
||||
|
||||
如果走这条主线,会得到下面这些收益。
|
||||
|
||||
### 6.1 更接近真正的全 Rust 架构
|
||||
|
||||
可以把下面几层都收口到 Rust:
|
||||
|
||||
- kernel
|
||||
- editor core
|
||||
- command
|
||||
- import/export
|
||||
- AI bridge
|
||||
- CLI
|
||||
|
||||
浏览器端只保留必要的 WASM / UI glue。
|
||||
|
||||
### 6.2 更容易实现底层统一
|
||||
|
||||
统一的对象将不再是“前端编辑器文档”,而是:
|
||||
|
||||
- Rust block model
|
||||
- Rust command
|
||||
- Rust kernel projection
|
||||
|
||||
这样:
|
||||
|
||||
- UI 改文档,走 Rust command
|
||||
- CLI 改文档,走 Rust command
|
||||
- AI 改文档,走 Rust command
|
||||
|
||||
这比 `BlockNote` 或 `Tiptap` 更容易收口成单一语义层。
|
||||
|
||||
### 6.3 更符合 AI-first 场景
|
||||
|
||||
AI-first 场景不要求复杂富文本壳,而要求:
|
||||
|
||||
- 可预测
|
||||
- 可序列化
|
||||
- 可 patch
|
||||
- 可审计
|
||||
- 可通过命令复用
|
||||
|
||||
Rust-native minimal editor 更符合这一点。
|
||||
|
||||
### 6.4 更容易保持轻量
|
||||
|
||||
只要控制能力面,就可以故意不做:
|
||||
|
||||
- 协作
|
||||
- 评论
|
||||
- 高级表格
|
||||
- 大量 WYSIWYG 富文本
|
||||
- 大型插件系统
|
||||
|
||||
从而保持更轻、更稳、更快。
|
||||
|
||||
---
|
||||
|
||||
## 7. 这条路线会牺牲什么
|
||||
|
||||
要明确承认,这条路线不是零代价。
|
||||
|
||||
### 7.1 会牺牲成熟富文本生态
|
||||
|
||||
你们会失去 `Tiptap / ProseMirror` 现成提供的大量能力:
|
||||
|
||||
- 完整富文本 mark 生态
|
||||
- 复杂 selection 行为
|
||||
- 丰富 node view 生态
|
||||
- 复杂粘贴与 HTML 解析
|
||||
- 大量成熟 extension
|
||||
|
||||
### 7.2 会牺牲“快速接近 Notion 视觉体验”的速度
|
||||
|
||||
如果走 Rust-native minimal editor,你们能更快得到“可用”,但更慢得到:
|
||||
|
||||
- polished Notion-like 体验
|
||||
- 高级菜单
|
||||
- 丰富交互细节
|
||||
- 页面级完整富文本 polish
|
||||
|
||||
### 7.3 仍然需要自己补的,主要是胶水与 `mnote` 特有语义
|
||||
|
||||
这条路线并不等于“全部自己造”,真正需要你们补的应尽量只剩下面这些:
|
||||
|
||||
- Rust editor core 到 `tree-first graph kernel` 的映射层
|
||||
- `[[page]]` / `((block))` 与页面、块引用语义的接缝
|
||||
- `mnote` 特有 block props、projection、读态派生
|
||||
- 主文档页 feature flag、迁移兼容、回滚链路
|
||||
- AI / CLI 到统一 Rust command 的工具面
|
||||
- `mindmap`、`onlineTable`、媒体等延期块的 placeholder 接缝
|
||||
|
||||
下面这些不应默认进入“先自研再说”的名单:
|
||||
|
||||
- import / export
|
||||
- history / undo redo
|
||||
- diff / merge
|
||||
- 文本缓冲与 selection primitives
|
||||
- Markdown round-trip
|
||||
|
||||
### 7.4 需要接受第一版更“工程化”
|
||||
|
||||
第一版应更像:
|
||||
|
||||
- AI-first 块 Markdown 编辑器
|
||||
- 结构化块命令壳
|
||||
- 轻量 Obsidian + CLI
|
||||
|
||||
而不是:
|
||||
|
||||
- 完整 Notion 替代品
|
||||
|
||||
---
|
||||
|
||||
## 8. 最小功能面
|
||||
|
||||
在新的主线下,MVP 功能面应明确收窄。
|
||||
|
||||
## 8.1 第一优先级必须有
|
||||
|
||||
- 段落
|
||||
- 标题
|
||||
- 有序列表
|
||||
- 无序列表
|
||||
- 待办
|
||||
- 折叠标题 / toggle
|
||||
- 引用块
|
||||
- 代码块
|
||||
- 分割线
|
||||
- 拆块
|
||||
- 合并块
|
||||
- 插入块
|
||||
- 删除块
|
||||
- 上移下移或拖拽重排
|
||||
- 有限缩进
|
||||
- Markdown 快捷输入
|
||||
- `[[page]]`
|
||||
- `((block))` 或等价块引用
|
||||
- Markdown 导入导出
|
||||
|
||||
## 8.2 第二优先级可以后补
|
||||
|
||||
- 媒体块
|
||||
- 进度块
|
||||
- 目录派生
|
||||
- 基础只读渲染优化
|
||||
- 基础批量选择
|
||||
- 多块复制粘贴
|
||||
|
||||
## 8.3 明确不进入第一阶段
|
||||
|
||||
- 协作
|
||||
- 评论
|
||||
- 高级表格
|
||||
- mindmap 深度编辑
|
||||
- OnlyOffice 深度接入
|
||||
- 富文本高级 mark 体系
|
||||
- 完整 AI suggestion UI
|
||||
|
||||
---
|
||||
|
||||
## 9. 替换 `BlockNote` 的方向
|
||||
|
||||
替换方向不再是“平移到 `Tiptap`”,而是下面三步。
|
||||
|
||||
### 9.1 先抽离语义,不先抽离视觉
|
||||
|
||||
先把 `BlockNote` 里仍然有价值的东西抽成:
|
||||
|
||||
- 块类型
|
||||
- 动作合同
|
||||
- token 引用语义
|
||||
- 读态派生规则
|
||||
|
||||
而不是先追求新 UI 和新视觉。
|
||||
|
||||
### 9.2 先对齐 Rust command,再做新编辑壳
|
||||
|
||||
应先收口出统一命令,例如:
|
||||
|
||||
- `replace_block`
|
||||
- `insert_block_after`
|
||||
- `delete_block`
|
||||
- `move_block`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
- `attach_reference_token`
|
||||
- `detach_reference_token`
|
||||
|
||||
只有命令稳定后,UI、CLI、AI 才能共用。
|
||||
|
||||
### 9.3 先做最小读写链,再做复杂块
|
||||
|
||||
优先保证:
|
||||
|
||||
- 打开文档
|
||||
- 编辑基础块
|
||||
- 保存
|
||||
- 导出 Markdown
|
||||
- AI 修改
|
||||
|
||||
之后再补:
|
||||
|
||||
- 媒体
|
||||
- progress
|
||||
- mindmap placeholder
|
||||
- 更复杂的 read view
|
||||
|
||||
---
|
||||
|
||||
## 10. 开发阶段
|
||||
|
||||
## 阶段 0:冻结需求与能力边界
|
||||
|
||||
### 目标
|
||||
|
||||
在新前提下,重新冻结能力边界。
|
||||
|
||||
### 输出
|
||||
|
||||
- 一份“必须做 / 可延后 / 不做”的能力清单
|
||||
- 一份内部交互样例摘录(来自 `mnote-next`,仅供对照)
|
||||
- 一份 `BlockNote` 仍需迁移的块类型清单
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 主线需求被限制在轻量块编辑器范围
|
||||
- 协作和完整富文本不再进入第一阶段
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已完成 `BlockNote` 自定义块盘点与三档分级,基线以 [`rust-block-editor-phase0-baseline-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-phase0-baseline-v1.md) 为准:阶段 1 固定 `heading`、基础段落/列表、等价待办、`pageReference`、`blockReference`;`media`、`progressMeter` 后补;`mindmap`、`onlineTable` 延期。
|
||||
- [x] 已完成 `BlockNote` 读写重耦合盘点,确认当前主风险仍集中在 `HocuspocusProvider + Yjs`、`buildDocumentSavePayload`、`DocumentToc`、`comments/search palette/editor bridge`,并在迁移文档中保留为观察点。
|
||||
- [x] 已完成 `mnote-next` 内部交互样例提取,见 [`rust-block-editor-interaction-samples-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-interaction-samples-v1.md);这些样例只作为回归样本,不作为“已验证结论”或主 benchmark。
|
||||
- [x] 已在本文中冻结“不进入第一阶段”的能力:协作、评论、高级表格、`mindmap` 深度编辑、`OnlyOffice` 深度接入、完整富文本 mark 体系。
|
||||
- [x] 已明确第一阶段唯一主目标是 AI-first 的轻量块 Markdown 编辑器,不再追求完整 Notion 视觉和能力逼近。
|
||||
- [x] 已冻结四个参考层的采用矩阵,并单独声明 `mnote-next` 只作为内部样本;当前执行口径是“先复用参考层,只有缺口才自研胶水”,详见 [`rust-block-editor-adoption-matrix-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-adoption-matrix-v0.md)。
|
||||
|
||||
## 阶段 1:定义 Rust block model 与 command
|
||||
|
||||
### 目标
|
||||
|
||||
先把底层语言定下来。
|
||||
|
||||
### 输出
|
||||
|
||||
- Rust block model
|
||||
- block props 结构
|
||||
- editor command 列表
|
||||
- Markdown import/export 边界
|
||||
- kernel content node 载荷边界
|
||||
|
||||
### 完成判定
|
||||
|
||||
- UI、CLI、AI 都能围绕这套命令讨论
|
||||
- 结构不再依赖 `BlockNote` JSON
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已完成 `edita-core` 适配性评估与最小 spike,明确 `Editor / Block / Command / 无头执行边界` 可直接映射为 `mnote` editor core,详见 [`rust-block-editor-edita-spike-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-edita-spike-v0.md)。
|
||||
- [x] 已完成 `blocks` 适配性评估与最小 spike,明确 Markdown round-trip、history、diff/merge 的可复用面与缺口,详见 [`rust-block-editor-blocks-spike-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-blocks-spike-v0.md)。
|
||||
- [x] 已冻结首批 Rust block type、`BlockProps`、editor command、Markdown import/export 边界与 `content_node` 载荷格式,见 [`rust-block-editor-model-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-model-v0.md) 与 [`rust-block-editor-command-contract-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-command-contract-v0.md)。
|
||||
- [x] 已明确 editor command 与 kernel command 的关系,以及 `pageReference` / `blockReference` 第一版先走 token 方案;`blocks`、`kode-doc`、`leptos-tiptap` 的定位已写入阶段 1 采用矩阵 v0。
|
||||
- [x] 当前阶段 1 的对外落点是:结构不再以 `BlockNote` JSON 为长期格式,UI / CLI / AI 均围绕同一套 Rust command 讨论。
|
||||
|
||||
## 阶段 2:实现最小 Rust editor core
|
||||
|
||||
### 目标
|
||||
|
||||
基于 `edita-core` 思路或等价自研实现最小无头编辑器核心。
|
||||
|
||||
### 范围
|
||||
|
||||
- 基础块
|
||||
- 拆块 / 合并
|
||||
- 插入 / 删除
|
||||
- 缩进 / 反缩进
|
||||
- 标题折叠
|
||||
- 简单 undo / redo
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 已经可以在纯命令层编辑文档
|
||||
- CLI 可直接调用 editor core
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已选择“`edita-core` 思路 + `blocks` 转换能力 + `mnote` 胶水”的实现路径,并在 [`rust/crates/mnote-editor-core/`](/mnt/Data1T/mnote/rust/crates/mnote-editor-core/) 落地最小 Rust editor core。
|
||||
- [x] 已实现 block document 内存模型、最小 command executor、undo/redo、Markdown import/export、只读结构树派生,并把 `replace/insert/delete/split/merge/move/indent/outdent/toggle collapse` 覆盖到纯 Rust 测试。
|
||||
- [x] 已把 `mnote-next` 抽取的人层交互样例沉淀为回归样本,见 [`rust-block-editor-interaction-samples-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-interaction-samples-v1.md);CLI 可直接调用 `markdown-roundtrip`、`session-demo`、`ai-pipeline`。
|
||||
- [x] 当前阶段 2 的客观验证已跑通:`cargo test -p mnote-editor-core`、`cargo test -p mnote-cli`。
|
||||
|
||||
## 阶段 3:接 Leptos 最小 UI 壳
|
||||
|
||||
### 目标
|
||||
|
||||
在 Leptos 中做一个够用的块编辑壳。
|
||||
|
||||
### 范围
|
||||
|
||||
- 文本输入
|
||||
- slash 菜单
|
||||
- 块菜单
|
||||
- 上移下移或拖拽
|
||||
- token 引用
|
||||
- 基础只读渲染
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 人可以完成日常 Markdown + 折叠 + 列表式编辑
|
||||
- 不再需要 `BlockNote` 才能完成基本写作
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 当前阶段 3 已先以 [`mnote-web` 文档壳](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs) 达成最小 Rust-native UI 壳目标:block list、focus、selection、hover、slash 菜单、标题折叠、有限缩进、`[[page]]` / `((block))` token 交互都已可见并有测试覆盖。
|
||||
- [x] `kode` / `kode-leptos` / `kode-doc` 的采用结论已冻结为“块内输入器与选择处理参考层”;当前基线先用 Rust shell + 最小 DOM 交互验证语义,不在第一版追求复杂 `contenteditable` 或整壳 Leptos 化。
|
||||
- [x] 主文档页已经完成双路径切换:默认由 `mnoteWebDocumentShellEnabled` + `mnoteWebDocumentShellUrl` 驱动接入新壳,`?editor=compat` 保留旧 `BlockNote` 兼容入口。
|
||||
- [x] 新 UI 壳首屏不依赖 `HocuspocusProvider`、`Yjs`、`comments`、旧 `BlockNoteView`;默认主路径只先加载文档壳 iframe,旧编辑器仅在 compat 路径进入。
|
||||
- [x] 最小浏览器回归链已经补齐并跑通,见 [`task103-document-shell-cutover-smoke.js`](/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js)。
|
||||
|
||||
### 阶段 3 纠偏说明(2026-04-18)
|
||||
|
||||
上面这些勾选只代表:
|
||||
|
||||
- `mnote-web` 的 document shell prototype 已经打通
|
||||
- 默认文档页已经能够切到这个 prototype surface
|
||||
- block list / focus / selection / slash / indent / 引用 token 这些外层交互语义已经有最小验证
|
||||
|
||||
它们**不等于**“真人可编辑 runtime 已完成”。
|
||||
|
||||
当前默认主路径仍缺下面这层真正决定“像不像 Tiptap / Wolai 块编辑器”的能力:
|
||||
|
||||
- 块内文本输入 runtime
|
||||
- 光标与选区更新
|
||||
- IME / `beforeinput` / composition 处理
|
||||
- 回车拆块、退格合并、Tab 缩进这类真实编辑事务
|
||||
- 与 Rust editor core 对接的保存链,而不是只在壳内维护局部 UI state
|
||||
|
||||
因此,后续任务必须把“document shell prototype”与“human editing runtime”明确拆开:
|
||||
|
||||
- 现有 `task-059..062` 应理解为 prototype 子阶段
|
||||
- 新增的纠偏批次应以“真人可编辑 runtime 接入”作为真正的 Phase 3 主目标
|
||||
- 在真人可编辑 runtime 没完成之前,不应把当前壳视作已经达到 Wolai/Tiptap 类块编辑器基线
|
||||
|
||||
### 阶段 3R-1:真人编辑 runtime 契约冻结(task-072)
|
||||
|
||||
真人编辑 runtime 与 prototype shell 的边界固定如下:
|
||||
|
||||
- prototype shell 只证明:
|
||||
- 默认主文档页可切到 `mnote-web` surface
|
||||
- block list / focus / hover / slash 按钮等外层 UI 可见
|
||||
- 兼容入口 `?editor=compat` 仍可保留
|
||||
- human editing runtime 必须额外证明:
|
||||
- 每个可编辑块都存在真实输入宿主,而不是只渲染标题/按钮
|
||||
- 文本输入走 `beforeinput` / `input` / composition / selection 事件面
|
||||
- Enter 拆块、Backspace 合并、Tab / Shift+Tab 缩进、块类型切换属于真实编辑事务
|
||||
- 编辑结果会进入保存链,并在刷新后回放
|
||||
- 阶段 3 后续验收一律按“真人 runtime 闭环”判定,而不是按“壳存在”判定
|
||||
|
||||
### 阶段 3R-2:真人编辑 runtime 实现路径冻结(task-073)
|
||||
|
||||
这一批实现路径固定为三层:
|
||||
|
||||
1. `tree-first graph kernel + Rust command contract`
|
||||
- 继续作为事实源与长期语义 owner
|
||||
2. `mnote-web human editing runtime`
|
||||
- 当前批次先在 `mnote-web` 文档壳内落最小真人输入 runtime
|
||||
- 真实输入宿主采用“块级输入器 + selection / beforeinput / composition / autosave”闭环
|
||||
- 结构事务优先通过 `mnote-editor-core` 命令执行,而不是继续停留在纯前端按钮改状态
|
||||
3. `save / replay / compat bridge`
|
||||
- 写回继续走 `documents.save`
|
||||
- 刷新回放与 `?editor=compat` 对照保留为最后验收面
|
||||
|
||||
参考层采用口径固定如下:
|
||||
|
||||
- `kode`
|
||||
- 仍是第一优先参考层
|
||||
- 当前主要借用其 buffer / selection / input runtime 分层思路,而不是等待整壳 Leptos 化完成后再开始可写闭环
|
||||
- `leptos-tiptap`
|
||||
- 保留为 fallback 参考
|
||||
- 只在 `mnote-web` 现批次无法尽快形成可写闭环时再启用
|
||||
- `compat`
|
||||
- 只作为回退与对照路径,不再作为默认主编辑器事实层
|
||||
|
||||
也就是说,当前批次的目标不是“继续美化 prototype shell”,而是先把:
|
||||
|
||||
- 文本输入
|
||||
- 选区/光标
|
||||
- IME / composition
|
||||
- 结构事务
|
||||
- 保存/刷新回放
|
||||
|
||||
这五条真人编辑 runtime 主链补齐,再决定下一步是否进一步把输入器向 `kode` 的更完整形态收敛。
|
||||
|
||||
## 阶段 4:AI-first 编辑链打通
|
||||
|
||||
### 目标
|
||||
|
||||
让 AI 成为第一编辑主体。
|
||||
|
||||
### 范围
|
||||
|
||||
- AI 调用 Rust command
|
||||
- AI 导入文本并结构化成块
|
||||
- AI 重写、总结、扩写、整理块结构
|
||||
- CLI 与 AI 共用同一动作合同
|
||||
|
||||
### 完成判定
|
||||
|
||||
- AI 已能稳定创建、改写、整理文档
|
||||
- 人类编辑退居辅助地位
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已冻结 AI/CLI 共用的 Rust editor command tool contract,并明确 AI/CLI 不再模拟 DOM/UI,见 [`rust-block-editor-ai-tool-contract-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-ai-tool-contract-v0.md)。
|
||||
- [x] 已打通“纯文本导入 -> AI 结构化成块 -> 按目标重写文档 -> 返回审计与变更报告”的最小链路,代码位于 [`mnote-editor-core/src/pipeline.rs`](/mnt/Data1T/mnote/rust/crates/mnote-editor-core/src/pipeline.rs) 与 [`mnote-cli/src/lib.rs`](/mnt/Data1T/mnote/rust/crates/mnote-cli/src/lib.rs)。
|
||||
- [x] CLI 与 AI 已共用同一条命令合同与输出面,当前验证已覆盖 `MeetingNotesToTodos`、`LongParagraphToTitle`、`PageReorder` 三种场景。
|
||||
- [x] 已补 AI 回归样例与显式 `ai_regression_*` 测试;协作、评论、suggestion review UI 仍保持不进入当前 AI-first 基线。
|
||||
|
||||
## 阶段 5:清理旧 `BlockNote` 壳
|
||||
|
||||
### 目标
|
||||
|
||||
将 `BlockNote` 从主文档页和主数据链中移出。
|
||||
|
||||
### 输出
|
||||
|
||||
- 旧 `BlockNote` 退场清单
|
||||
- 数据迁移或兼容策略
|
||||
- 测试与回滚方案
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 主文档页默认不再挂 `BlockNote`
|
||||
- 新 Rust-native 编辑器成为唯一主路径
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已完成 `BlockNote` 入口、依赖与内容格式迁移策略盘点,见 [`rust-block-editor-blocknote-migration-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-blocknote-migration-v1.md)。
|
||||
- [x] 主文档页默认路径已不再挂旧 `BlockNote`,而是切到 `mnote-web` 新文档壳;兼容入口仍保留 `?editor=compat` / debug 用途。
|
||||
- [x] 文档页测试基线已切到“默认新壳 + compat 回退”双路径,当前基线验证包括 `task103-document-shell-cutover-smoke.js`、`pnpm test`、`cargo test -p mnote-web`。
|
||||
- 后续收尾:`task-068` 继续清理 `@blocknote/*`、Yjs、Mantine、旧 CSS 覆盖,以及 `mindmap` / `onlineTable` / 媒体块里仅供旧编辑器使用的残留耦合。
|
||||
- 后续收尾:在 compat/debug 真正退场后,再删除 `blocknote-editor.tsx` 及其最终调用点,并完成包依赖瘦身。
|
||||
|
||||
---
|
||||
|
||||
## 11. 参考层的最终定位
|
||||
|
||||
### `edita-core`
|
||||
|
||||
定位:
|
||||
|
||||
- 主线参考
|
||||
- Rust editor core 思路参考
|
||||
- 命令与块模型参考
|
||||
|
||||
不是:
|
||||
|
||||
- 可直接完整拿来用的产品方案
|
||||
|
||||
### `blocks`
|
||||
|
||||
定位:
|
||||
|
||||
- 主线参考
|
||||
- Rust block document model 参考
|
||||
- Markdown / HTML / JSON / Plain Text 转换参考
|
||||
- history、diff / merge、sanitizer 参考
|
||||
|
||||
不是:
|
||||
|
||||
- UI 壳
|
||||
- `mnote` kernel 的直接事实源
|
||||
|
||||
### `kode`
|
||||
|
||||
定位:
|
||||
|
||||
- 主线参考
|
||||
- Leptos 编辑输入层参考
|
||||
- 块内编辑器、buffer、selection、WYSIWYG/tree editor 构件参考
|
||||
|
||||
不是:
|
||||
|
||||
- `mnote` 整个块编排系统的现成成品
|
||||
- `tree-first graph kernel` 的替代品
|
||||
|
||||
### `mnote-next`
|
||||
|
||||
定位:
|
||||
|
||||
- 内部原型与交互样例来源
|
||||
- 历史回归样例与反例来源
|
||||
|
||||
不是:
|
||||
|
||||
- 主 benchmark
|
||||
- 外部成熟参考
|
||||
- “已经验证完成”的结论来源
|
||||
|
||||
### `Tiptap`
|
||||
|
||||
定位:
|
||||
|
||||
- fallback
|
||||
- 体验 benchmark
|
||||
- 交互参考
|
||||
|
||||
不是:
|
||||
|
||||
- 当前主线 benchmark
|
||||
|
||||
### `leptos-tiptap`
|
||||
|
||||
定位:
|
||||
|
||||
- 若 Rust-native 路线推进受阻时的现实 fallback 接入层
|
||||
- 对照用接缝方案
|
||||
|
||||
不是:
|
||||
|
||||
- 当前第一优先开发基线
|
||||
|
||||
### `BlockNote`
|
||||
|
||||
定位:
|
||||
|
||||
- 历史实现来源
|
||||
- 迁移对象
|
||||
- 当前自定义块与读写耦合清点来源
|
||||
|
||||
不是:
|
||||
|
||||
- 长期主线
|
||||
- 新编辑器 benchmark
|
||||
|
||||
---
|
||||
|
||||
## 12. 最终收口
|
||||
|
||||
本文之后,`mnote` 在块编辑器路线上的统一口径固定为:
|
||||
|
||||
> **后续编辑器开发以 AI-first、CLI-first、Rust-native 的轻量块编辑器为主线;以 `edita-core + blocks + kode` 作为主基准组合;以 `leptos-tiptap` 作为 Leptos fallback 接入参考;`mnote-next` 只保留为内部交互样本,不作为主 benchmark。**
|
||||
|
||||
对应推进原则固定为:
|
||||
|
||||
- 不再以 `BlockNote` 兼容性为最高约束
|
||||
- 不再默认把完整 Notion 富文本体验当作必须目标
|
||||
- 协作默认不进第一阶段
|
||||
- 编辑器首先服务 AI 命令、CLI 和 Rust command 统一
|
||||
- 优先复用 `edita-core`、`blocks`、`kode`、`leptos-tiptap` 的参考层,只在缺口处自研胶水与主线集成
|
||||
- `mnote-next` 只作为内部样本与回归案例来源,不再作为主要参考
|
||||
- 以轻量、结构化、可脚本化、可维护为最高优先级
|
||||
|
||||
---
|
||||
|
||||
## 13. 最终采用矩阵(2026-04-18 基线)
|
||||
|
||||
| 参考层 | 当前采用结论 | 当前已落地的主线位置 | 保留为后续/兼容的部分 |
|
||||
| --- | --- | --- | --- |
|
||||
| `edita-core` | 直接采用其无头 command / editor 组织思路 | [`mnote-editor-core`](/mnt/Data1T/mnote/rust/crates/mnote-editor-core/) 的 block model、command executor、history 组织 | 暂不直接复用其完整 UI 或最终导出壳 |
|
||||
| `blocks` | 部分采用 | Markdown import/export、history / diff / merge 思路,以及阶段 1/2 的 spike 与采用矩阵结论 | 若后续需要更深的 HTML/JSON 转换,再继续补胶水 |
|
||||
| `kode` | 部分采用 | 作为块内输入器、selection、输入规则、tree editor 分层的参考层;当前先冻结为下一阶段可继续下沉的输入策略 | 还未把整体文档壳彻底替换为 `kode-leptos` 实现 |
|
||||
| `leptos-tiptap` | 仅保留 fallback 参考 | 作为 Leptos bridge / runtime 接法与 SSR/CSR 接入样例,不进入当前主路径 | 若 Rust-native 输入层受阻,可作为临时 fallback 样板 |
|
||||
| `mnote-next` | 仅保留内部样本 | 已抽取为 [`rust-block-editor-interaction-samples-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-interaction-samples-v1.md) 的回归语义样例 | 不再作为 benchmark 或“以前写过所以继续沿用”的理由 |
|
||||
| `BlockNote` | 降级为 compat/debug 与迁移来源 | 默认主文档页当前已切到 document shell prototype;`?editor=compat` 仍保留为真实可写回退链与迁移观察点 | 等真人可编辑 runtime 完成后,再执行依赖清理与最终删除主线路径 |
|
||||
@@ -0,0 +1,260 @@
|
||||
# [recycle] Rust 全栈块编辑器替代方案选型
|
||||
|
||||
你现在的栈是**纯 Rust 全栈:Axum(后端)+ Leptos(前端 WASM/SSR)**,目标是**彻底换掉 BlockNote、做轻量化块编辑器、全栈 Rust 化**,这个组合非常成熟,我直接给你**只适配该技术栈**的最优替换方案、分层选型和最终落地架构,不掺任何无关技术。
|
||||
|
||||
# [recycle] 一、核心结论(直接照抄用)
|
||||
**唯一最优方案**
|
||||
`Axum + Leptos + leptos-yrs + blocks crate + 自研轻量块组件`
|
||||
- 完全抛弃 BlockNote 等重型 JS 编辑器
|
||||
- 100% 全 Rust,前端 WASM 渲染、后端 Axum 提供接口+协作同步
|
||||
- 完美实现 Notion/飞书/思源的**块结构、嵌套、富文本、协作、本地优先**
|
||||
|
||||
# 二、前端块编辑层(Leptos 侧,替代 BlockNote)
|
||||
只选**Leptos 原生/可无缝集成的 Rust 方案**,拒绝任何重型 JS 绑定
|
||||
|
||||
## 1. 首选:自研 Leptos 轻量块编辑器(最推荐,可控+极轻)
|
||||
直接基于 Leptos 自己搭块编辑器,比 BlockNote 轻量 10 倍以上,完全贴合你的需求
|
||||
- 用 Leptos `Signal / RwSignal` 管理块列表、选中态、拖拽、折叠
|
||||
- 块类型(标题/段落/代码/引用/嵌套子块)自己定义,想加数据库/看板随时扩展
|
||||
- 纯 Rust 渲染,无 JS 运行时,和 Leptos 生命周期完全对齐
|
||||
- 支持快捷键、撤销/重做、拖拽排序,按需实现,不堆无用功能
|
||||
|
||||
## 2. 次选:leptos-editor(社区轻量富文本,快速改块编辑器)
|
||||
Leptos 生态原生轻量编辑器,纯 Rust/WASM 实现
|
||||
- 开箱即用的富文本(粗体/斜体/链接)
|
||||
- 外层包一层块容器,快速改成 Notion 风格块布局
|
||||
- 开发成本最低,适合快速出原型
|
||||
|
||||
## 3. 兜底:editable / text-editor crates
|
||||
Rust 原生底层编辑内核,无任何前端依赖,可深度封装进 Leptos 组件
|
||||
- 适合追求极致性能、底层可控的场景
|
||||
|
||||
---
|
||||
|
||||
# 三、块数据 & 协作核心(Rust 通用层)
|
||||
这部分是 Notion 类编辑器的灵魂,直接用成熟 Rust 库,不重复造轮子
|
||||
1. **blocks crate**
|
||||
块结构标准库:块类型、嵌套父子结构、JSON/Markdown 序列化、diff/merge
|
||||
完美对接 Leptos 状态管理
|
||||
2. **yrs(必用)**
|
||||
Rust 官方实现的 Yjs CRDT,**飞书/Notion/Wolai 协作底层**
|
||||
支持多人实时编辑块、无冲突合并
|
||||
3. **leptos-yrs**
|
||||
Leptos 与 yrs 官方绑定,用 Signal 直接同步 CRDT 文档
|
||||
前端改块 → 自动同步到后端 → 其他客户端实时更新
|
||||
|
||||
---
|
||||
|
||||
# 四、后端 Axum 配套方案
|
||||
Axum 只做两件事:**块数据持久化 + 协作同步**
|
||||
1. **WebSocket 实时同步(Axum + tokio-tungstenite)**
|
||||
前端 yrs 产生编辑更新 → Axum 转发 → 所有在线客户端同步
|
||||
二进制协议,比 JSON 快很多,完全替代 JS 协作层
|
||||
2. **REST 接口**
|
||||
文档/块的增删改查、权限、目录管理
|
||||
3. **存储层**
|
||||
- 服务端持久化:`PostgreSQL + sqlx`(存块结构+yrs文档快照)
|
||||
- 本地优先缓存:`redb`(Rust 嵌入式 KV,对标思源本地存储)
|
||||
- 全文搜索:`tantivy`(Rust 全文搜索引擎,实现 Notion 全局搜索)
|
||||
|
||||
---
|
||||
|
||||
# 五、两套最终落地配置(直接用)
|
||||
## 方案 A:极简单用户版(快速上线,无协作)
|
||||
**Leptos 自研块 + blocks crate + Axum REST + redb**
|
||||
- 无重型依赖
|
||||
- 纯 Rust 块编辑
|
||||
- 本地优先 + 云端备份
|
||||
- 对标:思源笔记单用户体验
|
||||
|
||||
## 方案 B:完整版(多用户协作,对标 Notion/飞书)⭐⭐⭐⭐⭐
|
||||
**Leptos + leptos-yrs + blocks + Axum(WebSocket+REST) + Postgres + tantivy**
|
||||
- 多人实时块编辑
|
||||
- 块嵌套/拖拽/富文本全覆盖
|
||||
- 全栈 Rust,无任何 JS 编辑器冗余
|
||||
- 可扩展表格/看板/数据库块
|
||||
|
||||
---
|
||||
|
||||
# 六、关键实现小示例(Leptos 块结构伪代码)
|
||||
```rust
|
||||
// 块数据模型(用 blocks crate 或自定义)
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum BlockType {
|
||||
H1, H2, Paragraph, Code, Quote, Toggle
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn BlockEditor(doc_id: String) -> impl IntoView {
|
||||
// 块列表状态
|
||||
let (blocks, set_blocks) = create_signal(vec![]);
|
||||
// 绑定 yrs 协作文档
|
||||
let yrs_doc = use_yrs_doc(doc_id);
|
||||
|
||||
view! {
|
||||
<div class="notion-block-container">
|
||||
<For
|
||||
each=blocks
|
||||
key=|b| b.id
|
||||
children=move |block| {
|
||||
view! { <BlockComponent block=block set_blocks/> }
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 七、避坑(非常重要)
|
||||
1. **坚决不用 leptos-tiptap / tiptap-rs**
|
||||
本质还是套 JS 重型编辑器,违背你全 Rust、轻量化的初衷
|
||||
2. **不要自研 CRDT**
|
||||
直接用 yrs,成本差 100 倍,稳定性天壤之别
|
||||
3. **块渲染用 Leptos 组件拆分**
|
||||
避免单组件巨量渲染,性能对标原生飞书
|
||||
4. **Axum 只做同步与存储**
|
||||
块编辑逻辑全部放在前端 WASM,后端无状态,横向扩展极简单
|
||||
|
||||
如果你需要,我可以直接给你:
|
||||
- 可运行的 `Axum + Leptos + yrs` 最小工程模板
|
||||
- 块编辑器的完整 Leptos 组件代码(含拖拽、回车拆块、删除块)
|
||||
- 块数据结构与 Axum 接口定义
|
||||
|
||||
# 全 Rust 替代 BlockNote 方案 & 针对性推荐
|
||||
你现在的核心诉求非常明确:**自研类 Notion/飞书/思源笔记**,嫌弃 BlockNote 前端太重、JS 依赖重,想要**全 Rust 栈、轻量化、块编辑核心可控**。
|
||||
|
||||
我直接按「**落地优先级**」给你方案,所有方案均彻底摆脱 BlockNote 这类重型 JS 编辑器,核心逻辑/UI 尽可能全 Rust,适配 Tauri 桌面端(你做笔记软件几乎必用)。
|
||||
|
||||
---
|
||||
|
||||
## 核心结论先给
|
||||
**最优解:Tauri + egui(Rust 原生 UI)+ yrs + blocks crate**
|
||||
100% 全 Rust,零重型 JS 依赖,块编辑性能、体积、定制性拉满,完美对标 Notion/飞书块结构。
|
||||
|
||||
---
|
||||
|
||||
# 一、可直接替换 BlockNote 的 Rust 块编辑方案(按推荐度排序)
|
||||
## 1. 首选:egui 生态 + 自研块系统(桌面端 Tauri 天花板)
|
||||
**完全抛弃 JS 编辑器,纯 Rust 原生 UI 做块渲染,最轻量、最可控**
|
||||
- 核心 UI:**egui**(Rust 即时模式 UI,跨桌面/Web,无任何 JS runtime)
|
||||
- 块编辑组件:
|
||||
- `egui-notes` / `egui-notion`:现成类 Notion 块编辑,支持嵌套块、标题/列表/代码块、拖拽、撤销重做
|
||||
- `egui-rich-text` + 自定义块封装:灵活度拉满,可自定义数据库块、嵌入块、看板等思源/飞书特色块
|
||||
- 适配:Tauri 桌面、纯原生、Wasm Web 三端通用
|
||||
- 优势:**零 JS 臃肿、编译体积极小、性能碾压 BlockNote、可深度魔改**
|
||||
- 劣势:需自己封装少量交互(快捷键、块拖拽),但工作量远小于适配 BlockNote
|
||||
|
||||
## 2. 次选:Rust Wasm 前端框架 + 极简块编辑(Web/跨端)
|
||||
适合你需要 Web + 桌面双端,全 Rust Wasm 编译,无重型前端框架
|
||||
- Leptos + `leptos-editor`(Rust 顶流 Web 框架,Wasm 原生)
|
||||
- Yew + `yew-editor`(类 React Rust 前端,块编辑组件)
|
||||
- Dioxus + `dioxus-rich-text`(语法友好,块扩展简单)
|
||||
- 优势:全 Rust 类型安全,Web/桌面通吃,比 BlockNote 轻 90%
|
||||
- 劣势:Wasm 体积略大于纯 egui 原生
|
||||
|
||||
## 3. 兜底:现成全 Rust 笔记项目(直接抄架构,少造轮子)
|
||||
直接剥离块编辑核心,用到你的项目里,最快落地
|
||||
- **lume-note**:Rust + Tauri + egui,轻量块笔记,Tauri 适配完美
|
||||
- **neon-notes**:纯 egui 块笔记,代码极简,二次开发成本极低
|
||||
- **AppFlowy Core**:剥离其 Rust 块引擎/协作/存储,前端用 egui 重写(抛弃 Flutter)
|
||||
|
||||
---
|
||||
|
||||
# 二、块编辑底层核心库(全 Rust,必用,替代 BlockNote 底层逻辑)
|
||||
不管选哪种 UI,**块数据、协作、存储** 全用 Rust 库,彻底摆脱 JS 状态管理:
|
||||
1. **yrs**(⭐最高,Yjs Rust 实现)
|
||||
块级 CRDT 协作,Notion/飞书多人协作核心,Rust 原生,性能远超 JS 版
|
||||
2. **blocks crate**
|
||||
Rust 原生块模型引擎:块嵌套、序列化(Markdown/JSON)、diff/merge、撤销重做
|
||||
3. **redb/sled**
|
||||
Rust 嵌入式数据库,本地存储块数据,本地优先架构
|
||||
4. **tantivy**
|
||||
Rust 全文搜索,实现思源/Notion 级全局搜索
|
||||
|
||||
---
|
||||
|
||||
# 三、最终落地推荐组合(2 套直接用)
|
||||
## 组合 1:【极致全 Rust 桌面端】⭐⭐⭐⭐⭐(最适合你)
|
||||
**Tauri + egui + yrs + blocks crate**
|
||||
- 前端渲染:egui(纯 Rust,零 JS,块编辑 UI)
|
||||
- 块核心:blocks crate(块结构、嵌套、序列化)
|
||||
- 协作同步:yrs(Rust CRDT,块级协作)
|
||||
- 本地存储:redb
|
||||
- 打包:Tauri
|
||||
- 适配场景:自研桌面笔记,对标 Notion/飞书/思源,全 Rust 无冗余
|
||||
|
||||
## 组合 2:【Web+桌面双端】⭐⭐⭐⭐
|
||||
**Leptos(Wasm) + yrs + Tauri**
|
||||
- 前端:Leptos(Rust 编译 Wasm)
|
||||
- 块编辑:自定义 Leptos 块组件
|
||||
- 核心:yrs + blocks
|
||||
- 适配场景:需要网页版 + 桌面版的笔记工具
|
||||
|
||||
## 组合 3:【快速上线】⭐⭐⭐
|
||||
**魔改 lume-note 架构**
|
||||
直接用其 Tauri + egui 块编辑底座,替换核心逻辑为你的需求,零从头搭建成本
|
||||
|
||||
---
|
||||
|
||||
# 四、避坑关键(针对你的重构场景)
|
||||
1. **彻底放弃 BlockNote/Tiptap 相关 JS 绑定**
|
||||
本质还是 JS 重型依赖,违背你全 Rust 化的初衷
|
||||
2. **优先用 yrs 做块协作**
|
||||
这是 Notion 类编辑器的核心,Rust 实现比 JS 更稳定高效
|
||||
3. **egui 是桌面端最优解**
|
||||
比 Wasm 前端更轻,无浏览器兼容问题,Tauri 集成无缝
|
||||
4. **块模型直接用 blocks crate**
|
||||
省去自己写块嵌套、序列化的大量重复代码
|
||||
|
||||
|
||||
以下是 **Axum + Leptos + yrs + blocks** 方案的全部核心链接(官方仓库、文档、crates.io),按模块整理,方便你直接集成:
|
||||
|
||||
---
|
||||
|
||||
## 一、Leptos 前端框架(核心)
|
||||
- **Leptos 官方仓库**:https://github.com/leptos-rs/leptos
|
||||
- **Leptos 官网**:https://leptos.dev
|
||||
- **Leptos 文档**:https://book.leptos.dev
|
||||
- **crates.io**:https://crates.io/crates/leptos
|
||||
|
||||
---
|
||||
|
||||
## 二、块编辑核心库
|
||||
### 1. yrs(CRDT 协作,必用)
|
||||
- **yrs 官方仓库(y-crdt)**:https://github.com/y-crdt/y-crdt
|
||||
- **crates.io**:https://crates.io/crates/yrs
|
||||
- **文档**:https://docs.rs/yrs/latest/yrs/
|
||||
|
||||
### 2. blocks crate(块模型)
|
||||
- **blocks 仓库**:https://github.com/brenogonzaga/blocks
|
||||
- **crates.io**:https://crates.io/crates/blocks
|
||||
|
||||
### 3. Leptos 编辑器/富文本(可选)
|
||||
- **leptos-editor**(轻量富文本):https://github.com/leptos-rs/leptos-editor
|
||||
- **awesome-leptos**(生态列表):https://github.com/leptos-rs/awesome-leptos
|
||||
|
||||
---
|
||||
|
||||
## 三、Axum 后端
|
||||
- **Axum 官方仓库**:https://github.com/tokio-rs/axum
|
||||
- **Axum 文档**:https://docs.rs/axum/latest/axum/
|
||||
|
||||
---
|
||||
|
||||
## 四、配套工具(存储/搜索/同步)
|
||||
- **redb(嵌入式存储)**:https://github.com/redb/redb
|
||||
- **tantivy(全文搜索)**:https://github.com/quickwit-oss/tantivy
|
||||
- **tokio-tungstenite(WebSocket)**:https://github.com/snapview/tokio-tungstenite
|
||||
|
||||
---
|
||||
|
||||
## 五、示例与模板(直接抄)
|
||||
- **leptos-yrs 示例**:https://github.com/leptos-rs/leptos/tree/main/examples/yrs
|
||||
- **leptos + axum 全栈模板**:https://github.com/leptos-rs/leptos-axum-starter
|
||||
- **yrs + WebSocket 同步示例**:https://github.com/y-crdt/y-crdt/tree/main/examples/websocket
|
||||
|
||||
---
|
||||
|
||||
需要我基于这些库,给你生成一个可直接运行的 **Axum + Leptos + yrs** 最小块编辑器模板吗?
|
||||
+568
@@ -0,0 +1,568 @@
|
||||
# [recycle] mnote 从 Runtime 壳回到 Wolai 目标页的纠偏清单 v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 最终交付对标基准:
|
||||
> - 本地目标截图:`/mnt/Data1T/mnote/tmp/image copy 14.png`
|
||||
> - 目标页面:`https://www.wolai.com/liaibo/ikFSSM1a4GgvmHBYFCNfVd`
|
||||
> - 当前偏移截图:`/mnt/Data1T/mnote/tmp/image copy 13.png`
|
||||
>
|
||||
> 说明:
|
||||
> - `dokobot` 当前只能稳定读到 Wolai 的 SPA 外壳标题,拿不到正文结构。
|
||||
> - 因此本文以本地目标截图 `image copy 14.png` 作为最终视觉与交互验收基准。
|
||||
|
||||
## 1. 这份文档解决什么问题
|
||||
|
||||
当前代码和阶段文档虽然把“最小可写 runtime 闭环”做通了,但实际交付物仍然是一个:
|
||||
|
||||
- `iframe` 挂载的独立文档壳
|
||||
- 带 `Document Editor Shell` 标题的调试页
|
||||
- 带 revision/save/focus/selection/hover/event log 的 runtime 面板
|
||||
- 基于 `textarea + 按钮工具条 + 块卡片` 的工程壳
|
||||
|
||||
它和目标页的差距,不是“差一点样式”,而是:
|
||||
|
||||
- 页面架构不对
|
||||
- 交互壳不对
|
||||
- 验收标准不对
|
||||
- 默认主路径不对
|
||||
|
||||
本文的目的,是把后续交付标准重新冻结为:
|
||||
|
||||
> **最终默认交付必须逼近 `image copy 14.png` 这种一体化 Wolai 页面,而不是继续优化 `image copy 13.png` 这类 runtime 调试壳。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 最终交付标准
|
||||
|
||||
最终默认交付以目标截图为准,至少必须满足下面这些显性特征。
|
||||
|
||||
### 2.1 页面结构标准
|
||||
|
||||
- 左侧是产品级导航树,而不是单独文档调试页。
|
||||
- 中间是文档页面本身,不通过 `iframe` 二次嵌套。
|
||||
- 顶部是轻量面包屑 / 导航动作,不出现 runtime 诊断信息。
|
||||
- 正文区域是沉浸式页面画布,不出现工程面板、状态卡、调试日志。
|
||||
- 右上角是产品动作区,不是编辑器内部调试控件区。
|
||||
|
||||
### 2.2 编辑体验标准
|
||||
|
||||
- 页面标题是正文的一部分,视觉上属于页面内容,而不是壳标题。
|
||||
- 首块内容直接以内联块形态出现,不是“卡片块列表 + badge + tag”。
|
||||
- 占位文案应以内联方式出现在正文里,例如“输入 `/` 选择,按 `空格` 打开 AI...”,而不是单独的控制台提示区。
|
||||
- 勾选框、标题、段落、列表在正文中应呈现为自然文档流,不应被包成工程卡片。
|
||||
- slash、引用、缩进、折叠等交互应在光标附近或块上下文中触发,不依赖页顶按钮排布。
|
||||
|
||||
### 2.3 禁止出现的元素
|
||||
|
||||
默认交付页中禁止出现:
|
||||
|
||||
- `Document Editor Shell`
|
||||
- `revision=`
|
||||
- `updatedAt=`
|
||||
- `save=`
|
||||
- `focus=`
|
||||
- `selection=`
|
||||
- `hover=`
|
||||
- `交互状态`
|
||||
- `最小状态机`
|
||||
- `最近事件`
|
||||
- `block_editor_shell.ready`
|
||||
- 页顶固定一排 `Slash 菜单 / 折叠标题 / 缩进 / [[page]] / ((block))` 按钮
|
||||
- `Paragraph / Heading / Todo` 这种脱离光标上下文的演示按钮
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前偏移的根因
|
||||
|
||||
### 3.1 默认主路径被切成了 `iframe` 文档壳
|
||||
|
||||
当前默认文档页在命中文档壳开关后,直接返回 `MnoteWebDocumentShellHost`,并在其中挂一个 `iframe`。
|
||||
|
||||
相关代码:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx:183`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/mnote-web-document-shell-host.tsx:39`
|
||||
|
||||
这一步本身就决定了它不会长成目标截图那种“一体化页面”。
|
||||
|
||||
### 3.2 当前 `/document` 路由本质是 runtime 调试页
|
||||
|
||||
当前 Rust 文档页直接输出了:
|
||||
|
||||
- `Document Editor Shell`
|
||||
- hero 说明
|
||||
- meta pills
|
||||
- status card
|
||||
- toolbar buttons
|
||||
- interaction sidebar
|
||||
- event log
|
||||
|
||||
相关代码:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:790`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:805`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:821`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:838`
|
||||
|
||||
这不是“产品 UI 还没 polish”,而是“页面职责定义错了”。
|
||||
|
||||
### 3.3 当前块渲染是工程卡片流,不是正文文档流
|
||||
|
||||
当前每个 block 是:
|
||||
|
||||
- `li.editor-row`
|
||||
- 左侧 badge
|
||||
- 中间 title/meta
|
||||
- 下方 `textarea`
|
||||
- 右侧 state tag
|
||||
|
||||
相关代码:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:1408`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:1420`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:1426`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:1437`
|
||||
|
||||
这类结构适合调试事务,不适合对标 Wolai 页面。
|
||||
|
||||
### 3.4 验收标准被收缩成“最小可写闭环”
|
||||
|
||||
当前 smoke / harness 的判断标准主要是:
|
||||
|
||||
- 文档页里出现 `iframe`
|
||||
- `iframe` 里出现 `Document Editor Shell`
|
||||
- 能找到 `textarea[data-block-input-id]`
|
||||
- 能点击页顶 slash 按钮
|
||||
- 能看到 event log 标记
|
||||
|
||||
相关脚本:
|
||||
|
||||
- `/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js:17`
|
||||
- `/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js:25`
|
||||
- `/mnt/Data1T/mnote/scripts/task104-document-runtime-input-smoke.js:29`
|
||||
- `/mnt/Data1T/mnote/scripts/task104-document-runtime-input-smoke.js:46`
|
||||
- `/mnt/Data1T/mnote/scripts/task106-document-runtime-slash-reference-smoke.js:34`
|
||||
|
||||
相关任务口径:
|
||||
|
||||
- `/mnt/Data1T/mnote/harness-tasks.json:2124`
|
||||
- `/mnt/Data1T/mnote/harness-tasks.json:2248`
|
||||
- `/mnt/Data1T/mnote/harness-progress.txt:193`
|
||||
- `/mnt/Data1T/mnote/harness-progress.txt:203`
|
||||
|
||||
所以代码确实“通过了验收”,但那个验收根本不是目标页验收。
|
||||
|
||||
### 3.5 文档策略与产品目标之间出现了中途收缩
|
||||
|
||||
当前主基线文档明确收口到:
|
||||
|
||||
- AI-first
|
||||
- CLI-first
|
||||
- Rust-native
|
||||
- 轻量块编辑
|
||||
- 最小可写闭环
|
||||
|
||||
相关文档:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md:19`
|
||||
- `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md:105`
|
||||
|
||||
这条路线适合先打通内核,但不能直接当作“产品默认完成态”。
|
||||
|
||||
---
|
||||
|
||||
## 4. 纠偏总原则
|
||||
|
||||
后续纠偏必须遵循下面四条。
|
||||
|
||||
### 4.1 默认交付优先级重排
|
||||
|
||||
优先级改为:
|
||||
|
||||
1. 默认页面必须像目标截图那样是一体化产品页
|
||||
2. 编辑交互必须内嵌在页面内容流中
|
||||
3. Rust command / save chain / projection 继续保留
|
||||
4. runtime 调试信息只能退到 debug 模式
|
||||
|
||||
不能再把“Rust runtime 闭环”放在默认页面形态之上。
|
||||
|
||||
### 4.2 `/document` 壳降级为 debug / prototype
|
||||
|
||||
`mnote-web /document` 可以保留,但只能作为:
|
||||
|
||||
- `?editor=runtime-debug`
|
||||
- `?editor=prototype`
|
||||
- 内部调试壳
|
||||
- transaction / IME / save chain 诊断页
|
||||
|
||||
它不能再是默认主编辑器。
|
||||
|
||||
### 4.3 `iframe` 方案退出默认主链
|
||||
|
||||
默认文档页必须回到页面内原生渲染:
|
||||
|
||||
- 不再通过 `iframe` 承载默认编辑器
|
||||
- 不再让页面主体验依赖另一个独立 HTML 文档
|
||||
- 不再让产品页布局与编辑器页布局分裂
|
||||
|
||||
### 4.4 调试能力后移,不再前置
|
||||
|
||||
这些能力应该保留,但只能后移到 debug:
|
||||
|
||||
- focus / selection / hover
|
||||
- event log
|
||||
- revision / updatedAt / save status
|
||||
- runtime 状态机说明
|
||||
- editor command buttons
|
||||
|
||||
---
|
||||
|
||||
## 5. 必须立即执行的纠偏项
|
||||
|
||||
下面这些项不是“优化建议”,而是必须执行的逆转动作。
|
||||
|
||||
### 5.1 默认主路径逆转
|
||||
|
||||
- [ ] 把默认文档页从 `MnoteWebDocumentShellHost` 切回页面内主渲染路径。
|
||||
- [ ] `page.tsx` 中默认逻辑不得再优先返回 `iframe` 壳。
|
||||
- [ ] `mnoteWebDocumentShellEnabled` 只能控制实验入口,不能控制默认编辑器。
|
||||
- [ ] `?editor=compat` 保留,但新增 `?editor=runtime-debug`,明确实验壳用途。
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/mnote-web-document-shell-host.tsx`
|
||||
|
||||
### 5.2 `/document` runtime 页角色重命名
|
||||
|
||||
- [ ] 把 `Document Editor Shell` 明确改名为 `Runtime Debug Shell` 或等价名称。
|
||||
- [ ] 页面文案必须承认它是 debug/prototype,而不是默认编辑器。
|
||||
- [ ] 该页默认不再通过主文档页直达。
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs`
|
||||
|
||||
### 5.3 删除默认页面中的调试区块
|
||||
|
||||
默认产品页必须移除:
|
||||
|
||||
- [ ] hero 说明区
|
||||
- [ ] meta pills
|
||||
- [ ] status card
|
||||
- [ ] interaction sidebar
|
||||
- [ ] event log
|
||||
- [ ] 页面顶部固定命令按钮
|
||||
|
||||
注意:
|
||||
|
||||
- 调试区块可以迁到 `debug panel`
|
||||
- 但不能继续留在默认编辑页 DOM 里
|
||||
|
||||
### 5.4 页面内编辑器替换卡片流
|
||||
|
||||
- [ ] 块渲染必须退出 `li + badge + meta + textarea + state-tag` 这种工程卡片结构。
|
||||
- [ ] 默认正文必须改成自然文档流:
|
||||
- 标题就是标题
|
||||
- 勾选就是勾选
|
||||
- 段落就是段落
|
||||
- 列表就是列表
|
||||
- [ ] 块级信息如 `depth=0 / editable=true / type:paragraph` 只能进入 debug,不得出现在正文。
|
||||
- [ ] 正文留白、字号、宽度、块间距必须向目标截图靠拢。
|
||||
|
||||
---
|
||||
|
||||
## 6. 分阶段纠偏清单
|
||||
|
||||
## 阶段 A:先把默认主路径拉回产品页
|
||||
|
||||
### 目标
|
||||
|
||||
先解决“默认打开文档时为什么像独立调试站点”。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 默认文档页退出 `iframe` 模式。
|
||||
- [ ] 页面回到 `DocumentShell -> DocumentContent` 一体化主链。
|
||||
- [ ] 保留 runtime 壳,但只允许通过显式 debug 参数进入。
|
||||
- [ ] 主路径不再展示 `Document Editor Shell` 文案。
|
||||
- [ ] 主路径不再依赖 `mnote-web-document-shell-host.tsx`。
|
||||
- [ ] 补回归:
|
||||
- 默认文档页 DOM 中不再存在 `iframe[title^="mnote-web-document-shell"]`
|
||||
- `editor=runtime-debug` 时才允许出现 iframe 或独立壳
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 用户打开页面时,首先看到的是产品页,而不是 runtime 站点。
|
||||
|
||||
## 阶段 B:恢复 Wolai 式页面骨架
|
||||
|
||||
### 目标
|
||||
|
||||
把“产品页壳”恢复到目标截图的感觉。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 左侧继续复用现有页面树/导航系统,不为编辑器单独造站。
|
||||
- [ ] 顶部保留产品导航:
|
||||
- 面包屑
|
||||
- 返回/前进
|
||||
- 右上角产品动作
|
||||
- [ ] 页面标题回到正文主列内部,而不是 debug 页标题。
|
||||
- [ ] 正文宽度、留白、顶部间距对齐目标截图。
|
||||
- [ ] 页面空白区域保留沉浸式阅读/编辑氛围,不塞调试信息。
|
||||
- [ ] 若现有 compat 页骨架更接近目标截图,优先复用它的产品壳,而不是继续美化 runtime 壳。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 不看功能,只看静态截图,页面也应该更像 Wolai 页面而不是内部工具页。
|
||||
|
||||
## 阶段 C:把编辑器从“卡片列表”改成“正文文档流”
|
||||
|
||||
### 目标
|
||||
|
||||
解决当前最刺眼的视觉偏移。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 去掉块左侧 badge 序号。
|
||||
- [ ] 去掉块右侧 `block:id / type:xxx` 标签。
|
||||
- [ ] 去掉每块上方 `Paragraph / depth=0 / editable=true` 元信息。
|
||||
- [ ] 标题块渲染为正文中的标题,不再单独加工程 title 行。
|
||||
- [ ] todo 块渲染为真实勾选项。
|
||||
- [ ] `textarea` 若继续保留,只能作为无边框、无卡片感的内联输入宿主。
|
||||
- [ ] 若 `textarea` 无法支撑自然正文体验,则必须把 `kode` 真正接入“块内输入器”层,而不是只停留在文档参考。
|
||||
- [ ] `[[page]]` / `((block))` 的插入入口改成光标附近上下文,不再只靠页顶按钮。
|
||||
- [ ] slash 菜单改为光标上下文菜单。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 用户看到正文时,应先感知“文档内容”,而不是“块调试单元”。
|
||||
|
||||
## 阶段 D:把交互从“页顶按钮”改成“块内上下文”
|
||||
|
||||
### 目标
|
||||
|
||||
解决当前交互方式明显不像 Wolai 的问题。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 去掉页顶固定 `Slash 菜单 / 折叠标题 / 缩进 / [[page]] / ((block))` 按钮。
|
||||
- [ ] slash 触发改为输入 `/`。
|
||||
- [ ] 页面引用改为输入 `[[`。
|
||||
- [ ] 块引用改为输入 `((` 或等价上下文入口。
|
||||
- [ ] 缩进 / 反缩进改为 `Tab / Shift+Tab` 主触发。
|
||||
- [ ] 标题折叠优先放在标题左侧 affordance,而不是页顶全局按钮。
|
||||
- [ ] 块 hover 菜单应轻量化,只在块附近出现。
|
||||
- [ ] 保留调试按钮,但移入 debug 模式或开发者工具抽屉。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 核心编辑动作不依赖页面级按钮栏。
|
||||
|
||||
## 阶段 E:调试能力彻底后移
|
||||
|
||||
### 目标
|
||||
|
||||
让默认产品页彻底脱离 runtime 调试感。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] `revision / save status / focus / selection / hover` 移到 debug 面板。
|
||||
- [ ] `event log` 移到 debug 面板。
|
||||
- [ ] `最小状态机` 文案移到 debug 面板。
|
||||
- [ ] debug 面板默认关闭,只在显式调试开关下出现。
|
||||
- [ ] 生产态 DOM 中不出现 `block_editor_shell.ready` 等字符串。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 默认页面截图不再暴露任何 runtime 术语。
|
||||
|
||||
## 阶段 F:验收标准重写
|
||||
|
||||
### 目标
|
||||
|
||||
防止再次出现“工程闭环通过了,但产品页仍然不对”的误判。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 废弃以 `iframe + Document Editor Shell + textarea` 为成功条件的默认验收。
|
||||
- [ ] 重写 `task103~107` 类 smoke 的默认成功条件。
|
||||
- [ ] 新增产品级验收断言:
|
||||
- 默认文档页没有 `iframe[title^="mnote-web-document-shell"]`
|
||||
- 页面不存在 `Document Editor Shell`
|
||||
- 页面不存在 `交互状态`
|
||||
- 页面不存在 `最近事件`
|
||||
- 页面存在产品导航树
|
||||
- 页面标题在正文主列内
|
||||
- 第一块内容以内联块形式出现
|
||||
- 占位文案直接出现在正文中
|
||||
- slash 菜单由输入 `/` 触发
|
||||
- [ ] 新增截图验收:
|
||||
- 至少保留一组默认页截图
|
||||
- 用于与目标截图做人工对照
|
||||
- [ ] Final QA 的表述从“最小可写闭环”改为“产品级默认编辑页达到目标页标准”。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 测试通过时,默认页面也必须像目标页,而不是仅仅可写。
|
||||
|
||||
---
|
||||
|
||||
## 7. 参考层在这次纠偏中的正确用法
|
||||
|
||||
这次纠偏不是要推翻 Rust 内核,而是要纠正“参考层只停留在文档里,没有真正进入主路径”的问题。
|
||||
|
||||
### `edita-core`
|
||||
|
||||
继续用于:
|
||||
|
||||
- command 容器
|
||||
- transform / transaction 思路
|
||||
|
||||
不用于:
|
||||
|
||||
- 默认产品页 UI
|
||||
|
||||
### `blocks`
|
||||
|
||||
继续用于:
|
||||
|
||||
- block model
|
||||
- import/export
|
||||
- history / diff / merge
|
||||
|
||||
不用于:
|
||||
|
||||
- 正文视觉与交互壳
|
||||
|
||||
### `kode`
|
||||
|
||||
这次必须优先重新评估它是否能真正承担:
|
||||
|
||||
- 块内输入器
|
||||
- Markdown/WYSIWYG 内联编辑
|
||||
- 光标附近上下文交互
|
||||
|
||||
如果 `textarea` 无法做出目标页的正文感,`kode` 不能再只停在“设计参考”。
|
||||
|
||||
### `leptos-tiptap`
|
||||
|
||||
继续作为 fallback:
|
||||
|
||||
- 当 `kode` 与自有块编排组合后仍无法提供足够自然的编辑体验时
|
||||
- 用来验证 Leptos 对接成熟编辑 runtime 的现实成本
|
||||
|
||||
### `mnote-next`
|
||||
|
||||
只继续作为:
|
||||
|
||||
- 交互样例
|
||||
- 历史反例
|
||||
|
||||
不再作为:
|
||||
|
||||
- 视觉或产品交付参考
|
||||
|
||||
---
|
||||
|
||||
## 8. 关于 “Tiptap 体验” 的补充判断
|
||||
|
||||
这一条必须单独说清。
|
||||
|
||||
### 8.1 仅执行本文前面的纠偏项,不足以自动获得 Tiptap 级体验
|
||||
|
||||
原因很直接:
|
||||
|
||||
- 前文主要纠正的是“默认页面壳”
|
||||
- 纠正的是 `iframe`、调试面板、卡片块流、验收标准
|
||||
- 这些动作可以让页面更像 Wolai
|
||||
- 但它们不会自动提供 `Tiptap / ProseMirror` 那种输入体验内核
|
||||
|
||||
如果底层仍然是:
|
||||
|
||||
- `textarea` 作为主要输入宿主
|
||||
- 自己拼 selection / composition / slash / token / hover 菜单
|
||||
- 自己再把这些交互拼成正文体验
|
||||
|
||||
那么最终结果最多是:
|
||||
|
||||
- “更像 Wolai 的产品壳”
|
||||
- “更自然的块 Markdown 编辑器”
|
||||
|
||||
而不是天然等于:
|
||||
|
||||
- “接近 Tiptap 的成熟输入体验”
|
||||
|
||||
### 8.2 如果最终目标明确是“类似 Tiptap 的体验”,应该把 `leptos-tiptap` 从 fallback 提升为重点实施分支
|
||||
|
||||
当下面这些诉求成立时,建议把 `leptos-tiptap` 提升为主候选,而不是继续只把它当 fallback:
|
||||
|
||||
- 你要的是接近 Wolai / Tiptap 的输入手感
|
||||
- 你重视成熟 selection / composition / inline behavior
|
||||
- 你希望 slash、placeholder、mark、node 行为更接近现成富文本编辑器
|
||||
- 你不希望长期自己维护 `textarea + 自拼交互` 这条路
|
||||
|
||||
这时更合理的路线是:
|
||||
|
||||
1. 产品页壳按本文前半部分纠偏,先回到一体化页面
|
||||
2. 人类编辑 surface 优先改成 `leptos-tiptap`
|
||||
3. Rust kernel 继续保留为事实源、命令层、AI/CLI 层
|
||||
4. 通过 adapter 把 `Tiptap JSON / command / selection event` 映射到 Rust block model / command
|
||||
|
||||
### 8.3 更准确的架构收口应该是“双层分工”,而不是二选一
|
||||
|
||||
如果采用 `leptos-tiptap`,建议分工固定为:
|
||||
|
||||
- `Tiptap / leptos-tiptap`
|
||||
- 负责人类交互体验
|
||||
- 负责 selection、composition、placeholder、toolbar/slash、inline behavior
|
||||
- Rust editor core / kernel
|
||||
- 负责事实源
|
||||
- 负责命令合同
|
||||
- 负责 AI/CLI 共用写链
|
||||
- 负责导入导出、审计、projection、持久化
|
||||
|
||||
也就是说:
|
||||
|
||||
> **对“人类编辑体验”妥协给 `Tiptap`,不等于对“系统语义主导权”妥协给 `Tiptap`。**
|
||||
|
||||
### 8.4 什么时候不该上 `leptos-tiptap`
|
||||
|
||||
只有在下面这些条件同时成立时,才继续坚持纯 Rust-native 输入壳:
|
||||
|
||||
- 你接受第一版明显不像 Tiptap,只求最小可写
|
||||
- 你接受更长时间去补 selection / IME / inline interaction
|
||||
- 你把 Rust-native 纯度放在“像 Wolai/Tiptap 的体验”之前
|
||||
|
||||
而你当前这一轮明确表达的是:
|
||||
|
||||
> **你真正想要的是类似 Tiptap 的体验。**
|
||||
|
||||
在这个前提下,继续默认押注 `textarea + 自拼交互`,风险会非常高。
|
||||
|
||||
### 8.5 本文后的新增执行要求
|
||||
|
||||
基于上面的判断,后续纠偏不应只做“页面壳纠偏”,还应新增一个并行分支:
|
||||
|
||||
- [ ] 新增 `Tiptap 体验纠偏` 分支文档
|
||||
- [ ] 用 `leptos-tiptap` 做一个真正的页面内 spike,而不是停留在 reference-code
|
||||
- [ ] 对比三条路线:
|
||||
- `textarea + 自拼交互`
|
||||
- `kode` 块内输入器
|
||||
- `leptos-tiptap`
|
||||
- [ ] 以“接近目标页体验”而不是“谁更 Rust-native”作为第一判断标准
|
||||
- [ ] 若 `leptos-tiptap` 的 spike 明显更接近目标页,应把它提升为默认人类编辑 surface 方案
|
||||
|
||||
---
|
||||
|
||||
## 9. 最终一句话收口
|
||||
|
||||
后续默认交付口径固定为:
|
||||
|
||||
> **Rust editor core 可以继续最小化,但默认页面不能再像 runtime 调试站点;默认页面必须回到 Wolai 式一体化产品页,runtime 壳只保留为 debug/prototype。**
|
||||
|
||||
如果进一步收紧到“体验也要接近 Tiptap/Wolai”,那么还要再补一句:
|
||||
|
||||
> **页面壳纠偏只能解决“像不像产品页”,不能单独解决“像不像 Tiptap”;若目标是 Tiptap 级体验,应认真把 `leptos-tiptap` 提升为主候选,而不是继续只放在 fallback。**
|
||||
@@ -0,0 +1,129 @@
|
||||
# [recycle] BlockNote Migration Plan v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
|
||||
## 1. 目标
|
||||
|
||||
这份文档用于冻结从 BlockNote 主路径迁移到 Rust block editor 的最小迁移顺序。
|
||||
|
||||
目标不是一次性删除所有旧代码,而是:
|
||||
|
||||
- 先让主文档页默认切到新 Rust editor 壳
|
||||
- 再把 BlockNote 降到 compat/debug
|
||||
- 最后清理旧依赖和主线耦合
|
||||
|
||||
## 2. 当前盘点
|
||||
|
||||
当前主路径仍直接依赖下面这些入口或依赖:
|
||||
|
||||
- `@blocknote/core`
|
||||
- `@blocknote/react`
|
||||
- `@blocknote/mantine`
|
||||
- `blocknote-editor.tsx`
|
||||
- `schema.ts`
|
||||
- `HocuspocusProvider`
|
||||
- `Yjs`
|
||||
|
||||
主路径宿主文件主要是:
|
||||
|
||||
- `wolai-frontend/src/components/editor/document-content.tsx`
|
||||
- `wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
- `wolai-frontend/src/components/editor/schema.ts`
|
||||
|
||||
## 3. 迁移分层
|
||||
|
||||
迁移固定分三层:
|
||||
|
||||
- 主路径
|
||||
由 Rust block editor 接管
|
||||
- compat
|
||||
保留旧 BlockNote 入口,供临时回退
|
||||
- debug
|
||||
保留最小诊断入口,帮助排障
|
||||
|
||||
禁止继续把 BlockNote 当成默认编辑器。
|
||||
|
||||
## 4. 内容格式迁移策略
|
||||
|
||||
第一阶段不要求一次性消灭所有旧内容格式。
|
||||
|
||||
固定策略如下:
|
||||
|
||||
- 主写链以 Rust editor command 和 Rust document model 为准
|
||||
- 旧 BlockNote JSON 通过兼容 codec 读取
|
||||
- 写回时优先生成 Rust 侧统一结构
|
||||
- 必要时保留从旧格式到新格式的单向迁移适配
|
||||
|
||||
重点对象:
|
||||
|
||||
- 段落
|
||||
- 标题
|
||||
- list/todo
|
||||
- `pageReference`
|
||||
- `blockReference`
|
||||
- 附件和重型自定义块
|
||||
|
||||
## 5. 入口切换顺序
|
||||
|
||||
第一步:
|
||||
|
||||
- 在文档页入口切到 `mnoteWebDocumentShellEnabled`
|
||||
- 默认走 `mnote-web /document`
|
||||
- 保留 `?editor=compat` 回退参数
|
||||
|
||||
第二步:
|
||||
|
||||
- `document-content.tsx` 继续保留为 compat 宿主壳
|
||||
- `blocknote-editor.tsx` 继续保留为 compat 编辑器实现
|
||||
|
||||
第三步:
|
||||
|
||||
- 新主编辑器稳定后,再把主路径对 `schema.ts`、`HocuspocusProvider`、`Yjs` 的依赖收缩到 compat/debug
|
||||
|
||||
## 6. 依赖收敛
|
||||
|
||||
迁移完成前后要明确区分:
|
||||
|
||||
- 主路径是否仍依赖 `@blocknote/core`
|
||||
- 主路径是否仍依赖 `blocknote-editor.tsx`
|
||||
- 主路径是否仍依赖 `HocuspocusProvider`
|
||||
- 主路径是否仍依赖 `Yjs`
|
||||
|
||||
最终要求:
|
||||
|
||||
- compat/debug 允许暂时保留
|
||||
- 主路径不再依赖以上能力
|
||||
|
||||
## 7. 风险
|
||||
|
||||
主要风险如下:
|
||||
|
||||
- 旧页面内容与新结构双写不一致
|
||||
- history / snapshot / TOC / stats 边界退化
|
||||
- 重型块在新主路径下缺少最小宿主能力
|
||||
- 回退入口不明确导致线上排障困难
|
||||
|
||||
## 8. 回退策略
|
||||
|
||||
必须保留显式回退:
|
||||
|
||||
- 文档页参数级 compat 入口
|
||||
- 旧 BlockNote 组件继续可挂载
|
||||
- debug 场景可直接验证旧壳
|
||||
|
||||
在没有确认新主路径稳定前,不得直接删除 compat/debug。
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
满足以下条件即可视为 migration v1 达标:
|
||||
|
||||
- 主文档页默认落到新 Rust editor 壳
|
||||
- compat/debug 入口仍可独立使用
|
||||
- `blocknote-editor.tsx` 不再是默认主路径
|
||||
- `schema.ts` 不再决定默认主编辑器
|
||||
- `@blocknote/core`
|
||||
- `blocknote-editor.tsx`
|
||||
- `schema.ts`
|
||||
- `HocuspocusProvider`
|
||||
- `Yjs`
|
||||
已被明确标记为 compat/debug 或待删除对象
|
||||
@@ -0,0 +1,140 @@
|
||||
# [recycle] mnote Rust Block Editor Blocks Spike v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本 spike 聚焦 `blocks` 参考层是否适合承担:
|
||||
|
||||
- Markdown
|
||||
- round-trip
|
||||
- history
|
||||
- diff
|
||||
- merge
|
||||
|
||||
以及哪些地方存在 `缺口`。
|
||||
|
||||
## 2. 参考入口
|
||||
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/document.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/block.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/converters.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/history.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/diff.rs`
|
||||
|
||||
## 3. 适配性结论
|
||||
|
||||
### 3.1 Markdown
|
||||
|
||||
`blocks` 最适合直接借的是 Markdown 相关思路。
|
||||
|
||||
原因:
|
||||
|
||||
- 已有文档模型
|
||||
- 已有 converter facade
|
||||
- 方向上天然适合 Markdown import/export
|
||||
|
||||
这意味着第一阶段不应该自己先写一套新的 Markdown parser 胶水。
|
||||
|
||||
### 3.2 round-trip
|
||||
|
||||
`Markdown -> blocks -> Markdown round-trip` 是 `blocks` 最容易先验证的一条链。
|
||||
|
||||
对 `mnote` 的价值:
|
||||
|
||||
- 可以快速检查标题、列表、待办、引用、代码块的稳定性
|
||||
- 可以给 `task-057` 的导入导出提供直接参考
|
||||
|
||||
### 3.3 history
|
||||
|
||||
`blocks` 的 history 适合直接借思路,不建议第一阶段重写。
|
||||
|
||||
至少在 `undo / redo` 的双栈组织上,它比从零发明更稳。
|
||||
|
||||
### 3.4 diff / merge
|
||||
|
||||
`blocks` 的 diff / merge 很适合做 AI-first 和 CLI-first 后续阶段的基础参考。
|
||||
|
||||
第一阶段不要求完整采用,但必须明确后续方向,否则 AI 重写链会缺审计基础。
|
||||
|
||||
## 4. 主要缺口
|
||||
|
||||
### 4.1 文档模型仍偏扁平
|
||||
|
||||
当前 `blocks::Document` 是 `Vec<Block>` 为主。
|
||||
|
||||
而 `mnote` 要的不是简单扁平列表,而是:
|
||||
|
||||
- 块树
|
||||
- children
|
||||
- 自定义 `BlockProps`
|
||||
- 引用 token
|
||||
- 字符串 ID
|
||||
|
||||
这就是第一大 `缺口`。
|
||||
|
||||
### 4.2 BlockType 与 mnote 目标不完全一致
|
||||
|
||||
`blocks` 的 `BlockType` 更偏通用编辑器,不直接覆盖:
|
||||
|
||||
- `page_reference`
|
||||
- `block_reference`
|
||||
- `media_placeholder`
|
||||
- `progress_placeholder`
|
||||
|
||||
因此不能直接拿来当最终枚举。
|
||||
|
||||
### 4.3 默认 ID 体系不适合
|
||||
|
||||
`blocks` 用 `Uuid`。
|
||||
|
||||
但 `mnote` 当前更适合:
|
||||
|
||||
- `DocumentId = String`
|
||||
- `BlockId = String`
|
||||
|
||||
因为要跟现有页面、引用、CLI、AI 命令保持统一可读标识。
|
||||
|
||||
## 5. Spike 结论
|
||||
|
||||
`blocks` 的定位应固定为:
|
||||
|
||||
- 采用:Markdown 思路
|
||||
- 采用:round-trip 参考
|
||||
- 采用:history 思路
|
||||
- 采用:diff / merge 思路
|
||||
- 不直接采用:最终 block model
|
||||
- 不直接采用:最终 ID 体系
|
||||
|
||||
## 6. 建议落地方式
|
||||
|
||||
建议在 `mnote-editor-core` 中这样接:
|
||||
|
||||
- 自定义 `EditorDocument`
|
||||
- 自定义 `EditorBlockNode`
|
||||
- 自定义 `EditorBlockType`
|
||||
- 用 `blocks` 作为后续 `markdown/history/diff/merge` 参考或局部依赖
|
||||
|
||||
不要反过来把 `mnote` 的核心模型强行塞进 `blocks`。
|
||||
|
||||
## 7. 第一阶段保留判断
|
||||
|
||||
第一阶段可接受的保守做法:
|
||||
|
||||
- 先不把 `blocks` 作为正式依赖
|
||||
- 但在文档中冻结:后续 Markdown、history、diff、merge 优先复用其思路
|
||||
|
||||
若到 `task-057` 时验证表明 `blocks` 已足够稳定,再引正式依赖。
|
||||
|
||||
## 8. 完成判定映射
|
||||
|
||||
`task-052` 的完成判定要求本文显式包含:
|
||||
|
||||
- Markdown
|
||||
- round-trip
|
||||
- history
|
||||
- diff
|
||||
- merge
|
||||
- 缺口
|
||||
|
||||
当前文档已满足这些项。
|
||||
@@ -0,0 +1,137 @@
|
||||
# [recycle] mnote Rust Block Editor Edita Spike v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本 spike 只回答一个问题:
|
||||
|
||||
`edita-core` 的 `Editor / Block / Command / 无头执行` 边界,是否适合作为 `mnote` 新主编辑器第一阶段的命令核参考。
|
||||
|
||||
结论先写:
|
||||
|
||||
- 适合直接借 `Editor`
|
||||
- 适合直接借 `Command`
|
||||
- 适合借 `Block` 作为注册与分发思路
|
||||
- 适合借 `无头执行`
|
||||
- 不适合直接拿来当最终 `mnote` block document 模型
|
||||
- 不适合直接拿来当最终前端胶水
|
||||
|
||||
## 2. 参考入口
|
||||
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita-core/src/lib.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/editor.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/state.rs`
|
||||
|
||||
## 3. 直接可复用面
|
||||
|
||||
### 3.1 `Editor`
|
||||
|
||||
`Editor<Node, State, Input>` 的价值不在 UI,而在:
|
||||
|
||||
- 持有统一 `state`
|
||||
- 提供统一命令入口
|
||||
- 提供 block registry
|
||||
- 允许 fallback block
|
||||
|
||||
这和 `mnote` 第一阶段要的“无头编辑器容器”是对齐的。
|
||||
|
||||
对 `mnote` 的映射建议:
|
||||
|
||||
- `Node` 不直接映射前端 DOM 节点
|
||||
- `State` 映射 `EditorDocument`
|
||||
- `Input` 第一阶段不必暴露给外部 UI,可先收口成 `EditorCommandEnvelope`
|
||||
|
||||
### 3.2 `Command`
|
||||
|
||||
`Command<State>` 很适合当 `mnote-editor-core` 的最小命令执行边界。
|
||||
|
||||
建议保留这层思想,但不要照搬 trait 名:
|
||||
|
||||
- `replace_block`
|
||||
- `insert_block_after`
|
||||
- `delete_block`
|
||||
- `split_block`
|
||||
- `merge_with_previous`
|
||||
- `move_block`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
|
||||
这些命令应当最终作用在 `EditorDocument` 上,而不是作用在 Leptos 组件状态上。
|
||||
|
||||
### 3.3 `Block`
|
||||
|
||||
`Block` trait 适合借来做“块类型注册 + 输入接管”思路,但当前实现更偏 parser pipeline。
|
||||
|
||||
`mnote` 第一阶段不建议直接照抄,因为我们更需要:
|
||||
|
||||
- 树形 block document
|
||||
- 固定字符串 ID
|
||||
- `BlockProps`
|
||||
- `content_node`
|
||||
- 引用 token
|
||||
|
||||
因此这里只借“块处理器是可注册单元”的思路。
|
||||
|
||||
### 3.4 无头执行
|
||||
|
||||
这是最值得采用的点。
|
||||
|
||||
新主编辑器必须先做无头执行,因为:
|
||||
|
||||
- CLI 要共用
|
||||
- AI 要共用
|
||||
- Web UI 只是壳
|
||||
|
||||
因此 `edita-core` 的无头执行边界适合直接进入采用矩阵。
|
||||
|
||||
## 4. 不直接采用的部分
|
||||
|
||||
### 4.1 不直接采用其最终 block 形态
|
||||
|
||||
原因:
|
||||
|
||||
- `mnote` 要的是块树,不是只围绕 parser block 运转
|
||||
- `mnote` 要保留 `page_reference / block_reference / media_placeholder / progress_placeholder`
|
||||
- 还要和 kernel `content_node` 边界对齐
|
||||
|
||||
### 4.2 不直接采用其 UI/editor 外壳
|
||||
|
||||
原因:
|
||||
|
||||
- 我们的 UI 壳后面要接 Leptos / kode
|
||||
- `edita-core` 当前更适合做 headless 胶水参考
|
||||
|
||||
## 5. Spike 结论
|
||||
|
||||
`edita-core` 在 `mnote` 第一阶段的定位固定为:
|
||||
|
||||
- 采用:`Editor`
|
||||
- 采用:`Command`
|
||||
- 部分采用:`Block`
|
||||
- 采用:`无头执行`
|
||||
- 不采用:最终产品 UI
|
||||
- 不采用:最终文档模型
|
||||
|
||||
## 6. 后续胶水建议
|
||||
|
||||
后续实现时,建议在 `mnote-editor-core` 里形成下面三层:
|
||||
|
||||
1. `EditorDocument`
|
||||
2. `EditorCommand`
|
||||
3. `EditorRuntime`
|
||||
|
||||
其中:
|
||||
|
||||
- `EditorRuntime` 借 `edita-core` 的无头执行边界
|
||||
- `EditorCommand` 借 `Command`
|
||||
- `EditorDocument` 不直接复用 `edita-core` block 结构,而是自定义
|
||||
|
||||
## 7. 本结论如何进入后续任务
|
||||
|
||||
- `task-051` 完成判定:
|
||||
- 本文明确写清 `Editor / Block / Command / 无头执行 / 胶水`
|
||||
- `task-055` 开始时:
|
||||
- `mnote-editor-core` 优先实现无头编辑器容器
|
||||
- 不先实现 Leptos UI
|
||||
@@ -0,0 +1,192 @@
|
||||
# [recycle] Rust Block Editor Model v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-phase0-baseline-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita-core/src/lib.rs`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/document.rs`
|
||||
> - `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
|
||||
## 1. 目的
|
||||
|
||||
本文冻结 `task-053` 的第一阶段 Rust block editor 模型口径。
|
||||
|
||||
目标不是一次定义最终全文档系统,而是先确定:
|
||||
|
||||
- 首批 Rust block type
|
||||
- `BlockProps` 的最小字段面
|
||||
- `reference token` 的表示方式
|
||||
- `content_node` 的载荷格式
|
||||
- 哪些内容明确退出对 BlockNote JSON 的长期依赖
|
||||
|
||||
## 2. 第一阶段 block type
|
||||
|
||||
第一阶段固定以下块类型进入 canonical model:
|
||||
|
||||
- `paragraph`
|
||||
- `heading`
|
||||
- `bullet_list_item`
|
||||
- `numbered_list_item`
|
||||
- `quote`
|
||||
- `todo`
|
||||
- `code_block`
|
||||
- `page_reference`
|
||||
- `block_reference`
|
||||
|
||||
补充说明:
|
||||
|
||||
- `paragraph` 是默认文本块。
|
||||
- `heading` 是标题块,级别收敛到 `BlockProps.heading_level`。
|
||||
- `bullet_list_item` 与 `numbered_list_item` 先保留为列表项,而不是先做复杂 list container。
|
||||
- `todo` 用来承接当前 `advancedTodo` 的第一阶段简化版。
|
||||
- `page_reference` 与 `block_reference` 先作为稳定引用块进入模型。
|
||||
|
||||
下面这些不进入第一阶段 canonical model,只保留 placeholder 或 compat:
|
||||
|
||||
- `media`
|
||||
- `progressMeter`
|
||||
- `mindmap`
|
||||
- `onlineTable`
|
||||
|
||||
## 3. `BlockProps`
|
||||
|
||||
第一阶段 `BlockProps` 固定为轻量可扩展对象。
|
||||
|
||||
核心字段:
|
||||
|
||||
- `indent`
|
||||
- `heading_level`
|
||||
- `checked`
|
||||
- `collapsed`
|
||||
- `language`
|
||||
- `reference_target_id`
|
||||
- `reference_label`
|
||||
- `reference_token_strategy`
|
||||
- `extra`
|
||||
|
||||
固定口径:
|
||||
|
||||
- `indent` 表示有限缩进层级,不承诺长期树协议。
|
||||
- `heading_level` 只对 `heading` 生效。
|
||||
- `checked` 只对 `todo` 生效。
|
||||
- `collapsed` 只对 `heading` 生效,用于折叠标题。
|
||||
- `language` 只对 `code_block` 生效。
|
||||
- `reference_target_id` 与 `reference_label` 服务 `page_reference` / `block_reference`。
|
||||
- `extra` 只用于迁移期兼容字段,不能成为长期语义事实源。
|
||||
|
||||
## 4. `reference token` 策略
|
||||
|
||||
第一阶段引用 token 固定区分两类:
|
||||
|
||||
- `page_reference`
|
||||
- `block_reference`
|
||||
|
||||
第一阶段 token strategy 固定支持三种:
|
||||
|
||||
- `double_bracket`
|
||||
- `double_paren`
|
||||
- `inline_chip`
|
||||
|
||||
对应语义:
|
||||
|
||||
- `double_bracket` 对应 `[[page]]`
|
||||
- `double_paren` 对应 `((block))`
|
||||
- `inline_chip` 只用于 UI 渲染或迁移期兼容,不作为长期文本事实源
|
||||
|
||||
引用 token 最小字段:
|
||||
|
||||
- `kind`
|
||||
- `strategy`
|
||||
- `target_id`
|
||||
- `label`
|
||||
- `raw_token`
|
||||
|
||||
## 5. `content_node` 载荷格式
|
||||
|
||||
第一阶段 `content_node` 是 block 内内容的最小结构单元。
|
||||
|
||||
固定 payload 类型:
|
||||
|
||||
- `text`
|
||||
- `hard_break`
|
||||
- `reference_token`
|
||||
|
||||
### 5.1 `text`
|
||||
|
||||
`text` 节点字段:
|
||||
|
||||
- `text`
|
||||
- `marks`
|
||||
|
||||
`marks` 第一阶段只支持:
|
||||
|
||||
- `bold`
|
||||
- `italic`
|
||||
- `underline`
|
||||
- `strike`
|
||||
- `code`
|
||||
|
||||
### 5.2 `hard_break`
|
||||
|
||||
`hard_break` 用于显式换行,不引入复杂段内结构树。
|
||||
|
||||
### 5.3 `reference_token`
|
||||
|
||||
`reference_token` 节点直接挂稳定 token 对象,而不是把引用仅留在原始文本里。
|
||||
|
||||
## 6. 块对象与文档对象
|
||||
|
||||
第一阶段 canonical block 结构固定为:
|
||||
|
||||
- `block_id`
|
||||
- `block_type`
|
||||
- `props`
|
||||
- `content_nodes`
|
||||
- `child_block_ids`
|
||||
|
||||
第一阶段 canonical document 结构固定为:
|
||||
|
||||
- `document_id`
|
||||
- `root_block_ids`
|
||||
- `blocks`
|
||||
|
||||
设计原则:
|
||||
|
||||
- `blocks` 先按稳定数组序列输出,便于命令执行和导入导出。
|
||||
- `child_block_ids` 先表达父子关系,不强求复杂树索引结构。
|
||||
- `content_nodes` 替代对 BlockNote inline JSON 的长期依赖。
|
||||
|
||||
## 7. 与 BlockNote JSON 的边界
|
||||
|
||||
第一阶段固定口径:
|
||||
|
||||
- BlockNote JSON 只作为迁移期外部格式
|
||||
- Rust editor model 才是长期事实层
|
||||
- `BlockProps`、`content_node`、`reference token` 必须能独立表达核心语义
|
||||
|
||||
换句话说:
|
||||
|
||||
- 可以保留 `BlockNote JSON -> Rust model` 适配器
|
||||
- 不能继续把 BlockNote JSON 当作长期 canonical schema
|
||||
|
||||
## 8. 第一阶段不解决的事
|
||||
|
||||
下面这些明确不在 `task-053` 解决:
|
||||
|
||||
- 多人协作状态
|
||||
- 复杂 mark 树
|
||||
- 富媒体完整属性系统
|
||||
- `mindmap` / `onlineTable` 的完整内嵌编辑语义
|
||||
- 完整批量选择与多块复制粘贴
|
||||
|
||||
## 9. 结论
|
||||
|
||||
`task-053` 的固定口径是:
|
||||
|
||||
- 用首批 `paragraph`、`heading`、`bullet_list_item`、`todo`、`page_reference`、`block_reference` 等块型建立 Rust canonical model
|
||||
- 用 `BlockProps` 收拢最小块属性
|
||||
- 用 `reference token` 固定 `[[page]]` / `((block))` 的稳定表示
|
||||
- 用 `content_node` 承接块内文本与引用
|
||||
- 从这一阶段开始退出对 BlockNote JSON 的长期依赖
|
||||
@@ -0,0 +1,255 @@
|
||||
# [recycle] Rust Block Editor Phase 0 Baseline v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/`
|
||||
> - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/schema.ts`
|
||||
> - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
> - `/mnt/Data1T/mnote/wolai-frontend/src/lib/blocks/block-ops-adapter.ts`
|
||||
|
||||
## 1. 目的
|
||||
|
||||
本文用于冻结 task-048 所需的 Phase 0 基线:
|
||||
|
||||
1. 盘点当前 BlockNote 自定义块真实范围
|
||||
2. 盘点文档读写耦合点
|
||||
3. 冻结第一阶段 Rust Block Editor 的必须做 / 后补 / 延期边界
|
||||
4. 明确 `reference-code` 的采用、部分采用、不采用依据
|
||||
|
||||
Phase 0 的目标不是替换完全部现有能力,而是先把“什么必须进入第一阶段主链、什么只能先保留占位、什么明确延期”说清楚。
|
||||
|
||||
## 2. 当前 BlockNote 自定义块清单
|
||||
|
||||
当前 `customBlockSchema` 已注册的自定义块如下:
|
||||
|
||||
- `heading`
|
||||
- `advancedTodo`
|
||||
- `pageReference`
|
||||
- `blockReference`
|
||||
- `media`
|
||||
- `progressMeter`
|
||||
- `mindmap`
|
||||
- `onlineTable`
|
||||
|
||||
对应实现位置:
|
||||
|
||||
- `heading`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/schema.ts`
|
||||
- 现状:覆盖 BlockNote 默认标题块,允许 1-5 级并支持折叠。
|
||||
- `advancedTodo`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/AdvancedTodoBlock.tsx`
|
||||
- 现状:状态为 `todo/doing/done/cancelled`,点击轮转,`Alt+Click` 可直接置为 `cancelled`。
|
||||
- `pageReference`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/PageReferenceBlock.tsx`
|
||||
- 现状:块级页面引用,依赖 `pageId/title/asChildPage`,点击直接跳转文档页。
|
||||
- `blockReference`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/BlockReferenceBlock.tsx`
|
||||
- 现状:块级引用占位,运行时拉 `/api/blocks/get`,仅对段落/标题提供纯文本回写。
|
||||
- `media`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MediaBlock.tsx`
|
||||
- 现状:附件卡片,带选择资源、对齐、边框、OCR、下载、OnlyOffice 打开等强 UI 行为。
|
||||
- `progressMeter`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/ProgressBlock.tsx`
|
||||
- 现状:支持自动/手动模式;自动模式由编辑器扫描后续 `advancedTodo` 并计算百分比。
|
||||
- `mindmap`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`
|
||||
- 现状:内嵌思维导图壳,包含预览、内联编辑、全屏、独立页、自动保存、资源联动。
|
||||
- `onlineTable`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/OnlineTableBlock.tsx`
|
||||
- 现状:内嵌表格预览卡片,支持拖拽改尺寸、删除、全屏编辑。
|
||||
|
||||
## 3. 当前读写耦合点
|
||||
|
||||
### 3.1 写链耦合
|
||||
|
||||
当前编辑主链仍深度绑定 BlockNote:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
- `useCreateBlockNote(...)` 直接以 `customBlockSchema` 启动编辑器。
|
||||
- 保存经 `saveContent(...) -> /api/documents/save` 整体回写块树快照。
|
||||
- 自定义块的大量副作用都挂在 BlockNote 壳上:
|
||||
- `progressMeter` 自动统计
|
||||
- `media` 删除与恢复
|
||||
- `mindmap` 删除、自动保存、资源广播
|
||||
- `onlineTable` 删除、全屏切换
|
||||
- 协作仍挂着 `HocuspocusProvider + Y.Doc`,这与“当前不需要协作”的新主线并不一致。
|
||||
|
||||
### 3.2 读链耦合
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
- 文档页在阅读态和编辑态之间切换。
|
||||
- `initialPageSubtree` 与 `serverPageSubtreeSnapshot` 已经进入页面读态,但编辑态仍直接消费 BlockNote 内容快照。
|
||||
- Phase 0 的关键判断:
|
||||
- 阅读态已经开始朝稳定 projection 收口。
|
||||
- 编辑态仍把 BlockNote 的块树和自定义 props 当成事实层。
|
||||
|
||||
### 3.3 工具与编辑器模型耦合
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/blocks/block-ops-adapter.ts`
|
||||
- 当前 AI / server tool 适配只支持 `paragraph` 与 `heading` 两种插入规格。
|
||||
- 这说明自定义块虽然很多,但真正能被稳定工具链操作的只是一小部分。
|
||||
|
||||
### 3.4 复杂块与正文块混杂
|
||||
|
||||
当前一个 BlockNote 文档同时承载:
|
||||
|
||||
- 轻量正文块:`heading`、段落、列表、待办
|
||||
- 语义引用块:`pageReference`、`blockReference`
|
||||
- 重型宿主块:`media`、`mindmap`、`onlineTable`
|
||||
- 派生统计块:`progressMeter`
|
||||
|
||||
这会导致第一阶段编辑器如果不先切分边界,就会被复杂块宿主逻辑拖着走。
|
||||
|
||||
## 4. 第一阶段迁移范围冻结
|
||||
|
||||
### 4.1 必须做
|
||||
|
||||
下面这些能力必须进入第一阶段 Rust Block Editor 主链:
|
||||
|
||||
- `heading`
|
||||
- 必须保留 1-5 级标题与折叠语义。
|
||||
- 原因:这是阅读态结构、目录、AI 引用定位的基础。
|
||||
- `advancedTodo`
|
||||
- 必须保留最小四态语义:`todo/doing/done/cancelled`。
|
||||
- 原因:当前 `progressMeter` 依赖它,且任务型记录是高频场景。
|
||||
- `pageReference`
|
||||
- 必须保留块级引用能力,并与后续行内页面引用 token 对齐。
|
||||
- 原因:页面导航和引用挂接是高频主链。
|
||||
- `blockReference`
|
||||
- 必须保留“占位插入 + 回显 + 最小跳转”能力。
|
||||
- 原因:当前产品已存在块引用占位语义,但不要求第一阶段保留远程原位编辑。
|
||||
- `media`
|
||||
- 必须保留“附件块占位 + 标题/描述 + 打开原资源”最小能力。
|
||||
- 原因:附件是文档主链,不应在替换编辑器时丢失。
|
||||
- 最小块命令集
|
||||
- 单块编辑
|
||||
- 插入同级块
|
||||
- 删除块
|
||||
- 合并块
|
||||
- 上移下移
|
||||
- 有限缩进
|
||||
- 最小格式收口
|
||||
- JSON 块快照
|
||||
- Markdown 导入导出
|
||||
- Rust command 层稳定执行
|
||||
|
||||
### 4.2 后补
|
||||
|
||||
下面这些能力保留,但不要求在第一阶段做成完整壳:
|
||||
|
||||
- `progressMeter`
|
||||
- 后补为“派生块”或“只读统计块”。
|
||||
- 原因:其核心不是富交互编辑,而是基于 `advancedTodo` 的派生结果。
|
||||
- `mindmap`
|
||||
- 后补为“占位卡片 + 打开独立编辑器/独立页”。
|
||||
- 原因:当前实现太重,不应绑进第一阶段核心编辑循环。
|
||||
- `onlineTable`
|
||||
- 后补为“占位卡片 + 打开全屏表格编辑器”。
|
||||
- 原因:表格预览和尺寸拖拽属于宿主 UI,不是轻量块编辑核心。
|
||||
- `media`
|
||||
- 第一阶段只保留最小资源卡片。
|
||||
- OCR、OnlyOffice、复杂菜单、拖拽尺寸属于后补。
|
||||
- `blockReference`
|
||||
- 第一阶段只保留占位和跳转。
|
||||
- 远程拉块、原位编辑、富回显属于后补。
|
||||
|
||||
### 4.3 延期
|
||||
|
||||
下面这些能力明确延期,不进入第一阶段主链:
|
||||
|
||||
- Yjs / Hocuspocus 协作
|
||||
- BlockNote / ProseMirror 专属菜单状态兼容
|
||||
- `mindmap` 内联复杂快捷键与嵌入式编辑器行为
|
||||
- `onlineTable` 内嵌尺寸拖拽与高保真表格交互
|
||||
- `media` 的完整 OCR / 上传 / replace-storage 流程
|
||||
- 任何依赖 BlockNote runtime 才能成立的行为
|
||||
|
||||
## 5. Phase 0 冻结清单
|
||||
|
||||
### 5.1 必须做清单
|
||||
|
||||
- [ ] 把 `heading`、段落、列表、基础待办、`advancedTodo` 抽成 Rust editor core 的稳定块模型
|
||||
- [ ] 把 `pageReference` 与 `blockReference` 抽成稳定引用块,而不是继续仅作为 BlockNote 自定义 React block
|
||||
- [ ] 把 `media` 抽成最小附件占位块,保留资源 id / 标题 / 打开动作
|
||||
- [ ] 规定 `mindmap`、`onlineTable` 第一阶段只以宿主占位块进入文档
|
||||
- [ ] 把块命令统一成 Rust 侧可测试命令,不再把 Enter / Backspace / Tab 语义写死在 BlockNote 事件壳
|
||||
- [ ] 把保存边界从“直接保存 BlockNote 快照”改为“保存 Rust block document + 最小宿主块 props”
|
||||
|
||||
### 5.2 后补清单
|
||||
|
||||
- [ ] `progressMeter` 自动统计改成独立派生器
|
||||
- [ ] `mindmap` 预览投影和独立编辑器接缝
|
||||
- [ ] `onlineTable` 预览投影和独立编辑器接缝
|
||||
- [ ] `blockReference` 远程块拉取和最小编辑同步
|
||||
- [ ] `media` 高级工具栏、OCR、OnlyOffice 入口
|
||||
|
||||
### 5.3 延期清单
|
||||
|
||||
- [ ] 协作协议
|
||||
- [ ] BlockNote 级 UI 状态兼容层
|
||||
- [ ] 重型块的嵌入式内核迁移
|
||||
|
||||
## 6. reference-code 采用依据
|
||||
|
||||
### 6.1 `edita-core`:采用
|
||||
|
||||
采用依据:
|
||||
|
||||
- `edita-core/src/lib.rs` 已经给出最小 `Editor / Block / Command` 无头边界。
|
||||
- 这与第一阶段“先把块命令从 UI 壳里抽出来”的目标完全一致。
|
||||
- 它不绑定 DOM、ProseMirror、Tiptap,因此适合作为 Rust editor core 的命令容器参考。
|
||||
|
||||
不直接采用的部分:
|
||||
|
||||
- `edita` UI 壳和示例不是当前主链事实层。
|
||||
- 原因:`mnote` 需要的是 command / state 组织,不是直接套它的现成编辑器外壳。
|
||||
|
||||
### 6.2 `blocks`:采用
|
||||
|
||||
采用依据:
|
||||
|
||||
- `blocks/src/block.rs`、`document.rs`、`converters.rs`、`history.rs`、`diff.rs`、`sanitizer.rs` 已覆盖:
|
||||
- 块文档模型
|
||||
- Markdown / HTML / JSON 转换
|
||||
- history
|
||||
- diff / merge
|
||||
- 这正是第一阶段最缺、且不该再自研一套的基础层。
|
||||
|
||||
部分保留胶水的地方:
|
||||
|
||||
- `mnote` 的 `pageReference`、`blockReference`、`mindmap`、`onlineTable` 不是 `blocks` 原生块型,需要补最小扩展映射。
|
||||
|
||||
### 6.3 `kode`:部分采用
|
||||
|
||||
部分采用依据:
|
||||
|
||||
- `kode-core` 的 buffer / selection / editing primitives 可作为块内输入器参考。
|
||||
- `kode-leptos` 的 `MarkdownEditorComponent` 与 `TreeWysiwygEditor` 可提供 Leptos 侧输入和光标处理样板。
|
||||
- `kode-doc` 的 tree node / token position 说明它适合借鉴“结构化编辑器内部表示”。
|
||||
|
||||
不直接全量采用的原因:
|
||||
|
||||
- `kode-doc` 自带一套更接近富文本树编辑器的内部结构。
|
||||
- `mnote` 第一阶段不需要把全部文档事实改造成另一套通用 tree editor 语义。
|
||||
|
||||
### 6.4 `leptos-tiptap`:部分采用
|
||||
|
||||
部分采用依据:
|
||||
|
||||
- `src/api/component.rs`、`use_tiptap_editor.rs`、`runtime/bridge.rs` 对 Leptos 接第三方 runtime 的方式很清楚。
|
||||
- 若 Rust-native UI 壳卡住,它适合当 fallback 接缝参考。
|
||||
|
||||
不作为主线采用的原因:
|
||||
|
||||
- 它本质仍是 Tiptap runtime bridge。
|
||||
- 第一阶段目标是 Rust-native 轻量块编辑器,而不是再回到 JS 富文本 runtime 作为主事实层。
|
||||
|
||||
## 7. Phase 0 结论
|
||||
|
||||
Phase 0 的冻结口径如下:
|
||||
|
||||
- 第一阶段必须覆盖 `heading`、`advancedTodo`、`pageReference`、`blockReference`、`media` 的最小稳定语义。
|
||||
- `progressMeter`、`mindmap`、`onlineTable` 进入第一阶段时只保留占位或派生结果,不把完整嵌入式编辑能力一起搬过去。
|
||||
- 命令核优先采用 `edita-core` 思路,文档模型与转换优先采用 `blocks`,输入壳局部借鉴 `kode`,`leptos-tiptap` 仅作 fallback 参考。
|
||||
@@ -0,0 +1,76 @@
|
||||
# [recycle] AI Tool Cutover Matrix
|
||||
|
||||
> 更新时间:2026-04-16
|
||||
>
|
||||
> 目标:建立 `builtins` 到 Rust Tool registry 的一一对应矩阵,并明确每个工具当前的割接状态。
|
||||
|
||||
## 1. 结论
|
||||
|
||||
- 本文覆盖 `docsServerTools`、`mediaServerTools`、`mindmapServerTools`、`onlyofficeServerTools`、`registryBuiltins` 与 `ai-agent/run` 的一一对应关系。
|
||||
- `docs_search`、`docs_read` 已进入 Rust tool runtime;`ai-agent/run` 在 Convex 模式下直接通过 `executeRustBridgeTool(...)` 执行,`docsServerTools` 仅保留非 Convex / 兼容 fallback。
|
||||
- `search_web`、`image_read`、`slash_run` 已进入 Rust tool registry,TS 仅保留最小 transport 壳或数据加载壳。
|
||||
- `asset_extract_outline`、`asset_to_mindmap`、`oo_*` 继续保留 TS transport / 产品编排边界,但 `asset_to_mindmap` 的真正导图写入已收口到 Rust `mindmap_apply_ops`。
|
||||
- AI 工具链的运行态证据已经补齐:`wolai-frontend/src/lib/ai-agent/runtime/runAgent.test.ts` 已验证 `docs_search -> docs_read` 的 runtime 调用顺序与事件流。
|
||||
|
||||
## 2. 一一对应矩阵
|
||||
|
||||
| builtin tool | Rust toolset | Rust tool name | 当前状态 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `search_web` | `toolset.readonly` | `search_web` | `RUST_OWNER` | 联网检索已完全切到 Rust runtime,TS route 只保留 transport 壳。 |
|
||||
| `image_read` | `toolset.media_read` | `image_read` | `RUST_OWNER` | 图片/附件 OCR 结果已由 Rust runtime 统一归一化,TS 仅负责最小 transport 取数。 |
|
||||
| `slash_run` | `toolset.slash_write` | `slash_run` | `RUST_OWNER` | Rust 负责命令解析与结果标准化,TS 只保留创建/重命名 transport 写壳。 |
|
||||
| `docs_search` | `toolset.docs_read` | `docs_search` | `RUST_OWNER` | Convex 主链已改为 Rust runtime 执行搜索与结果归一化,TS 仅负责拉取搜索数据集 transport。 |
|
||||
| `docs_read` | `toolset.docs_read` | `docs_read` | `RUST_OWNER` | Convex 主链已改为 Rust runtime 执行裁剪与结果归一化,TS 仅负责读取目标文档 transport。 |
|
||||
| `rag_lightrag_query` | `toolset.rag_read` | `rag_lightrag_query` | `TS_TRANSPORT_KEEP` | 依赖外部 LightRAG 服务,不在本轮切换主线。 |
|
||||
| `asset_extract_outline` | `toolset.onlyoffice_read` | `asset_extract_outline` | `TS_TRANSPORT_KEEP` | 仍需附件下载和 MinerU 解析,长期保留 TS transport / 外部服务编排。 |
|
||||
| `asset_to_mindmap` | `toolset.onlyoffice_write` | `asset_to_mindmap` | `TS_TRANSPORT_KEEP` | 附件大纲提取仍依赖 TS/MinerU,但真正的导图写入已经改走 Rust `mindmap_apply_ops`。 |
|
||||
| `oo_get_selection` | `toolset.onlyoffice_editor` | `oo_get_selection` | `TS_TRANSPORT_KEEP` | 客户端插件内执行,Web 侧只做回传。 |
|
||||
| `oo_replace_selection` | `toolset.onlyoffice_editor` | `oo_replace_selection` | `TS_TRANSPORT_KEEP` | 客户端插件内执行,Web 侧只做回传。 |
|
||||
| `oo_insert_text` | `toolset.onlyoffice_editor` | `oo_insert_text` | `TS_TRANSPORT_KEEP` | 客户端插件内执行,Web 侧只做回传。 |
|
||||
| `oo_insert_html` | `toolset.onlyoffice_editor` | `oo_insert_html` | `TS_TRANSPORT_KEEP` | 客户端插件内执行,Web 侧只做回传。 |
|
||||
| `oo_insert_image` | `toolset.onlyoffice_editor` | `oo_insert_image` | `TS_TRANSPORT_KEEP` | 客户端插件内执行,Web 侧只做回传。 |
|
||||
|
||||
## 3. Rust registry 对照
|
||||
|
||||
- `toolset.readonly`:`search_web`
|
||||
- `toolset.media_read`:`image_read`
|
||||
- `toolset.slash_write`:`slash_run`
|
||||
- `toolset.docs_read`:`docs_search`、`docs_read`
|
||||
- `toolset.rag_read`:`rag_lightrag_query`
|
||||
- `toolset.onlyoffice_read`:`asset_extract_outline`
|
||||
- `toolset.onlyoffice_write`:`asset_to_mindmap`
|
||||
- `toolset.onlyoffice_editor`:`oo_*`
|
||||
|
||||
## 4. 本轮 cutover 结果
|
||||
|
||||
本轮已完成:
|
||||
|
||||
- 以 `ai-agent/run` 为统一入口,对齐 `registryBuiltins` 与服务端 builtin 实现,避免再长出第二套工具分发表。
|
||||
- 把 `docs_search`、`docs_read`、`search_web`、`image_read`、`slash_run` 接到 Rust tool runtime,并在代码侧绑定里冻结为 Rust owner。
|
||||
- 把 `asset_to_mindmap` 收口为“TS 提纲提取 + Rust 导图写入”的明确边界,不再保留模糊的 `TS_COMPAT_PENDING` 口径。
|
||||
- 保留 `oo_*`、`client-tool-result`、`asset_extract_outline` 这类长期 `TS_TRANSPORT_KEEP` / 产品编排壳。
|
||||
|
||||
## 5. 后续维护边界
|
||||
|
||||
- `docsServerTools` 继续只服务于非 Convex / fallback 模式,不再承担 Convex 主链真执行。
|
||||
- `asset_extract_outline` 若未来要继续 Rust 化,应只处理解析链迁移,不涉及当前 Rust tool runtime 的 owner 边界回退。
|
||||
- `asset_to_mindmap` 若未来继续下沉,可进一步把“大纲转导图 ops”的编排逻辑迁入 Rust;在此之前,它已经是明确的 `TS_TRANSPORT_KEEP`。
|
||||
- `oo_*` 继续保持客户端插件边界,只通过 `/api/ai-agent/client-tool-result` 回传结果。
|
||||
|
||||
## 6. 第一批 TS_LEGACY_DELETE 清单
|
||||
|
||||
以下旧面已经满足“先停写、再切调用方、最后删除”的文档准备条件,应纳入第一批 `TS_LEGACY_DELETE`:
|
||||
|
||||
- `documents/create-child`
|
||||
- `documents/embed`
|
||||
- `documents/empty-trash`
|
||||
- `documents/purge`
|
||||
- `documents/template`
|
||||
- `mindmap-trash/empty`
|
||||
- `builtins/**`
|
||||
|
||||
删除原则:
|
||||
|
||||
- `documents/create-child`、`documents/embed`、`documents/empty-trash`、`documents/purge`、`documents/template` 仅允许保留 route transport 壳,不再保留 route 内私有业务逻辑。
|
||||
- `mindmap-trash/empty` 需要在确认 Rust tool/runtime 与统一失败落账稳定后,删除 TS 旧实现。
|
||||
- `builtins/**` 中已经被 Rust tool runtime 接管的服务端真入口,应先收口到 `ai-agent/run` 或 Rust runtime,再物理删除 legacy helper。
|
||||
@@ -0,0 +1,132 @@
|
||||
# [recycle] Mindmap 与 OnlyOffice 当前边界说明
|
||||
|
||||
> 更新时间:2026-04-14
|
||||
>
|
||||
> 主仓:`/mnt/Data1T/mnote`
|
||||
|
||||
## 1. Mindmap 当前主链
|
||||
|
||||
### 1.1 主组件与嵌入链路
|
||||
|
||||
- 主组件:`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`
|
||||
- BlockNote 注册入口:`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/schema.ts`
|
||||
- 独立全屏页:`/mnt/Data1T/mnote/wolai-frontend/src/app/mindmap/[docId]/[mindmapId]/page.tsx`
|
||||
|
||||
当前事实:
|
||||
|
||||
- `MindmapBlockView` 同时服务文档内嵌块与独立全屏页。
|
||||
- 独立页通过 `editorStub` 复用同一组件,不额外复制第二套思维导图主壳。
|
||||
- 文档内嵌与独立页共用同一套工具栏、侧栏、导航器、缩略图与上下文菜单子组件。
|
||||
|
||||
### 1.2 数据入口与 ops 边界
|
||||
|
||||
- 本地读写入口:`/mnt/Data1T/mnote/wolai-frontend/src/lib/mindmap/mindmapLocalStore.ts`
|
||||
- 节点操作入口:`/mnt/Data1T/mnote/wolai-frontend/src/lib/mindmap/mindmapOps.ts`
|
||||
- 页面 API:
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/mindmap/[docId]/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/mindmap/[docId]/[mindmapId]/route.ts`
|
||||
|
||||
当前拆分:
|
||||
|
||||
- 前端交互状态保留在 `MindmapBlock.tsx` 及其子组件内。
|
||||
- 本体数据读写由 `mindmapLocalStore.ts` 提供本地文件落盘兼容接口。
|
||||
- 节点增删改、引用、注释、链接等操作由 `mindmapOps.ts` 统一描述为 `MindmapOp`。
|
||||
- 页面 API 当前通过 Convex `api.mindmaps.*` 承接 `get/put/restore/purge`,保留现有前端形态不变。
|
||||
- 当前 Mindmap API 已补统一元信息口径:
|
||||
- `pageId = documentId`
|
||||
- `attachmentId = mindmapId`
|
||||
- `workspaceId` 统一来自页面归属工作区
|
||||
- `requestId/traceId` 统一从请求头透传或在路由侧兜底生成
|
||||
|
||||
### 1.3 后续 adapter 目标
|
||||
|
||||
后续若进入 Rust adapter,只抽象以下边界,不复制历史仓 UI:
|
||||
|
||||
- 节点树结构
|
||||
- 节点引用 `refs`
|
||||
- 节点操作日志 `MindmapOp`
|
||||
- mindmap 与 page/block 的绑定关系
|
||||
|
||||
## 2. OnlyOffice 当前主链
|
||||
|
||||
### 2.1 页面与 API 边界
|
||||
|
||||
- 页面入口:
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/onlyoffice/page.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/onlyoffice/OnlyOfficeClientPage.tsx`
|
||||
- 页面侧面板:
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/onlyoffice/OnlyOfficeAiAgentPanel.tsx`
|
||||
- API 边界:
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/onlyoffice/sign/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/onlyoffice/proxy/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/onlyoffice/callback/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/onlyoffice/forcesave/route.ts`
|
||||
|
||||
当前拆分:
|
||||
|
||||
- `page.tsx` 负责页面级早期 DOM 补丁与容器加载。
|
||||
- `OnlyOfficeClientPage.tsx` 负责编辑器初始化、内部请求改写、代理地址适配与 callback URL 组装。
|
||||
- `sign` 负责 JWT 签名。
|
||||
- `proxy` 负责同源代理与回源 URL 安全限制。
|
||||
- `callback` 负责保存回写存储。
|
||||
- `forcesave` 负责触发文档服务器强制保存。
|
||||
|
||||
### 2.2 与正文和附件的关系
|
||||
|
||||
- Office 文件入口仍来自 `MediaBlock`:`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MediaBlock.tsx`
|
||||
- 正文里只保留附件块与跳转入口,不在 BlockNote 内直接运行 OnlyOffice 编辑器。
|
||||
- OnlyOffice 编辑器继续在独立 `/onlyoffice` 页面运行。
|
||||
- 当前 OnlyOffice 已稳定使用:
|
||||
- `documentId` 作为页面业务标识
|
||||
- `assetId` 作为附件业务标识
|
||||
- 页面归属仍由文档/附件在 Convex 中的 `workspace_id` 与 `document_id` 决定
|
||||
|
||||
## 2.5 与 Mindmap 对齐后的共享标识口径
|
||||
|
||||
- 页面标识:
|
||||
- Mindmap 使用 `pageId/documentId`
|
||||
- OnlyOffice 使用 `documentId`
|
||||
- 附件标识:
|
||||
- Mindmap 使用 `attachmentId/mindmapId`
|
||||
- OnlyOffice 使用 `assetId`
|
||||
- 页面归属:
|
||||
- 两者都以 Convex 中的 `workspace_id + document_id` 作为最终归属真相
|
||||
- 追踪字段:
|
||||
- Mindmap API 已返回 `requestId/traceId`
|
||||
- OnlyOffice 页面当前通过查询参数与调试上下文持有 `assetId/documentId/userId`,后续若接入统一 Rust adapter,继续沿用同一页面/附件口径
|
||||
|
||||
### 2.3 静态资源边界
|
||||
|
||||
- 根目录静态资源保留在 `/mnt/Data1T/mnote/src/components/onlyoffice/`
|
||||
- 当前已确认存在:
|
||||
- `onlyoffice-web-apps/`
|
||||
- `onlyoffice-plugins/`
|
||||
- `onlyoffice-data/`
|
||||
|
||||
这些目录继续保留为运行所需静态资源、插件和数据目录,不做迁移式替换。
|
||||
|
||||
### 2.4 后续 adapter 目标
|
||||
|
||||
后续若进入 Rust adapter,只抽象以下边界:
|
||||
|
||||
- 对象解析与附件定位
|
||||
- 签名生成
|
||||
- callback 写回
|
||||
- forcesave 触发
|
||||
- 代理回源请求
|
||||
|
||||
## 3. 历史仓禁止误复制范围
|
||||
|
||||
以下历史仓路径只作为参考,不进入主仓主线:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/app/documents/[id]/mindmap/page.tsx`
|
||||
- `/mnt/Data1T/mnote-rust/app/documents/[id]/office/page.tsx`
|
||||
- `/mnt/Data1T/mnote-rust/components/sidebar/sidebar.tsx`
|
||||
- `/mnt/Data1T/mnote-rust/app/page.tsx`
|
||||
- `/mnt/Data1T/mnote-rust/app/onlyoffice/page.tsx`
|
||||
|
||||
约束:
|
||||
|
||||
- 不复制第二套对象页壳。
|
||||
- 不复制第二套 sidebar 壳。
|
||||
- `adapter-onlyoffice`、`adapter-mindmap` 继续作为后置实现项,而不是本阶段直接迁入主线。
|
||||
@@ -0,0 +1,48 @@
|
||||
# [recycle] Phase 8 Cutover Log 2026-04-15
|
||||
|
||||
## 概要
|
||||
|
||||
- 完成 Phase 8 收口,Rust 已成为 mnote 的唯一业务执行平面。
|
||||
- 完成 `task-036` 到 `task-044`,并清空 harness 中全部历史失败/待办状态。
|
||||
- ONLYOFFICE 已升级到 `9.3.1`,真实页面打开、插件桥、callback、forcesave 回归通过。
|
||||
|
||||
## 本轮关键变更
|
||||
|
||||
### 1. 页面与对象主链
|
||||
|
||||
- 页面旧面 `documents/create-child`、`documents/embed`、`documents/empty-trash`、`documents/purge`、`documents/template` 已收口到统一 adapter,route 仅保留 transport。
|
||||
- Mindmap 剩余旧面已接到 Rust mindmap adapter/tool,AI/Mindmap 写链失败态开始统一落账。
|
||||
- OnlyOffice sign/proxy/callback/forcesave 已固定为 `TS_TRANSPORT_KEEP`,对象规则与 session/sign/proxy/callback 准备由 Rust adapter 持有。
|
||||
|
||||
### 2. AI 工具面
|
||||
|
||||
- `search_web`、`image_read`、`slash_run` 已切到 Rust Tool runtime 主路径。
|
||||
- `ai-agent/run` 不再保留 `search_web` 的 TS fallback。
|
||||
- `mediaServerTools`、`slashServerTools` 已收缩为 transport helper,不再作为私有真入口。
|
||||
|
||||
### 3. 统一观测与状态语义
|
||||
|
||||
- 已补 workspace 级 bridge 总览查询。
|
||||
- 已补失败态、冲突态、补偿态第一批统一落账。
|
||||
- `bridge-log/runtime` 与页面主写链、AI/Mindmap 写链的状态语义开始对齐。
|
||||
|
||||
### 4. 文档与清单
|
||||
|
||||
- 已更新 `rust-kernel-cutover-v1.md`。
|
||||
- 已更新 `release-readiness-source-audit.md`。
|
||||
- 已更新 `rust-kernel-backport-phase-checklist.md`。
|
||||
- 已更新 `rust-kernel-missing-targets.md`。
|
||||
- 已新增 `rust-kernel-legacy-delete-list.md`。
|
||||
- 已更新 `ai-tool-cutover-matrix.md`。
|
||||
|
||||
## 当前结论
|
||||
|
||||
可以使用下面这句统一口径:
|
||||
|
||||
> Rust 已成为 mnote 的唯一业务执行平面;Web route 只保留 transport、auth、session、streaming、proxy 与 callback 壳。
|
||||
|
||||
## 后续建议
|
||||
|
||||
- 执行第一批 `TS_LEGACY_DELETE` 的物理删除。
|
||||
- 继续压缩 `builtins/**` 中剩余的 TS 兼容实现。
|
||||
- 对 OnlyOffice、Mindmap 和 AI 工具链做一轮发布前运行态回归复核。
|
||||
@@ -0,0 +1,193 @@
|
||||
# [recycle] 发布前源代码审计
|
||||
|
||||
> 更新时间:2026-04-16
|
||||
>
|
||||
> 审计范围:`/mnt/Data1T/mnote`
|
||||
|
||||
## 1. Bridge 入口与日志回查
|
||||
|
||||
### 1.1 最小查询与写入入口
|
||||
|
||||
当前已确认的 bridge 主线入口:
|
||||
|
||||
- 查询:
|
||||
- `src/app/api/documents/content/route.ts`
|
||||
- `src/app/api/documents/meta/route.ts`
|
||||
- `src/app/api/sidebar/route.ts`
|
||||
- 写入:
|
||||
- `src/app/api/documents/title/route.ts`
|
||||
- `src/app/api/documents/stats/route.ts`
|
||||
- `src/app/api/documents/options/route.ts`
|
||||
- `src/app/api/documents/save/route.ts`
|
||||
- `src/app/api/blocks/patch/route.ts`
|
||||
- `src/app/api/onlyoffice/callback/route.ts`
|
||||
|
||||
### 1.2 日志与事件落账
|
||||
|
||||
当前已确认:
|
||||
|
||||
- `src/lib/documents/metadata-command-adapter.ts` 成功路径会调用 `recordBridgeCommandArtifacts`
|
||||
- `src/lib/documents/save-command-adapter.ts` 成功路径会调用 `recordBridgeCommandArtifacts`
|
||||
- `src/app/api/blocks/patch/route.ts` 成功路径会调用 `recordBridgeCommandArtifacts`
|
||||
- `src/lib/documents/media-asset-command-adapter.ts` 会在 ONLYOFFICE callback 成功写回后尝试复用同一 bridge log 落账
|
||||
- `src/lib/documents/bridge-log.ts` 会同时写入:
|
||||
- `bridgeLogs.recordCommandLog`
|
||||
- `bridgeLogs.recordDomainEvent`
|
||||
- 回查入口:
|
||||
- `src/app/api/bridge/request/route.ts -> bridgeLogs.listByRequest`
|
||||
- `src/app/api/bridge/trace/route.ts -> bridgeLogs.listByTrace`
|
||||
|
||||
结论:
|
||||
|
||||
- bridge 的最小查询、最小写入、日志生成、事件生成和错误返回已经形成源码闭环。
|
||||
|
||||
## 2. 保存口径审计
|
||||
|
||||
当前正文与页面元信息主写链如下:
|
||||
|
||||
- `documents.save -> api.documents.updateContent`
|
||||
- `documents.title.update -> api.documents.updateTitle`
|
||||
- `documents.stats.update -> api.documents.updateStats`
|
||||
- `documents.options.update -> api.documents.updateOptions`
|
||||
- `blocks.patch -> 读取文档内容后回写 api.documents.updateContent`
|
||||
|
||||
OnlyOffice 写回:
|
||||
|
||||
- `src/app/api/onlyoffice/callback/route.ts -> media.assets.replace_storage -> api.mediaAssets.replaceStorageFromUpload`
|
||||
|
||||
结论:
|
||||
|
||||
- 页面正文、页面元信息、块补丁与 OnlyOffice 附件写回最终都以 Convex mutation 为主事实写入点。
|
||||
- 当前未发现把页面/块主真相改写到 `mnote-rust`、本地缓存目录或其他派生目录的主写链。
|
||||
|
||||
## 3. `trace_id` / `request_id` / `workspace_id` / `page_id` 一致性
|
||||
|
||||
### 3.1 前端与 route
|
||||
|
||||
- `buildDocumentBridgeContext()` 统一生成 `requestId`、`traceId`、`workspaceId`
|
||||
- `buildDocumentBridgeContextWithActor()` 可为 ONLYOFFICE callback 这类无用户 cookie 的服务端回调显式注入 `actor/source`
|
||||
- `buildDocumentCommandEnvelope()` 统一挂载 `commandId`、`target.pageId`
|
||||
- `documents.title/stats/options/save` 均将 `normalizedDocumentId` 作为 `target.pageId`
|
||||
- `onlyoffice/callback` 会把 `asset.document_id` 作为 `target.pageId`,并透传 `workspaceId`
|
||||
|
||||
### 3.2 日志落账
|
||||
|
||||
- `recordBridgeCommandArtifacts()` 使用同一 `context.requestId`、`context.traceId`
|
||||
- `workspaceId` 统一取 `target.workspaceId` 或 `context.workspaceId`
|
||||
- `targetPageId` 统一落到 command log
|
||||
- domain event payload 同时记录 `request_id`、`trace_id`、`command_id`、`command_name`
|
||||
|
||||
### 3.3 当前辅助验证
|
||||
|
||||
- `src/lib/documents/bridge.test.ts` 已覆盖:
|
||||
- `target.pageId = doc_1`
|
||||
- `executeSaveBridgeCommand` 成功路径会调用 `recordBridgeCommandArtifacts`
|
||||
- 返回结果保留 `req_1`、`trace_1`
|
||||
- `src/app/api/onlyoffice/callback/route.test.ts` 已覆盖:
|
||||
- callback 成功路径会调用 `executeMediaAssetWritebackBridgeCommand`
|
||||
- `storageId`、`assetId`、`documentId`、`workspaceId` 会进入 `media.assets.replace_storage` envelope
|
||||
|
||||
结论:
|
||||
|
||||
- `trace_id`、`request_id`、`workspace_id`、`page_id` 在 route、adapter、bridge log、回查入口之间已有统一口径。
|
||||
|
||||
## 4. 禁止事项复查
|
||||
|
||||
### 4.1 不双仓并行
|
||||
|
||||
- 主仓脚本与文档已统一指向 `/mnt/Data1T/mnote`
|
||||
- `wolai-frontend/src`、`wolai-frontend/convex`、`rust/` 下未发现直接依赖 `/mnt/Data1T/mnote-rust` 的运行时代码路径
|
||||
|
||||
### 4.2 不跨仓链接
|
||||
|
||||
- 排除 `node_modules`、`.next`、`.venv`、`rust/target` 等构建产物后,主仓源码范围内未发现跨仓符号链接
|
||||
|
||||
### 4.3 不复制第二套产品前端壳
|
||||
|
||||
- 主仓保留的是既有 `wolai-frontend` 主壳
|
||||
- 历史仓对象页壳与第二套 sidebar 仅在文档中被标记为禁止误复制范围
|
||||
|
||||
### 4.4 不把对象页壳 / diagnostics / smoke 页带入主线
|
||||
|
||||
- 当前主仓 `wolai-frontend/src/app` 下未发现新增的 `diagnostic` / `smoke` 页面目录
|
||||
- 历史仓相关页面已在边界文档中明确列入禁止误复制清单
|
||||
|
||||
### 4.5 不散落多个 Rust 源码根
|
||||
|
||||
- 主仓顶层 `Cargo.toml` 仅存在 `rust/Cargo.toml`
|
||||
- 其余 crate `Cargo.toml` 均位于 `rust/crates/*`
|
||||
|
||||
## 5. 运行态复核结果与最终结论
|
||||
|
||||
本审计只能关闭源码可验证项,但截至 2026-04-16,发布前运行态复核已经补齐五条直接证据:
|
||||
|
||||
- 已复跑 `scripts/task019-document-ui-regression.js`,文档页标题、正文、保存、刷新、回查链通过。
|
||||
- 已复跑 `scripts/task021-mindmap-ui-regression.js`,Mindmap 全屏页的新增子节点、删除子节点、保存链、刷新回查与 `requestId/traceId` 元信息同步通过。
|
||||
- 已复跑 `scripts/task022-onlyoffice-ui-regression.js`,ONLYOFFICE `9.3.1` 基线下的打开、插件桥、callback 写回、forcesave 与 `storage_id` 变化通过。
|
||||
- 已通过 `pnpm exec vitest run src/lib/ai-agent/runtime/runAgent.test.ts`,补齐 `docs_search -> docs_read` 的 AI runtime 调用证据。
|
||||
- 已通过 `bash /mnt/Data1T/mnote/rust/scripts/task049-cli-smoke.sh`,补齐 CLI 主链的最小真实执行验收。
|
||||
|
||||
当前已不再存在阻塞发布的 CLI / AI 运行态尾项。
|
||||
|
||||
补充核验结果:
|
||||
|
||||
- `cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -p bridge-runtime` 已通过。
|
||||
- `wolai-frontend` 相关定向 `eslint` 当前无 error,仅剩仓库既有 warnings,不构成本轮发布阻塞。
|
||||
|
||||
仍然要求环境满足的前置条件:
|
||||
|
||||
- 浏览器回归环境可访问 `http://127.0.0.1:3000`
|
||||
- `MNOTE_ONLYOFFICE_PROBE_DOCX=/tmp/mnote-onlyoffice-probe/probe.docx` 可用
|
||||
- 已登录态可通过 `/auth` 或测试账号快速登录建立
|
||||
|
||||
## 6. 旧 TS 执行面割接清单
|
||||
|
||||
当前源码审计之外,还需要看“哪些 TS route 还能留,哪些只是历史兼容”。
|
||||
|
||||
本轮已新增:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-cutover-v1.md`
|
||||
|
||||
该文档已把主仓当前执行面明确分成四类:
|
||||
|
||||
- `RUST_OWNER`
|
||||
- `TS_TRANSPORT_KEEP`
|
||||
- `TS_COMPAT_PENDING`
|
||||
- `TS_LEGACY_DELETE`
|
||||
|
||||
其中当前仍需要长期观察但不再构成主链阻塞的旧面主要包括:
|
||||
|
||||
- `src/app/api/documents/embed/route.ts`
|
||||
- `src/app/api/mindmap-ai/**`
|
||||
|
||||
其中页面旧面在 2026-04-16 的最终口径如下:
|
||||
|
||||
- `src/app/api/documents/create-child/route.ts` 已改走 `documents.create`
|
||||
- `src/app/api/documents/empty-trash/route.ts` 已改走 `documents.emptyTrashByWorkspace`
|
||||
- `src/app/api/documents/purge/route.ts` 已改走 `documents.purge`
|
||||
- `src/app/api/documents/template/route.ts` 已改走 `documents.template`
|
||||
- `src/app/api/documents/embed/route.ts` 已改走 `documents.save`,但目标页面插入位与 `pageReference` block 组装仍在 TS,因此继续按 `TS_COMPAT_PENDING` / 产品编排壳维护
|
||||
- `src/app/api/mindmap/[docId]/route.ts` 与 `src/app/api/mindmap/[docId]/[mindmapId]/route.ts` 的 `DELETE/PATCH` 已改走 `mindmaps.delete/restore/purge`
|
||||
- `src/app/api/mindmap-trash/empty/route.ts` 已改走 `mindmaps.emptyTrashByWorkspace`
|
||||
- `src/app/api/blocks/*` 现网主链实际统一走 `src/lib/blocks/block-command-adapter.ts`;`src/lib/documents/block-command-adapter.ts` 应视为遗留兼容 helper,而非现网主执行面
|
||||
- `src/app/api/ai-agent/run/route.ts` 的核心工具主链现在已经统一走 Rust tool runtime:`docs_search`、`docs_read`、`search_web`、`image_read`、`slash_run` 不再保留 TS 真执行兜底
|
||||
|
||||
结论:
|
||||
|
||||
- 当前可以说“Rust 已成为 mnote 的主业务执行平面”。
|
||||
- 仍保留的 TS 路径只剩 `TS_TRANSPORT_KEEP`、少量 `TS_COMPAT_PENDING` 产品编排壳与冻结的 `TS_LEGACY_DELETE` 清单。
|
||||
- 第一批 `TS_LEGACY_DELETE` 已不再阻塞这轮 release readiness;它们现在的要求是“保持纯壳 / 冻结 helper,不允许反向长出新的业务规则”。
|
||||
|
||||
最终 release readiness 口径:
|
||||
|
||||
- 若按源码审计口径:可以说明“Rust 已成为 mnote 的唯一业务执行平面”,同时明确仍保留 `TS_TRANSPORT_KEEP` 与少量产品编排壳。
|
||||
- 若按完整发布口径:文档页、Mindmap、OnlyOffice、AI runtime、CLI smoke 的 Phase 8 scoped gate 已闭合,可以给出最终完成结论。
|
||||
- 后续维护口径:剩余 `TS_TRANSPORT_KEEP` / `TS_LEGACY_DELETE` 只属于长期清理与边界维护,不再构成第二套业务执行平面。
|
||||
|
||||
补记:
|
||||
|
||||
- 2026-04-15 已复跑 `scripts/task019-document-ui-regression.js`,确认文档页标题、正文、保存与刷新回查通过。
|
||||
- 2026-04-15 已复跑 `scripts/task021-mindmap-ui-regression.js`,确认新增/删除节点、保存链、刷新回查与 `requestId` / `traceId` 元信息同步通过。
|
||||
- 2026-04-15 已复跑 `scripts/task022-onlyoffice-ui-regression.js`,在升级后的 ONLYOFFICE `9.3.1` 基线上确认 `oo_insert_text` 插件桥、`同步保存`、callback 写回与 `storage_id` 变化仍然通过;本次 callback 最终写回已不再由 route 直接调用 `api.mediaAssets.replaceStorageFromUpload`,而是先经 `media.assets.replace_storage` bridge 命令再落到 Convex。
|
||||
- 2026-04-16 已通过 `src/lib/ai-agent/runtime/runAgent.test.ts`,确认 `docs_search -> docs_read` 的运行态工具链顺序、事件流与结果归一化闭合。
|
||||
- 2026-04-16 已通过 `rust/scripts/task049-cli-smoke.sh`,确认 `sidebar/page/block/search/tool/mindmap` 主链 CLI 真实执行闭合。
|
||||
@@ -0,0 +1,85 @@
|
||||
# [recycle] Rust 回迁资产审计清单
|
||||
|
||||
> 更新时间:2026-04-14
|
||||
>
|
||||
> 主仓:`/mnt/Data1T/mnote`
|
||||
|
||||
## 1. 本轮已确认的主仓 Rust 资产
|
||||
|
||||
### 1.1 Workspace 根
|
||||
|
||||
- `rust/Cargo.toml`
|
||||
- `rust/Cargo.lock`
|
||||
|
||||
### 1.2 已并入主线的 crate
|
||||
|
||||
- `rust/crates/core-domain/`
|
||||
- `rust/crates/core-protocol/`
|
||||
- `rust/crates/event-log/`
|
||||
- `rust/crates/storage-convex-bridge/`
|
||||
- `rust/crates/index-fts/`
|
||||
|
||||
### 1.3 已并入主线的 Rust 设计文档
|
||||
|
||||
- `rust/design/INDEX.md`
|
||||
- `rust/design/core/01-domain-model-v0.md`
|
||||
- `rust/design/core/02-command-query-tool-protocol-v0.md`
|
||||
- `rust/design/core/03-storage-event-indexing-v0.md`
|
||||
- `rust/design/core/04-onlyoffice-integration-boundary-v0.md`
|
||||
|
||||
### 1.4 本轮新增的主仓设计文档
|
||||
|
||||
- `design/rust-kernel-backport-plan.md`
|
||||
- `design/rust-kernel-backport-phase-checklist.md`
|
||||
- `design/rust-single-repo-maintenance-boundary.md`
|
||||
- `design/mindmap-onlyoffice-boundary.md`
|
||||
- `design/sidebar-rust-query-target.md`
|
||||
|
||||
## 2. 当前仍未并入主线的目录
|
||||
|
||||
以下目录当前未在主仓 `rust/` 内出现,仍保持后置状态:
|
||||
|
||||
- `rust/crates/adapter-onlyoffice/`
|
||||
- `rust/crates/adapter-mindmap/`
|
||||
- `rust/crates/adapter-legacy-mnote/`
|
||||
- `rust/crates/mnote-cli/`
|
||||
- `rust/bridge/`
|
||||
- `rust/scripts/`
|
||||
- `rust/fixtures/`
|
||||
- `rust/tests/`
|
||||
|
||||
## 3. 已确认没有误带入的历史噪音
|
||||
|
||||
当前主仓 `rust/` 与根 `design/` 下未发现以下类型内容被直接带入主线:
|
||||
|
||||
- 第二套 Next 页面壳
|
||||
- 历史 `diagnostics` 页面
|
||||
- `smoke` 页面
|
||||
- 历史仓 `.next/`、`.tmp/` 产物目录
|
||||
- `adapter-*` 与 `mnote-cli` 实现目录
|
||||
|
||||
备注:
|
||||
|
||||
- `rust/target/` 为本地编译产物,不属于回迁设计资产。
|
||||
- 发布前若需进一步清理编译产物,应单独征得用户同意,不在本轮处理。
|
||||
|
||||
## 4. 主仓启动路径核对
|
||||
|
||||
当前可验证的主仓入口如下:
|
||||
|
||||
- 根目录热启动:`npm run desktop:hot`
|
||||
- 前端开发:`cd /mnt/Data1T/mnote/wolai-frontend && pnpm dev`
|
||||
- 后端开发:`cd /mnt/Data1T/mnote/wolai-backend && uvicorn app.main:app --reload --port 8000`
|
||||
- Rust 校验:`cargo --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml ...`
|
||||
|
||||
核对结果:
|
||||
|
||||
- 根 `package.json` 的仓库级脚本只指向 `scripts/*.js`
|
||||
- `scripts/desktop-hot.js` 只从主仓根目录解析 `.env.all`
|
||||
- `scripts/desktop-hot.js` 只调起 `wolai-frontend/` 与 `wolai-backend/`
|
||||
- `AGENTS.md` 的常用开发命令也全部指向 `/mnt/Data1T/mnote`
|
||||
|
||||
## 5. 当前结论
|
||||
|
||||
- 主仓已具备“只进入 `/mnt/Data1T/mnote` 就能找到前端、后端、Rust、OnlyOffice 相关入口”的文档与脚本口径。
|
||||
- 本轮新增目录与文档均位于主仓 `design/` 或既有 `rust/` 主线下,未把 `mnote-rust` 的历史页面壳或构建噪音带入主线。
|
||||
@@ -0,0 +1,399 @@
|
||||
# [recycle] Rust 内核总割接方案 v1
|
||||
|
||||
> 更新时间:2026-04-16
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-replacement-roadmap-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-missing-targets.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-backport-phase-checklist.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/release-readiness-source-audit.md`
|
||||
|
||||
## 1. 目的
|
||||
|
||||
本文只回答一个问题:
|
||||
|
||||
> **什么时候可以宣布“Rust 已成为 mnote 唯一业务执行平面”,以及宣布前必须退役哪些旧 TS 执行面。**
|
||||
|
||||
这里的“退役”不等于“把所有 Next route 全删掉”。
|
||||
|
||||
真正要退役的是:
|
||||
|
||||
- TS route 内部承载的业务规则
|
||||
- TS 私有工具注册表里的第二套执行语义
|
||||
- 绕过 Rust command/query/tool 的直连 Convex 主链
|
||||
|
||||
允许长期保留的是:
|
||||
|
||||
- transport
|
||||
- auth
|
||||
- session
|
||||
- streaming
|
||||
- SSR / BFF
|
||||
- 第三方对象服务回调壳
|
||||
|
||||
---
|
||||
|
||||
## 2. 状态定义
|
||||
|
||||
为了避免后续再出现“看起来迁了,实际上没迁”的模糊表述,本文统一使用下面四类状态。
|
||||
|
||||
### 2.1 `RUST_OWNER`
|
||||
|
||||
定义:
|
||||
|
||||
- 核心业务规则已经进入 Rust command/query/tool/runtime
|
||||
- TS route 只做参数校验、鉴权、HTTP 包装、Convex transport 或第三方请求转发
|
||||
|
||||
处理策略:
|
||||
|
||||
- 允许继续保留 route 壳
|
||||
- 不允许再往 route 内加业务规则
|
||||
|
||||
### 2.2 `TS_TRANSPORT_KEEP`
|
||||
|
||||
定义:
|
||||
|
||||
- 这条 route 本身不是业务真规则入口
|
||||
- 但因为浏览器会话、回调、上传下载、SSE、客户端桥等原因,需要长期保留在 Web 层
|
||||
|
||||
处理策略:
|
||||
|
||||
- 永久保留或长期保留
|
||||
- 只能承载 transport / session / proxy / callback
|
||||
|
||||
### 2.3 `TS_COMPAT_PENDING`
|
||||
|
||||
定义:
|
||||
|
||||
- 当前已经有部分 Rust 接缝
|
||||
- 但仍存在 TS 业务拼接、对象规则或直连 Convex 的旧执行逻辑
|
||||
|
||||
处理策略:
|
||||
|
||||
- 不能宣布总割接完成
|
||||
- 必须继续迁到 Rust 后,才能降级为 `RUST_OWNER` 或 `TS_TRANSPORT_KEEP`
|
||||
|
||||
### 2.4 `TS_LEGACY_DELETE`
|
||||
|
||||
定义:
|
||||
|
||||
- 该 route 或旧逻辑只服务于历史兼容、诊断、临时过渡或旧 UI
|
||||
- 一旦对应能力完成 Rust cutover 并切完调用方,就应删除
|
||||
|
||||
处理策略:
|
||||
|
||||
- 先停写
|
||||
- 再切调用
|
||||
- 最后物理删除
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前总盘点
|
||||
|
||||
## 3.1 页面系统
|
||||
|
||||
### 已进入 `RUST_OWNER`
|
||||
|
||||
- `wolai-frontend/src/app/api/documents/meta/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/content/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/title/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/stats/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/options/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/save/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/create/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/move/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/delete/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/restore/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/duplicate/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/copy-tree/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/create-child/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/empty-trash/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/purge/route.ts`
|
||||
- `wolai-frontend/src/app/api/documents/template/route.ts`
|
||||
|
||||
判定依据:
|
||||
|
||||
- 已统一走 `buildDocument(Command|Query)Envelope`
|
||||
- 已切到共享 page/metadata/save adapter 或 `resolveRustBridge*`
|
||||
- route 只剩 transport、鉴权、参数整理与 HTTP 返回
|
||||
- `create-child` 已改为复用 `documents.create`
|
||||
- `template`、`empty-trash`、`purge` 已补齐 Rust runtime/transport 映射,不再由 `page-command-adapter.ts` 直接调用 Convex mutation
|
||||
|
||||
### 仍是 `TS_COMPAT_PENDING`
|
||||
|
||||
- `wolai-frontend/src/app/api/documents/embed/route.ts`
|
||||
|
||||
原因:
|
||||
|
||||
- `embed` 已不再直接调用 `api.documents.updateContent`,最终写入已改走 `documents.save`。
|
||||
- 但目标页面插入点计算、`pageReference` block 组装仍在 TS 侧完成,尚未进入独立 Rust command 面。
|
||||
- 因此它已经摆脱“直连 Convex mutation”的假完成状态,但还不能宣布为完全 `RUST_OWNER`。
|
||||
|
||||
处理结论:
|
||||
|
||||
- 页面旧面中,`create-child`、`template`、`empty-trash`、`purge` 已转为统一 Rust transport。
|
||||
- `embed` 仍是 Phase 8 前需要继续清理的剩余页面旧面。
|
||||
- 当前仍不可直接物理删除这些 route 文件,必须先切完调用方并确认不再承载 TS 内容编排。
|
||||
|
||||
---
|
||||
|
||||
## 3.2 块系统
|
||||
|
||||
### 已进入 `RUST_OWNER`
|
||||
|
||||
- `wolai-frontend/src/app/api/blocks/get/route.ts`
|
||||
- `wolai-frontend/src/app/api/blocks/patch/route.ts`
|
||||
- `wolai-frontend/src/app/api/blocks/move/route.ts`
|
||||
- `wolai-frontend/src/app/api/blocks/embed/route.ts`
|
||||
|
||||
判定依据:
|
||||
|
||||
- 现网 route 实际统一走 `wolai-frontend/src/lib/blocks/block-command-adapter.ts`
|
||||
- `patch/move/embed` 已通过 `documents.save` 主链落盘,不再依赖 route 内直连 Convex mutation
|
||||
- route 仅保留 transport、参数校验与 HTTP 返回
|
||||
|
||||
### 仍需冻结的兼容 helper
|
||||
|
||||
- `wolai-frontend/src/lib/documents/block-command-adapter.ts`
|
||||
|
||||
说明:
|
||||
|
||||
- 该文件当前不再是 `/api/blocks/*` 的主调用链。
|
||||
- 它仍保留旧桥接 helper 语义,因此需要明确按兼容层冻结,不能再被当成“现网主执行面”继续扩展。
|
||||
|
||||
### 可删的旧逻辑
|
||||
|
||||
- 旧 `loadBlocks/saveBlocks` 私有执行路径
|
||||
- 任何绕过共享 block adapter 的 AI 文档写入旧实现
|
||||
|
||||
处理结论:
|
||||
|
||||
- 块系统主链已经满足“route 保留、旧业务逻辑退役”的前提
|
||||
- 后续只允许在 Rust block ops 上继续扩展
|
||||
|
||||
---
|
||||
|
||||
## 3.3 查询聚合与观测
|
||||
|
||||
### 已进入 `RUST_OWNER`
|
||||
|
||||
- `wolai-frontend/src/app/api/sidebar/route.ts`
|
||||
- `wolai-frontend/src/app/api/search/documents/route.ts`
|
||||
- `wolai-frontend/src/app/api/bridge/request/route.ts`
|
||||
- `wolai-frontend/src/app/api/bridge/trace/route.ts`
|
||||
|
||||
### 仍是 `TS_TRANSPORT_KEEP`
|
||||
|
||||
- `wolai-frontend/src/app/api/search/recent/route.ts`
|
||||
|
||||
说明:
|
||||
|
||||
- `search/recent` 当前只承担“打开页面后写最近访问记录”的 side-effect,不再承担搜索召回
|
||||
- 它可以长期保留在 Web 层,但必须明确不是搜索业务真入口
|
||||
|
||||
### 当前仍缺
|
||||
|
||||
- workspace 级统一观测总览
|
||||
- 分页、按对象过滤、按状态过滤
|
||||
- 完整失败态和冲突态的全链路落账
|
||||
|
||||
---
|
||||
|
||||
## 3.4 AI 与工具面
|
||||
|
||||
### 已进入 `RUST_OWNER`
|
||||
|
||||
- `wolai-frontend/src/app/api/ai-agent/run/route.ts` 中的 `doc_get`
|
||||
- `wolai-frontend/src/app/api/ai-agent/run/route.ts` 中的 `doc_find`
|
||||
- `wolai-frontend/src/app/api/ai-agent/run/route.ts` 中的 `doc_insert_blocks`
|
||||
- `wolai-frontend/src/app/api/ai-agent/run/route.ts` 中的 `doc_replace_range`
|
||||
- `wolai-frontend/src/app/api/ai-agent/run/route.ts` 中的 `docs_search`
|
||||
- `wolai-frontend/src/app/api/ai-agent/run/route.ts` 中的 `docs_read`
|
||||
- `wolai-frontend/src/app/api/ai-agent/run/route.ts` 中的 `search_web`
|
||||
- `wolai-frontend/src/app/api/ai-agent/run/route.ts` 中的 `image_read`
|
||||
- `wolai-frontend/src/app/api/ai-agent/run/route.ts` 中的 `slash_run`
|
||||
|
||||
说明:
|
||||
|
||||
- Convex 主链下,`ai-agent/run` 已直接通过 `executeRustBridgeTool(...)` 执行 `docs_search`、`docs_read`、`search_web`、`image_read`、`slash_run`。
|
||||
- `docsServerTools` 当前只保留非 Convex / fallback 模式,不再承担 Convex 主链真执行。
|
||||
- `search_web`、`image_read`、`slash_run` 已切到 Rust Tool runtime,`ai-agent/run` 不再保留 TS 真执行兜底。
|
||||
|
||||
### 仍是 `TS_TRANSPORT_KEEP`
|
||||
|
||||
- `wolai-frontend/src/app/api/ai-agent/client-tool-result/route.ts`
|
||||
- `wolai-frontend/src/lib/ai-agent/tools/builtins/onlyoffice/onlyofficeServerTools.ts` 中的 `asset_extract_outline`
|
||||
- `wolai-frontend/src/lib/ai-agent/tools/builtins/onlyoffice/onlyofficeServerTools.ts` 中的 `asset_to_mindmap`
|
||||
- `wolai-frontend/src/lib/ai-agent/tools/builtins/onlyoffice/onlyofficeServerTools.ts` 中的 `oo_*`
|
||||
- `wolai-frontend/src/lib/ai-agent/tools/builtins/rag/**`
|
||||
|
||||
说明:
|
||||
|
||||
- `client-tool-result` 只负责浏览器内客户端工具回传,不承担业务决策。
|
||||
- `asset_extract_outline` 仍依赖附件下载与 MinerU 解析,因此长期保留 TS transport / 外部服务编排。
|
||||
- `asset_to_mindmap` 当前属于“TS 提纲提取 + Rust 导图写入”的明确编排边界:附件解析仍在 TS/MinerU,但导图应用已改走 Rust `mindmap_apply_ops`。
|
||||
- `oo_*` 继续保持客户端插件边界,不再尝试下沉为第二套服务端真执行入口。
|
||||
|
||||
### 仍是 `TS_COMPAT_PENDING`
|
||||
|
||||
- `wolai-frontend/src/app/api/mindmap-ai/agent/route.ts`
|
||||
- `wolai-frontend/src/app/api/mindmap-ai/assets/route.ts`
|
||||
- `wolai-frontend/src/app/api/mindmap-ai/expand-node/route.ts`
|
||||
- `wolai-frontend/src/app/api/mindmap-ai/outline-to-mindmap/route.ts`
|
||||
|
||||
处理结论:
|
||||
|
||||
- AI builtins 主链已经完成从 `TS_COMPAT_PENDING` 到 `RUST_OWNER` / `TS_TRANSPORT_KEEP` 的最终归类。
|
||||
- `mindmap-ai/**` 若继续保留,必须明确只是上层产品编排,不能成为对象规则真入口。
|
||||
- AI 工具矩阵与第一批旧面删除清单分别见:
|
||||
- `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/ai-tool-cutover-matrix.md`
|
||||
- `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-legacy-delete-list.md`
|
||||
|
||||
---
|
||||
|
||||
## 3.5 对象域
|
||||
|
||||
### Mindmap:已进入 `RUST_OWNER`
|
||||
|
||||
- `wolai-frontend/src/app/api/mindmap/[docId]/route.ts`
|
||||
- `wolai-frontend/src/app/api/mindmap/[docId]/[mindmapId]/route.ts`
|
||||
|
||||
说明:
|
||||
|
||||
- `GET/POST` 之前已在 Rust query/command 主链上。
|
||||
- 2026-04-15 起,`DELETE` 与 `PATCH(action=restore|purge)` 也已补齐 Rust runtime/transport 映射,不再由 route 直接调用 Convex mutation。
|
||||
|
||||
### Mindmap:仍是 `TS_COMPAT_PENDING`
|
||||
|
||||
- `wolai-frontend/src/app/api/mindmap-ai/**`
|
||||
|
||||
### Mindmap:已进入 `RUST_OWNER` 的补充兼容入口
|
||||
|
||||
- `wolai-frontend/src/app/api/mindmap-trash/empty/route.ts`
|
||||
|
||||
说明:
|
||||
|
||||
- `mindmap-trash/empty` 现已切到 `mindmaps.emptyTrashByWorkspace` Rust runtime/transport。
|
||||
- 该 route 仍保留为 Web transport 壳,但不再直接持有 TS 真执行。
|
||||
|
||||
### OnlyOffice:已进入 `TS_TRANSPORT_KEEP`
|
||||
|
||||
- `wolai-frontend/src/app/api/onlyoffice/sign/route.ts`
|
||||
- `wolai-frontend/src/app/api/onlyoffice/proxy/route.ts`
|
||||
- `wolai-frontend/src/app/api/onlyoffice/callback/route.ts`
|
||||
- `wolai-frontend/src/app/api/onlyoffice/forcesave/route.ts`
|
||||
|
||||
说明:
|
||||
|
||||
- 这四条路由因为 JWT、proxy、下载上传、第三方回调等原因会长期保留
|
||||
- 但对象规则、session 边界、签名、回写准备、forcesave 计划已进入 Rust adapter
|
||||
|
||||
处理结论:
|
||||
|
||||
- OnlyOffice 当前不是“可删除 route”,而是“保留 route 壳、禁止再长业务规则”
|
||||
|
||||
---
|
||||
|
||||
## 4. Phase 8 Cutover Gate
|
||||
|
||||
只有下面四组 gate 同时满足,才能宣布“Rust 成为唯一业务执行平面”。
|
||||
|
||||
### Gate A:页面、块、查询主链全部 Rust 持有
|
||||
|
||||
要求:
|
||||
|
||||
- `documents.*` 主链不再存在 TS 业务路由
|
||||
- `blocks.*` 主链不再存在第二套执行逻辑
|
||||
- `search.documents`、`sidebar.dataset.list`、`bridge request/trace/command` 全部走 Rust query/runtime
|
||||
|
||||
### Gate B:对象域只保留 transport 壳
|
||||
|
||||
要求:
|
||||
|
||||
- Mindmap 主读写统一进入 Rust adapter
|
||||
- OnlyOffice 只保留签名、代理、回调、forcesave 等 transport 壳
|
||||
- 任何对象域规则都不再从 route 直连 Convex mutation 发展
|
||||
|
||||
### Gate C:AI 与 CLI 共用同一工具面
|
||||
|
||||
要求:
|
||||
|
||||
- `ai-agent/run` 不再为核心工具保留 `builtins/**` 私有真入口
|
||||
- CLI 与 AI 调用同一组 Rust `Tool`
|
||||
- `event_replay`、`index_rebuild`、`bridge_*_get` 这组观测/恢复命令对 CLI、AI、Web 等价可用
|
||||
- `docs_search`、`docs_read`、`search_web`、`image_read`、`slash_run` 必须保持 `RUST_OWNER`
|
||||
|
||||
### Gate D:旧 TS 兼容面完成物理退役或明确降级
|
||||
|
||||
要求:
|
||||
|
||||
- `create-child`、`embed`、`empty-trash`、`purge`、`template`、`mindmap-trash/empty` 等旧接口要么迁入 Rust,要么标记为废弃并从 UI 脱钩
|
||||
- `mindmap-ai/**` 若继续保留,必须明确只是上层产品编排,不能成为对象规则真入口
|
||||
- 旧 `docTools` / `mindmapTools` / 页面私有写入胶水不再允许继续扩展
|
||||
|
||||
---
|
||||
|
||||
## 5. 允许删除与禁止删除
|
||||
|
||||
## 5.1 现在就禁止继续扩展的旧面
|
||||
|
||||
- `documents/create-child`
|
||||
- `documents/embed`
|
||||
- `documents/empty-trash`
|
||||
- `documents/purge`
|
||||
- `documents/template`
|
||||
- `mindmap-trash/empty`
|
||||
- `mindmap-ai/**` 私有写入逻辑
|
||||
- `ai-agent` 中非 Rust 的核心编辑工具真入口
|
||||
|
||||
规则:
|
||||
|
||||
- 这些路径可以暂时存在
|
||||
- 但不允许再加新业务逻辑
|
||||
- 第一批 `TS_LEGACY_DELETE` 冻结清单见 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-legacy-delete-list.md`
|
||||
|
||||
## 5.2 完成 cutover 后允许删除的旧面
|
||||
|
||||
- 上述旧接口本身
|
||||
- 被 Rust adapter 替代后的旧 `builtins/**` 服务端工具实现
|
||||
- 任何只为过渡期保留的直接 Convex 业务拼接 helper
|
||||
|
||||
当前已冻结的第一批删除对象:
|
||||
|
||||
- `documents/create-child`
|
||||
- `documents/embed`
|
||||
- `documents/empty-trash`
|
||||
- `documents/purge`
|
||||
- `documents/template`
|
||||
- `mindmap-trash/empty`
|
||||
- `builtins/**`
|
||||
|
||||
## 5.3 必须长期保留的壳
|
||||
|
||||
- `/api/onlyoffice/sign`
|
||||
- `/api/onlyoffice/proxy`
|
||||
- `/api/onlyoffice/callback`
|
||||
- `/api/onlyoffice/forcesave`
|
||||
- `/api/ai-agent/client-tool-result`
|
||||
- 其他承担 auth/session/streaming/proxy/callback 的 Web route
|
||||
|
||||
这些接口可以保留,但只能承担 Web transport 责任。
|
||||
|
||||
---
|
||||
|
||||
## 6. 最终宣布口径
|
||||
|
||||
只有当本文第 4 节四组 gate 在主链范围内同时满足时,才允许对外使用下面这句话:
|
||||
|
||||
> **Rust 已成为 mnote 的唯一业务执行平面;Web route 只保留 transport、auth、session、streaming、proxy、callback 与少量明确冻结的产品编排壳。**
|
||||
|
||||
截至 2026-04-16:
|
||||
|
||||
- `docs_search`、`docs_read`、`search_web`、`image_read`、`slash_run` 已满足 Gate C 的主链要求。
|
||||
- `bridge-log/runtime` 已补齐统一失败态、冲突态与补偿态的状态语义,页面主写链与 AI/Mindmap 写链已接入统一失败落账。
|
||||
- `mnote-cli` 已通过仓库内真实执行 smoke,CLI 与 AI 现在可以共用同一组 Rust Tool / runtime。
|
||||
- `TS_TRANSPORT_KEEP`、`TS_COMPAT_PENDING`、`TS_LEGACY_DELETE` 三类边界已经完成最终冻结,可作为后续发布与退役口径。
|
||||
|
||||
现在可以统一表述为:
|
||||
|
||||
> **Rust 已成为 mnote 的主业务执行平面;仍保留的 TS 代码只承担 transport、外部服务编排、客户端桥与冻结兼容壳责任。**
|
||||
@@ -0,0 +1,113 @@
|
||||
# [recycle] Rust 内核最终收尾详细清单
|
||||
|
||||
> 更新时间:2026-04-16
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-cutover-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/release-readiness-source-audit.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-legacy-delete-list.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/ai-tool-cutover-matrix.md`
|
||||
|
||||
## 1. 当前结论
|
||||
|
||||
截至 2026-04-16,上一轮最终收尾里剩下的 CLI、AI runtime 与文档口径尾项已经闭合。
|
||||
|
||||
现在这份清单不再是“还缺什么”,而是给出最终关闭结果:
|
||||
|
||||
- 第一批 `TS_LEGACY_DELETE` 已经被压缩为“纯 transport 壳 / 冻结 helper / 明确删除清单”,不再允许继续长出第二套业务语义。
|
||||
- 页面、块、Mindmap、OnlyOffice 与 AI 工具主链已经完成最终收口,不再存在主链级别的“Rust 包装 + TS 真执行”假完成状态。
|
||||
- `mnote-cli` 已进入最小真实执行态,并且有可复跑的仓库内 smoke。
|
||||
- AI 工具矩阵里的 builtins 主链已经完成最终归类,不再保留模糊的 builtins 级 `TS_COMPAT_PENDING`。
|
||||
- 发布前运行态复核证据已经补齐,可以给出统一最终口径。
|
||||
|
||||
---
|
||||
|
||||
## 2. 尾项关闭结果
|
||||
|
||||
### 2.1 第一批 `TS_LEGACY_DELETE`
|
||||
|
||||
当前状态:已关闭为“冻结清单 + 纯壳边界”。
|
||||
|
||||
结果:
|
||||
|
||||
- `documents/create-child`、`documents/empty-trash`、`documents/purge`、`documents/template` 已进入统一 Rust transport / adapter 主链。
|
||||
- `mindmap-trash/empty` 已进入 Rust runtime / transport 主链。
|
||||
- `builtins/**` 中已被 Rust runtime 接管的服务端真入口,不再允许继续作为主执行面扩展。
|
||||
- `documents/embed` 与 `mindmap-ai/**` 这类仍保留的上层编排路径,必须按冻结边界维护,不能反向长出新的核心业务规则。
|
||||
|
||||
验收结论:第一批旧面已经不再阻塞“Rust 成为主业务执行平面”的宣布口径。
|
||||
|
||||
### 2.2 双路径执行清理
|
||||
|
||||
当前状态:已关闭。
|
||||
|
||||
结果:
|
||||
|
||||
- 页面主写链、块主写链、Mindmap 删除/恢复/清空回收站、OnlyOffice callback/forcesave 均已切到 Rust command/query/tool/runtime 主链。
|
||||
- 旧 TS 侧保留的仅是 transport、外部服务编排或历史兼容 helper,不再是现网主真执行入口。
|
||||
- `src/lib/documents/block-command-adapter.ts` 明确被冻结为遗留兼容 helper;现网 `/api/blocks/*` 统一走 `src/lib/blocks/block-command-adapter.ts`。
|
||||
|
||||
### 2.3 CLI 最小真实执行态
|
||||
|
||||
当前状态:已关闭。
|
||||
|
||||
结果:
|
||||
|
||||
- `mnote-cli` 新增并稳定支持 `--execute` 真实执行模式。
|
||||
- 已修复 `block patch --execute` 的块树回写形状问题,`block move --execute` 不再因为内容快照损坏而失败。
|
||||
- 仓库内最小 smoke 已扩展到:
|
||||
- `sidebar dataset`
|
||||
- `page get/create/title/save/move/delete/restore`
|
||||
- `block insert/patch/move/embed`
|
||||
- `search documents`
|
||||
- `tool run docs_search/docs_read`
|
||||
- `mindmap put/get/op`
|
||||
- 运行命令:`bash /mnt/Data1T/mnote/rust/scripts/task049-cli-smoke.sh`
|
||||
- 验收产物会落盘到 `/tmp/task049-cli-smoke-*`,便于失败后回查逐步 JSON 输出。
|
||||
|
||||
### 2.4 AI 工具矩阵收口
|
||||
|
||||
当前状态:已关闭。
|
||||
|
||||
结果:
|
||||
|
||||
- `docs_search`、`docs_read` 已进入 Rust tool runtime;Convex 主链下 `ai-agent/run` 直接通过 `executeRustBridgeTool(...)` 执行,`docsServerTools` 仅保留 fallback。
|
||||
- `search_web`、`image_read`、`slash_run` 持续保持 `RUST_OWNER`。
|
||||
- `asset_extract_outline`、`asset_to_mindmap`、`oo_*` 已明确归类为 `TS_TRANSPORT_KEEP`,不再保留 builtins 级 `TS_COMPAT_PENDING`。
|
||||
- `asset_to_mindmap` 的真正导图写入已收口到 Rust `mindmap_apply_ops`;TS 侧仅保留外部解析与编排壳。
|
||||
|
||||
### 2.5 发布前运行态复核
|
||||
|
||||
当前状态:已关闭。
|
||||
|
||||
截至 2026-04-16,已经具备以下运行态证据:
|
||||
|
||||
- 浏览器回归:`scripts/task019-document-ui-regression.js`
|
||||
- 浏览器回归:`scripts/task021-mindmap-ui-regression.js`
|
||||
- 浏览器回归:`scripts/task022-onlyoffice-ui-regression.js`
|
||||
- AI runtime 证据:`pnpm exec vitest run src/lib/ai-agent/runtime/runAgent.test.ts`
|
||||
- CLI 主链证据:`bash /mnt/Data1T/mnote/rust/scripts/task049-cli-smoke.sh`
|
||||
- Rust 执行层证据:`cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p mnote-cli -p bridge-runtime`
|
||||
|
||||
补充说明:定向 `eslint` 当前无 error,仅剩仓库既有 warnings,不构成这轮收尾阻塞项。
|
||||
|
||||
---
|
||||
|
||||
## 3. 最终完成标准核对
|
||||
|
||||
现在已经同时满足下面几条:
|
||||
|
||||
- 第一批 `TS_LEGACY_DELETE` 已物理删除或明确冻结为不可扩展壳。
|
||||
- 仓库里不再存在主链级别的“Rust 包装 + TS 真执行”双路径假完成状态。
|
||||
- `mnote-cli` 已覆盖一批关键主链的真实执行,并具备仓库内可复跑 smoke。
|
||||
- AI 工具矩阵中的 builtins 主链已不再保留模糊的 `TS_COMPAT_PENDING`。
|
||||
- 发布前运行态复核已经重新通过。
|
||||
- 文档口径与代码现状可以统一到同一个最终结论。
|
||||
|
||||
---
|
||||
|
||||
## 4. 一句话结论
|
||||
|
||||
现在可以把最后一阶段的结论收口为:
|
||||
|
||||
> **Rust 已成为 mnote 的主业务执行平面;Web / TS 侧仅保留 transport、外部服务编排、客户端桥与明确冻结的兼容壳。**
|
||||
@@ -0,0 +1,73 @@
|
||||
# [recycle] Rust Kernel Legacy Delete List
|
||||
|
||||
> 目标:给 Phase 8 第一批 `TS_LEGACY_DELETE` 提供固定清单,先停写并切调用方,再删除已被 Rust 替代的 TS 真入口。
|
||||
|
||||
## 1. 第一批删除对象
|
||||
|
||||
- `documents/create-child`
|
||||
- `documents/embed`
|
||||
- `documents/empty-trash`
|
||||
- `documents/purge`
|
||||
- `documents/template`
|
||||
- `mindmap-trash/empty`
|
||||
- `builtins/**`
|
||||
|
||||
## 2. 删除前提
|
||||
|
||||
- 页面旧面必须已经切到统一 page adapter 或 lifecycle adapter,route 仅剩 transport。
|
||||
- AI 核心工具必须已经切到 Rust Tool runtime 或 `ai-agent/run` 统一入口,不能再由 `builtins/**` 私下持有真执行面。
|
||||
- 统一 bridge-log 必须能记录失败态、冲突态与补偿态,避免删除旧面后失去回查能力。
|
||||
|
||||
## 3. 删除策略
|
||||
|
||||
### 3.1 页面旧面
|
||||
|
||||
- `documents/create-child`
|
||||
- `documents/embed`
|
||||
- `documents/empty-trash`
|
||||
- `documents/purge`
|
||||
- `documents/template`
|
||||
|
||||
策略:
|
||||
|
||||
- 先冻结 route 内业务逻辑,确保所有调用方都走 adapter。
|
||||
- 保留 Web route 的 auth、参数校验和 HTTP transport。
|
||||
- 先继续清理 `page-command-adapter.ts` / `page-lifecycle-command-adapter.ts` 中残留的 TS 真执行。
|
||||
- 在前端调用方仍直接依赖这些 route,且 adapter 仍承载真实写入前,不得直接物理删除 route 文件。
|
||||
- 当 release audit 与 cutover gate 均确认无回退需求后,再物理删除历史 helper/分支。
|
||||
|
||||
### 3.2 Mindmap 旧面
|
||||
|
||||
- `mindmap-trash/empty`
|
||||
|
||||
策略:
|
||||
|
||||
- `mindmap-trash/empty` 现已切到 `mindmaps.emptyTrashByWorkspace` Rust runtime/transport。
|
||||
- 该 route 仍是兼容入口,但只保留 Web transport 壳,不再直接持有 TS 真执行。
|
||||
- 仍需确认 `task-042` 的失败/冲突/补偿落账和最终运行态回归稳定,再删除纯兼容分支。
|
||||
|
||||
### 3.3 AI 工具旧面
|
||||
|
||||
- `builtins/**`
|
||||
|
||||
策略:
|
||||
|
||||
- `search_web`、`image_read`、`slash_run` 这类已切到 Rust runtime 的工具,删除其服务端真入口,只保留必要 transport helper。
|
||||
- `client-tool-result` 与 `oo_*` 属于 `TS_TRANSPORT_KEEP`,不在本批物理删除范围。
|
||||
- `asset_extract_outline`、`asset_to_mindmap` 尚未完全 Rust 化,暂不从 `builtins/**` 整体删除,但必须禁止继续扩展新的私有真入口。
|
||||
|
||||
## 4. 当前结论
|
||||
|
||||
- 第一批 `TS_LEGACY_DELETE` 已完成清单冻结。
|
||||
- `documents/create-child`、`documents/embed`、`documents/empty-trash`、`documents/purge`、`documents/template`、`mindmap-trash/empty`、`builtins/**` 已被明确纳入 Phase 8 的旧面退役范围。
|
||||
- 截至 2026-04-15,这批对象中还没有可直接物理删除的 route 文件;当前可安全推进的是:
|
||||
- 把 route 固定为纯 transport 壳。
|
||||
- 把 adapter 内残留的 TS 真执行继续收口。
|
||||
- 为每个旧面补齐调用方清单与“可删前置条件”。
|
||||
- 其中页面旧面已完成第一轮收口:
|
||||
- `create-child` 已复用 `documents.create`
|
||||
- `template`、`empty-trash`、`purge` 已补齐 Rust runtime/transport 映射
|
||||
- `embed` 已切到 `documents.save`,但插入位计算与 `pageReference` block 组装仍在 TS
|
||||
- 因此页面旧面的主要残留已缩到 `wolai-frontend/src/lib/documents/page-command-adapter.ts` 中的 `embed` 内容编排,以及 `wolai-frontend/src/lib/documents/page-lifecycle-command-adapter.ts` 的历史兼容面。
|
||||
- `mindmap-trash/empty` 已不再由 route 直接调用 `api.mindmaps.emptyTrashByWorkspace`,但在调用方与最终运行态回归全部通过前,仍不满足物理删除条件。
|
||||
- 后续删除动作必须以 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-cutover-v1.md` 和 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/release-readiness-source-audit.md` 的最终 gate 为准。
|
||||
@@ -0,0 +1,110 @@
|
||||
# [recycle] mnote 单仓维护边界约定
|
||||
|
||||
> 更新时间:2026-04-14
|
||||
>
|
||||
> 主仓:`/mnt/Data1T/mnote`
|
||||
>
|
||||
> 历史仓:`/mnt/Data1T/mnote-rust`
|
||||
|
||||
## 1. 主结论
|
||||
|
||||
- 只保留 `/mnt/Data1T/mnote` 作为启动、规划、保存、验证的唯一主仓。
|
||||
- `/mnt/Data1T/mnote-rust` 只作为历史资产来源,不再承担主执行入口。
|
||||
- 任何新设计、执行清单、架构说明都优先写入 `/mnt/Data1T/mnote/design/` 或 `/mnt/Data1T/mnote/rust/design/`。
|
||||
|
||||
## 2. 主仓执行入口
|
||||
|
||||
- 根目录脚本入口只认 `/mnt/Data1T/mnote/package.json` 与 `/mnt/Data1T/mnote/scripts/`。
|
||||
- 当前 `package.json` 仅通过 `node scripts/*.js` 暴露仓库级入口,没有任何脚本要求先进入 `mnote-rust`。
|
||||
- 如需调用 Rust,统一使用 `cargo --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml ...`,不从历史仓发起 Cargo 命令。
|
||||
- 当前 `scripts/desktop-hot.js` 已固定从主仓根目录解析 `.env.all`,并以 `wolai-frontend/`、`wolai-backend/` 作为唯一联调入口。
|
||||
|
||||
## 3. `mnote-rust` 历史资料分类
|
||||
|
||||
### 3.1 核心规范
|
||||
|
||||
这些内容可作为协议与设计参考,但主线副本以 `/mnt/Data1T/mnote/rust/` 为准:
|
||||
|
||||
- `design/blueprint/`
|
||||
- `design/core/`
|
||||
- `crates/core-domain/`
|
||||
- `crates/core-protocol/`
|
||||
- `crates/event-log/`
|
||||
- `crates/storage-convex-bridge/`
|
||||
- `crates/index-fts/`
|
||||
|
||||
### 3.2 历史阶段
|
||||
|
||||
这些内容保留为阶段记录,不作为当前实现依据:
|
||||
|
||||
- `design/phases/**`
|
||||
- `design/execution/**`
|
||||
- `design/UI/**`
|
||||
- `design/mindmap/**`
|
||||
|
||||
### 3.3 参考实现
|
||||
|
||||
这些内容可用于盘点边界、提取协议或核对交互,但不能整块复制覆盖主仓:
|
||||
|
||||
- `app/**`
|
||||
- `components/**`
|
||||
- `lib/**`
|
||||
- `convex/**`
|
||||
- `infra/onlyoffice/**`
|
||||
- `crates/mnote-cli/`
|
||||
- `crates/adapter-onlyoffice/`
|
||||
- `crates/adapter-mindmap/`
|
||||
- `crates/adapter-legacy-mnote/`
|
||||
|
||||
### 3.4 错误方向
|
||||
|
||||
以下内容明确不进入主仓主线:
|
||||
|
||||
- 对象页壳与试验页:`app/page.tsx`、`app/onlyoffice/page.tsx`、`app/documents/**`
|
||||
- 第二套导航壳:`components/sidebar/sidebar.tsx`
|
||||
- 诊断与构建产物:`.next/diagnostics/**`、`.next/**`、`.tmp/**`
|
||||
- 仅用于历史 smoke/夹具的目录:`.tmp/phase7-test-fixtures/**`
|
||||
|
||||
## 4. 主仓默认不动目录
|
||||
|
||||
除非当前任务明确要求接线或修复,否则默认不扩写以下目录:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/`
|
||||
- `/mnt/Data1T/mnote/wolai-backend/`
|
||||
- `/mnt/Data1T/mnote/infra/convex/`
|
||||
- `/mnt/Data1T/mnote/src/components/onlyoffice/`
|
||||
- `/mnt/Data1T/mnote/recycle/`
|
||||
|
||||
## 5. 最小维护约定
|
||||
|
||||
### 5.1 Workspace 责任面
|
||||
|
||||
- 入口文件:`/mnt/Data1T/mnote/rust/Cargo.toml`、`/mnt/Data1T/mnote/rust/Cargo.lock`
|
||||
- 维护范围:`/mnt/Data1T/mnote/rust/crates/*`
|
||||
- 要求:新增 crate、依赖调整、共享协议变更,必须先在主仓 workspace 内完成,不得回写到历史仓再反向复制。
|
||||
|
||||
### 5.2 Bridge 责任面
|
||||
|
||||
- 入口文件:`/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/bridge.ts`
|
||||
- 相关边界:
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/metadata-command-adapter.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/save-command-adapter.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/**`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/sidebar/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/convex/bridgeLogs.ts`
|
||||
- 要求:新 command/query 先收口协议边界,再决定是否继续下沉到 Rust crate。
|
||||
|
||||
### 5.3 接入链责任面
|
||||
|
||||
- C1 页面元信息:`src/app/(app)/documents/[id]/page.tsx`、`src/components/editor/document-content.tsx`
|
||||
- C2 Sidebar 聚合:`src/components/sidebar/**`、`src/hooks/use-convex-sidebar-data.ts`、`src/lib/sidebar-data.ts`
|
||||
- C3 正文保存:`src/components/editor/blocknote-editor.tsx`、`src/app/api/documents/save/route.ts`
|
||||
- Phase D 边界:`src/components/editor/blocks/MindmapBlock.tsx`、`src/app/onlyoffice/**`、`src/app/api/onlyoffice/**`
|
||||
- 要求:优先改主仓现有链路,不复制历史仓页面壳替换现实现。
|
||||
|
||||
## 6. 禁止误复制清单
|
||||
|
||||
- 不把 `mnote-rust` 当成新的主执行仓。
|
||||
- 不复制第二套 Next 前端壳、对象页壳、diagnostics 页、smoke 页进入主线。
|
||||
- 不通过软链接、硬链接、跨仓路径偷接来伪装“已迁移”。
|
||||
- 不在主仓根目录散落多个 Rust 源码根。
|
||||
@@ -0,0 +1,629 @@
|
||||
# [recycle] AI 前端精简方案 v1
|
||||
|
||||
> 更新时间:2026-04-15
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-final-closure-checklist.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/ai-tool-cutover-matrix.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-cutover-v1.md`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
这份方案只回答一个问题:
|
||||
|
||||
> **当前网页前端中,AI 相关部分应该如何精简,才能真正降低加载负担,并把前端 AI 收口为“轻桥接层”。**
|
||||
|
||||
目标方向已经明确:
|
||||
|
||||
> **前端 AI 面板不再承担工具注册、工具编排、能力路由和执行框架,只保留为 Hermes API Server + mnote Rust 业务能力的轻桥接 UI。**
|
||||
|
||||
这意味着后续 AI 前端不再是一个“小型平台”,而只是:
|
||||
|
||||
- 收集上下文
|
||||
- 发送用户输入
|
||||
- 展示流式结果
|
||||
- 承接极少数浏览器专属 client tool
|
||||
|
||||
## 1.1 现状校准:当前真正已经落地的后端 AI 边界
|
||||
|
||||
在继续谈“前端该怎么精简”之前,必须先对齐一个事实:
|
||||
|
||||
> **当前本机已经有可核验的 Hermes Agent 与其官方 API Server 方案;同时 mnote 自己也已经有 Rust protocol/runtime 边界。后续前端 AI 应该桥接这两层,而不是自己继续承担平台层。**
|
||||
|
||||
当前已确认的 Hermes 事实:
|
||||
|
||||
- 本机 Hermes 安装目录:`/home/lix/.hermes`
|
||||
- Hermes 本体仓:`/home/lix/.hermes/hermes-agent`
|
||||
- 当前网关进程已在运行:`hermes gateway run --replace`
|
||||
- 官方文档已提供 OpenAI 兼容 API Server:
|
||||
- 启用方式:`API_SERVER_ENABLED=true`
|
||||
- 默认监听:`http://127.0.0.1:8642`
|
||||
- 入口:`/v1/chat/completions`、`/v1/responses`、`/health`
|
||||
|
||||
但当前本机状态也要说明白:
|
||||
|
||||
- Hermes gateway 在跑
|
||||
- Hermes API Server 当前还没有启用
|
||||
- mnote 前端当前也还没有接通 Hermes API Server
|
||||
|
||||
因此,现阶段不是“是否存在 Hermes”的问题,而是:
|
||||
|
||||
- **Hermes 已存在,但还没有接入 mnote Web AI 主链**
|
||||
- **mnote Rust 业务能力已存在,但还没有作为 Hermes 的统一业务工具面完全暴露**
|
||||
|
||||
当前 mnote 已能明确核验到的 Rust 业务边界是:
|
||||
|
||||
- `rust/crates/core-protocol/src/tool.rs`
|
||||
已定义 `ToolSpec`、`ToolSetSpec`、`ToolRegistry`,并冻结了 `toolset.readonly`、`toolset.media_read`、`toolset.doc_read`、`toolset.doc_write`、`toolset.mindmap_read`、`toolset.mindmap_write`、`toolset.onlyoffice_service`、`toolset.slash_write` 等基础集合。
|
||||
- `rust/crates/bridge-runtime/src/lib.rs`
|
||||
已提供统一 `RuntimeInput::{Tool, Query, Command}` 入口,支持 `plan`、`result`、`explain-plan`、`validateOnly`、`dryRun` 等运行模式,并输出统一计划结构。
|
||||
- `rust/crates/mnote-cli/README.md`
|
||||
已冻结 `tool run` 的 JSON 契约,说明 CLI 化目标已经开始按稳定协议推进。
|
||||
- `wolai-frontend/src/lib/documents/rust-runtime.ts`
|
||||
当前前端已经可以通过 `executeRustBridgeTool()` 直接调用 Rust `bridge-runtime`,说明 Web 并不是从零开始接 Rust。
|
||||
|
||||
结合 `ai-tool-cutover-matrix.md` 与 `run/route.ts`,当前可以按下面口径理解能力归属:
|
||||
|
||||
- 已有明确 Rust owner 或 Rust 主入口的能力:
|
||||
`search_web`、`image_read`、`slash_run`、`doc_*`、`mindmap_*`、`onlyoffice_* service`
|
||||
- 仍暂时保留在 TS transport 或兼容层的能力:
|
||||
`docs_search`、`docs_read`、`rag_lightrag_query`、`asset_extract_outline`、`asset_to_mindmap`、`oo_*`
|
||||
|
||||
因此,这份方案后续提到的“后移”应理解成:
|
||||
|
||||
- 先把前端收口到 Hermes bridge
|
||||
- 再把 mnote 业务能力通过 Rust 边界继续收口,并作为 Hermes 可调用能力暴露
|
||||
- 最终形成“Hermes 负责 agent runtime,Rust 负责 mnote 业务真执行面,前端只负责 UI 与 client bridge”的结构
|
||||
|
||||
## 1.2 推荐的最终分工
|
||||
|
||||
基于当前仓库和 Hermes 官方能力,推荐的长期结构不是单中心,而是双层分工:
|
||||
|
||||
### A. Hermes 负责什么
|
||||
|
||||
- agent loop
|
||||
- 通用 tool runtime
|
||||
- 多轮会话状态
|
||||
- OpenAI 兼容 API Server
|
||||
- 流式输出与工具调用事件
|
||||
- 通用记忆、skills、MCP、delegate 等 agent 能力
|
||||
|
||||
### B. mnote Rust 负责什么
|
||||
|
||||
- 文档、块、导图、OnlyOffice 等产品业务真执行
|
||||
- 统一 command/query/tool protocol
|
||||
- 审计、trace、request/command/event 口径
|
||||
- CLI 化与稳定 JSON 契约
|
||||
|
||||
### C. mnote Web 前端负责什么
|
||||
|
||||
- 输入框、聊天记录、SSE 展示
|
||||
- document/mindmap/onlyoffice context 采集
|
||||
- 少量浏览器专属 client tool
|
||||
- 必要的鉴权、会话映射和 client-tool-result 回传
|
||||
|
||||
### D. mnote 与 Hermes 的推荐衔接方式
|
||||
|
||||
当前更合理的方向不是让前端直接承接 Hermes 的全部能力,而是:
|
||||
|
||||
- 前端 -> mnote Next route
|
||||
- mnote Next route -> Hermes API Server
|
||||
- Hermes 在需要 mnote 业务操作时,再调用 mnote 暴露给它的 Rust 能力面
|
||||
|
||||
这层“mnote 暴露给 Hermes 的能力面”后续可以落在:
|
||||
|
||||
- MCP server
|
||||
- Hermes plugin/tool adapter
|
||||
- 或 mnote 自己维护的一层最小业务 bridge
|
||||
|
||||
但无论具体接法选哪一种,原则都应一致:
|
||||
|
||||
> **Hermes 不应复制一套 mnote 业务真逻辑;mnote Rust 才是产品业务真执行面。**
|
||||
|
||||
## 1.3 Hermes API Server 调用 mnote Rust 的最小业务桥
|
||||
|
||||
这部分是 task-058 的关键边界:先把“谁调用谁、调用什么、返回什么”说清楚,再决定后续是否补更重的适配层。
|
||||
|
||||
### 最小结论
|
||||
|
||||
Hermes API Server 不应直接接触前端 `/api/ai-agent/run` 的整套 TS 编排逻辑,而应通过一个非常窄的 mnote Rust 业务桥来调用真实能力。
|
||||
|
||||
这个桥只做三件事:
|
||||
|
||||
1. 接收 Hermes 的标准化 tool 调用请求
|
||||
2. 转换成 mnote Rust 的 `RuntimeInput`
|
||||
3. 返回 Rust 的 `plan` 或 `result`
|
||||
|
||||
### 推荐的桥接层级
|
||||
|
||||
- Hermes API Server
|
||||
负责 agent loop、tool 调度、流式输出与会话状态。
|
||||
- mnote Rust bridge
|
||||
负责把 Hermes 的 tool 调用映射到 `core-protocol` / `bridge-runtime`。
|
||||
- mnote 业务执行面
|
||||
负责文档、块、导图、OnlyOffice 等产品能力的真实读写。
|
||||
|
||||
### 最小接口边界
|
||||
|
||||
建议把 Hermes 可调用的 mnote 能力,先收敛成下面三类:
|
||||
|
||||
- `query`
|
||||
只读查询,例如 `page get`、`docs_search`、`docs_read`
|
||||
- `command`
|
||||
写入命令,例如 `block insert`
|
||||
- `tool`
|
||||
复用 Rust tool registry 的能力,例如 `doc_insert_blocks`、`doc_replace_range`
|
||||
|
||||
其中最优先的两条业务能力是:
|
||||
|
||||
- `page get` -> Rust query:`documents.content.get`
|
||||
- `block insert` -> Rust write/tool:`doc_insert_blocks`
|
||||
|
||||
### 当前仓库里已经存在的可复用接缝
|
||||
|
||||
- `rust/crates/core-protocol/src/tool.rs`
|
||||
已经冻结了 `ToolSpec`、`ToolSetSpec`、`ToolRegistry`,并且 `docs_search`、`docs_read`、`doc_insert_blocks` 都已经在工具注册表中。
|
||||
- `rust/crates/bridge-runtime/src/lib.rs`
|
||||
已经提供 `execute_runtime_input()`、`execute_runtime_query()`、`execute_query()`、`execute_command()`、`execute_tool_plan()`、`execute_tool_result()` 这些统一入口。
|
||||
- `rust/crates/core-protocol/src/query.rs`
|
||||
已经有 `GetPageContent`、`SearchDocuments`、`GetBlock` 等查询载体。
|
||||
- `rust/crates/core-protocol/src/command.rs`
|
||||
已经有 `CommandEnvelope` 和 `CommandResult`,说明命令执行结果口径是存在的。
|
||||
- `wolai-frontend/src/lib/documents/rust-runtime.ts`
|
||||
当前前端已经能把 JSON 输入交给 Rust bridge 进程,说明“进程级 Rust bridge”这条路是可复用的。
|
||||
|
||||
### 最小业务桥的推荐形态
|
||||
|
||||
建议 Hermes 侧只认识一个很窄的桥协议,避免再次长成一套前端平台层:
|
||||
|
||||
```text
|
||||
Hermes tool call
|
||||
-> mnote rust bridge input
|
||||
-> Rust plan/result
|
||||
-> Hermes tool result
|
||||
```
|
||||
|
||||
其中输入字段建议至少包含:
|
||||
|
||||
- `toolName`
|
||||
- `invocationKind`
|
||||
- `executionMode`
|
||||
- `args`
|
||||
- `workspaceId`
|
||||
- `target`
|
||||
- `actor`
|
||||
- `source`
|
||||
|
||||
输出字段建议至少包含:
|
||||
|
||||
- `ok`
|
||||
- `kind`
|
||||
- `plan` 或 `result`
|
||||
- `error`(失败时)
|
||||
|
||||
### 最小桥的落地原则
|
||||
|
||||
- 先支持只读桥接,再补写入桥接
|
||||
- 先接 `page get`,再接 `block insert`
|
||||
- 先复用现有 `bridge-runtime`,不要先重写新执行器
|
||||
- 不要让 Hermes 直接依赖前端 `run/route.ts` 的 builtin registry
|
||||
- 不要把能力定义重新散落到多个前端面板里
|
||||
|
||||
### 对当前前端的影响
|
||||
|
||||
前端后续只应保留:
|
||||
|
||||
- 场景上下文采集
|
||||
- SSE/UI 展示
|
||||
- 少量浏览器专属 client tool
|
||||
|
||||
前端不应继续承担:
|
||||
|
||||
- tool registry
|
||||
- tool policy
|
||||
- builtin 装配
|
||||
- provider 编排
|
||||
- Rust 能力路由
|
||||
|
||||
这意味着后续如果要继续减重,应该优先把 `Hermes API Server -> mnote Rust bridge` 这条线做清楚,而不是继续在 Web 里扩充 AI 平台逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前问题
|
||||
|
||||
## 2.1 当前不是一个 AI 面板,而是多套前端 AI 系统
|
||||
|
||||
当前仓库至少存在下面几套 AI UI:
|
||||
|
||||
- 全局 AI:`src/components/ai-agent/AiAgentPanel.tsx`
|
||||
- 全局 Host:`src/components/ai-agent/GlobalAiAgentHost.tsx`
|
||||
- 页面 AI:`src/components/editor/DocumentAiAgentPanel.tsx`
|
||||
- Mindmap AI:`src/components/editor/blocks/MindmapAiAgentPanel.tsx`
|
||||
- OnlyOffice AI:`src/components/onlyoffice/OnlyOfficeAiAgentPanel.tsx`
|
||||
|
||||
这些面板不只是视觉上重复,而是每一套都各自持有一大批前端状态和交互逻辑,例如:
|
||||
|
||||
- 对话 session/history
|
||||
- SSE 解析
|
||||
- tool logs
|
||||
- provider/model 选择
|
||||
- localStorage 持久化
|
||||
- 工具白名单/手动选择
|
||||
- codex mode
|
||||
- 中止/恢复交互
|
||||
|
||||
这会持续拉高:
|
||||
|
||||
- 前端代码体积
|
||||
- 客户端状态复杂度
|
||||
- 维护成本
|
||||
- 页面加载后的水合成本
|
||||
|
||||
## 2.2 `/api/ai-agent/run` 当前仍是前端侧 AI 编排中心
|
||||
|
||||
当前 `src/app/api/ai-agent/run/route.ts` 仍承担了大量本应属于统一后端 AI 服务的职责:
|
||||
|
||||
- scope -> toolset 映射
|
||||
- builtin registry 构建
|
||||
- allowed tools 解析
|
||||
- 各类 server tools 装配
|
||||
- codex 与本地/在线 provider 多分支逻辑
|
||||
- client tool bridge 协调
|
||||
- 一部分 Rust tool 执行接线
|
||||
- 一部分 TS builtin fallback
|
||||
|
||||
这意味着:
|
||||
|
||||
- 前端 Next route 仍然是 AI 平台层
|
||||
- AI 执行面没有完全后移
|
||||
- AI 相关复杂度还在 Web 进程里增长
|
||||
|
||||
## 2.3 builtin tool registry 仍保留为前端框架资产
|
||||
|
||||
当前仍保留:
|
||||
|
||||
- `src/lib/ai-agent/tools/registry.ts`
|
||||
- `src/lib/ai-agent/tools/builtins/registryBuiltins.ts`
|
||||
- `src/lib/ai-agent/runtime/runAgent.ts`
|
||||
- 多个 `create*ServerTools`
|
||||
|
||||
这说明:
|
||||
|
||||
- 前端不只是“调 AI”
|
||||
- 前端还在“定义 AI 能干什么、怎么调、怎么路由”
|
||||
|
||||
这与“前端只桥接 Hermes + mnote Rust”的方向相冲突。
|
||||
|
||||
## 2.4 当前 AI 能力边界仍分散在多个场景面板里
|
||||
|
||||
例如:
|
||||
|
||||
- 页面 AI 直接持有 `doc_*` 工具集合与文档快照
|
||||
- Mindmap AI 直接持有 mindmap tool 集与附件选择
|
||||
- OnlyOffice AI 直接持有 `oo_*` client tool 协议
|
||||
- 全局 AI 又有自己的 toolset chips 和 capability 展示
|
||||
|
||||
这意味着“场景上下文”与“工具编排”没有分离。
|
||||
|
||||
更合理的结构应该是:
|
||||
|
||||
- 场景只提供 context
|
||||
- agent 编排由 Hermes 决定
|
||||
- mnote 业务执行由 Rust 决定
|
||||
- 前端只负责 UI 与极少数 client capability
|
||||
|
||||
---
|
||||
|
||||
## 3. 精简总原则
|
||||
|
||||
## 3.1 前端 AI 只保留三类职责
|
||||
|
||||
后续前端 AI 只应保留:
|
||||
|
||||
### A. 轻 UI
|
||||
|
||||
- 输入框
|
||||
- 聊天记录
|
||||
- 流式输出
|
||||
- 打断/继续
|
||||
- 极少量面板开关
|
||||
|
||||
### B. 场景上下文采集
|
||||
|
||||
- 当前 documentId
|
||||
- 当前 mindmapId
|
||||
- 当前 onlyoffice 文件信息
|
||||
- 当前 selection/block snapshot
|
||||
|
||||
### C. 浏览器专属 client tool
|
||||
|
||||
例如:
|
||||
|
||||
- OnlyOffice 插件回调
|
||||
- 浏览器本地文件/剪贴板
|
||||
- 未来确实只能在浏览器执行的少数能力
|
||||
|
||||
除此之外,前端不应继续承担:
|
||||
|
||||
- tool registry
|
||||
- tool policy
|
||||
- builtin 分类
|
||||
- tool routing
|
||||
- AI orchestration
|
||||
- 多 provider 执行框架
|
||||
|
||||
## 3.2 AI 面板本身不再按功能域复制实现
|
||||
|
||||
最终应从“多个重面板”收口到:
|
||||
|
||||
- 一个通用 `AiBridgePanel`
|
||||
- 多个轻量 context adapter
|
||||
|
||||
即:
|
||||
|
||||
- `GlobalAiEntry`
|
||||
- `DocumentAiEntry`
|
||||
- `MindmapAiEntry`
|
||||
- `OnlyOfficeAiEntry`
|
||||
|
||||
这些 entry 只负责传不同 context,不再复制整套面板逻辑。
|
||||
|
||||
## 3.3 前端不再维护 AI 工具产品说明体系
|
||||
|
||||
像下面这些内容,不应再长期保留在前端:
|
||||
|
||||
- tool labels
|
||||
- tool chips
|
||||
- capability 展示矩阵
|
||||
- per-scope tool lists
|
||||
|
||||
这些都属于后端 AI 能力描述的一部分,应由 Hermes 返回,或由 mnote 后端统一下发,而不是继续硬编码在前端。
|
||||
|
||||
---
|
||||
|
||||
## 4. 建议的精简方案
|
||||
|
||||
## Phase A:后移 AI 编排层到 Hermes,并给 mnote Rust 留清晰业务边界
|
||||
|
||||
第一步不是删 UI,而是把重逻辑后移。
|
||||
|
||||
### 需要后移的内容
|
||||
|
||||
- `createToolRegistry`
|
||||
- `resolveAllowedToolIds`
|
||||
- `builtinTools`
|
||||
- `builtinToolSets`
|
||||
- `runAiAgent`
|
||||
- 各类 `create*ServerTools`
|
||||
- scope -> toolset 的静态映射逻辑
|
||||
|
||||
### 目标结构
|
||||
|
||||
前端 `/api/ai-agent/run` 只保留:
|
||||
|
||||
- 鉴权
|
||||
- 规范化 `scope/messages/context/attachments/clientCapabilities`
|
||||
- 把请求转发给 Hermes API Server
|
||||
- 把 Hermes 的 SSE / `tool_call` / `tool_result` 回流给前端面板
|
||||
- 转发 client tool call/result
|
||||
- 在 Hermes 需要调用 mnote 业务能力时,转给 mnote Rust 暴露出来的业务能力面
|
||||
- 对少数短期无法迁走的 `TS_TRANSPORT_KEEP` 能力保留最小兼容壳
|
||||
|
||||
换句话说:
|
||||
|
||||
> `/api/ai-agent/run` 从“AI 编排器”降级为“AI 网关”。`
|
||||
|
||||
### 收益
|
||||
|
||||
- 显著降低 Next route 的 AI 编排复杂度
|
||||
- agent runtime 与 mnote 业务执行面彻底分层
|
||||
- 前端后续不必再继续长 builtins、registry 和 provider 编排
|
||||
- 后续若继续 CLI 化,也能直接复用 Hermes API Server 与 mnote Rust 协议边界
|
||||
|
||||
### Phase A 的现实限制
|
||||
|
||||
这一阶段不能简单理解为“删除所有 TS builtins 就结束”。
|
||||
|
||||
因为当前还有两类事情没有完全打通:
|
||||
|
||||
- Hermes API Server 还没在本机正式启用并接入 mnote
|
||||
- mnote Rust 业务能力还没全部以 Hermes 可调用的方式暴露
|
||||
|
||||
所以 Phase A 的实际目标应是:
|
||||
|
||||
- 先让 `/api/ai-agent/run` 从“自己编排”变成“转发 + 桥接”
|
||||
- 再逐步清理遗留 builtin
|
||||
- 不是一刀切直接删除所有中间层
|
||||
|
||||
## Phase B:统一 AI 面板实现
|
||||
|
||||
在后移编排层之后,再收 UI。
|
||||
|
||||
### 当前问题
|
||||
|
||||
四套 AI 面板都在重复维护:
|
||||
|
||||
- 对话
|
||||
- 工具日志
|
||||
- provider/model
|
||||
- localStorage
|
||||
- 中断/恢复
|
||||
|
||||
### 目标结构
|
||||
|
||||
新增统一通用面板,例如:
|
||||
|
||||
- `AiBridgePanel`
|
||||
|
||||
再由不同场景只提供轻量包装:
|
||||
|
||||
- `DocumentAiEntry`
|
||||
- `MindmapAiEntry`
|
||||
- `OnlyOfficeAiEntry`
|
||||
- `GlobalAiEntry`
|
||||
|
||||
这些 entry 只负责:
|
||||
|
||||
- 是否显示
|
||||
- 传入 context
|
||||
- 传入 clientCapabilities
|
||||
- 传入 UI 文案
|
||||
|
||||
### 收益
|
||||
|
||||
- 删除四套重复状态机
|
||||
- 降低包体与维护成本
|
||||
- 后续新场景不再复制面板
|
||||
|
||||
## Phase C:弱化或下线全局 AI
|
||||
|
||||
当前全局 AI 被直接挂在:
|
||||
|
||||
- `src/app/(app)/layout.tsx`
|
||||
|
||||
它带来的问题不是“首屏一定特别重”,而是:
|
||||
|
||||
- 全局产品心智更复杂
|
||||
- 持续占用一套入口与状态
|
||||
- 会诱导继续扩展“全局工具平台”
|
||||
|
||||
建议策略:
|
||||
|
||||
- 第一阶段:保留代码,但默认隐藏/弱化入口
|
||||
- 第二阶段:若页面 AI 已覆盖主场景,则把全局 AI 下线为开发页或实验开关
|
||||
|
||||
### 为什么优先保页面 AI 而不是全局 AI
|
||||
|
||||
因为页面 AI 更接近核心使用场景:
|
||||
|
||||
- 对页面正文直接读写
|
||||
- 与编辑器桥接紧密
|
||||
- 用户心智最清晰
|
||||
|
||||
全局 AI 更像附加层,不值得优先保留完整前端壳。
|
||||
|
||||
## Phase D:明确保留的少数浏览器专属能力
|
||||
|
||||
有一些能力确实不能完全后移,应明确留在前端:
|
||||
|
||||
- `oo_*` 这类 OnlyOffice 客户端工具
|
||||
- `/api/ai-agent/client-tool-result`
|
||||
- 少数必须读浏览器本地态的能力
|
||||
|
||||
这些能力的处理原则是:
|
||||
|
||||
- 保留
|
||||
- 但只能作为 `client capability bridge`
|
||||
- 不得重新长成新的前端 AI 平台层
|
||||
|
||||
---
|
||||
|
||||
## 5. 推荐优先级
|
||||
|
||||
如果只按收益排序,我建议这样做:
|
||||
|
||||
### P0:先做
|
||||
|
||||
- 启用并验证 Hermes API Server
|
||||
- 把 `/api/ai-agent/run` 收口成 Hermes bridge
|
||||
- 冻结前端新增 builtin tool / toolset / provider 编排逻辑
|
||||
- 明确 mnote Rust 能力如何暴露给 Hermes 调用
|
||||
|
||||
### P1:接着做
|
||||
|
||||
- 把多套 AI 面板收口为一个通用 `AiBridgePanel`
|
||||
- 页面 / Mindmap / OnlyOffice 改成 context adapter
|
||||
|
||||
### P2:再做
|
||||
|
||||
- 弱化或隐藏全局 AI Host
|
||||
- 让全局 AI 退到实验入口或开发入口
|
||||
|
||||
### P3:最后做
|
||||
|
||||
- 清理旧的 builtins、registry、重复 localStorage/session 管理
|
||||
- 删除历史兼容面板实现
|
||||
|
||||
---
|
||||
|
||||
## 6. 除 AI 之外,还有哪些可以精简
|
||||
|
||||
这部分先只记录,不进入当前 harness 任务。
|
||||
|
||||
## 6.1 文档页加载链可以继续减重
|
||||
|
||||
当前文档页存在:
|
||||
|
||||
- `DocumentShell` 的 `mounted + dynamic + ssr:false`
|
||||
- `DocumentContent` 再 `dynamic` 到 `BlockNoteEditor`
|
||||
- `meta` 与 `content` 的两阶段加载
|
||||
|
||||
这些都可能造成刷新时的“空一下再出现”的体感。
|
||||
|
||||
建议后续单独评估:
|
||||
|
||||
- 去掉 `DocumentShell` 这层多余壳
|
||||
- 减少文档页串行加载层级
|
||||
- 优先把首屏必需数据前移
|
||||
|
||||
## 6.2 SearchPalette 目前是全局常驻挂载
|
||||
|
||||
当前:
|
||||
|
||||
- `SearchPalette` 直接挂在 `app/(app)/layout.tsx`
|
||||
|
||||
如果它本身比较重,后续可考虑:
|
||||
|
||||
- 改为按需挂载
|
||||
- 或在首次打开时再加载
|
||||
|
||||
## 6.3 Sidebar 职责仍然非常重
|
||||
|
||||
`Sidebar` 当前承担了太多:
|
||||
|
||||
- 页面树
|
||||
- 文件树
|
||||
- 资产操作
|
||||
- 拖拽复制
|
||||
- 删除恢复
|
||||
- 批处理
|
||||
- mindmap/table/media 入口
|
||||
|
||||
这会让 Sidebar 成为高复杂度常驻组件。
|
||||
|
||||
后续可以考虑:
|
||||
|
||||
- 按功能拆分
|
||||
- 降低初始挂载职责
|
||||
- 把非首屏必要的动作延后
|
||||
|
||||
## 6.4 文档页周边抽屉与面板可以按需加载
|
||||
|
||||
当前文档页同时带着:
|
||||
|
||||
- `PageOptionsSidebar`
|
||||
- `PageBacklinksPanel`
|
||||
- `DocumentHistoryDrawer`
|
||||
- `DocumentCommentsDrawer`
|
||||
- `DocumentAiAgentPanel`
|
||||
|
||||
后续可评估哪些可以从“默认挂载”改成“首次打开再加载”。
|
||||
|
||||
---
|
||||
|
||||
## 7. 最终建议
|
||||
|
||||
如果你的目标是:
|
||||
|
||||
> **先精简能精简的东西来改善网页前端加载与复杂度**
|
||||
|
||||
那么最值得优先推进的不是重写 Web,也不是先重做编辑器,而是:
|
||||
|
||||
> **把前端 AI 从“平台层”收缩成“Hermes + mnote Rust 的桥接层”。**
|
||||
|
||||
一句话版结论:
|
||||
|
||||
- **该砍的不是 AI 按钮本身,而是前端 AI 编排框架。**
|
||||
- **该保留的是轻面板、Hermes bridge、mnote Rust 业务执行面和浏览器专属 client bridge。**
|
||||
- **全局 AI 可以弱化,页面 AI 保留为主入口。**
|
||||
- **除 AI 外,文档页加载链、全局 SearchPalette、巨型 Sidebar 也是后续值得继续精简的方向,但先不进入当前 harness。**
|
||||
@@ -0,0 +1,459 @@
|
||||
# [recycle] 文档访问性能根治重构路线 v1
|
||||
|
||||
> 更新时间:2026-04-16
|
||||
>
|
||||
> 相关文档:
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/ai-frontend-simplification-plan-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-cutover-v1.md`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
这份文档只回答一个长期问题:
|
||||
|
||||
> **如果不满足于“临时优化”,而要从架构上根治文档访问和页面切换卡顿,mnote 应该重构成什么样。**
|
||||
|
||||
本文的目标不是“再做几处按需加载”,而是定义:
|
||||
|
||||
- 哪些前端能力应继续保留
|
||||
- 哪些能力应退出当前重前端壳
|
||||
- Rust Web 层应该采用什么形态
|
||||
- `BlockNote` 应该如何被隔离,而不是继续拖慢整个页面系统
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前事实
|
||||
|
||||
结合当前仓库结构,可以先确认四个事实:
|
||||
|
||||
### 2.1 当前最重的前端成本,不在服务端语言
|
||||
|
||||
当前文档页主链仍然是:
|
||||
|
||||
`documents/[id]/page.tsx -> DocumentShell -> DocumentContent -> BlockNoteEditor`
|
||||
|
||||
而且存在以下典型问题:
|
||||
|
||||
- 文档进入页面后仍有 `meta -> content -> editor` 的串行加载链
|
||||
- `DocumentShell` 与 `DocumentContent` 仍在客户端串联挂载
|
||||
- `BlockNoteEditor` 本身是当前最重的前端运行时之一
|
||||
- 文档页周边仍挂载多类抽屉、面板和桥接组件
|
||||
|
||||
这说明当前访问速度问题的核心,不是“Node 不够快”,而是:
|
||||
|
||||
> **页面切换时仍然把太多东西当成同一层前端运行时来初始化。**
|
||||
|
||||
### 2.2 当前只有一类能力明确难以短期替换
|
||||
|
||||
从产品结构看,短期最难替换的是:
|
||||
|
||||
- `BlockNote`
|
||||
|
||||
因为它同时承担:
|
||||
|
||||
- 文档编辑核心
|
||||
- 自定义 block 运行时
|
||||
- 当前正文交互和保存链
|
||||
|
||||
而下面这些东西,从长期看都不是必须继续绑在当前 React/Next 大壳里的:
|
||||
|
||||
- Sidebar / 页面树 / 文件树
|
||||
- 搜索壳与搜索结果面板
|
||||
- Mindmap
|
||||
- AI 面板
|
||||
- 历史 / 评论 / 回链 / 页面选项等周边面板
|
||||
- 在线表格
|
||||
|
||||
### 2.3 OnlyOffice 不应被当成主阻塞项
|
||||
|
||||
根据当前架构,OnlyOffice 本来就是独立页面型编辑器,不是正文内嵌主编辑器。
|
||||
|
||||
这意味着:
|
||||
|
||||
- 它不会决定文档页的首屏切换模式
|
||||
- 它可以继续保留“外挂页面/外部编辑器”定位
|
||||
- 它不应影响主文档访问性能路线判断
|
||||
|
||||
### 2.4 Mindmap 是优先级更高的可替换对象
|
||||
|
||||
当前 Mindmap 虽然复用了前端组件链,但它并不像 BlockNote 那样不可轻易动。
|
||||
|
||||
因此长期上:
|
||||
|
||||
- Mindmap 可以先于 BlockNote 重写
|
||||
- 它很适合作为“从当前重前端壳中剥离”的第一批对象
|
||||
|
||||
---
|
||||
|
||||
## 3. 根治原则
|
||||
|
||||
如果目标是“根治”,而不是“补丁式提速”,那么应遵守以下原则。
|
||||
|
||||
### 3.1 页面切换必须先回到“读优先”
|
||||
|
||||
当前问题的根子之一,是页面切换几乎默认在进入“编辑器世界”。
|
||||
|
||||
长期正确方向应改为:
|
||||
|
||||
- 先快速进入页面阅读态
|
||||
- 再按需进入编辑态
|
||||
- `BlockNote` 不得阻塞普通页面访问
|
||||
|
||||
换句话说:
|
||||
|
||||
> **文档页要从“先挂编辑器,再展示页面”改成“先展示页面,再按需挂编辑器”。**
|
||||
|
||||
### 3.2 除 `BlockNote` 外,其余能力应尽量退出当前重前端壳
|
||||
|
||||
长期目标不应是“继续给当前 Next/React 应用做瘦身”,而应是:
|
||||
|
||||
- 让能退出的都退出
|
||||
- 让必须保留在浏览器的只剩真正需要浏览器的那部分
|
||||
|
||||
理想形态是:
|
||||
|
||||
- 文档阅读页:极薄
|
||||
- 文档编辑岛:`BlockNote`
|
||||
- 外挂编辑器:OnlyOffice
|
||||
- 独立能力:Mindmap、AI、搜索、Sidebar 等各自最小化
|
||||
|
||||
### 3.3 Rust 化的意义在于“统一业务执行面”,不是“自动更快”
|
||||
|
||||
把东西改成 Rust 并不会自动让页面切换更快。
|
||||
|
||||
Rust 真正的价值在于:
|
||||
|
||||
- 统一业务执行平面
|
||||
- 让文档、块、导图、搜索、树结构、审计、AI 业务桥收口到同一体系
|
||||
- 支持更薄的前端和更强的 SSR / server-first 输出
|
||||
|
||||
所以“Rust 化”必须和“页面运行模型重构”一起发生,才构成根治。
|
||||
|
||||
---
|
||||
|
||||
## 4. 技术选型判断
|
||||
|
||||
这一节只回答一个问题:
|
||||
|
||||
> **长期重构时,到底是“就用 Rust”,还是要明确选择 `axum` 等 Rust Web 框架。**
|
||||
|
||||
结论先给出:
|
||||
|
||||
> **不能只说“用 Rust”。长期要落地,必须明确分层选型。推荐方案是:`axum` 作为 Web/服务承载层,配合 `Leptos Islands` 作为 server-first 页面壳;不推荐只用 `axum`,也不建议把 Dioxus/Yew 作为主文档访问层的第一选择。**
|
||||
|
||||
### 4.1 为什么不能只说“Rust”
|
||||
|
||||
“Rust”是语言,不是 Web 渲染方案。
|
||||
|
||||
如果只决定“以后改成 Rust”,但不明确:
|
||||
|
||||
- 服务端路由由谁承接
|
||||
- 页面 SSR 由谁承接
|
||||
- islands / hydration 模型由谁承接
|
||||
- 浏览器交互层由谁承接
|
||||
|
||||
那最后很容易回到:
|
||||
|
||||
- 只是把 API 改写成 Rust
|
||||
- 但前端页面模型仍然没变
|
||||
|
||||
这不能根治。
|
||||
|
||||
### 4.2 `axum` 的定位:非常适合做 Web 承载层,但不负责前端 UI 模型
|
||||
|
||||
官方 `axum` 文档显示,它非常适合承担:
|
||||
|
||||
- Router
|
||||
- Handler
|
||||
- Middleware
|
||||
- JSON / Query / Form / WebSocket / SSE
|
||||
- `tokio` 上的高并发服务
|
||||
|
||||
这使它非常适合作为 mnote 长期的:
|
||||
|
||||
- API 层
|
||||
- 文档查询层
|
||||
- 树结构聚合层
|
||||
- 搜索层
|
||||
- AI / Hermes 业务桥
|
||||
- SSR 页面外壳承载层
|
||||
|
||||
但要注意:
|
||||
|
||||
> **`axum` 不是前端渲染框架。它能承接 Web 服务,不会替你解决“页面如何减少 hydration、如何隔离 BlockNote”这类前端模型问题。**
|
||||
|
||||
因此,**只用 `axum` 不够。**
|
||||
|
||||
### 4.3 为什么推荐 `Leptos Islands`
|
||||
|
||||
官方 Leptos Islands 文档最值得关注的点是:
|
||||
|
||||
- 默认服务端组件不进入客户端
|
||||
- 只有显式 `#[island]` 的部分才被编译到浏览器
|
||||
- islands 应尽量“小而具体”
|
||||
|
||||
这和 mnote 的长期目标高度一致,因为你现在最想要的是:
|
||||
|
||||
- 绝大多数页面内容不再变成大块客户端运行时
|
||||
- 只有真正需要交互的部分进入浏览器
|
||||
- `BlockNote` 成为最后一个重交互 island
|
||||
|
||||
也就是说,Leptos Islands 的思路天然支持:
|
||||
|
||||
- 页面树/文件树作为轻交互 island
|
||||
- 搜索框作为轻交互 island
|
||||
- AI 面板作为独立 island
|
||||
- `BlockNote` 作为最后的重 island
|
||||
- 其余大量阅读内容只做 server-rendered HTML
|
||||
|
||||
这比“继续在全页 React hydration 里做局部优化”更接近根治。
|
||||
|
||||
### 4.4 为什么不把 Dioxus 作为主推荐
|
||||
|
||||
官方 Dioxus Fullstack 文档自己就把它定位得更偏“app-like”:
|
||||
|
||||
- 它支持 SSR
|
||||
- 但更强调交互型应用
|
||||
- 文档中明确区分 CSR 的 app 架构与 SSR 的 site 架构
|
||||
|
||||
而 mnote 的长期目标不是继续维持一个“整页 app 先跑起来”的文档访问模型,而是:
|
||||
|
||||
- 页面先出来
|
||||
- 交互再局部接上
|
||||
|
||||
所以 Dioxus 不是不能用,而是**不如 Leptos Islands 对这个目标直接。**
|
||||
|
||||
### 4.5 为什么不把 Yew 作为主推荐
|
||||
|
||||
Yew 更接近经典 Rust/WASM 前端框架。
|
||||
|
||||
它的问题不是能力不够,而是:
|
||||
|
||||
- 更偏客户端应用思路
|
||||
- 不天然突出 islands-first
|
||||
- 对你当前“尽量减少客户端代码、把交互压缩为少数岛”的目标,不是最优选
|
||||
|
||||
---
|
||||
|
||||
## 5. 推荐的长期方案
|
||||
|
||||
## 5.1 总体选型
|
||||
|
||||
推荐长期架构采用:
|
||||
|
||||
- **服务承载层:`axum`**
|
||||
- **页面壳与 islands 模型:`Leptos Islands`**
|
||||
- **业务执行面:现有 mnote Rust `core-protocol + bridge-runtime + storage-convex-bridge` 继续演进**
|
||||
- **AI runtime:Hermes API Server**
|
||||
- **最后保留的重编辑岛:`BlockNote`**
|
||||
|
||||
一句话版:
|
||||
|
||||
> **`axum` 负责服务,`Leptos` 负责薄页面与 islands,Rust 内核负责业务,Hermes 负责 agent,`BlockNote` 留作最后的重前端孤岛。**
|
||||
|
||||
## 5.2 为什么这是最适合 mnote 的方案
|
||||
|
||||
因为这个方案同时满足四个条件:
|
||||
|
||||
### A. 适合逐步迁移
|
||||
|
||||
你不需要一次性推翻所有前端。
|
||||
|
||||
可以先把:
|
||||
|
||||
- Sidebar
|
||||
- 页面树 / 文件树
|
||||
- 搜索壳
|
||||
- 阅读页
|
||||
- Mindmap
|
||||
|
||||
逐步迁到 `axum + Leptos`
|
||||
|
||||
再把 `BlockNote` 留在最后处理。
|
||||
|
||||
### B. 适合“读写分离”
|
||||
|
||||
`Leptos Islands` 很适合把页面拆成:
|
||||
|
||||
- 读页面:服务端输出
|
||||
- 写页面:局部 island
|
||||
|
||||
这正是文档访问性能根治的核心。
|
||||
|
||||
### C. 适合 Rust 单栈长期收口
|
||||
|
||||
当前你已经做过大范围 Rust 重构,未来继续统一到:
|
||||
|
||||
- Web 壳
|
||||
- 业务协议
|
||||
- AI 业务桥
|
||||
- CLI
|
||||
|
||||
会比继续维护“Rust 内核 + 重 Next 前端壳”的分裂状态更稳定。
|
||||
|
||||
### D. 风险可控
|
||||
|
||||
你不需要一开始就重写 `BlockNote`。
|
||||
|
||||
最难的部分可以留到最后,不会卡死整个长期路线。
|
||||
|
||||
---
|
||||
|
||||
## 6. 最终目标架构
|
||||
|
||||
长期目标建议收敛成下面这张逻辑图:
|
||||
|
||||
```text
|
||||
浏览器
|
||||
-> axum 路由层
|
||||
-> Leptos server-rendered 文档壳
|
||||
-> 少量 islands
|
||||
- Sidebar / 页面树 / 文件树
|
||||
- Search
|
||||
- AI 面板
|
||||
- Mindmap(重写后)
|
||||
- BlockNote(最后保留的重岛)
|
||||
|
||||
axum / Leptos
|
||||
-> mnote Rust 业务执行面
|
||||
- 文档
|
||||
- 块
|
||||
- 搜索
|
||||
- 导图
|
||||
- 审计 / trace / event
|
||||
|
||||
Hermes API Server
|
||||
-> 调用 mnote Rust 暴露的业务能力桥
|
||||
|
||||
OnlyOffice
|
||||
-> 继续独立页面 / 外挂编辑器
|
||||
```
|
||||
|
||||
在这个目标架构里:
|
||||
|
||||
- 页面切换不再默认进入“整页前端应用”
|
||||
- 绝大多数阅读内容不需要大块 hydration
|
||||
- `BlockNote` 成为唯一需要谨慎处理的大前端负担
|
||||
|
||||
---
|
||||
|
||||
## 7. 推荐迁移顺序
|
||||
|
||||
长期路线建议分四阶段,不建议一口气重做。
|
||||
|
||||
## Phase 1:先把文档访问模型改成 server-first
|
||||
|
||||
先做:
|
||||
|
||||
- 用 Rust Web 壳承接文档阅读态
|
||||
- 页面进入时直接输出 `meta + content`
|
||||
- 不再进入页面后再串行拉正文
|
||||
- 把“页面切换”和“编辑器启动”拆开
|
||||
|
||||
这一阶段的目标不是替换 BlockNote,而是先把它从切页主链里移走。
|
||||
|
||||
## Phase 2:优先重写可替换的常驻模块
|
||||
|
||||
优先级建议:
|
||||
|
||||
1. Sidebar / 页面树 / 文件树
|
||||
2. 搜索壳与搜索结果页
|
||||
3. 页面选项、历史、评论、回链等周边面板
|
||||
4. AI 面板
|
||||
|
||||
原因:
|
||||
|
||||
- 这些东西是跨页面常驻能力
|
||||
- 高频出现
|
||||
- 比 BlockNote 更容易先抽离
|
||||
|
||||
## Phase 3:重写 Mindmap,继续缩小前端重负担
|
||||
|
||||
Mindmap 是非常适合优先退出当前重前端壳的对象。
|
||||
|
||||
建议:
|
||||
|
||||
- 把导图数据和执行面继续放在 Rust
|
||||
- 前端重新实现为更轻的独立渲染层
|
||||
- 不再依赖当前文档页大壳去承载
|
||||
|
||||
## Phase 4:最后再处理 BlockNote
|
||||
|
||||
这时再决定:
|
||||
|
||||
- 是否继续保留 BlockNote,但隔离为独立 island
|
||||
- 是否逐步替换 BlockNote 的部分能力
|
||||
- 是否长期把编辑体验拆成更多 Rust 原生能力 + 少量 JS 编辑器兼容层
|
||||
|
||||
**在此之前,不建议把 BlockNote 当成第一刀。**
|
||||
|
||||
---
|
||||
|
||||
## 8. 非目标
|
||||
|
||||
为了避免路线漂移,下面这些都不应被误认为“根治方案”。
|
||||
|
||||
### 8.1 不是“把 Next API 改成 Rust 就算完成”
|
||||
|
||||
如果页面仍然是:
|
||||
|
||||
- 重客户端壳
|
||||
- 重 hydration
|
||||
- 切页即初始化编辑器
|
||||
|
||||
那只是服务端换语言,不是根治。
|
||||
|
||||
### 8.2 不是“先全面重写 BlockNote”
|
||||
|
||||
BlockNote 是最难对象,不应先作为第一刀。
|
||||
|
||||
正确顺序应是:
|
||||
|
||||
- 先移除外围负担
|
||||
- 最后再碰 BlockNote
|
||||
|
||||
### 8.3 不是“继续在当前 React 壳里无限做局部修补”
|
||||
|
||||
按需加载、拆组件这些短期仍有价值,但如果最终仍然保留:
|
||||
|
||||
- 大型客户端文档壳
|
||||
- 大型常驻 Sidebar
|
||||
- 大型客户端页面切换模型
|
||||
|
||||
那还是治标不治本。
|
||||
|
||||
---
|
||||
|
||||
## 9. 最终建议
|
||||
|
||||
如果以“根治文档访问性能”为唯一目标,我给出的长期技术判断是:
|
||||
|
||||
> **应该继续推进 Rust 主导重构,但不是笼统地说“以后用 Rust”,而是明确采用 `axum + Leptos Islands + mnote Rust 内核 + Hermes` 的组合。**
|
||||
|
||||
其中:
|
||||
|
||||
- `axum`
|
||||
适合做 Web 服务承载层、API、SSE、AI 业务桥、SSR 外壳。
|
||||
- `Leptos Islands`
|
||||
适合做 server-first 文档页和极小交互岛,是把 `BlockNote` 隔离成“最后一个重前端岛”的最佳路线。
|
||||
- `Dioxus`
|
||||
可以关注,但更适合作为偏 app 化交互场景的备选,不是当前文档访问性能根治的第一推荐。
|
||||
- `Yew`
|
||||
不作为当前主路线推荐。
|
||||
|
||||
最后用一句话收口:
|
||||
|
||||
> **mnote 的根治路线,不是“把现有前端改快一点”,而是“把除了 BlockNote 之外的大多数页面能力从当前重前端壳中迁出,交给 Rust 主导的 server-first Web 架构承载”。**
|
||||
|
||||
---
|
||||
|
||||
## 10. 外部参考
|
||||
|
||||
以下外部资料支持上面的技术判断:
|
||||
|
||||
- `axum` 官方文档:
|
||||
https://docs.rs/axum/latest/axum/
|
||||
- Leptos Islands 指南:
|
||||
https://book.leptos.dev/islands.html
|
||||
- Dioxus Fullstack SSR 文档:
|
||||
https://dioxuslabs.com/learn/0.7/essentials/fullstack/ssr/
|
||||
@@ -0,0 +1,284 @@
|
||||
# [recycle] mnote 单仓收口 Rust 内核阶段 Checklist
|
||||
|
||||
> 更新时间:2026-04-14
|
||||
>
|
||||
> 主工作路径:`/mnt/Data1T/mnote`
|
||||
>
|
||||
> 第二工作路径:`/mnt/Data1T/mnote-rust`(仅作为历史资产来源,不作为主执行仓)
|
||||
>
|
||||
> 对齐文档:`/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-backport-plan.md`
|
||||
>
|
||||
> 目标:把 `mnote-rust` 的必要 Rust 内核逐步回迁到 `mnote` 主仓,并始终维持“单仓启动、单仓保存、主前端不分叉”的执行口径。
|
||||
|
||||
## 1. 执行口径与硬边界
|
||||
|
||||
- [x] 只保留 `/mnt/Data1T/mnote` 作为唯一主产品仓。
|
||||
- [x] `/mnt/Data1T/mnote-rust` 只保留为历史资产来源、参考实现和待吸收规范,不再承担主线执行入口。
|
||||
- [x] `wolai-frontend` 继续承担唯一主产品前端壳,不复制 `mnote-rust` 的第二套 Next 前端壳。
|
||||
- [x] Rust 内核统一收口到 `/mnt/Data1T/mnote/rust/`,不再在主仓根目录散落多个 Rust 源码根。
|
||||
- [x] 最终保存只认 Convex 主事实层;Rust 的事件、索引、缓存、fixtures 都属于派生层。
|
||||
- [x] 不通过跨仓软链接、硬链接或路径偷接来假装完成迁移。
|
||||
- [x] 第一阶段不迁入 `design/phases/**`、`design/execution/**`、`design/UI/**` 这类历史叙事文档。
|
||||
- [x] 第一阶段不迁入 `mnote-cli`、`adapter-onlyoffice`、`adapter-mindmap`、`adapter-legacy-mnote`。
|
||||
- [x] Mindmap 继续以 `mnote` 当前实现为主:BlockNote 自定义块为主形态,独立全屏页为辅助形态。
|
||||
- [x] OnlyOffice 继续保持“正文只嵌文件入口、编辑器在独立页面运行”的产品形态,不回嵌 BlockNote。
|
||||
- [x] `wolai-frontend/`、`wolai-backend/`、`infra/convex/`、`src/components/onlyoffice/` 默认保持不动,除非后续阶段明确接线需要。
|
||||
|
||||
## 2. 当前已确认基线(2026-04-13 已核对)
|
||||
|
||||
### 2.1 已完成事实
|
||||
|
||||
- [x] `/mnt/Data1T/mnote/rust/Cargo.toml` 与 `/mnt/Data1T/mnote/rust/Cargo.lock` 已存在。
|
||||
- [x] `/mnt/Data1T/mnote/rust/crates/` 下已落位 `core-domain`、`core-protocol`、`event-log`、`storage-convex-bridge`、`index-fts`、`bridge-runtime`、`mnote-cli`。
|
||||
- [x] `/mnt/Data1T/mnote/rust/design/INDEX.md` 与 `design/core/01~04` 核心设计文档已落位。
|
||||
- [x] workspace members 仅指向 `/mnt/Data1T/mnote/rust/crates/*`。
|
||||
- [x] 已验证 `cargo metadata --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml --format-version 1` 可通过。
|
||||
- [x] 已验证 `cargo check --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml` 可通过。
|
||||
- [x] 已核对 `/mnt/Data1T/mnote/rust/` 下未发现直接引用 `/mnt/Data1T/mnote-rust` 的残留路径。
|
||||
- [x] 已确认当前主前端链路仍在 `mnote`:文档页为 `DocumentShell -> DocumentContent -> BlockNoteEditor`,Mindmap 仍复用现有 `MindmapBlock` 体系,OnlyOffice 仍为独立页面型编辑器。
|
||||
|
||||
### 2.2 当前仍未开始或未初始化的事实
|
||||
|
||||
- [x] `/mnt/Data1T/mnote/rust/bridge/` 已初始化最小说明目录,`crates/bridge-runtime` 已提供 Phase 1 最小真实执行样板。
|
||||
- [x] `/mnt/Data1T/mnote/rust/scripts/` 当前未初始化。
|
||||
- [x] `/mnt/Data1T/mnote/rust/fixtures/` 当前未初始化。
|
||||
- [x] `/mnt/Data1T/mnote/rust/tests/` 当前未初始化。
|
||||
- [x] `mnote-cli` 已并入 `/mnt/Data1T/mnote/rust/crates/`,当前已冻结 `page/block/search/sidebar/tool` 的最小命令面与 `--json` 输出协议。
|
||||
- [x] `adapter-onlyoffice`、`adapter-mindmap`、`adapter-legacy-mnote` 尚未并入主仓。
|
||||
- [x] `/api/documents/content`、`/api/documents/save`、`/api/documents/title`、`/api/documents/stats`、`/api/sidebar`、`/api/blocks/patch` 已接入主仓当前 bridge 包装层并统一返回 `request_id/trace_id` 元信息;其中 `documents/title`、`documents/stats`、`documents/options`、`documents/save` 已进一步收口到统一的 bridge mutation request 运行时接缝,可先构造与 `storage-convex-bridge::build_write_request` 对齐的 `functionName/payloadJson/args` 对象再落到 Convex,但整条链仍未直接切到 `core-protocol -> storage-convex-bridge` 的真实 Rust crate 执行链。
|
||||
|
||||
2026-04-15 Phase 1/2 补记:`documents.content.get`、`documents.title.update`、`documents.save` 这 1 读 2 写链现已从主仓 Next route 真实进入 `crates/bridge-runtime`,再由 TS 仅做 Convex transport;同时 `crates/mnote-cli` 已在主仓 workspace 落位,并通过 `page/block/search/sidebar/tool` 五类最小命令面的 `--json` 计划输出冻结当前 CLI 协议。
|
||||
|
||||
## 3. Phase A:Rust workspace 落位(主目标已完成)
|
||||
|
||||
完成目标:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/` 成为唯一 Rust workspace 根。
|
||||
- P0 crate 与核心规范文档已在主仓内有真实副本。
|
||||
- 新 workspace 已具备最小自检能力。
|
||||
|
||||
Checklist:
|
||||
|
||||
- [x] 固定 `/mnt/Data1T/mnote/rust/` 为唯一 Rust workspace 根,后续 Rust 命令统一从这里发起。
|
||||
- [x] 固定 workspace members 只指向 `crates/core-domain`、`crates/core-protocol`、`crates/event-log`、`crates/storage-convex-bridge`、`crates/index-fts`。
|
||||
- [x] 复制并落位 `core-domain`、`core-protocol`、`event-log`、`storage-convex-bridge`、`index-fts`。
|
||||
- [x] 复制并落位 `rust/design/INDEX.md` 与 `rust/design/core/01-domain-model-v0.md`、`02-command-query-tool-protocol-v0.md`、`03-storage-event-indexing-v0.md`、`04-onlyoffice-integration-boundary-v0.md`。
|
||||
- [x] 统一 workspace 级别的 `edition`、`license`、`version`、`authors` 约定。
|
||||
- [x] 跑通 `cargo metadata --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml --format-version 1`。
|
||||
- [x] 跑通 `cargo check --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml`。
|
||||
- [x] 已补跑 `cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p core-domain` 与 `cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p core-protocol`。
|
||||
- [x] 已在本 checklist 第 1 节固定“第一阶段禁止迁入清单”,明确 `mnote-cli`、`adapter-*`、`design/phases/**`、`design/execution/**`、第二套 Next 前端壳均不属于本阶段交付。
|
||||
|
||||
Phase A 验收:
|
||||
|
||||
- [x] Cargo 可在主仓内解析 workspace。
|
||||
- [x] 主仓内已有 P0 内核与长期规范副本。
|
||||
- [x] 当前未出现对第二工作路径的直接 Cargo 路径依赖。
|
||||
- [x] 已完成 crate 单测与文档级禁止迁入清单,Phase A 可视为关闭。
|
||||
|
||||
## 4. Phase B:bridge 入口与 Convex 桥接
|
||||
|
||||
完成目标:
|
||||
|
||||
- 在 `mnote` 主仓内形成唯一 Rust command/query 入口。
|
||||
- 首批读写入口从“前端 API 直接调 Convex”切到“前端 API -> Rust bridge -> Convex”。
|
||||
|
||||
当前起点(已确认):
|
||||
|
||||
- [x] `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/content/route.ts` 当前直接 `query(api.documents.getContent)`。
|
||||
- [x] `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/save/route.ts` 当前已改为构造稳定 envelope 后交给 `save-command-adapter` 执行。
|
||||
- [x] `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/title/route.ts` 当前已不再直接拼 `mutation(api.documents.updateTitle)` 参数,改为构造稳定 envelope 后交给页面元信息 command adapter 执行。
|
||||
- [x] `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/stats/route.ts` 当前已不再直接拼 `mutation(api.documents.updateStats)` 参数,改为构造稳定 envelope 后交给页面元信息 command adapter 执行。
|
||||
- [x] `/mnt/Data1T/mnote/wolai-frontend/src/app/api/sidebar/route.ts` 当前直接聚合多个 Convex query 结果。
|
||||
- [x] `/mnt/Data1T/mnote/wolai-frontend/src/app/api/blocks/patch/route.ts` 当前直接读取文档内容、替换 block 树后回写 Convex。
|
||||
|
||||
Checklist:
|
||||
|
||||
- [x] 已在 `wolai-frontend/src/lib/documents/bridge.ts` 固定当前 bridge 入口,先由主仓 API route 统一走同一层包装。
|
||||
- [x] 已固定首批 bridge envelope:`request_id`、`trace_id`、`workspace_id`、`actor`、`source`、`idempotency_key`。
|
||||
- [x] 约定所有新命令和查询都先进入 `core-protocol` envelope,再转给 `storage-convex-bridge`。
|
||||
- [x] 已把 `documents/content` 与 `sidebar` 两条低风险读链接入当前 bridge query 包装层。
|
||||
- [x] 已把 `documents/title`、`documents/stats`、`documents/save`、`blocks/patch` 接入当前 bridge command 包装层。
|
||||
- [x] 已为 `documents/title`、`documents/stats` 收口稳定的页面元信息 command adapter,冻结 route 到 Convex 之间的 payload/target/meta 映射,避免 route 继续直接耦合 `updateTitle/updateStats` 的参数细节。
|
||||
- [x] 已为首批 4 条写链固定当前 `bridge -> Convex` 的函数名、payload 结构和 workspace scope;其中 `documents.title`、`documents.stats`、`documents.options`、`documents.save` 已先收口到 `buildDocumentBridgeMutationRequest` 这层最小运行时接缝,统一构造与 `storage-convex-bridge::build_write_request` 对齐的 `functionName/payloadJson/args`,再由同一执行器落到 Convex;`blocks.patch` 仍未收口到该执行层,且整条链尚未直接切到 Rust crate 内统一执行。
|
||||
- [x] 已在 `wolai-frontend/convex/schema.ts` 新增 `command_logs`、`domain_events` 两张表,并新增 `wolai-frontend/convex/bridgeLogs.ts` 与 `src/lib/documents/bridge-log.ts` 承接首批写链日志落账。
|
||||
- [x] 首批 4 条写链(`documents/title`、`documents/stats`、`documents/save`、`blocks/patch`)当前会在成功路径同时写入 command log 与 domain event;失败态、回滚态和统一事务性仍待后续补齐。
|
||||
- [x] 当前已接入的查询链(`documents/content`、`sidebar`)保持只读,不产生写副作用。
|
||||
- [x] 已统一 `documents/content`、`documents/save`、`documents/title`、`documents/stats`、`sidebar` 的首批 bridge 错误返回结构。
|
||||
- [x] 当前 bridge 仅负责协议校验、请求映射与响应元信息包装,尚未吞并产品 UI 逻辑。
|
||||
|
||||
2026-04-14 B 阶段补记:已在 `rust/crates/core-protocol` 中补齐 `documents.meta.get`、`documents.content.get`、`sidebar.dataset.list`、`documents.save`、`blocks.patch` 的最小 query/command 协议对象,并在 `rust/crates/storage-convex-bridge` 中补齐对应的 query/command name -> Convex function 映射与单测。当前主仓新增链路已统一先构建稳定 envelope,再由后续真实 Rust bridge 执行链消费;本轮已通过 `cargo test -p core-protocol -p storage-convex-bridge` 与 `cargo check --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml` 验证协议与映射闭环。
|
||||
2026-04-14 B 阶段第三刀补记:已在 `wolai-frontend/src/lib/documents/bridge.ts` 新增 `buildDocumentBridgeMutationRequest` 与统一执行器,把 `documents.title.update`、`documents.stats.update`、`documents.options.update`、`documents.save` 进一步推进到最小可运行的 Rust bridge 风格接缝。当前这些写链会先生成与 `storage-convex-bridge::build_write_request` 对齐的 `functionName/payloadJson/args` 运行时对象,再由适配层调用 Convex mutation;配套 `src/lib/documents/bridge.test.ts` 已新增 request builder 断言,确认标题更新与正文保存两条链的运行时 request 形状稳定。
|
||||
|
||||
Phase B 验收:
|
||||
|
||||
- [x] 当前至少已有 2 条查询链(`documents/content`、`sidebar`)和 4 条写链(`documents/title`、`documents/stats`、`documents/save`、`blocks/patch`)通过当前 bridge 包装层读写 Convex。
|
||||
- [x] 已补 `/api/bridge/trace`、`/api/bridge/request` 回查入口,并已切到 `wolai-frontend/convex/bridgeLogs.ts` 查询主线;`convex/audit.ts` 当前保留为并行旧实现线,不作为本轮回查入口。
|
||||
- [x] 当前已接入的请求错误结构已统一到 bridge 错误响应,不再直接抛出临时调试字符串。
|
||||
- [x] 本批改动只改 API route 与调用 payload,未改页面结构与 UI 外观。
|
||||
|
||||
建议验证:
|
||||
|
||||
- [x] `cargo check --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml`
|
||||
- [x] 已对 `/api/documents/content`、`/api/sidebar` 做最小读链 smoke:当前主仓实例固定为 `http://127.0.0.1:3001`,`/api/sidebar` 已返回 `activeWorkspaceId=ws-smoke-temp`,新建文档后 `/api/documents/content` 可正常返回内容与 `requestId/traceId`。
|
||||
- [x] 已对 `/api/documents/title`、`/api/documents/stats`、`/api/documents/save`、`/api/blocks/patch` 做最小写链 smoke:真实写入成功,且随后 `/api/documents/content` 已读回 `blocks.patch` 改写后的正文。
|
||||
- [x] 已完成真实 Convex 运行前置验证:`infra/convex/docker-compose.yml` 可启动本地自托管 Convex,`pnpm exec convex dev --once --tail-logs disable --env-file ../.env.all --run ping:ping` 已通过。
|
||||
- [x] 已用真实 Convex CLI 对 `bridgeLogs:recordCommandLog`、`bridgeLogs:recordDomainEvent`、`bridgeLogs:listByTrace`、`bridgeLogs:listByRequest` 做最小 smoke,确认 command log、domain event、trace/request 字段能同时落账并回查。
|
||||
- [x] 已完成网页/API 级 smoke:从真实 `/api/documents/title`、`/api/documents/stats`、`/api/documents/save`、`/api/blocks/patch` 返回的 `requestId/traceId`,可继续经 `/api/bridge/trace` 与 `/api/bridge/request` 回查到对应 `command_logs/domain_events`。本轮同时修复 `src/lib/documents/bridge-log.ts` 中 `getAuthedConvexClient()` 返回值解构错误(此前会导致写链落账时报 `client.mutation is not a function`)。
|
||||
|
||||
## 5. Phase C:页面元信息、Sidebar 与正文保存接入
|
||||
|
||||
完成目标:
|
||||
|
||||
- 在不更换主前端壳的前提下,把文档元信息、Sidebar 聚合、BlockNote 正文保存逐步切到 Rust 协议层。
|
||||
- 执行顺序严格按 `C1 页面元信息 -> C2 Sidebar 聚合 -> C3 正文保存` 推进,不跳步。
|
||||
|
||||
### C1 页面元信息
|
||||
|
||||
- [x] 已盘点并锁定文档页元信息读写边界:`src/app/(app)/documents/[id]/page.tsx` 负责元信息入口,`document-content.tsx` 负责标题/页面选项/统计信息写入,`document-shell.tsx` 仅继续透传既有 UI 壳参数。
|
||||
- [x] 已锁定并接入当前 C1 最小读链:`src/app/(app)/documents/[id]/page.tsx` 原先直调 `api.documents.getMeta`,现已改为经 `GET /api/documents/meta` 进入现有 bridge query 包装层;新增的服务端 helper 仅负责透传当前请求认证/追踪头并发起同源 bridge route 请求。
|
||||
- [x] 已把标题更新、页面属性、页面选项、统计信息这批低风险页面元信息操作切到统一的当前协议/适配边界:标题、统计信息、页面选项 3 条写链都已接入当前 bridge command 包装层;其中 `documents.title.update`、`documents.stats.update`、`documents.options.update` 已统一收口到 `wolai-frontend/src/lib/documents/metadata-command-adapter.ts`,route 只保留 validation、`buildDocumentBridgeContext`、`buildDocumentCommandEnvelope` 与稳定错误模型。`documents.options` 本轮不再内联 `client.mutation(api.documents.updateOptions, ...)`。同时已补齐 `rust/crates/core-protocol` 的 `UpdatePageTitle`、`UpdatePageStats`、`UpdatePageOptions` 结构与 `storage-convex-bridge` 的 command name -> Convex mutation 映射测试;2026-04-14 又进一步把这 3 条写链切到 `buildDocumentBridgeMutationRequest`,先构造与 Rust `ConvexMutationRequest` 对齐的最小运行时 request,再交给统一执行器落到 Convex,为后续真实 Rust 执行链留出稳定接缝。
|
||||
- [x] 当前最小替换已保持现有页面 UI、权限判断、动态导入结构不变:`DocumentShell` 入参、`readOnly/disableDownload/disableCopy` 计算、`notFound()` 分支与页面结构保持不变,仅将元信息读取入口切到 `documents.meta` bridge query。
|
||||
- [x] 已统一页面元信息链当前口径:`documents.meta` 读链与 `documents.title/stats/options` 写链均复用 `buildDocumentBridgeContext` / envelope;query payload 与 command payload 已固定 `documentId/workspaceId`,target.pageId 固定文档 business id,回传元信息统一为 `requestId/traceId/queryName|commandId|commandName`,actor/source 也统一来自 bridge context。
|
||||
- [x] 已为标题更新、页面属性、统计信息补齐当前最小回归验证。
|
||||
当前最小验证:已对页面选项写链补跑相关文件 eslint / vitest,并完成真实 HTTP smoke,确认在主仓 `http://127.0.0.1:3001` 实例下以 `ws-smoke-temp` 工作区调用 `POST /api/documents/options` 后,会返回 `requestId=req_aef4159e-24f2-43a8-b168-864c4b2b2c6c`、`traceId=trace_3cbc114e-db01-410f-933c-e63d8bd83393`、`commandId=cmd_2cb6871d-9075-4252-b698-a7c4ea069bfd`,且后续 `/api/bridge/request` 与 `/api/bridge/trace` 已能回查到对应 `command_logs/domain_events`。本轮同时保留 `documents.meta` 读链最小验证:真实 `GET /api/documents/meta?documentId=...&workspaceId=ws-smoke-temp` 已返回 `doc + meta.requestId/traceId/queryName=documents.meta.get`。另外 `documents/stats` 写链的真实 HTTP smoke 也已完成并可回查。2026-04-14 新增一轮真实复测:同一 `documentId` 连续执行 `POST /api/documents/options {showToc:true,layoutDensity:\"compact\"}` 与 `POST /api/documents/stats {wordCount:12,characterCount:34,blockCount:2,todoTotal:5,todoDone:3}` 后,紧跟 5 次 `GET /api/documents/meta` 均稳定返回本次写入值,未再复现“同次写入后 meta 读回不稳定”的现象。为避免历史脏数据导致偶发漂移,已把 `convex/documents.ts` 的关键 `by_document_id` 读写收口到 deterministic canonical record 选择逻辑,并补了重复 business id 的回归测试。
|
||||
2026-04-14 第二批真实收口:已将仍直接依赖 `by_document_id.first()` 的高风险页面/文档关联路径继续切到同一 canonical document helper,当前已覆盖 `convex/documentShares.ts`、`convex/documentGroupShares.ts`、`convex/documentStars.ts`、`convex/comments.ts`、`convex/references.ts`。这批路径分别影响页面共享权限判定、群组公开、收藏可见性、评论线程读写与反链标题读取;此前若同一 business `documentId` 存在重复物理记录,会继续存在命中漂移风险。本轮已补跑目标文件 `eslint`、`src/lib/documents/document-record.test.ts`,并用源码扫描确认这批目标文件内已不再残留 `by_document_id.first()`;当前未继续扩到 `mediaAssets.ts`、`mindmaps.ts` 等非本批最小闭环路径。
|
||||
2026-04-14 第三批真实收口:继续只处理剩余 still-high-risk 的 `documents.by_document_id` 直接命中点,已新增 `convex/_utils/documentRecord.ts#getCanonicalParentDocumentId`,并把 `convex/comments.ts`、`convex/documents.ts` 中共享/权限祖先链扫描统一改到该 helper,避免同一 business `documentId` 的重复物理记录在父链遍历时再次漂移。同时已把 `convex/mindmaps.ts` 的 owner 校验从 `by_document_id.first()` 切到 `requireCanonicalOwnedDocument`,并将 `convex/mediaAssets.ts#createWithStorage` 的页面归属校验改为 `getCanonicalDocumentByBusinessId`,以覆盖页面内容附件上传这一仍会直接命中旧记录的高风险入口。本批最小验证目标为:补充 canonical helper 单测、对上述目标文件跑 `eslint`,并再次用源码扫描确认 `comments/documents/mindmaps/mediaAssets` 内不再残留直接依赖 `documents.by_document_id` 的实现。
|
||||
2026-04-14 第四批真实收口:按本轮 C1 要求只处理 `convex/documentStars.ts` 剩余两个父链祖先扫描点,已将 `resolveSharePermission` 与 `resolveGroupSharePermission` 中手写的 `documents.by_document_id` 父级推进统一改为 `getCanonicalParentDocumentId`。同时已补充 `src/lib/documents/document-record.test.ts` 的 helper 回归用例,验证重复 business `documentId` 下即使旧记录已删除,父链 helper 仍稳定返回 canonical 父页面 id;并已补跑目标文件 `eslint` 与 `vitest`。本轮源码扫描确认 `wolai-frontend/convex/` 业务路径中已不再残留直接 `withIndex(\"by_document_id\")` 命中点,当前仅剩 `convex/_utils/documentRecord.ts` 作为底层 canonical helper 持有该查询。
|
||||
|
||||
C1 验收:
|
||||
|
||||
- [x] 页面元信息至少已有 3 条低风险链路切入 Rust 层。
|
||||
- [x] 文档页外观与交互未退化。
|
||||
- [x] trace 字段已可回查到页面级写入:`documents.title.update`、`documents.stats.update`、`documents.options.update` 的真实 HTTP 写入均已返回 `requestId/traceId`,且后续 `/api/bridge/request` 与 `/api/bridge/trace` 已复核可查到对应 `command_logs/domain_events`。
|
||||
|
||||
2026-04-14 C1 浏览器回归补记:已新增 `/mnt/Data1T/mnote/scripts/task019-document-ui-regression.js` 并在真实本地实例 `http://127.0.0.1:3001` 上执行。脚本会创建临时页面,确认 Sidebar 主导航与“私有 / 我的页面”分区可见、文档页标题输入框与 BlockNote 编辑区正常渲染,然后实际修改标题与正文,等待 `/api/documents/title`、`/api/documents/save` 成功返回并出现“已保存”,最后刷新页面确认标题与正文仍保留,再调用 `/api/documents/purge` 清理临时页面。实跑结果通过,说明本轮协议层切换后文档页主交互未退化。
|
||||
|
||||
### C2 Sidebar 聚合
|
||||
|
||||
- [x] 盘点 Sidebar 真实数据来源,锁定 `sidebar.tsx`、`private-tree.tsx`、`file-tree.tsx`、`use-convex-sidebar-data.ts`、`sidebar-tree.ts`。
|
||||
- [x] 先把 Sidebar 的查询聚合逻辑抽象成 Rust query 目标,避免继续在前端和 API 中重复拼树。
|
||||
- [x] 为 Sidebar 建立最小查询契约,至少覆盖页面列表、层级关系、文件树行模型和展开状态所需字段。
|
||||
- [x] 保留现有 Sidebar UI 和交互,不引入第二套导航壳,也不从 `mnote-rust` 复制 `sidebar.tsx` 覆盖现实现。
|
||||
- [x] 把 Mindmap、表格、媒体等派生资源的聚合边界整理成后续 Rust query 可接入的明确目标。
|
||||
|
||||
2026-04-14 盘点补记:当前主入口已确认位于 `wolai-frontend/src/components/sidebar/sidebar.tsx`,私有树/文件树分别位于 `components/sidebar/private-tree.tsx` 与 `components/sidebar/file-tree.tsx`;数据侧同时存在 `/api/sidebar` 聚合 route、`hooks/use-convex-sidebar-data.ts` 实时订阅组装,以及 `lib/sidebar-tree.ts` 的树/section 构建逻辑,说明 C2 的真实起点是“API 聚合 + 前端二次拼装”的双层结构。
|
||||
2026-04-14 契约补记:当前最小 Rust query 目标已明确为兼容 `SidebarInitialData` 的单查询返回,至少覆盖 `active_workspace_id`、`workspaces`、`documents`、`trashed_documents`、`media_assets`、`trashed_media_assets`、`mindmap_assets`、`trashed_mindmap_assets`、`table_assets`、`trashed_table_assets`、`mindmap_docs`、`mindmap_asset_children`,从而先替换聚合来源而不改 `buildDocumentTree/buildVisibleRows` 的前端渲染逻辑。
|
||||
2026-04-14 边界补记:当前已明确必须保持不变的 UI/交互包括 `starred/public/shared/private/templates` 分区语义、`sort_order -> created_at` 排序口径、`doc/index.md/asset-folder/asset` 文件树行语义、拖拽/剪贴板协议,以及回收站双 tab 与资源计数行为;Mindmap、表格、媒体三类派生资源的聚合边界已被明确列为 Rust query 后续接入目标。
|
||||
2026-04-14 C2 第一刀收口补记:已新增 `wolai-frontend/src/lib/sidebar-data.ts` 与 `wolai-frontend/src/lib/server/sidebar-data.ts`,把 `src/app/(app)/layout.tsx`、`src/app/api/sidebar/route.ts`、`src/hooks/use-convex-sidebar-data.ts` 之间重复的 `SidebarInitialData` 组装统一收口到共享 helper;同时修复 hook 链路中 `mindmapAssetChildren` 长期为空的漂移,补充 `src/lib/sidebar-data.test.ts`,并通过 `pnpm test src/lib/sidebar-data.test.ts src/lib/file-tree/rows.test.ts` 与定向 `eslint` 验证。基于本轮复核,当前已可确认“多个前端位置不再重复拼装同一棵树”这一验收点达成;但由于 SidebarInitialData 仍未冻结为明确 Rust query contract,`C2` 其余验收项暂不提前勾选。
|
||||
2026-04-14 C2 第二刀补记:已新增 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/sidebar-rust-query-target.md`,把 `sidebar.dataset.list` 的最小 payload/result、Rust query 与前端投影职责边界、以及“不复制第二套导航壳/不直接输出前端渲染树”的约束固定成文档。当前可确认 Sidebar 查询聚合目标已经从“散落在 route 和 hook 中的实现细节”收口为明确的 Rust query 接入目标。
|
||||
|
||||
C2 验收:
|
||||
|
||||
- [x] Sidebar 至少一条主查询已经改由 Rust query 供给。
|
||||
- [x] 文档树、文件树、回收站等视图语义保持一致。
|
||||
- [x] 不再需要在多个前端位置重复拼装同一棵树。
|
||||
|
||||
2026-04-14 C2 第三刀补记:已新增 `wolai-frontend/convex/sidebar.ts`,把页面列表、回收站、Mindmap、媒体、表格等数据集收口成单个 `sidebar.datasetList` 主查询;`src/lib/server/sidebar-data.ts` 与 `src/hooks/use-convex-sidebar-data.ts` 均已切换为消费这条单查询返回,再通过既有 `SidebarInitialData` 投影层驱动 UI。配合 `src/lib/sidebar-data.test.ts` 的契约冻结、`src/lib/file-tree/rows.test.ts` 的文件树行语义验证,以及定向 `eslint`,当前可确认文档树、文件树、回收站双 tab 依旧沿用原有前端投影逻辑,没有因数据源切换而改变语义。
|
||||
2026-04-15 C2 第四刀补记:`/api/sidebar` 现已从“先直跑 Convex helper,再额外挂 meta”推进为真实 Rust query transport。当前 route 会先执行 `buildDocumentQueryEnvelope(name=\"sidebar.dataset.list\") -> resolveRustBridgeQueryPlan -> executeRustBridgeQueryTransport`,再把 `sidebar:datasetList` 的结果投影回 `SidebarInitialData`;这意味着 Sidebar API 主链已经真正进入 Rust runtime/bridge,而不再只是挂一个 envelope 名字。
|
||||
|
||||
### C3 BlockNote 正文保存
|
||||
|
||||
- [x] 盘点正文保存链路,锁定 `blocknote-editor.tsx`、`schema.ts`、文档保存 API 当前入口。
|
||||
- [x] 先把正文保存切成“前端快照采集”和“Rust command 提交”两个边界。
|
||||
- [x] 为正文保存定义最小协议对象,至少包含 `page_id`、`workspace_id`、`revision`、`actor`、`source`、block 快照、冲突检测字段。
|
||||
- [x] 先接入低风险保存模式,例如显式保存或节流保存,不先处理复杂协同细节。
|
||||
- [x] 确保正文链与页面元信息链、Sidebar 链共用同一 identity 口径,不再出现多套 page id 映射。
|
||||
|
||||
C3 验收:
|
||||
|
||||
- [x] 正文保存至少一条正式链路已经通过 Rust command 回写 Convex。
|
||||
- [x] 保存失败时可返回稳定错误模型。
|
||||
- [x] 保存成功时可回查 command log、domain event 与 trace。
|
||||
|
||||
2026-04-14 C3 第一刀收口:已为 `documents.getContent` / `documents.save` 增加独立 `content_revision` 与 `content_conflict_key`,避免标题/统计信息更新污染正文 revision;`/api/documents/content` 现会透传 `revision/conflictDetectionKey`,`BlockNoteEditor` 会在自动保存时携带 `revision`、`conflictDetectionKey`、`snapshotCapturedAt`、`blockCount`,并在成功后刷新本地保存元数据。`save-command-adapter` 现把 Convex 冲突归一为 `409 REJECTED` bridge 错误,编辑器右上角也会展示保存失败信息。当前最小验证已补 `save-contract`/`bridge` 单测,并完成定向 eslint;结果为无 error,仅保留仓库既有 warnings。
|
||||
2026-04-14 C3 第二刀补记:源码复核确认 `BlockNoteEditor` 当前通过 `useDebouncedCallback(saveContent, 800)` 以防抖自动保存作为最小低风险保存模式,没有把复杂协同状态直接并入正文写链;同时 `documents.meta/title/stats/options/save` 这些 route 都把同一个 `normalizedDocumentId` 作为 `target.pageId`,`documents.content/save` payload 也统一使用 `documentId/workspaceId`,`sidebar.dataset.list` 则以同一 `workspace_id` 作用域返回 `documents[].id` 作为页面业务 id,因此当前正文链、页面元信息链与 Sidebar 聚合链已不存在第二套 page id 映射口径。
|
||||
2026-04-14 C3 第三刀补记:在此前真实 `/api/documents/save -> /api/bridge/request|trace` smoke 已确认可回查的基础上,本轮又补了 `src/lib/documents/bridge.test.ts` 的成功路径断言,明确 `executeSaveBridgeCommand` 在 revision/conflictDetectionKey 新协议下仍会调用 `recordBridgeCommandArtifacts`,并把同一 `requestId/traceId/commandId` 暴露给回查入口。因此当前“保存成功时可回查 command log、domain event 与 trace”已具备历史 HTTP smoke 与当前单测的双重闭环。
|
||||
2026-04-14 C3 第四刀补记:`save-command-adapter` 已进一步切到与页面元信息链一致的最小运行时接缝。当前 `documents.save` 会先构造 `functionName=documents:updateContent`、`payloadJson`、`args` 组成的 bridge mutation request,再由统一执行器实际调用 Convex;这让正文保存不再长期停留在“adapter 内直接手写 `client.mutation(...)`”的临时态,同时保留现有冲突归一和落账逻辑不变。
|
||||
2026-04-15 Phase 5 第一刀补记:`search.documents`、`search.recent` 已补齐到主仓 Rust query/runtime 主链。当前 `rust/crates/core-protocol` 新增 `SearchDocuments/SearchRecent`,`storage-convex-bridge` 与 `bridge-runtime` 已支持 query name -> Convex function 映射和运行时分发;`rust/crates/index-fts` 新增 `evaluate_search_documents`,负责标题/正文/思维导图/表格/附件的召回合并、排序、高亮 snippet 与 OCR 待补队列决策。`wolai-frontend/src/app/api/search/documents/route.ts` 现仅保留参数整理、原始数据装载与 HTTP 回传,不再持有 TS 侧的核心 ranking/snippet 逻辑;`/api/search/recent` 继续只承担最近访问写入 side-effect。
|
||||
|
||||
## 6. Phase D:Mindmap 与 OnlyOffice 边界接入
|
||||
|
||||
完成目标:
|
||||
|
||||
- 保留 `mnote` 当前成熟的 Mindmap 与 OnlyOffice 前端实现。
|
||||
- 把数据边界、对象边界和回调边界逐步纳入 Rust adapter / protocol 规划,而不是复制第二套对象页壳。
|
||||
|
||||
Checklist:
|
||||
|
||||
- [x] 锁定 Mindmap 当前主组件和嵌入链路,确认 `MindmapBlock.tsx` 同时服务 BlockNote 内嵌块与独立全屏页。
|
||||
- [x] 盘点 Mindmap 数据入口,锁定 `mindmapLocalStore.ts`、`mindmapOps.ts`、`app/api/mindmap/**`、`app/mindmap/**`。
|
||||
- [x] 把 Mindmap 的前端交互状态与本体数据/ops 边界拆开,后者逐步映射到 Rust 协议层。
|
||||
- [x] 明确 `mnote-rust/app/documents/[id]/mindmap/page.tsx` 等对象页壳不进入主仓主线。
|
||||
- [x] 为 Mindmap 设计最小 adapter 目标,至少覆盖节点树、节点引用、节点操作日志和与 page/block 的绑定关系。
|
||||
- [x] 锁定 OnlyOffice 当前页面和 API 边界,确认 `src/app/onlyoffice/`、`src/app/api/onlyoffice/**`、`src/components/onlyoffice/` 的职责分工。
|
||||
- [x] 保留根目录 `src/components/onlyoffice/` 的静态资源、插件和数据目录,不做误删、不做迁移式替换。
|
||||
- [x] 把 OnlyOffice 的对象解析、签名、callback、forcesave、代理请求边界整理成 Rust adapter 的明确目标。
|
||||
- [x] 继续保持“正文只嵌文件入口,OnlyOffice 在独立页面运行”的产品形态。
|
||||
- [x] 统一 Mindmap 与 OnlyOffice 的页面标识、附件标识、页面归属和追踪字段。
|
||||
|
||||
2026-04-14 Phase D 盘点补记:已新增 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/mindmap-onlyoffice-boundary.md`,固定 `MindmapBlock.tsx` 与独立全屏页共用同一核心组件、`mindmapLocalStore.ts`/`mindmapOps.ts`/`app/api/mindmap/**` 的数据与 ops 边界,以及 OnlyOffice 的 `page -> client -> sign/proxy/callback/forcesave` 职责分层。文档同时明确 `MediaBlock` 仅作为 Office 附件入口、`/onlyoffice` 继续作为独立编辑页面,且历史仓中的对象页壳与第二套 sidebar 只保留为参考实现。
|
||||
2026-04-14 Phase D 收口补记:已把 Mindmap API 与组件侧统一到与 OnlyOffice 对齐的业务标识口径。当前 Mindmap 统一使用 `pageId=documentId`、`attachmentId=mindmapId`、`workspaceId` 作为页面归属,并在 `/api/mindmap/**` 返回稳定的 `requestId/traceId` 元信息;OnlyOffice 继续使用 `documentId + assetId` 作为页面/附件标识,因此两条链后续接 Rust adapter 时不再需要第二套 page/attachment 映射。
|
||||
2026-04-14 Phase D 浏览器回归补记:已新增 `/mnt/Data1T/mnote/scripts/task021-mindmap-ui-regression.js` 并在真实本地实例 `http://127.0.0.1:3001` 上执行。脚本使用当前共享的 `MindmapBlockView` 全屏入口 `/mindmap/[docId]/[mindmapId]`,验证页面 `data-page-id/data-document-id/data-attachment-id/data-mindmap-id/data-workspace-id` 元信息、工具栏与“大纲”侧栏渲染、子节点新增与删除后的持久化、以及 `/api/mindmap/**` 返回的 `requestId/traceId` 会同步回 DOM。考虑到内嵌块与独立页共用同一核心组件,这轮浏览器回归可视为覆盖当前共享交互/保存内核,实跑未见退化。
|
||||
2026-04-15 Phase D OnlyOffice callback 收口补记:已为 `OnlyOffice callback` 新增最小命令 `media.assets.replace_storage`,同步补齐 `rust/crates/core-protocol` 的 `ReplaceMediaAssetStorage`、`rust/crates/storage-convex-bridge` 的 command name -> Convex mutation 映射,以及主仓 `buildDocumentBridgeMutationRequest` 的运行时接缝。`src/app/api/onlyoffice/callback/route.ts` 现保留下载文件、上传到 Convex Files 获得 `storageId` 的现有流程,但最终写回已改为经 `buildDocumentBridgeContextWithActor -> buildDocumentCommandEnvelope -> executeMediaAssetWritebackBridgeCommand` 进入统一 bridge 边界,再调用 `api.mediaAssets.replaceStorageFromUpload`。配套 `src/app/api/onlyoffice/callback/route.test.ts`、`src/lib/documents/bridge.test.ts`、Rust 单测与 `scripts/task022-onlyoffice-ui-regression.js` 已通过,确认 `storage_id` 在真实浏览器里继续发生变化。
|
||||
|
||||
Phase D 验收:
|
||||
|
||||
- [x] Mindmap 与 OnlyOffice 的前端界面仍以 `mnote` 当前实现为主,没有引入第二套主壳。
|
||||
- [x] Mindmap 的节点/操作边界与 OnlyOffice 的签名/callback/forcesave 边界都已进入统一规划。
|
||||
- [x] `src/components/onlyoffice/` 保持可用且未被误删。
|
||||
- [x] `adapter-onlyoffice`、`adapter-mindmap` 仍作为后置实现项,而不是本阶段强行复制进主线。
|
||||
|
||||
## 7. Phase E:历史收口与单仓执行统一
|
||||
|
||||
完成目标:
|
||||
|
||||
- `mnote-rust` 正式退出主产品角色。
|
||||
- `mnote` 主仓成为唯一启动、保存、规划和验证入口。
|
||||
|
||||
Checklist:
|
||||
|
||||
- [x] 统一团队口径:主产品仓只有 `/mnt/Data1T/mnote`,`/mnt/Data1T/mnote-rust` 只保留为历史参考和资产来源。
|
||||
- [x] 把所有新的设计、执行清单、架构说明优先写入 `/mnt/Data1T/mnote/design/` 与 `/mnt/Data1T/mnote/rust/design/`。
|
||||
- [x] 复查仓库脚本和说明文档,去掉“先进 `mnote-rust` 再启动”的旧叙事。
|
||||
- [x] 统一根目录脚本入口,让仓库级脚本只从 `/mnt/Data1T/mnote/scripts/` 发起,再按需调用 `cargo --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml ...`。
|
||||
- [x] 对仍需保留的 `mnote-rust` 资料按“核心规范 / 历史阶段 / 参考实现 / 错误方向”分类,避免后续误复制。
|
||||
- [x] 把已确认不应主线保留的对象页壳、diagnostics 页、smoke 页继续留在历史仓,不进入主仓实现。
|
||||
- [x] 对主仓内新增的 Rust 目录建立最小维护约定,明确谁负责 workspace、谁负责 bridge、谁负责接入链。
|
||||
- [x] 重新审查 `ARCHITECTURE.md`、`AGENTS.md`、`rust-kernel-backport-plan.md` 与本 checklist,保证四者口径一致。
|
||||
- [x] 固定“哪些目录默认不动”的硬边界,特别是 `wolai-frontend/`、`wolai-backend/`、`infra/convex/`、`src/components/onlyoffice/`。
|
||||
|
||||
Phase E 验收:
|
||||
|
||||
- [x] 后续协作默认只进入 `mnote` 主仓规划和开发。
|
||||
- [x] 主仓文档和脚本不再把 `mnote-rust` 叙述为主执行入口。
|
||||
- [x] 历史仓的保留范围、参考价值和禁止误复制范围都已固定下来。
|
||||
|
||||
2026-04-14 Phase E 收口补记:已新增 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-single-repo-maintenance-boundary.md`,统一记录主仓执行入口、历史仓资料分类、禁止误复制清单与 workspace/bridge/接入链责任面;同时已复查根 `package.json` 与 `scripts/desktop-hot.js`,确认仓库级脚本仍只从 `/mnt/Data1T/mnote/scripts/` 发起,且 `ARCHITECTURE.md` 已移除把 `mnote-rust` 写成未来执行承载面的表述。
|
||||
|
||||
## 8. 发布前总审计(跨阶段)
|
||||
|
||||
- [x] 为 `/mnt/Data1T/mnote/rust/` 补基础验证矩阵,至少覆盖 `cargo metadata`、`cargo check`、必要 crate 单测。
|
||||
- [x] 为 bridge 入口补集成 smoke,验证最小查询、最小写入、日志生成、事件生成和错误返回。
|
||||
- [x] 为文档页元信息、Sidebar、BlockNote 保存链补端到端回归,确认 UI 未因为协议层切换而退化。
|
||||
- [x] 为 Mindmap 补交互回归,重点验证内嵌块、独立页、数据保存、节点操作和工具栏/侧栏行为。
|
||||
- [x] 为 OnlyOffice 补回归,重点验证签名、代理、callback、forcesave、文档打开和静态资源可用性。
|
||||
- [x] 检查主仓启动路径,确保开发者只需进入 `/mnt/Data1T/mnote` 就能完成前端、后端、Rust、OnlyOffice 相关联调。
|
||||
- [x] 检查保存口径,确认页面、块、文档、索引的最终真相仍然落在 Convex,而不是被临时缓存目录劫持。
|
||||
- [x] 检查 `trace_id`、`request_id`、`workspace_id`、`page_id` 在前端、bridge、Convex、日志中的一致性。
|
||||
- [x] 对本次回迁引入的所有新目录和新文档做一次清点,确认没有误带入 `mnote-rust` 的历史噪音。
|
||||
- [x] 为统一观测面补正式 Rust 能力面,至少包含 `bridge_request_get`、`bridge_trace_get`、`bridge_command_get`、`event_replay`、`index_rebuild` 这组 query/job/tool,并确认 Web 回查入口不再私有直连。
|
||||
|
||||
2026-04-14 发布前审计补记:已新增 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-backport-audit-inventory.md`,清点主仓 `rust/` 已并入的 workspace/crates/设计文档、本轮新增的根 `design/` 文档,以及当前仍未并入主线的 `adapter-*`、`mnote-cli`、`bridge/scripts/fixtures/tests` 目录。文档同时核对了 `package.json`、`scripts/desktop-hot.js` 与 `AGENTS.md` 的主仓启动口径,确认开发者只需进入 `/mnt/Data1T/mnote` 即可找到前端、后端、Rust 与 OnlyOffice 的联调入口。
|
||||
- 2026-04-15 task-034 补记:已在 `rust/crates/core-protocol` 增补 `GetBridgeRequest/GetBridgeTrace/GetBridgeCommand` 以及 `bridge_request_get`、`bridge_trace_get`、`bridge_command_get`、`event_replay`、`index_rebuild` 五个统一观测/恢复工具;`rust/crates/storage-convex-bridge` 已补 query name -> Convex `bridgeLogs:*` 映射;`rust/crates/bridge-runtime` 已支持这组 query/job 的 plan/result 输出,并把 `index-fts::rebuild_from_events` 暴露为正式 `index_rebuild` 恢复入口。前端 `/api/bridge/request`、`/api/bridge/trace` 已切到 `buildDocumentQueryEnvelope -> resolveRustBridgeQueryPlan -> executeRustBridgeQueryTransport` 主链,同时支持按 `commandId` 过滤与统一排序返回;`src/lib/documents/bridge-log.ts` 也已把命令日志/领域事件状态显式化,为后续失败态、冲突态与补偿落账提供稳定边界。配套验证已通过全量 `cargo test --manifest-path rust/Cargo.toml` 与 bridge 定向 eslint。
|
||||
- 2026-04-15 OnlyOffice 升级与实跑补记:已把 `8082` 文档服务从旧仓 `mnote-rust/infra/onlyoffice/docker-compose.yml` 迁回当前主仓 `/mnt/Data1T/mnote/infra/onlyoffice/docker-compose.yml`,并升级到 `onlyoffice/documentserver:9.3.1`。同时修复了 `/onlyoffice/plugins/*` 被 middleware 重定向到 `/auth` 导致自定义插件桥无法 ready 的问题,为 `wolai-frontend/public/onlyoffice/plugins/agent-tools/` 补齐非可视插件配置与 `plugins.js` 加载。最终 `scripts/task022-onlyoffice-ui-regression.js` 已在真实浏览器中通过,覆盖文档打开、`oo_insert_text` 插件调用、`同步保存`、callback 写回以及 `storage_id` 刷新变化闭环。
|
||||
- 2026-04-15 OnlyOffice callback bridge 补记:本轮在复跑 `scripts/task022-onlyoffice-ui-regression.js` 前,曾因 `/auth` 快速登录触发 `POST /api/auth -> Could not find public function for 'auth:signIn'` 导致浏览器脚本卡在登录页;已通过 `cd /mnt/Data1T/mnote/wolai-frontend && pnpm exec convex dev --once --tail-logs disable --env-file ../.env.all --run ping:ping` 重新注册 Convex functions 后恢复。恢复后同一浏览器脚本再次通过,确认新接入的 `media.assets.replace_storage` bridge 命令没有破坏 OnlyOffice 打开、插件桥、forcesave、callback 与 `storage_id` 更新闭环。
|
||||
- 2026-04-14 浏览器实跑补记:`task-019-document-ui-regression.js` 已在真实浏览器中覆盖“首页进入文档页 -> Sidebar 可见 -> 修改标题 -> 修改 BlockNote 正文 -> 等待保存成功 -> 刷新后仍保留”的最小闭环。运行前曾因本地 Convex 实例缺少 `workspaces:ensureDefaultWorkspace` 导致首页 `500`,本轮已通过 `pnpm exec convex dev --once --tail-logs disable --env-file ../.env.all --run ping:ping` 重新注册函数后恢复;最终浏览器脚本通过,且临时页面已清理。
|
||||
- [x] 对以下禁止事项做最终复查:不双仓并行、不跨仓链接、不复制第二套产品前端壳、不把对象页壳/diagnostics/smoke 页带入主线、不散落多个 Rust 源码根。
|
||||
- [x] 产出最终 cutover 文档,按能力域列清哪些 route 已仅剩 transport,哪些旧 TS 执行面必须继续迁移或后续删除。
|
||||
- [ ] 以“单仓路径可启动、可保存、可验证、可继续演进”作为发布前收口标准。
|
||||
|
||||
2026-04-14 实跑记录:已重新执行 `cargo metadata --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml --format-version 1`、`cargo check --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml`、`cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p core-domain`、`cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p core-protocol`、`cargo test --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml -p storage-convex-bridge`,均通过;其中 `cargo metadata` 已再次确认 workspace members 固定为 `core-domain`、`core-protocol`、`event-log`、`storage-convex-bridge`、`index-fts` 五个 crate。
|
||||
2026-04-14 源码审计补记:已新增 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/release-readiness-source-audit.md`。该文档基于当前 route、adapter、Convex bridgeLogs 与定向测试,确认 bridge 入口已经覆盖最小查询/写入/日志/事件/错误返回闭环;确认正文、页面元信息、块补丁与 OnlyOffice 附件写回的最终真相仍然落在 Convex mutation;确认 `trace_id`、`request_id`、`workspace_id`、`page_id` 在 route、bridge、Convex 日志与回查入口之间使用统一口径;并完成“不双仓并行、不跨仓链接、不复制第二套前端壳、不把 diagnostics/smoke 页带入主线、不散落多个 Rust 源码根”的源码级复查。
|
||||
2026-04-15 task-035 补记:已新增 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-cutover-v1.md`,把主仓当前 TS 执行面按 `RUST_OWNER / TS_TRANSPORT_KEEP / TS_COMPAT_PENDING / TS_LEGACY_DELETE` 四类状态完成盘点。文档明确了页面、块、查询聚合、AI、Mindmap、OnlyOffice 的当前归属,也明确了 `documents/create-child`、`documents/embed`、`documents/empty-trash`、`documents/purge`、`documents/template`、`mindmap-trash/empty`、`mindmap-ai/**` 与 `ai-agent` 非 Rust 核心工具面属于 Phase 8 前必须继续处理的旧面;同时为“何时允许宣布 Rust 成为唯一业务执行平面”补齐了四组 gate。
|
||||
2026-04-15 task-040/task-042/task-043/task-044 补记:AI 非 `doc_*` 核心工具中的 `search_web`、`image_read`、`slash_run` 已完成第一批 Rust Tool runtime cutover,`ai-agent/run` 不再保留这些工具的 TS 真执行兜底;`bridge-log/runtime` 已补齐统一失败态、冲突态与补偿态的第一批状态闭环,并覆盖页面主写链与 AI/Mindmap 写链;同时已新增 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-legacy-delete-list.md`,把 `documents/create-child`、`documents/embed`、`documents/empty-trash`、`documents/purge`、`documents/template`、`mindmap-trash/empty`、`builtins/**` 固定为第一批 `TS_LEGACY_DELETE`。至此 Phase 8 的文档口径已经允许在源码审计层面使用“Rust 已成为 mnote 的唯一业务执行平面;Web route 只保留 transport、auth、session、streaming、proxy 与 callback 壳”这句统一结论。
|
||||
@@ -0,0 +1,656 @@
|
||||
# [recycle] mnote 单仓收口 Rust 内核回迁计划
|
||||
|
||||
> 更新时间:2026-04-14
|
||||
>
|
||||
> 当前状态:已完成第一批 P0 crate 与核心设计文档的真实复制,复制目标目录为 `/mnt/Data1T/mnote/rust/`。
|
||||
|
||||
## 1. 结论
|
||||
|
||||
基于当前代码现状、`/mnt/Data1T/mnote-rust/design/` 的长期原则,以及你现在明确提出的要求:
|
||||
|
||||
- 不希望长期维护两个仓库
|
||||
- 不希望跨仓链接
|
||||
- 最终只希望在一个文件夹里启动和保存
|
||||
|
||||
当前最合适的路线已经明确并开始落地:
|
||||
|
||||
1. **停止把 `/mnt/Data1T/mnote-rust/` 继续当作未来主产品仓推进**
|
||||
2. **把 `mnote-rust` 的必要 Rust 内核内容真实复制进 `/mnt/Data1T/mnote/`**
|
||||
3. **最终只保留 `/mnt/Data1T/mnote/` 作为唯一主仓**
|
||||
4. **`/mnt/Data1T/mnote/wolai-frontend/` 继续承担主产品前端壳**
|
||||
5. **Rust 内核在 `/mnt/Data1T/mnote/rust/` 下统一收口**
|
||||
|
||||
一句话版:
|
||||
|
||||
> **不是双仓回接,而是单仓收口:把必要 Rust 内核复制进 `mnote`,最终只在 `/mnt/Data1T/mnote` 启动与保存。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 为什么要改成单仓复制,而不是继续双仓
|
||||
|
||||
## 2.1 先做 Rust 内核本身没有错
|
||||
|
||||
`/mnt/Data1T/mnote-rust/design/blueprint/ai-native-note-architecture-blueprint-v0.md` 与 `/mnt/Data1T/mnote-rust/design/phases/phase5/packaging-and-shell-strategy-v0.md` 已明确长期原则:
|
||||
|
||||
1. Convex 仍是主事实层
|
||||
2. Rust 负责统一协议、命令、查询、事件、索引、adapter
|
||||
3. 前端主壳继续复用成熟 React/Next
|
||||
|
||||
也就是说:
|
||||
|
||||
- “先写 Rust 内核”是对的
|
||||
- “不重写一套新前端主栈”也是对的
|
||||
|
||||
真正要调整的是载体:
|
||||
|
||||
- 不再把 `mnote-rust` 作为第二个长期主仓
|
||||
- 不再维持“一个仓写内核,一个仓跑产品前端”的长期分裂状态
|
||||
|
||||
## 2.2 双仓会持续制造启动、保存、认知和迁移成本
|
||||
|
||||
如果继续保留:
|
||||
|
||||
- `/mnt/Data1T/mnote/`
|
||||
- `/mnt/Data1T/mnote-rust/`
|
||||
|
||||
长期并行,会持续出现四类成本:
|
||||
|
||||
1. **启动成本**
|
||||
- 需要判断到底从哪个目录启动
|
||||
- 脚本、环境变量、依赖路径容易双份化
|
||||
|
||||
2. **保存口径成本**
|
||||
- 用户会天然希望“最终只有一个真实主仓”
|
||||
- 双仓天然让“哪个仓是现在的真主线”不断摇摆
|
||||
|
||||
3. **迁移成本**
|
||||
- 每做一项功能,都要先判断该落在 `mnote` 还是 `mnote-rust`
|
||||
- 这会导致团队持续在仓库边界上损耗
|
||||
|
||||
4. **认知成本**
|
||||
- 新协作者很难快速判断:
|
||||
- 哪个仓是产品主线
|
||||
- 哪个仓只是实验壳
|
||||
- 哪些文档是真约束
|
||||
|
||||
## 2.3 当前真正成熟的是 `mnote` 前端,而不是 `mnote-rust` 前端
|
||||
|
||||
当前成熟可直接承载产品面的能力主要在:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/onlyoffice/`
|
||||
|
||||
而 `mnote-rust` 当前前端更多是:
|
||||
|
||||
1. 联调壳
|
||||
2. smoke 壳
|
||||
3. 协议验证壳
|
||||
4. 部分对象页桥接壳
|
||||
|
||||
因此,单仓收口时,正确的继承关系应该是:
|
||||
|
||||
- **保留 `mnote` 前端主壳**
|
||||
- **复制 `mnote-rust` 的内核与协议成果**
|
||||
|
||||
而不是反过来复制一整套 `mnote-rust` 的 Next 前端壳。
|
||||
|
||||
---
|
||||
|
||||
## 3. 新目标架构
|
||||
|
||||
单仓收口后的目标结构应是:
|
||||
|
||||
```text
|
||||
/mnt/Data1T/mnote/
|
||||
wolai-frontend/ # 唯一主前端
|
||||
wolai-backend/ # 现有辅助后端
|
||||
infra/convex/ # 现有 Convex 基础设施
|
||||
src/components/onlyoffice/
|
||||
scripts/ # 仓库级统一启动入口
|
||||
design/ # 全局设计与总览
|
||||
rust/ # 新增:Rust 内核统一收口目录
|
||||
```
|
||||
|
||||
连接关系应变成:
|
||||
|
||||
```text
|
||||
mnote 前端
|
||||
-> mnote 内部 API / BFF / bridge
|
||||
-> rust/ 内核协议层
|
||||
-> Convex 主事实层 + Rust 事件/索引层
|
||||
```
|
||||
|
||||
这意味着:
|
||||
|
||||
1. 前端只认 `/mnt/Data1T/mnote`
|
||||
2. Rust 也只存在于 `/mnt/Data1T/mnote/rust`
|
||||
3. 启动、构建、联调都只从 `/mnt/Data1T/mnote` 发起
|
||||
|
||||
---
|
||||
|
||||
## 4. 单仓目录落位方案
|
||||
|
||||
推荐把 Rust 相关内容整体收进:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/`
|
||||
|
||||
而不要把 `crates/`、`design/`、`scripts/` 直接摊到仓库根。
|
||||
|
||||
目标结构:
|
||||
|
||||
```text
|
||||
/mnt/Data1T/mnote/
|
||||
wolai-frontend/
|
||||
wolai-backend/
|
||||
infra/convex/
|
||||
src/components/onlyoffice/
|
||||
scripts/
|
||||
design/
|
||||
rust/
|
||||
Cargo.toml
|
||||
Cargo.lock
|
||||
crates/
|
||||
core-domain/
|
||||
core-protocol/
|
||||
event-log/
|
||||
storage-convex-bridge/
|
||||
index-fts/
|
||||
bridge-runtime/ # Phase 1 最小真实执行器
|
||||
mnote-cli/ # Phase 2 已落位,先冻结 --json 协议
|
||||
adapter-onlyoffice/ # 后置按需引入
|
||||
adapter-mindmap/ # 后置按需引入
|
||||
adapter-legacy-mnote/ # 后置按需引入
|
||||
bridge/
|
||||
design/
|
||||
INDEX.md
|
||||
core/
|
||||
blueprint/
|
||||
phases/
|
||||
scripts/
|
||||
fixtures/
|
||||
tests/
|
||||
```
|
||||
|
||||
### 4.1 `rust/` 下各目录职责
|
||||
|
||||
- `rust/crates/`
|
||||
放所有 Rust workspace 成员,保持 Cargo workspace 语义集中。
|
||||
|
||||
- `rust/bridge/`
|
||||
放 Rust 侧桥接层、协议适配、生成代码、FFI/IPC glue。
|
||||
|
||||
- `rust/design/`
|
||||
放 Rust 专属设计、阶段文档、路线图。
|
||||
|
||||
- `rust/scripts/`
|
||||
放 Rust 专属构建、测试、检查、保存脚本。
|
||||
|
||||
- `rust/fixtures/` / `rust/tests/`
|
||||
放 Rust 侧测试资源与测试代码。
|
||||
|
||||
### 4.2 根目录哪些保持不动
|
||||
|
||||
以下现有目录建议明确保持不动:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/`
|
||||
- `/mnt/Data1T/mnote/wolai-backend/`
|
||||
- `/mnt/Data1T/mnote/infra/convex/`
|
||||
- `/mnt/Data1T/mnote/src/components/onlyoffice/`
|
||||
- `/mnt/Data1T/mnote/recycle/`
|
||||
- `/mnt/Data1T/mnote/scripts/`
|
||||
- `/mnt/Data1T/mnote/design/`
|
||||
|
||||
其中:
|
||||
|
||||
- `design/` 继续承担全局总览和跨系统路线说明
|
||||
- Rust 的详细阶段文档放进 `rust/design/`
|
||||
|
||||
---
|
||||
|
||||
## 5. 启动与保存口径必须怎么统一
|
||||
|
||||
## 5.1 启动口径
|
||||
|
||||
统一口径只有一个:
|
||||
|
||||
> 所有开发、构建、联调命令都从 `/mnt/Data1T/mnote` 发起。
|
||||
|
||||
执行方式:
|
||||
|
||||
1. 仓库级脚本统一保留在 `/mnt/Data1T/mnote/scripts/`
|
||||
2. Rust 命令只作为子任务存在,例如:
|
||||
- `cargo --manifest-path /mnt/Data1T/mnote/rust/Cargo.toml ...`
|
||||
3. 不再保留“先进 `mnote-rust` 再进 `mnote`”的双仓启动方式
|
||||
|
||||
## 5.2 保存口径
|
||||
|
||||
统一口径只有一个:
|
||||
|
||||
> 页面、块、文档、任务、索引的最终保存都只认 Convex 主事实层。
|
||||
|
||||
这意味着:
|
||||
|
||||
1. 人工操作、CLI、Agent 的写入都应先进入 Rust 统一命令层,再落到 Convex
|
||||
2. Rust 的 `event-log`、`index-fts`、fixtures、本地缓存都只是派生层
|
||||
3. 文件系统只保存:
|
||||
- 静态资源
|
||||
- 附件
|
||||
- 导出物
|
||||
- 缓存
|
||||
- 测试数据
|
||||
4. 文件系统不承载主业务真相
|
||||
|
||||
---
|
||||
|
||||
## 5.3 当前已落地的首批复制结果
|
||||
|
||||
截至 2026-04-13,以下内容已经真实复制进 `/mnt/Data1T/mnote/rust/`:
|
||||
|
||||
### 已复制的 workspace 根文件
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/Cargo.toml`
|
||||
- `/mnt/Data1T/mnote/rust/Cargo.lock`
|
||||
|
||||
### 已复制的 P0 crate
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/crates/core-domain/`
|
||||
- `/mnt/Data1T/mnote/rust/crates/core-protocol/`
|
||||
- `/mnt/Data1T/mnote/rust/crates/event-log/`
|
||||
- `/mnt/Data1T/mnote/rust/crates/storage-convex-bridge/`
|
||||
- `/mnt/Data1T/mnote/rust/crates/index-fts/`
|
||||
|
||||
### 已复制的核心设计文档
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/design/INDEX.md`
|
||||
- `/mnt/Data1T/mnote/rust/design/core/01-domain-model-v0.md`
|
||||
- `/mnt/Data1T/mnote/rust/design/core/02-command-query-tool-protocol-v0.md`
|
||||
- `/mnt/Data1T/mnote/rust/design/core/03-storage-event-indexing-v0.md`
|
||||
- `/mnt/Data1T/mnote/rust/design/core/04-onlyoffice-integration-boundary-v0.md`
|
||||
|
||||
这一状态意味着:
|
||||
|
||||
1. `mnote` 主仓里已经存在可继续扩展的 Rust workspace 根
|
||||
2. P0 内核定义已经不再只存在于 `/mnt/Data1T/mnote-rust/`
|
||||
3. 后续回迁工作可以直接以 `/mnt/Data1T/mnote/rust/` 为唯一落位继续推进
|
||||
|
||||
---
|
||||
|
||||
## 6. 第一阶段应该真实复制进 `mnote` 的内容
|
||||
|
||||
子 agent 的结论一致:第一阶段应复制“定义真相和协议”的内核,而不是复制第二套产品前端。
|
||||
|
||||
## 6.1 必须复制的 crate
|
||||
|
||||
### P0:第一阶段必须复制
|
||||
|
||||
以下 crate 应真实复制到:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/crates/`
|
||||
|
||||
#### `core-domain`
|
||||
|
||||
来源:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/crates/core-domain`
|
||||
|
||||
复制后职责:
|
||||
|
||||
1. 领域模型
|
||||
2. 一等对象定义
|
||||
3. ID / revision / 时间 /审计基础类型
|
||||
|
||||
#### `core-protocol`
|
||||
|
||||
来源:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/crates/core-protocol`
|
||||
|
||||
复制后职责:
|
||||
|
||||
1. `Command / Query / Tool` 统一协议壳
|
||||
2. actor/source/target/meta 结构
|
||||
3. `CreatePage / InsertBlock / SearchPages` 等协议模型
|
||||
|
||||
#### `event-log`
|
||||
|
||||
来源:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/crates/event-log`
|
||||
|
||||
复制后职责:
|
||||
|
||||
1. 命令日志
|
||||
2. 领域事件
|
||||
3. 写入后的事件生成规则
|
||||
|
||||
#### `storage-convex-bridge`
|
||||
|
||||
来源:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/crates/storage-convex-bridge`
|
||||
|
||||
复制后职责:
|
||||
|
||||
1. 协议到 Convex 的桥接
|
||||
2. 命令执行与回写
|
||||
3. 命令日志和事件落账
|
||||
|
||||
#### `index-fts`
|
||||
|
||||
来源:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/crates/index-fts`
|
||||
|
||||
复制后职责:
|
||||
|
||||
1. 可重建的索引与搜索层
|
||||
2. 事件投影
|
||||
3. page/block 搜索
|
||||
|
||||
### P1:建议第二阶段复制
|
||||
|
||||
#### `mnote-cli`
|
||||
|
||||
来源:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/crates/mnote-cli`
|
||||
|
||||
复制后职责:
|
||||
|
||||
1. 批处理入口
|
||||
2. 导入导出入口
|
||||
3. 内核 smoke 与排障入口
|
||||
|
||||
### P2:按需后置复制
|
||||
|
||||
#### `adapter-onlyoffice`
|
||||
|
||||
来源:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/crates/adapter-onlyoffice`
|
||||
|
||||
复制后职责:
|
||||
|
||||
1. OnlyOffice 资产定位
|
||||
2. 会话定位
|
||||
3. 任务边界适配
|
||||
|
||||
#### `adapter-mindmap`
|
||||
|
||||
来源:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/crates/adapter-mindmap`
|
||||
|
||||
复制后职责:
|
||||
|
||||
1. 思维导图结构化 ops
|
||||
2. 节点读取与引用边界
|
||||
|
||||
#### `adapter-legacy-mnote`
|
||||
|
||||
来源:
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/crates/adapter-legacy-mnote`
|
||||
|
||||
复制后职责:
|
||||
|
||||
1. 旧逻辑兼容
|
||||
2. 迁移期桥接
|
||||
|
||||
## 6.2 必须复制的设计资产
|
||||
|
||||
建议同步复制到:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/design/`
|
||||
|
||||
### 必复制
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/design/INDEX.md`
|
||||
- `/mnt/Data1T/mnote-rust/design/core/01-domain-model-v0.md`
|
||||
- `/mnt/Data1T/mnote-rust/design/core/02-command-query-tool-protocol-v0.md`
|
||||
- `/mnt/Data1T/mnote-rust/design/core/03-storage-event-indexing-v0.md`
|
||||
- `/mnt/Data1T/mnote-rust/design/core/04-onlyoffice-integration-boundary-v0.md`
|
||||
|
||||
原因:
|
||||
|
||||
1. 这些是长期稳定规范
|
||||
2. 直接对应 `core-domain / core-protocol / event-log / storage-convex-bridge / index-fts / adapter-onlyoffice`
|
||||
|
||||
### 暂不建议第一阶段复制的设计资产
|
||||
|
||||
以下内容先留在 `mnote-rust` 作为参考,不作为第一阶段主迁移目标:
|
||||
|
||||
- `design/blueprint/**`
|
||||
- `design/phases/**`
|
||||
- `design/execution/**`
|
||||
- `design/UI/**`
|
||||
|
||||
原因:
|
||||
|
||||
1. 这些更多是阶段叙事与历史推进资料
|
||||
2. 不是必须进入主仓的长期规范
|
||||
|
||||
---
|
||||
|
||||
## 7. 第一阶段明确不应复制的内容
|
||||
|
||||
这里要明确“禁止误复制”的范围。
|
||||
|
||||
## 7.1 不应继续作为第一阶段复制目标的前端内容
|
||||
|
||||
### 不复制为主线
|
||||
|
||||
- `/mnt/Data1T/mnote-rust/app/documents/[id]/mindmap/page.tsx`
|
||||
- `/mnt/Data1T/mnote-rust/app/documents/[id]/office/page.tsx`
|
||||
- `/mnt/Data1T/mnote-rust/components/sidebar/sidebar.tsx`
|
||||
- `/mnt/Data1T/mnote-rust/app/page.tsx`
|
||||
- `/mnt/Data1T/mnote-rust/app/onlyoffice/page.tsx`
|
||||
|
||||
原因:
|
||||
|
||||
1. 它们大多属于对象页壳、诊断页、smoke 页或单文件硬拼壳
|
||||
2. 不应再形成第二套产品前端主线
|
||||
|
||||
## 7.2 只能保留为参考 / smoke / 实验的内容
|
||||
|
||||
以下内容可以保留在 `mnote-rust` 参考,但不作为第一阶段复制目标:
|
||||
|
||||
1. `app/documents/[id]/mindmap/page.tsx` 里的 diagnostics 区
|
||||
2. `app/documents/[id]/office/page.tsx` 里的 diagnostics 区
|
||||
3. `app/onlyoffice/OnlyOfficeClientPage.tsx` 里的环境兼容 hack
|
||||
4. `app/page.tsx` 的 smoke 首页文案
|
||||
5. `phase7` 中已经标注为历史错误方向的文档
|
||||
|
||||
## 7.3 可复制的少量薄桥接代码
|
||||
|
||||
以下内容可以作为后续参考性复制对象,但仍不是第一阶段主目标:
|
||||
|
||||
- `components/editor/document-shell.tsx`
|
||||
- `components/editor/document-content.tsx`
|
||||
- `components/editor/blocknote-editor.tsx`
|
||||
- `hooks/use-convex-sidebar-data.ts`
|
||||
- `lib/sidebar-tree.ts`
|
||||
- `lib/file-tree/rows.ts`
|
||||
- `lib/document-embeds.ts`
|
||||
- `app/layout.tsx`
|
||||
|
||||
原因:
|
||||
|
||||
1. 这些属于宿主桥接、投影层或 provider 接线
|
||||
2. 不是完整产品前端壳
|
||||
|
||||
---
|
||||
|
||||
## 8. 单仓路线下,`mnote` 里最先该接入的 5 条能力链
|
||||
|
||||
这 5 条仍然是最值得优先打通的主线。
|
||||
|
||||
## 8.1 文档正文命令链
|
||||
|
||||
接入目标:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/save/route.ts`
|
||||
|
||||
目标:
|
||||
|
||||
1. 保留现有成熟 BlockNote 前端
|
||||
2. 把正文保存逐步切到 Rust command 层
|
||||
3. 让 block 操作、正文快照、stats 逐步进入统一协议
|
||||
|
||||
## 8.2 页面元信息与页面属性链
|
||||
|
||||
接入目标:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
|
||||
目标:
|
||||
|
||||
1. 标题更新
|
||||
2. 页面选项
|
||||
3. 文档统计
|
||||
4. 页面属性变更
|
||||
|
||||
## 8.3 Sidebar 数据聚合链
|
||||
|
||||
接入目标:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/sidebar.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/private-tree.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/sidebar/file-tree.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/hooks/use-convex-sidebar-data.ts`
|
||||
|
||||
目标:
|
||||
|
||||
1. 保留现有 UI
|
||||
2. 逐步让数据聚合层走 Rust query / index 层
|
||||
|
||||
## 8.4 Mindmap 数据链
|
||||
|
||||
接入目标:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/mindmap/mindmapLocalStore.ts`
|
||||
|
||||
目标:
|
||||
|
||||
1. 保留现有成熟思维导图前端
|
||||
2. 逐步把数据读写、ops 和边界纳入 Rust adapter / protocol
|
||||
|
||||
## 8.5 OnlyOffice 边界链
|
||||
|
||||
接入目标:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/onlyoffice/`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/onlyoffice/`
|
||||
- `/mnt/Data1T/mnote/src/components/onlyoffice/`
|
||||
|
||||
目标:
|
||||
|
||||
1. 保留现有 OnlyOffice 页面和静态资源
|
||||
2. 把对象解析、回调、签名和桥接逐步回到 Rust adapter 统一边界
|
||||
|
||||
当前补记:
|
||||
|
||||
1. `OnlyOffice callback` 已从 route 里直接调用 `api.mediaAssets.replaceStorageFromUpload`,推进到最小 `media.assets.replace_storage` bridge 命令边界。
|
||||
2. 当前主仓实现会先在 `wolai-frontend/src/app/api/onlyoffice/callback/route.ts` 下载 ONLYOFFICE 输出文件、上传到 Convex Files 获得 `storageId`,再通过 `buildDocumentBridgeContextWithActor`、`buildDocumentCommandEnvelope` 与 `executeMediaAssetWritebackBridgeCommand` 构造和执行与 `storage-convex-bridge` 对齐的运行时 request。
|
||||
3. Rust 侧已补 `core-protocol::ReplaceMediaAssetStorage` 与 `storage-convex-bridge` 的 `media.assets.replace_storage -> mediaAssets:replaceStorageFromUpload` 映射;因此 OnlyOffice 附件写回现已进入与页面元信息、正文保存一致的协议接缝,只是最终执行器仍在 TypeScript 侧落到 Convex mutation。
|
||||
|
||||
---
|
||||
|
||||
## 9. 推荐实施顺序
|
||||
|
||||
## Phase A:先把 Rust 内核真实复制进 `mnote`
|
||||
|
||||
第一阶段执行动作:
|
||||
|
||||
1. 在 `/mnt/Data1T/mnote/` 下建立 `/rust/`
|
||||
2. 复制 `P0` crate
|
||||
3. 复制 `design/core` 四份长期文档和 `design/INDEX.md`
|
||||
4. 建立新的 `rust/Cargo.toml` 与 `rust/Cargo.lock`
|
||||
5. 不动 `mnote` 主前端壳
|
||||
6. 不复制 `mnote-rust` 的第二套产品前端壳
|
||||
|
||||
当前状态:
|
||||
|
||||
- 以上动作已经完成
|
||||
- `bridge/` 已初始化最小说明目录,但 `scripts/`、`fixtures/`、`tests/` 仍未初始化
|
||||
- `mnote-cli` 已并入当前 workspace,`adapter-*` 仍未并入
|
||||
|
||||
## Phase B:在 `mnote` 中建立 Rust bridge 接口
|
||||
|
||||
执行动作:
|
||||
|
||||
1. 在 `mnote` 现有 API/BFF 层加 Rust command/query 入口
|
||||
2. 保留当前 UI,不先大改组件
|
||||
3. 先打通低风险调用链
|
||||
|
||||
当前建议的第一批 bridge 入口落位:
|
||||
|
||||
1. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/content/route.ts`
|
||||
- 作为优先读入口,后续先接 `storage-convex-bridge` 的 `read_path`
|
||||
2. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/save/route.ts`
|
||||
- 作为优先写入口,后续先接 `write_path`
|
||||
3. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/title/route.ts`
|
||||
- 作为页面标题更新的低风险写入口
|
||||
4. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/stats/route.ts`
|
||||
- 作为页面统计的低风险入口
|
||||
5. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/sidebar/route.ts`
|
||||
- 作为 Sidebar 聚合读入口,后续切 Rust query / index
|
||||
6. `/mnt/Data1T/mnote/wolai-frontend/src/app/api/blocks/patch/route.ts`
|
||||
- 作为块级局部写入口,适合正文链路第二步接入
|
||||
|
||||
当前判断:
|
||||
|
||||
1. `storage-convex-bridge` crate 内已经具备 `context / validation / read_path / write_path / mapping` 的桥接骨架
|
||||
2. `mnote` 主仓已为 `documents.title.update`、`documents.stats.update`、`documents.options.update`、`documents.save` 补上与 `storage-convex-bridge::build_write_request` 对齐的最小运行时接缝,会先生成 `functionName/payloadJson/args` 形式的 bridge mutation request,再交给统一执行器落到 Convex
|
||||
3. 但主仓 API route 仍未直接调用 Rust crate,`documents.content`、`blocks.patch` 等链路也还没有切到同一层执行器,因此 Phase B 还不能视为完全关闭
|
||||
|
||||
## Phase C:先接低风险元信息,再接正文链
|
||||
|
||||
顺序建议:
|
||||
|
||||
1. 页面标题 / 页面选项
|
||||
2. Sidebar 数据聚合
|
||||
3. BlockNote 正文保存
|
||||
|
||||
## Phase D:再接 Mindmap 与 OnlyOffice
|
||||
|
||||
原则:
|
||||
|
||||
1. 前端仍然在 `mnote`
|
||||
2. 数据、ops、边界逐步切到 Rust adapter / protocol
|
||||
|
||||
## Phase E:让 `mnote-rust` 退出主产品角色
|
||||
|
||||
处理方式:
|
||||
|
||||
1. `mnote-rust` 可保留为历史参考或过渡仓
|
||||
2. 但不再承担短期产品主线
|
||||
3. 最终只保留 `/mnt/Data1T/mnote/` 作为可启动、可保存主仓
|
||||
4. 历史资料分类与主仓维护边界统一以 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-single-repo-maintenance-boundary.md` 为准
|
||||
|
||||
---
|
||||
|
||||
## 10. 最不该做的事
|
||||
|
||||
1. 不要继续把 `mnote-rust` 的 Next 前端补成完整产品前端
|
||||
2. 不要让 `mnote` 和 `mnote-rust` 长期各维护一套产品前端
|
||||
3. 不要跨仓链接代码来“假装已迁移”
|
||||
4. 不要把对象页壳、诊断页、smoke 页当作主产品实现复制进来
|
||||
5. 不要把 Rust 内容散落复制到 `mnote` 根目录多个平行源码根
|
||||
|
||||
---
|
||||
|
||||
## 11. 一句话路线判断
|
||||
|
||||
最优路线不是:
|
||||
|
||||
> 继续在 `mnote-rust` 里补前端,再想办法替换 `mnote`
|
||||
|
||||
而是:
|
||||
|
||||
> 把 `mnote-rust` 的必要 Rust 内核真实复制进 `/mnt/Data1T/mnote/rust/`,保留 `mnote` 现有成熟前端作为唯一主产品壳,最终只在 `/mnt/Data1T/mnote` 这个单仓里启动和保存。
|
||||
@@ -0,0 +1,388 @@
|
||||
# [recycle] mnote Rust 内核替换剩余目标清单
|
||||
|
||||
> 更新时间:2026-04-15
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-backport-plan.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-backport-phase-checklist.md`
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
|
||||
## 1. 本文目的
|
||||
|
||||
本文不再回答“Rust 内容是否已经回迁到主仓”,而是直接回答:
|
||||
|
||||
> **距离“用 Rust 内核替换原有内核,并让绝大部分功能 CLI 化、可供 AI 自主编辑”这一最终目标,我们现在还差什么。**
|
||||
|
||||
当前结论很明确:
|
||||
|
||||
- **单仓收口已经基本完成**
|
||||
- **Rust workspace 与 P0 crate 已经落位**
|
||||
- **部分前端 API 已经接入 Rust 风格协议与 bridge 接缝**
|
||||
- **但“Rust 真正成为唯一执行内核”这件事还没有完成**
|
||||
|
||||
也就是说,当前更接近:
|
||||
|
||||
> **“前端/Node 先学会说 Rust 协议”**
|
||||
|
||||
而不是:
|
||||
|
||||
> **“产品已经由 Rust 内核统一执行”**
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前已经完成的基础
|
||||
|
||||
截至目前,已经完成的只是替换前的基础设施准备:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/` 已成为主仓内唯一 Rust workspace 根。
|
||||
- `core-domain`、`core-protocol`、`event-log`、`storage-convex-bridge`、`index-fts` 已落位。
|
||||
- `rust/design/core/01~04` 核心设计文档已落位。
|
||||
- 文档内容、标题、统计、Sidebar 等链路,已经开始使用统一 envelope、`request_id`、`trace_id`、`idempotency_key` 等 bridge 元信息。
|
||||
- `command_logs`、`domain_events` 已有首批落账能力。
|
||||
|
||||
这些工作解决的是:
|
||||
|
||||
- 单仓问题
|
||||
- 协议问题
|
||||
- 目录问题
|
||||
- 第一批接缝问题
|
||||
|
||||
**它们还没有解决“原内核是否已经被 Rust 取代”的问题。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 还差的核心目标
|
||||
|
||||
## 3.1 还没有形成“Rust 唯一执行内核”
|
||||
|
||||
这是当前最大的缺口。
|
||||
|
||||
虽然已有一部分 route 在构造 Rust 风格 request,但真实执行主链仍然大量停留在 `Next.js route + TypeScript + Convex client`。
|
||||
|
||||
当前仍明显属于旧执行面的代表链路包括:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/create/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/delete/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/move/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/restore/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/documents/duplicate/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/search/documents/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/mindmap/[docId]/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/onlyoffice/*.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/media/*.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/tables/*.ts`
|
||||
|
||||
这说明目前还没有做到:
|
||||
|
||||
- 所有核心读写先进入 Rust `Command / Query / Tool` 层
|
||||
- 所有执行规则由 Rust 决定
|
||||
- Node/Next 只做 transport、auth、session、streaming、UI 适配
|
||||
|
||||
最终目标需要变成:
|
||||
|
||||
- Web route 只负责接请求、鉴权、转发、回流结果
|
||||
- Rust 负责真正的命令执行、查询聚合、约束校验、冲突处理、日志和事件生成
|
||||
|
||||
验收标准:
|
||||
|
||||
- 文档、块、Sidebar、搜索、页面树、Mindmap、OnlyOffice、媒体、表格等主链路都有 Rust 执行入口
|
||||
- 前端 route 不再手写业务规则和数据聚合
|
||||
- TypeScript 侧不再直接成为业务真内核
|
||||
|
||||
## 3.2 还没有完成“全域能力模型”的 Rust 化
|
||||
|
||||
当前 Rust workspace 只有 P0 通用内核 crate,缺的不是“再多几个通用 crate”,而是产品能力域本身还没被吸进 Rust。
|
||||
|
||||
还没有真正完成 Rust 化的能力域至少包括:
|
||||
|
||||
- 页面树与层级操作
|
||||
- 页面创建、移动、复制、删除、恢复、清空回收站
|
||||
- BlockNote block 级操作全量协议
|
||||
- 搜索与召回
|
||||
- 引用、反链、嵌入
|
||||
- 媒体与附件
|
||||
- 在线表格
|
||||
- Mindmap
|
||||
- OnlyOffice 会话、签名、回调、强制保存
|
||||
- AI 调用的写入工具面
|
||||
|
||||
当前 `core-domain` 和 `core-protocol` 更像底层骨架,但还没长成完整产品内核。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 每个产品域都有明确 Rust domain model、command、query、tool contract
|
||||
- 前端不再自行定义第二套 payload 形状
|
||||
- “页面系统”和“对象系统”不再由不同 TS route 各自发明规则
|
||||
|
||||
## 3.3 CLI 入口还没有建立起来
|
||||
|
||||
你的目标里有一条是关键约束:
|
||||
|
||||
> **绝大部分功能 CLI 化**
|
||||
|
||||
这件事当前还远未完成。
|
||||
|
||||
直接证据是:
|
||||
|
||||
- `mnote-cli` 已并入主仓 workspace,但当前还只是最小命令面与 `--json` 计划输出协议
|
||||
- `/mnt/Data1T/mnote/rust/scripts/` 还未初始化
|
||||
- `/mnt/Data1T/mnote/rust/bridge/` 已初始化最小说明目录,且 `crates/bridge-runtime` 已提供 Phase 1 最小真实执行样板
|
||||
- `/mnt/Data1T/mnote/rust/tests/`、`/mnt/Data1T/mnote/rust/fixtures/` 还未形成 CLI 驱动的验收体系
|
||||
|
||||
这意味着目前仍然缺少:
|
||||
|
||||
- 真正可执行而不止输出计划的统一 CLI 二进制入口
|
||||
- 可脚本化的命令集
|
||||
- 稳定的 stdout/stderr/json 输出协议
|
||||
- 面向 AI 的非交互调用模式
|
||||
- dry-run / plan / apply / rollback 风格能力
|
||||
|
||||
最终至少应该具备的 CLI 面包括:
|
||||
|
||||
- `page create/get/update/move/delete/restore/list`
|
||||
- `block insert/replace/move/delete/get`
|
||||
- `search query`
|
||||
- `sidebar dataset`
|
||||
- `mindmap get/put/op`
|
||||
- `onlyoffice sign/callback/forcesave/session`
|
||||
- `media upload/list/get/delete`
|
||||
- `table create/get/update`
|
||||
- `tool run <tool-name> --json`
|
||||
|
||||
验收标准:
|
||||
|
||||
- 核心功能可以不经过浏览器完成
|
||||
- 核心功能可以稳定返回 JSON
|
||||
- shell、脚本、AI agent 都能直接调用
|
||||
- CLI 和 Web 不再各维护一套业务实现
|
||||
|
||||
## 3.4 AI 还没有真正建立在 Rust 工具面之上
|
||||
|
||||
当前已经有 AI 入口:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.tsx`
|
||||
|
||||
但这套 AI 执行仍主要建立在前端/Node 工具注册表与页面侧 bridge 上,不是建立在 Rust 原生 tool protocol 上。
|
||||
|
||||
这会带来四个问题:
|
||||
|
||||
- AI 能调用的工具面和 Web 内部实现强耦合
|
||||
- AI 与 CLI 不是同一执行平面
|
||||
- AI 写入行为缺少统一事务语义
|
||||
- AI 很难获得稳定、可审计、可回放的编辑能力
|
||||
|
||||
为了实现“AI 可自行编辑”,至少还差以下目标:
|
||||
|
||||
- Rust 提供稳定的 `Tool` 执行协议,而不是只提供 `Command/Query` 壳
|
||||
- AI 调用和 CLI 调用共享同一工具注册面
|
||||
- 每个写入工具都支持明确的目标对象、权限校验、冲突返回、审计日志
|
||||
- 支持 `validate_only`、`dry_run`、`explain_plan` 之类的安全模式
|
||||
- 支持机器可消费的错误码,而不是前端文案式错误
|
||||
|
||||
最终目标不是“AI 像用户点按钮一样绕进前端”,而是:
|
||||
|
||||
> **AI 直接调用 Rust 工具内核完成读写,Web 只是展示层。**
|
||||
|
||||
验收标准:
|
||||
|
||||
- AI agent 使用的写入工具与 CLI 使用的工具完全同源
|
||||
- AI 的每次编辑都能追踪到 command、event、trace、目标对象和 actor
|
||||
- AI 可稳定执行页面编辑、块编辑、检索、结构化改写、批处理操作
|
||||
|
||||
## 3.5 观测、审计、幂等和失败恢复还只完成了首批链路
|
||||
|
||||
现在已有 `command_logs`、`domain_events`,但仍是首批写链路覆盖,不是全域治理。
|
||||
|
||||
仍缺的能力包括:
|
||||
|
||||
- 所有命令统一落账
|
||||
- 所有失败态统一编码
|
||||
- 统一重试与幂等语义
|
||||
- 统一冲突模型
|
||||
- 统一补偿与回放
|
||||
- 统一事件重建和索引重放
|
||||
|
||||
如果没有这一层,CLI 和 AI 即使能写,也不够稳定。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 任一写操作都能查 request、trace、command、domain event
|
||||
- 幂等 key 在 CLI、AI、Web 三侧语义一致
|
||||
- 冲突、拒绝、权限不足、对象不存在等错误有统一 code
|
||||
- 可从事件或命令日志重建关键派生层
|
||||
|
||||
2026-04-15 进展补记:
|
||||
|
||||
- `rust/crates/core-protocol` 与 `bridge-runtime` 已新增 `bridge_request_get`、`bridge_trace_get`、`bridge_command_get`、`event_replay`、`index_rebuild` 这组统一观测/恢复能力;当前 `/api/bridge/request` 与 `/api/bridge/trace` 也已切到 Rust query plan + TS transport 的同一路径,不再各自直连 Convex 查询。
|
||||
- `src/lib/documents/bridge-log.ts` 已把命令日志与领域事件的状态语义显式化,支持 `pending/succeeded/failed/rolled_back` 与 `pending/committed/rejected/failed` 两套状态模型,为后续冲突、失败和补偿写回提供稳定落点。
|
||||
|
||||
当前仍未关闭的缺口:
|
||||
|
||||
- 失败态、冲突态虽然已经在页面主写链与 AI/Mindmap 写链补上第一批落账,但仍未覆盖所有对象域写链。
|
||||
- `event_replay` / `index_rebuild` 目前已成为正式命令面,但还主要停留在 runtime/工具层,尚未形成完整的持久化游标、任务调度和断点续跑体系。
|
||||
- workspace 级总览、分页、按对象范围筛选等观测 UI 仍未完善。
|
||||
|
||||
## 3.6 搜索、索引和派生视图仍在持续切换中
|
||||
|
||||
2026-04-15 进展补记:
|
||||
|
||||
- `search.documents` 已不再把核心 ranking / snippet / filter 留在 TS route。当前 `/mnt/Data1T/mnote/wolai-frontend/src/app/api/search/documents/route.ts` 只保留参数校验、原始数据装载与 HTTP 回传;标题/正文/思维导图/表格/附件的匹配合并、高亮 snippet 与 OCR 待补队列决策已进入 `rust/crates/index-fts/src/lib.rs`。
|
||||
- `search.recent` 已补齐独立 Rust query 名称,并通过 runtime 返回最近访问结果;当前保留 `/api/search/recent` 作为“打开页面后写最近访问记录”的 side-effect 接口,不与搜索召回混在一条写链里。
|
||||
- `sidebar.dataset.list` 已从“只在 route 上挂一个 Rust queryName”推进为真实 runtime query transport,`/api/sidebar` 现会先经过 Rust runtime,再调用 `sidebar:datasetList`。
|
||||
|
||||
当前仍未完全关闭的缺口:
|
||||
|
||||
- 索引重建、校验与回放命令还未落到产品主路径。
|
||||
- `src/app/(app)/layout.tsx` 的 SSR 侧边栏初始数据仍复用现有 `loadSidebarDataFromConvex` helper,没有一并切到 runtime transport。
|
||||
|
||||
更新后的验收标准:
|
||||
|
||||
- `search_web`、`image_read`、`slash_run` 这类 AI 核心工具必须保持 `RUST_OWNER`,不能回退到 TS 真执行兜底。
|
||||
- `builtins/**` 中已被 Rust 替代的服务端真入口必须进入第一批 `TS_LEGACY_DELETE`,清单以 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-legacy-delete-list.md` 为准。
|
||||
|
||||
- 搜索结果由 Rust query / index 层给出。
|
||||
- TS 前端只做 UI 投影,不做核心排序与召回逻辑。
|
||||
- 索引可重建、可校验、可回放。
|
||||
|
||||
## 3.7 Mindmap 和 OnlyOffice 还没有进入真正的 Rust adapter 执行层
|
||||
|
||||
现在对这两个对象域,文档上已经明确了边界,但执行层仍主要在现有 TS/Convex 逻辑。
|
||||
|
||||
现状更接近:
|
||||
|
||||
- **边界想清楚了**
|
||||
- **前端形态保住了**
|
||||
- **但 Rust adapter 还没真正接管**
|
||||
|
||||
缺口主要包括:
|
||||
|
||||
- `adapter-mindmap` 尚未并入主仓
|
||||
- `adapter-onlyoffice` 尚未并入主仓
|
||||
- Mindmap 节点操作还没有稳定的 Rust ops 协议
|
||||
- OnlyOffice 的 sign / proxy / callback / forcesave 还没统一进入 Rust 对象适配层
|
||||
|
||||
验收标准:
|
||||
|
||||
- Mindmap 的结构操作可由 CLI 与 AI 直接调用
|
||||
- OnlyOffice 的对象能力具备统一 session/asset 边界
|
||||
- 不需要依赖前端 route 才能操作这些对象
|
||||
|
||||
## 3.8 仍缺一条“从旧内核切换到新内核”的明确割接路线
|
||||
|
||||
目前已有 backport plan,也有 phase checklist,但还缺一个更硬的最终割接视角:
|
||||
|
||||
- 哪些旧 TS route 会被逐步下线
|
||||
- 哪些功能先双写、再单写
|
||||
- 哪些功能允许长期保留在前端侧
|
||||
- 哪些功能必须强制进入 Rust
|
||||
- 何时可以宣布“原内核不再是主执行面”
|
||||
|
||||
如果没有这一条,项目会长期停留在“看起来在迁移,实际上双内核并存”的状态。
|
||||
|
||||
验收标准:
|
||||
|
||||
- 列出旧执行面的退役清单
|
||||
- 每个能力域有 cutover milestone
|
||||
- 明确宣布 Rust 成为唯一业务执行平面时的准入条件
|
||||
|
||||
2026-04-15 进展补记:
|
||||
|
||||
- 本轮已新增 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-cutover-v1.md`,首次把页面系统、块系统、查询聚合、AI、Mindmap、OnlyOffice、兼容接口统一盘点为 `RUST_OWNER / TS_TRANSPORT_KEEP / TS_COMPAT_PENDING / TS_LEGACY_DELETE` 四类状态。
|
||||
- 当前“不明确”的问题已经收口为“执行尚未完成”的问题:退役清单、阶段 gate 与最终宣布口径都已写清,但旧接口的物理删除和 AI 工具面的完全统一还未完成。
|
||||
|
||||
---
|
||||
|
||||
## 4. 对最终目标的重新拆解
|
||||
|
||||
如果目标是:
|
||||
|
||||
> **Rust 内核替换原有内核,绝大部分功能 CLI 化,AI 可以自行编辑**
|
||||
|
||||
那么最终至少要同时满足下面四件事。
|
||||
|
||||
## 4.1 Rust 是唯一业务执行平面
|
||||
|
||||
要求:
|
||||
|
||||
- Web、CLI、AI 都调用同一 Rust command/query/tool 内核
|
||||
- 前端不再是业务规则主载体
|
||||
|
||||
## 4.2 CLI 是一等公民,不是调试附属品
|
||||
|
||||
要求:
|
||||
|
||||
- 大部分核心能力都能通过 CLI 完成
|
||||
- 输出稳定 JSON
|
||||
- 支持脚本化、批处理和非交互执行
|
||||
|
||||
## 4.3 AI 只是 CLI/Tool 的智能调度者
|
||||
|
||||
要求:
|
||||
|
||||
- AI 不再依赖页面私有 bridge 才能编辑
|
||||
- AI 调用的每一步都可审计、可回放、可限权
|
||||
|
||||
## 4.4 Convex 继续是事实层,但不再直接暴露产品规则
|
||||
|
||||
要求:
|
||||
|
||||
- Convex 主要承担持久化与事实保存
|
||||
- 规则、协议、工具、索引、对象适配统一收进 Rust
|
||||
|
||||
---
|
||||
|
||||
## 5. 建议按优先级补齐的剩余里程碑
|
||||
|
||||
按最终目标倒推,接下来最应该补的不是 UI,而是下面六个里程碑。
|
||||
|
||||
### M1. 建立真实 Rust 执行入口
|
||||
|
||||
- 初始化 `rust/bridge/`
|
||||
- 让 Web route 可以调用真实 Rust 运行时,而不是只在 TS 中模拟 Rust request
|
||||
- 先覆盖 `documents.create/get/save/title/options/stats/sidebar/search`
|
||||
|
||||
### M2. 并入 `mnote-cli`
|
||||
|
||||
- 在主仓加入 CLI crate
|
||||
- 先定义稳定命令面和 JSON 输出协议
|
||||
- 让页面、块、搜索、Sidebar 至少先能命令行操作
|
||||
|
||||
### M3. 完成页面系统与块系统的 Rust 接管
|
||||
|
||||
- 页面创建、移动、删除、恢复、复制
|
||||
- block 插入、替换、移动、删除
|
||||
- 页面树与回收站
|
||||
|
||||
### M4. 完成搜索/索引/派生视图 Rust 化
|
||||
|
||||
- 把搜索、snippet、排序、Sidebar 数据集聚合切到 Rust
|
||||
- 建立索引重建与校验命令
|
||||
|
||||
### M5. 完成对象域 adapter
|
||||
|
||||
- `adapter-mindmap`
|
||||
- `adapter-onlyoffice`
|
||||
- 后续再看媒体、表格等对象域
|
||||
|
||||
### M6. 让 AI 与 CLI 共用同一 Tool 面
|
||||
|
||||
- AI 不再调用前端私有写入逻辑
|
||||
- 改为直接使用 Rust tool protocol
|
||||
- 加入 dry-run、权限、审计、回放能力
|
||||
|
||||
---
|
||||
|
||||
## 6. 一句话结论
|
||||
|
||||
当前不是“还差一点点就完成 Rust 内核替换”,而是:
|
||||
|
||||
> **我们已经完成了 Rust 内核替换前的单仓收口、协议奠基和首批接缝,但距离“Rust 真正取代旧内核,并让 CLI 与 AI 成为一等执行入口”还差一整层执行面重构。**
|
||||
|
||||
最关键的剩余目标只有三条:
|
||||
|
||||
- **把业务执行权从 TS route 真正移交给 Rust**
|
||||
- **把核心能力系统化地做成 CLI**
|
||||
- **让 AI 与 CLI 共用同一套 Rust tool 内核**
|
||||
|
||||
只要这三条没完成,就还不能说“原有内核已经被 Rust 替换”。
|
||||
@@ -0,0 +1,415 @@
|
||||
# [recycle] mnote Rust 内核替换总路线图 v1
|
||||
|
||||
> 更新时间:2026-04-15
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-backport-plan.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-backport-phase-checklist.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-missing-targets.md`
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
|
||||
## 1. 目标定义
|
||||
|
||||
本路线图对应的最终目标只有一句话:
|
||||
|
||||
> **用 Rust 内核取代当前 TypeScript/Next route 主导的业务执行面,让 Web、CLI、AI 共享同一套 Rust Command / Query / Tool 内核。**
|
||||
|
||||
这里包含三个同时成立的条件:
|
||||
|
||||
- Rust 成为唯一业务执行平面
|
||||
- 绝大部分核心能力都能通过 CLI 调用
|
||||
- AI 编辑能力建立在与 CLI 同源的 Rust 工具面上
|
||||
|
||||
如果只完成其中一部分,都不能算“Rust 内核已经替换原有内核”。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前阶段判断
|
||||
|
||||
当前仓库已经完成的是:
|
||||
|
||||
- 单仓收口
|
||||
- Rust workspace 落位
|
||||
- P0 crate 与核心设计文档落位
|
||||
- 首批 bridge 协议接缝
|
||||
- 首批日志与事件落账
|
||||
|
||||
当前仓库还没有完成的是:
|
||||
|
||||
- Rust 真实执行入口
|
||||
- `mnote-cli`
|
||||
- 全域能力域建模
|
||||
- AI/CLI 共用 Tool 面
|
||||
- 旧 TS 执行面的系统性退役
|
||||
|
||||
因此当前阶段应定义为:
|
||||
|
||||
> **Phase 0 完成,Phase 1 即将开始。**
|
||||
|
||||
其中:
|
||||
|
||||
- `Phase 0` = 单仓收口 + 协议奠基
|
||||
- `Phase 1` 以后才是“真正的内核替换”
|
||||
|
||||
---
|
||||
|
||||
## 3. 总体迁移原则
|
||||
|
||||
整个替换过程必须遵守下面六条原则。
|
||||
|
||||
### 3.1 Web 不再承载业务真规则
|
||||
|
||||
Next route 可以保留 transport、auth、session、streaming、SSR/BFF 职责,但不再长期承载核心业务执行规则。
|
||||
|
||||
### 3.2 CLI 与 AI 必须共用同一执行面
|
||||
|
||||
不能出现:
|
||||
|
||||
- CLI 走一套实现
|
||||
- AI 走一套实现
|
||||
- Web 再走第三套实现
|
||||
|
||||
最终只能保留一套 Rust 内核,三种入口共享。
|
||||
|
||||
### 3.3 Convex 继续是事实层,不是业务规则层
|
||||
|
||||
Convex 继续负责主事实保存,但对象规则、命令语义、查询聚合、索引、工具协议应逐步收回 Rust。
|
||||
|
||||
### 3.4 先做执行面替换,再做旧面退役
|
||||
|
||||
不能先删旧链路再补 Rust,也不能长期停留在双内核并行。
|
||||
|
||||
正确顺序是:
|
||||
|
||||
1. 建立 Rust 执行链
|
||||
2. 双入口对齐
|
||||
3. 完成回归
|
||||
4. 退役旧 TS 业务执行逻辑
|
||||
|
||||
### 3.5 先覆盖高频主链路,再覆盖对象域
|
||||
|
||||
优先级应是:
|
||||
|
||||
1. 页面系统
|
||||
2. 块系统
|
||||
3. Sidebar / 搜索 / 索引
|
||||
4. AI 写入工具面
|
||||
5. Mindmap / OnlyOffice / Media / Table
|
||||
|
||||
### 3.6 每一阶段都必须有割接标准
|
||||
|
||||
每个阶段都要明确:
|
||||
|
||||
- 哪些功能已由 Rust 接管
|
||||
- 哪些 TS route 仍是临时面
|
||||
- 哪些旧实现可退役
|
||||
|
||||
---
|
||||
|
||||
## 4. 分阶段路线
|
||||
|
||||
## Phase 1:建立真实 Rust 执行入口
|
||||
|
||||
目标:
|
||||
|
||||
- 在主仓内建立最小 Rust bridge/runtime
|
||||
- Web route 能调用真实 Rust 执行器
|
||||
- 不再只是在 TypeScript 中构造 Rust 风格 request
|
||||
|
||||
优先覆盖:
|
||||
|
||||
- `documents.meta`
|
||||
- `documents.content`
|
||||
- `documents.save`
|
||||
- `documents.title`
|
||||
- `documents.options`
|
||||
- `documents.stats`
|
||||
- `sidebar.dataset.list`
|
||||
- `search.documents`
|
||||
|
||||
阶段产出:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/bridge/`
|
||||
- 最小 runtime executor
|
||||
- Web -> Rust -> Convex 的真实样板链
|
||||
|
||||
阶段完成标准:
|
||||
|
||||
- 至少 1 条读链和 2 条写链真实经过 Rust 执行
|
||||
- 对应 TS route 不再直接持有业务拼装逻辑
|
||||
|
||||
## Phase 2:引入 `mnote-cli` 并冻结 JSON 协议
|
||||
|
||||
目标:
|
||||
|
||||
- 把 CLI 变成一等入口
|
||||
- 定义稳定的 machine-readable 输出
|
||||
- 为后续 AI 共用工具面打基础
|
||||
|
||||
首批 CLI 面:
|
||||
|
||||
- `page`
|
||||
- `block`
|
||||
- `search`
|
||||
- `sidebar`
|
||||
- `tool`
|
||||
|
||||
阶段产出:
|
||||
|
||||
- `rust/crates/mnote-cli/`
|
||||
- 统一 exit code 约定
|
||||
- `--json` 输出契约
|
||||
- `dry-run` / `validate-only` 基础能力
|
||||
|
||||
阶段完成标准:
|
||||
|
||||
- 核心页面和块操作能脱离浏览器完成
|
||||
- CLI 与 Web 调用同一 Rust 执行面
|
||||
|
||||
## Phase 3:页面系统 Rust 化
|
||||
|
||||
目标:
|
||||
|
||||
- 页面创建、移动、复制、删除、恢复、回收站等能力收口到 Rust
|
||||
- 页面树、父子关系、排序、引用更新不再由 TS route 零散实现
|
||||
|
||||
优先覆盖:
|
||||
|
||||
- `documents.create`
|
||||
- `documents.move`
|
||||
- `documents.delete`
|
||||
- `documents.restore`
|
||||
- `documents.duplicate`
|
||||
- `documents.empty-trash`
|
||||
- `documents.copy-tree`
|
||||
|
||||
阶段完成标准:
|
||||
|
||||
- 页面生命周期操作统一走 Rust command
|
||||
- 旧 TS route 只保留 transport 包装
|
||||
|
||||
## Phase 4:块系统 Rust 化
|
||||
|
||||
目标:
|
||||
|
||||
- 把 BlockNote 正文相关的结构化编辑命令真正变成 Rust 内核能力
|
||||
- AI/CLI 可直接调用块操作,不依赖浏览器交互细节
|
||||
|
||||
优先覆盖:
|
||||
|
||||
- `blocks.get`
|
||||
- `blocks.patch`
|
||||
- `blocks.move`
|
||||
- `blocks.embed`
|
||||
- `doc_insert_blocks`
|
||||
- `doc_replace_range`
|
||||
|
||||
阶段完成标准:
|
||||
|
||||
- Rust 拥有稳定的 block ops 协议
|
||||
- Web 编辑器只负责快照采集、渲染与交互
|
||||
|
||||
## Phase 5:搜索、索引与派生视图 Rust 化
|
||||
|
||||
目标:
|
||||
|
||||
- 把搜索、排序、snippet、Sidebar 数据集聚合统一移入 Rust
|
||||
- 建立索引重建和派生视图回放能力
|
||||
|
||||
优先覆盖:
|
||||
|
||||
- `search.documents`
|
||||
- `search.recent`
|
||||
- `sidebar.dataset.list`
|
||||
- 索引重建与校验命令
|
||||
|
||||
阶段完成标准:
|
||||
|
||||
- TS route 不再做核心召回和 ranking
|
||||
- `index-fts` 真正进入产品主路径
|
||||
|
||||
2026-04-15 Phase 5 第一刀补记:`search.documents`、`search.recent`、`sidebar.dataset.list` 已补齐到主仓 Rust query/runtime 主链。`rust/crates/core-protocol` 新增 `SearchDocuments/SearchRecent` 契约,`storage-convex-bridge` 新增 query name -> Convex function 映射,`bridge-runtime` 现同时支持 query plan 输出与“携带原始 dataset 时直接在 Rust 内执行并返回 result”。其中 `search.documents` 的召回后排序、snippet、高亮、OCR 待补队列决策已迁入 `rust/crates/index-fts` 的 `evaluate_search_documents`;`/api/search/documents` route 现仅保留参数归一化、Convex 原始数据拉取与 HTTP 回传,不再持有标题/正文/思维导图/表格/附件的打分合并逻辑。`/api/sidebar` 也已改为先经 Rust runtime 生成 `sidebar.dataset.list` plan,再通过统一 query transport 调用 `sidebar:datasetList`,不再只是挂一个 queryName 元信息。
|
||||
|
||||
## Phase 6:AI 与 CLI 共用 Rust Tool 面
|
||||
|
||||
目标:
|
||||
|
||||
- 让 AI 直接调用 Rust tool protocol
|
||||
- Web 中的 AI Agent 只成为对话和流式展示层
|
||||
|
||||
能力要求:
|
||||
|
||||
- `Tool` 注册表
|
||||
- 统一权限与对象目标
|
||||
- 统一错误码
|
||||
- 统一审计
|
||||
- `dry-run` / `validate-only` / `explain-plan`
|
||||
|
||||
阶段完成标准:
|
||||
|
||||
- AI 与 CLI 调用同一 Tool 面
|
||||
- AI 编辑结果可追踪到 command、event、trace
|
||||
|
||||
2026-04-15 Phase 6 补记:已把统一观测面补进 Rust `Tool` 注册表与 `bridge-runtime`。当前 `bridge_request_get`、`bridge_trace_get`、`bridge_command_get` 三条观测查询,以及 `event_replay`、`index_rebuild` 两条恢复/重建任务,已经成为 Rust 正式能力面;`/api/bridge/request`、`/api/bridge/trace` 也已改为先经 Rust runtime 生成 query plan,再由 TS 仅做 Convex transport。后续 CLI 与 AI 若要稳定运行,必须以这组能力为统一回查/恢复前置,不再允许各入口各自手写 trace 查询与索引恢复脚本。
|
||||
|
||||
## Phase 7:对象域 adapter 接入
|
||||
|
||||
目标:
|
||||
|
||||
- 在不改变当前前端形态的前提下,把对象域执行层移入 Rust adapter
|
||||
|
||||
优先对象域:
|
||||
|
||||
- `adapter-mindmap`
|
||||
- `adapter-onlyoffice`
|
||||
- 后续再扩展 media / table
|
||||
|
||||
阶段完成标准:
|
||||
|
||||
- Mindmap 与 OnlyOffice 都可由 CLI / AI 直接操作核心对象能力
|
||||
- 前端 route 不再是对象域业务真入口
|
||||
|
||||
## Phase 8:旧内核退役与总割接
|
||||
|
||||
目标:
|
||||
|
||||
- 明确哪些旧 TS route 只剩 transport
|
||||
- 明确哪些旧业务实现可删除
|
||||
- 正式宣布 Rust 成为唯一业务执行平面
|
||||
|
||||
阶段完成标准:
|
||||
|
||||
- Web、CLI、AI 全部指向同一 Rust 内核
|
||||
- 核心域不再存在第二套业务执行实现
|
||||
|
||||
2026-04-15 Phase 8 补记:当前主仓已新增 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/done/rust-kernel-cutover-v1.md` 作为最终割接文档。后续 Phase 8 不再接受“按感觉判断是否可以退役”的说法,而统一按该文档中的四组 gate 执行:页面/块/查询主链全部 Rust 持有,对象域只剩 transport 壳,AI/CLI 共用同一 Tool 面,以及旧 TS 兼容接口完成物理退役或明确降级。
|
||||
|
||||
---
|
||||
|
||||
## 5. 建议的切换顺序
|
||||
|
||||
为了降低风险,建议按下面顺序切换,而不是全面铺开。
|
||||
|
||||
### 批次 A:文档基础链
|
||||
|
||||
- `documents.meta`
|
||||
- `documents.content`
|
||||
- `documents.save`
|
||||
- `documents.title`
|
||||
- `documents.options`
|
||||
- `documents.stats`
|
||||
|
||||
### 批次 B:页面结构链
|
||||
|
||||
- `documents.create`
|
||||
- `documents.move`
|
||||
- `documents.delete`
|
||||
- `documents.restore`
|
||||
- `documents.duplicate`
|
||||
- `documents.copy-tree`
|
||||
|
||||
### 批次 C:块编辑链
|
||||
|
||||
- `blocks.get`
|
||||
- `blocks.patch`
|
||||
- `blocks.move`
|
||||
- `blocks.embed`
|
||||
|
||||
### 批次 D:查询聚合链
|
||||
|
||||
- `sidebar.dataset.list`
|
||||
- `search.documents`
|
||||
- `search.recent`
|
||||
|
||||
### 批次 E:AI/CLI 工具链
|
||||
|
||||
- `doc_get`
|
||||
- `doc_find`
|
||||
- `doc_insert_blocks`
|
||||
- `doc_replace_range`
|
||||
- `slash_run`
|
||||
|
||||
### 批次 F:对象域链
|
||||
|
||||
- Mindmap
|
||||
- OnlyOffice
|
||||
- Media
|
||||
- Table
|
||||
|
||||
---
|
||||
|
||||
## 6. 每阶段统一验收口径
|
||||
|
||||
无论哪个阶段,验收都应统一看下面六项。
|
||||
|
||||
### 6.1 执行入口是否真的进了 Rust
|
||||
|
||||
不是“TS 构造了 Rust 风格对象”,而是“Rust 真正执行了这条链”。
|
||||
|
||||
### 6.2 Web/CLI/AI 是否同源
|
||||
|
||||
如果一个能力还存在两套或三套实现,就不算完成。
|
||||
|
||||
### 6.3 是否具备 JSON 级输出与错误码
|
||||
|
||||
没有稳定 JSON 和错误码,就无法支撑 CLI 与 AI。
|
||||
|
||||
### 6.4 是否具备审计与回放
|
||||
|
||||
没有 command/event/trace,就无法长期稳定运行。
|
||||
|
||||
这里的“具备”不是指只落了一批日志表,而是至少同时满足:
|
||||
|
||||
- 任一写链都能回查 `request_id`、`trace_id`、`command_id`
|
||||
- 命令、事件、冲突、失败态使用统一状态语义
|
||||
- 至少有一条正式的 `event_replay` / `index_rebuild` 命令可被 CLI / AI / Web 复用
|
||||
- TS route 不再私有维护第二套排障与恢复入口
|
||||
|
||||
### 6.5 是否补了自动化验证
|
||||
|
||||
每阶段都必须至少补:
|
||||
|
||||
- Rust 单测
|
||||
- Web smoke
|
||||
- 必要时的浏览器回归
|
||||
- 对应 CLI smoke
|
||||
|
||||
### 6.6 是否明确了可退役旧面
|
||||
|
||||
必须显式标注:
|
||||
|
||||
- 哪些 TS 逻辑只剩壳
|
||||
- 哪些逻辑仍是临时态
|
||||
- 哪些代码已允许删除
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与防漂移要求
|
||||
|
||||
整个路线最容易失败的点有四个。
|
||||
|
||||
### 7.1 长期停留在“桥接完成即算完成”
|
||||
|
||||
这会导致项目永远停留在半替换状态。
|
||||
|
||||
### 7.2 CLI 迟迟不建立
|
||||
|
||||
如果不尽早建立 CLI,AI 最终还是会绕回 Web 私有逻辑。
|
||||
|
||||
### 7.3 对象域长期例外化
|
||||
|
||||
Mindmap、OnlyOffice、Media、Table 如果一直被当例外处理,最终不会形成统一内核。
|
||||
|
||||
### 7.4 旧 TS 执行面没有退役时点
|
||||
|
||||
如果不定义退役清单,旧逻辑会持续存活并反向污染新内核。
|
||||
|
||||
---
|
||||
|
||||
## 8. 一句话结论
|
||||
|
||||
这条路线不是“继续补几条 bridge”就能结束,而是要完成一次完整的执行面替换:
|
||||
|
||||
> **先把 Rust 变成真实执行器,再把 CLI 变成一等入口,最后让 AI 与 Web 共同收口到这套 Rust 内核。**
|
||||
|
||||
在这三个条件同时成立之前,都还不能宣布“Rust 内核已经替换原有内核”。
|
||||
@@ -0,0 +1,76 @@
|
||||
# [recycle] Sidebar Rust Query 目标说明
|
||||
|
||||
> 更新时间:2026-04-14
|
||||
>
|
||||
> 适用主仓:`/mnt/Data1T/mnote`
|
||||
|
||||
## 1. 当前起点
|
||||
|
||||
当前 Sidebar 仍是“同一份数据集,多处复用”的结构,但重复拼装已经先收口到共享 helper:
|
||||
|
||||
- 查询 payload / result 契约:`/mnt/Data1T/mnote/wolai-frontend/src/lib/sidebar-data.ts`
|
||||
- 服务端聚合入口:`/mnt/Data1T/mnote/wolai-frontend/src/lib/server/sidebar-data.ts`
|
||||
- API route:`/mnt/Data1T/mnote/wolai-frontend/src/app/api/sidebar/route.ts`
|
||||
- 实时订阅 hook:`/mnt/Data1T/mnote/wolai-frontend/src/hooks/use-convex-sidebar-data.ts`
|
||||
|
||||
当前事实:
|
||||
|
||||
- `sidebar.dataset.list` 已有稳定 payload:`{ workspace_id }`
|
||||
- `sidebar.dataset.list` 已有稳定 result:`active_workspace_id`、`workspaces`、`documents`、`trashed_documents`、`media_assets`、`trashed_media_assets`、`mindmap_assets`、`trashed_mindmap_assets`、`table_assets`、`trashed_table_assets`、`mindmap_docs`、`mindmap_asset_children`
|
||||
- `src/lib/sidebar-data.test.ts` 已对上述契约做冻结测试
|
||||
|
||||
## 2. Rust Query 最小目标
|
||||
|
||||
后续 Rust query 不直接输出 UI rows,而是只负责输出兼容 `SidebarInitialData` 的共享数据集。
|
||||
|
||||
最小目标:
|
||||
|
||||
- 输入:
|
||||
- `workspace_id`
|
||||
- 输出:
|
||||
- `active_workspace_id`
|
||||
- `workspaces`
|
||||
- `documents`
|
||||
- `trashed_documents`
|
||||
- `media_assets`
|
||||
- `trashed_media_assets`
|
||||
- `mindmap_assets`
|
||||
- `trashed_mindmap_assets`
|
||||
- `table_assets`
|
||||
- `trashed_table_assets`
|
||||
- `mindmap_docs`
|
||||
- `mindmap_asset_children`
|
||||
|
||||
## 3. 前后端边界
|
||||
|
||||
### 3.1 Rust query 负责
|
||||
|
||||
- 聚合工作区范围内页面、回收站、媒体、导图、表格的原始数据集
|
||||
- 保持字段命名与共享 contract 一致
|
||||
- 保持 `workspace_id` 作用域明确
|
||||
|
||||
### 3.2 前端继续负责
|
||||
|
||||
- `buildDocumentTree`
|
||||
- `buildVisibleRows`
|
||||
- `starred/public/shared/private/templates` 分区语义
|
||||
- `sort_order -> created_at` 排序投影
|
||||
- `doc/index.md/asset-folder/asset` 文件树行语义
|
||||
- 拖拽、剪贴板、展开折叠、回收站双 tab 等交互行为
|
||||
|
||||
## 4. 本轮明确不做的事
|
||||
|
||||
- 不复制 `mnote-rust` 的 `components/sidebar/sidebar.tsx`
|
||||
- 不引入第二套导航壳
|
||||
- 不在 Rust query 阶段直接输出前端渲染树
|
||||
- 不改变当前 Sidebar UI 结构与交互
|
||||
|
||||
## 5. 验证锚点
|
||||
|
||||
以下文件共同构成本轮 Sidebar Rust query 目标冻结点:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/sidebar-data.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/server/sidebar-data.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/api/sidebar/route.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/hooks/use-convex-sidebar-data.ts`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/sidebar-data.test.ts`
|
||||
@@ -0,0 +1,13 @@
|
||||
# old 回收稿索引
|
||||
|
||||
> 本目录只放 `[recycle]` 状态的历史设计稿。
|
||||
>
|
||||
> 分组说明:
|
||||
> - `process/`:已废弃,但属于历史上的草稿、方案、spike、路线稿
|
||||
> - `done/`:已废弃,但属于历史上的定稿、审计、报告、边界说明
|
||||
>
|
||||
> 大类说明:
|
||||
> - `01-tree-first-graph-kernel/`:已被新清单替代的旧内核稿
|
||||
> - `03-rust-web/`:已被新清单替代的旧 Rust Web 稿
|
||||
> - `05-editor-mainline/`:旧编辑器路线、选型与实验稿
|
||||
> - `08-legacy-rust-kernel/`:上一轮 Rust cutover 相关历史稿
|
||||
Reference in New Issue
Block a user