返回 DeepSeek-Reasonix
DOCK_FILE_NAVIGATION.zh-CN.md
根目录 / docs / DOCK_FILE_NAVIGATION.zh-CN.md
1 # Dock 文件导航
2
3 右侧 Dock 如何打开一个文件、结果归谁所有,以及为什么一次点击不再引发渲染
4 循环。改动 `WorkspaceDockRegion`、`WorkspacePanel`、`RemotePanel` 或
5 `lib/fileNavigation*` 之前请先读本文。
6
7 ## 契约
8
9 打开文件是一条命令,不是一次渲染。
10
11 ```
12 文件行 / Markdown 链接 / 宿主验证的回答引用 / 文件树 / 预览操作
13 → 已绑定运行实例的打开命令(FileNavigationOwner)
14 → 解析资源并校验该命令是否仍然有效
15 → 确定目标 Dock 与预览位置
16 → 提交导航记录
17 → Dock 订阅并渲染已提交的结果
18 ```
19
20 - **渲染不执行导航。** 面板通过 `useSyncExternalStore` 读取
21 `owner.getSnapshot(key)`,React 渲染期间不发布任何请求。
22 - **资源身份、访问上下文与导航参数分离。** 预览身份由资源空间 + 后端规范路径
23 决定;会话标签、来源和可选 `toolCallId` 属于 `FileAccessContext`;
24 `preview`、`source`、`reveal-tree` 只是参数。切换参数复用同一个预览标签,
25 切换会话则可在不改变文件身份的情况下重绑定访问权限。
26 - **访问凭据随命令传递。** workspace、presented 与宿主验证的回答引用使用
27 不同读取入口。从另一个入口重新打开同一路径时,只使用本次命令的凭据,
28 不沿用此前 presented 请求的权限;回答引用的每次读取与直接操作都会由宿主
29 重新解析和授权。
30 - **记录按 Dock 标签索引。** 记录包含 Dock 实例身份、生命周期代数、
31 带访问上下文的条目、最后一次导航意图、显示修订、内容修订与生命周期信号。
32 - **只有显式命令推进修订。** 等价命令会保持条目对象与条目列表完全一致,
33 因此仍有效的内容读取不会被重新启动。
34
35 ## Harness 设计 → Reasonix 实现
36
37 | Harness 设计 | Reasonix 实现 |
38 | --- | --- |
39 | 命令驱动导航:打开由事件触发,渲染只读取结果 | `FileNavigationOwner`(`lib/fileNavigationOwner.ts`)提交记录;`WorkspaceDockRegion` 不再在渲染中构造请求;`useFileNavigationRecord`(`app-shell/useFileNavigation.ts`)只读取 |
40 | 稳定资源身份:按文件规范坐标识别,不按对象引用 | `FileResourceRef` / `FileAccessContext` / `ResolvedFileResource`(`lib/fileResource.ts`);后端解析的 `identityPath` 合并相对/绝对路径别名,访问上下文另行区分 workspace、presented 与验证引用读取 |
41 | 导航参数与身份分离 | `FileNavigationParams`——`action`(`preview`/`source`/`reveal-tree`)与 `view`(`files`/`changed`)不改变资源本身 |
42 | 每个 Dock 独立导航实例 | 每个运行实例一个 `FileNavigationOwner`(`useFileNavigationRuntime`),每个 Dock 标签一条记录,每次生命周期一个 `generation` |
43 | 命令结果上报而非抛出 | `FileNavigationOutcome`——`opened` / `cancelled`(superseded、closed、disposed)/ `failed`;取消不显示错误 |
44 | 取消浏览器预览时的 URL 归属 | `openBrowserPreview` 只释放一次 URL:交接后归标签页所有,未交接则由命令撤销 |
45 | 取消全局化 | 删除 `fileNavigationLifetime.ts`;新命令取代该记录尚未完成的旧命令,`retain`/`bindScope` 结束记录生命周期 |
46 | 恢复不重放命令 | 记录在 Dock 折叠后保留;重新挂载只读取已保留的选择,不重新应用最后一次意图,也不重新执行打开命令 |
47
48 未移植:分栏、浮窗、布局撤销历史、Cordis 插件框架,以及
49 `dsh-resource://` 外部协议(资源标识仅用于内部导航)。
50
51 ## 生命周期与取消
52
53 | 事件 | 效果 |
54 | --- | --- |
55 | 同一 Dock 的新命令 | 中止该记录尚未完成的旧命令,旧结果返回 `cancelled` |
56 | 发往其他 Dock 的命令 | 无影响:面板之间不互相取消 |
57 | Dock 标签关闭或移出当前工作区 | `retain` 删除记录并中止其生命周期 |
58 | 同一项目内切换会话 | `bindScope` 保留条目与选择,并把访问上下文替换为当前会话的 workspace 权限 |
59 | 项目或远程主机资源空间变化 | `bindScope` 重建记录:条目清空、代数递增、旧生命周期中止 |
60 | Dock 折叠(`workspacePanelOpen` 为 false) | 无影响:展开时正是靠该记录恢复 |
61 | 运行实例卸载 | `dispose` 中止全部记录与一次性操作 |
62 | 关闭后复用同一标签 ID | 使用新的生命周期代数;记忆中的路径仅以 workspace 访问权限恢复 |
63
64 ## 持久化
65
66 只持久化路径(`workspaceViewMemory`、Dock 标签列表)。生命周期代数、取消信号
67 与访问上下文仅存于内存,不写入 `localStorage`。旧版本写入的存储按路径读取,
68 并重新走当前工作区校验,因此恢复的预览不会重新获得 presented 工具范围。
69
70 不修改 Go/Electron 桥接负载、会话日志、工具 schema、standing instruction 或
71 provider 请求字节,对提示缓存无影响。
72
73 ## 验证
74
75 | 范围 | 测试 |
76 | --- | --- |
77 | 导航实例 | `file-navigation-owner.test.ts` |
78 | 命令顺序与取消 | `file-navigation-races.test.ts` |
79 | 验证回答引用的读取与源码切换 | `workspace-reference-reader.test.tsx` |
80 | 真实 Dock 链路(本地与远程) | `dock-file-navigation.test.tsx` |
81 | 预览标签、上限、源码模式、定位、代数 | `file-navigation-dock.test.tsx` |
82 | 渲染循环缺陷、StrictMode、重渲染、重挂载 | `file-navigation-lifecycle.test.tsx` |
83 | 远程读取、保存隔离、断连 | `remote-file-navigation-races.test.tsx` |
84 | Dock 请求投递 | `dock-navigation.test.ts`、`dock-view-requests.test.tsx` |
85 | 真实 DOM 与 Electron | `bench/dock-file-navigation.mjs`(`test:app-browser`、`test:dock-electron`) |
86 | 回答引用点击进入运行实例 Owner | `bench/chat-file-reference.mjs`(`test:chat-file-browser`) |
87
87 lines MARKDOWN