# 3-22 Sidebar Tree Runtime 二阶段拆分 v1

> 创建时间：2026-05-25
> 完成时间：2026-05-25
> 状态：`done`
> 来源：承接 `3-19` / `3-20` / `3-21` 完成后的剩余规模问题。

## 1. 背景

`3-21` 已经解决原始痛点：CodeGraph 不能有效定位 `layout.rs` 内大型 Rust raw string。现在 `SIDEBAR_TREE_JS` 已外置为：

```text
rust/crates/mnote-web/browser/sidebar-tree-runtime.js
```

这让 CodeGraph、IDE、浏览器 stack trace 都能把它当作一等 JavaScript 文件处理。但该文件仍约 1 万行，职责仍包含 sidebar workspace、page tree、file tree、Page AI panel、local folder/mindmap observer、resource open intent、secondary pane 目标、外部 drop 和 resize 等多条链路。

因此二阶段目标不是再解决 raw string，而是把一个可索引的 JS 巨文件继续拆成可审计、可派发、可测试的小模块。

## 2. 原则

- 保持 Rust kernel / mnote-web projection / tree command 语义不变。
- 每一刀只迁一个边界清楚的 helper 或 runtime 簇。
- 不重写 sidebar，不改变用户可见行为。
- 不把 Page AI panel 混入 tree/filetree runtime；AI 相关大块应转入 `07-ai`。
- `layout.rs` 继续只承担 SSR/bootstrap 和 asset script 注入。

## 3. 推荐拆分顺序

### Batch A：Page AI panel 边界审计

- [x] 只读审计 `sidebar-tree-runtime.js` 中 Page AI panel 状态、消息、profile、skills、permission requests、gateway health 等代码块。
- [x] 判断是否新建 `design/07-ai/process/...` 承接，而不是继续放在 `03-rust-web`。
- [x] 本批不迁代码，只输出 owner 和最小 smoke 建议。

执行结果：Page AI panel 已拆到 `design/07-ai/process/7-38-page-ai-sidebar-runtime-owner-split-v1.md`，后续由 `07-ai` 承接。当前粗略搜索 `pageAi*` / `data-page-ai` / `PAGE_AI` 仍约 800 处，不能继续作为 tree/filetree runtime 的普通剩余项处理。

### Batch B：local folder / mindmap fetch observer

- [x] 只读审计 `installMindmapAssetFetchObserver`、local mindmap source 参数、fetch hook、sidebar snapshot refresh 的边界。
- [x] 只迁纯 URL/body/method/file name/helper 层以外仍适合外置的最小 runtime。
- [x] 将后续实现和 `task455-local-folder-mindmap-clean-smoke.js` / `task479-local-folder-markdown-resource-lifecycle-smoke.js` 验证要求拆到新的 owner 文档。

执行结果：本批不在 `3-22` 中继续迁代码，后续拆到 `design/03-rust-web/process/3-23-sidebar-local-folder-resource-runtime-followup-v1.md`。原因是剩余边界仍强依赖 fetch hook、projection render、workspace/root/document 状态和 selection/focus 恢复，不能作为 sidebar shell helper 同批处理。

### Batch C：sidebar workspace / tab / resize shell helper

- [x] 拆出 workspace sidebar collapse、tab restore、resize 相关的纯 DOM/state helper。
- [x] 不迁 page tree / filetree projection 语义。
- [x] 用轻量 DOM 单测或浏览器 smoke 证明侧栏首屏不回归。

执行结果：新增 `rust/crates/mnote-web/browser/sidebar-shell-runtime.js`，迁出 `toggleWorkspaceSidebar`、`installWorkspaceSidebarResizer`、`readStoredSidebarTreeMode`、`persistSidebarTreeMode`、`activeSidebarTreeMode`、`applySidebarTreeTab`、`switchSidebarTreeTab`、`restoreSidebarTreeTab`。`sidebar-tree-runtime.js` 仅保留薄 wrapper，未触碰 page tree / filetree projection 语义。

### Batch D：resource/filetree 编排剩余薄壳

- [x] 复核 `refreshLocalFolderSidebarSnapshot`、`appendUploadedAssetRow`、`resolveFileTreeUploadTarget` 目前有意保留在 shell 的原因是否仍成立。
- [x] 只迁稳定的 DOM helper，不迁 fetch、projection render、selection/focus 编排。
- [x] 继续用 `task479` 覆盖本地 Markdown 附件和文件树 drop 目标。

执行结果：后续拆到 `design/03-rust-web/process/3-23-sidebar-local-folder-resource-runtime-followup-v1.md`，本文件不继续承接 local folder/resource 编排。

## 4. 不建议的下一步

- 不建议直接整体重写 `sidebar-tree-runtime.js`。
- 不建议把 `mnote-web --lib` 全量失败作为本文件前置阻塞；该问题已拆到 `bugs/03-rust-web/process/3-24-mnote-web-lib-test-failures-v1.md`。
- 不建议为了 DRY 把 shell 业务编排强行迁入小 helper，尤其是需要 workspace/root/document/session 状态的函数。

## 5. 验收基线

每个实现批次至少选择相关命令：

```bash
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
NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task455-local-folder-mindmap-clean-smoke.js
```

涉及 AI panel 的批次应另走 `07-ai` 设计和页面 AI smoke。

## 6. 归档条件

- [x] `sidebar-tree-runtime.js` 的下一批高风险职责已有 owner，不再把 Page AI、local folder、filetree 编排混在一个无边界任务里。
- [x] 至少一个二阶段实现批次落地，且 smoke 通过。
- [x] 后续剩余项已经拆到对应 owner 文档，而不是继续堆在本文件。

## 7. 验证记录

- 2026-05-25：Batch C 落地 `sidebar-shell-runtime.js`。`sidebar-tree-runtime.js` 从 10,654 行降至 10,594 行，新模块 150 行。
- 2026-05-25：已验证 `node --check rust/crates/mnote-web/browser/sidebar-shell-runtime.js` 与 `node --check rust/crates/mnote-web/browser/sidebar-tree-runtime.js`。
- 2026-05-25：已验证 `cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`。
- 2026-05-25：已验证专项 Rust 单测：`cargo test --manifest-path rust/Cargo.toml -p mnote-web ssr::pages::layout::tests:: -- --test-threads=1`，38/38 通过；`cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::tests::mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`，1/1 通过。
- 2026-05-25：已启动当前源码临时服务 `MNOTE_WEB_BIND=127.0.0.1:3024 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3024 cargo run --manifest-path rust/Cargo.toml -p mnote-web`，并通过 `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3024 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。结果 `ok:true`，截图：`/tmp/mnote-task479-lifecycle-MZEmnG/screenshots-1779720559336/final-state.png`。
- 2026-05-25：Playwright 轻量断言通过：`window.__mnoteSidebarShellRuntime` 存在，页面同时加载 `sidebar-shell-runtime.js` 与 `sidebar-tree-runtime.js` module script，`data-mnote-sidebar-resizer-bound="true"`。
