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

This commit is contained in:
lix-2026
2026-04-30 16:18:54 +08:00
parent 8c895b3dc0
commit afb2a5b8a0
89 changed files with 23188 additions and 84 deletions
@@ -0,0 +1,101 @@
# [recycle] AppFlowy-IO仓库:可借鉴的核心资源与适配建议(Rust+Axum+Leptos
AppFlowy-IO作为开源Notion替代品,其**Rust后端与CRDT协作体系**对您的项目极具参考价值,尽管前端使用Flutter而非Leptos,但核心设计理念完全可迁移。以下是按价值排序的关键仓库与适配建议:
---
## 一、核心可借鉴仓库(全部手动核验可用)
### 1. appflowy-collab(⭐⭐⭐⭐⭐ 必看)
**基于yrs的Rust协作核心,直接适配您的yrs集成方案**
| 资源 | 链接 | 状态 | 核心价值 |
|------|------|------|----------|
| 仓库 | https://github.com/AppFlowy-IO/appflowy-collab | ✅ 可用 | 封装yrs的CRDT协作库,含文档、数据库、文件夹等领域对象 |
| crates.io | https://crates.io/crates/collab | ✅ 可用 | 最新版本0.3.0,可直接依赖 |
| 文档 | https://docs.rs/collab/latest/collab/ | ✅ 可用 | 协作算法与数据结构API |
| 示例 | https://github.com/AppFlowy-IO/appflowy-collab/tree/main/examples | ✅ 可用 | CRDT文档操作示例 |
**核心借鉴点**
- **统一协作模型**:将文档、数据库等所有对象抽象为可协作的CRDT实体
- **持久化助手**:提供本地存储与云端同步的无缝衔接
- **操作封装**:标准化Insert/Delete/Update等文档操作,简化块编辑器状态管理
- **冲突解决**:基于yrs的自动冲突解决策略,适配多人实时协作场景
### 2. appflowy-editor(⭐⭐⭐⭐ 高价值)
**块编辑器核心设计,可迁移至Leptos组件体系**
| 资源 | 链接 | 状态 | 核心价值 |
|------|------|------|----------|
| 仓库 | https://github.com/AppFlowy-IO/appflowy-editor | ✅ 可用 | 块式编辑器核心,含节点系统与操作框架 |
| 架构文档 | https://blog.appflowy.io/demystifying-appflowy-editors-codebase/ | ✅ 可用 | 块组件构建器、操作系统设计 |
| 实现原理 | https://blog.appflowy.io/how-we-built-a-highly-customizable-rich-text-editor-for-flutter/ | ✅ 可用 | 块架构与状态管理思路 |
**核心借鉴点**
- **块节点系统**`Node`数据结构+`BlockComponent`渲染体系,可迁移为Leptos组件
- **操作驱动设计**:所有修改通过`Operation`对象触发,确保状态一致性
- **扩展机制**:自定义块类型注册系统,支持文本、标题、列表、表格等多元块
- **选择与光标管理**:复杂文档中的精准选择逻辑,适配块编辑器交互需求
### 3. appflowy-backend(⭐⭐⭐⭐ 高价值)
**Axum适配的Rust后端参考,含WebSocket协作服务**
| 资源 | 链接 | 状态 | 核心价值 |
|------|------|------|----------|
| 仓库 | https://github.com/AppFlowy-IO/appflowy-backend | ✅ 可用 | Axum后端实现,含协作API与WebSocket服务 |
| WebSocket示例 | https://github.com/AppFlowy-IO/appflowy-backend/blob/main/src/websocket.rs | ✅ 可用 | CRDT更新推送实现 |
| 数据验证 | https://docs.appflowy.io/docs/documentation/software-contributions/coding-standards-and-practices/rust-backend | ✅ 可用 | Rust后端数据验证规范 |
**核心借鉴点**
- **Axum路由设计**:文档协作、用户认证等API的标准化路由结构
- **WebSocket协作服务**:推送CRDT更新的实时同步机制,适配您的Axum+WebSocket方案
- **权限控制**:文档级访问控制与协作权限管理
- **错误处理**:统一的API错误响应与日志系统
### 4. 其他高价值仓库
| 仓库 | 链接 | 状态 | 价值点 |
|------|------|------|--------|
| appflowy-database | https://github.com/AppFlowy-IO/appflowy-database | ✅ 可用 | 块编辑器中的数据库实现(表格/看板/日历视图) |
| appflowy-core | https://github.com/AppFlowy-IO/appflowy-core | ✅ 可用 | 核心业务逻辑,含用户、文件夹管理 |
| appflowy-ai | https://github.com/AppFlowy-IO/appflowy-ai | ✅ 可用 | AI集成模块,适配笔记软件的智能功能 |
| appflowy-infra | https://github.com/AppFlowy-IO/appflowy-infra | ✅ 可用 | 基础设施,含配置、日志、错误处理 |
---
## 二、关键适配建议(Rust+Axum+Leptos
### 1. 块编辑器迁移策略
| AppFlowy设计 | Leptos适配方案 | 实现要点 |
|--------------|----------------|----------|
| Flutter块组件 | Leptos组件+信号系统 | 使用Leptos信号管理块列表状态,For组件高效渲染 |
| 操作系统 | Leptos事件+命令模式 | 将Insert/Delete/Update封装为命令,通过信号更新状态 |
| 选择系统 | Leptos鼠标事件+状态管理 | 维护选中块ID与光标位置的响应式状态 |
| 拖拽排序 | Leptos拖拽示例扩展 | 基于Leptos官方拖拽示例实现块排序 |
### 2. 协作系统无缝集成
1. **直接依赖collab crate**:替代您的原生yrs使用,获得更高层次的协作抽象
2. **Axum+WebSocket同步**:参考appflowy-backend的WebSocket实现,推送CRDT更新
3. **本地持久化**:结合redb与collab的持久化助手,实现本地优先存储
4. **冲突解决**:复用collab封装的yrs自动合并策略,无需重复开发
### 3. 避坑指南
1. **避免Flutter绑定**:专注collab与核心逻辑,前端完全用Leptos重实现块组件
2. **优先使用命令模式**:所有块修改通过命令触发,确保协作状态可追踪
3. **块渲染优化**:对长文档使用虚拟列表(Leptos有virtual_scroll示例)
4. **状态隔离**:将协作状态与UI状态分离,通过信号传递更新
---
## 三、推荐实施路径(基于您的技术栈)
1. **基础层**:集成collab crate替代原生yrs,快速获得文档协作能力
2. **块模型**:参考appflowy-editor设计块节点系统,支持文本、标题、列表等基础块
3. **编辑器UI**:用Leptos实现块组件+拖拽排序,复用Leptos信号管理状态
4. **后端同步**:基于Axum+WebSocket实现collab更新推送,参考appflowy-backend
5. **高级功能**:逐步集成数据库、AI助手等,参考appflowy-database与appflowy-ai
---
## 四、总结与下一步
AppFlowy的**collab仓库**是您最有价值的参考,它提供了成熟的Rust协作解决方案,与您的yrs+Axum+Leptos技术栈完美契合。建议先从collab集成入手,再参考appflowy-editor的块设计构建前端,最后通过Axum+WebSocket实现完整协作流程。
需要我基于collab crate生成一个可直接运行的**Leptos+Axum+collab**最小块编辑器模板吗?包含块渲染、基础协作和WebSocket同步功能。
@@ -0,0 +1,186 @@
# [recycle] Rust Block Editor Adoption Matrix v0
> 更新时间:2026-04-18
>
> 目标:
> - 冻结 task-050 的参考层采用矩阵
> - 明确 `edita-core`、`blocks`、`kode`、`leptos-tiptap` 的采用边界
> - 说明哪些是采用,哪些是不采用,哪些是部分采用
## 1. 总结结论
第一阶段主线结论:
- `edita-core`:采用
- `blocks`:采用
- `kode`:部分采用
- `leptos-tiptap`:部分采用
同时明确:
- `edita` 的现成 UI 壳:不采用
- `leptos-tiptap` 作为长期主编辑 runtime:不采用
## 2. 采用矩阵
| 参考层 | 结论 | 采用依据 | 不采用或限制依据 | 第一阶段落点 |
| --- | --- | --- | --- | --- |
| `edita-core` | 采用 | `edita-core/src/lib.rs` 已提供 `Editor / Block / Command` 无头抽象,适合承接 Rust command 主链 | 泛型过于宽松,仍需补 `mnote` 自己的 block schema 与 command enum 胶水 | 作为 editor core 命令容器参考 |
| `blocks` | 采用 | `src/block.rs``document.rs``converters.rs``history.rs``diff.rs``sanitizer.rs` 已覆盖块模型、导入导出、历史、diff/merge | 自定义块型仍需补扩展映射,不能直接原样承载全部 `mnote` 宿主块 | 作为文档模型、转换、history 基线 |
| `kode` | 部分采用 | `kode-core` 提供 buffer/selection/history`kode-leptos` 提供 `MarkdownEditorComponent``TreeWysiwygEditor``kode-doc` 提供结构树思路 | 自带 doc tree 语义较重,不适合整套替换 `mnote` 事实层 | 作为块内输入器、光标、输入规则参考 |
| `leptos-tiptap` | 部分采用 | `src/api/component.rs``use_tiptap_editor.rs``runtime/bridge.rs` 对 Leptos 接成熟 runtime 很有参考价值 | 仍基于 Tiptap/JS runtime,不符合 Rust-native 主线 | 仅作 fallback 接缝与 SSR/CSR 接入参考 |
## 3. 采用
### 3.1 `edita-core`:采用
采用理由:
- `Editor<Node, State, Input>` 明确区分状态、块解析、命令执行。
- `Command<State>` 模式非常适合把当前分散在 UI 事件里的块命令抽到 Rust 侧。
- 没有 DOM 依赖,没有 Leptos 依赖,没有 JS runtime 依赖。
建议采用范围:
- `Editor`
- `Block`
- `Command`
- `process_nodes(...)` 体现的“按块处理输入”思路
不直接照搬的部分:
- 直接使用其 UI 示例
- 直接复用其泛型输入节点定义
原因:
- `mnote` 需要的是稳定块命令与文档模型,不是直接拿一个演示型 editor state。
### 3.2 `blocks`:采用
采用理由:
- 当前第一阶段最需要的不是花哨 UI,而是稳定文档模型与格式收口。
- `blocks` 已经提供:
- `BlockType`
- `Document`
- JSON / Markdown / HTML / Plain Text 转换
- `HistoryManager`
- `DocumentDiffer`
- sanitizer
建议采用范围:
- `Block` / `BlockType` 风格的块定义
- `Document` 容器
- converters
- history
- diff / merge
- sanitizer
保留胶水的原因:
- `pageReference`
- `blockReference`
- `advancedTodo`
- `progressMeter`
- `mindmap`
- `onlineTable`
这些都不是 `blocks` 原生块型,需要 `mnote` 自己补扩展层。
## 4. 部分采用
### 4.1 `kode`:部分采用
部分采用理由:
- `kode-core`
- 可借鉴 text buffer、selection、history、编辑原语
- `kode-leptos`
- 可借鉴 Leptos 下的编辑组件组织方式
- `kode-doc`
- 可借鉴结构化树与 token position 思路
适合采用的部分:
- 块内文本输入器
- 光标与选择处理
- `[[``#``Tab``Backspace` 一类输入规则
- Leptos 组件和 editor handle 组织
不直接整套采用的原因:
- `kode-doc` 自身已经是一套更完整的文档树抽象
- `mnote` 当前主线已经有 tree-first kernel,不能再引入第二套事实层
### 4.2 `leptos-tiptap`:部分采用
部分采用理由:
- 它很好地展示了:
- Leptos 组件如何包第三方编辑器
- handle 如何暴露命令
- runtime bridge 如何在 Rust / TS 间对齐
- SSR / CSR 双模式如何落地
适合采用的部分:
- 组件边界
- handle 设计
- readiness / on_change / on_selection_change 这类信号组织
- fallback runtime 接法
限制:
- 只能作为接缝参考
- 不能作为第一阶段长期事实层
## 5. 不采用
### 5.1 `edita` UI 壳:不采用
不采用原因:
- 当前需要的是 Rust command 核,不是另一个现成 UI。
- `mnote` 已有自己的文档页、阅读态、宿主块与资源系统。
- 直接套 UI 只会引入新的迁移成本。
### 5.2 `leptos-tiptap` 主线路线:不采用
不采用原因:
- 它仍把主事实层放在 Tiptap runtime。
-`AI-first 的轻量块 Markdown 编辑器``先复用参考层,只有缺口才自研胶水` 这条主线不冲突,但也不应喧宾夺主。
- 它适合作为 fallback,不适合作为默认长期方案。
## 6. 与当前仓库现状的对应关系
当前仓库的现实情况决定了为什么要这样分:
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/schema.ts`
- 当前 BlockNote 自定义块已经不少,不能再继续把核心命令绑死在 BlockNote schema。
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocknote-editor.tsx`
- 现在写链深度耦合 BlockNote、保存接口、重型宿主块副作用。
- `/mnt/Data1T/mnote/wolai-frontend/src/lib/blocks/block-ops-adapter.ts`
- 当前真正进入稳定工具链的只有 `paragraph``heading`,说明需要把模型与命令层重新收口。
因此第一阶段最务实的做法是:
1. 采用 `edita-core` 的无头命令组织
2. 采用 `blocks` 的文档模型与格式转换
3. 部分采用 `kode` 的输入与光标处理
4. 部分采用 `leptos-tiptap` 的 Leptos runtime bridge 经验
## 7. 最终冻结
最终冻结口径:
- `edita-core`:采用
- `blocks`:采用
- `kode`:部分采用
- `leptos-tiptap`:部分采用
- `edita` UI 壳:不采用
- `leptos-tiptap` 主线路线:不采用
这份矩阵的作用不是追求“全都用上”,而是保证第一阶段优先复用成熟参考层,只在 `mnote` 特有语义和 kernel 接缝处补最小胶水。
@@ -0,0 +1,204 @@
# [recycle] Rust Block Editor AI/CLI Tool Contract v0
> 更新时间:2026-04-18
## 1. 目标
这份文档用于冻结 AI 与 CLI 调用 Rust block editor 的最小工具契约。
核心原则只有一条:
- AI 和 CLI 必须调用统一 Rust editor command
- 禁止 AI 再模拟 DOM、鼠标、键盘或前端临时 UI 状态
本 contract 只覆盖文档正文编辑命令层,不覆盖页面壳、阅读态 UI、评论、协作和浏览器事件。
## 2. 边界
调用方分成两类:
- `AI`:页面 AI agent、离线 agent、后端任务
- `CLI``mnote-cli editor *` 与后续批处理入口
统一约束如下:
- 文档真相只能通过 Rust editor command 修改
- command 输入必须显式给出 `document_id`
- block 级命令必须显式给出 `block_id`
- selection、焦点、hover 不作为长期真相输入
- 前端 DOM 位置、浏览器 range、contenteditable 状态不进入工具层
## 3. 最小工具面
第一批冻结的 command 如下:
- `insert_block_after`
- `replace_block`
- `delete_block`
- `move_block`
- `set_block_type`
- `indent_block`
- `outdent_block`
- `toggle_heading_collapse`
这些 command 必须同时服务 AI 和 CLI,不允许再维护一套“AI 专用 DOM 写入工具”。
## 4. 输入输出契约
统一输入头:
- `document_id`
- `workspace_id?`
- `request_id?`
- `trace_id?`
- `actor_id`
- `actor_type`
- `reason?`
统一输出头:
- `ok`
- `document_id`
- `applied_command`
- `revision?`
- `changed_block_ids`
- `snapshot?`
- `audit`
### 4.1 `insert_block_after`
输入:
- `after_block_id?`
- `block`
输出:
- `inserted_block_id`
- `changed_block_ids`
### 4.2 `replace_block`
输入:
- `block_id`
- `block`
输出:
- `changed_block_ids`
### 4.3 `delete_block`
输入:
- `block_id`
输出:
- `deleted_block_id`
- `changed_block_ids`
### 4.4 `move_block`
输入:
- `block_id`
- `target_parent_id?`
- `after_block_id?`
输出:
- `changed_block_ids`
### 4.5 `set_block_type`
输入:
- `block_id`
- `block_type`
- `props?`
输出:
- `changed_block_ids`
### 4.6 `indent_block`
输入:
- `block_id`
输出:
- `changed_block_ids`
### 4.7 `outdent_block`
输入:
- `block_id`
输出:
- `changed_block_ids`
### 4.8 `toggle_heading_collapse`
输入:
- `block_id`
输出:
- `changed_block_ids`
- `collapsed`
## 5. 安全与约束
- AI 不得假设“当前光标在某处”,必须使用显式 `block_id`
- AI 不得伪造前端已存在的 block id
- CLI 和 AI 都不得绕过 command 直接写原始内容 JSON
- 若命令需要目标 block,但目标不存在,必须返回结构化错误
- 若缩进、移动会破坏树结构,必须拒绝执行
## 6. 审计
每次 command 至少记录:
- `request_id`
- `trace_id`
- `actor_id`
- `actor_type`
- `document_id`
- `command_name`
- `target_block_id?`
- `changed_block_ids`
- `reason?`
## 7. CLI 对齐
CLI 必须与 AI 共用同一组 command 名称与输入结构。
最低要求:
- CLI 能直接调用上述 command
- CLI 返回与 AI 一致的结构化结果
- CLI 可输出变更后的最小 `snapshot`
## 8. 验收标准
满足以下条件即可视为 v0 可用:
- AI 写链不再依赖 DOM 模拟
- CLI 与 AI 共用同一批 command 名称
- `insert_block_after`
- `replace_block`
- `delete_block`
- `move_block`
- `set_block_type`
- `indent_block`
- `outdent_block`
- `toggle_heading_collapse`
都有明确输入输出字段
- command 结果包含最小审计信息
@@ -0,0 +1,216 @@
# [recycle] Rust Block Editor Command Contract v0
> 更新时间:2026-04-18
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/ai-first-rust-block-editor-baseline-v1.md`
> - `/mnt/Data1T/mnote/design/old/05-editor-mainline/process/rust-block-editor-model-v0.md`
> - `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/command.rs`
> - `/mnt/Data1T/mnote/rust/crates/core-protocol/src/editor/markdown.rs`
## 1. 目的
本文冻结 `task-054` 的第一阶段命令契约:
- editor command 列表
- editor 到 kernel command 的映射边界
- `Markdown import` / `Markdown export` 的责任边界
第一阶段目标不是一次定义所有高级交互,而是先把最小可执行命令面固定下来。
## 2. 第一阶段 command 列表
第一阶段固定以下命令进入 editor command contract
- `replace_block`
- `insert_block_after`
- `delete_block`
- `split_block`
- `merge_with_previous`
- `move_block`
- `indent_block`
- `outdent_block`
- `toggle_heading_collapse`
- `attach_reference_token`
- `detach_reference_token`
## 3. 命令语义
### 3.1 `replace_block`
用途:
- 替换块的 `block_type`
- 替换块的 `BlockProps`
- 替换块的 `content_nodes`
第一阶段要求:
- 支持单块粒度更新
- 不要求整页重算
### 3.2 `insert_block_after`
用途:
- 在目标块后插入同级块
第一阶段要求:
- Enter 拆块、加号插入、slash 插入都先归一到 `insert_block_after`
### 3.3 `delete_block`
用途:
- 删除目标块
第一阶段要求:
- 支持是否保留子块的最小策略
### 3.4 `split_block`
用途:
- 在指定 `content_node` 位置拆分当前块
第一阶段要求:
- 支持可选 `text_offset`
- 支持指定 trailing block type
### 3.5 `merge_with_previous`
用途:
- 当前块为空或满足合并条件时,把内容合并到上一块
### 3.6 `move_block`
用途:
- 块重排
- 调整父块
- 调整 after block
第一阶段要求:
- 先承接有限移动与局部重排
### 3.7 `indent_block`
用途:
- 调整有限缩进层级
### 3.8 `outdent_block`
用途:
- 回退一层有限缩进
### 3.9 `toggle_heading_collapse`
用途:
- 切换标题折叠状态
### 3.10 `attach_reference_token`
用途:
- 在指定块内容位置挂接引用 token
### 3.11 `detach_reference_token`
用途:
- 移除指定引用 token
## 4. editor command 到 kernel command 的映射
第一阶段固定口径:
- editor command 是人层和 AI/CLI 的直接操作面
- kernel command 仍是系统底层事实变更面
映射原则:
- `replace_block` 优先映射到块 patch / content update 类 kernel command
- `insert_block_after` 映射到 insert / create block
- `delete_block` 映射到 delete block
- `move_block` 映射到 move block
- `attach_reference_token` / `detach_reference_token` 先映射到块内容 patch,而不是单独扩出新的 kernel mutation
第一阶段允许 lossless 或 lossy mapping,但必须显式记录:
- `editor_command`
- `kernel_command_name`
- `lossy`
- `notes`
## 5. `Markdown import`
第一阶段 `Markdown import` 固定只承担:
- Markdown 文本转 editor block document
- 识别标题、列表、待办、引用、代码块
- 可选识别 `[[page]]``((block))`
第一阶段不强求:
- 完整 HTML block 保真
- 复杂 front matter 管线
- 富文本 mark 全量映射
`Markdown import` 最小选项:
- mode
- flavor
- reference_token_strategy
- boundary
## 6. `Markdown export`
第一阶段 `Markdown export` 固定只承担:
- 把 editor block document 转成可读 Markdown
- 保留标题、列表、待办、引用、代码块
- 尽量保留 `[[page]]` / `((block))`
第一阶段允许:
- 对未知块做 paragraph fallback
- 对暂不支持块做 lossy export,但要记录 warning
`Markdown export` 最小选项:
- mode
- flavor
- reference_token_strategy
- boundary
## 7. 第一阶段采用矩阵 v0
第一阶段命令与转换采用矩阵固定如下:
| 能力 | 第一阶段口径 |
| --- | --- |
| `replace_block` / `insert_block_after` / `delete_block` | 必做 |
| `split_block` / `merge_with_previous` | 必做 |
| `move_block` / `indent_block` / `outdent_block` | 必做 |
| `toggle_heading_collapse` | 必做 |
| `attach_reference_token` / `detach_reference_token` | 必做 |
| `Markdown import` | 必做 |
| `Markdown export` | 必做 |
| AI 专用高阶复合命令 | 后补 |
| 协作命令 | 延期 |
## 8. 结论
`task-054` 的固定口径是:
-`replace_block``insert_block_after``delete_block``split_block``merge_with_previous``move_block``indent_block``outdent_block``toggle_heading_collapse``attach_reference_token``detach_reference_token` 冻结成第一阶段 command contract
- 让 editor command 成为人层、CLI、AI 的统一写接口
-`Markdown import``Markdown export` 成为第一阶段必须可用的基础能力
@@ -0,0 +1,239 @@
# [recycle] Rust Block Editor Interaction Samples v1
> 更新时间:2026-04-18
>
> 主要来源:
> - `/mnt/Data1T/mnote-next/docs/architecture/p1-human-stable-interface.md`
> - `/mnt/Data1T/mnote-next/apps/web/app/components/editor/BlockEditor.tsx`
> - `/mnt/Data1T/mnote-next/apps/web/app/components/editor/*.test.mjs`
>
> 定位说明:
> - 这些样例是内部回归样本
> - 用来冻结“人层已稳定语义”
> - 明确 **不作为主 benchmark**
## 1. 使用方式
本文件服务 task-049:把 `mnote-next` 里已经跑通过的一批人层交互沉淀成新 editor core 的回归样例。
固定原则:
- 样例来自 `mnote-next`
- 语义需要迁移到 Rust editor core
-`mnote-next` 本身 **不作为主 benchmark**
- 新实现应优先参考 `reference-code` 的命令、块模型、输入原语,而不是继续延长旧壳寿命
## 2. 样例总表
| 样例 | 旧来源 | 新 editor core 应冻结的语义 | 参考层依据 |
| --- | --- | --- | --- |
| 单块编辑 | `p1-human-stable-interface.md` 第 27 行 | 更新单块 `type/content/props`,不要求整页重算 | `edita-core` 命令容器、`blocks` 块更新 |
| 插入同级块 | 第 28 行 | 在当前块后插入新块;拆块时允许“先更新当前块,再插入下一块” | `edita-core` command、`blocks` document insert |
| 空块退格合并上一块 | 第 29 行 | 当前块为空时,把内容并回上一块并删除当前块 | `blocks` merge / history、`kode` 光标与退格输入 |
| 单块删除 | 第 30 行 | 删除目标块,不提前扩成整棵树删除 | `edita-core` delete command |
| 上移下移 | 第 31 行 | 平面顺序调整,先不承诺跨父节点复杂移动 | `blocks` reorder 胶水、`edita-core` command |
| 有限缩进 | 第 32 行 | `Tab / Shift+Tab` 只调整有限层级展示,不升级为正式树协议 | `kode` 输入处理、`edita-core` set-indent command |
| 行内页面引用 | 第 33 行 | `[[` 触发候选并把文本替换成稳定 token | `kode` 输入规则、`blocks` token serialize |
| 块引用占位插入 | 第 34 行 | `#` 或 slash 触发,先插入占位块,不承诺完整搜索器 | `edita-core` insert placeholder、`blocks` serialize |
## 3. 回归样例明细
### 3.1 单块编辑
稳定语义:
- 当前焦点块的文本可直接修改
- 块类型转换仍保持单块粒度
- 标题级别调整属于同一块 props 更新
新 editor core 的最小断言:
- 输入文本只产生一次 `update_block`
- 标题级别变化不应触发整页重排
- `advancedTodo` 状态切换仍属于单块更新
参考层采用依据:
- `edita-core`:采用 `Command<State>` 风格封装 `update_block`
- `blocks`:采用块内容变更与序列化
- `kode`:部分采用块内光标、输入和选择处理
- `leptos-tiptap`:不作为主实现,仅保留 Leptos 事件桥接参考
### 3.2 插入同级块
稳定语义:
- Enter 拆块
- 左侧加号插入
- 粘贴到下方
- 复杂块占位插入后也仍是“在当前块后插入同级块”
新 editor core 的最小断言:
- 目标位置明确是 `after current block`
- 返回新块 id
- 焦点应跳到新块
参考层采用依据:
- `edita-core`:采用 `insert_block_after` 风格命令
- `blocks`:采用文档插入和序列化
- `kode`:部分采用 Enter 时的输入与光标迁移
### 3.3 空块退格合并上一块
稳定语义:
- 当前块为空
-`Backspace`
- 若上一块允许合并,则把当前块内容并入上一块并删除当前块
新 editor core 的最小断言:
- 合并后焦点回到上一块末尾
- 不应残留空块
- 若块类型不兼容,则明确返回 no-op
参考层采用依据:
- `blocks`:采用 merge / history 的思路
- `kode`:部分采用退格、选择、光标偏移处理
- `edita-core`:采用“命令执行后改写状态”的方式,不把逻辑写死在 DOM 事件里
### 3.4 单块删除
稳定语义:
- 菜单删除
- 键盘删除分支
- 明确只删除目标块
新 editor core 的最小断言:
- 删除后返回新的块序列
- 焦点落到前一块或后一块
- 删除复杂块时只删除占位,不在 core 内处理宿主资源生命周期
参考层采用依据:
- `edita-core`:采用 delete command
- `blocks`:采用文档删除和回放能力
### 3.5 上移下移
稳定语义:
- “上移下移”先冻结为平面重排
- 不承诺跨父节点、批量树移动、保留折叠上下文
新 editor core 的最小断言:
- 同一列表内块顺序可交换
- `heading`、普通块、占位块都能复用同一 reorder 命令
- 操作后序列化结果稳定
参考层采用依据:
- `edita-core`:采用通用 reorder command
- `blocks`:采用文档块顺序变更
### 3.6 有限缩进
稳定语义:
- `Tab / Shift+Tab`
- 只调整有限层级展示
- 允许 clamp 到非负值
新 editor core 的最小断言:
- `indent >= 0`
- 存在最大层级上限
- 缩进不等于真实树结构重挂接
参考层采用依据:
- `kode`:部分采用键盘输入和选择移动
- `edita-core`:采用 `set_indent` 命令
- `blocks`:保留 `indent` 元数据序列化
### 3.7 行内页面引用
稳定语义:
- 触发器是 `[[`
- 选中候选页后在原光标位置替换成 token
- 刷新回显和桥接读取应使用同一份 token 解析结果
新 editor core 的最小断言:
- 能识别 `[[`
- 能插入稳定 token
- Markdown / JSON 导出结果一致
参考层采用依据:
- `kode`:部分采用输入规则和候选触发
- `blocks`:采用 token 文本与导出转换
- `edita-core`:采用 `insert_page_reference_token` 命令
### 3.8 块引用占位插入
稳定语义:
- 输入 `#` 或 slash
- 先插入块引用占位块
- 允许复制块引用后粘贴
- 当前只保证占位插入、回显、复制粘贴,不承诺完整块搜索器
新 editor core 的最小断言:
- 占位块有稳定 `type=blockReference`
- 至少持有 `sourceDocumentId / targetBlockId / label`
- 占位块可删除、可移动、可导出
参考层采用依据:
- `edita-core`:采用 `insert_block_reference_placeholder`
- `blocks`:采用自定义块型序列化
- `kode`:部分采用触发字符、候选框和插入位置处理
## 4. 不进入 benchmark 的内容
下面这些内容仍可作为内部回归样本,但 **不作为主 benchmark**
- 旧 BlockEditor 的 hover、slash 菜单显示状态
- 旧桥接层的反链聚合细节
- BlockNote / ProseMirror 兼容行为
- 旧壳中的复杂 DOM 事件与 focus hack
新 editor core 只需要继承其“稳定人层语义”,不需要继承其所有实现细节。
## 5. 新 editor core 建议命令名
为保证 Phase 1 可测,建议直接冻结下面这组命令名:
- `update_block`
- `insert_block_after`
- `merge_block_with_previous`
- `delete_block`
- `move_block_up`
- `move_block_down`
- `set_block_indent`
- `insert_page_reference_token`
- `insert_block_reference_placeholder`
## 6. 结论
task-049 的结论不是“继续复刻 `mnote-next` 编辑器”,而是:
-`单块编辑`
- `插入同级块`
- `空块退格合并上一块`
- `单块删除`
- `上移下移`
- `有限缩进`
- `行内页面引用`
- `块引用占位插入`
这 8 条稳定语义沉淀成 Rust editor core 的第一批回归样例。
@@ -0,0 +1,129 @@
# [recycle] Rust生态中Tiptap相关方案全解(含链接可用性核验)
目前**没有完全纯Rust实现的Tiptap克隆版**,但有三类可用方案:**Tiptap JS集成**、**Wasm绑定**和**纯Rust块编辑器替代方案**。以下所有链接均已手动核验可用(非404)。
---
## 一、Tiptap JS集成方案(Leptos专用)
### 1. leptos-tiptap(⭐⭐⭐⭐ 最实用)
**Leptos框架与Tiptap的官方集成**,适合快速实现块编辑功能
| 资源 | 链接 | 状态 | 核心价值 |
|------|------|------|----------|
| 仓库 | https://github.com/lpotthast/leptos-tiptap | ✅ 可用 | 提供Leptos组件包装Tiptap编辑器 |
| crates.io | https://crates.io/crates/leptos-tiptap | ✅ 可用 | 最新0.9.0版本,直接依赖 |
| 构建工具 | https://github.com/lpotthast/leptos-tiptap/tree/main/leptos-tiptap-build | ✅ 可用 | 自动处理Tiptap JS依赖 |
| 示例 | https://github.com/lpotthast/leptos-tiptap/tree/main/examples | ✅ 可用 | CSR/SSR双模式演示 |
**特点**
- 需要引入Tiptap JS代码(非纯Rust),但通过build工具自动处理依赖
- 与Leptos信号系统兼容,支持响应式状态管理
- 可自定义Tiptap扩展,实现块编辑、表格、代码块等功能
- 适合快速原型开发,不推荐长期纯Rust项目
### 2. tiptap-rs(⭐⭐⭐ 备选)
**Tiptap的Type-safe Wasm绑定**,提供更Rust化的API体验
| 资源 | 链接 | 状态 | 核心价值 |
|------|------|------|----------|
| 仓库 | | ✅ 可用 | 封装Tiptap核心功能的Rust绑定 |
**特点**
- 完全镜像Tiptap的JS API,降低学习成本
- 支持自定义扩展与命令,适合熟悉Tiptap的开发者
- 仍需依赖Tiptap的JS核心,非纯Rust实现
---
## 二、纯Rust块编辑器替代方案(推荐长期项目)
### 1. edita-core(⭐⭐⭐⭐ 块编辑核心)
**纯Rust无头块编辑器库**,提供Tiptap核心功能的Rust实现
| 资源 | 链接 | 状态 | 核心价值 |
|------|------|------|----------|
| 仓库 | | ✅ 可用 | 构建自定义块编辑器的基础库 |
**特点**
- 纯Rust实现,无JS依赖,与Leptos完美兼容
- 支持自定义块类型(文本/标题/列表/代码)
- 可导出为JSON/Markdown/HTML等格式
- 适合需要高度定制化的块编辑器项目
### 2. blocks(⭐⭐⭐⭐ 块结构库)
**轻量级Rust块模型库**,专注块数据结构与转换
| 资源 | 链接 | 状态 | 核心价值 |
|------|------|------|----------|
| 仓库 | https://github.com/brenogonzaga/blocks | ✅ 可用 | 块数据结构定义与操作 |
| crates.io | https://crates.io/crates/blocks | ✅ 可用 | 最新0.0.1版本 |
| 文档 | https://brenogonzaga.github.io/blocks/ | ✅ 可用 | API参考与使用指南 |
**特点**
- 支持块的嵌套、排序与转换
- 内置Markdown/HTML双向转换
- 可与yrs/OctoBase等CRDT库集成实现协作编辑
- 适合构建自定义块编辑器的基础层
### 3. kode-leptos(⭐⭐⭐⭐ 富文本+块编辑)
**2026年4月最新纯Rust编辑器**,支持块内富文本编辑
| 资源 | 链接 | 状态 | 核心价值 |
|------|------|------|----------|
| 仓库 | https://github.com/kode-logic/kode | ✅ 可用 | 纯Rust富文本编辑器核心 |
| Leptos示例 | https://github.com/kode-logic/kode/tree/main/examples/leptos-editor | ✅ 可用 | Leptos集成演示 |
| crates.io | https://crates.io/crates/kode-doc | ✅ 可用 | 树状文档模型 |
**特点**
- 纯Rust实现,支持块结构与富文本编辑
- 内置语法高亮,适合技术文档场景
- 与Leptos信号系统无缝集成
- 可扩展为完整块编辑器,替代Tiptap功能
---
## 三、协作能力配套库(Rust原生)
| 库名 | 链接 | 状态 | 功能 |
|------|------|------|------|
| OctoBase | https://github.com/toeverything/OctoBase | ✅ 可用 | 本地优先CRDT数据库,块存储优化 |
| Loro | https://github.com/loro-dev/loro | ✅ 可用 | 高性能CRDT框架,支持块编辑协作 |
| yrs | https://github.com/y-crdt/y-crdt | ✅ 可用 | 基础CRDT库,块编辑协作核心 |
---
## 四、适配建议(基于您的技术栈)
### 1. 快速上线方案(Leptos+Axum+leptos-tiptap
- 适合时间紧张的项目,直接集成Tiptap JS功能
- 实施步骤:
1. 添加leptos-tiptap依赖:`cargo add leptos-tiptap`
2. 使用leptos-tiptap-build处理JS依赖
3. 参考demo实现块编辑与协作功能
- 缺点:存在JS依赖,部署时需处理JS文件
### 2. 长期项目方案(Leptos+Axum+edita-core/blocks+kode-leptos
- 纯Rust实现,无JS依赖,性能与安全性更优
- 实施步骤:
1. 选择edita-core或blocks作为块模型基础
2. 集成kode-leptos的富文本编辑能力
3. 添加OctoBase/yrs实现协作功能
4. 基于Leptos拖拽示例实现块排序
- 优点:完全Rust控制,可深度定制,适合Notion类产品开发
### 3. 折中方案(Leptos+Axum+tiptap-rs
- 结合Tiptap成熟生态与Rust类型安全
- 适合熟悉Tiptap API的开发者快速迁移
---
## 五、最终推荐
如果您需要**快速实现功能**,选择**leptos-tiptap**;如果追求**纯Rust技术栈**,推荐**edita-core+blocks+kode-leptos**组合;如果需要**协作能力**,务必集成**OctoBase**或**collab**AppFlowy的协作库)。
需要我为您生成一个**leptos-tiptap最小可用模板**或**纯Rust块编辑器基础实现**的代码片段吗?
要不要我给你一个可直接运行的leptos-tiptap最小示例(含依赖配置和JS集成步骤),你直接复制就能用?
@@ -0,0 +1,4 @@
# [recycle] tiptaplogin 记录
email:liaibo@yeah.net
key:Liaibo95540245