对齐 Wolai 侧栏体验并收拢设计入库

This commit is contained in:
lix-2026
2026-04-30 16:18:54 +08:00
parent 8c895b3dc0
commit afb2a5b8a0
89 changed files with 23188 additions and 84 deletions
@@ -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`,直到真实文档页完成切流为止。**
@@ -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 页面体验”
### 阶段 BLeptos-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 主线
### 阶段 CRust 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,再映射回编辑器展示
### 阶段 DAI-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` 的更完整形态收敛。
## 阶段 4AI-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 + eguiRust 原生 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**
- 前端:LeptosRust 编译 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. yrsCRDT 协作,必用)
- **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-tungsteniteWebSocket**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** 最小块编辑器模板吗?
@@ -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 参考。