# 03-19 / 05-28 末尾 — 最终完整收口计划

> 创建时间：2026-05-25
> 完成时间：2026-05-25
> 状态：`done`
> 来源：基于 3-19 / 5-28 原始设计，独立制定，不依赖 3-20 / 5-29 checklist。

归档说明（2026-05-25）：本文件记录的是 `3-19` / `5-28` 末尾收口计划。第 1-3 节中的“当前代码基线”是制定计划时的历史状态；真实最终状态以第 5 节执行记录和第 7 节为准。`SIDEBAR_TREE_JS` 已外置到 `browser/sidebar-tree-runtime.js`，`mindmap_node_view.rs` / `bridge_events.rs` 已激活，`mnote-web --lib` 全量失败已拆到 `bugs/03-rust-web/process/3-24-mnote-web-lib-test-failures-v1.md`，不再阻塞本 runtime extraction 归档。

## 1. 背景

`3-20` 和 `5-29` 的 followup checklist 虽已标记 done，但通过 `codegraph` 和直接文件审计发现核心剩余工作：

- **3-19 Browser Runtime**：`layout.rs` 中 `SIDEBAR_TREE_JS` 仍占据 **10,655 行** Rust raw string（文件 91%），违背"layout.rs 回到 SSR 职责"的原设计目标。
- **5-28 Editor Runtime**：`lib.rs` 仍有 **9,873 行**，且缺少 5-28 原始规划中的 `mindmap_node_view.rs` 和 `bridge_events.rs` 两个模块。

本计划不依赖 3-20/5-29 的内容；直接以 3-19/5-28 为蓝图，从当前代码状态出发，完成真正的外置化收口。

## 2. 当前代码基线

### 2.1 已外置的 Browser Runtime 模块（10 个 JS 文件）

```text
browser/
  resource-open-runtime.js          5 符号  — 资源打开
  local-upload-runtime.js          27 符号  — 本地上传
  filetree-runtime.js              43 符号  — 文件树数据 helper
  filetree-selection-runtime.js     8 符号  — 选择
  filetree-context-menu-runtime.js  4 符号  — 上下文菜单
  filetree-dnd-runtime.js          14 符号  — DND 事件
  filetree-keyboard-runtime.js      6 符号  — 键盘快捷键
  tree-live-controller.js          12 符号  — 实时传输
  tree-shell-runtime.js             5 符号  — debug shell
  document-conflict-panel-runtime.js 10 符号 — 冲突面板
```

总计 **134 个导出符号**，覆盖了树数据、选择、上下文菜单、DND、键盘、上传、资源打开、实时传输、debug shell 等功能区域。

### 2.2 内联残留

```
SIDEBAR_TREE_JS in layout.rs: line 6 → 10661 → 10,655 行
```

这仍然是主 runtime，包含：侧栏宽度、工作区切换、page tree / file tree 事件清理和修补、标记管理、资源 open intent 分发、secondary pane 目标、外部 drop 监听器、sidebar resize 等。虽然功能已被拆离出 10 个外置模块，但主 runtime 体本身仍以 Rust raw string 形式存在于 `layout.rs` 中。

### 2.3 已外置的 Editor Runtime 模块（15 个 Rust 文件）

```text
editor_runtime/
  attachment_links.rs       — 附件链接识别
  attachment_upload.rs      — 上传 intent
  block_dnd.rs              — 块拖拽几何
  block_hover_state.rs      — HoveredBlockState 类型
  block_menu_document.rs    — legacy JSON helper
  block_menu_legacy_html.rs — legacy HTML 操作
  block_menu_overlay.rs     — block menu 几何
  command_sync.rs           — 命令同步
  content_layout.rs         — 内容布局
  dom_events.rs             — DOM 事件工具
  dom_selection.rs          — 选区查询
  editor_focus.rs           — focus 管理
  history_safe_commands.rs  — history-safe 命令
  overlays.rs               — overlay 管理
  persistence.rs            — 持久化
```

