256 lines
11 KiB
Markdown
256 lines
11 KiB
Markdown
# [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 参考。
|