- 为 Rust Web 主入口补齐本地文件夹/云空间切换、最近目录与路径回填体验\n- 对齐 local markdown media 与 inline marks 的 Rust shell / TipTap converter 语义\n- 让 documents/page 优先消费 Rust page aggregate snapshot,并保留 TS fallback\n- 补强 tree live、local markdown 与主入口 smoke,并同步设计稿状态
481 lines
18 KiB
Markdown
481 lines
18 KiB
Markdown
# 4-20 [done] VSCode Explorer 文件树对标设计 v1
|
||
|
||
> 更新时间:2026-05-06
|
||
>
|
||
> 关联:
|
||
> - `/mnt/Data1T/mnote/tmp/image copy 47.png`
|
||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
|
||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-2-sidebar-pagetree-filetree-product-interaction-contract-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-4-tree-projection-protocol-contract-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-7-tree-shell-ui-state-boundary-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-14-task-005-file-tree-final-renderer-capability-checklist-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/90-reference/90-1-filetree.md`
|
||
|
||
## 1. 目的
|
||
|
||
这份文档只处理 `filesystem / Explorer` 文件树下一阶段怎么对标 VSCode Explorer。
|
||
|
||
它不重新定义树域协议。已有结论继续成立:
|
||
|
||
- `tree-first graph kernel` 是长期对象真相层。
|
||
- `Convex` 继续作为本地自托管存储、实时、文件协作底座。
|
||
- `mnote-web` 是 Rust Web 主执行面,负责 projection、command、transport 与 realtime stream。
|
||
- 前端和 tree shell 只消费稳定 projection,并持有本地交互态。
|
||
|
||
本设计补的是此前文档没有写细的 Explorer 级显示和操作合同:行模型、选择/焦点、打开、右键、键盘、拖放、剪贴板、新建、重命名、删除、上传、资源移动和验收清单。
|
||
|
||
## 2. 当前截图判断
|
||
|
||
`/mnt/Data1T/mnote/tmp/image copy 47.png` 显示当前文件树已经有 Explorer 入口,但仍不像 VSCode Explorer:
|
||
|
||
- `我的页面` 下直接混排页面节点和 `index.md`,但没有清晰的 workspace root / resource root 语义。
|
||
- 页面行使用小菱形 / 折叠箭头,文件行使用蓝色文档图标,信息密度接近,但行列网格不稳定。
|
||
- 当前选中态落在 `index.md` 文件行,和右侧文档页 `新页面` 的 active / focus / selection 关系不够清楚。
|
||
- `Explorer` 标题像 tab 文案,不像 VSCode Explorer 的 section header + toolbar。
|
||
- hover 操作只看到 `+`,缺少新建文件、新建文件夹、刷新、全部折叠、更多等容器级动作。
|
||
- 长列表中大量重复 `新页面/index.md`,缺少资源装饰、路径 disambiguation、命名冲突处理和 reveal 规则。
|
||
|
||
结论:当前不是“样式再修一点”的问题,而是 Explorer 行为合同还没有写到足够细。
|
||
|
||
## 3. 唯一真源
|
||
|
||
### 3.1 结构显示真源
|
||
|
||
文件树显示的唯一真源固定为:
|
||
|
||
`kernel.project_view(file_tree)` 产出的 `KernelProjectionResult`。
|
||
|
||
当前代码证据:
|
||
|
||
- 首屏 query 会从 Convex `sidebar.datasetList` 取数据,再通过 `resolveKernelFileTreeProjection` 调 Rust `kernel.project_view(file_tree)` 附加 `kernel_file_tree_projection`。
|
||
- 实时链路 `/api/mnote-web/stream` snapshot 也会附加同一份 file tree projection。
|
||
- Rust 侧生成点是 `bridge-runtime` 的 `build_file_tree_projection_result`。
|
||
- projection item 的核心字段已经在 `core-protocol/src/kernel.rs` 与前端 `tree-protocol.ts` 固定。
|
||
|
||
因此,Explorer 不能在 UI 层重新推导层级、排序、资源类型、child count、capabilities 或合法性。UI 只能把 projection item materialize 成 row。
|
||
|
||
### 3.2 持久化操作真源
|
||
|
||
持久化动作的唯一正式命令面固定为:
|
||
|
||
- 页面:`tree.node.*`、`tree.subtree.*`
|
||
- 资源:`tree.resource.*`
|
||
- 文件树预检:`tree.filetree.*.preflight`
|
||
|
||
当前已经成立:
|
||
|
||
- 页面新建、重命名、移动、归档、恢复、永久删除、嵌入、复制子树已经接到 `tree.*`。
|
||
- 文件树 `drop/delete/paste/upload-target` 已有 Rust preflight。
|
||
- 资源 `copy/move/upload` 已有 `tree.resource.copy/move/upload`。
|
||
|
||
当前仍未完成:
|
||
|
||
- 资源 `rename/delete/restore` 仍经过 legacy `/api/media/batch` 直接 patch / restore。
|
||
- `cut` 还没有成为剪贴板动作。
|
||
- 资源“移动到...”仍有 `prompt("输入目标页面 ID")` 的临时 UX。
|
||
|
||
### 3.3 本地交互态
|
||
|
||
这些状态只能属于 tree shell / runtime 本地态,不得写进 projection:
|
||
|
||
- `expanded`
|
||
- `selected`
|
||
- `focused`
|
||
- `hovered`
|
||
- `editingName`
|
||
- `clipboard`
|
||
- `dragging`
|
||
- `dropTarget`
|
||
- `contextMenuOpen`
|
||
- `keyboardAnchor`
|
||
- `revealPending`
|
||
|
||
`expandedByDefault` 只是 projection 给初始展开的建议,不是展开态真相。
|
||
|
||
## 4. VSCode Explorer 对标边界
|
||
|
||
### 4.1 必须对齐
|
||
|
||
文件树必须对齐 VSCode Explorer 的资源管理器模型:
|
||
|
||
- 一行就是一个资源命中目标。
|
||
- `twistie` 只负责展开/折叠,打开资源是另一种动作。
|
||
- 图标来自 `rowKind/resourceMeta/iconHint/assetKind`,不是装饰。
|
||
- `selection`、`focus`、`hover`、`active` 分离。
|
||
- 右键点击已选区内资源时保留选区,右键点击未选资源时切换到该资源。
|
||
- 键盘能完整操作可见行。
|
||
- 内部拖拽、外部文件拖入、剪贴板和 context menu 都基于同一 selection/focus 状态。
|
||
- 所有危险或跨资源类型动作先走 preflight plan,再落正式 command。
|
||
|
||
### 4.2 不能照搬
|
||
|
||
文件树不能照搬 VSCode 的真实文件系统语义:
|
||
|
||
- mnote 的文件树是页面、`index.md`、附件、mindmap 文件夹、导图子资源、table/book/pdf 等复合资源投影,不是磁盘目录树。
|
||
- `page -> index.md -> asset-folder -> asset` 这类多语义链路不能用 VSCode `compact folders` 强行压成一行。
|
||
- “在终端打开”“Reveal in Finder/Explorer”“文件比较”等 OS 动作不进入默认合同,除非资源模型已有明确承载。
|
||
- VSCode 的单击预览 / 双击固定标签页节奏不能直接照搬。mnote 的打开必须服从当前文档页、Page Aggregate、附件打开链路和权限边界。
|
||
- Explorer 是 sidebar 中的创新视图,不是整个产品的唯一主导航。`section` 页面树仍保留 Wolai / Notion 方向。
|
||
|
||
## 5. 显示合同
|
||
|
||
### 5.1 Row Grid
|
||
|
||
每个 file tree row 固定为以下视觉列:
|
||
|
||
| 列 | 说明 |
|
||
| --- | --- |
|
||
| indent | 只由 `depth` 决定,步长固定 |
|
||
| twistie | 仅 `expandable=true` 时可见并可点击 |
|
||
| icon | 由 `iconHint/assetKind/resourceKind` 决定 |
|
||
| label | 主名称,单行截断 |
|
||
| decoration | 条件性 badge、状态色、资源提示 |
|
||
| actions | hover / focus 时出现的轻量动作 |
|
||
|
||
不得让 label 长度、hover 按钮或选中态改变行高和缩进。
|
||
|
||
### 5.2 Row Kind
|
||
|
||
当前稳定行类型继续保留:
|
||
|
||
- `doc`
|
||
- `index`
|
||
- `asset-folder`
|
||
- `asset`
|
||
|
||
后续如果需要真实目录或资源组,必须先扩展 Rust projection 的 `rowKind/resourceKind`,不能在 UI 层把某类 asset 临时伪装成目录。
|
||
|
||
### 5.3 Root
|
||
|
||
Explorer 顶部应有明确 root:
|
||
|
||
- 单工作区:显示 workspace root,root 下再进入页面/资源树。
|
||
- 多工作区:允许多个 workspace root。
|
||
- root 行是否可折叠由 projection capability 决定。
|
||
|
||
当前截图中 `我的页面 -> Explorer` 的组合不够清晰,后续应改为:
|
||
|
||
- section header:`EXPLORER`
|
||
- root row:当前 workspace 名称
|
||
- toolbar:新建页面 / 新建资源 / 刷新 / 全部折叠 / 更多
|
||
|
||
### 5.4 Icon 与 Decoration
|
||
|
||
图标语义至少覆盖:
|
||
|
||
- workspace / folder / page / index
|
||
- asset
|
||
- image / video / audio
|
||
- pdf / book
|
||
- table
|
||
- mindmap
|
||
- unknown
|
||
|
||
Decoration 只表达轻量状态,例如:
|
||
|
||
- stale / indexing / uploading
|
||
- read-only / shared / public
|
||
- conflict / invalid target
|
||
- cut clipboard visual
|
||
|
||
Decoration 不得变成第二行副标题。
|
||
|
||
### 5.5 Active、Selection、Focus
|
||
|
||
三者必须分离:
|
||
|
||
- `active`:右侧主区域正在打开的 document / asset。
|
||
- `selection`:Explorer 当前选中集合,支持多选。
|
||
- `focus`:键盘焦点所在 row。
|
||
|
||
截图中 `index.md` 选中与右侧 `新页面` active 的关系不清晰。目标规则:
|
||
|
||
- 打开文档时,`doc` 与 `index` 可共享 active 文档语义,但只能有一个 focused row。
|
||
- 如果 active document 映射到 `index.md`,应显式定义 active 高亮落在 `index` 还是 `doc`。建议:active 落 `index.md`,doc 行只作为目录/页面容器。
|
||
- 多选时 active 不覆盖 selection,selection 背景优先。
|
||
|
||
## 6. 操作合同
|
||
|
||
### 6.1 打开
|
||
|
||
P0:
|
||
|
||
- `Enter` 打开 focused row。
|
||
- 双击 row 打开资源。
|
||
- 单击只负责 selection/focus,不自动打开重资源。
|
||
- `index.md` 打开对应文档页。
|
||
- asset 打开对应资源 viewer / editor。
|
||
|
||
P1:
|
||
|
||
- 支持“Reveal active resource in Explorer”。
|
||
- 页面切换后自动展开祖先链并定位 active row。
|
||
- 搜索过滤后保留 active/reveal 的路径上下文。
|
||
|
||
说明:当前代码已明确与 VSCode 单击打开不同,这是合理差异,但必须写成正式合同,避免后续反复摇摆。
|
||
|
||
### 6.2 展开 / 折叠
|
||
|
||
P0:
|
||
|
||
- 点击 twistie 只展开/折叠,不打开。
|
||
- `ArrowRight`:可展开行未展开则展开;已展开或叶子则可进入打开动作,具体由 runtime 定义。
|
||
- `ArrowLeft`:已展开则折叠;未展开则焦点移动到父节点。
|
||
- `Home/End` 跳到第一/最后可见行。
|
||
|
||
P1:
|
||
|
||
- `Collapse All` 容器动作。
|
||
- 外部拖拽悬停到可展开目标一段时间后自动展开。
|
||
|
||
### 6.3 选择与右键
|
||
|
||
P0:
|
||
|
||
- 单击:单选并设置 anchor/focus。
|
||
- `Ctrl/Cmd + Click`:切换选中。
|
||
- `Shift + Click`:从 anchor 到目标做范围选。
|
||
- 右键已选区内 row:保留选区并打开菜单。
|
||
- 右键未选 row:切换 selection 到该 row,再打开菜单。
|
||
- 空白区点击:清空 selection/focus。
|
||
|
||
P1:
|
||
|
||
- `Ctrl/Cmd + A` 选中当前 Explorer 可见资源。
|
||
- 过滤态下范围选只覆盖可见 row。
|
||
|
||
### 6.4 键盘
|
||
|
||
P0 最小按键:
|
||
|
||
| 按键 | 动作 |
|
||
| --- | --- |
|
||
| `ArrowUp/ArrowDown` | 上下移动 focus |
|
||
| `ArrowLeft/ArrowRight` | 折叠 / 展开 / 父子导航 |
|
||
| `Home/End` | 首尾行 |
|
||
| `Enter` | 打开 focused row |
|
||
| `F2` | 重命名 |
|
||
| `Delete/Backspace` | 删除 / 归档 |
|
||
| `Ctrl/Cmd+C` | copy |
|
||
| `Ctrl/Cmd+X` | cut |
|
||
| `Ctrl/Cmd+V` | paste |
|
||
| `Esc` | 取消 inline rename / 关闭菜单 / 清理 drag feedback |
|
||
|
||
当前缺口:已有 copy/paste,但缺 `cut`;键盘能力需要以 runtime contract 覆盖 file tree,而不是散在 `sidebar.tsx`。
|
||
|
||
### 6.5 新建
|
||
|
||
P0:
|
||
|
||
- `New Page`:映射 `tree.node.create`,在目标 doc/root 下创建页面,并进入 inline rename。
|
||
- `New File`:只允许创建 mnote 资源模型内真实存在的资源类型;如果当前没有通用文件创建命令,入口应隐藏或置灰。
|
||
- `New Folder`:当前没有真实目录语义,不提供默认入口;mindmap asset-folder 不是通用文件夹。
|
||
|
||
P1:
|
||
|
||
- 支持上传文件作为 `New File` 的可用替代路径,走 `tree.filetree.upload-target.preflight -> tree.resource.upload`。
|
||
- 如果未来引入通用资源目录,先扩展 kernel projection / command,再开启 `New Folder`。
|
||
|
||
### 6.6 重命名
|
||
|
||
P0:
|
||
|
||
- `F2` 和 context menu `Rename` 进入 inline rename,不再使用 `prompt`。
|
||
- doc / index 重命名最终落 `tree.node.rename` 或 Page Aggregate title command 的正式命名面。
|
||
- asset 重命名必须从 legacy `/api/media/batch` 收口到 `tree.resource.rename` 或等价正式命令。
|
||
- `Enter` 提交,`Esc` 取消。
|
||
- 空名、重名、非法字符、只读资源必须由 preflight / command 返回可解释错误。
|
||
|
||
P1:
|
||
|
||
- 文件型 asset 默认只选中文件名 stem,不误选扩展名。
|
||
- rename 期间保留 row 高度和缩进,不触发布局跳动。
|
||
|
||
### 6.7 删除 / 恢复 / 永久删除
|
||
|
||
P0:
|
||
|
||
- doc 删除走 `tree.node.archive`。
|
||
- doc 恢复走 `tree.node.restore`。
|
||
- doc 永久删除走 `tree.node.purge`,必须是更强确认路径。
|
||
- asset 删除 / 恢复必须补正式 `tree.resource.archive/restore` 或等价命令,不能长期停留在 legacy `/api/media/batch`。
|
||
- 多选删除先走 `tree.filetree.delete.preflight`,展示将影响的 doc/asset 集合。
|
||
|
||
P1:
|
||
|
||
- 支持删除后 undo / toast action,命令结果仍以 kernel event 和 projection resync 为准。
|
||
|
||
### 6.8 剪贴板
|
||
|
||
P0:
|
||
|
||
- `copy`:复制选中 doc subtree / asset resource。
|
||
- `cut`:进入 cut clipboard 状态,源 row 降噪显示;paste 成功后清空 cut 状态。
|
||
- `paste`:目标由 focused row / context target / active doc 推导,必须先走 `tree.filetree.paste.preflight`。
|
||
- 多选 paste 保持原有可见顺序,并过滤父子重复选择。
|
||
|
||
P1:
|
||
|
||
- 系统剪贴板只保存安全 payload,不暴露敏感内部 token。
|
||
- 跨窗口 paste 需要校验 workspace 和权限。
|
||
|
||
### 6.9 拖放
|
||
|
||
P0:
|
||
|
||
- 内部拖放支持 move/copy,修饰键控制 copy。
|
||
- 外部文件拖入支持 upload target preflight。
|
||
- 非法目标即时反馈,不发持久化命令。
|
||
- doc 拖到自身或后代节点必须拒绝。
|
||
- asset-folder / asset 的可投放能力由 `capabilities` 和 preflight 决定。
|
||
|
||
P1:
|
||
|
||
- 拖到可展开目标 hover 延迟后自动展开。
|
||
- 显示 before / inside / after 三种 drop indicator,而不是只有整行背景。
|
||
- 多选拖放过滤已被父节点包含的子节点。
|
||
|
||
### 6.10 Context Menu
|
||
|
||
菜单按 `rowKind/resourceKind/selection` 裁剪:
|
||
|
||
| 目标 | P0 动作 |
|
||
| --- | --- |
|
||
| root | 新建页面、上传文件、刷新、全部折叠 |
|
||
| doc | 打开、新建子页面、重命名、复制、剪切、粘贴、移动到、归档、复制链接 |
|
||
| index | 打开、复制、剪切、粘贴、Reveal 页面 |
|
||
| asset-folder | 打开、复制、剪切、粘贴、重命名、删除、移动到 |
|
||
| asset | 打开、复制、剪切、重命名、删除、下载、移动到 |
|
||
| 多选 | 打开禁用、复制、剪切、删除、移动到 |
|
||
|
||
当前缺口:
|
||
|
||
- mindmap rename 提示不支持。
|
||
- table move/download 提示不支持。
|
||
- asset 移动仍靠输入页面 ID。
|
||
|
||
这些必须进入 P0/P1 差异清单,不得被样式修复掩盖。
|
||
|
||
## 7. 资源排序与混排规则
|
||
|
||
P0 固定当前最小规则:
|
||
|
||
1. workspace root
|
||
2. doc row
|
||
3. doc 的 `index.md`
|
||
4. doc 的 direct assets / asset folders
|
||
5. child documents
|
||
|
||
排序由 Rust projection 的 `position` 与 preorder 决定。UI 只渲染。
|
||
|
||
需要新增到 projection meta 的候选规则:
|
||
|
||
- `ordering: "kernel_file_tree_preorder"`
|
||
- `folderPolicy: "mnote_composite_resource"`
|
||
- `indexPlacement: "first_child"`
|
||
- `assetPlacement: "after_index_before_child_documents"`
|
||
|
||
这样可以避免 UI 通过 hardcode 解释为什么 `index.md` 总是出现在页面下第一行。
|
||
|
||
## 8. Search / Filter / Reveal
|
||
|
||
P0:
|
||
|
||
- Explorer filter 只改变可见 row,不改变 projection truth。
|
||
- filter 命中子节点时必须补全祖先链。
|
||
- active row 在 filter 后不可见时,保留 active 状态但不强制显示。
|
||
- `Reveal in Explorer` 必须展开祖先链、滚动到目标 row、设置 focus,不破坏 selection,除非调用者明确要求 select。
|
||
|
||
P1:
|
||
|
||
- 支持 `maxResults` 下的 ancestor completion。
|
||
- 支持 indexing visibility,展示 stale / refreshing 状态。
|
||
|
||
当前 `KernelFileTreeProjection.meta.search.indexingVisibility` 已有雏形,后续应把它用于 UI 状态,而不是在 Explorer 自行发明刷新状态。
|
||
|
||
## 9. 分阶段任务
|
||
|
||
### P0:让当前 Explorer 行为不再含糊
|
||
|
||
- 固定 active / selection / focus 的视觉优先级。
|
||
- 补齐 file tree keyboard runtime:`Arrow/Home/End/Enter/F2/Delete/Cut/Copy/Paste/Esc`。
|
||
- 补 `cut` clipboard 状态和 cut row decoration。
|
||
- 把 doc / index / asset / asset-folder 的 context menu 动作裁剪成合同。
|
||
- 移除资源移动的 `prompt("输入目标页面 ID")`,改用 tree picker / move target picker。
|
||
- asset rename/delete/restore 制定 `tree.resource.*` 正式命令或等价 bridge 命名,并保留 legacy alias。
|
||
- 写 smoke 固化当前截图中重复页面 + `index.md` 场景的 active/selection/reveal 行为。
|
||
|
||
### P1:让资源管理器达到 VSCode 常用操作预期
|
||
|
||
- inline rename 全面替代 prompt。
|
||
- root toolbar 对齐 Explorer:新建页面、上传文件、刷新、全部折叠、更多。
|
||
- 拖放支持 before / inside / after indicator 与 hover auto expand。
|
||
- 上传 target preflight 失败时在 drop target 上显示明确反馈。
|
||
- 多选 delete / move / copy 的 preflight plan 显示影响集合。
|
||
- mindmap/table 的 rename/move/download 能力从“不支持”变为可解释菜单状态或正式实现。
|
||
|
||
### P2:高级 Explorer 能力
|
||
|
||
- compact folders 只用于未来真实目录链,不用于页面/附件复合链。
|
||
- git-like / sync-like decorations,如果未来有对应数据源再加入。
|
||
- 多 workspace root。
|
||
- 大树虚拟化和增量加载。
|
||
- 跨窗口剪贴板。
|
||
|
||
## 10. 验收
|
||
|
||
### 10.1 单元 / 合同测试
|
||
|
||
必须覆盖:
|
||
|
||
- projection item 到 row model 的稳定映射。
|
||
- active / selection / focus 分离。
|
||
- `Ctrl/Cmd` 多选、`Shift` 范围选、右键保留选区。
|
||
- 可见 row 变化后的 selection normalize。
|
||
- `cut/copy/paste` payload encode/decode 与目标推导。
|
||
- drop preflight payload 构造。
|
||
- illegal drop:自身、后代、跨 workspace、只读资源。
|
||
- inline rename submit/cancel。
|
||
|
||
### 10.2 Rust 测试
|
||
|
||
必须覆盖:
|
||
|
||
- `kernel.project_view(file_tree)` 输出 `doc/index/asset-folder/asset`。
|
||
- `resourceMeta`、`capabilities`、`iconHint` 稳定。
|
||
- `tree.filetree.*.preflight` 对多选和非法目标返回 plan。
|
||
- 新增 `tree.resource.rename/archive/restore` 或等价命令后,覆盖 command event 与 projection resync。
|
||
|
||
### 10.3 浏览器 smoke
|
||
|
||
必须覆盖:
|
||
|
||
- 当前截图同类数据:多个同名 `新页面`,每个页面下有 `index.md`。
|
||
- 点击 `index.md` 后右侧打开对应页面,Explorer active/focus/selection 可解释。
|
||
- 右键已选区保留多选。
|
||
- `F2` inline rename,`Esc` 取消,`Enter` 提交。
|
||
- `Ctrl/Cmd+X` 后 cut decoration 出现,paste 成功后消失。
|
||
- 外部文件拖入 doc / index / asset-folder 的目标差异。
|
||
- 资源移动不再弹 `prompt`。
|
||
|
||
## 11. 非目标
|
||
|
||
- 不把文件树改成真实磁盘文件系统。
|
||
- 不把 `Explorer` 替代 `section` 页面树作为唯一导航。
|
||
- 不在 UI 层重新拼排序、层级、资源类型或权限。
|
||
- 不为了像 VSCode 而绕过 Page Aggregate、tree projection 或 `tree.*` 命令。
|
||
- 不把 `90-1-filetree.md` 的 Rust 生态调研当作实现模板;它只作为参考资料。
|
||
|
||
## 12. 完成判定
|
||
|
||
这份设计进入 done 的条件:
|
||
|
||
- P0 项全部有代码和 smoke 覆盖。
|
||
- file tree 的 active / selection / focus / keyboard / context menu / clipboard / DnD 有稳定 runtime contract。
|
||
- 资源 rename/delete/restore 不再只依赖 legacy `/api/media/batch` 主路径。
|
||
- 当前截图暴露的 Explorer 行为歧义能通过测试说明和验证。
|
||
- 新增能力没有制造第二套树真相。
|