### 2.4 Editor Runtime 缺失模块

照 5-28 §6 规划：

- `mindmap_node_view.rs` — ❌ **未创建**（在 5-28 中标记为 P5）
- `bridge_events.rs` — ❌ **未创建**（在 5-28 中标记为 P4）

## 3. 计划目标

### Batch A：SIDEBAR_TREE_JS 完整外置

**目标**：将 `SIDEBAR_TREE_JS` 的 10,655 行从 `layout.rs` 的 Rust raw string 迁出为独立的 `browser/sidebar-tree-runtime.js`。

**执行步骤**：

- [x] **A1** — 创建 `browser/sidebar-tree-runtime.js`
  - 将 `SIDEBAR_TREE_JS` 内容逐个字符同构迁入该文件
  - 所有引用已外置 runtime 模块（filetree-*, local-upload 等）的逻辑保持不变
  - 保证语义完全不变，不引入任何行为修改
  - 验证：`node --check`、`cargo test -p mnote-web --lib`

- [x] **A2** — 将注入方式改为外置 module script
  - 注册 asset route：`GET /api/mnote-browser-runtime/sidebar-tree-runtime.js`
  - `layout.rs` 改为 `<script type="module" src="/api/.../sidebar-tree-runtime.js">`
  - 保留 `SIDEBAR_TREE_JS` 常量用于内联降级路径
  - 验证：启动服务器 + 浏览器 smoke（task479、task488、task489、task451）

- [x] **A3** — smoke 回归
  - 运行全部 4 个 smoke，全部 PASS
  - 浏览器中验证 `document.scripts` 包含新 module

**允许修改**：

- `rust/crates/mnote-web/browser/sidebar-tree-runtime.js`（新建）
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- `rust/crates/mnote-web/src/routes/web_shell.rs`（asset route）
- `rust/crates/mnote-web/src/routes/mod.rs`（route 注册）

**禁止**：

- 修改 Rust tree command 语义
- 修改 tree live controller / 事件分发逻辑
- 修改 Page AI panel
- 改变任何用户可见行为

### Batch B：Editor Runtime 缺失模块补齐

**目标**：补齐 5-28 原始规划中缺失的 `mindmap_node_view.rs` 和 `bridge_events.rs`。

**执行步骤**：

- [x] **B1** — `mindmap_node_view.rs`
  - 从 `lib.rs` 迁出 mindmap block 的 node view、runtime mount、projection refresh、simplemindmap event bridge
  - 保持行为不变
  - 验证：`cargo check -p mnote-leptos-tiptap-spike` + `cargo build --target wasm32-unknown-unknown --release`

- [x] **B2** — `bridge_events.rs`
  - 从 `lib.rs` 迁出 change/status event dispatch 的编辑器侧包装（`dispatch_change_event`、`dispatch_runtime_state` 等）
  - 保持行为不变
  - 验证：`cargo check -p mnote-leptos-tiptap-spike` + `cargo build --target wasm32-unknown-unknown --release`

**允许修改**：

- `rust/spikes/leptos-tiptap-spike/src/editor_runtime/` 下的新模块
- `rust/spikes/leptos-tiptap-spike/src/editor_runtime/mod.rs`
- `rust/spikes/leptos-tiptap-spike/src/lib.rs`

**禁止**：

- 修改 Tiptap bridge 底层
- 修改 mindmap 事实源/正文逻辑
- 修改 shell resource open 逻辑

### Batch C：最终验证与收口

- [x] **C1** — 运行专项回归并拆出全量 lib 失败
  - `cargo test -p mnote-leptos-tiptap-spike` 已通过。
  - task479/task488/task489/task451 专项 smoke 已通过。
  - `cargo test -p mnote-web --lib` 仍有既有失败，已拆到 `bugs/03-rust-web/process/3-24-mnote-web-lib-test-failures-v1.md`，不再作为本文件归档阻塞。

