# [recycle] 4-12 [done] task-003 page tree final renderer 执行清单 v1 > 口径修正(2026-04-28):本文件的 `done` 状态只表示 page tree 的 Rust initial DOM、hydration patch 与 state-family 阶段完成;不表示 `4-11` 所定义的 final Rust DOM shell 已完成。默认主路径仍依赖 `TreeShellIframeHost` / `iframe_srcdoc` 时,不得引用本文宣称 page tree final renderer 已最终收口。 > > 最终清理更新(2026-05-16):`TreeShellIframeHost` 与 `iframe_srcdoc` 已退出 active source 并移动到 `recycle/`;本文保留为历史 state-family 阶段记录,当前口径以 `4-18` / `4-19` 为准。 ## 目标 让 `page tree` 默认运行时从 inline compat JS 整树重绘路径继续收口到 Rust family renderer contract: - 首屏 DOM 继续由 `data-rust-page-renderer="initial_v1"` 输出。 - 焦点、active 高亮与键盘移动不再触发 `renderTree()` 整树重绘,只 patch 已 hydrate 的 Rust DOM row 状态。 - 展开/折叠不再把 Rust 初始 DOM 替换成 compat DOM;在已 hydrate Rust DOM 内 patch `aria-expanded`、toggle 文案与子树 fragment。 - `mnote-web /tree` debug shell 与 3000 inline host 保持同一可测试合同,避免默认路径和 debug 路径继续漂移。 ## 执行项 - [x] Rust `page_renderer` 初始 HTML 为有子节点的 page row 输出 `tree-node-toggle`,使 Rust DOM 自身具备展开/折叠交互锚点。 - [x] 3000 inline `buildInlinePageTreeHtml` 对齐 Rust 初始 HTML,避免 `TreeShellIframeHost` 继续生成缺少 toggle 的第二份 page DOM 合同。 - [x] 3000 inline runtime 增加 hydrated page DOM patch: - [x] `focusNode` 在 `usedRustInitialRenderer` 下只更新 `data-active / data-focused / tabIndex`。 - [x] `toggleExpand` 在 `usedRustInitialRenderer` 下只 patch 当前节点 `aria-expanded`、toggle 文案与子树显示/插入。 - [x] 新插入的 page 子树 row 复用同一 `bindPageRowEvents`,保留 open/create/rename/menu 事件。 - [x] `mnote-web /tree` debug runtime 对齐同一 page DOM patch 合同,保留 direct debug 验证边界。 - [x] 测试覆盖: - [x] `cargo test -p mnote-web page_renderer` - [x] `pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx` - [x] `node scripts/task112-tree-rust-family-regression-smoke.js` - [x] 本地 Convex 函数已通过 `npx convex dev --once --tail-logs disable --env-file ../.env.all --run ping:ping` 同步,避免 `documents.move` 运行旧 validator 拒绝 `normalizedMove`。 ## 非目标 - 本清单不把 file tree 与 picker 一并切 final renderer;它们对应 `task-004`、`task-005`。 - 本清单不恢复 `3104` 默认入口;`/tree` 仍只作为显式 debug/internal 验证边界。