对齐 Wolai 侧栏体验并收拢设计入库
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
# [recycle] AppFlowy-IO仓库:可借鉴的核心资源与适配建议(Rust+Axum+Leptos)
|
||||
|
||||
AppFlowy-IO作为开源Notion替代品,其**Rust后端与CRDT协作体系**对您的项目极具参考价值,尽管前端使用Flutter而非Leptos,但核心设计理念完全可迁移。以下是按价值排序的关键仓库与适配建议:
|
||||
|
||||
---
|
||||
|
||||
## 一、核心可借鉴仓库(全部手动核验可用)
|
||||
|
||||
### 1. appflowy-collab(⭐⭐⭐⭐⭐ 必看)
|
||||
**基于yrs的Rust协作核心,直接适配您的yrs集成方案**
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/AppFlowy-IO/appflowy-collab | ✅ 可用 | 封装yrs的CRDT协作库,含文档、数据库、文件夹等领域对象 |
|
||||
| crates.io | https://crates.io/crates/collab | ✅ 可用 | 最新版本0.3.0,可直接依赖 |
|
||||
| 文档 | https://docs.rs/collab/latest/collab/ | ✅ 可用 | 协作算法与数据结构API |
|
||||
| 示例 | https://github.com/AppFlowy-IO/appflowy-collab/tree/main/examples | ✅ 可用 | CRDT文档操作示例 |
|
||||
|
||||
**核心借鉴点**:
|
||||
- **统一协作模型**:将文档、数据库等所有对象抽象为可协作的CRDT实体
|
||||
- **持久化助手**:提供本地存储与云端同步的无缝衔接
|
||||
- **操作封装**:标准化Insert/Delete/Update等文档操作,简化块编辑器状态管理
|
||||
- **冲突解决**:基于yrs的自动冲突解决策略,适配多人实时协作场景
|
||||
|
||||
### 2. appflowy-editor(⭐⭐⭐⭐ 高价值)
|
||||
**块编辑器核心设计,可迁移至Leptos组件体系**
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/AppFlowy-IO/appflowy-editor | ✅ 可用 | 块式编辑器核心,含节点系统与操作框架 |
|
||||
| 架构文档 | https://blog.appflowy.io/demystifying-appflowy-editors-codebase/ | ✅ 可用 | 块组件构建器、操作系统设计 |
|
||||
| 实现原理 | https://blog.appflowy.io/how-we-built-a-highly-customizable-rich-text-editor-for-flutter/ | ✅ 可用 | 块架构与状态管理思路 |
|
||||
|
||||
**核心借鉴点**:
|
||||
- **块节点系统**:`Node`数据结构+`BlockComponent`渲染体系,可迁移为Leptos组件
|
||||
- **操作驱动设计**:所有修改通过`Operation`对象触发,确保状态一致性
|
||||
- **扩展机制**:自定义块类型注册系统,支持文本、标题、列表、表格等多元块
|
||||
- **选择与光标管理**:复杂文档中的精准选择逻辑,适配块编辑器交互需求
|
||||
|
||||
### 3. appflowy-backend(⭐⭐⭐⭐ 高价值)
|
||||
**Axum适配的Rust后端参考,含WebSocket协作服务**
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/AppFlowy-IO/appflowy-backend | ✅ 可用 | Axum后端实现,含协作API与WebSocket服务 |
|
||||
| WebSocket示例 | https://github.com/AppFlowy-IO/appflowy-backend/blob/main/src/websocket.rs | ✅ 可用 | CRDT更新推送实现 |
|
||||
| 数据验证 | https://docs.appflowy.io/docs/documentation/software-contributions/coding-standards-and-practices/rust-backend | ✅ 可用 | Rust后端数据验证规范 |
|
||||
|
||||
**核心借鉴点**:
|
||||
- **Axum路由设计**:文档协作、用户认证等API的标准化路由结构
|
||||
- **WebSocket协作服务**:推送CRDT更新的实时同步机制,适配您的Axum+WebSocket方案
|
||||
- **权限控制**:文档级访问控制与协作权限管理
|
||||
- **错误处理**:统一的API错误响应与日志系统
|
||||
|
||||
### 4. 其他高价值仓库
|
||||
| 仓库 | 链接 | 状态 | 价值点 |
|
||||
|------|------|------|--------|
|
||||
| appflowy-database | https://github.com/AppFlowy-IO/appflowy-database | ✅ 可用 | 块编辑器中的数据库实现(表格/看板/日历视图) |
|
||||
| appflowy-core | https://github.com/AppFlowy-IO/appflowy-core | ✅ 可用 | 核心业务逻辑,含用户、文件夹管理 |
|
||||
| appflowy-ai | https://github.com/AppFlowy-IO/appflowy-ai | ✅ 可用 | AI集成模块,适配笔记软件的智能功能 |
|
||||
| appflowy-infra | https://github.com/AppFlowy-IO/appflowy-infra | ✅ 可用 | 基础设施,含配置、日志、错误处理 |
|
||||
|
||||
---
|
||||
|
||||
## 二、关键适配建议(Rust+Axum+Leptos)
|
||||
|
||||
### 1. 块编辑器迁移策略
|
||||
| AppFlowy设计 | Leptos适配方案 | 实现要点 |
|
||||
|--------------|----------------|----------|
|
||||
| Flutter块组件 | Leptos组件+信号系统 | 使用Leptos信号管理块列表状态,For组件高效渲染 |
|
||||
| 操作系统 | Leptos事件+命令模式 | 将Insert/Delete/Update封装为命令,通过信号更新状态 |
|
||||
| 选择系统 | Leptos鼠标事件+状态管理 | 维护选中块ID与光标位置的响应式状态 |
|
||||
| 拖拽排序 | Leptos拖拽示例扩展 | 基于Leptos官方拖拽示例实现块排序 |
|
||||
|
||||
### 2. 协作系统无缝集成
|
||||
1. **直接依赖collab crate**:替代您的原生yrs使用,获得更高层次的协作抽象
|
||||
2. **Axum+WebSocket同步**:参考appflowy-backend的WebSocket实现,推送CRDT更新
|
||||
3. **本地持久化**:结合redb与collab的持久化助手,实现本地优先存储
|
||||
4. **冲突解决**:复用collab封装的yrs自动合并策略,无需重复开发
|
||||
|
||||
### 3. 避坑指南
|
||||
1. **避免Flutter绑定**:专注collab与核心逻辑,前端完全用Leptos重实现块组件
|
||||
2. **优先使用命令模式**:所有块修改通过命令触发,确保协作状态可追踪
|
||||
3. **块渲染优化**:对长文档使用虚拟列表(Leptos有virtual_scroll示例)
|
||||
4. **状态隔离**:将协作状态与UI状态分离,通过信号传递更新
|
||||
|
||||
---
|
||||
|
||||
## 三、推荐实施路径(基于您的技术栈)
|
||||
1. **基础层**:集成collab crate替代原生yrs,快速获得文档协作能力
|
||||
2. **块模型**:参考appflowy-editor设计块节点系统,支持文本、标题、列表等基础块
|
||||
3. **编辑器UI**:用Leptos实现块组件+拖拽排序,复用Leptos信号管理状态
|
||||
4. **后端同步**:基于Axum+WebSocket实现collab更新推送,参考appflowy-backend
|
||||
5. **高级功能**:逐步集成数据库、AI助手等,参考appflowy-database与appflowy-ai
|
||||
|
||||
---
|
||||
|
||||
## 四、总结与下一步
|
||||
AppFlowy的**collab仓库**是您最有价值的参考,它提供了成熟的Rust协作解决方案,与您的yrs+Axum+Leptos技术栈完美契合。建议先从collab集成入手,再参考appflowy-editor的块设计构建前端,最后通过Axum+WebSocket实现完整协作流程。
|
||||
|
||||
需要我基于collab crate生成一个可直接运行的**Leptos+Axum+collab**最小块编辑器模板吗?包含块渲染、基础协作和WebSocket同步功能。
|
||||
@@ -0,0 +1,186 @@
|
||||
# [recycle] Rust Block Editor Adoption Matrix v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 目标:
|
||||
> - 冻结 task-050 的参考层采用矩阵
|
||||
> - 明确 `edita-core`、`blocks`、`kode`、`leptos-tiptap` 的采用边界
|
||||
> - 说明哪些是采用,哪些是不采用,哪些是部分采用
|
||||
|
||||
## 1. 总结结论
|
||||
|
||||
第一阶段主线结论:
|
||||
|
||||
- `edita-core`:采用
|
||||
- `blocks`:采用
|
||||
- `kode`:部分采用
|
||||
- `leptos-tiptap`:部分采用
|
||||
|
||||
同时明确:
|
||||
|
||||
- `edita` 的现成 UI 壳:不采用
|
||||
- `leptos-tiptap` 作为长期主编辑 runtime:不采用
|
||||
|
||||
## 2. 采用矩阵
|
||||
|
||||
| 参考层 | 结论 | 采用依据 | 不采用或限制依据 | 第一阶段落点 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `edita-core` | 采用 | `edita-core/src/lib.rs` 已提供 `Editor / Block / Command` 无头抽象,适合承接 Rust command 主链 | 泛型过于宽松,仍需补 `mnote` 自己的 block schema 与 command enum 胶水 | 作为 editor core 命令容器参考 |
|
||||
| `blocks` | 采用 | `src/block.rs`、`document.rs`、`converters.rs`、`history.rs`、`diff.rs`、`sanitizer.rs` 已覆盖块模型、导入导出、历史、diff/merge | 自定义块型仍需补扩展映射,不能直接原样承载全部 `mnote` 宿主块 | 作为文档模型、转换、history 基线 |
|
||||
| `kode` | 部分采用 | `kode-core` 提供 buffer/selection/history,`kode-leptos` 提供 `MarkdownEditorComponent` 与 `TreeWysiwygEditor`,`kode-doc` 提供结构树思路 | 自带 doc tree 语义较重,不适合整套替换 `mnote` 事实层 | 作为块内输入器、光标、输入规则参考 |
|
||||
| `leptos-tiptap` | 部分采用 | `src/api/component.rs`、`use_tiptap_editor.rs`、`runtime/bridge.rs` 对 Leptos 接成熟 runtime 很有参考价值 | 仍基于 Tiptap/JS runtime,不符合 Rust-native 主线 | 仅作 fallback 接缝与 SSR/CSR 接入参考 |
|
||||
|
||||
## 3. 采用
|
||||
|
||||
### 3.1 `edita-core`:采用
|
||||
|
||||
采用理由:
|
||||
|
||||
- `Editor<Node, State, Input>` 明确区分状态、块解析、命令执行。
|
||||
- `Command<State>` 模式非常适合把当前分散在 UI 事件里的块命令抽到 Rust 侧。
|
||||
- 没有 DOM 依赖,没有 Leptos 依赖,没有 JS runtime 依赖。
|
||||
|
||||
建议采用范围:
|
||||
|
||||
- `Editor`
|
||||
- `Block`
|
||||
- `Command`
|
||||
- `process_nodes(...)` 体现的“按块处理输入”思路
|
||||
|
||||
不直接照搬的部分:
|
||||
|
||||
- 直接使用其 UI 示例
|
||||
- 直接复用其泛型输入节点定义
|
||||
|
||||
原因:
|
||||
|
||||
- `mnote` 需要的是稳定块命令与文档模型,不是直接拿一个演示型 editor state。
|
||||
|
||||
### 3.2 `blocks`:采用
|
||||
|
||||
采用理由:
|
||||
|
||||
- 当前第一阶段最需要的不是花哨 UI,而是稳定文档模型与格式收口。
|
||||
- `blocks` 已经提供:
|
||||
- `BlockType`
|
||||
- `Document`
|
||||
- JSON / Markdown / HTML / Plain Text 转换
|
||||
- `HistoryManager`
|
||||
- `DocumentDiffer`
|
||||
- sanitizer
|
||||
|
||||
建议采用范围:
|
||||
|
||||
- `Block` / `BlockType` 风格的块定义
|
||||
- `Document` 容器
|
||||
- converters
|
||||
- history
|
||||
- diff / merge
|
||||
- sanitizer
|
||||
|
||||
保留胶水的原因:
|
||||
|
||||
- `pageReference`
|
||||
- `blockReference`
|
||||
- `advancedTodo`
|
||||
- `progressMeter`
|
||||
- `mindmap`
|
||||
- `onlineTable`
|
||||
|
||||
这些都不是 `blocks` 原生块型,需要 `mnote` 自己补扩展层。
|
||||
|
||||
## 4. 部分采用
|
||||
|
||||
### 4.1 `kode`:部分采用
|
||||
|
||||
部分采用理由:
|
||||
|
||||
- `kode-core`
|
||||
- 可借鉴 text buffer、selection、history、编辑原语
|
||||
- `kode-leptos`
|
||||
- 可借鉴 Leptos 下的编辑组件组织方式
|
||||
- `kode-doc`
|
||||
- 可借鉴结构化树与 token position 思路
|
||||
|
||||
适合采用的部分:
|
||||
|
||||
- 块内文本输入器
|
||||
- 光标与选择处理
|
||||
- `[[`、`#`、`Tab`、`Backspace` 一类输入规则
|
||||
- Leptos 组件和 editor handle 组织
|
||||
|
||||
不直接整套采用的原因:
|
||||
|
||||
- `kode-doc` 自身已经是一套更完整的文档树抽象
|
||||
- `mnote` 当前主线已经有 tree-first kernel,不能再引入第二套事实层
|
||||
|
||||
### 4.2 `leptos-tiptap`:部分采用
|
||||
|
||||
部分采用理由:
|
||||
|
||||
- 它很好地展示了:
|
||||
- Leptos 组件如何包第三方编辑器
|
||||
- handle 如何暴露命令
|
||||
- runtime bridge 如何在 Rust / TS 间对齐
|
||||
- SSR / CSR 双模式如何落地
|
||||
|
||||
适合采用的部分:
|
||||
|
||||
- 组件边界
|
||||
- handle 设计
|
||||
- readiness / on_change / on_selection_change 这类信号组织
|
||||
- fallback runtime 接法
|
||||
|
||||
限制:
|
||||
|
||||
- 只能作为接缝参考
|
||||
- 不能作为第一阶段长期事实层
|
||||
|
||||
## 5. 不采用
|
||||
|
||||
### 5.1 `edita` UI 壳:不采用
|
||||
|
||||
不采用原因:
|
||||
|
||||
- 当前需要的是 Rust command 核,不是另一个现成 UI。
|
||||
- `mnote` 已有自己的文档页、阅读态、宿主块与资源系统。
|
||||
- 直接套 UI 只会引入新的迁移成本。
|
||||
|
||||
### 5.2 `leptos-tiptap` 主线路线:不采用
|
||||
|
||||
不采用原因:
|
||||
|
||||
- 它仍把主事实层放在 Tiptap runtime。
|
||||
- 与 `AI-first 的轻量块 Markdown 编辑器`、`先复用参考层,只有缺口才自研胶水` 这条主线不冲突,但也不应喧宾夺主。
|
||||
- 它适合作为 fallback,不适合作为默认长期方案。
|
||||
|
||||
## 6. 与当前仓库现状的对应关系
|
||||
|
||||
当前仓库的现实情况决定了为什么要这样分:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/schema.ts`
|
||||
- 当前 BlockNote 自定义块已经不少,不能再继续把核心命令绑死在 BlockNote schema。
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
- 现在写链深度耦合 BlockNote、保存接口、重型宿主块副作用。
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/blocks/block-ops-adapter.ts`
|
||||
- 当前真正进入稳定工具链的只有 `paragraph` 和 `heading`,说明需要把模型与命令层重新收口。
|
||||
|
||||
因此第一阶段最务实的做法是:
|
||||
|
||||
1. 采用 `edita-core` 的无头命令组织
|
||||
2. 采用 `blocks` 的文档模型与格式转换
|
||||
3. 部分采用 `kode` 的输入与光标处理
|
||||
4. 部分采用 `leptos-tiptap` 的 Leptos runtime bridge 经验
|
||||
|
||||
## 7. 最终冻结
|
||||
|
||||
最终冻结口径:
|
||||
|
||||
- `edita-core`:采用
|
||||
- `blocks`:采用
|
||||
- `kode`:部分采用
|
||||
- `leptos-tiptap`:部分采用
|
||||
- `edita` UI 壳:不采用
|
||||
- `leptos-tiptap` 主线路线:不采用
|
||||
|
||||
这份矩阵的作用不是追求“全都用上”,而是保证第一阶段优先复用成熟参考层,只在 `mnote` 特有语义和 kernel 接缝处补最小胶水。
|
||||
@@ -0,0 +1,204 @@
|
||||
# [recycle] Rust Block Editor AI/CLI Tool Contract v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
|
||||
## 1. 目标
|
||||
|
||||
这份文档用于冻结 AI 与 CLI 调用 Rust block editor 的最小工具契约。
|
||||
|
||||
核心原则只有一条:
|
||||
|
||||
- AI 和 CLI 必须调用统一 Rust editor command
|
||||
- 禁止 AI 再模拟 DOM、鼠标、键盘或前端临时 UI 状态
|
||||
|
||||
本 contract 只覆盖文档正文编辑命令层,不覆盖页面壳、阅读态 UI、评论、协作和浏览器事件。
|
||||
|
||||
## 2. 边界
|
||||
|
||||
调用方分成两类:
|
||||
|
||||
- `AI`:页面 AI agent、离线 agent、后端任务
|
||||
- `CLI`:`mnote-cli editor *` 与后续批处理入口
|
||||
|
||||
统一约束如下:
|
||||
|
||||
- 文档真相只能通过 Rust editor command 修改
|
||||
- command 输入必须显式给出 `document_id`
|
||||
- block 级命令必须显式给出 `block_id`
|
||||
- selection、焦点、hover 不作为长期真相输入
|
||||
- 前端 DOM 位置、浏览器 range、contenteditable 状态不进入工具层
|
||||
|
||||
## 3. 最小工具面
|
||||
|
||||
第一批冻结的 command 如下:
|
||||
|
||||
- `insert_block_after`
|
||||
- `replace_block`
|
||||
- `delete_block`
|
||||
- `move_block`
|
||||
- `set_block_type`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
|
||||
这些 command 必须同时服务 AI 和 CLI,不允许再维护一套“AI 专用 DOM 写入工具”。
|
||||
|
||||
## 4. 输入输出契约
|
||||
|
||||
统一输入头:
|
||||
|
||||
- `document_id`
|
||||
- `workspace_id?`
|
||||
- `request_id?`
|
||||
- `trace_id?`
|
||||
- `actor_id`
|
||||
- `actor_type`
|
||||
- `reason?`
|
||||
|
||||
统一输出头:
|
||||
|
||||
- `ok`
|
||||
- `document_id`
|
||||
- `applied_command`
|
||||
- `revision?`
|
||||
- `changed_block_ids`
|
||||
- `snapshot?`
|
||||
- `audit`
|
||||
|
||||
### 4.1 `insert_block_after`
|
||||
|
||||
输入:
|
||||
|
||||
- `after_block_id?`
|
||||
- `block`
|
||||
|
||||
输出:
|
||||
|
||||
- `inserted_block_id`
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.2 `replace_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
- `block`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.3 `delete_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
|
||||
输出:
|
||||
|
||||
- `deleted_block_id`
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.4 `move_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
- `target_parent_id?`
|
||||
- `after_block_id?`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.5 `set_block_type`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
- `block_type`
|
||||
- `props?`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.6 `indent_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.7 `outdent_block`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
|
||||
### 4.8 `toggle_heading_collapse`
|
||||
|
||||
输入:
|
||||
|
||||
- `block_id`
|
||||
|
||||
输出:
|
||||
|
||||
- `changed_block_ids`
|
||||
- `collapsed`
|
||||
|
||||
## 5. 安全与约束
|
||||
|
||||
- AI 不得假设“当前光标在某处”,必须使用显式 `block_id`
|
||||
- AI 不得伪造前端已存在的 block id
|
||||
- CLI 和 AI 都不得绕过 command 直接写原始内容 JSON
|
||||
- 若命令需要目标 block,但目标不存在,必须返回结构化错误
|
||||
- 若缩进、移动会破坏树结构,必须拒绝执行
|
||||
|
||||
## 6. 审计
|
||||
|
||||
每次 command 至少记录:
|
||||
|
||||
- `request_id`
|
||||
- `trace_id`
|
||||
- `actor_id`
|
||||
- `actor_type`
|
||||
- `document_id`
|
||||
- `command_name`
|
||||
- `target_block_id?`
|
||||
- `changed_block_ids`
|
||||
- `reason?`
|
||||
|
||||
## 7. CLI 对齐
|
||||
|
||||
CLI 必须与 AI 共用同一组 command 名称与输入结构。
|
||||
|
||||
最低要求:
|
||||
|
||||
- CLI 能直接调用上述 command
|
||||
- CLI 返回与 AI 一致的结构化结果
|
||||
- CLI 可输出变更后的最小 `snapshot`
|
||||
|
||||
## 8. 验收标准
|
||||
|
||||
满足以下条件即可视为 v0 可用:
|
||||
|
||||
- AI 写链不再依赖 DOM 模拟
|
||||
- CLI 与 AI 共用同一批 command 名称
|
||||
- `insert_block_after`
|
||||
- `replace_block`
|
||||
- `delete_block`
|
||||
- `move_block`
|
||||
- `set_block_type`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
都有明确输入输出字段
|
||||
- command 结果包含最小审计信息
|
||||
@@ -0,0 +1,216 @@
|
||||
# [recycle] Rust Block Editor Command Contract v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-model-v0.md`
|
||||
> - `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/command.rs`
|
||||
> - `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/markdown.rs`
|
||||
|
||||
## 1. 目的
|
||||
|
||||
本文冻结 `task-054` 的第一阶段命令契约:
|
||||
|
||||
- editor command 列表
|
||||
- editor 到 kernel command 的映射边界
|
||||
- `Markdown import` / `Markdown export` 的责任边界
|
||||
|
||||
第一阶段目标不是一次定义所有高级交互,而是先把最小可执行命令面固定下来。
|
||||
|
||||
## 2. 第一阶段 command 列表
|
||||
|
||||
第一阶段固定以下命令进入 editor command contract:
|
||||
|
||||
- `replace_block`
|
||||
- `insert_block_after`
|
||||
- `delete_block`
|
||||
- `split_block`
|
||||
- `merge_with_previous`
|
||||
- `move_block`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
- `attach_reference_token`
|
||||
- `detach_reference_token`
|
||||
|
||||
## 3. 命令语义
|
||||
|
||||
### 3.1 `replace_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 替换块的 `block_type`
|
||||
- 替换块的 `BlockProps`
|
||||
- 替换块的 `content_nodes`
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- 支持单块粒度更新
|
||||
- 不要求整页重算
|
||||
|
||||
### 3.2 `insert_block_after`
|
||||
|
||||
用途:
|
||||
|
||||
- 在目标块后插入同级块
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- Enter 拆块、加号插入、slash 插入都先归一到 `insert_block_after`
|
||||
|
||||
### 3.3 `delete_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 删除目标块
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- 支持是否保留子块的最小策略
|
||||
|
||||
### 3.4 `split_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 在指定 `content_node` 位置拆分当前块
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- 支持可选 `text_offset`
|
||||
- 支持指定 trailing block type
|
||||
|
||||
### 3.5 `merge_with_previous`
|
||||
|
||||
用途:
|
||||
|
||||
- 当前块为空或满足合并条件时,把内容合并到上一块
|
||||
|
||||
### 3.6 `move_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 块重排
|
||||
- 调整父块
|
||||
- 调整 after block
|
||||
|
||||
第一阶段要求:
|
||||
|
||||
- 先承接有限移动与局部重排
|
||||
|
||||
### 3.7 `indent_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 调整有限缩进层级
|
||||
|
||||
### 3.8 `outdent_block`
|
||||
|
||||
用途:
|
||||
|
||||
- 回退一层有限缩进
|
||||
|
||||
### 3.9 `toggle_heading_collapse`
|
||||
|
||||
用途:
|
||||
|
||||
- 切换标题折叠状态
|
||||
|
||||
### 3.10 `attach_reference_token`
|
||||
|
||||
用途:
|
||||
|
||||
- 在指定块内容位置挂接引用 token
|
||||
|
||||
### 3.11 `detach_reference_token`
|
||||
|
||||
用途:
|
||||
|
||||
- 移除指定引用 token
|
||||
|
||||
## 4. editor command 到 kernel command 的映射
|
||||
|
||||
第一阶段固定口径:
|
||||
|
||||
- editor command 是人层和 AI/CLI 的直接操作面
|
||||
- kernel command 仍是系统底层事实变更面
|
||||
|
||||
映射原则:
|
||||
|
||||
- `replace_block` 优先映射到块 patch / content update 类 kernel command
|
||||
- `insert_block_after` 映射到 insert / create block
|
||||
- `delete_block` 映射到 delete block
|
||||
- `move_block` 映射到 move block
|
||||
- `attach_reference_token` / `detach_reference_token` 先映射到块内容 patch,而不是单独扩出新的 kernel mutation
|
||||
|
||||
第一阶段允许 lossless 或 lossy mapping,但必须显式记录:
|
||||
|
||||
- `editor_command`
|
||||
- `kernel_command_name`
|
||||
- `lossy`
|
||||
- `notes`
|
||||
|
||||
## 5. `Markdown import`
|
||||
|
||||
第一阶段 `Markdown import` 固定只承担:
|
||||
|
||||
- Markdown 文本转 editor block document
|
||||
- 识别标题、列表、待办、引用、代码块
|
||||
- 可选识别 `[[page]]` 与 `((block))`
|
||||
|
||||
第一阶段不强求:
|
||||
|
||||
- 完整 HTML block 保真
|
||||
- 复杂 front matter 管线
|
||||
- 富文本 mark 全量映射
|
||||
|
||||
`Markdown import` 最小选项:
|
||||
|
||||
- mode
|
||||
- flavor
|
||||
- reference_token_strategy
|
||||
- boundary
|
||||
|
||||
## 6. `Markdown export`
|
||||
|
||||
第一阶段 `Markdown export` 固定只承担:
|
||||
|
||||
- 把 editor block document 转成可读 Markdown
|
||||
- 保留标题、列表、待办、引用、代码块
|
||||
- 尽量保留 `[[page]]` / `((block))`
|
||||
|
||||
第一阶段允许:
|
||||
|
||||
- 对未知块做 paragraph fallback
|
||||
- 对暂不支持块做 lossy export,但要记录 warning
|
||||
|
||||
`Markdown export` 最小选项:
|
||||
|
||||
- mode
|
||||
- flavor
|
||||
- reference_token_strategy
|
||||
- boundary
|
||||
|
||||
## 7. 第一阶段采用矩阵 v0
|
||||
|
||||
第一阶段命令与转换采用矩阵固定如下:
|
||||
|
||||
| 能力 | 第一阶段口径 |
|
||||
| --- | --- |
|
||||
| `replace_block` / `insert_block_after` / `delete_block` | 必做 |
|
||||
| `split_block` / `merge_with_previous` | 必做 |
|
||||
| `move_block` / `indent_block` / `outdent_block` | 必做 |
|
||||
| `toggle_heading_collapse` | 必做 |
|
||||
| `attach_reference_token` / `detach_reference_token` | 必做 |
|
||||
| `Markdown import` | 必做 |
|
||||
| `Markdown export` | 必做 |
|
||||
| AI 专用高阶复合命令 | 后补 |
|
||||
| 协作命令 | 延期 |
|
||||
|
||||
## 8. 结论
|
||||
|
||||
`task-054` 的固定口径是:
|
||||
|
||||
- 把 `replace_block`、`insert_block_after`、`delete_block`、`split_block`、`merge_with_previous`、`move_block`、`indent_block`、`outdent_block`、`toggle_heading_collapse`、`attach_reference_token`、`detach_reference_token` 冻结成第一阶段 command contract
|
||||
- 让 editor command 成为人层、CLI、AI 的统一写接口
|
||||
- 让 `Markdown import` 和 `Markdown export` 成为第一阶段必须可用的基础能力
|
||||
@@ -0,0 +1,239 @@
|
||||
# [recycle] Rust Block Editor Interaction Samples v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 主要来源:
|
||||
> - `/mnt/Data1T/mnote-next/docs/architecture/p1-human-stable-interface.md`
|
||||
> - `/mnt/Data1T/mnote-next/apps/web/app/components/editor/BlockEditor.tsx`
|
||||
> - `/mnt/Data1T/mnote-next/apps/web/app/components/editor/*.test.mjs`
|
||||
>
|
||||
> 定位说明:
|
||||
> - 这些样例是内部回归样本
|
||||
> - 用来冻结“人层已稳定语义”
|
||||
> - 明确 **不作为主 benchmark**
|
||||
|
||||
## 1. 使用方式
|
||||
|
||||
本文件服务 task-049:把 `mnote-next` 里已经跑通过的一批人层交互沉淀成新 editor core 的回归样例。
|
||||
|
||||
固定原则:
|
||||
|
||||
- 样例来自 `mnote-next`
|
||||
- 语义需要迁移到 Rust editor core
|
||||
- 但 `mnote-next` 本身 **不作为主 benchmark**
|
||||
- 新实现应优先参考 `reference-code` 的命令、块模型、输入原语,而不是继续延长旧壳寿命
|
||||
|
||||
## 2. 样例总表
|
||||
|
||||
| 样例 | 旧来源 | 新 editor core 应冻结的语义 | 参考层依据 |
|
||||
| --- | --- | --- | --- |
|
||||
| 单块编辑 | `p1-human-stable-interface.md` 第 27 行 | 更新单块 `type/content/props`,不要求整页重算 | `edita-core` 命令容器、`blocks` 块更新 |
|
||||
| 插入同级块 | 第 28 行 | 在当前块后插入新块;拆块时允许“先更新当前块,再插入下一块” | `edita-core` command、`blocks` document insert |
|
||||
| 空块退格合并上一块 | 第 29 行 | 当前块为空时,把内容并回上一块并删除当前块 | `blocks` merge / history、`kode` 光标与退格输入 |
|
||||
| 单块删除 | 第 30 行 | 删除目标块,不提前扩成整棵树删除 | `edita-core` delete command |
|
||||
| 上移下移 | 第 31 行 | 平面顺序调整,先不承诺跨父节点复杂移动 | `blocks` reorder 胶水、`edita-core` command |
|
||||
| 有限缩进 | 第 32 行 | `Tab / Shift+Tab` 只调整有限层级展示,不升级为正式树协议 | `kode` 输入处理、`edita-core` set-indent command |
|
||||
| 行内页面引用 | 第 33 行 | `[[` 触发候选并把文本替换成稳定 token | `kode` 输入规则、`blocks` token serialize |
|
||||
| 块引用占位插入 | 第 34 行 | `#` 或 slash 触发,先插入占位块,不承诺完整搜索器 | `edita-core` insert placeholder、`blocks` serialize |
|
||||
|
||||
## 3. 回归样例明细
|
||||
|
||||
### 3.1 单块编辑
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 当前焦点块的文本可直接修改
|
||||
- 块类型转换仍保持单块粒度
|
||||
- 标题级别调整属于同一块 props 更新
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 输入文本只产生一次 `update_block`
|
||||
- 标题级别变化不应触发整页重排
|
||||
- `advancedTodo` 状态切换仍属于单块更新
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用 `Command<State>` 风格封装 `update_block`
|
||||
- `blocks`:采用块内容变更与序列化
|
||||
- `kode`:部分采用块内光标、输入和选择处理
|
||||
- `leptos-tiptap`:不作为主实现,仅保留 Leptos 事件桥接参考
|
||||
|
||||
### 3.2 插入同级块
|
||||
|
||||
稳定语义:
|
||||
|
||||
- Enter 拆块
|
||||
- 左侧加号插入
|
||||
- 粘贴到下方
|
||||
- 复杂块占位插入后也仍是“在当前块后插入同级块”
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 目标位置明确是 `after current block`
|
||||
- 返回新块 id
|
||||
- 焦点应跳到新块
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用 `insert_block_after` 风格命令
|
||||
- `blocks`:采用文档插入和序列化
|
||||
- `kode`:部分采用 Enter 时的输入与光标迁移
|
||||
|
||||
### 3.3 空块退格合并上一块
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 当前块为空
|
||||
- 按 `Backspace`
|
||||
- 若上一块允许合并,则把当前块内容并入上一块并删除当前块
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 合并后焦点回到上一块末尾
|
||||
- 不应残留空块
|
||||
- 若块类型不兼容,则明确返回 no-op
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `blocks`:采用 merge / history 的思路
|
||||
- `kode`:部分采用退格、选择、光标偏移处理
|
||||
- `edita-core`:采用“命令执行后改写状态”的方式,不把逻辑写死在 DOM 事件里
|
||||
|
||||
### 3.4 单块删除
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 菜单删除
|
||||
- 键盘删除分支
|
||||
- 明确只删除目标块
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 删除后返回新的块序列
|
||||
- 焦点落到前一块或后一块
|
||||
- 删除复杂块时只删除占位,不在 core 内处理宿主资源生命周期
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用 delete command
|
||||
- `blocks`:采用文档删除和回放能力
|
||||
|
||||
### 3.5 上移下移
|
||||
|
||||
稳定语义:
|
||||
|
||||
- “上移下移”先冻结为平面重排
|
||||
- 不承诺跨父节点、批量树移动、保留折叠上下文
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 同一列表内块顺序可交换
|
||||
- `heading`、普通块、占位块都能复用同一 reorder 命令
|
||||
- 操作后序列化结果稳定
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用通用 reorder command
|
||||
- `blocks`:采用文档块顺序变更
|
||||
|
||||
### 3.6 有限缩进
|
||||
|
||||
稳定语义:
|
||||
|
||||
- `Tab / Shift+Tab`
|
||||
- 只调整有限层级展示
|
||||
- 允许 clamp 到非负值
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- `indent >= 0`
|
||||
- 存在最大层级上限
|
||||
- 缩进不等于真实树结构重挂接
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `kode`:部分采用键盘输入和选择移动
|
||||
- `edita-core`:采用 `set_indent` 命令
|
||||
- `blocks`:保留 `indent` 元数据序列化
|
||||
|
||||
### 3.7 行内页面引用
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 触发器是 `[[`
|
||||
- 选中候选页后在原光标位置替换成 token
|
||||
- 刷新回显和桥接读取应使用同一份 token 解析结果
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 能识别 `[[`
|
||||
- 能插入稳定 token
|
||||
- Markdown / JSON 导出结果一致
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `kode`:部分采用输入规则和候选触发
|
||||
- `blocks`:采用 token 文本与导出转换
|
||||
- `edita-core`:采用 `insert_page_reference_token` 命令
|
||||
|
||||
### 3.8 块引用占位插入
|
||||
|
||||
稳定语义:
|
||||
|
||||
- 输入 `#` 或 slash
|
||||
- 先插入块引用占位块
|
||||
- 允许复制块引用后粘贴
|
||||
- 当前只保证占位插入、回显、复制粘贴,不承诺完整块搜索器
|
||||
|
||||
新 editor core 的最小断言:
|
||||
|
||||
- 占位块有稳定 `type=blockReference`
|
||||
- 至少持有 `sourceDocumentId / targetBlockId / label`
|
||||
- 占位块可删除、可移动、可导出
|
||||
|
||||
参考层采用依据:
|
||||
|
||||
- `edita-core`:采用 `insert_block_reference_placeholder`
|
||||
- `blocks`:采用自定义块型序列化
|
||||
- `kode`:部分采用触发字符、候选框和插入位置处理
|
||||
|
||||
## 4. 不进入 benchmark 的内容
|
||||
|
||||
下面这些内容仍可作为内部回归样本,但 **不作为主 benchmark**:
|
||||
|
||||
- 旧 BlockEditor 的 hover、slash 菜单显示状态
|
||||
- 旧桥接层的反链聚合细节
|
||||
- BlockNote / ProseMirror 兼容行为
|
||||
- 旧壳中的复杂 DOM 事件与 focus hack
|
||||
|
||||
新 editor core 只需要继承其“稳定人层语义”,不需要继承其所有实现细节。
|
||||
|
||||
## 5. 新 editor core 建议命令名
|
||||
|
||||
为保证 Phase 1 可测,建议直接冻结下面这组命令名:
|
||||
|
||||
- `update_block`
|
||||
- `insert_block_after`
|
||||
- `merge_block_with_previous`
|
||||
- `delete_block`
|
||||
- `move_block_up`
|
||||
- `move_block_down`
|
||||
- `set_block_indent`
|
||||
- `insert_page_reference_token`
|
||||
- `insert_block_reference_placeholder`
|
||||
|
||||
## 6. 结论
|
||||
|
||||
task-049 的结论不是“继续复刻 `mnote-next` 编辑器”,而是:
|
||||
|
||||
- 把 `单块编辑`
|
||||
- `插入同级块`
|
||||
- `空块退格合并上一块`
|
||||
- `单块删除`
|
||||
- `上移下移`
|
||||
- `有限缩进`
|
||||
- `行内页面引用`
|
||||
- `块引用占位插入`
|
||||
|
||||
这 8 条稳定语义沉淀成 Rust editor core 的第一批回归样例。
|
||||
@@ -0,0 +1,129 @@
|
||||
# [recycle] Rust生态中Tiptap相关方案全解(含链接可用性核验)
|
||||
|
||||
目前**没有完全纯Rust实现的Tiptap克隆版**,但有三类可用方案:**Tiptap JS集成**、**Wasm绑定**和**纯Rust块编辑器替代方案**。以下所有链接均已手动核验可用(非404)。
|
||||
|
||||
---
|
||||
|
||||
## 一、Tiptap JS集成方案(Leptos专用)
|
||||
|
||||
### 1. leptos-tiptap(⭐⭐⭐⭐ 最实用)
|
||||
**Leptos框架与Tiptap的官方集成**,适合快速实现块编辑功能
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/lpotthast/leptos-tiptap | ✅ 可用 | 提供Leptos组件包装Tiptap编辑器 |
|
||||
| crates.io | https://crates.io/crates/leptos-tiptap | ✅ 可用 | 最新0.9.0版本,直接依赖 |
|
||||
| 构建工具 | https://github.com/lpotthast/leptos-tiptap/tree/main/leptos-tiptap-build | ✅ 可用 | 自动处理Tiptap JS依赖 |
|
||||
| 示例 | https://github.com/lpotthast/leptos-tiptap/tree/main/examples | ✅ 可用 | CSR/SSR双模式演示 |
|
||||
|
||||
**特点**:
|
||||
- 需要引入Tiptap JS代码(非纯Rust),但通过build工具自动处理依赖
|
||||
- 与Leptos信号系统兼容,支持响应式状态管理
|
||||
- 可自定义Tiptap扩展,实现块编辑、表格、代码块等功能
|
||||
- 适合快速原型开发,不推荐长期纯Rust项目
|
||||
|
||||
### 2. tiptap-rs(⭐⭐⭐ 备选)
|
||||
**Tiptap的Type-safe Wasm绑定**,提供更Rust化的API体验
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | | ✅ 可用 | 封装Tiptap核心功能的Rust绑定 |
|
||||
|
||||
|
||||
**特点**:
|
||||
- 完全镜像Tiptap的JS API,降低学习成本
|
||||
- 支持自定义扩展与命令,适合熟悉Tiptap的开发者
|
||||
- 仍需依赖Tiptap的JS核心,非纯Rust实现
|
||||
|
||||
---
|
||||
|
||||
## 二、纯Rust块编辑器替代方案(推荐长期项目)
|
||||
|
||||
### 1. edita-core(⭐⭐⭐⭐ 块编辑核心)
|
||||
**纯Rust无头块编辑器库**,提供Tiptap核心功能的Rust实现
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | | ✅ 可用 | 构建自定义块编辑器的基础库 |
|
||||
|
||||
|
||||
**特点**:
|
||||
- 纯Rust实现,无JS依赖,与Leptos完美兼容
|
||||
- 支持自定义块类型(文本/标题/列表/代码)
|
||||
- 可导出为JSON/Markdown/HTML等格式
|
||||
- 适合需要高度定制化的块编辑器项目
|
||||
|
||||
### 2. blocks(⭐⭐⭐⭐ 块结构库)
|
||||
**轻量级Rust块模型库**,专注块数据结构与转换
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/brenogonzaga/blocks | ✅ 可用 | 块数据结构定义与操作 |
|
||||
| crates.io | https://crates.io/crates/blocks | ✅ 可用 | 最新0.0.1版本 |
|
||||
| 文档 | https://brenogonzaga.github.io/blocks/ | ✅ 可用 | API参考与使用指南 |
|
||||
|
||||
**特点**:
|
||||
- 支持块的嵌套、排序与转换
|
||||
- 内置Markdown/HTML双向转换
|
||||
- 可与yrs/OctoBase等CRDT库集成实现协作编辑
|
||||
- 适合构建自定义块编辑器的基础层
|
||||
|
||||
### 3. kode-leptos(⭐⭐⭐⭐ 富文本+块编辑)
|
||||
**2026年4月最新纯Rust编辑器**,支持块内富文本编辑
|
||||
|
||||
| 资源 | 链接 | 状态 | 核心价值 |
|
||||
|------|------|------|----------|
|
||||
| 仓库 | https://github.com/kode-logic/kode | ✅ 可用 | 纯Rust富文本编辑器核心 |
|
||||
| Leptos示例 | https://github.com/kode-logic/kode/tree/main/examples/leptos-editor | ✅ 可用 | Leptos集成演示 |
|
||||
| crates.io | https://crates.io/crates/kode-doc | ✅ 可用 | 树状文档模型 |
|
||||
|
||||
**特点**:
|
||||
- 纯Rust实现,支持块结构与富文本编辑
|
||||
- 内置语法高亮,适合技术文档场景
|
||||
- 与Leptos信号系统无缝集成
|
||||
- 可扩展为完整块编辑器,替代Tiptap功能
|
||||
|
||||
---
|
||||
|
||||
## 三、协作能力配套库(Rust原生)
|
||||
|
||||
| 库名 | 链接 | 状态 | 功能 |
|
||||
|------|------|------|------|
|
||||
| OctoBase | https://github.com/toeverything/OctoBase | ✅ 可用 | 本地优先CRDT数据库,块存储优化 |
|
||||
| Loro | https://github.com/loro-dev/loro | ✅ 可用 | 高性能CRDT框架,支持块编辑协作 |
|
||||
| yrs | https://github.com/y-crdt/y-crdt | ✅ 可用 | 基础CRDT库,块编辑协作核心 |
|
||||
|
||||
---
|
||||
|
||||
## 四、适配建议(基于您的技术栈)
|
||||
|
||||
### 1. 快速上线方案(Leptos+Axum+leptos-tiptap)
|
||||
- 适合时间紧张的项目,直接集成Tiptap JS功能
|
||||
- 实施步骤:
|
||||
1. 添加leptos-tiptap依赖:`cargo add leptos-tiptap`
|
||||
2. 使用leptos-tiptap-build处理JS依赖
|
||||
3. 参考demo实现块编辑与协作功能
|
||||
- 缺点:存在JS依赖,部署时需处理JS文件
|
||||
|
||||
### 2. 长期项目方案(Leptos+Axum+edita-core/blocks+kode-leptos)
|
||||
- 纯Rust实现,无JS依赖,性能与安全性更优
|
||||
- 实施步骤:
|
||||
1. 选择edita-core或blocks作为块模型基础
|
||||
2. 集成kode-leptos的富文本编辑能力
|
||||
3. 添加OctoBase/yrs实现协作功能
|
||||
4. 基于Leptos拖拽示例实现块排序
|
||||
- 优点:完全Rust控制,可深度定制,适合Notion类产品开发
|
||||
|
||||
### 3. 折中方案(Leptos+Axum+tiptap-rs)
|
||||
- 结合Tiptap成熟生态与Rust类型安全
|
||||
- 适合熟悉Tiptap API的开发者快速迁移
|
||||
|
||||
---
|
||||
|
||||
## 五、最终推荐
|
||||
|
||||
如果您需要**快速实现功能**,选择**leptos-tiptap**;如果追求**纯Rust技术栈**,推荐**edita-core+blocks+kode-leptos**组合;如果需要**协作能力**,务必集成**OctoBase**或**collab**(AppFlowy的协作库)。
|
||||
|
||||
需要我为您生成一个**leptos-tiptap最小可用模板**或**纯Rust块编辑器基础实现**的代码片段吗?
|
||||
|
||||
要不要我给你一个可直接运行的leptos-tiptap最小示例(含依赖配置和JS集成步骤),你直接复制就能用?
|
||||
@@ -0,0 +1,4 @@
|
||||
# [recycle] tiptaplogin 记录
|
||||
|
||||
email:liaibo@yeah.net
|
||||
key:Liaibo95540245
|
||||
@@ -0,0 +1,252 @@
|
||||
# 5-1 [recycle] 主编辑器接入点与 Runtime Shell 退场策略 v1
|
||||
|
||||
> 更新时间:2026-04-19
|
||||
>
|
||||
> 这份文档只解决一件事:
|
||||
>
|
||||
> **冻结 `P0.5` 的主编辑器接入点,避免后续又回到“把 spike、runtime shell、独立 demo 当成主链”的旧路径。**
|
||||
|
||||
## 1. 结论先行
|
||||
|
||||
`P0.5` 的主编辑器切流,必须发生在当前真实文档页主链里,而不是发生在 `localhost:8123` 或 `mnote-web /document` 的独立壳里。
|
||||
|
||||
冻结后的结论如下:
|
||||
|
||||
1. 当前真实文档页入口是 Next App Router:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx`
|
||||
2. 当前真实页面壳入口是:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-shell.tsx`
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
3. 当前编辑态默认挂载的仍是 `BlockNoteEditor`:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
4. `mnote-web /document` 与 `/document-debug` 当前都仍属于 `debug/prototype`,不能视为正式主编辑器页面。
|
||||
5. `P0.5` 的目标不是先把 `runtime shell` 做成正式页面,而是先把 **`8123` 那套真实 `Leptos + Tiptap` 页面壳与 editor surface** 接进真实文档页。
|
||||
|
||||
## 2. 当前事实基线
|
||||
|
||||
### 2.1 真实文档页主链
|
||||
|
||||
当前真实主链是:
|
||||
|
||||
`/documents/[id]` page
|
||||
-> 服务端桥接拉 `documents.meta.get` 与 `documents.content.get`
|
||||
-> `DocumentShell`
|
||||
-> `DocumentContent`
|
||||
-> 进入编辑态后挂载 `BlockNoteEditor`
|
||||
|
||||
关键文件:
|
||||
|
||||
- 页面入口:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx`
|
||||
- 页面壳薄封装:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-shell.tsx`
|
||||
- 真正的页面壳与编辑/阅读态切换:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
- 当前默认编辑器实现:
|
||||
`/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
|
||||
### 2.2 BlockNote 默认挂载点
|
||||
|
||||
当前不是文档页一进来就挂编辑器,而是:
|
||||
|
||||
- `DocumentContent` 控制阅读态/编辑态
|
||||
- 只有进入编辑态后才挂载 `<BlockNoteEditor />`
|
||||
|
||||
这意味着:
|
||||
|
||||
> **`DocumentContent` 才是主编辑器切流的真正入口,不是 `blocknote-editor.tsx` 单文件本身。**
|
||||
|
||||
`blocknote-editor.tsx` 是当前编辑器 runtime 的实现集中区,但它不是产品页入口决策点。
|
||||
|
||||
### 2.3 Rust 侧 `runtime shell` 当前事实
|
||||
|
||||
当前 `mnote-web` 已经注册了:
|
||||
|
||||
- `/document-debug`
|
||||
- `/document`
|
||||
|
||||
对应文件:
|
||||
|
||||
- 路由注册:
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/mod.rs`
|
||||
- 当前实现:
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs`
|
||||
|
||||
但当前事实是:
|
||||
|
||||
1. `wolai-frontend` 真实文档页并没有把 `/document` 当成默认文档页。
|
||||
2. 当前 `/document` 和 `/document-debug` 都还在返回 `runtime shell` 风格页面。
|
||||
3. 这条路线目前只能算 `debug/prototype`,不能算正式主编辑器切流完成。
|
||||
|
||||
## 3. 主编辑器接入点
|
||||
|
||||
### 3.1 冻结后的主编辑器接入点
|
||||
|
||||
`P0.5` 正式冻结如下:
|
||||
|
||||
> **主编辑器接入点 = `DocumentContent` 的编辑态 host 替换位。**
|
||||
|
||||
也就是:
|
||||
|
||||
- 保持 `/documents/[id]` 页面入口不变
|
||||
- 保持 `DocumentShell` 作为薄封装不变
|
||||
- 在 `DocumentContent` 内,把当前编辑态挂载的 `BlockNoteEditor` 替换为新的主编辑器 host
|
||||
|
||||
这样做的原因是:
|
||||
|
||||
1. 真实页面的布局、读写切换、评论、历史、检查器、AI 面板、移动/嵌入弹层都已经挂在这条链上。
|
||||
2. 如果不从这里切流,很多真实 bug 根本不会暴露。
|
||||
3. 这条链已经消费了 Rust `documents.content.get` 与 `pageSubtree`,最接近最终主链。
|
||||
|
||||
### 3.2 不冻结成主入口的点
|
||||
|
||||
下面这些点都不能被当成 `P0.5` 的正式主入口:
|
||||
|
||||
- `localhost:8123`
|
||||
原因:它只是 `leptos-tiptap` 的 spike/验证页。
|
||||
|
||||
- `mnote-web /document`
|
||||
原因:它当前仍是 `runtime shell`/独立宿主页,不是产品默认文档页。
|
||||
|
||||
- `mnote-web /document-debug`
|
||||
原因:它明确只应保留给诊断、回归和 debug。
|
||||
|
||||
## 4. 切流 Flag 策略
|
||||
|
||||
### 4.1 Flag 归属
|
||||
|
||||
主编辑器切流 flag 必须归属于真实文档页主链,而不是归属于 `runtime shell`。
|
||||
|
||||
冻结后的原则:
|
||||
|
||||
1. flag 归 `wolai-frontend` 文档页 host 所有。
|
||||
2. flag 控制的是 `DocumentContent` 编辑态里挂哪个 editor host。
|
||||
3. 不复用 `mnoteWebTreeShellEnabled` 之类的 tree shell 开关。
|
||||
|
||||
### 4.2 Flag 语义
|
||||
|
||||
`P0.5` 推荐的切流语义应当是:
|
||||
|
||||
- `blocknote`
|
||||
- `leptos_tiptap`
|
||||
|
||||
也就是说:
|
||||
|
||||
- 阅读态继续保持现有 `DocumentReadView`
|
||||
- 只替换编辑态 host
|
||||
- 页面路由不改
|
||||
- 文档 query/load 主链不改
|
||||
|
||||
### 4.3 Flag 不该控制的内容
|
||||
|
||||
切流 flag 不应控制:
|
||||
|
||||
- `mnote-web /document-debug` 是否可访问
|
||||
- tree shell 是否启用
|
||||
- 任何与 sidebar/filetree 实验壳有关的逻辑
|
||||
|
||||
原因:
|
||||
|
||||
这些不是主编辑器切流本身,混在一起只会让验证口径再次失真。
|
||||
|
||||
## 5. Runtime Shell 退场策略
|
||||
|
||||
### 5.1 `runtime shell` 的保留定位
|
||||
|
||||
`runtime shell` 不是立即删除,而是降级为下面两种用途:
|
||||
|
||||
1. `debug/prototype`
|
||||
2. 独立验收与对照环境
|
||||
|
||||
也就是说:
|
||||
|
||||
- `/document-debug` 继续存在
|
||||
- `/document` 在 `P0.5` 前也仍可保留为 Rust 侧独立 host
|
||||
- 但它们都不代表正式主编辑器切流完成
|
||||
|
||||
### 5.2 明确退场线
|
||||
|
||||
从这版开始,下面这句话固定下来:
|
||||
|
||||
> **只要默认文档页仍然不是 `Next /documents/[id] -> DocumentContent -> 新 editor host`,就不能宣称主编辑器已经切流完成。**
|
||||
|
||||
这条线用来防止后面再次把:
|
||||
|
||||
- 独立 demo
|
||||
- `runtime shell`
|
||||
- debug 页
|
||||
- 仅可单独访问的 Leptos 页面
|
||||
|
||||
误当成正式交付。
|
||||
|
||||
## 6. `P0.5` 的实际落点
|
||||
|
||||
`P0.5` 后续任务的实际落点应当按下面顺序推进:
|
||||
|
||||
1. 在 `DocumentContent` 中抽出“编辑态 host”这一层
|
||||
2. 先让 **`8123` 的真实 editor surface** 能进入真实文档页
|
||||
3. 再打通 load/save 与 Rust truth
|
||||
4. 再补 block id、schema、command、验收链
|
||||
|
||||
这里最重要的不是“先做更多 feature”,而是:
|
||||
|
||||
> **先让真实文档页开始消费新的 editor host。**
|
||||
|
||||
## 7. 未支持能力降级
|
||||
|
||||
在 `P0.5` 内,下面这些能力允许暂不支持,但必须显式降级:
|
||||
|
||||
- 图片上传
|
||||
- 表格
|
||||
- 页面引用
|
||||
- 块引用
|
||||
- heading collapse
|
||||
- 普通块缩进增强
|
||||
|
||||
原则:
|
||||
|
||||
1. 未支持能力不允许伪装成已完成。
|
||||
2. 未支持能力不能污染正式保存合同。
|
||||
3. 降级策略必须发生在真实主编辑器链路里,而不是藏在 `runtime shell` 中。
|
||||
|
||||
## 8. 对 task-002 的直接要求
|
||||
|
||||
`task-002` 开始时应直接按这份冻结结果执行:
|
||||
|
||||
1. 不改真实文档页入口
|
||||
2. 不先把 `mnote-web /document` 做成产品页
|
||||
3. 先在 `DocumentContent` 的编辑态 host 位接入 **`8123` 的真实 `leptos-tiptap` surface**
|
||||
4. `runtime shell` 继续只作为 debug/prototype 与对照验收环境
|
||||
|
||||
## 8.1 对 task-002 的额外冻结
|
||||
|
||||
从这版开始,`task-002` 的完成标准额外加上一条:
|
||||
|
||||
> **真实 `/documents/[id]` 页面里看到的编辑态,必须直接继承 `8123` 那套页面壳 / editor stage / toolbar / slash / handle 行为模型;不接受“只是换成另一个新的 host,但长得不像 8123”。**
|
||||
|
||||
这也意味着:
|
||||
|
||||
1. 不接受把 `/document` 或 `/document-debug` 的 runtime shell 套壳后冒充成主编辑器。
|
||||
2. 如果临时桥接层需要嵌入式承载 `8123` surface,也必须承载 **`rust/spikes/leptos-tiptap-spike` 的真实页面壳**,而不是另一套重新发明的 debug UI。
|
||||
3. `task-002` 只解决“让 8123 体验进入真实文档页”;load/save、Rust truth、block id 等正式合同继续留给后续 `task-003+`。
|
||||
|
||||
## 8.2 对接入方式的额外冻结
|
||||
|
||||
`task-002` 允许临时使用 `iframe` 承载真实 `8123` runtime,但不允许再走下面这条错误路线:
|
||||
|
||||
1. 把 `rust/spikes/leptos-tiptap-spike/dist` 直接当成 `wolai-frontend/public` 下的静态页面来嵌。
|
||||
2. 依赖宿主侧轮询 iframe DOM、注入一大段 CSS,去“修”出像 8123 的样子。
|
||||
3. 在真实页面外面再包一层新的 bridge card / bridge banner / debug 文案,导致最终截图看起来已经不是 8123 行为模型。
|
||||
|
||||
冻结后的临时接法应当是:
|
||||
|
||||
1. 真实 `/documents/[id]` 页面继续作为唯一验收入口。
|
||||
2. `MainEditorHost` 只负责承载 `8123` runtime,不再重新发明一层页面壳。
|
||||
3. `8123` 自己提供嵌入模式,把 standalone/debug UI 在 spike 内原生收掉。
|
||||
4. host 与 spike 之间通过显式 embed 协议同步 `ready` / `height`,而不是依赖脆弱的跨文档 DOM 轮询。
|
||||
|
||||
## 9. 最终口径
|
||||
|
||||
这份文档固定下来的最终口径是:
|
||||
|
||||
> **主编辑器接入点是 `wolai-frontend` 的真实文档页编辑态 host,不是 `runtime shell`;`mnote-web /document` 与 `/document-debug` 继续只保留为 `debug/prototype`,直到真实文档页完成切流为止。**
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
# 5-3 [recycle] Tiptap + `leptos-tiptap` + Rust Kernel 迁移清单 v1
|
||||
|
||||
> 更新时间:2026-04-19
|
||||
>
|
||||
> 这份文档已经按当前现实重排:
|
||||
>
|
||||
> `localhost:8123` 证明了 `Leptos + leptos-tiptap` 这条路线可行,当前不再需要继续证明“能不能做出一个像 Tiptap 的 spike”。
|
||||
>
|
||||
> 当前最关键的问题是:
|
||||
>
|
||||
> **它还没有接入主编辑器,所以真实页面中的加载、保存、路由、投影、权限、布局、回归 bug 都还没有暴露完全。**
|
||||
|
||||
## 1. 当前结论
|
||||
|
||||
这条线现在应当明确收口到下面这个口径:
|
||||
|
||||
1. 体验 benchmark 继续参考 `Tiptap` 官方 `notion-like-editor`。
|
||||
2. 浏览器编辑 runtime 继续使用 `leptos-tiptap`。
|
||||
3. 文档真相、块语义、引用语义、保存合同继续由 Rust/kernel 掌握。
|
||||
4. 当前阶段不再平均推进所有功能,而是先完成 `P0.5`:
|
||||
**把当前编辑器接入主编辑器,并接上 Rust truth。**
|
||||
|
||||
这意味着:
|
||||
|
||||
- 不是继续打磨孤立 spike 页面。
|
||||
- 不是继续围绕 runtime shell 做体验修补。
|
||||
- 不是先做一长串 Wolai 对标增强项。
|
||||
- 而是先让这套编辑器进入真实文档链路。
|
||||
|
||||
## 2. 参考优先级
|
||||
|
||||
主参考优先级固定如下:
|
||||
|
||||
1. `Tiptap` 官方 `notion-like-editor`
|
||||
2. `leptos-tiptap`
|
||||
3. `core-protocol` / `mnote-editor-core` / `mnote-web`
|
||||
4. `blocks`
|
||||
5. `kode`
|
||||
|
||||
明确降级说明:
|
||||
|
||||
- `mnote-next` 只保留为历史样本,不作为当前主参考。
|
||||
- `edita-core` 只保留为 Rust-native 块模型参考,不作为当前体验 benchmark。
|
||||
- `runtime shell` 只保留为 spike/debug,不再作为正式编辑器目标页。
|
||||
|
||||
## 3. 本地参考位置
|
||||
|
||||
### 3.1 官方体验 benchmark
|
||||
|
||||
- 模板入口:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-templates/notion-like/page.tsx`
|
||||
- 主编辑器:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-templates/notion-like/components/notion-like-editor.tsx`
|
||||
- 浮动工具条:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-templates/notion-like/components/notion-like-editor-toolbar-floating.tsx`
|
||||
- slash 菜单:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-ui/slash-dropdown-menu/slash-dropdown-menu.tsx`
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-ui/slash-dropdown-menu/use-slash-dropdown-menu.ts`
|
||||
- 块菜单与手柄:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-ui/drag-context-menu/drag-context-menu.tsx`
|
||||
- 缩进扩展:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-notion-like-registry/materialized/tiptap-extension/indent-extension.ts`
|
||||
|
||||
### 3.2 Leptos 运行时桥接参考
|
||||
|
||||
- `leptos-tiptap` crate:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/Cargo.toml`
|
||||
- Rust 扩展枚举:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/extensions.rs`
|
||||
- Rust 命令 API:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/commands.rs`
|
||||
- 运行时注册:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/runtime/registration.rs`
|
||||
- JS 扩展实现:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/tiptap/src/extensions/`
|
||||
|
||||
### 3.3 `mnote` 当前主线落点
|
||||
|
||||
- 当前 `Leptos + Tiptap` spike:
|
||||
`/mnt/Data1T/mnote/rust/spikes/leptos-tiptap-spike/src/main.rs`
|
||||
- Rust editor 模型:
|
||||
`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
- Rust editor 命令:
|
||||
`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/command.rs`
|
||||
- Rust editor core:
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-editor-core/src/model.rs`
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-editor-core/src/command.rs`
|
||||
- Rust 文档与查询桥:
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/documents.rs`
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/query_support.rs`
|
||||
`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs`
|
||||
|
||||
### 3.4 辅助参考
|
||||
|
||||
- `blocks`:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/lib.rs`
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/history.rs`
|
||||
- `kode`:
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/markdown_editor_component.rs`
|
||||
`/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/wysiwyg/tree_editor.rs`
|
||||
|
||||
## 4. 当前真实基线
|
||||
|
||||
### 4.1 已经完成的基础验证
|
||||
|
||||
当前 spike 已经证明下面这些能力可以跑起来:
|
||||
|
||||
- 真正的 Tiptap surface 已可渲染
|
||||
- `paragraph / heading / bullet list / ordered list / task list / quote / code block / divider` 可编辑
|
||||
- slash 菜单已具备最小产品级入口
|
||||
- 浮动工具条已具备常用文本操作
|
||||
- 左侧块手柄、块菜单、拖拽、`turn into` 已有一版
|
||||
- reload 后结构不再退化成纯文本
|
||||
- `get_html()` / `get_json()` / `on_change` 可用
|
||||
|
||||
### 4.2 当前真正缺的不是“再做一个 spike 功能”
|
||||
|
||||
当前真正缺的是:
|
||||
|
||||
- 还没进入主编辑器
|
||||
- 还没接上正式 Rust 保存链路
|
||||
- 还没建立稳定 block id 真相边界
|
||||
- 还没把当前 schema 与 `EditorBlockType` / `EditorCommand` 对齐成正式合同
|
||||
|
||||
因此,下面这些事项都不应继续排在前面:
|
||||
|
||||
- Markdown 导入导出回归
|
||||
- `kode` 分层整理
|
||||
- 完整表格
|
||||
- 协作
|
||||
- Tiptap Cloud AI
|
||||
|
||||
## 5. 当前阶段划分
|
||||
|
||||
新的阶段划分改为:
|
||||
|
||||
- `P0.5`:接入主编辑器,接上 Rust truth
|
||||
- `P1`:补齐成为正式主编辑器所需的结构能力
|
||||
- `P1.5`:对标 Wolai 的操作层与块体验增强
|
||||
- `P2`:增强项与后置兼容项
|
||||
|
||||
其中:
|
||||
|
||||
**当前只重点规划 `P0.5`。**
|
||||
|
||||
`P1 / P1.5` 先只保留方向,不在本轮继续展开成大而全 checklist。
|
||||
|
||||
## 6. P0.5 Checklist
|
||||
|
||||
### 6.1 目标
|
||||
|
||||
`P0.5` 的目标不是继续“把 demo 做得更像官方模板”,而是:
|
||||
|
||||
> **把当前已经可用的 `leptos-tiptap` 编辑器接入真实主编辑器链路,让 bug 在真实环境里暴露,并让 Rust 成为正式保存与块语义边界。**
|
||||
|
||||
### 6.2 完成标准
|
||||
|
||||
满足下面条件,才算 `P0.5` 完成:
|
||||
|
||||
1. 默认文档编辑路径使用真正的 Tiptap/Leptos 编辑器,而不是 runtime shell。
|
||||
2. 文档打开、刷新、保存、重新进入后,结构保持稳定。
|
||||
3. 块级结构与 Rust `EditorBlockDocument` 有明确映射。
|
||||
4. 块的身份标识不再依赖前端临时索引。
|
||||
5. 当前已有的 slash / toolbar / handle / turn into 能在真实页面中工作。
|
||||
|
||||
### 6.3 Checklist
|
||||
|
||||
- [ ] 明确主编辑器接入点。
|
||||
目标:不再让 `localhost:8123` 这种孤立 spike 充当“完成态”。
|
||||
要求:确定真实文档页的接入入口,后续 bug 统一在主页面暴露和验证。
|
||||
|
||||
- [ ] 明确 runtime shell 退场策略。
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs`
|
||||
原则:该路线只保留为 debug/prototype,不再作为正式文档页 benchmark。
|
||||
|
||||
- [ ] 将当前 `leptos-tiptap` 编辑器嵌入真实页面壳。
|
||||
要求:进入真实文档路由、真实布局、真实加载链,而不是单独的实验页。
|
||||
目的:尽早暴露首屏、刷新、回填、焦点、布局、侧栏联动、回归问题。
|
||||
补充口径:这里指的是把 `8123` 那套真实 `Leptos + Tiptap` 页面壳 / editor stage 接进主编辑器,而不是另做一套长得不像 `8123` 的 host。
|
||||
|
||||
- [ ] 建立正式 load/save 边界。
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/documents.rs`
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
要求:前端 runtime 输入与 Rust 真相之间有单一转换边界,不再引入 shell 专用格式。
|
||||
|
||||
- [ ] 明确 `EditorBlockDocument -> Tiptap doc -> EditorBlockDocument` 的最小映射。
|
||||
`P0.5` 至少覆盖:
|
||||
`Paragraph`、`Heading`、`BulletListItem`、`NumberedListItem`、`Todo`、`Quote`、`CodeBlock`
|
||||
目标:先保证主链常用块类型可稳定往返,不追求一次做完整能力面。
|
||||
|
||||
- [ ] 提前引入稳定 block id 策略。
|
||||
参考:官方 `UniqueID.configure(...)`
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
原则:Rust `EditorBlock.block_id` 是最终真相;`UniqueID` 只作为浏览器 runtime 辅助,不应反过来成为持久化规则。
|
||||
|
||||
- [ ] 对齐最小命令合同,而不是先做完所有体验功能。
|
||||
参考:`/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/command.rs`
|
||||
`P0.5` 优先对齐:
|
||||
`ReplaceBlock`
|
||||
`InsertBlockAfter`
|
||||
`DeleteBlock`
|
||||
`SplitBlock`
|
||||
`MergeWithPrevious`
|
||||
`MoveBlock`
|
||||
说明:`IndentBlock`、`OutdentBlock`、`ToggleHeadingCollapse` 保留到后续阶段,不作为 `P0.5` 阻塞项。
|
||||
|
||||
- [ ] 复用当前已有的 slash / toolbar / handle / turn into,而不是重写第二遍。
|
||||
落点:以现有 spike 行为为基础迁入主页面,再按官方行为微调。
|
||||
原则:当前阶段优先“接入”,不是再做一轮自娱自乐式 UI 重构。
|
||||
|
||||
- [ ] 定义未支持能力的降级策略。
|
||||
当前明确未进入 `P0.5`:
|
||||
图片上传、表格、页面引用、块引用、heading collapse、普通块缩进增强
|
||||
要求:未支持时要有显式降级,不允许用临时假功能污染正式合同。
|
||||
|
||||
- [ ] 建立主编辑器验收口径。
|
||||
最低验收应覆盖:
|
||||
打开文档
|
||||
编辑常用块
|
||||
slash 插入块
|
||||
turn into 切换块类型
|
||||
刷新后结构保留
|
||||
再次进入后结构保留
|
||||
目的:从现在开始,bug 判断以主编辑器为准,而不是以 spike 页面为准。
|
||||
|
||||
## 7. P1 方向
|
||||
|
||||
`P1` 只保留方向,不在这一轮继续扩成大 checklist。
|
||||
|
||||
`P1` 解决的是“成为正式主编辑器还缺的结构能力”:
|
||||
|
||||
- 页面引用 / 块引用
|
||||
- heading collapse 与树语义对齐
|
||||
- 最小图片/附件桥接
|
||||
- 更完整的 Rust save pipeline 与局部更新
|
||||
|
||||
其中优先级判断如下:
|
||||
|
||||
1. 页面引用 / 块引用
|
||||
2. heading collapse
|
||||
3. 最小图片/附件桥接
|
||||
4. 其它结构增强
|
||||
|
||||
## 8. P1.5 方向
|
||||
|
||||
`P1.5` 才进入真正的 Wolai 操作层对标。
|
||||
|
||||
这一阶段再重新规划下面这些体验增强:
|
||||
|
||||
- 手柄行为优化
|
||||
- `turn into page`
|
||||
- 普通块缩进体验
|
||||
- 块菜单分组与文案优化
|
||||
- 更细的 hover / selection / anchor 行为
|
||||
- 更接近 Wolai 的块级交互细节
|
||||
|
||||
说明:
|
||||
|
||||
这些都值得做,但它们不应先于“成为主编辑器”发生。
|
||||
|
||||
## 9. P2 方向
|
||||
|
||||
`P2` 才处理后置增强与兼容项:
|
||||
|
||||
- 表格评估
|
||||
- 更完整的图片上传链
|
||||
- Markdown 导入导出回归
|
||||
- `blocks` 差异校验
|
||||
- `kode` 组件分层整理
|
||||
- AI 命令入口
|
||||
|
||||
## 10. 当前不进入主线前排的事项
|
||||
|
||||
下面这些项不删除,但从主 checklist 前排降级:
|
||||
|
||||
- Markdown 导入导出回归
|
||||
作用是 compatibility/testing,不直接解锁主编辑器接入。
|
||||
|
||||
- `kode` 组件分层参考
|
||||
它是实现注记,不是交付里程碑。
|
||||
|
||||
- 完整表格
|
||||
当前不是高频需求,也不是主编辑器切流前置项。
|
||||
|
||||
- 协作与 Tiptap Cloud AI
|
||||
与当前 AI-first / Rust-kernel-first 路线不一致。
|
||||
|
||||
## 11. 一句话收口
|
||||
|
||||
当前正确路线不是继续把 spike 做得更漂亮,而是:
|
||||
|
||||
**先把现有 `leptos-tiptap` 编辑器接入主编辑器,并用 Rust 接管正式保存与块真相;等这一步完成后,再系统规划 Wolai 对标体验增强。**
|
||||
@@ -0,0 +1,282 @@
|
||||
# 5 [recycle] mnote 编辑器主线重置定稿 v2
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 这份文档用于替代此前偏向“Rust-native 全自研编辑器”的主线口径。
|
||||
>
|
||||
> 本文结论只解决两件事:
|
||||
> 1. 后续编辑器主线到底以谁为基准
|
||||
> 2. 最近 git 历史里,哪些改动不该整包回滚,哪些应该局部撤回或降级
|
||||
|
||||
## 1. 结论先行
|
||||
|
||||
当前应冻结为下面这条主线:
|
||||
|
||||
> **后续编辑器的体验 benchmark 以 `Tiptap` 官方能力模型为准,Leptos 接入层以 `leptos-tiptap` 为主参考;Rust 侧继续掌握 kernel / command / projection / AI / CLI 语义主导权。**
|
||||
|
||||
同时固定三条边界:
|
||||
|
||||
- `edita-core` 不再被视为主线 benchmark,只保留为 Rust-native headless block editor 的设计参考。
|
||||
- `kode`、`blocks` 只作为局部参考层,不作为最终交付体验 benchmark。
|
||||
- `mnote-next` 只作为内部样本和反例来源,不作为“已经验证过的主参考实现”。
|
||||
|
||||
一句话收口:
|
||||
|
||||
> **我们不再把“做出一个 Rust 原生最小编辑器”当成交付目标,而是把“做出接近 Tiptap / Wolai 的实际编辑体验,同时让 Rust 继续掌握底层语义”当成交付目标。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 为什么要重置口径
|
||||
|
||||
此前路线逐渐偏成了下面这个结构:
|
||||
|
||||
- 用 `AI-first / CLI-first / Rust-native` 作为最高目标
|
||||
- 把“最小可写闭环”当成阶段验收
|
||||
- 结果自然滑向 `runtime shell + textarea + command button + debug panel`
|
||||
|
||||
这条路线的问题不在于“做错了”,而在于:
|
||||
|
||||
- 它更像内核验证路线,不像产品交付路线
|
||||
- 它会天然高估 `edita-core` 这类薄内核的完成度
|
||||
- 它会天然低估 `Tiptap` 在 selection、IME、输入规则、历史、富文本细节上的工程积累
|
||||
|
||||
从当前代码看,偏移已经非常具体:
|
||||
|
||||
- 默认文档页已经能被切到 `iframe` 挂载的 `mnote-web /document` 壳
|
||||
- [page.tsx](/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx)
|
||||
- [mnote-web-document-shell-host.tsx](/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/mnote-web-document-shell-host.tsx)
|
||||
- Rust `/document` 路由现在是 runtime/debug 壳,不是产品文档页
|
||||
- [editor.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs)
|
||||
- 新 smoke 脚本已经把“看到 `Document Editor Shell` + `textarea[data-block-input-id]`”当成通过标准
|
||||
- [task103-document-shell-cutover-smoke.js](/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js)
|
||||
- [task104-document-runtime-input-smoke.js](/mnt/Data1T/mnote/scripts/task104-document-runtime-input-smoke.js)
|
||||
|
||||
这说明问题不是“样式没做完”,而是默认交付口径已经被带偏。
|
||||
|
||||
---
|
||||
|
||||
## 3. 新的主线基准
|
||||
|
||||
### 3.1 主 benchmark
|
||||
|
||||
主 benchmark 固定为两层:
|
||||
|
||||
1. 体验与能力模型:`Tiptap`
|
||||
2. Leptos 接入层:`leptos-tiptap`
|
||||
|
||||
对应的设计含义是:
|
||||
|
||||
- 需要接近 `Wolai / Notion / Tiptap` 的输入手感、selection、slash、块间过渡和工具栏组织
|
||||
- 不需要为了“全 Rust”主动放弃成熟编辑器 runtime
|
||||
- Rust 的职责转移到更适合 Rust 的层:kernel truth、command contract、projection、AI/CLI、save pipeline
|
||||
|
||||
### 3.2 辅助参考层
|
||||
|
||||
这些库继续保留,但口径必须降级:
|
||||
|
||||
- `edita-core`
|
||||
- 只参考 `Editor / Block / Command` 这种 headless 组织方式
|
||||
- 不再把它视为可直接对标 `Tiptap` 体验的候选
|
||||
- `blocks`
|
||||
- 参考文档模型、导入导出、history、diff/merge
|
||||
- 不负责最终交互体验 benchmark
|
||||
- `kode`
|
||||
- 参考 Leptos 下文本/Markdown/WYSIWYG 组织方式
|
||||
- 适合借鉴局部实现,不适合作为当前最终产品 benchmark
|
||||
- `mnote-next`
|
||||
- 只保留为内部交互样本、历史问题样本
|
||||
- 不能再作为“因为我们以前做过,所以现在能直接沿着这条线走”的依据
|
||||
|
||||
### 3.3 Rust 侧的正确位置
|
||||
|
||||
Rust 仍然是主线,但不是靠“自己重写整套输入 runtime”来体现。
|
||||
|
||||
Rust 侧应继续掌握:
|
||||
|
||||
- 文档树 / 子树 / 边 / projection
|
||||
- 编辑命令合同
|
||||
- AI 命令合同
|
||||
- CLI 批处理与自动化
|
||||
- 存储、save、审计、导入导出
|
||||
|
||||
Rust 侧不应优先承担:
|
||||
|
||||
- 第一阶段的人类编辑交互 runtime
|
||||
- 复杂 selection / IME / 富文本输入细节
|
||||
- 与成熟浏览器编辑器生态重复造轮子
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前代码后的判断
|
||||
|
||||
### 4.1 不建议整包回滚的部分
|
||||
|
||||
下面这些改动虽然和编辑器路线有关,但不应整包撤销:
|
||||
|
||||
- `aa6ee384 feat: 接入 mnote web tree shell 与主页链路整理`
|
||||
- `f1c1bcf0 feat: 收口 tree-first graph 主链与前端测试修复`
|
||||
- `f9a45e89 feat: complete tree shell cutover and regression coverage`
|
||||
|
||||
原因很简单:
|
||||
|
||||
- 这三次提交的主轴是 `tree-first graph kernel`、sidebar/tree shell、projection、stream、transport
|
||||
- 它们不是“文档编辑器主线基准错误”的根因
|
||||
- 直接整包回滚会误伤已经有效的 tree shell 主链工作
|
||||
|
||||
因此:
|
||||
|
||||
> **不建议回滚最近三次已提交主线 commit。**
|
||||
|
||||
### 4.2 应局部回退或降级的部分
|
||||
|
||||
真正需要处理的是当前工作区里把 runtime 壳推上默认主链的那一层实验改动。
|
||||
|
||||
优先级最高的局部回退对象:
|
||||
|
||||
- 默认文档页中的 `mnoteWebDocumentShellEnabled` 分支
|
||||
- [page.tsx](/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx)
|
||||
- `iframe` 挂载壳本身
|
||||
- [mnote-web-document-shell-host.tsx](/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/mnote-web-document-shell-host.tsx)
|
||||
- 把 runtime debug 壳当默认验收的 smoke 脚本
|
||||
- [task103-document-shell-cutover-smoke.js](/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js)
|
||||
- [task104-document-runtime-input-smoke.js](/mnt/Data1T/mnote/scripts/task104-document-runtime-input-smoke.js)
|
||||
- [task105-document-runtime-transactions-smoke.js](/mnt/Data1T/mnote/scripts/task105-document-runtime-transactions-smoke.js)
|
||||
- [task106-document-runtime-slash-reference-smoke.js](/mnt/Data1T/mnote/scripts/task106-document-runtime-slash-reference-smoke.js)
|
||||
- [task107-document-runtime-save-smoke.js](/mnt/Data1T/mnote/scripts/task107-document-runtime-save-smoke.js)
|
||||
|
||||
这些部分的问题不是“代码质量差”,而是:
|
||||
|
||||
- 它们把 debug/prototype 壳变成了默认产品路径
|
||||
- 它们会持续把开发注意力引向 runtime shell polish,而不是真正的页面级编辑体验
|
||||
|
||||
因此:
|
||||
|
||||
> **建议优先局部撤回“默认走 runtime shell”这组未提交改动。**
|
||||
|
||||
### 4.3 可保留但必须降级定位的部分
|
||||
|
||||
下面这些可以保留,但不能继续被描述成默认主编辑器:
|
||||
|
||||
- `mnote-web` 的 `/document` 路由
|
||||
- [editor.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs)
|
||||
- 文档 meta/content/save API
|
||||
- [documents.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/documents.rs)
|
||||
- [query_support.rs](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/query_support.rs)
|
||||
- 运行时配置里的 document shell 字段
|
||||
- [runtime-config.ts](/mnt/Data1T/mnote/wolai-frontend/src/lib/runtime-config.ts)
|
||||
|
||||
保留条件只有一个:
|
||||
|
||||
> **它们只能作为 debug / prototype / bridge API 存在,不能再主导默认文档页。**
|
||||
|
||||
### 4.4 可继续保留观察的 Rust editor core 尝试
|
||||
|
||||
当前未提交的这些底层尝试,不建议直接删除,但也不应被提升为主 benchmark:
|
||||
|
||||
- [core-protocol/src/editor/mod.rs](/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/mod.rs)
|
||||
- [mnote-editor-core/src/lib.rs](/mnt/Data1T/mnote/rust/crates/mnote-editor-core/src/lib.rs)
|
||||
|
||||
原因:
|
||||
|
||||
- 它们已经抽出了 `EditorBlockDocument / EditorCommand / CommandExecutor` 这类可复用底层合同
|
||||
- 这层更像“Rust command/model spike”
|
||||
- 它们比 runtime shell UI 更有保留价值
|
||||
|
||||
但当前也要明确:
|
||||
|
||||
- 它们还不足以支撑 `Tiptap` 级人类编辑体验
|
||||
- 它们只能服务未来的命令合同、AI pipeline、导入导出和调试转换
|
||||
- 不能再反向决定默认产品体验
|
||||
|
||||
---
|
||||
|
||||
## 5. 后续开发阶段
|
||||
|
||||
### 阶段 A:主链纠偏
|
||||
|
||||
目标:
|
||||
|
||||
- 把默认文档页从 runtime shell 退回产品页主链
|
||||
- 明确 `runtime shell = debug`
|
||||
|
||||
清单:
|
||||
|
||||
- [x] 取消默认文档页对 `mnote-web document shell iframe` 的优先切换
|
||||
- [x] 将 `/document` 路由和相关文案重命名为 debug/prototype
|
||||
- [x] 停止用 `task103-107` 这组脚本作为默认文档页主验收
|
||||
- [ ] 重新把验收标准收口到“是否接近 Wolai/Tiptap 页面体验”
|
||||
|
||||
### 阶段 B:Leptos-Tiptap 接入最小闭环
|
||||
|
||||
目标:
|
||||
|
||||
- 用 `leptos-tiptap` 验证真正的 Leptos + Tiptap 组合是否能在当前工程中跑通
|
||||
|
||||
清单:
|
||||
|
||||
- [x] 建一个最小 `leptos-tiptap` 文档页 spike,不接入复杂业务,只验证真实输入体验
|
||||
- [ ] 打通基础 schema:标题、段落、列表、todo、blockquote、code block
|
||||
- [x] 验证命令触发:slash、toggle heading、toggle list、引用插入
|
||||
- [ ] 记录 CSR/SSR、构建体积、初始化耗时、输入延迟、保存触发点
|
||||
|
||||
补充说明(2026-04-18):
|
||||
|
||||
- 已新增独立 spike 工程:`/mnt/Data1T/mnote/rust/spikes/leptos-tiptap-spike`
|
||||
- 该 spike 当前目标是先验证真实 Tiptap surface 与基础 schema/命令,不进入默认产品文档页
|
||||
- 已完成 `cargo +1.89.0 check` 与 `env -u NO_COLOR trunk build --release`
|
||||
- 已通过本地浏览器访问 `http://127.0.0.1:8123/` 验证 CSR 页面可渲染、可输入,并能触发 heading/list/blockquote/code block/slash 引用插入
|
||||
- 当前 `Todo` 仍是 HTML 占位插入,尚未拿到 `taskList/taskItem` 级真实 schema 语义,因此第二项暂不勾满
|
||||
- 已记录到的观察值:
|
||||
- CSR:可用
|
||||
- SSR:未接入
|
||||
- 构建体积:`js 45K`,`wasm 592K`
|
||||
- 首次页面就绪时间:约 `552ms`(本地 Playwright `networkidle` 口径)
|
||||
- 单字符输入到保存触发点计数变化:约 `26ms`
|
||||
|
||||
补充说明(2026-04-19):
|
||||
|
||||
- `Tiptap` 官方 `Notion-like template` 是 `React + Tiptap Editor + Tiptap UI Components + CLI` 的产品级模板,适合作为后续 UI benchmark,而不是当前 `Leptos` 主线的直接实现路径
|
||||
- 在当前阶段,最合适的接入时机是:先把 `leptos-tiptap` 的最小输入闭环、基础 schema、保存边界和 Rust kernel 映射稳定住,再单独开一轮模板对标 spike
|
||||
- 这个模板的价值主要在于对标 `slash`、floating toolbar、drag & drop、emoji、mentions、collaboration、AI、context menu 等“产品级编辑体验”,不是替代我们当前的 Rust kernel / projection 主线
|
||||
|
||||
### 阶段 C:Rust kernel 对接
|
||||
|
||||
目标:
|
||||
|
||||
- 让 `Tiptap/leptos-tiptap` 负责编辑 surface
|
||||
- 让 Rust 继续掌握 document truth
|
||||
|
||||
清单:
|
||||
|
||||
- [ ] 确定 Tiptap JSON / HTML / 自定义节点 与 Rust `EditorBlockDocument` 的映射边界
|
||||
- [ ] 统一 save pipeline,不再维护“runtime 壳专用数据格式”
|
||||
- [ ] page subtree / outline / evidence 继续由 Rust 侧提供
|
||||
- [ ] AI/CLI 改文档时,优先走 Rust command contract,再映射回编辑器展示
|
||||
|
||||
### 阶段 D:AI-first 产品收口
|
||||
|
||||
目标:
|
||||
|
||||
- 编辑器体验接近 Wolai/Tiptap
|
||||
- 系统语义主导权仍在 Rust/AI/CLI
|
||||
|
||||
清单:
|
||||
|
||||
- [ ] 人类编辑入口只保留高频块能力,不追完整 Notion
|
||||
- [ ] AI 输出优先落到块级命令,而不是直接拼 HTML
|
||||
- [ ] debug 壳只保留给事务诊断、IME 排障、save 链路回归
|
||||
- [ ] 正式文档页只保留产品级界面,不再暴露 runtime panel
|
||||
|
||||
---
|
||||
|
||||
## 6. 最终决策
|
||||
|
||||
最终固定如下:
|
||||
|
||||
1. 编辑器主 benchmark 改为 `Tiptap + leptos-tiptap`。
|
||||
2. `edita-core` 不再作为主线 benchmark,只保留为 headless 设计参考。
|
||||
3. 最近三次已提交的 `tree shell / tree-first graph` 主线 commit 不建议整包回滚。
|
||||
4. 当前工作区里“把 runtime shell 接成默认文档页”的实验改动,建议局部撤回。
|
||||
5. `mnote-editor-core` 与 `core-protocol/editor` 可以保留为底层 spike,但不得继续主导默认交付路线。
|
||||
|
||||
如果后续要继续补文档、排期或拆 checklist,都以本文为准,不再以此前偏向“Rust-native 全自研编辑器”的文档口径为准。
|
||||
@@ -0,0 +1,936 @@
|
||||
# [recycle] mnote AI-First Rust 块编辑器主基线定稿 v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-1-tree-first-graph-kernel-checklist-v2.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/blockselect.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/done/tiptap.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-model-v0.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-command-contract-v0.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/`
|
||||
> - `/mnt/Data1T/mnote-next/docs/architecture/p1-human-stable-interface.md`
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本文用于在新的产品前提下,重新冻结 `mnote` 后续块编辑器开发的主基线。
|
||||
|
||||
这版定稿不再以“是否尽量接近 `BlockNote` / `Tiptap` 的完整体验”为第一判断,而是以:
|
||||
|
||||
- AI-first
|
||||
- CLI-first
|
||||
- Rust-native
|
||||
- 轻量块编辑
|
||||
- 低复杂度、低维护成本
|
||||
|
||||
作为最高优先级。
|
||||
|
||||
本文回答四件事:
|
||||
|
||||
1. 后续块编辑器主线应该以谁为基准
|
||||
2. `edita`、`blocks`、`kode`、`leptos-tiptap`、`mnote-next` 各自的正确定位
|
||||
3. 在新的前提下,为何可以接受更轻量的编辑器能力面
|
||||
4. 替换现有 `BlockNote` 的方向与开发阶段
|
||||
|
||||
---
|
||||
|
||||
## 2. 新前提
|
||||
|
||||
这版定稿建立在下面四条前提上。
|
||||
|
||||
### 2.1 `mnote-next` 只能提供内部交互样本,不能当主基准
|
||||
|
||||
`mnote-next` 中确实出现过一批值得回看的交互尝试,包括:
|
||||
|
||||
- 拆块
|
||||
- 合并块
|
||||
- 插入块
|
||||
- 删除块
|
||||
- 有限缩进
|
||||
- 标题折叠
|
||||
- 引用 token
|
||||
- 基础块菜单与 slash 命令
|
||||
|
||||
这意味着:
|
||||
|
||||
> **我们不是从零开始想象块编辑器,但也不能把 `mnote-next` 当成已经外部验证完成的 benchmark。它更适合作为内部样例、回归样本和反例来源。**
|
||||
|
||||
### 2.2 用户真实高频需求不是完整 Notion
|
||||
|
||||
用户长期高频需求主要是:
|
||||
|
||||
- Markdown 风格记录
|
||||
- 标题与列表
|
||||
- 折叠
|
||||
- 待办
|
||||
- 基础引用
|
||||
- 少量导入、修改、校对
|
||||
|
||||
而不是:
|
||||
|
||||
- 重度协作
|
||||
- 复杂表格
|
||||
- 大量富文本格式
|
||||
- 高级评论系统
|
||||
- 完整页面级可视化编辑生态
|
||||
|
||||
### 2.3 当前不需要协作
|
||||
|
||||
协作不是当前主线需求,因此:
|
||||
|
||||
- 不需要为协作牺牲大量复杂度
|
||||
- 不需要为了 Yjs / Hocuspocus / comment thread 去设计第一版架构
|
||||
- 不需要把“多人同时编辑一致性”当成当前编辑器路线的决定性条件
|
||||
|
||||
### 2.4 产品最终意义是 AI-first 笔记软件
|
||||
|
||||
项目最终方向固定为:
|
||||
|
||||
> **一个 AI 为底层、类似轻量化 Obsidian + CLI 的笔记软件;AI 是主要编辑与输出主体,人类主要负责导入、校对和局部修改。**
|
||||
|
||||
这意味着:
|
||||
|
||||
- 编辑器首先服务 AI 命令和结构化输出
|
||||
- 人类编辑只是辅助链路
|
||||
- 命令统一比富文本完整性更重要
|
||||
- 结构可编排、可脚本化、可审计,比可视化 polish 更重要
|
||||
|
||||
---
|
||||
|
||||
## 3. 最终定稿结论
|
||||
|
||||
最终口径固定如下:
|
||||
|
||||
> **后续编辑器开发以 AI-first、CLI-first、Rust-native 的轻量块编辑器为主线;以 `edita-core + blocks + kode` 作为主基准组合;以 `leptos-tiptap` 作为 Leptos fallback 接入参考;`mnote-next` 只保留为内部交互样本,不作为主 benchmark。**
|
||||
|
||||
进一步展开就是:
|
||||
|
||||
- **事实源基线**:仍然只有 Rust kernel 的 `node / edge / subtree / projection / command`
|
||||
- **编辑器主线基线**:Rust-native minimal block editor
|
||||
- **Rust 编辑器参考**:`edita-core` 的 headless block editor / command 思路
|
||||
- **块模型与转换参考**:`blocks` 的文档模型、JSON/Markdown/HTML 转换、history / diff / merge 思路
|
||||
- **Leptos 编辑输入参考**:`kode` 的 `kode-core / kode-leptos / kode-doc` 分层与 Markdown/WYSIWYG/tree editor 构件
|
||||
- **Leptos fallback 参考**:`leptos-tiptap` 的组件、hook、runtime bridge 与 demo
|
||||
- **第一阶段模型冻结**:以 `rust-block-editor-model-v0.md` 中的 `EditorDocument / EditorBlockNode / BlockProps / content_node` 口径为准
|
||||
- **第一阶段命令冻结**:以 `rust-block-editor-command-contract-v0.md` 中的 `replace_block / insert_block_after / delete_block / split_block / merge_with_previous / move_block / indent_block / outdent_block / toggle_heading_collapse / attach_reference_token / detach_reference_token` 为准
|
||||
- **内部样本参考**:`mnote-next` 只作为曾尝试过的拆块、合并、缩进、折叠、引用交互样本,不作为采用优先级
|
||||
|
||||
一句话总结:
|
||||
|
||||
> **主线不再是“如何更好地使用 `Tiptap`”,而是“如何基于现有外部 Rust / Leptos 参考层拼出一个由 Rust command 驱动、以 AI 为主要操作者的最小块编辑器”。**
|
||||
|
||||
再收口一句:
|
||||
|
||||
> **这不是“从零全写编辑器”的路线,而是“先采用 `edita-core / blocks / kode / leptos-tiptap` 已有能力,再只为 `mnote` 的 kernel 接缝和特有语义补胶水”的路线。**
|
||||
|
||||
---
|
||||
|
||||
## 4. 主线选型口径
|
||||
|
||||
### 4.1 主线基准:`edita-core + blocks + kode`
|
||||
|
||||
主线基准不是某个现成成品,而是三部分外部参考组合:
|
||||
|
||||
1. `edita-core`
|
||||
- 负责提供无头 Rust 编辑器的参考方向
|
||||
- 重点参考:
|
||||
- block model
|
||||
- command execution
|
||||
- headless editor 边界
|
||||
2. `blocks`
|
||||
- 负责提供块文档模型与多格式转换参考
|
||||
- 重点参考:
|
||||
- `Document / Block / BlockType`
|
||||
- JSON / Markdown / HTML / Plain Text 双向转换
|
||||
- history / undo redo 思路
|
||||
- diff / merge / pipeline / sanitizer
|
||||
3. `kode`
|
||||
- 负责提供 Leptos 侧编辑输入和文档树构件参考
|
||||
- 重点参考:
|
||||
- `kode-core` 的 text buffer / selection / editing primitives
|
||||
- `kode-leptos` 的 Leptos 组件组织
|
||||
- `MarkdownEditorComponent`
|
||||
- `kode-doc` 的 structured document model
|
||||
|
||||
补充两条非主基准但仍值得保留的参考:
|
||||
|
||||
4. `leptos-tiptap`
|
||||
- 负责提供 Leptos 下对接成熟编辑器 runtime 的 fallback 样本
|
||||
- 重点参考:
|
||||
- `TiptapEditor` 组件
|
||||
- `use_tiptap_editor` hook
|
||||
- runtime bridge
|
||||
- CSR / SSR demo
|
||||
5. `mnote-next`
|
||||
- 只作为内部交互样本和回归样例来源
|
||||
- 重点参考:
|
||||
- 曾经尝试过的拆块 / 合并 / 缩进 / 引用交互
|
||||
- 曾经暴露过的问题与不稳定边界
|
||||
- 不作为:
|
||||
- 主 benchmark
|
||||
- 外部成熟参考
|
||||
- 第一采用优先级
|
||||
|
||||
这意味着:
|
||||
|
||||
> **`edita-core` 负责回答“命令核怎么组织”,`blocks` 负责回答“块与格式转换怎么组织”,`kode` 负责回答“Leptos 输入层怎么组织”,`leptos-tiptap` 负责回答“如果 Rust-native UI 壳受阻,Leptos 怎么对接成熟 editor runtime”,`mnote-next` 只负责提供内部样例。**
|
||||
|
||||
### 4.1.1 本地参考代码位置
|
||||
|
||||
下面这些参考代码已经拉到本地,后续讨论一律优先引用本地路径。
|
||||
|
||||
- `edita`(当前本地快照:`2045c3a`)
|
||||
- 仓库入口:[reference-code/edita/README.md](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/README.md)
|
||||
- headless core trait:[reference-code/edita/edita-core/src/lib.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita-core/src/lib.rs)
|
||||
- editor 壳:[reference-code/edita/edita/src/editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/editor.rs)
|
||||
- state 组织:[reference-code/edita/edita/src/state.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/state.rs)
|
||||
- 基础 nodes:[reference-code/edita/edita/src/nodes/paragraph.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/nodes/paragraph.rs)、[reference-code/edita/edita/src/nodes/heading.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/nodes/heading.rs)、[reference-code/edita/edita/src/nodes/task_item.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/nodes/task_item.rs)
|
||||
- `blocks`(当前本地快照:`86b5e00`)
|
||||
- 仓库入口:[reference-code/blocks/README.md](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/README.md)
|
||||
- 文档模型:[reference-code/blocks/src/document.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/document.rs)
|
||||
- 块模型:[reference-code/blocks/src/block.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/block.rs)
|
||||
- 转换层:[reference-code/blocks/src/converters.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/converters.rs)
|
||||
- history:[reference-code/blocks/src/history.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/history.rs)
|
||||
- diff / merge:[reference-code/blocks/src/diff.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/diff.rs)
|
||||
- sanitizer:[reference-code/blocks/src/sanitizer.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/sanitizer.rs)
|
||||
- 示例:[reference-code/blocks/examples/json_api.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/examples/json_api.rs)、[reference-code/blocks/examples/file_ops.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/examples/file_ops.rs)
|
||||
- `kode`(当前本地快照:`96ccff4`)
|
||||
- 仓库入口:[reference-code/kode/README.md](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/README.md)
|
||||
- `kode-core` editor/buffer/selection/history:[reference-code/kode/kode-core/src/editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-core/src/editor.rs)、[reference-code/kode/kode-core/src/buffer.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-core/src/buffer.rs)、[reference-code/kode/kode-core/src/selection.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-core/src/selection.rs)、[reference-code/kode/kode-core/src/history.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-core/src/history.rs)
|
||||
- `kode-doc` 结构文档模型:[reference-code/kode/kode-doc/src/lib.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-doc/src/lib.rs)、[reference-code/kode/kode-doc/src/node.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-doc/src/node.rs)、[reference-code/kode/kode-doc/src/transform.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-doc/src/transform.rs)
|
||||
- `kode-leptos` 组件入口:[reference-code/kode/kode-leptos/src/lib.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/lib.rs)
|
||||
- Markdown/WYSIWYG 组件:[reference-code/kode/kode-leptos/src/markdown_editor_component.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/markdown_editor_component.rs)、[reference-code/kode/kode-leptos/src/wysiwyg/tree_editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-leptos/src/wysiwyg/tree_editor.rs)
|
||||
- Markdown 规则:[reference-code/kode/kode-markdown/src/markdown_editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-markdown/src/markdown_editor.rs)、[reference-code/kode/kode-markdown/src/input_rules.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/kode/kode-markdown/src/input_rules.rs)
|
||||
- `leptos-tiptap`(当前本地快照:`8f16e57`)
|
||||
- 仓库入口:[reference-code/leptos-tiptap/README.md](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/README.md)
|
||||
- 组件入口:[reference-code/leptos-tiptap/src/api/component.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/component.rs)
|
||||
- hook 入口:[reference-code/leptos-tiptap/src/api/use_tiptap_editor.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/use_tiptap_editor.rs)
|
||||
- 命令与内容 API:[reference-code/leptos-tiptap/src/api/commands.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/commands.rs)、[reference-code/leptos-tiptap/src/api/content.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/api/content.rs)
|
||||
- runtime bridge:[reference-code/leptos-tiptap/src/runtime/bridge.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/src/runtime/bridge.rs)、[reference-code/leptos-tiptap/tiptap/src/bridge_runtime.ts](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/tiptap/src/bridge_runtime.ts)
|
||||
- demo:[reference-code/leptos-tiptap/examples/demo-csr/src/main.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/examples/demo-csr/src/main.rs)、[reference-code/leptos-tiptap/examples/demo-ssr/src/app.rs](/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/leptos-tiptap/examples/demo-ssr/src/app.rs)
|
||||
- `mnote-next` 只作为内部样本
|
||||
- 人层接口草案:[mnote-next/docs/architecture/p1-human-stable-interface.md](/mnt/Data1T/mnote-next/docs/architecture/p1-human-stable-interface.md)
|
||||
- 编辑器尝试:[mnote-next/apps/web/app/components/editor/BlockEditor.tsx](/mnt/Data1T/mnote-next/apps/web/app/components/editor/BlockEditor.tsx)
|
||||
- 用法约束:只用来回看交互样例与历史问题,不作为“已经验证完成”的外部 benchmark
|
||||
|
||||
### 4.2 复用优先原则
|
||||
|
||||
这条主线不是“从零全写”,而是:
|
||||
|
||||
> **优先复用参考层,只有缺口才自研胶水、集成层和最小必要功能。**
|
||||
|
||||
固定原则如下:
|
||||
|
||||
- 优先复用 `edita-core` 的 headless command / editor 组织思路
|
||||
- 优先复用 `blocks` 的文档模型、导入导出、history、diff/merge 思路
|
||||
- 优先复用 `kode-core / kode-leptos / kode-doc` 的输入、缓冲、Leptos 组件与 tree editor 构件思路
|
||||
- 优先把 `leptos-tiptap` 留作 Leptos 接入成熟 runtime 的 fallback 样本
|
||||
- `mnote-next` 只用于补充内部交互样例和回归案例,不进入“优先采用”序列
|
||||
- 只在下面情况才自研:
|
||||
- 参考库不覆盖你方场景
|
||||
- 参考库抽象不适配 `tree-first graph kernel`
|
||||
- 参考库能力过重或过轻
|
||||
- 缺少稳定 API,必须用胶水层隔离
|
||||
|
||||
### 4.2.1 参考采用矩阵
|
||||
|
||||
| 参考层 | 优先直接采用 | 借鉴后接入 | 当前不作为主线采用 |
|
||||
| --- | --- | --- | --- |
|
||||
| `edita-core` | headless editor 边界、`Editor / Block / Command` 组织方式、命令执行框架 | 导出接口、块注册方式、状态编排方式 | 直接把它当成最终产品 UI 或完整文档系统 |
|
||||
| `blocks` | `Document / Block / BlockType`、JSON/Markdown/HTML/Plain Text 转换、history、diff/merge、sanitizer 思路 | 与 `mnote` kernel 的格式映射、引用 token 扩展、最小 block props 扩展 | 自己先空白重写一套 import/export/history/diff/merge |
|
||||
| `kode` | `kode-core` 的 text buffer / selection / editing primitives、`kode-leptos` 组件组织、`MarkdownEditorComponent`、`TreeWysiwygEditor`、`kode-doc` 分层 | 块内输入器、Leptos 焦点与菜单接缝、块级编排外壳 | 一开始就自建完整文本输入系统或重型 `contenteditable` 壳 |
|
||||
| `leptos-tiptap` | Leptos 组件/hook、runtime bridge、demo、SSR/CSR 接入方式 | 某些 selection / 菜单 / 富文本细节处理思路 | 主线路线、长期语义事实源 |
|
||||
| `mnote-next` | 旧交互样例、历史回归场景、失败边界 | 从旧实现里提取测试样例与交互反例 | 主 benchmark、已外部验证结论、第一采用优先级 |
|
||||
|
||||
执行口径固定为:
|
||||
|
||||
- 先证明参考层能不能直接承担,再决定是否补胶水
|
||||
- 若 `blocks` 已能承担转换或 history,就不再另起一套
|
||||
- 若 `kode` 已能承担块内输入器,就不先写大而全 `textarea/contenteditable` 内核
|
||||
- 若 `edita-core` 已能承担命令编排,就不先发明第二套 editor command 容器
|
||||
- `mnote-next` 只能用来补样例,不得作为“因为我们以前写过,所以现在继续沿用”的理由
|
||||
### 4.3 为什么不再把 `Tiptap` 作为主线 benchmark
|
||||
|
||||
`Tiptap` 仍然是成熟、强大、值得参考的编辑器体系。
|
||||
|
||||
但在新的产品前提下,它不再是主线 benchmark,原因是:
|
||||
|
||||
- 它的能力面明显大于当前真实需求
|
||||
- 它解决了很多你当前并不需要的问题,例如复杂协作和完整富文本生态
|
||||
- 它仍然会把主线注意力拉回浏览器富文本壳,而不是 Rust command 与 AI-first 流程
|
||||
- 如果当前主要操作者是 AI 与 CLI,而不是人类重度可视化编辑,那么 `Tiptap` 的大量优势暂时用不上
|
||||
|
||||
因此这版定稿把它降级为:
|
||||
|
||||
- 体验参考
|
||||
- fallback 方案
|
||||
- 交互 benchmark
|
||||
|
||||
而不是主线目标。
|
||||
|
||||
### 4.4 为什么 `BlockNote` 更不应继续作为主线
|
||||
|
||||
在新的前提下,`BlockNote` 的问题更加明显:
|
||||
|
||||
- 它是更高一层的现成编辑器壳
|
||||
- 对当前“轻量、命令驱动、AI-first”的目标来说过重
|
||||
- 它会继续把设计重心拉回 UI 壳、兼容层和编辑器运行时
|
||||
- 它不利于继续收口 Rust command 和 CLI 统一
|
||||
|
||||
因此:
|
||||
|
||||
> **`BlockNote` 只保留为历史实现与迁移来源,不再作为长期方向。**
|
||||
|
||||
---
|
||||
|
||||
## 5. 主线架构定义
|
||||
|
||||
后续块编辑器主线架构固定为五层。
|
||||
|
||||
## 5.1 Rust kernel 事实源层
|
||||
|
||||
负责:
|
||||
|
||||
- `node`
|
||||
- `edge`
|
||||
- `subtree`
|
||||
- `projection`
|
||||
- `command`
|
||||
|
||||
这一层继续是系统唯一事实源。
|
||||
|
||||
## 5.2 Rust editor core 层
|
||||
|
||||
负责:
|
||||
|
||||
- block model
|
||||
- editor command
|
||||
- content transform
|
||||
- import / export
|
||||
- undo / redo 基础能力
|
||||
|
||||
这一层应尽量 Rust-native,并尽量无头。
|
||||
|
||||
第一阶段模型与命令边界固定为:
|
||||
|
||||
- 块模型参考 `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-model-v0.md`
|
||||
- 命令与 Markdown 边界参考 `/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-command-contract-v0.md`
|
||||
|
||||
这一层的复用优先级固定为:
|
||||
|
||||
- 第一优先:`edita-core`
|
||||
- 第二优先:`blocks`
|
||||
- 第三优先:在这两者之间补你方胶水与集成层
|
||||
|
||||
## 5.3 Leptos UI 壳层
|
||||
|
||||
负责:
|
||||
|
||||
- 块列表展示
|
||||
- 文本输入
|
||||
- 块菜单
|
||||
- slash 菜单
|
||||
- 拖拽或等价移动
|
||||
- 焦点、选中、折叠等 UI 状态
|
||||
|
||||
这一层不再承担事实源角色。
|
||||
|
||||
这一层的复用优先级固定为:
|
||||
|
||||
- 第一优先:`kode-leptos` / `kode-core` / `kode-doc`
|
||||
- 第二优先:`leptos-tiptap` 的 Leptos bridge / runtime 接法
|
||||
- 第三优先:只在块级编排和你方特有语义处自研
|
||||
|
||||
## 5.4 CLI / AI command 层
|
||||
|
||||
负责:
|
||||
|
||||
- 直接调用统一 Rust command
|
||||
- 结构化修改文档
|
||||
- 自动导入、自动整理、自动总结、自动改写
|
||||
|
||||
长期上:
|
||||
|
||||
- CLI 与 AI 不应模拟 UI
|
||||
- CLI 与 AI 应直接操作 editor command / kernel command
|
||||
|
||||
## 5.5 读态与导出层
|
||||
|
||||
负责:
|
||||
|
||||
- Markdown 输出
|
||||
- HTML 输出
|
||||
- 轻量阅读渲染
|
||||
- 结构树与目录派生
|
||||
|
||||
---
|
||||
|
||||
## 6. 这条路线会得到什么
|
||||
|
||||
如果走这条主线,会得到下面这些收益。
|
||||
|
||||
### 6.1 更接近真正的全 Rust 架构
|
||||
|
||||
可以把下面几层都收口到 Rust:
|
||||
|
||||
- kernel
|
||||
- editor core
|
||||
- command
|
||||
- import/export
|
||||
- AI bridge
|
||||
- CLI
|
||||
|
||||
浏览器端只保留必要的 WASM / UI glue。
|
||||
|
||||
### 6.2 更容易实现底层统一
|
||||
|
||||
统一的对象将不再是“前端编辑器文档”,而是:
|
||||
|
||||
- Rust block model
|
||||
- Rust command
|
||||
- Rust kernel projection
|
||||
|
||||
这样:
|
||||
|
||||
- UI 改文档,走 Rust command
|
||||
- CLI 改文档,走 Rust command
|
||||
- AI 改文档,走 Rust command
|
||||
|
||||
这比 `BlockNote` 或 `Tiptap` 更容易收口成单一语义层。
|
||||
|
||||
### 6.3 更符合 AI-first 场景
|
||||
|
||||
AI-first 场景不要求复杂富文本壳,而要求:
|
||||
|
||||
- 可预测
|
||||
- 可序列化
|
||||
- 可 patch
|
||||
- 可审计
|
||||
- 可通过命令复用
|
||||
|
||||
Rust-native minimal editor 更符合这一点。
|
||||
|
||||
### 6.4 更容易保持轻量
|
||||
|
||||
只要控制能力面,就可以故意不做:
|
||||
|
||||
- 协作
|
||||
- 评论
|
||||
- 高级表格
|
||||
- 大量 WYSIWYG 富文本
|
||||
- 大型插件系统
|
||||
|
||||
从而保持更轻、更稳、更快。
|
||||
|
||||
---
|
||||
|
||||
## 7. 这条路线会牺牲什么
|
||||
|
||||
要明确承认,这条路线不是零代价。
|
||||
|
||||
### 7.1 会牺牲成熟富文本生态
|
||||
|
||||
你们会失去 `Tiptap / ProseMirror` 现成提供的大量能力:
|
||||
|
||||
- 完整富文本 mark 生态
|
||||
- 复杂 selection 行为
|
||||
- 丰富 node view 生态
|
||||
- 复杂粘贴与 HTML 解析
|
||||
- 大量成熟 extension
|
||||
|
||||
### 7.2 会牺牲“快速接近 Notion 视觉体验”的速度
|
||||
|
||||
如果走 Rust-native minimal editor,你们能更快得到“可用”,但更慢得到:
|
||||
|
||||
- polished Notion-like 体验
|
||||
- 高级菜单
|
||||
- 丰富交互细节
|
||||
- 页面级完整富文本 polish
|
||||
|
||||
### 7.3 仍然需要自己补的,主要是胶水与 `mnote` 特有语义
|
||||
|
||||
这条路线并不等于“全部自己造”,真正需要你们补的应尽量只剩下面这些:
|
||||
|
||||
- Rust editor core 到 `tree-first graph kernel` 的映射层
|
||||
- `[[page]]` / `((block))` 与页面、块引用语义的接缝
|
||||
- `mnote` 特有 block props、projection、读态派生
|
||||
- 主文档页 feature flag、迁移兼容、回滚链路
|
||||
- AI / CLI 到统一 Rust command 的工具面
|
||||
- `mindmap`、`onlineTable`、媒体等延期块的 placeholder 接缝
|
||||
|
||||
下面这些不应默认进入“先自研再说”的名单:
|
||||
|
||||
- import / export
|
||||
- history / undo redo
|
||||
- diff / merge
|
||||
- 文本缓冲与 selection primitives
|
||||
- Markdown round-trip
|
||||
|
||||
### 7.4 需要接受第一版更“工程化”
|
||||
|
||||
第一版应更像:
|
||||
|
||||
- AI-first 块 Markdown 编辑器
|
||||
- 结构化块命令壳
|
||||
- 轻量 Obsidian + CLI
|
||||
|
||||
而不是:
|
||||
|
||||
- 完整 Notion 替代品
|
||||
|
||||
---
|
||||
|
||||
## 8. 最小功能面
|
||||
|
||||
在新的主线下,MVP 功能面应明确收窄。
|
||||
|
||||
## 8.1 第一优先级必须有
|
||||
|
||||
- 段落
|
||||
- 标题
|
||||
- 有序列表
|
||||
- 无序列表
|
||||
- 待办
|
||||
- 折叠标题 / toggle
|
||||
- 引用块
|
||||
- 代码块
|
||||
- 分割线
|
||||
- 拆块
|
||||
- 合并块
|
||||
- 插入块
|
||||
- 删除块
|
||||
- 上移下移或拖拽重排
|
||||
- 有限缩进
|
||||
- Markdown 快捷输入
|
||||
- `[[page]]`
|
||||
- `((block))` 或等价块引用
|
||||
- Markdown 导入导出
|
||||
|
||||
## 8.2 第二优先级可以后补
|
||||
|
||||
- 媒体块
|
||||
- 进度块
|
||||
- 目录派生
|
||||
- 基础只读渲染优化
|
||||
- 基础批量选择
|
||||
- 多块复制粘贴
|
||||
|
||||
## 8.3 明确不进入第一阶段
|
||||
|
||||
- 协作
|
||||
- 评论
|
||||
- 高级表格
|
||||
- mindmap 深度编辑
|
||||
- OnlyOffice 深度接入
|
||||
- 富文本高级 mark 体系
|
||||
- 完整 AI suggestion UI
|
||||
|
||||
---
|
||||
|
||||
## 9. 替换 `BlockNote` 的方向
|
||||
|
||||
替换方向不再是“平移到 `Tiptap`”,而是下面三步。
|
||||
|
||||
### 9.1 先抽离语义,不先抽离视觉
|
||||
|
||||
先把 `BlockNote` 里仍然有价值的东西抽成:
|
||||
|
||||
- 块类型
|
||||
- 动作合同
|
||||
- token 引用语义
|
||||
- 读态派生规则
|
||||
|
||||
而不是先追求新 UI 和新视觉。
|
||||
|
||||
### 9.2 先对齐 Rust command,再做新编辑壳
|
||||
|
||||
应先收口出统一命令,例如:
|
||||
|
||||
- `replace_block`
|
||||
- `insert_block_after`
|
||||
- `delete_block`
|
||||
- `move_block`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
- `attach_reference_token`
|
||||
- `detach_reference_token`
|
||||
|
||||
只有命令稳定后,UI、CLI、AI 才能共用。
|
||||
|
||||
### 9.3 先做最小读写链,再做复杂块
|
||||
|
||||
优先保证:
|
||||
|
||||
- 打开文档
|
||||
- 编辑基础块
|
||||
- 保存
|
||||
- 导出 Markdown
|
||||
- AI 修改
|
||||
|
||||
之后再补:
|
||||
|
||||
- 媒体
|
||||
- progress
|
||||
- mindmap placeholder
|
||||
- 更复杂的 read view
|
||||
|
||||
---
|
||||
|
||||
## 10. 开发阶段
|
||||
|
||||
## 阶段 0:冻结需求与能力边界
|
||||
|
||||
### 目标
|
||||
|
||||
在新前提下,重新冻结能力边界。
|
||||
|
||||
### 输出
|
||||
|
||||
- 一份“必须做 / 可延后 / 不做”的能力清单
|
||||
- 一份内部交互样例摘录(来自 `mnote-next`,仅供对照)
|
||||
- 一份 `BlockNote` 仍需迁移的块类型清单
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 主线需求被限制在轻量块编辑器范围
|
||||
- 协作和完整富文本不再进入第一阶段
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已完成 `BlockNote` 自定义块盘点与三档分级,基线以 [`rust-block-editor-phase0-baseline-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-phase0-baseline-v1.md) 为准:阶段 1 固定 `heading`、基础段落/列表、等价待办、`pageReference`、`blockReference`;`media`、`progressMeter` 后补;`mindmap`、`onlineTable` 延期。
|
||||
- [x] 已完成 `BlockNote` 读写重耦合盘点,确认当前主风险仍集中在 `HocuspocusProvider + Yjs`、`buildDocumentSavePayload`、`DocumentToc`、`comments/search palette/editor bridge`,并在迁移文档中保留为观察点。
|
||||
- [x] 已完成 `mnote-next` 内部交互样例提取,见 [`rust-block-editor-interaction-samples-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-interaction-samples-v1.md);这些样例只作为回归样本,不作为“已验证结论”或主 benchmark。
|
||||
- [x] 已在本文中冻结“不进入第一阶段”的能力:协作、评论、高级表格、`mindmap` 深度编辑、`OnlyOffice` 深度接入、完整富文本 mark 体系。
|
||||
- [x] 已明确第一阶段唯一主目标是 AI-first 的轻量块 Markdown 编辑器,不再追求完整 Notion 视觉和能力逼近。
|
||||
- [x] 已冻结四个参考层的采用矩阵,并单独声明 `mnote-next` 只作为内部样本;当前执行口径是“先复用参考层,只有缺口才自研胶水”,详见 [`rust-block-editor-adoption-matrix-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-adoption-matrix-v0.md)。
|
||||
|
||||
## 阶段 1:定义 Rust block model 与 command
|
||||
|
||||
### 目标
|
||||
|
||||
先把底层语言定下来。
|
||||
|
||||
### 输出
|
||||
|
||||
- Rust block model
|
||||
- block props 结构
|
||||
- editor command 列表
|
||||
- Markdown import/export 边界
|
||||
- kernel content node 载荷边界
|
||||
|
||||
### 完成判定
|
||||
|
||||
- UI、CLI、AI 都能围绕这套命令讨论
|
||||
- 结构不再依赖 `BlockNote` JSON
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已完成 `edita-core` 适配性评估与最小 spike,明确 `Editor / Block / Command / 无头执行边界` 可直接映射为 `mnote` editor core,详见 [`rust-block-editor-edita-spike-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-edita-spike-v0.md)。
|
||||
- [x] 已完成 `blocks` 适配性评估与最小 spike,明确 Markdown round-trip、history、diff/merge 的可复用面与缺口,详见 [`rust-block-editor-blocks-spike-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-blocks-spike-v0.md)。
|
||||
- [x] 已冻结首批 Rust block type、`BlockProps`、editor command、Markdown import/export 边界与 `content_node` 载荷格式,见 [`rust-block-editor-model-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-model-v0.md) 与 [`rust-block-editor-command-contract-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-command-contract-v0.md)。
|
||||
- [x] 已明确 editor command 与 kernel command 的关系,以及 `pageReference` / `blockReference` 第一版先走 token 方案;`blocks`、`kode-doc`、`leptos-tiptap` 的定位已写入阶段 1 采用矩阵 v0。
|
||||
- [x] 当前阶段 1 的对外落点是:结构不再以 `BlockNote` JSON 为长期格式,UI / CLI / AI 均围绕同一套 Rust command 讨论。
|
||||
|
||||
## 阶段 2:实现最小 Rust editor core
|
||||
|
||||
### 目标
|
||||
|
||||
基于 `edita-core` 思路或等价自研实现最小无头编辑器核心。
|
||||
|
||||
### 范围
|
||||
|
||||
- 基础块
|
||||
- 拆块 / 合并
|
||||
- 插入 / 删除
|
||||
- 缩进 / 反缩进
|
||||
- 标题折叠
|
||||
- 简单 undo / redo
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 已经可以在纯命令层编辑文档
|
||||
- CLI 可直接调用 editor core
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已选择“`edita-core` 思路 + `blocks` 转换能力 + `mnote` 胶水”的实现路径,并在 [`rust/crates/mnote-editor-core/`](/mnt/Data1T/mnote/rust/crates/mnote-editor-core/) 落地最小 Rust editor core。
|
||||
- [x] 已实现 block document 内存模型、最小 command executor、undo/redo、Markdown import/export、只读结构树派生,并把 `replace/insert/delete/split/merge/move/indent/outdent/toggle collapse` 覆盖到纯 Rust 测试。
|
||||
- [x] 已把 `mnote-next` 抽取的人层交互样例沉淀为回归样本,见 [`rust-block-editor-interaction-samples-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-interaction-samples-v1.md);CLI 可直接调用 `markdown-roundtrip`、`session-demo`、`ai-pipeline`。
|
||||
- [x] 当前阶段 2 的客观验证已跑通:`cargo test -p mnote-editor-core`、`cargo test -p mnote-cli`。
|
||||
|
||||
## 阶段 3:接 Leptos 最小 UI 壳
|
||||
|
||||
### 目标
|
||||
|
||||
在 Leptos 中做一个够用的块编辑壳。
|
||||
|
||||
### 范围
|
||||
|
||||
- 文本输入
|
||||
- slash 菜单
|
||||
- 块菜单
|
||||
- 上移下移或拖拽
|
||||
- token 引用
|
||||
- 基础只读渲染
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 人可以完成日常 Markdown + 折叠 + 列表式编辑
|
||||
- 不再需要 `BlockNote` 才能完成基本写作
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 当前阶段 3 已先以 [`mnote-web` 文档壳](/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs) 达成最小 Rust-native UI 壳目标:block list、focus、selection、hover、slash 菜单、标题折叠、有限缩进、`[[page]]` / `((block))` token 交互都已可见并有测试覆盖。
|
||||
- [x] `kode` / `kode-leptos` / `kode-doc` 的采用结论已冻结为“块内输入器与选择处理参考层”;当前基线先用 Rust shell + 最小 DOM 交互验证语义,不在第一版追求复杂 `contenteditable` 或整壳 Leptos 化。
|
||||
- [x] 主文档页已经完成双路径切换:默认由 `mnoteWebDocumentShellEnabled` + `mnoteWebDocumentShellUrl` 驱动接入新壳,`?editor=compat` 保留旧 `BlockNote` 兼容入口。
|
||||
- [x] 新 UI 壳首屏不依赖 `HocuspocusProvider`、`Yjs`、`comments`、旧 `BlockNoteView`;默认主路径只先加载文档壳 iframe,旧编辑器仅在 compat 路径进入。
|
||||
- [x] 最小浏览器回归链已经补齐并跑通,见 [`task103-document-shell-cutover-smoke.js`](/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js)。
|
||||
|
||||
### 阶段 3 纠偏说明(2026-04-18)
|
||||
|
||||
上面这些勾选只代表:
|
||||
|
||||
- `mnote-web` 的 document shell prototype 已经打通
|
||||
- 默认文档页已经能够切到这个 prototype surface
|
||||
- block list / focus / selection / slash / indent / 引用 token 这些外层交互语义已经有最小验证
|
||||
|
||||
它们**不等于**“真人可编辑 runtime 已完成”。
|
||||
|
||||
当前默认主路径仍缺下面这层真正决定“像不像 Tiptap / Wolai 块编辑器”的能力:
|
||||
|
||||
- 块内文本输入 runtime
|
||||
- 光标与选区更新
|
||||
- IME / `beforeinput` / composition 处理
|
||||
- 回车拆块、退格合并、Tab 缩进这类真实编辑事务
|
||||
- 与 Rust editor core 对接的保存链,而不是只在壳内维护局部 UI state
|
||||
|
||||
因此,后续任务必须把“document shell prototype”与“human editing runtime”明确拆开:
|
||||
|
||||
- 现有 `task-059..062` 应理解为 prototype 子阶段
|
||||
- 新增的纠偏批次应以“真人可编辑 runtime 接入”作为真正的 Phase 3 主目标
|
||||
- 在真人可编辑 runtime 没完成之前,不应把当前壳视作已经达到 Wolai/Tiptap 类块编辑器基线
|
||||
|
||||
### 阶段 3R-1:真人编辑 runtime 契约冻结(task-072)
|
||||
|
||||
真人编辑 runtime 与 prototype shell 的边界固定如下:
|
||||
|
||||
- prototype shell 只证明:
|
||||
- 默认主文档页可切到 `mnote-web` surface
|
||||
- block list / focus / hover / slash 按钮等外层 UI 可见
|
||||
- 兼容入口 `?editor=compat` 仍可保留
|
||||
- human editing runtime 必须额外证明:
|
||||
- 每个可编辑块都存在真实输入宿主,而不是只渲染标题/按钮
|
||||
- 文本输入走 `beforeinput` / `input` / composition / selection 事件面
|
||||
- Enter 拆块、Backspace 合并、Tab / Shift+Tab 缩进、块类型切换属于真实编辑事务
|
||||
- 编辑结果会进入保存链,并在刷新后回放
|
||||
- 阶段 3 后续验收一律按“真人 runtime 闭环”判定,而不是按“壳存在”判定
|
||||
|
||||
### 阶段 3R-2:真人编辑 runtime 实现路径冻结(task-073)
|
||||
|
||||
这一批实现路径固定为三层:
|
||||
|
||||
1. `tree-first graph kernel + Rust command contract`
|
||||
- 继续作为事实源与长期语义 owner
|
||||
2. `mnote-web human editing runtime`
|
||||
- 当前批次先在 `mnote-web` 文档壳内落最小真人输入 runtime
|
||||
- 真实输入宿主采用“块级输入器 + selection / beforeinput / composition / autosave”闭环
|
||||
- 结构事务优先通过 `mnote-editor-core` 命令执行,而不是继续停留在纯前端按钮改状态
|
||||
3. `save / replay / compat bridge`
|
||||
- 写回继续走 `documents.save`
|
||||
- 刷新回放与 `?editor=compat` 对照保留为最后验收面
|
||||
|
||||
参考层采用口径固定如下:
|
||||
|
||||
- `kode`
|
||||
- 仍是第一优先参考层
|
||||
- 当前主要借用其 buffer / selection / input runtime 分层思路,而不是等待整壳 Leptos 化完成后再开始可写闭环
|
||||
- `leptos-tiptap`
|
||||
- 保留为 fallback 参考
|
||||
- 只在 `mnote-web` 现批次无法尽快形成可写闭环时再启用
|
||||
- `compat`
|
||||
- 只作为回退与对照路径,不再作为默认主编辑器事实层
|
||||
|
||||
也就是说,当前批次的目标不是“继续美化 prototype shell”,而是先把:
|
||||
|
||||
- 文本输入
|
||||
- 选区/光标
|
||||
- IME / composition
|
||||
- 结构事务
|
||||
- 保存/刷新回放
|
||||
|
||||
这五条真人编辑 runtime 主链补齐,再决定下一步是否进一步把输入器向 `kode` 的更完整形态收敛。
|
||||
|
||||
## 阶段 4:AI-first 编辑链打通
|
||||
|
||||
### 目标
|
||||
|
||||
让 AI 成为第一编辑主体。
|
||||
|
||||
### 范围
|
||||
|
||||
- AI 调用 Rust command
|
||||
- AI 导入文本并结构化成块
|
||||
- AI 重写、总结、扩写、整理块结构
|
||||
- CLI 与 AI 共用同一动作合同
|
||||
|
||||
### 完成判定
|
||||
|
||||
- AI 已能稳定创建、改写、整理文档
|
||||
- 人类编辑退居辅助地位
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已冻结 AI/CLI 共用的 Rust editor command tool contract,并明确 AI/CLI 不再模拟 DOM/UI,见 [`rust-block-editor-ai-tool-contract-v0.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-ai-tool-contract-v0.md)。
|
||||
- [x] 已打通“纯文本导入 -> AI 结构化成块 -> 按目标重写文档 -> 返回审计与变更报告”的最小链路,代码位于 [`mnote-editor-core/src/pipeline.rs`](/mnt/Data1T/mnote/rust/crates/mnote-editor-core/src/pipeline.rs) 与 [`mnote-cli/src/lib.rs`](/mnt/Data1T/mnote/rust/crates/mnote-cli/src/lib.rs)。
|
||||
- [x] CLI 与 AI 已共用同一条命令合同与输出面,当前验证已覆盖 `MeetingNotesToTodos`、`LongParagraphToTitle`、`PageReorder` 三种场景。
|
||||
- [x] 已补 AI 回归样例与显式 `ai_regression_*` 测试;协作、评论、suggestion review UI 仍保持不进入当前 AI-first 基线。
|
||||
|
||||
## 阶段 5:清理旧 `BlockNote` 壳
|
||||
|
||||
### 目标
|
||||
|
||||
将 `BlockNote` 从主文档页和主数据链中移出。
|
||||
|
||||
### 输出
|
||||
|
||||
- 旧 `BlockNote` 退场清单
|
||||
- 数据迁移或兼容策略
|
||||
- 测试与回滚方案
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 主文档页默认不再挂 `BlockNote`
|
||||
- 新 Rust-native 编辑器成为唯一主路径
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] 已完成 `BlockNote` 入口、依赖与内容格式迁移策略盘点,见 [`rust-block-editor-blocknote-migration-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-blocknote-migration-v1.md)。
|
||||
- [x] 主文档页默认路径已不再挂旧 `BlockNote`,而是切到 `mnote-web` 新文档壳;兼容入口仍保留 `?editor=compat` / debug 用途。
|
||||
- [x] 文档页测试基线已切到“默认新壳 + compat 回退”双路径,当前基线验证包括 `task103-document-shell-cutover-smoke.js`、`pnpm test`、`cargo test -p mnote-web`。
|
||||
- 后续收尾:`task-068` 继续清理 `@blocknote/*`、Yjs、Mantine、旧 CSS 覆盖,以及 `mindmap` / `onlineTable` / 媒体块里仅供旧编辑器使用的残留耦合。
|
||||
- 后续收尾:在 compat/debug 真正退场后,再删除 `blocknote-editor.tsx` 及其最终调用点,并完成包依赖瘦身。
|
||||
|
||||
---
|
||||
|
||||
## 11. 参考层的最终定位
|
||||
|
||||
### `edita-core`
|
||||
|
||||
定位:
|
||||
|
||||
- 主线参考
|
||||
- Rust editor core 思路参考
|
||||
- 命令与块模型参考
|
||||
|
||||
不是:
|
||||
|
||||
- 可直接完整拿来用的产品方案
|
||||
|
||||
### `blocks`
|
||||
|
||||
定位:
|
||||
|
||||
- 主线参考
|
||||
- Rust block document model 参考
|
||||
- Markdown / HTML / JSON / Plain Text 转换参考
|
||||
- history、diff / merge、sanitizer 参考
|
||||
|
||||
不是:
|
||||
|
||||
- UI 壳
|
||||
- `mnote` kernel 的直接事实源
|
||||
|
||||
### `kode`
|
||||
|
||||
定位:
|
||||
|
||||
- 主线参考
|
||||
- Leptos 编辑输入层参考
|
||||
- 块内编辑器、buffer、selection、WYSIWYG/tree editor 构件参考
|
||||
|
||||
不是:
|
||||
|
||||
- `mnote` 整个块编排系统的现成成品
|
||||
- `tree-first graph kernel` 的替代品
|
||||
|
||||
### `mnote-next`
|
||||
|
||||
定位:
|
||||
|
||||
- 内部原型与交互样例来源
|
||||
- 历史回归样例与反例来源
|
||||
|
||||
不是:
|
||||
|
||||
- 主 benchmark
|
||||
- 外部成熟参考
|
||||
- “已经验证完成”的结论来源
|
||||
|
||||
### `Tiptap`
|
||||
|
||||
定位:
|
||||
|
||||
- fallback
|
||||
- 体验 benchmark
|
||||
- 交互参考
|
||||
|
||||
不是:
|
||||
|
||||
- 当前主线 benchmark
|
||||
|
||||
### `leptos-tiptap`
|
||||
|
||||
定位:
|
||||
|
||||
- 若 Rust-native 路线推进受阻时的现实 fallback 接入层
|
||||
- 对照用接缝方案
|
||||
|
||||
不是:
|
||||
|
||||
- 当前第一优先开发基线
|
||||
|
||||
### `BlockNote`
|
||||
|
||||
定位:
|
||||
|
||||
- 历史实现来源
|
||||
- 迁移对象
|
||||
- 当前自定义块与读写耦合清点来源
|
||||
|
||||
不是:
|
||||
|
||||
- 长期主线
|
||||
- 新编辑器 benchmark
|
||||
|
||||
---
|
||||
|
||||
## 12. 最终收口
|
||||
|
||||
本文之后,`mnote` 在块编辑器路线上的统一口径固定为:
|
||||
|
||||
> **后续编辑器开发以 AI-first、CLI-first、Rust-native 的轻量块编辑器为主线;以 `edita-core + blocks + kode` 作为主基准组合;以 `leptos-tiptap` 作为 Leptos fallback 接入参考;`mnote-next` 只保留为内部交互样本,不作为主 benchmark。**
|
||||
|
||||
对应推进原则固定为:
|
||||
|
||||
- 不再以 `BlockNote` 兼容性为最高约束
|
||||
- 不再默认把完整 Notion 富文本体验当作必须目标
|
||||
- 协作默认不进第一阶段
|
||||
- 编辑器首先服务 AI 命令、CLI 和 Rust command 统一
|
||||
- 优先复用 `edita-core`、`blocks`、`kode`、`leptos-tiptap` 的参考层,只在缺口处自研胶水与主线集成
|
||||
- `mnote-next` 只作为内部样本与回归案例来源,不再作为主要参考
|
||||
- 以轻量、结构化、可脚本化、可维护为最高优先级
|
||||
|
||||
---
|
||||
|
||||
## 13. 最终采用矩阵(2026-04-18 基线)
|
||||
|
||||
| 参考层 | 当前采用结论 | 当前已落地的主线位置 | 保留为后续/兼容的部分 |
|
||||
| --- | --- | --- | --- |
|
||||
| `edita-core` | 直接采用其无头 command / editor 组织思路 | [`mnote-editor-core`](/mnt/Data1T/mnote/rust/crates/mnote-editor-core/) 的 block model、command executor、history 组织 | 暂不直接复用其完整 UI 或最终导出壳 |
|
||||
| `blocks` | 部分采用 | Markdown import/export、history / diff / merge 思路,以及阶段 1/2 的 spike 与采用矩阵结论 | 若后续需要更深的 HTML/JSON 转换,再继续补胶水 |
|
||||
| `kode` | 部分采用 | 作为块内输入器、selection、输入规则、tree editor 分层的参考层;当前先冻结为下一阶段可继续下沉的输入策略 | 还未把整体文档壳彻底替换为 `kode-leptos` 实现 |
|
||||
| `leptos-tiptap` | 仅保留 fallback 参考 | 作为 Leptos bridge / runtime 接法与 SSR/CSR 接入样例,不进入当前主路径 | 若 Rust-native 输入层受阻,可作为临时 fallback 样板 |
|
||||
| `mnote-next` | 仅保留内部样本 | 已抽取为 [`rust-block-editor-interaction-samples-v1.md`](/mnt/Data1T/mnote/design/old/05-editor-mainline/done/rust-block-editor-interaction-samples-v1.md) 的回归语义样例 | 不再作为 benchmark 或“以前写过所以继续沿用”的理由 |
|
||||
| `BlockNote` | 降级为 compat/debug 与迁移来源 | 默认主文档页当前已切到 document shell prototype;`?editor=compat` 仍保留为真实可写回退链与迁移观察点 | 等真人可编辑 runtime 完成后,再执行依赖清理与最终删除主线路径 |
|
||||
@@ -0,0 +1,260 @@
|
||||
# [recycle] Rust 全栈块编辑器替代方案选型
|
||||
|
||||
你现在的栈是**纯 Rust 全栈:Axum(后端)+ Leptos(前端 WASM/SSR)**,目标是**彻底换掉 BlockNote、做轻量化块编辑器、全栈 Rust 化**,这个组合非常成熟,我直接给你**只适配该技术栈**的最优替换方案、分层选型和最终落地架构,不掺任何无关技术。
|
||||
|
||||
# [recycle] 一、核心结论(直接照抄用)
|
||||
**唯一最优方案**
|
||||
`Axum + Leptos + leptos-yrs + blocks crate + 自研轻量块组件`
|
||||
- 完全抛弃 BlockNote 等重型 JS 编辑器
|
||||
- 100% 全 Rust,前端 WASM 渲染、后端 Axum 提供接口+协作同步
|
||||
- 完美实现 Notion/飞书/思源的**块结构、嵌套、富文本、协作、本地优先**
|
||||
|
||||
# 二、前端块编辑层(Leptos 侧,替代 BlockNote)
|
||||
只选**Leptos 原生/可无缝集成的 Rust 方案**,拒绝任何重型 JS 绑定
|
||||
|
||||
## 1. 首选:自研 Leptos 轻量块编辑器(最推荐,可控+极轻)
|
||||
直接基于 Leptos 自己搭块编辑器,比 BlockNote 轻量 10 倍以上,完全贴合你的需求
|
||||
- 用 Leptos `Signal / RwSignal` 管理块列表、选中态、拖拽、折叠
|
||||
- 块类型(标题/段落/代码/引用/嵌套子块)自己定义,想加数据库/看板随时扩展
|
||||
- 纯 Rust 渲染,无 JS 运行时,和 Leptos 生命周期完全对齐
|
||||
- 支持快捷键、撤销/重做、拖拽排序,按需实现,不堆无用功能
|
||||
|
||||
## 2. 次选:leptos-editor(社区轻量富文本,快速改块编辑器)
|
||||
Leptos 生态原生轻量编辑器,纯 Rust/WASM 实现
|
||||
- 开箱即用的富文本(粗体/斜体/链接)
|
||||
- 外层包一层块容器,快速改成 Notion 风格块布局
|
||||
- 开发成本最低,适合快速出原型
|
||||
|
||||
## 3. 兜底:editable / text-editor crates
|
||||
Rust 原生底层编辑内核,无任何前端依赖,可深度封装进 Leptos 组件
|
||||
- 适合追求极致性能、底层可控的场景
|
||||
|
||||
---
|
||||
|
||||
# 三、块数据 & 协作核心(Rust 通用层)
|
||||
这部分是 Notion 类编辑器的灵魂,直接用成熟 Rust 库,不重复造轮子
|
||||
1. **blocks crate**
|
||||
块结构标准库:块类型、嵌套父子结构、JSON/Markdown 序列化、diff/merge
|
||||
完美对接 Leptos 状态管理
|
||||
2. **yrs(必用)**
|
||||
Rust 官方实现的 Yjs CRDT,**飞书/Notion/Wolai 协作底层**
|
||||
支持多人实时编辑块、无冲突合并
|
||||
3. **leptos-yrs**
|
||||
Leptos 与 yrs 官方绑定,用 Signal 直接同步 CRDT 文档
|
||||
前端改块 → 自动同步到后端 → 其他客户端实时更新
|
||||
|
||||
---
|
||||
|
||||
# 四、后端 Axum 配套方案
|
||||
Axum 只做两件事:**块数据持久化 + 协作同步**
|
||||
1. **WebSocket 实时同步(Axum + tokio-tungstenite)**
|
||||
前端 yrs 产生编辑更新 → Axum 转发 → 所有在线客户端同步
|
||||
二进制协议,比 JSON 快很多,完全替代 JS 协作层
|
||||
2. **REST 接口**
|
||||
文档/块的增删改查、权限、目录管理
|
||||
3. **存储层**
|
||||
- 服务端持久化:`PostgreSQL + sqlx`(存块结构+yrs文档快照)
|
||||
- 本地优先缓存:`redb`(Rust 嵌入式 KV,对标思源本地存储)
|
||||
- 全文搜索:`tantivy`(Rust 全文搜索引擎,实现 Notion 全局搜索)
|
||||
|
||||
---
|
||||
|
||||
# 五、两套最终落地配置(直接用)
|
||||
## 方案 A:极简单用户版(快速上线,无协作)
|
||||
**Leptos 自研块 + blocks crate + Axum REST + redb**
|
||||
- 无重型依赖
|
||||
- 纯 Rust 块编辑
|
||||
- 本地优先 + 云端备份
|
||||
- 对标:思源笔记单用户体验
|
||||
|
||||
## 方案 B:完整版(多用户协作,对标 Notion/飞书)⭐⭐⭐⭐⭐
|
||||
**Leptos + leptos-yrs + blocks + Axum(WebSocket+REST) + Postgres + tantivy**
|
||||
- 多人实时块编辑
|
||||
- 块嵌套/拖拽/富文本全覆盖
|
||||
- 全栈 Rust,无任何 JS 编辑器冗余
|
||||
- 可扩展表格/看板/数据库块
|
||||
|
||||
---
|
||||
|
||||
# 六、关键实现小示例(Leptos 块结构伪代码)
|
||||
```rust
|
||||
// 块数据模型(用 blocks crate 或自定义)
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum BlockType {
|
||||
H1, H2, Paragraph, Code, Quote, Toggle
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn BlockEditor(doc_id: String) -> impl IntoView {
|
||||
// 块列表状态
|
||||
let (blocks, set_blocks) = create_signal(vec![]);
|
||||
// 绑定 yrs 协作文档
|
||||
let yrs_doc = use_yrs_doc(doc_id);
|
||||
|
||||
view! {
|
||||
<div class="notion-block-container">
|
||||
<For
|
||||
each=blocks
|
||||
key=|b| b.id
|
||||
children=move |block| {
|
||||
view! { <BlockComponent block=block set_blocks/> }
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 七、避坑(非常重要)
|
||||
1. **坚决不用 leptos-tiptap / tiptap-rs**
|
||||
本质还是套 JS 重型编辑器,违背你全 Rust、轻量化的初衷
|
||||
2. **不要自研 CRDT**
|
||||
直接用 yrs,成本差 100 倍,稳定性天壤之别
|
||||
3. **块渲染用 Leptos 组件拆分**
|
||||
避免单组件巨量渲染,性能对标原生飞书
|
||||
4. **Axum 只做同步与存储**
|
||||
块编辑逻辑全部放在前端 WASM,后端无状态,横向扩展极简单
|
||||
|
||||
如果你需要,我可以直接给你:
|
||||
- 可运行的 `Axum + Leptos + yrs` 最小工程模板
|
||||
- 块编辑器的完整 Leptos 组件代码(含拖拽、回车拆块、删除块)
|
||||
- 块数据结构与 Axum 接口定义
|
||||
|
||||
# 全 Rust 替代 BlockNote 方案 & 针对性推荐
|
||||
你现在的核心诉求非常明确:**自研类 Notion/飞书/思源笔记**,嫌弃 BlockNote 前端太重、JS 依赖重,想要**全 Rust 栈、轻量化、块编辑核心可控**。
|
||||
|
||||
我直接按「**落地优先级**」给你方案,所有方案均彻底摆脱 BlockNote 这类重型 JS 编辑器,核心逻辑/UI 尽可能全 Rust,适配 Tauri 桌面端(你做笔记软件几乎必用)。
|
||||
|
||||
---
|
||||
|
||||
## 核心结论先给
|
||||
**最优解:Tauri + egui(Rust 原生 UI)+ yrs + blocks crate**
|
||||
100% 全 Rust,零重型 JS 依赖,块编辑性能、体积、定制性拉满,完美对标 Notion/飞书块结构。
|
||||
|
||||
---
|
||||
|
||||
# 一、可直接替换 BlockNote 的 Rust 块编辑方案(按推荐度排序)
|
||||
## 1. 首选:egui 生态 + 自研块系统(桌面端 Tauri 天花板)
|
||||
**完全抛弃 JS 编辑器,纯 Rust 原生 UI 做块渲染,最轻量、最可控**
|
||||
- 核心 UI:**egui**(Rust 即时模式 UI,跨桌面/Web,无任何 JS runtime)
|
||||
- 块编辑组件:
|
||||
- `egui-notes` / `egui-notion`:现成类 Notion 块编辑,支持嵌套块、标题/列表/代码块、拖拽、撤销重做
|
||||
- `egui-rich-text` + 自定义块封装:灵活度拉满,可自定义数据库块、嵌入块、看板等思源/飞书特色块
|
||||
- 适配:Tauri 桌面、纯原生、Wasm Web 三端通用
|
||||
- 优势:**零 JS 臃肿、编译体积极小、性能碾压 BlockNote、可深度魔改**
|
||||
- 劣势:需自己封装少量交互(快捷键、块拖拽),但工作量远小于适配 BlockNote
|
||||
|
||||
## 2. 次选:Rust Wasm 前端框架 + 极简块编辑(Web/跨端)
|
||||
适合你需要 Web + 桌面双端,全 Rust Wasm 编译,无重型前端框架
|
||||
- Leptos + `leptos-editor`(Rust 顶流 Web 框架,Wasm 原生)
|
||||
- Yew + `yew-editor`(类 React Rust 前端,块编辑组件)
|
||||
- Dioxus + `dioxus-rich-text`(语法友好,块扩展简单)
|
||||
- 优势:全 Rust 类型安全,Web/桌面通吃,比 BlockNote 轻 90%
|
||||
- 劣势:Wasm 体积略大于纯 egui 原生
|
||||
|
||||
## 3. 兜底:现成全 Rust 笔记项目(直接抄架构,少造轮子)
|
||||
直接剥离块编辑核心,用到你的项目里,最快落地
|
||||
- **lume-note**:Rust + Tauri + egui,轻量块笔记,Tauri 适配完美
|
||||
- **neon-notes**:纯 egui 块笔记,代码极简,二次开发成本极低
|
||||
- **AppFlowy Core**:剥离其 Rust 块引擎/协作/存储,前端用 egui 重写(抛弃 Flutter)
|
||||
|
||||
---
|
||||
|
||||
# 二、块编辑底层核心库(全 Rust,必用,替代 BlockNote 底层逻辑)
|
||||
不管选哪种 UI,**块数据、协作、存储** 全用 Rust 库,彻底摆脱 JS 状态管理:
|
||||
1. **yrs**(⭐最高,Yjs Rust 实现)
|
||||
块级 CRDT 协作,Notion/飞书多人协作核心,Rust 原生,性能远超 JS 版
|
||||
2. **blocks crate**
|
||||
Rust 原生块模型引擎:块嵌套、序列化(Markdown/JSON)、diff/merge、撤销重做
|
||||
3. **redb/sled**
|
||||
Rust 嵌入式数据库,本地存储块数据,本地优先架构
|
||||
4. **tantivy**
|
||||
Rust 全文搜索,实现思源/Notion 级全局搜索
|
||||
|
||||
---
|
||||
|
||||
# 三、最终落地推荐组合(2 套直接用)
|
||||
## 组合 1:【极致全 Rust 桌面端】⭐⭐⭐⭐⭐(最适合你)
|
||||
**Tauri + egui + yrs + blocks crate**
|
||||
- 前端渲染:egui(纯 Rust,零 JS,块编辑 UI)
|
||||
- 块核心:blocks crate(块结构、嵌套、序列化)
|
||||
- 协作同步:yrs(Rust CRDT,块级协作)
|
||||
- 本地存储:redb
|
||||
- 打包:Tauri
|
||||
- 适配场景:自研桌面笔记,对标 Notion/飞书/思源,全 Rust 无冗余
|
||||
|
||||
## 组合 2:【Web+桌面双端】⭐⭐⭐⭐
|
||||
**Leptos(Wasm) + yrs + Tauri**
|
||||
- 前端:Leptos(Rust 编译 Wasm)
|
||||
- 块编辑:自定义 Leptos 块组件
|
||||
- 核心:yrs + blocks
|
||||
- 适配场景:需要网页版 + 桌面版的笔记工具
|
||||
|
||||
## 组合 3:【快速上线】⭐⭐⭐
|
||||
**魔改 lume-note 架构**
|
||||
直接用其 Tauri + egui 块编辑底座,替换核心逻辑为你的需求,零从头搭建成本
|
||||
|
||||
---
|
||||
|
||||
# 四、避坑关键(针对你的重构场景)
|
||||
1. **彻底放弃 BlockNote/Tiptap 相关 JS 绑定**
|
||||
本质还是 JS 重型依赖,违背你全 Rust 化的初衷
|
||||
2. **优先用 yrs 做块协作**
|
||||
这是 Notion 类编辑器的核心,Rust 实现比 JS 更稳定高效
|
||||
3. **egui 是桌面端最优解**
|
||||
比 Wasm 前端更轻,无浏览器兼容问题,Tauri 集成无缝
|
||||
4. **块模型直接用 blocks crate**
|
||||
省去自己写块嵌套、序列化的大量重复代码
|
||||
|
||||
|
||||
以下是 **Axum + Leptos + yrs + blocks** 方案的全部核心链接(官方仓库、文档、crates.io),按模块整理,方便你直接集成:
|
||||
|
||||
---
|
||||
|
||||
## 一、Leptos 前端框架(核心)
|
||||
- **Leptos 官方仓库**:https://github.com/leptos-rs/leptos
|
||||
- **Leptos 官网**:https://leptos.dev
|
||||
- **Leptos 文档**:https://book.leptos.dev
|
||||
- **crates.io**:https://crates.io/crates/leptos
|
||||
|
||||
---
|
||||
|
||||
## 二、块编辑核心库
|
||||
### 1. yrs(CRDT 协作,必用)
|
||||
- **yrs 官方仓库(y-crdt)**:https://github.com/y-crdt/y-crdt
|
||||
- **crates.io**:https://crates.io/crates/yrs
|
||||
- **文档**:https://docs.rs/yrs/latest/yrs/
|
||||
|
||||
### 2. blocks crate(块模型)
|
||||
- **blocks 仓库**:https://github.com/brenogonzaga/blocks
|
||||
- **crates.io**:https://crates.io/crates/blocks
|
||||
|
||||
### 3. Leptos 编辑器/富文本(可选)
|
||||
- **leptos-editor**(轻量富文本):https://github.com/leptos-rs/leptos-editor
|
||||
- **awesome-leptos**(生态列表):https://github.com/leptos-rs/awesome-leptos
|
||||
|
||||
---
|
||||
|
||||
## 三、Axum 后端
|
||||
- **Axum 官方仓库**:https://github.com/tokio-rs/axum
|
||||
- **Axum 文档**:https://docs.rs/axum/latest/axum/
|
||||
|
||||
---
|
||||
|
||||
## 四、配套工具(存储/搜索/同步)
|
||||
- **redb(嵌入式存储)**:https://github.com/redb/redb
|
||||
- **tantivy(全文搜索)**:https://github.com/quickwit-oss/tantivy
|
||||
- **tokio-tungstenite(WebSocket)**:https://github.com/snapview/tokio-tungstenite
|
||||
|
||||
---
|
||||
|
||||
## 五、示例与模板(直接抄)
|
||||
- **leptos-yrs 示例**:https://github.com/leptos-rs/leptos/tree/main/examples/yrs
|
||||
- **leptos + axum 全栈模板**:https://github.com/leptos-rs/leptos-axum-starter
|
||||
- **yrs + WebSocket 同步示例**:https://github.com/y-crdt/y-crdt/tree/main/examples/websocket
|
||||
|
||||
---
|
||||
|
||||
需要我基于这些库,给你生成一个可直接运行的 **Axum + Leptos + yrs** 最小块编辑器模板吗?
|
||||
+568
@@ -0,0 +1,568 @@
|
||||
# [recycle] mnote 从 Runtime 壳回到 Wolai 目标页的纠偏清单 v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 最终交付对标基准:
|
||||
> - 本地目标截图:`/mnt/Data1T/mnote/tmp/image copy 14.png`
|
||||
> - 目标页面:`https://www.wolai.com/liaibo/ikFSSM1a4GgvmHBYFCNfVd`
|
||||
> - 当前偏移截图:`/mnt/Data1T/mnote/tmp/image copy 13.png`
|
||||
>
|
||||
> 说明:
|
||||
> - `dokobot` 当前只能稳定读到 Wolai 的 SPA 外壳标题,拿不到正文结构。
|
||||
> - 因此本文以本地目标截图 `image copy 14.png` 作为最终视觉与交互验收基准。
|
||||
|
||||
## 1. 这份文档解决什么问题
|
||||
|
||||
当前代码和阶段文档虽然把“最小可写 runtime 闭环”做通了,但实际交付物仍然是一个:
|
||||
|
||||
- `iframe` 挂载的独立文档壳
|
||||
- 带 `Document Editor Shell` 标题的调试页
|
||||
- 带 revision/save/focus/selection/hover/event log 的 runtime 面板
|
||||
- 基于 `textarea + 按钮工具条 + 块卡片` 的工程壳
|
||||
|
||||
它和目标页的差距,不是“差一点样式”,而是:
|
||||
|
||||
- 页面架构不对
|
||||
- 交互壳不对
|
||||
- 验收标准不对
|
||||
- 默认主路径不对
|
||||
|
||||
本文的目的,是把后续交付标准重新冻结为:
|
||||
|
||||
> **最终默认交付必须逼近 `image copy 14.png` 这种一体化 Wolai 页面,而不是继续优化 `image copy 13.png` 这类 runtime 调试壳。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 最终交付标准
|
||||
|
||||
最终默认交付以目标截图为准,至少必须满足下面这些显性特征。
|
||||
|
||||
### 2.1 页面结构标准
|
||||
|
||||
- 左侧是产品级导航树,而不是单独文档调试页。
|
||||
- 中间是文档页面本身,不通过 `iframe` 二次嵌套。
|
||||
- 顶部是轻量面包屑 / 导航动作,不出现 runtime 诊断信息。
|
||||
- 正文区域是沉浸式页面画布,不出现工程面板、状态卡、调试日志。
|
||||
- 右上角是产品动作区,不是编辑器内部调试控件区。
|
||||
|
||||
### 2.2 编辑体验标准
|
||||
|
||||
- 页面标题是正文的一部分,视觉上属于页面内容,而不是壳标题。
|
||||
- 首块内容直接以内联块形态出现,不是“卡片块列表 + badge + tag”。
|
||||
- 占位文案应以内联方式出现在正文里,例如“输入 `/` 选择,按 `空格` 打开 AI...”,而不是单独的控制台提示区。
|
||||
- 勾选框、标题、段落、列表在正文中应呈现为自然文档流,不应被包成工程卡片。
|
||||
- slash、引用、缩进、折叠等交互应在光标附近或块上下文中触发,不依赖页顶按钮排布。
|
||||
|
||||
### 2.3 禁止出现的元素
|
||||
|
||||
默认交付页中禁止出现:
|
||||
|
||||
- `Document Editor Shell`
|
||||
- `revision=`
|
||||
- `updatedAt=`
|
||||
- `save=`
|
||||
- `focus=`
|
||||
- `selection=`
|
||||
- `hover=`
|
||||
- `交互状态`
|
||||
- `最小状态机`
|
||||
- `最近事件`
|
||||
- `block_editor_shell.ready`
|
||||
- 页顶固定一排 `Slash 菜单 / 折叠标题 / 缩进 / [[page]] / ((block))` 按钮
|
||||
- `Paragraph / Heading / Todo` 这种脱离光标上下文的演示按钮
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前偏移的根因
|
||||
|
||||
### 3.1 默认主路径被切成了 `iframe` 文档壳
|
||||
|
||||
当前默认文档页在命中文档壳开关后,直接返回 `MnoteWebDocumentShellHost`,并在其中挂一个 `iframe`。
|
||||
|
||||
相关代码:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx:183`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/mnote-web-document-shell-host.tsx:39`
|
||||
|
||||
这一步本身就决定了它不会长成目标截图那种“一体化页面”。
|
||||
|
||||
### 3.2 当前 `/document` 路由本质是 runtime 调试页
|
||||
|
||||
当前 Rust 文档页直接输出了:
|
||||
|
||||
- `Document Editor Shell`
|
||||
- hero 说明
|
||||
- meta pills
|
||||
- status card
|
||||
- toolbar buttons
|
||||
- interaction sidebar
|
||||
- event log
|
||||
|
||||
相关代码:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:790`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:805`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:821`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:838`
|
||||
|
||||
这不是“产品 UI 还没 polish”,而是“页面职责定义错了”。
|
||||
|
||||
### 3.3 当前块渲染是工程卡片流,不是正文文档流
|
||||
|
||||
当前每个 block 是:
|
||||
|
||||
- `li.editor-row`
|
||||
- 左侧 badge
|
||||
- 中间 title/meta
|
||||
- 下方 `textarea`
|
||||
- 右侧 state tag
|
||||
|
||||
相关代码:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:1408`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:1420`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:1426`
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs:1437`
|
||||
|
||||
这类结构适合调试事务,不适合对标 Wolai 页面。
|
||||
|
||||
### 3.4 验收标准被收缩成“最小可写闭环”
|
||||
|
||||
当前 smoke / harness 的判断标准主要是:
|
||||
|
||||
- 文档页里出现 `iframe`
|
||||
- `iframe` 里出现 `Document Editor Shell`
|
||||
- 能找到 `textarea[data-block-input-id]`
|
||||
- 能点击页顶 slash 按钮
|
||||
- 能看到 event log 标记
|
||||
|
||||
相关脚本:
|
||||
|
||||
- `/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js:17`
|
||||
- `/mnt/Data1T/mnote/scripts/task103-document-shell-cutover-smoke.js:25`
|
||||
- `/mnt/Data1T/mnote/scripts/task104-document-runtime-input-smoke.js:29`
|
||||
- `/mnt/Data1T/mnote/scripts/task104-document-runtime-input-smoke.js:46`
|
||||
- `/mnt/Data1T/mnote/scripts/task106-document-runtime-slash-reference-smoke.js:34`
|
||||
|
||||
相关任务口径:
|
||||
|
||||
- `/mnt/Data1T/mnote/harness-tasks.json:2124`
|
||||
- `/mnt/Data1T/mnote/harness-tasks.json:2248`
|
||||
- `/mnt/Data1T/mnote/harness-progress.txt:193`
|
||||
- `/mnt/Data1T/mnote/harness-progress.txt:203`
|
||||
|
||||
所以代码确实“通过了验收”,但那个验收根本不是目标页验收。
|
||||
|
||||
### 3.5 文档策略与产品目标之间出现了中途收缩
|
||||
|
||||
当前主基线文档明确收口到:
|
||||
|
||||
- AI-first
|
||||
- CLI-first
|
||||
- Rust-native
|
||||
- 轻量块编辑
|
||||
- 最小可写闭环
|
||||
|
||||
相关文档:
|
||||
|
||||
- `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md:19`
|
||||
- `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md:105`
|
||||
|
||||
这条路线适合先打通内核,但不能直接当作“产品默认完成态”。
|
||||
|
||||
---
|
||||
|
||||
## 4. 纠偏总原则
|
||||
|
||||
后续纠偏必须遵循下面四条。
|
||||
|
||||
### 4.1 默认交付优先级重排
|
||||
|
||||
优先级改为:
|
||||
|
||||
1. 默认页面必须像目标截图那样是一体化产品页
|
||||
2. 编辑交互必须内嵌在页面内容流中
|
||||
3. Rust command / save chain / projection 继续保留
|
||||
4. runtime 调试信息只能退到 debug 模式
|
||||
|
||||
不能再把“Rust runtime 闭环”放在默认页面形态之上。
|
||||
|
||||
### 4.2 `/document` 壳降级为 debug / prototype
|
||||
|
||||
`mnote-web /document` 可以保留,但只能作为:
|
||||
|
||||
- `?editor=runtime-debug`
|
||||
- `?editor=prototype`
|
||||
- 内部调试壳
|
||||
- transaction / IME / save chain 诊断页
|
||||
|
||||
它不能再是默认主编辑器。
|
||||
|
||||
### 4.3 `iframe` 方案退出默认主链
|
||||
|
||||
默认文档页必须回到页面内原生渲染:
|
||||
|
||||
- 不再通过 `iframe` 承载默认编辑器
|
||||
- 不再让页面主体验依赖另一个独立 HTML 文档
|
||||
- 不再让产品页布局与编辑器页布局分裂
|
||||
|
||||
### 4.4 调试能力后移,不再前置
|
||||
|
||||
这些能力应该保留,但只能后移到 debug:
|
||||
|
||||
- focus / selection / hover
|
||||
- event log
|
||||
- revision / updatedAt / save status
|
||||
- runtime 状态机说明
|
||||
- editor command buttons
|
||||
|
||||
---
|
||||
|
||||
## 5. 必须立即执行的纠偏项
|
||||
|
||||
下面这些项不是“优化建议”,而是必须执行的逆转动作。
|
||||
|
||||
### 5.1 默认主路径逆转
|
||||
|
||||
- [ ] 把默认文档页从 `MnoteWebDocumentShellHost` 切回页面内主渲染路径。
|
||||
- [ ] `page.tsx` 中默认逻辑不得再优先返回 `iframe` 壳。
|
||||
- [ ] `mnoteWebDocumentShellEnabled` 只能控制实验入口,不能控制默认编辑器。
|
||||
- [ ] `?editor=compat` 保留,但新增 `?editor=runtime-debug`,明确实验壳用途。
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/mnote-web-document-shell-host.tsx`
|
||||
|
||||
### 5.2 `/document` runtime 页角色重命名
|
||||
|
||||
- [ ] 把 `Document Editor Shell` 明确改名为 `Runtime Debug Shell` 或等价名称。
|
||||
- [ ] 页面文案必须承认它是 debug/prototype,而不是默认编辑器。
|
||||
- [ ] 该页默认不再通过主文档页直达。
|
||||
|
||||
涉及文件:
|
||||
|
||||
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/editor.rs`
|
||||
|
||||
### 5.3 删除默认页面中的调试区块
|
||||
|
||||
默认产品页必须移除:
|
||||
|
||||
- [ ] hero 说明区
|
||||
- [ ] meta pills
|
||||
- [ ] status card
|
||||
- [ ] interaction sidebar
|
||||
- [ ] event log
|
||||
- [ ] 页面顶部固定命令按钮
|
||||
|
||||
注意:
|
||||
|
||||
- 调试区块可以迁到 `debug panel`
|
||||
- 但不能继续留在默认编辑页 DOM 里
|
||||
|
||||
### 5.4 页面内编辑器替换卡片流
|
||||
|
||||
- [ ] 块渲染必须退出 `li + badge + meta + textarea + state-tag` 这种工程卡片结构。
|
||||
- [ ] 默认正文必须改成自然文档流:
|
||||
- 标题就是标题
|
||||
- 勾选就是勾选
|
||||
- 段落就是段落
|
||||
- 列表就是列表
|
||||
- [ ] 块级信息如 `depth=0 / editable=true / type:paragraph` 只能进入 debug,不得出现在正文。
|
||||
- [ ] 正文留白、字号、宽度、块间距必须向目标截图靠拢。
|
||||
|
||||
---
|
||||
|
||||
## 6. 分阶段纠偏清单
|
||||
|
||||
## 阶段 A:先把默认主路径拉回产品页
|
||||
|
||||
### 目标
|
||||
|
||||
先解决“默认打开文档时为什么像独立调试站点”。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 默认文档页退出 `iframe` 模式。
|
||||
- [ ] 页面回到 `DocumentShell -> DocumentContent` 一体化主链。
|
||||
- [ ] 保留 runtime 壳,但只允许通过显式 debug 参数进入。
|
||||
- [ ] 主路径不再展示 `Document Editor Shell` 文案。
|
||||
- [ ] 主路径不再依赖 `mnote-web-document-shell-host.tsx`。
|
||||
- [ ] 补回归:
|
||||
- 默认文档页 DOM 中不再存在 `iframe[title^="mnote-web-document-shell"]`
|
||||
- `editor=runtime-debug` 时才允许出现 iframe 或独立壳
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 用户打开页面时,首先看到的是产品页,而不是 runtime 站点。
|
||||
|
||||
## 阶段 B:恢复 Wolai 式页面骨架
|
||||
|
||||
### 目标
|
||||
|
||||
把“产品页壳”恢复到目标截图的感觉。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 左侧继续复用现有页面树/导航系统,不为编辑器单独造站。
|
||||
- [ ] 顶部保留产品导航:
|
||||
- 面包屑
|
||||
- 返回/前进
|
||||
- 右上角产品动作
|
||||
- [ ] 页面标题回到正文主列内部,而不是 debug 页标题。
|
||||
- [ ] 正文宽度、留白、顶部间距对齐目标截图。
|
||||
- [ ] 页面空白区域保留沉浸式阅读/编辑氛围,不塞调试信息。
|
||||
- [ ] 若现有 compat 页骨架更接近目标截图,优先复用它的产品壳,而不是继续美化 runtime 壳。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 不看功能,只看静态截图,页面也应该更像 Wolai 页面而不是内部工具页。
|
||||
|
||||
## 阶段 C:把编辑器从“卡片列表”改成“正文文档流”
|
||||
|
||||
### 目标
|
||||
|
||||
解决当前最刺眼的视觉偏移。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 去掉块左侧 badge 序号。
|
||||
- [ ] 去掉块右侧 `block:id / type:xxx` 标签。
|
||||
- [ ] 去掉每块上方 `Paragraph / depth=0 / editable=true` 元信息。
|
||||
- [ ] 标题块渲染为正文中的标题,不再单独加工程 title 行。
|
||||
- [ ] todo 块渲染为真实勾选项。
|
||||
- [ ] `textarea` 若继续保留,只能作为无边框、无卡片感的内联输入宿主。
|
||||
- [ ] 若 `textarea` 无法支撑自然正文体验,则必须把 `kode` 真正接入“块内输入器”层,而不是只停留在文档参考。
|
||||
- [ ] `[[page]]` / `((block))` 的插入入口改成光标附近上下文,不再只靠页顶按钮。
|
||||
- [ ] slash 菜单改为光标上下文菜单。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 用户看到正文时,应先感知“文档内容”,而不是“块调试单元”。
|
||||
|
||||
## 阶段 D:把交互从“页顶按钮”改成“块内上下文”
|
||||
|
||||
### 目标
|
||||
|
||||
解决当前交互方式明显不像 Wolai 的问题。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 去掉页顶固定 `Slash 菜单 / 折叠标题 / 缩进 / [[page]] / ((block))` 按钮。
|
||||
- [ ] slash 触发改为输入 `/`。
|
||||
- [ ] 页面引用改为输入 `[[`。
|
||||
- [ ] 块引用改为输入 `((` 或等价上下文入口。
|
||||
- [ ] 缩进 / 反缩进改为 `Tab / Shift+Tab` 主触发。
|
||||
- [ ] 标题折叠优先放在标题左侧 affordance,而不是页顶全局按钮。
|
||||
- [ ] 块 hover 菜单应轻量化,只在块附近出现。
|
||||
- [ ] 保留调试按钮,但移入 debug 模式或开发者工具抽屉。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 核心编辑动作不依赖页面级按钮栏。
|
||||
|
||||
## 阶段 E:调试能力彻底后移
|
||||
|
||||
### 目标
|
||||
|
||||
让默认产品页彻底脱离 runtime 调试感。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] `revision / save status / focus / selection / hover` 移到 debug 面板。
|
||||
- [ ] `event log` 移到 debug 面板。
|
||||
- [ ] `最小状态机` 文案移到 debug 面板。
|
||||
- [ ] debug 面板默认关闭,只在显式调试开关下出现。
|
||||
- [ ] 生产态 DOM 中不出现 `block_editor_shell.ready` 等字符串。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 默认页面截图不再暴露任何 runtime 术语。
|
||||
|
||||
## 阶段 F:验收标准重写
|
||||
|
||||
### 目标
|
||||
|
||||
防止再次出现“工程闭环通过了,但产品页仍然不对”的误判。
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] 废弃以 `iframe + Document Editor Shell + textarea` 为成功条件的默认验收。
|
||||
- [ ] 重写 `task103~107` 类 smoke 的默认成功条件。
|
||||
- [ ] 新增产品级验收断言:
|
||||
- 默认文档页没有 `iframe[title^="mnote-web-document-shell"]`
|
||||
- 页面不存在 `Document Editor Shell`
|
||||
- 页面不存在 `交互状态`
|
||||
- 页面不存在 `最近事件`
|
||||
- 页面存在产品导航树
|
||||
- 页面标题在正文主列内
|
||||
- 第一块内容以内联块形式出现
|
||||
- 占位文案直接出现在正文中
|
||||
- slash 菜单由输入 `/` 触发
|
||||
- [ ] 新增截图验收:
|
||||
- 至少保留一组默认页截图
|
||||
- 用于与目标截图做人工对照
|
||||
- [ ] Final QA 的表述从“最小可写闭环”改为“产品级默认编辑页达到目标页标准”。
|
||||
|
||||
### 完成判定
|
||||
|
||||
- 测试通过时,默认页面也必须像目标页,而不是仅仅可写。
|
||||
|
||||
---
|
||||
|
||||
## 7. 参考层在这次纠偏中的正确用法
|
||||
|
||||
这次纠偏不是要推翻 Rust 内核,而是要纠正“参考层只停留在文档里,没有真正进入主路径”的问题。
|
||||
|
||||
### `edita-core`
|
||||
|
||||
继续用于:
|
||||
|
||||
- command 容器
|
||||
- transform / transaction 思路
|
||||
|
||||
不用于:
|
||||
|
||||
- 默认产品页 UI
|
||||
|
||||
### `blocks`
|
||||
|
||||
继续用于:
|
||||
|
||||
- block model
|
||||
- import/export
|
||||
- history / diff / merge
|
||||
|
||||
不用于:
|
||||
|
||||
- 正文视觉与交互壳
|
||||
|
||||
### `kode`
|
||||
|
||||
这次必须优先重新评估它是否能真正承担:
|
||||
|
||||
- 块内输入器
|
||||
- Markdown/WYSIWYG 内联编辑
|
||||
- 光标附近上下文交互
|
||||
|
||||
如果 `textarea` 无法做出目标页的正文感,`kode` 不能再只停在“设计参考”。
|
||||
|
||||
### `leptos-tiptap`
|
||||
|
||||
继续作为 fallback:
|
||||
|
||||
- 当 `kode` 与自有块编排组合后仍无法提供足够自然的编辑体验时
|
||||
- 用来验证 Leptos 对接成熟编辑 runtime 的现实成本
|
||||
|
||||
### `mnote-next`
|
||||
|
||||
只继续作为:
|
||||
|
||||
- 交互样例
|
||||
- 历史反例
|
||||
|
||||
不再作为:
|
||||
|
||||
- 视觉或产品交付参考
|
||||
|
||||
---
|
||||
|
||||
## 8. 关于 “Tiptap 体验” 的补充判断
|
||||
|
||||
这一条必须单独说清。
|
||||
|
||||
### 8.1 仅执行本文前面的纠偏项,不足以自动获得 Tiptap 级体验
|
||||
|
||||
原因很直接:
|
||||
|
||||
- 前文主要纠正的是“默认页面壳”
|
||||
- 纠正的是 `iframe`、调试面板、卡片块流、验收标准
|
||||
- 这些动作可以让页面更像 Wolai
|
||||
- 但它们不会自动提供 `Tiptap / ProseMirror` 那种输入体验内核
|
||||
|
||||
如果底层仍然是:
|
||||
|
||||
- `textarea` 作为主要输入宿主
|
||||
- 自己拼 selection / composition / slash / token / hover 菜单
|
||||
- 自己再把这些交互拼成正文体验
|
||||
|
||||
那么最终结果最多是:
|
||||
|
||||
- “更像 Wolai 的产品壳”
|
||||
- “更自然的块 Markdown 编辑器”
|
||||
|
||||
而不是天然等于:
|
||||
|
||||
- “接近 Tiptap 的成熟输入体验”
|
||||
|
||||
### 8.2 如果最终目标明确是“类似 Tiptap 的体验”,应该把 `leptos-tiptap` 从 fallback 提升为重点实施分支
|
||||
|
||||
当下面这些诉求成立时,建议把 `leptos-tiptap` 提升为主候选,而不是继续只把它当 fallback:
|
||||
|
||||
- 你要的是接近 Wolai / Tiptap 的输入手感
|
||||
- 你重视成熟 selection / composition / inline behavior
|
||||
- 你希望 slash、placeholder、mark、node 行为更接近现成富文本编辑器
|
||||
- 你不希望长期自己维护 `textarea + 自拼交互` 这条路
|
||||
|
||||
这时更合理的路线是:
|
||||
|
||||
1. 产品页壳按本文前半部分纠偏,先回到一体化页面
|
||||
2. 人类编辑 surface 优先改成 `leptos-tiptap`
|
||||
3. Rust kernel 继续保留为事实源、命令层、AI/CLI 层
|
||||
4. 通过 adapter 把 `Tiptap JSON / command / selection event` 映射到 Rust block model / command
|
||||
|
||||
### 8.3 更准确的架构收口应该是“双层分工”,而不是二选一
|
||||
|
||||
如果采用 `leptos-tiptap`,建议分工固定为:
|
||||
|
||||
- `Tiptap / leptos-tiptap`
|
||||
- 负责人类交互体验
|
||||
- 负责 selection、composition、placeholder、toolbar/slash、inline behavior
|
||||
- Rust editor core / kernel
|
||||
- 负责事实源
|
||||
- 负责命令合同
|
||||
- 负责 AI/CLI 共用写链
|
||||
- 负责导入导出、审计、projection、持久化
|
||||
|
||||
也就是说:
|
||||
|
||||
> **对“人类编辑体验”妥协给 `Tiptap`,不等于对“系统语义主导权”妥协给 `Tiptap`。**
|
||||
|
||||
### 8.4 什么时候不该上 `leptos-tiptap`
|
||||
|
||||
只有在下面这些条件同时成立时,才继续坚持纯 Rust-native 输入壳:
|
||||
|
||||
- 你接受第一版明显不像 Tiptap,只求最小可写
|
||||
- 你接受更长时间去补 selection / IME / inline interaction
|
||||
- 你把 Rust-native 纯度放在“像 Wolai/Tiptap 的体验”之前
|
||||
|
||||
而你当前这一轮明确表达的是:
|
||||
|
||||
> **你真正想要的是类似 Tiptap 的体验。**
|
||||
|
||||
在这个前提下,继续默认押注 `textarea + 自拼交互`,风险会非常高。
|
||||
|
||||
### 8.5 本文后的新增执行要求
|
||||
|
||||
基于上面的判断,后续纠偏不应只做“页面壳纠偏”,还应新增一个并行分支:
|
||||
|
||||
- [ ] 新增 `Tiptap 体验纠偏` 分支文档
|
||||
- [ ] 用 `leptos-tiptap` 做一个真正的页面内 spike,而不是停留在 reference-code
|
||||
- [ ] 对比三条路线:
|
||||
- `textarea + 自拼交互`
|
||||
- `kode` 块内输入器
|
||||
- `leptos-tiptap`
|
||||
- [ ] 以“接近目标页体验”而不是“谁更 Rust-native”作为第一判断标准
|
||||
- [ ] 若 `leptos-tiptap` 的 spike 明显更接近目标页,应把它提升为默认人类编辑 surface 方案
|
||||
|
||||
---
|
||||
|
||||
## 9. 最终一句话收口
|
||||
|
||||
后续默认交付口径固定为:
|
||||
|
||||
> **Rust editor core 可以继续最小化,但默认页面不能再像 runtime 调试站点;默认页面必须回到 Wolai 式一体化产品页,runtime 壳只保留为 debug/prototype。**
|
||||
|
||||
如果进一步收紧到“体验也要接近 Tiptap/Wolai”,那么还要再补一句:
|
||||
|
||||
> **页面壳纠偏只能解决“像不像产品页”,不能单独解决“像不像 Tiptap”;若目标是 Tiptap 级体验,应认真把 `leptos-tiptap` 提升为主候选,而不是继续只放在 fallback。**
|
||||
@@ -0,0 +1,129 @@
|
||||
# [recycle] BlockNote Migration Plan v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
|
||||
## 1. 目标
|
||||
|
||||
这份文档用于冻结从 BlockNote 主路径迁移到 Rust block editor 的最小迁移顺序。
|
||||
|
||||
目标不是一次性删除所有旧代码,而是:
|
||||
|
||||
- 先让主文档页默认切到新 Rust editor 壳
|
||||
- 再把 BlockNote 降到 compat/debug
|
||||
- 最后清理旧依赖和主线耦合
|
||||
|
||||
## 2. 当前盘点
|
||||
|
||||
当前主路径仍直接依赖下面这些入口或依赖:
|
||||
|
||||
- `@blocknote/core`
|
||||
- `@blocknote/react`
|
||||
- `@blocknote/mantine`
|
||||
- `blocknote-editor.tsx`
|
||||
- `schema.ts`
|
||||
- `HocuspocusProvider`
|
||||
- `Yjs`
|
||||
|
||||
主路径宿主文件主要是:
|
||||
|
||||
- `wolai-frontend/src/components/editor/document-content.tsx`
|
||||
- `wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
- `wolai-frontend/src/components/editor/schema.ts`
|
||||
|
||||
## 3. 迁移分层
|
||||
|
||||
迁移固定分三层:
|
||||
|
||||
- 主路径
|
||||
由 Rust block editor 接管
|
||||
- compat
|
||||
保留旧 BlockNote 入口,供临时回退
|
||||
- debug
|
||||
保留最小诊断入口,帮助排障
|
||||
|
||||
禁止继续把 BlockNote 当成默认编辑器。
|
||||
|
||||
## 4. 内容格式迁移策略
|
||||
|
||||
第一阶段不要求一次性消灭所有旧内容格式。
|
||||
|
||||
固定策略如下:
|
||||
|
||||
- 主写链以 Rust editor command 和 Rust document model 为准
|
||||
- 旧 BlockNote JSON 通过兼容 codec 读取
|
||||
- 写回时优先生成 Rust 侧统一结构
|
||||
- 必要时保留从旧格式到新格式的单向迁移适配
|
||||
|
||||
重点对象:
|
||||
|
||||
- 段落
|
||||
- 标题
|
||||
- list/todo
|
||||
- `pageReference`
|
||||
- `blockReference`
|
||||
- 附件和重型自定义块
|
||||
|
||||
## 5. 入口切换顺序
|
||||
|
||||
第一步:
|
||||
|
||||
- 在文档页入口切到 `mnoteWebDocumentShellEnabled`
|
||||
- 默认走 `mnote-web /document`
|
||||
- 保留 `?editor=compat` 回退参数
|
||||
|
||||
第二步:
|
||||
|
||||
- `document-content.tsx` 继续保留为 compat 宿主壳
|
||||
- `blocknote-editor.tsx` 继续保留为 compat 编辑器实现
|
||||
|
||||
第三步:
|
||||
|
||||
- 新主编辑器稳定后,再把主路径对 `schema.ts`、`HocuspocusProvider`、`Yjs` 的依赖收缩到 compat/debug
|
||||
|
||||
## 6. 依赖收敛
|
||||
|
||||
迁移完成前后要明确区分:
|
||||
|
||||
- 主路径是否仍依赖 `@blocknote/core`
|
||||
- 主路径是否仍依赖 `blocknote-editor.tsx`
|
||||
- 主路径是否仍依赖 `HocuspocusProvider`
|
||||
- 主路径是否仍依赖 `Yjs`
|
||||
|
||||
最终要求:
|
||||
|
||||
- compat/debug 允许暂时保留
|
||||
- 主路径不再依赖以上能力
|
||||
|
||||
## 7. 风险
|
||||
|
||||
主要风险如下:
|
||||
|
||||
- 旧页面内容与新结构双写不一致
|
||||
- history / snapshot / TOC / stats 边界退化
|
||||
- 重型块在新主路径下缺少最小宿主能力
|
||||
- 回退入口不明确导致线上排障困难
|
||||
|
||||
## 8. 回退策略
|
||||
|
||||
必须保留显式回退:
|
||||
|
||||
- 文档页参数级 compat 入口
|
||||
- 旧 BlockNote 组件继续可挂载
|
||||
- debug 场景可直接验证旧壳
|
||||
|
||||
在没有确认新主路径稳定前,不得直接删除 compat/debug。
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
满足以下条件即可视为 migration v1 达标:
|
||||
|
||||
- 主文档页默认落到新 Rust editor 壳
|
||||
- compat/debug 入口仍可独立使用
|
||||
- `blocknote-editor.tsx` 不再是默认主路径
|
||||
- `schema.ts` 不再决定默认主编辑器
|
||||
- `@blocknote/core`
|
||||
- `blocknote-editor.tsx`
|
||||
- `schema.ts`
|
||||
- `HocuspocusProvider`
|
||||
- `Yjs`
|
||||
已被明确标记为 compat/debug 或待删除对象
|
||||
@@ -0,0 +1,140 @@
|
||||
# [recycle] mnote Rust Block Editor Blocks Spike v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本 spike 聚焦 `blocks` 参考层是否适合承担:
|
||||
|
||||
- Markdown
|
||||
- round-trip
|
||||
- history
|
||||
- diff
|
||||
- merge
|
||||
|
||||
以及哪些地方存在 `缺口`。
|
||||
|
||||
## 2. 参考入口
|
||||
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/document.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/block.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/converters.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/history.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/diff.rs`
|
||||
|
||||
## 3. 适配性结论
|
||||
|
||||
### 3.1 Markdown
|
||||
|
||||
`blocks` 最适合直接借的是 Markdown 相关思路。
|
||||
|
||||
原因:
|
||||
|
||||
- 已有文档模型
|
||||
- 已有 converter facade
|
||||
- 方向上天然适合 Markdown import/export
|
||||
|
||||
这意味着第一阶段不应该自己先写一套新的 Markdown parser 胶水。
|
||||
|
||||
### 3.2 round-trip
|
||||
|
||||
`Markdown -> blocks -> Markdown round-trip` 是 `blocks` 最容易先验证的一条链。
|
||||
|
||||
对 `mnote` 的价值:
|
||||
|
||||
- 可以快速检查标题、列表、待办、引用、代码块的稳定性
|
||||
- 可以给 `task-057` 的导入导出提供直接参考
|
||||
|
||||
### 3.3 history
|
||||
|
||||
`blocks` 的 history 适合直接借思路,不建议第一阶段重写。
|
||||
|
||||
至少在 `undo / redo` 的双栈组织上,它比从零发明更稳。
|
||||
|
||||
### 3.4 diff / merge
|
||||
|
||||
`blocks` 的 diff / merge 很适合做 AI-first 和 CLI-first 后续阶段的基础参考。
|
||||
|
||||
第一阶段不要求完整采用,但必须明确后续方向,否则 AI 重写链会缺审计基础。
|
||||
|
||||
## 4. 主要缺口
|
||||
|
||||
### 4.1 文档模型仍偏扁平
|
||||
|
||||
当前 `blocks::Document` 是 `Vec<Block>` 为主。
|
||||
|
||||
而 `mnote` 要的不是简单扁平列表,而是:
|
||||
|
||||
- 块树
|
||||
- children
|
||||
- 自定义 `BlockProps`
|
||||
- 引用 token
|
||||
- 字符串 ID
|
||||
|
||||
这就是第一大 `缺口`。
|
||||
|
||||
### 4.2 BlockType 与 mnote 目标不完全一致
|
||||
|
||||
`blocks` 的 `BlockType` 更偏通用编辑器,不直接覆盖:
|
||||
|
||||
- `page_reference`
|
||||
- `block_reference`
|
||||
- `media_placeholder`
|
||||
- `progress_placeholder`
|
||||
|
||||
因此不能直接拿来当最终枚举。
|
||||
|
||||
### 4.3 默认 ID 体系不适合
|
||||
|
||||
`blocks` 用 `Uuid`。
|
||||
|
||||
但 `mnote` 当前更适合:
|
||||
|
||||
- `DocumentId = String`
|
||||
- `BlockId = String`
|
||||
|
||||
因为要跟现有页面、引用、CLI、AI 命令保持统一可读标识。
|
||||
|
||||
## 5. Spike 结论
|
||||
|
||||
`blocks` 的定位应固定为:
|
||||
|
||||
- 采用:Markdown 思路
|
||||
- 采用:round-trip 参考
|
||||
- 采用:history 思路
|
||||
- 采用:diff / merge 思路
|
||||
- 不直接采用:最终 block model
|
||||
- 不直接采用:最终 ID 体系
|
||||
|
||||
## 6. 建议落地方式
|
||||
|
||||
建议在 `mnote-editor-core` 中这样接:
|
||||
|
||||
- 自定义 `EditorDocument`
|
||||
- 自定义 `EditorBlockNode`
|
||||
- 自定义 `EditorBlockType`
|
||||
- 用 `blocks` 作为后续 `markdown/history/diff/merge` 参考或局部依赖
|
||||
|
||||
不要反过来把 `mnote` 的核心模型强行塞进 `blocks`。
|
||||
|
||||
## 7. 第一阶段保留判断
|
||||
|
||||
第一阶段可接受的保守做法:
|
||||
|
||||
- 先不把 `blocks` 作为正式依赖
|
||||
- 但在文档中冻结:后续 Markdown、history、diff、merge 优先复用其思路
|
||||
|
||||
若到 `task-057` 时验证表明 `blocks` 已足够稳定,再引正式依赖。
|
||||
|
||||
## 8. 完成判定映射
|
||||
|
||||
`task-052` 的完成判定要求本文显式包含:
|
||||
|
||||
- Markdown
|
||||
- round-trip
|
||||
- history
|
||||
- diff
|
||||
- merge
|
||||
- 缺口
|
||||
|
||||
当前文档已满足这些项。
|
||||
@@ -0,0 +1,137 @@
|
||||
# [recycle] mnote Rust Block Editor Edita Spike v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本 spike 只回答一个问题:
|
||||
|
||||
`edita-core` 的 `Editor / Block / Command / 无头执行` 边界,是否适合作为 `mnote` 新主编辑器第一阶段的命令核参考。
|
||||
|
||||
结论先写:
|
||||
|
||||
- 适合直接借 `Editor`
|
||||
- 适合直接借 `Command`
|
||||
- 适合借 `Block` 作为注册与分发思路
|
||||
- 适合借 `无头执行`
|
||||
- 不适合直接拿来当最终 `mnote` block document 模型
|
||||
- 不适合直接拿来当最终前端胶水
|
||||
|
||||
## 2. 参考入口
|
||||
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita-core/src/lib.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/editor.rs`
|
||||
- `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita/src/state.rs`
|
||||
|
||||
## 3. 直接可复用面
|
||||
|
||||
### 3.1 `Editor`
|
||||
|
||||
`Editor<Node, State, Input>` 的价值不在 UI,而在:
|
||||
|
||||
- 持有统一 `state`
|
||||
- 提供统一命令入口
|
||||
- 提供 block registry
|
||||
- 允许 fallback block
|
||||
|
||||
这和 `mnote` 第一阶段要的“无头编辑器容器”是对齐的。
|
||||
|
||||
对 `mnote` 的映射建议:
|
||||
|
||||
- `Node` 不直接映射前端 DOM 节点
|
||||
- `State` 映射 `EditorDocument`
|
||||
- `Input` 第一阶段不必暴露给外部 UI,可先收口成 `EditorCommandEnvelope`
|
||||
|
||||
### 3.2 `Command`
|
||||
|
||||
`Command<State>` 很适合当 `mnote-editor-core` 的最小命令执行边界。
|
||||
|
||||
建议保留这层思想,但不要照搬 trait 名:
|
||||
|
||||
- `replace_block`
|
||||
- `insert_block_after`
|
||||
- `delete_block`
|
||||
- `split_block`
|
||||
- `merge_with_previous`
|
||||
- `move_block`
|
||||
- `indent_block`
|
||||
- `outdent_block`
|
||||
- `toggle_heading_collapse`
|
||||
|
||||
这些命令应当最终作用在 `EditorDocument` 上,而不是作用在 Leptos 组件状态上。
|
||||
|
||||
### 3.3 `Block`
|
||||
|
||||
`Block` trait 适合借来做“块类型注册 + 输入接管”思路,但当前实现更偏 parser pipeline。
|
||||
|
||||
`mnote` 第一阶段不建议直接照抄,因为我们更需要:
|
||||
|
||||
- 树形 block document
|
||||
- 固定字符串 ID
|
||||
- `BlockProps`
|
||||
- `content_node`
|
||||
- 引用 token
|
||||
|
||||
因此这里只借“块处理器是可注册单元”的思路。
|
||||
|
||||
### 3.4 无头执行
|
||||
|
||||
这是最值得采用的点。
|
||||
|
||||
新主编辑器必须先做无头执行,因为:
|
||||
|
||||
- CLI 要共用
|
||||
- AI 要共用
|
||||
- Web UI 只是壳
|
||||
|
||||
因此 `edita-core` 的无头执行边界适合直接进入采用矩阵。
|
||||
|
||||
## 4. 不直接采用的部分
|
||||
|
||||
### 4.1 不直接采用其最终 block 形态
|
||||
|
||||
原因:
|
||||
|
||||
- `mnote` 要的是块树,不是只围绕 parser block 运转
|
||||
- `mnote` 要保留 `page_reference / block_reference / media_placeholder / progress_placeholder`
|
||||
- 还要和 kernel `content_node` 边界对齐
|
||||
|
||||
### 4.2 不直接采用其 UI/editor 外壳
|
||||
|
||||
原因:
|
||||
|
||||
- 我们的 UI 壳后面要接 Leptos / kode
|
||||
- `edita-core` 当前更适合做 headless 胶水参考
|
||||
|
||||
## 5. Spike 结论
|
||||
|
||||
`edita-core` 在 `mnote` 第一阶段的定位固定为:
|
||||
|
||||
- 采用:`Editor`
|
||||
- 采用:`Command`
|
||||
- 部分采用:`Block`
|
||||
- 采用:`无头执行`
|
||||
- 不采用:最终产品 UI
|
||||
- 不采用:最终文档模型
|
||||
|
||||
## 6. 后续胶水建议
|
||||
|
||||
后续实现时,建议在 `mnote-editor-core` 里形成下面三层:
|
||||
|
||||
1. `EditorDocument`
|
||||
2. `EditorCommand`
|
||||
3. `EditorRuntime`
|
||||
|
||||
其中:
|
||||
|
||||
- `EditorRuntime` 借 `edita-core` 的无头执行边界
|
||||
- `EditorCommand` 借 `Command`
|
||||
- `EditorDocument` 不直接复用 `edita-core` block 结构,而是自定义
|
||||
|
||||
## 7. 本结论如何进入后续任务
|
||||
|
||||
- `task-051` 完成判定:
|
||||
- 本文明确写清 `Editor / Block / Command / 无头执行 / 胶水`
|
||||
- `task-055` 开始时:
|
||||
- `mnote-editor-core` 优先实现无头编辑器容器
|
||||
- 不先实现 Leptos UI
|
||||
@@ -0,0 +1,192 @@
|
||||
# [recycle] Rust Block Editor Model v0
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-phase0-baseline-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/edita/edita-core/src/lib.rs`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocks/src/document.rs`
|
||||
> - `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/model.rs`
|
||||
|
||||
## 1. 目的
|
||||
|
||||
本文冻结 `task-053` 的第一阶段 Rust block editor 模型口径。
|
||||
|
||||
目标不是一次定义最终全文档系统,而是先确定:
|
||||
|
||||
- 首批 Rust block type
|
||||
- `BlockProps` 的最小字段面
|
||||
- `reference token` 的表示方式
|
||||
- `content_node` 的载荷格式
|
||||
- 哪些内容明确退出对 BlockNote JSON 的长期依赖
|
||||
|
||||
## 2. 第一阶段 block type
|
||||
|
||||
第一阶段固定以下块类型进入 canonical model:
|
||||
|
||||
- `paragraph`
|
||||
- `heading`
|
||||
- `bullet_list_item`
|
||||
- `numbered_list_item`
|
||||
- `quote`
|
||||
- `todo`
|
||||
- `code_block`
|
||||
- `page_reference`
|
||||
- `block_reference`
|
||||
|
||||
补充说明:
|
||||
|
||||
- `paragraph` 是默认文本块。
|
||||
- `heading` 是标题块,级别收敛到 `BlockProps.heading_level`。
|
||||
- `bullet_list_item` 与 `numbered_list_item` 先保留为列表项,而不是先做复杂 list container。
|
||||
- `todo` 用来承接当前 `advancedTodo` 的第一阶段简化版。
|
||||
- `page_reference` 与 `block_reference` 先作为稳定引用块进入模型。
|
||||
|
||||
下面这些不进入第一阶段 canonical model,只保留 placeholder 或 compat:
|
||||
|
||||
- `media`
|
||||
- `progressMeter`
|
||||
- `mindmap`
|
||||
- `onlineTable`
|
||||
|
||||
## 3. `BlockProps`
|
||||
|
||||
第一阶段 `BlockProps` 固定为轻量可扩展对象。
|
||||
|
||||
核心字段:
|
||||
|
||||
- `indent`
|
||||
- `heading_level`
|
||||
- `checked`
|
||||
- `collapsed`
|
||||
- `language`
|
||||
- `reference_target_id`
|
||||
- `reference_label`
|
||||
- `reference_token_strategy`
|
||||
- `extra`
|
||||
|
||||
固定口径:
|
||||
|
||||
- `indent` 表示有限缩进层级,不承诺长期树协议。
|
||||
- `heading_level` 只对 `heading` 生效。
|
||||
- `checked` 只对 `todo` 生效。
|
||||
- `collapsed` 只对 `heading` 生效,用于折叠标题。
|
||||
- `language` 只对 `code_block` 生效。
|
||||
- `reference_target_id` 与 `reference_label` 服务 `page_reference` / `block_reference`。
|
||||
- `extra` 只用于迁移期兼容字段,不能成为长期语义事实源。
|
||||
|
||||
## 4. `reference token` 策略
|
||||
|
||||
第一阶段引用 token 固定区分两类:
|
||||
|
||||
- `page_reference`
|
||||
- `block_reference`
|
||||
|
||||
第一阶段 token strategy 固定支持三种:
|
||||
|
||||
- `double_bracket`
|
||||
- `double_paren`
|
||||
- `inline_chip`
|
||||
|
||||
对应语义:
|
||||
|
||||
- `double_bracket` 对应 `[[page]]`
|
||||
- `double_paren` 对应 `((block))`
|
||||
- `inline_chip` 只用于 UI 渲染或迁移期兼容,不作为长期文本事实源
|
||||
|
||||
引用 token 最小字段:
|
||||
|
||||
- `kind`
|
||||
- `strategy`
|
||||
- `target_id`
|
||||
- `label`
|
||||
- `raw_token`
|
||||
|
||||
## 5. `content_node` 载荷格式
|
||||
|
||||
第一阶段 `content_node` 是 block 内内容的最小结构单元。
|
||||
|
||||
固定 payload 类型:
|
||||
|
||||
- `text`
|
||||
- `hard_break`
|
||||
- `reference_token`
|
||||
|
||||
### 5.1 `text`
|
||||
|
||||
`text` 节点字段:
|
||||
|
||||
- `text`
|
||||
- `marks`
|
||||
|
||||
`marks` 第一阶段只支持:
|
||||
|
||||
- `bold`
|
||||
- `italic`
|
||||
- `underline`
|
||||
- `strike`
|
||||
- `code`
|
||||
|
||||
### 5.2 `hard_break`
|
||||
|
||||
`hard_break` 用于显式换行,不引入复杂段内结构树。
|
||||
|
||||
### 5.3 `reference_token`
|
||||
|
||||
`reference_token` 节点直接挂稳定 token 对象,而不是把引用仅留在原始文本里。
|
||||
|
||||
## 6. 块对象与文档对象
|
||||
|
||||
第一阶段 canonical block 结构固定为:
|
||||
|
||||
- `block_id`
|
||||
- `block_type`
|
||||
- `props`
|
||||
- `content_nodes`
|
||||
- `child_block_ids`
|
||||
|
||||
第一阶段 canonical document 结构固定为:
|
||||
|
||||
- `document_id`
|
||||
- `root_block_ids`
|
||||
- `blocks`
|
||||
|
||||
设计原则:
|
||||
|
||||
- `blocks` 先按稳定数组序列输出,便于命令执行和导入导出。
|
||||
- `child_block_ids` 先表达父子关系,不强求复杂树索引结构。
|
||||
- `content_nodes` 替代对 BlockNote inline JSON 的长期依赖。
|
||||
|
||||
## 7. 与 BlockNote JSON 的边界
|
||||
|
||||
第一阶段固定口径:
|
||||
|
||||
- BlockNote JSON 只作为迁移期外部格式
|
||||
- Rust editor model 才是长期事实层
|
||||
- `BlockProps`、`content_node`、`reference token` 必须能独立表达核心语义
|
||||
|
||||
换句话说:
|
||||
|
||||
- 可以保留 `BlockNote JSON -> Rust model` 适配器
|
||||
- 不能继续把 BlockNote JSON 当作长期 canonical schema
|
||||
|
||||
## 8. 第一阶段不解决的事
|
||||
|
||||
下面这些明确不在 `task-053` 解决:
|
||||
|
||||
- 多人协作状态
|
||||
- 复杂 mark 树
|
||||
- 富媒体完整属性系统
|
||||
- `mindmap` / `onlineTable` 的完整内嵌编辑语义
|
||||
- 完整批量选择与多块复制粘贴
|
||||
|
||||
## 9. 结论
|
||||
|
||||
`task-053` 的固定口径是:
|
||||
|
||||
- 用首批 `paragraph`、`heading`、`bullet_list_item`、`todo`、`page_reference`、`block_reference` 等块型建立 Rust canonical model
|
||||
- 用 `BlockProps` 收拢最小块属性
|
||||
- 用 `reference token` 固定 `[[page]]` / `((block))` 的稳定表示
|
||||
- 用 `content_node` 承接块内文本与引用
|
||||
- 从这一阶段开始退出对 BlockNote JSON 的长期依赖
|
||||
@@ -0,0 +1,255 @@
|
||||
# [recycle] Rust Block Editor Phase 0 Baseline v1
|
||||
|
||||
> 更新时间:2026-04-18
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/`
|
||||
> - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/schema.ts`
|
||||
> - `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
> - `/mnt/Data1T/mnote/wolai-frontend/src/lib/blocks/block-ops-adapter.ts`
|
||||
|
||||
## 1. 目的
|
||||
|
||||
本文用于冻结 task-048 所需的 Phase 0 基线:
|
||||
|
||||
1. 盘点当前 BlockNote 自定义块真实范围
|
||||
2. 盘点文档读写耦合点
|
||||
3. 冻结第一阶段 Rust Block Editor 的必须做 / 后补 / 延期边界
|
||||
4. 明确 `reference-code` 的采用、部分采用、不采用依据
|
||||
|
||||
Phase 0 的目标不是替换完全部现有能力,而是先把“什么必须进入第一阶段主链、什么只能先保留占位、什么明确延期”说清楚。
|
||||
|
||||
## 2. 当前 BlockNote 自定义块清单
|
||||
|
||||
当前 `customBlockSchema` 已注册的自定义块如下:
|
||||
|
||||
- `heading`
|
||||
- `advancedTodo`
|
||||
- `pageReference`
|
||||
- `blockReference`
|
||||
- `media`
|
||||
- `progressMeter`
|
||||
- `mindmap`
|
||||
- `onlineTable`
|
||||
|
||||
对应实现位置:
|
||||
|
||||
- `heading`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/schema.ts`
|
||||
- 现状:覆盖 BlockNote 默认标题块,允许 1-5 级并支持折叠。
|
||||
- `advancedTodo`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/AdvancedTodoBlock.tsx`
|
||||
- 现状:状态为 `todo/doing/done/cancelled`,点击轮转,`Alt+Click` 可直接置为 `cancelled`。
|
||||
- `pageReference`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/PageReferenceBlock.tsx`
|
||||
- 现状:块级页面引用,依赖 `pageId/title/asChildPage`,点击直接跳转文档页。
|
||||
- `blockReference`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/BlockReferenceBlock.tsx`
|
||||
- 现状:块级引用占位,运行时拉 `/api/blocks/get`,仅对段落/标题提供纯文本回写。
|
||||
- `media`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MediaBlock.tsx`
|
||||
- 现状:附件卡片,带选择资源、对齐、边框、OCR、下载、OnlyOffice 打开等强 UI 行为。
|
||||
- `progressMeter`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/ProgressBlock.tsx`
|
||||
- 现状:支持自动/手动模式;自动模式由编辑器扫描后续 `advancedTodo` 并计算百分比。
|
||||
- `mindmap`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx`
|
||||
- 现状:内嵌思维导图壳,包含预览、内联编辑、全屏、独立页、自动保存、资源联动。
|
||||
- `onlineTable`
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/OnlineTableBlock.tsx`
|
||||
- 现状:内嵌表格预览卡片,支持拖拽改尺寸、删除、全屏编辑。
|
||||
|
||||
## 3. 当前读写耦合点
|
||||
|
||||
### 3.1 写链耦合
|
||||
|
||||
当前编辑主链仍深度绑定 BlockNote:
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
|
||||
- `useCreateBlockNote(...)` 直接以 `customBlockSchema` 启动编辑器。
|
||||
- 保存经 `saveContent(...) -> /api/documents/save` 整体回写块树快照。
|
||||
- 自定义块的大量副作用都挂在 BlockNote 壳上:
|
||||
- `progressMeter` 自动统计
|
||||
- `media` 删除与恢复
|
||||
- `mindmap` 删除、自动保存、资源广播
|
||||
- `onlineTable` 删除、全屏切换
|
||||
- 协作仍挂着 `HocuspocusProvider + Y.Doc`,这与“当前不需要协作”的新主线并不一致。
|
||||
|
||||
### 3.2 读链耦合
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`
|
||||
- 文档页在阅读态和编辑态之间切换。
|
||||
- `initialPageSubtree` 与 `serverPageSubtreeSnapshot` 已经进入页面读态,但编辑态仍直接消费 BlockNote 内容快照。
|
||||
- Phase 0 的关键判断:
|
||||
- 阅读态已经开始朝稳定 projection 收口。
|
||||
- 编辑态仍把 BlockNote 的块树和自定义 props 当成事实层。
|
||||
|
||||
### 3.3 工具与编辑器模型耦合
|
||||
|
||||
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/blocks/block-ops-adapter.ts`
|
||||
- 当前 AI / server tool 适配只支持 `paragraph` 与 `heading` 两种插入规格。
|
||||
- 这说明自定义块虽然很多,但真正能被稳定工具链操作的只是一小部分。
|
||||
|
||||
### 3.4 复杂块与正文块混杂
|
||||
|
||||
当前一个 BlockNote 文档同时承载:
|
||||
|
||||
- 轻量正文块:`heading`、段落、列表、待办
|
||||
- 语义引用块:`pageReference`、`blockReference`
|
||||
- 重型宿主块:`media`、`mindmap`、`onlineTable`
|
||||
- 派生统计块:`progressMeter`
|
||||
|
||||
这会导致第一阶段编辑器如果不先切分边界,就会被复杂块宿主逻辑拖着走。
|
||||
|
||||
## 4. 第一阶段迁移范围冻结
|
||||
|
||||
### 4.1 必须做
|
||||
|
||||
下面这些能力必须进入第一阶段 Rust Block Editor 主链:
|
||||
|
||||
- `heading`
|
||||
- 必须保留 1-5 级标题与折叠语义。
|
||||
- 原因:这是阅读态结构、目录、AI 引用定位的基础。
|
||||
- `advancedTodo`
|
||||
- 必须保留最小四态语义:`todo/doing/done/cancelled`。
|
||||
- 原因:当前 `progressMeter` 依赖它,且任务型记录是高频场景。
|
||||
- `pageReference`
|
||||
- 必须保留块级引用能力,并与后续行内页面引用 token 对齐。
|
||||
- 原因:页面导航和引用挂接是高频主链。
|
||||
- `blockReference`
|
||||
- 必须保留“占位插入 + 回显 + 最小跳转”能力。
|
||||
- 原因:当前产品已存在块引用占位语义,但不要求第一阶段保留远程原位编辑。
|
||||
- `media`
|
||||
- 必须保留“附件块占位 + 标题/描述 + 打开原资源”最小能力。
|
||||
- 原因:附件是文档主链,不应在替换编辑器时丢失。
|
||||
- 最小块命令集
|
||||
- 单块编辑
|
||||
- 插入同级块
|
||||
- 删除块
|
||||
- 合并块
|
||||
- 上移下移
|
||||
- 有限缩进
|
||||
- 最小格式收口
|
||||
- JSON 块快照
|
||||
- Markdown 导入导出
|
||||
- Rust command 层稳定执行
|
||||
|
||||
### 4.2 后补
|
||||
|
||||
下面这些能力保留,但不要求在第一阶段做成完整壳:
|
||||
|
||||
- `progressMeter`
|
||||
- 后补为“派生块”或“只读统计块”。
|
||||
- 原因:其核心不是富交互编辑,而是基于 `advancedTodo` 的派生结果。
|
||||
- `mindmap`
|
||||
- 后补为“占位卡片 + 打开独立编辑器/独立页”。
|
||||
- 原因:当前实现太重,不应绑进第一阶段核心编辑循环。
|
||||
- `onlineTable`
|
||||
- 后补为“占位卡片 + 打开全屏表格编辑器”。
|
||||
- 原因:表格预览和尺寸拖拽属于宿主 UI,不是轻量块编辑核心。
|
||||
- `media`
|
||||
- 第一阶段只保留最小资源卡片。
|
||||
- OCR、OnlyOffice、复杂菜单、拖拽尺寸属于后补。
|
||||
- `blockReference`
|
||||
- 第一阶段只保留占位和跳转。
|
||||
- 远程拉块、原位编辑、富回显属于后补。
|
||||
|
||||
### 4.3 延期
|
||||
|
||||
下面这些能力明确延期,不进入第一阶段主链:
|
||||
|
||||
- Yjs / Hocuspocus 协作
|
||||
- BlockNote / ProseMirror 专属菜单状态兼容
|
||||
- `mindmap` 内联复杂快捷键与嵌入式编辑器行为
|
||||
- `onlineTable` 内嵌尺寸拖拽与高保真表格交互
|
||||
- `media` 的完整 OCR / 上传 / replace-storage 流程
|
||||
- 任何依赖 BlockNote runtime 才能成立的行为
|
||||
|
||||
## 5. Phase 0 冻结清单
|
||||
|
||||
### 5.1 必须做清单
|
||||
|
||||
- [ ] 把 `heading`、段落、列表、基础待办、`advancedTodo` 抽成 Rust editor core 的稳定块模型
|
||||
- [ ] 把 `pageReference` 与 `blockReference` 抽成稳定引用块,而不是继续仅作为 BlockNote 自定义 React block
|
||||
- [ ] 把 `media` 抽成最小附件占位块,保留资源 id / 标题 / 打开动作
|
||||
- [ ] 规定 `mindmap`、`onlineTable` 第一阶段只以宿主占位块进入文档
|
||||
- [ ] 把块命令统一成 Rust 侧可测试命令,不再把 Enter / Backspace / Tab 语义写死在 BlockNote 事件壳
|
||||
- [ ] 把保存边界从“直接保存 BlockNote 快照”改为“保存 Rust block document + 最小宿主块 props”
|
||||
|
||||
### 5.2 后补清单
|
||||
|
||||
- [ ] `progressMeter` 自动统计改成独立派生器
|
||||
- [ ] `mindmap` 预览投影和独立编辑器接缝
|
||||
- [ ] `onlineTable` 预览投影和独立编辑器接缝
|
||||
- [ ] `blockReference` 远程块拉取和最小编辑同步
|
||||
- [ ] `media` 高级工具栏、OCR、OnlyOffice 入口
|
||||
|
||||
### 5.3 延期清单
|
||||
|
||||
- [ ] 协作协议
|
||||
- [ ] BlockNote 级 UI 状态兼容层
|
||||
- [ ] 重型块的嵌入式内核迁移
|
||||
|
||||
## 6. reference-code 采用依据
|
||||
|
||||
### 6.1 `edita-core`:采用
|
||||
|
||||
采用依据:
|
||||
|
||||
- `edita-core/src/lib.rs` 已经给出最小 `Editor / Block / Command` 无头边界。
|
||||
- 这与第一阶段“先把块命令从 UI 壳里抽出来”的目标完全一致。
|
||||
- 它不绑定 DOM、ProseMirror、Tiptap,因此适合作为 Rust editor core 的命令容器参考。
|
||||
|
||||
不直接采用的部分:
|
||||
|
||||
- `edita` UI 壳和示例不是当前主链事实层。
|
||||
- 原因:`mnote` 需要的是 command / state 组织,不是直接套它的现成编辑器外壳。
|
||||
|
||||
### 6.2 `blocks`:采用
|
||||
|
||||
采用依据:
|
||||
|
||||
- `blocks/src/block.rs`、`document.rs`、`converters.rs`、`history.rs`、`diff.rs`、`sanitizer.rs` 已覆盖:
|
||||
- 块文档模型
|
||||
- Markdown / HTML / JSON 转换
|
||||
- history
|
||||
- diff / merge
|
||||
- 这正是第一阶段最缺、且不该再自研一套的基础层。
|
||||
|
||||
部分保留胶水的地方:
|
||||
|
||||
- `mnote` 的 `pageReference`、`blockReference`、`mindmap`、`onlineTable` 不是 `blocks` 原生块型,需要补最小扩展映射。
|
||||
|
||||
### 6.3 `kode`:部分采用
|
||||
|
||||
部分采用依据:
|
||||
|
||||
- `kode-core` 的 buffer / selection / editing primitives 可作为块内输入器参考。
|
||||
- `kode-leptos` 的 `MarkdownEditorComponent` 与 `TreeWysiwygEditor` 可提供 Leptos 侧输入和光标处理样板。
|
||||
- `kode-doc` 的 tree node / token position 说明它适合借鉴“结构化编辑器内部表示”。
|
||||
|
||||
不直接全量采用的原因:
|
||||
|
||||
- `kode-doc` 自带一套更接近富文本树编辑器的内部结构。
|
||||
- `mnote` 第一阶段不需要把全部文档事实改造成另一套通用 tree editor 语义。
|
||||
|
||||
### 6.4 `leptos-tiptap`:部分采用
|
||||
|
||||
部分采用依据:
|
||||
|
||||
- `src/api/component.rs`、`use_tiptap_editor.rs`、`runtime/bridge.rs` 对 Leptos 接第三方 runtime 的方式很清楚。
|
||||
- 若 Rust-native UI 壳卡住,它适合当 fallback 接缝参考。
|
||||
|
||||
不作为主线采用的原因:
|
||||
|
||||
- 它本质仍是 Tiptap runtime bridge。
|
||||
- 第一阶段目标是 Rust-native 轻量块编辑器,而不是再回到 JS 富文本 runtime 作为主事实层。
|
||||
|
||||
## 7. Phase 0 结论
|
||||
|
||||
Phase 0 的冻结口径如下:
|
||||
|
||||
- 第一阶段必须覆盖 `heading`、`advancedTodo`、`pageReference`、`blockReference`、`media` 的最小稳定语义。
|
||||
- `progressMeter`、`mindmap`、`onlineTable` 进入第一阶段时只保留占位或派生结果,不把完整嵌入式编辑能力一起搬过去。
|
||||
- 命令核优先采用 `edita-core` 思路,文档模型与转换优先采用 `blocks`,输入壳局部借鉴 `kode`,`leptos-tiptap` 仅作 fallback 参考。
|
||||
Reference in New Issue
Block a user