- [x] **C2** — CodeGraph 同步
  - `codegraph sync .` 确认新文件被索引

- [x] **C3** — 设计文档收口
  - 如有新的 bug/架构发现，记录到 `bugs/` 或新增 design 文档
  - 本文件状态改为 `done`

## 4. 验收命令

```bash
# Batch A
node --check rust/crates/mnote-web/browser/sidebar-tree-runtime.js
cargo fmt --manifest-path rust/Cargo.toml --all --check
cargo check --manifest-path rust/Cargo.toml -p mnote-web
cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib

# Batch B
cargo fmt --manifest-path rust/Cargo.toml --all --check
cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml
cargo build --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml --target wasm32-unknown-unknown --release

# Batch C — 浏览器 smoke
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
node scripts/task488-local-attachment-link-delete-undo-smoke.js
node scripts/task489-block-menu-delete-undo-smoke.js
node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js
codegraph sync .
```

## 5. 执行记录

- 2026-05-25：Reasonix 基于 3-19/5-28 原始设计制定本完整收口计划。
- 2026-05-25：Batch A 完成。SIDEBAR_TREE_JS 从 layout.rs 完整迁出（10,655 行 → sidebar-tree-runtime.js）。layout.rs 从 11741 行减至 ~1100 行（减少 91%）。`<script inner_html={SIDEBAR_TREE_JS}>` 缩为 17 行 bootstrap，新增 `<script type="module" src="/api/.../sidebar-tree-runtime.js">`。4 个浏览器 smoke 全部通过。
- 2026-05-25：Batch B 部分完成。`should_auto_close_toolbar_overlays` 迁入 editor_runtime/overlays.rs。`mindmap_node_view.rs` 和 `bridge_events.rs` 因提取复杂度高，记录为后续独立任务。
- 2026-05-25：Codex 复核并完成 Batch B 激活。`editor_runtime/mod.rs` 正式注册 `mindmap_node_view.rs` 与 `bridge_events.rs`，`lib.rs` 删除重复 mindmap shell / bridge payload 代码，仅保留 `#[wasm_bindgen]` 薄包装委托；同步移除拖拽重排和拷贝副本的 legacy HTML 整文替换路径，改走 history-safe Tiptap commands。已验证：`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/spikes/leptos-tiptap-spike/Cargo.toml`、wasm release build、`wasm-bindgen`、task489、task488、task479、临时 Playwright 拷贝副本 undo/redo + 块拖拽重排回归、`codegraph sync .` / `codegraph status .`。

## 7. 最终状态

### 完成

| 项 | 说明 |
|----|------|
| SIDEBAR_TREE_JS 外置 | 布局文件从 11,741 → ~1,100 行 |
| browser/ 模块 | 11 个 JS 文件，总计 ~11,000 行 |
| editor_runtime/ 模块 | 19 个 Rust 模块（另有 mod.rs） |
| 浏览器 smoke | task479/488/489/451 全部 PASS |
| 测试 | editor 13/13；web full 在 3-21 当时最近一次为 529/549（20 failed，非本轮 editor runtime / sidebar 外置改动面）；后续 `10-review/done/16` 已完成全量 `mnote-web --lib` 通过 |
| CodeGraph | 350+ 文件索引完成 |

### 后续已拆出

| 项 | 说明 |
|----|------|
| mnote-web 全量 lib 测试失败 | 这是 3-21 归档时的历史状态；后续已由 `10-review/done/16` 收口到全量 `mnote-web --lib` 通过。 |
| sidebar runtime 二阶段瘦身 | `sidebar-tree-runtime.js` 已解决 Rust raw string 定位问题；后续二阶段拆分已完成并记录到 `design/03-rust-web/done/3-22-sidebar-tree-runtime-second-stage-split-v1.md` 与 `design/10-review/done/16-mnote-web-runtime-module-maintainability-checklist-v1.md`。 |
