# 4-2 [done] Sidebar / 页面树 / 文件树 产品交互合同 v1

> 更新时间：2026-04-17
>
> 关联文档：
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md`
> - `/mnt/Data1T/mnote/design/old/04-tree-domain/process/4-1-sidebar-pagetree-filetree-product-gap-analysis-v1.md`
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`

## 1. 文档目的

这份文档不是继续讨论“是否要做 Rust tree shell”。

这份文档要冻结的是：

- 页面树对标 Wolai / Notion 的最小产品交互合同
- 文件树对标 VS Code Explorer 的最小产品交互合同
- 当前旧树已经具备的能力基线
- 新 Rust tree shell 必须补齐的能力矩阵
- 后续 `projection / command / row model / selection model / focus model / keyboard / DnD / context menu` 的最低验收口径

也就是说，这份文档是下一阶段多人并行推进时的共同合同，而不是描述性分析。

---

## 2. 基本原则

- 页面树 / 文件树都不是事实源，它们都只是 `tree-first graph kernel` 的 projection。
- Sidebar 是壳，不是树真相。
- 新实现不能以“能显示树结构”作为完成标准，而要以“不比旧交互与 UI 差”作为最低标准。
- 文件树与页面树允许在 UI 上不同，但必须共享同一套 projection 与 command 主骨架。
- 所有新增能力都应优先落到可测试的 `row model / selection model / focus model / keyboard / DnD` 层，而不是先堆散落 UI 事件。

---

## 3. 页面树合同

### 3.1 对标目标

- 功能对标：Wolai / Notion 页面树
- 视觉与节奏对标：轻量、低干扰、hover 才显动作、不是调试面板

### 3.2 页面树必须具备的最低能力

- 稳定的页面层级展开 / 折叠
- 当前页高亮与祖先自动展开
- 行级 hover 动作区
- 新建子页面
- 重命名
- 页面移动
- 上下文菜单入口
- 焦点与键盘导航
- 基础拖拽排序
- 搜索过滤后仍保持树层级可理解

### 3.3 页面树必须保留的旧能力基线

- 右键菜单不是只有重命名/删除，而应保留工作区级高频动作入口
- 页面树不能退化成纯按钮列表
- 大树场景不能因切流而失去稳定滚动体验
- 主树 consumer 不能重新持有第二套结构真相

---

## 4. 文件树合同

### 4.1 对标目标

- 功能对标：VS Code Explorer
- 视觉密度对标：资源管理器，而不是文档树换皮

### 4.2 文件树必须具备的最低能力

- 文件树专用 row model
- 页面、`index.md`、附件、mindmap 文件夹、导图子附件的复合资源层级
- 单选
- 多选
- Shift 范围选
- 右键菜单入口
- 双击打开资源
- 基础键盘导航
- 目录拖拽骨架
- 外部文件拖入上传骨架
- 资源图标语义
- 资源类型菜单分支

### 4.3 文件树必须保留的旧能力基线

- 不能退化成“页面树加附件列表”
- 不能丢掉多选与范围选
- 不能丢掉资产级操作入口
- 不能丢掉内部拖拽/复制与外部文件拖入的扩展空间
- mindmap 相关资源不能被拍平成普通附件列表

---

## 5. 统一协议合同

### 5.1 Projection 合同

所有树 consumer 必须能明确声明自己消费哪一种 projection：

- `sidebar_tree`
- `page_tree`
- `file_tree`

最小字段基线：

- `row_id`
- `node_id`
- `parent_node_id`
- `node_type`
- `projection_kind`
- `depth`
- `position`
- `title`
- `capabilities`
- `resource_meta`

文件树扩展字段：

- `resource_kind`
- `asset_kind`
- `icon_hint`
- `expandable`
- `expanded_by_default`

### 5.2 Command 合同

命令面至少要为后续产品交互预留稳定口径：

- `tree.node.create`
- `tree.node.rename`
- `tree.subtree.move`
- `tree.node.archive`
- `tree.node.restore`
- `tree.asset.attach`
- `tree.asset.detach`

### 5.3 本地 UI 状态合同

以下状态不应回流为结构真相，只能留在 UI 本地状态层：

- `expanded`
- `selected`
- `hover`
- `focus`
- `dragging`
- `drop target`

---

## 6. 状态骨架合同

### 6.1 row model

必须单独存在，不能散落在 renderer 中。

最低要求：

- 页面树与文件树都能从 projection 映射到稳定 row model
- row model 可以独立测试
- row model 不再重新定义结构真相

### 6.2 selection model

最低要求：

- 单选
- 多选
- Shift 范围选
- 右键选中
- 可见行变化后的选择归一化

### 6.3 focus model

最低要求：

- 当前焦点行稳定可追踪
- 焦点与选中不完全等价
- 页面树与文件树都能共享焦点层定义

### 6.4 keyboard

最低要求：

- 上下导航
- 左右展开/折叠
- Enter 打开
- 文件树预留 copy / paste / delete 的按键入口

### 6.5 DnD

最低要求：

- 页面树支持基础排序/移动
- 文件树支持目录拖放骨架
- 文件树支持外部文件拖入的扩展点
- 非法投放校验必须可测试

### 6.6 context menu

最低要求：

- 页面树与文件树都有统一的 context menu 入口模型
- 菜单本身可按资源类型分支
- shell 与宿主之间要有稳定动作协议，而不是只靠壳内 `prompt/alert`

---

## 7. 当前判定

截至 2026-04-28 final DOM shell 收口后：

- `page_tree`：默认主路径已进入 `rust_wasm_dom_shell_host + dom_wasm`，展开/折叠、当前页高亮、祖先展开、行级动作、重命名、移动、上下文菜单、键盘与基础拖拽均由 Rust runtime contract 驱动或回放。
- `picker`：轻量选择器已复用同一 renderer state family，键盘高亮、Enter 选中、根节点与排除项逻辑已进入默认 DOM host。
- `file_tree`：默认主路径已进入 `rust_wasm_dom_shell_host + dom_wasm`，页面、`index.md`、附件、`mindmap` 文件夹、资源图标、单选/多选/范围选、右键、双击打开、内部拖放与外部文件拖入均保留。

本合同的产品交互最低门槛已由 `4-11` 与 `4-18` 收口；剩余 `normalizedMove` fallback、`document.snapshot.saved` 独立事件、`tree.node.embed pageReference` 等属于后续 Page Aggregate / runtime 深层收口，不再阻塞本文。

- `filetree` 模式闭环已由 Rust projection、runtime state family、preflight 与默认 DOM host 共同承接。
- 页面树 / 文件树正式交互合同已固定，并由 `task112/task113` smoke 与组件/Rust 测试覆盖。
- row model / selection model / focus model / keyboard / DnD / context menu 骨架已进入 renderer state family 与 runtime facade。

---

## 8. 完成判定

只有同时满足下面几条，才可以宣称“新树不比旧交互与 UI 差”：

- 页面树满足本合同第 3 节最低能力
- 文件树满足本合同第 4 节最低能力
- projection / command 合同不再漂移
- `row model / selection model / focus model / keyboard / DnD / context menu` 有独立实现与测试
- filetree 不再依赖协议裂缝或壳内临时拼装来维持主路径
