Files
mnote/design/04-tree-domain/done/4-4-tree-projection-protocol-contract-v1.md
T

219 lines
5.4 KiB
Markdown
Raw Normal View History

# 4-4 [done] Tree Projection Protocol Contract v1
> 更新时间:2026-04-18
>
> 关联:
> - `/mnt/Data1T/mnote/design/04-tree-domain/process/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md`
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/reference/1-tree-first-graph-kernel-v1.md`
## 1. 目的
这份文档用于冻结树域 projection contract,避免 `sidebar_tree``page_tree``file_tree` 在后续 Rust route、Leptos tree shell、Next 挂载切流阶段继续各自长字段。
这里固定三条原则:
- 树域只消费 projection,不消费前端自己拼出来的结构真相
- Rust 与前端共享同一组 projection 字段语义
- `sidebar_tree``page_tree``file_tree` 是同一协议家族,不是三套无关返回值
## 2. 协议分层
### 2.1 Rust canonical contract
Rust 侧的 canonical contract 统一表达为:
- `projection_id`
- `projection`
- `root_node_id`
- `items`
- `edges`
`items` 里的字段语义冻结如下。
### 2.2 TypeScript transport contract
前端 TypeScript 侧继续使用 camelCase 字段,但语义必须与 Rust 一致:
- `projectionId`
- `projectionKind`
- `rootNodeId`
- `parentNodeId`
- `resourceMeta`
- `iconHint`
- `expandedByDefault`
也就是说:
- Rust 是 canonical truth
- TypeScript 只是命名风格映射,不允许再引入第二套语义
## 3. 共享字段
下面这些字段属于 `sidebar_tree``page_tree``file_tree` 的共享基线。
| canonical | TS | 说明 |
| --- | --- | --- |
| `node_id` | `nodeId` | 当前 projection item 对应的 kernel node |
| `parent_node_id` | `parentNodeId` | 上级 node,根节点为 `null` |
| `node_type` | `nodeType` | `workspace/folder/page/section/asset/book/pdf/mindmap/table/index` 等语义类型 |
| `projection_kind` | `projectionKind` | 当前 item 属于哪种 projection`sidebar_tree/page_tree/file_tree` |
| `title` | `title` | 展示标题,允许兜底为“无标题” |
| `depth` | `depth` | 当前树深度 |
| `position` | `position` | 同级排序位置 |
| `child_count` | `childCount` | 子项数量 |
| `expandable` | `expandable` | 当前 item 是否理论上可展开 |
| `expanded_by_default` | `expandedByDefault` | 默认展开建议 |
| `capabilities` | `capabilities` | 当前行允许的交互能力 |
| `resource_meta` | `resourceMeta` | 绑定到业务资源的元信息 |
| `icon_hint` | `iconHint` | 给 shell / renderer 的图标提示 |
## 4. `capabilities` 冻结口径
当前允许的共享 `capabilities` 为:
- `expand`
- `open`
- `drag`
- `drop`
- `select`
- `create-child`
- `rename`
- `archive`
- `restore`
- `context-menu`
- `reorder`
- `open-asset`
- `pick`
约束:
- `capabilities` 只表达“允许做什么”
- 它不表达局部 UI 状态
- 它不表达 hover、selected、dragging、drop target 这类临时态
## 5. `resource_meta` 冻结口径
`resource_meta` 统一用于表达 projection item 背后的真实资源。
共享字段:
- `resource_kind`
- `document_id`
- `asset_id`
- `workspace_id`
- `asset_kind`
- `icon_hint`
- `extra`
### 5.1 `resource_kind`
当前冻结为:
- `workspace`
- `document`
- `index`
- `asset`
- `asset_folder`
- `mindmap`
- `table`
- `book`
- `pdf`
### 5.2 `asset_kind`
当前冻结为:
- `file`
- `mindmap`
- `table`
- `book`
- `pdf`
- `image`
- `video`
- `audio`
- `unknown`
## 6. 三类 projection 的差异字段
### 6.1 `sidebar_tree`
`sidebar_tree` 是最轻的导航 projection。
要求:
- 保留共享字段
- 不引入 `row_id`
- 资源主语义通常落在 `resource_kind=document`
- 允许后续继续作为 workspace 首屏 / stream snapshot 主链
### 6.2 `page_tree`
`page_tree` 是页面树 projection。
它在共享字段基础上新增:
- `row_id`
当前 `row_id` 命名约束:
- `page:<nodeId>`
要求:
- `page_tree` 必须可直接生成 picker 轻量列表
- `page_tree` 必须可直接生成可见 rows
- `page_tree` 不允许重新回退成前端自行 flatten 嵌套树
### 6.3 `file_tree`
`file_tree` 是页面树骨架上的更宽对象投影。
它在共享字段基础上强调这些字段必须稳定存在:
- `resource_kind`
- `asset_kind`
- `icon_hint`
- `expandable`
- `expanded_by_default`
要求:
- `file_tree` 不能继续由前端用 `asset-folder` / `asset` / `index` 临时猜语义作为长期真相
- `file_tree` 必须由 Rust 直接输出 `document/index/asset/asset_folder/mindmap/table/book/pdf` 等对象投影
## 7. 当前稳定项与过渡项
### 7.1 已稳定
- `sidebar_tree` 基础 contract
- `page_tree``row_id/node_id/parent_node_id/depth/position/capabilities/resource_meta`
- `expanded_by_default`
- `child_count`
### 7.2 仍处于过渡
- `file_tree` 里的 `asset_folder/asset/index` 仍有前端 adapter 残留
- `icon_hint` 还没有完全由 Rust 主导
- `asset_kind` 仍需要从更宽对象类型扩展到 `book/pdf`
## 8. Fixture 与 Contract Test 要求
后续所有树域 fixture / contract tests 至少覆盖:
- `sidebar_tree` 基础 fixture
- `page_tree` fixture
- `file_tree` fixture
- `resource_meta` 差异字段
- `capabilities`
- `icon_hint`
- `expanded_by_default`
## 9. 完成判定
当以下条件同时成立时,视为本 contract 冻结完成:
- `sidebar_tree``page_tree``file_tree` 的共享字段与差异字段都已写成文档与共享类型
- Rust 与前端共享同一组字段语义
- `file_tree` 不再依赖前端“补字段猜语义”
- 后续 shell / renderer / route 改造不再新增破坏性字段