# [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 参考。
