refactor: move block delete to history-safe commands

This commit is contained in:
lix-2026
2026-05-24 23:56:25 +08:00
parent 437c690a39
commit f2a7de9896
6 changed files with 343 additions and 76 deletions
@@ -1,7 +1,7 @@
# 5-28 Leptos Tiptap 编辑器 Runtime 模块拆分 v1
> 创建时间:2026-05-24
> 状态:`process`
> 状态:`done`
>
> 触发背景:
> - `bugs/0524.md` 第 4 条删除本地 Markdown 附件链接后 `Ctrl+Z/Ctrl+Y` 不可用,根因是编辑器删除路径绕过 Tiptap/ProseMirror history。
@@ -102,6 +102,7 @@ rust/spikes/leptos-tiptap-spike/src/
- `sync_persisted_editor_command` -> `command_sync.rs`
- `apply_html_update` / `apply_document_update` -> `command_sync.rs`,并标记 history 风险
- `delete_top_level_block_html` / `reorder_top_level_block_html` -> 待替换为 `history_safe_commands.rs`
- 2026-05-24`delete_top_level_block_html` 已删除,块菜单删除改走 `delete_top_level_block_with_history(...)` -> Tiptap `delete_range` / `insert_content_at`
- upload slash action / local asset path -> `attachment_upload.rs` / `local_markdown_paths.rs`
- toolbar/block menu DOM anchor -> `overlays.rs` / `block_menu.rs`
- [ ] 建立 history 风险清单:所有用户编辑动作中仍调用 `set_content` 或 HTML 整文替换的路径必须登记。
@@ -134,7 +135,8 @@ rust/spikes/leptos-tiptap-spike/src/
- host 替换文档
- 外部同步/恢复
- 非用户编辑的兼容兜底
- [ ] 用户编辑动作必须优先走 Tiptap command/transaction。
- [x] 用户编辑动作必须优先走 Tiptap command/transaction。
- 2026-05-24:块菜单删除已从 `apply_html_update` / `set_content(TiptapContent::html(...))` 迁到 `history_safe_commands.rs``delete_top_level_block_with_history(...)`,并由 `scripts/task489-block-menu-delete-undo-smoke.js` 覆盖 Ctrl+Z/Ctrl+Y。
### P3:抽出附件上传与本地 Markdown 路径
@@ -391,6 +393,50 @@ Reasonix read-only audit
- `resolveEditorUploadContext``editorRootFromUploadOptions``openEditorUploadFilePicker` 等 shell-side DOM/context 探针适合进入 `03-rust-web``local-upload-runtime.js` 后续批次。
- `uploadFileToMediaAsset``uploadFilesWithResolvedTarget``insertUploadedAssetIntoEditor` 仍是高耦合主流程,暂不迁入 editor runtime。
### 10.5 Batch 4 块菜单删除 history 收口(2026-05-24
Codex 主控直接完成:
- 新增 `scripts/task489-block-menu-delete-undo-smoke.js`,先在旧实现上确认 RED:块菜单删除 `Beta block` 后,`Ctrl+Z` 未恢复目标块,失败证据写入 `tmp/task489-block-menu-delete-undo-smoke/result.json`
- `editor_runtime/history_safe_commands.rs` 新增 `delete_top_level_block_with_history(editor, index)`
- 多块文档:计算顶层块完整 ProseMirror range 后调用 Tiptap `delete_range(...)`
- 单块文档:用 `insert_content_at(range, paragraph, update_selection=true)` 替换为空段落。
- 删除完成后仅调用 `sync_persisted_editor_command(...)` 同步快照和草稿,不再走 `apply_html_update`
- 删除 `src/lib.rs` 中旧的 `delete_top_level_block_html(...)`,避免块菜单删除继续保留 HTML 整文替换入口。
Reasonix read-only audit
- run id: `reasonix-2026-05-24T15-45-44-865Z-bb0ede13`
- handoff: `/home/lix/.codex/runtime/reasonix-coding-worker/reasonix-2026-05-24T15-45-44-865Z-bb0ede13/process-handoff.md`
- 类型:只读审计,主控已读取 Hindsight recall、`process-handoff.md``process-handoff.json`
- 采纳结论:可采纳;块菜单删除已从 `apply_html_update(...)` / `set_content(...)` 路径迁到 Tiptap `delete_range(...)` / `insert_content_at(...)`。Reasonix 提醒 `sync_persisted_editor_command(...)` 与 Tiptap `on_change` 后续可能存在 dirty count / localStorage 冗余,作为后续优化风险登记,不阻塞本批。
已通过验证:
- `node --check scripts/task489-block-menu-delete-undo-smoke.js`
- `cargo fmt --manifest-path rust/Cargo.toml --all --check`
- `cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`35 个既有 warning
- `cargo build --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml --target wasm32-unknown-unknown --release`35 个既有 warning
- `wasm-bindgen rust/spikes/leptos-tiptap-spike/target/wasm32-unknown-unknown/release/mnote_leptos_tiptap_spike.wasm --target web --out-dir rust/spikes/leptos-tiptap-spike/generated/island --out-name mnote-leptos-tiptap-spike-island`
- `node scripts/task489-block-menu-delete-undo-smoke.js`
- 结果:`ok=true`
- 证据:`tmp/task489-block-menu-delete-undo-smoke/result.json``tmp/task489-block-menu-delete-undo-smoke/after-redo.png`
- `node scripts/task488-local-attachment-link-delete-undo-smoke.js`
- 结果:`ok=true`
- 证据:`tmp/task488-local-attachment-link-delete-undo-smoke/result.json`
- `node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`
- 结果:`ok=true`
- 证据:命令 stdout JSON,覆盖上传目录、文件树 drop、broken link、无 active document 附件 tab、标题来源、附件键盘/手柄删除。
- smoke 启动方式:
- 使用稳定入口 `MNOTE_WEB_BIND=0.0.0.0:3000 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3000 cargo run --manifest-path rust/Cargo.toml -p mnote-web --bin mnote-web`
- `npm run dev:hot` / `cargo watch` 在本轮验收中发生过重编译重启,导致 `task479` 中段 `ERR_CONNECTION_REFUSED`;该结果已废弃,不作为功能失败证据。
- `codegraph search delete_top_level_block_with_history top_level_block_full_range prosemirror_node_size`
- 结果:`delete_top_level_block_with_history``top_level_block_full_range``prosemirror_node_size` 可定位到 `rust/spikes/leptos-tiptap-spike/src/editor_runtime/history_safe_commands.rs`
剩余风险:
- 块手柄拖拽重排和复制副本仍使用 HTML 整文替换,继续登记为后续 history-safe command 切片;本设计 done gate 只要求至少一个已登记用户编辑路径完成迁移。
## 11. 测试与验收
### Rust 检查
@@ -406,6 +452,7 @@ wasm-bindgen rust/spikes/leptos-tiptap-spike/target/wasm32-unknown-unknown/relea
```bash
node scripts/task488-local-attachment-link-delete-undo-smoke.js
node scripts/task489-block-menu-delete-undo-smoke.js
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
node scripts/task472-side-target-secondary-pane-smoke.js
```
@@ -427,12 +474,12 @@ node scripts/task472-side-target-secondary-pane-smoke.js
本设计移动到 `done/` 前必须满足:
- [ ] `attachment_links.rs``history_safe_commands.rs` 已落地。
- [ ] `command_sync.rs` 已落地,用户编辑动作与 host 替换动作边界明确。
- [ ] 至少一个仍使用 `set_content` 的用户编辑路径已迁到 Tiptap command/transaction。
- [ ] `task488` 通过,且至少覆盖一次真实键盘 `Ctrl+Z/Ctrl+Y`
- [ ] 新增 editor_runtime Rust 模块能被 CodeGraph 定位。
- [ ] `src/lib.rs` 不再新增附件/history/DOM bridge 大块逻辑。
- [x] `attachment_links.rs``history_safe_commands.rs` 已落地。
- [x] `command_sync.rs` 已落地,用户编辑动作与 host 替换动作边界明确。
- [x] 至少一个仍使用 `set_content` 的用户编辑路径已迁到 Tiptap command/transaction。
- [x] `task488` 通过,且至少覆盖一次真实键盘 `Ctrl+Z/Ctrl+Y`
- [x] 新增 editor_runtime Rust 模块能被 CodeGraph 定位。
- [x] `src/lib.rs` 不再新增附件/history/DOM bridge 大块逻辑。
## 14. 后续问题