# 4-7 [done] Tree Shell UI State Boundary v1 > 更新时间:2026-04-18 > > 目的:明确哪些状态属于 projection,哪些状态只能留在 tree shell / renderer 本地。 ## 1. 结论 树域必须严格区分: - projection truth - local UI state 否则 Rust projection、Leptos tree shell、Next 挂载切流会继续把状态缠回旧前端壳。 ## 2. 属于 projection 的状态 这些状态必须来自上游 projection 或 command 结果,而不是由 UI 自行猜测: - `nodeId` - `parentNodeId` - `projectionKind` - `nodeType` - `title` - `depth` - `position` - `childCount` - `expandable` - `expandedByDefault` - `capabilities` - `resourceMeta` - `iconHint` 说明: - projection 只描述“结构真相”和“允许做什么” - projection 不承载 hover、selected、dragging 等临时交互态 ## 3. 只能留在 UI 本地的状态 这些状态只能存在于 tree shell / renderer 本地: - `expanded` - `selected` - `hover` - `focus` - `dragging` - `drop target` - `context menu open` - `keyboard navigation anchor` 说明: - `expandedByDefault` 是 projection 建议值 - `expanded` 是本地会话态,不直接回写 projection ## 4. 边界规则 ### 4.1 `expanded` - 初始值可由 `expandedByDefault` 推导 - 运行期展开/折叠必须只保留在本地状态 - 不能把 `expanded` 反向写回 projection item ### 4.2 `selected` - `selected` 属于当前用户当前会话的局部状态 - 不能作为 projection 的共享字段 ### 4.3 `focus` - `focus` 属于 keyboard / accessibility 层本地状态 - 不能参与结构真相 ### 4.4 `dragging` / `drop target` - `dragging` 与 `drop target` 是瞬时状态 - 只能存在于 DnD 状态机 - drop 成功后,真正持久化的是 `tree.subtree.move` 等命令结果 ## 5. Rust 与前端共识 Rust 负责: - 输出 projection - 接收 command - 返回 command result / resync snapshot 前端或 Leptos shell 负责: - `expanded` - `selected` - `hover` - `focus` - `dragging` - `drop target` ## 6. 对 `page_tree` / `file_tree` / picker 的影响 - `page_tree`、`file_tree`、picker 必须共用同一套本地状态边界 - picker 只是交互能力更窄,不是另一套状态模型 - `keyboard`、`selection`、`focus`、`dragging` 的命名与语义必须一致 ## 7. 完成判定 当以下条件同时成立时,视为状态边界冻结完成: - 文档明确列出 projection 与本地状态 - Rust 和前端都不再把 `expanded/selected/focus/dragging/drop target` 写进 projection - 后续 Leptos shell 与 Next consumer 使用同一份边界说明