dsh-workspace-plus 0.1.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -39
- package/lib/client.js +1867 -1412
- package/lib/index.js +790 -707
- package/package.json +65 -65
package/README.md
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# dsh-workspace-plus
|
|
2
|
+
[](https://dsh-plugin.org/plugins/sen70s/dsh-workspace-plus)
|
|
2
3
|
|
|
3
|
-
DeepSeek Harness Web GUI
|
|
4
|
+
DeepSeek Harness Web GUI 的插件。一个功能,三个入口:
|
|
4
5
|
|
|
5
6
|
| 入口 | 说明 |
|
|
6
7
|
| --- | --- |
|
|
7
8
|
| **工作区行上的目录按钮**(弹窗) | 一个工作区登记 N 个目录,每个目录带标签;标签在对话里可直接指代 |
|
|
8
|
-
|
|
|
9
|
+
| **对话输入框里的 `@`** | 打 `@` 就能看到本工作区的标签,选中即插入一个指向该目录绝对路径的引用 |
|
|
10
|
+
| **设置 → `workspace+`** | **每个工作区一个开关**:是否在该工作区启用本功能(默认工作区默认关闭) |
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
它解决的问题:一个工作区(DSH 里 = 一个目录)装不下真实项目。前后端分在两个仓库、文档在第三个目录时,
|
|
11
13
|
每次都要在对话里手写绝对路径,而且目录名相似时 agent 容易改错文件。本插件让工作区带上
|
|
12
14
|
「标签 → 绝对路径」映射,并**在每个请求里把这张表注入 agent 上下文**,于是:
|
|
13
15
|
|
|
@@ -15,6 +17,12 @@ DeepSeek Harness Web GUI 的插件。一个功能,两个入口:
|
|
|
15
17
|
>
|
|
16
18
|
> agent 直接去 `D:\proj\backend`,不会因为旁边有个 `D:\proj\backend-legacy` 而改错。
|
|
17
19
|
|
|
20
|
+
名字相似的目录摆在一处,本就是容易出事的地方。让机器记住哪个是哪个,比每次都由人在对话里
|
|
21
|
+
把路径抄一遍要可靠些 —— 抄写这桩事,人向来是做不过机器的;而记错门牌这桩事,机器又向来是做不过人的。
|
|
22
|
+
插件能做的,只是把它擅长的那一半接过来。
|
|
23
|
+
|
|
24
|
+
> 想先看看它长什么样、怎么用:[**使用**](#使用) 一节里配了截图。
|
|
25
|
+
|
|
18
26
|
> 本仓库只包含插件本身;没有改动 DSH 安装目录里的任何文件。state 文件由插件自己写在 `~/.dsh/workspaceplus/`。
|
|
19
27
|
|
|
20
28
|
## 目录结构
|
|
@@ -27,7 +35,10 @@ dsh-workspace-plus/
|
|
|
27
35
|
│ ├── index.js # 宿主半:store + 设置 + 上下文注入 + workspace_dirs 工具 + HTTP 路由
|
|
28
36
|
│ └── client.js # 浏览器半:设置页 + 目录弹窗 + 工作区行按钮(无需构建)
|
|
29
37
|
├── icon.svg # 插件管理页显示的图标
|
|
30
|
-
├──
|
|
38
|
+
├── LICENSE # MIT
|
|
39
|
+
├── docs/ # README 里那几张截图
|
|
40
|
+
├── tools/verify.mjs # 离线自检(387 项断言,不需要 DSH 在跑)
|
|
41
|
+
├── tools/preview/ # 截图是怎么来的:真实 React + 真实壳层样式 + 无头 Edge(见其 README)
|
|
31
42
|
└── README.md
|
|
32
43
|
```
|
|
33
44
|
|
|
@@ -36,15 +47,18 @@ dsh-workspace-plus/
|
|
|
36
47
|
|
|
37
48
|
## 设置:设置 → `workspace+`
|
|
38
49
|
|
|
39
|
-
|
|
50
|
+
**每个工作区一个开关**:这个工作区里启用还是停用 workspace+。一个开关同时管三处 ——
|
|
51
|
+
工作区行上的目录按钮、注入该工作区对话的标签表、以及 `workspace_dirs` 工具。
|
|
52
|
+
关掉**不会删除**已登记的标签,只是让这个工作区暂时不参与。
|
|
40
53
|
|
|
41
|
-
|
|
42
|
-
指向它,即使它的注册被删掉这个身份也会保留)。它属于产品而不属于本插件,所以:
|
|
54
|
+
默认值不是一刀切:
|
|
43
55
|
|
|
44
|
-
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
- 除默认工作区外,**每个工作区默认开启**。
|
|
57
|
+
- 「默认工作区」是 DSH 首次启动时自动创建的那个(`workspaceRegistry` 里 `defaultWorkspaceId`
|
|
58
|
+
指向它,即使它的注册被删掉这个身份也会保留)。它属于产品而不属于本插件,所以**默认关闭**:
|
|
59
|
+
不显示目录按钮、不注入上下文、`workspace_dirs` 对它直接返回一个指向上面的开关的错误。
|
|
60
|
+
在设置页打开它自己那一行的开关即可,无需重启、无需刷新。
|
|
61
|
+
- 拨动开关写的是**这个工作区的显式值**,所以它一定覆盖默认值;设置页不会因为「默认值恰好相同」而装作没生效。
|
|
48
62
|
|
|
49
63
|
宿主怎么认出默认工作区:它只调用**公开的** `workspaceRegistry.initializeDefault()`,
|
|
50
64
|
并传一个「拒绝创建」的目录解析器 —— 已有默认工作区时该调用直接返回它(无副作用),
|
|
@@ -54,6 +68,26 @@ dsh-workspace-plus/
|
|
|
54
68
|
> 首次探测可能早于注册表服务初始化完成而失败。失败时状态保持「未决」并在下一次请求/工具调用时重试,
|
|
55
69
|
> 而不是把「没有默认工作区」永久缓存下来 —— 否则默认工作区会被静默启用。
|
|
56
70
|
|
|
71
|
+
### 设置是怎么存的
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"settings": {
|
|
76
|
+
"defaultWorkspaceEnabled": false,
|
|
77
|
+
"workspaceEnabled": { "d:\\projects\\api": false }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- `workspaceEnabled` 是**按工作区**那一层:键是工作区目录(和 `workspaces` 一样大小写折叠),
|
|
83
|
+
值 `true` / `false` 是显式覆盖。
|
|
84
|
+
- 键由宿主下发(`/workspaceplus/api` 快照里每个工作区的 `key`),浏览器半**不会**自己从路径猜 ——
|
|
85
|
+
Windows 下大小写折叠规则一旦不一致,猜出来的键会「存得进去但永远匹配不上」,是个静默失效。
|
|
86
|
+
旧宿主没有这个字段时,设置页把开关置灰并提示重启 dsh。
|
|
87
|
+
- `defaultWorkspaceEnabled` 是「默认工作区那一行的默认值」。字段保留是为了兼容旧状态文件,
|
|
88
|
+
正常使用中不需要手改;在设置页拨默认工作区那一行写入的是 `workspaceEnabled` 里的显式覆盖。
|
|
89
|
+
- 旧状态文件(没有 `settings`,或没有 `workspaceEnabled`)照常工作:缺失的那层归一化成「没有覆盖」。
|
|
90
|
+
|
|
57
91
|
## 功能是怎么工作的
|
|
58
92
|
|
|
59
93
|
### 数据
|
|
@@ -63,6 +97,7 @@ dsh-workspace-plus/
|
|
|
63
97
|
```json
|
|
64
98
|
{
|
|
65
99
|
"version": 1,
|
|
100
|
+
"settings": { "defaultWorkspaceEnabled": false, "workspaceEnabled": {} },
|
|
66
101
|
"workspaces": {
|
|
67
102
|
"d:\\proj": {
|
|
68
103
|
"path": "D:\\proj",
|
|
@@ -112,6 +147,8 @@ dsh-workspace-plus/
|
|
|
112
147
|
|
|
113
148
|
### 界面:一个弹窗,两个触发器
|
|
114
149
|
|
|
150
|
+
(第三个入口 —— 对话输入框里的 `@` —— 见下面单独一节。)
|
|
151
|
+
|
|
115
152
|
弹窗本身注册在 **`shell.overlay`** —— 和壳层自己的「重命名工作区 / 删除工作区」对话框同一个层
|
|
116
153
|
(那两个是 `workspace.session-rename`、`workspace.session-archive`),所以观感和层级天然一致。
|
|
117
154
|
|
|
@@ -122,7 +159,7 @@ dsh-workspace-plus/
|
|
|
122
159
|
> 「Workspace row menus are visual-only except Rename/Delete」,
|
|
123
160
|
> 代码里 `ProjectRowItem` 也只收到 `actions: { rename, delete }`。
|
|
124
161
|
|
|
125
|
-
所以「往那个菜单里加一项」无法用官方 API
|
|
162
|
+
所以「往那个菜单里加一项」无法用官方 API 做到。替代方案是把按钮**插进该行的操作区**
|
|
126
163
|
(`rowActions`),定位不依赖易变的哈希类名,而用壳层自己发出的稳定锚点:
|
|
127
164
|
|
|
128
165
|
- `data-slot="sidebar.workspaces"` —— 渲染器给每个 slot 出口都加的锚(`SlotOutlet`)
|
|
@@ -130,9 +167,14 @@ dsh-workspace-plus/
|
|
|
130
167
|
`buildGroup(workspace.workspaceId, workspace.workspaceId, …)` 保证这个 key **就是** workspace id,
|
|
131
168
|
与宿主 `workspaceRegistry` 的 `Workspace.id`(客户端 `workspaceView().workspaceId`)一致
|
|
132
169
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
170
|
+
**位置:夹在中间。** 壳层渲染的操作区是 `[「更多」菜单, 「新会话」按钮]`,本插件的按钮插在
|
|
171
|
+
**最后一个子节点之前**,于是那一行读作「更多 · 工作区目录设置 · 新会话」,新会话保持最后一个。
|
|
172
|
+
(`insertBefore(node, null)` 等价于 append,所以操作区只有一个子节点时会退化成「排在菜单之后」,
|
|
173
|
+
而不是硬造一对不存在的顺序。)
|
|
174
|
+
|
|
175
|
+
React 重渲染时并不知道这个节点的存在,可能把自己的子节点重新插到它两边,所以**位置是每次对账都重新
|
|
176
|
+
确认的**,不是创建时设一次。对账由 `MutationObserver` 驱动,覆盖增、删、移位三种情况,
|
|
177
|
+
重复对账是幂等的。点击时 `preventDefault + stopPropagation`,所以不会触发行本身的展开/折叠。
|
|
136
178
|
|
|
137
179
|
外观上刻意对齐壳层自己的行内按钮(`Rows.module.css` 的 `.iconButton`):16×16、无边框、无底色、
|
|
138
180
|
`color: label-tertiary`,**hover 只变颜色不加背景**;图标直接复用官方的文件夹图形
|
|
@@ -145,19 +187,50 @@ dsh-workspace-plus/
|
|
|
145
187
|
侧边栏不会多出任何图标(有一个 1.2s 的 settle 窗口避免闪烁)。
|
|
146
188
|
|
|
147
189
|
弹窗**锁定触发它的那个工作区**:从哪个工作区行点的,就只编辑那个工作区,弹窗里没有选择器,
|
|
148
|
-
|
|
190
|
+
只显示工作区名和根目录。卡片宽度是 `min(760px, 100%)`(壳层默认的 380px 装不下一行
|
|
191
|
+
「标签 + 绝对路径 + 说明 + 行尾图标」)。位于工作区根目录之外的目录,行尾是一个**黄色盾牌图标**,
|
|
192
|
+
说明放在 hover / 键盘聚焦才出现的 tooltip 里(见下面的沙箱一节)。
|
|
149
193
|
如果 HTTP 通道不可用,弹窗会明确说明原因(区分 401 / 404 / 传输不可达),而不是假装映射是空的 ——
|
|
150
194
|
此时仍然可以在对话里用 `workspace_dirs` 管理。
|
|
151
195
|
|
|
152
|
-
|
|
153
|
-
`ctx.uiWorkspace.pickDirectory()
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
`
|
|
196
|
+
**路径没有手动输入框**:添加表单的第一行就是一个字段样式的按钮(文件夹图标 + 「选择目录…」),
|
|
197
|
+
**点击它直接打开系统的目录选择器**,也就是壳层自己的 `ctx.uiWorkspace.pickDirectory()`
|
|
198
|
+
(就是「选择工作区」用的那一个),所以桌面版是原生对话框、浏览器版是壳层的浏览面板。
|
|
199
|
+
选中后路径显示在这个字段里,**标签为空时**还会按目录名猜一个合法标签
|
|
200
|
+
(`D:\proj\my api` → `my-api`;纯中文目录名不猜,避免塞一个宿主会拒绝的标签);
|
|
201
|
+
标签和说明在同一行,路径在它们的上一行。取消选择不会改动任何字段。
|
|
202
|
+
路径为空时「添加」保持禁用;`ui-workspace` 没有挂载时(合成里没有这一行)这个字段自动置灰,
|
|
203
|
+
并用 `title` 说明改用 `workspace_dirs`,插件其余部分照常工作。
|
|
157
204
|
|
|
158
205
|
> 唯一的例外是兜底入口:它是全局的、没有工作区上下文,所以那条路径下弹窗会退回显示选择器。
|
|
159
206
|
> 正常使用中不会看到它。
|
|
160
207
|
|
|
208
|
+
## 对话输入框里的 `@`
|
|
209
|
+
|
|
210
|
+
在会话里打 `@` 会多出一组「工作区目录」候选 —— 就是已登记的标签,可以按标签、说明或路径过滤。
|
|
211
|
+
选中后插入一个引用 chip:**chip 显示标签,提交给模型的是那个目录的绝对路径**。
|
|
212
|
+
|
|
213
|
+
这样一来,即使注入的标签表因为上下文压缩而不在了,模型拿到的仍然是路径本身,指代不会失焦:
|
|
214
|
+
|
|
215
|
+
> `@backend 加个健康检查` → 模型收到 `D:\proj\backend 加个健康检查`
|
|
216
|
+
|
|
217
|
+
候选来自**所有已启用的工作区**,按工作区标题分节;**当前会话所在工作区排在最前**(能读到它的 cwd 时)。
|
|
218
|
+
会话查找只是排序优化,不是前置条件 —— 早先按「只会话工作区」出候选的写法有个致命缺点:只要 cwd 读不到,
|
|
219
|
+
整组就静默变空,而空菜单和坏 bundle 从外面看一模一样,无法证伪。
|
|
220
|
+
|
|
221
|
+
实现要点(都在 [lib/client.js](lib/client.js) 里,用壳层的 input trigger 管线):
|
|
222
|
+
|
|
223
|
+
| 事项 | 做法 |
|
|
224
|
+
| --- | --- |
|
|
225
|
+
| 注册 | `ctx.inject(['inputTriggers'], …)` → `registerSource({ trigger: '@', name: 'workspace-plus', … })`;`(trigger, name)` 必须唯一,`@reference` 是官方占用的 |
|
|
226
|
+
| 候选 | 所有 `enabled !== false` 且登记了目录的工作区;被设置页关掉的工作区自然被排除 |
|
|
227
|
+
| 排序 | 从管道给的 `{ sessionId }` 反查 cwd(`sessions.list` 投影,与 `ui-reference`/`ui-session` 同一处),命中的工作区提到最前;读不到就按原序 |
|
|
228
|
+
| **必须是 async** | 管线是 `source.candidates(...).then(...)` 直接调用 —— 同步返回数组会在它自己的循环里抛错 |
|
|
229
|
+
| 分组标题 | `showGroupTitle: false` + 每行自带 `section`(工作区标题),否则标题会是管道字典里查不到的 key |
|
|
230
|
+
| chip | `appearance: 'folder'` → 文件夹图标、且不会被标成可点开;点击时因为本 source 没有 `openReference` 而是安全的空操作 |
|
|
231
|
+
| 序列化 | 本 source 自带 `codec.serialize(ref) => ref`;**chip 的 serializer 是按 source 名路由的,没有 codec 的 source 会让整次提交被拒** |
|
|
232
|
+
| 自检 | 设置 → `workspace+` 里有一行「输入框 `@` 候选」,直接告诉你候选源是否已注册、共有多少标签 |
|
|
233
|
+
|
|
161
234
|
## ⚠ 沙箱:一个会话只有一个可写根
|
|
162
235
|
|
|
163
236
|
DSH 的沙箱策略把每个会话的可写范围限定在 `SessionHeader.cwd`(也就是工作区目录);
|
|
@@ -211,7 +284,7 @@ for (const root of writableRoots(policy)) if (await isPathUnder(fresh.targetKey,
|
|
|
211
284
|
2. 保持 `workspace-write`,接受每次越界写入弹一次批准(agent 会按工具提示发起升级)。
|
|
212
285
|
|
|
213
286
|
插件本身不做任何绕过沙箱的事,只是把限制如实告诉 agent(上下文里会标注哪些目录在根目录之外)
|
|
214
|
-
|
|
287
|
+
和界面(行尾一个黄色盾牌图标,悬浮或键盘聚焦时用 tooltip 说明原因)。
|
|
215
288
|
|
|
216
289
|
## 安装
|
|
217
290
|
|
|
@@ -230,14 +303,15 @@ dsh plugin --profile desktop add <本地 checkout 目录>
|
|
|
230
303
|
|
|
231
304
|
`lib/` 是仓库里已提交的产物、包内没有 `prepare` 构建脚本,所以上面三种方式都不需要
|
|
232
305
|
pnpm 的 `allowBuilds` 构建授权,装到的就是可直接加载的代码。想锁定版本可以用
|
|
233
|
-
`github:Sen70s/dsh-workspace-plus#v0.
|
|
306
|
+
`github:Sen70s/dsh-workspace-plus#v0.4.0`。要求 DSH `>=0.1.7-rc.2`(桌面版当前就是
|
|
234
307
|
`@deepseek-ai/dsh-desktop 0.1.7-rc.2`;npm 上是 `next` 通道)。
|
|
235
308
|
|
|
236
309
|
> 0.1.0 曾以包名 `dsh-workspaceplus` 发布;自 0.1.1 起更名为 `dsh-workspace-plus`,旧包已废弃,
|
|
237
310
|
> 请勿再安装。
|
|
238
311
|
|
|
239
312
|
装完后**重启 `dsh`** 并刷新页面。悬停任一工作区那一行,操作区会多出一个目录图标,点它打开弹窗;
|
|
240
|
-
设置面板里会多出一个 `workspace+`
|
|
313
|
+
设置面板里会多出一个 `workspace+` 分区(默认工作区那一行默认关着,其它工作区默认开着,见下一节);
|
|
314
|
+
对话输入框里打 `@` 会多出一组「工作区目录」候选。
|
|
241
315
|
|
|
242
316
|
卸载:
|
|
243
317
|
|
|
@@ -247,17 +321,62 @@ dsh plugin --profile desktop remove dsh-workspace-plus
|
|
|
247
321
|
|
|
248
322
|
## 使用
|
|
249
323
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
324
|
+
我向来不惮于把话说明白,只是插件的用法,说一遍也就够了,不必绕弯子。
|
|
325
|
+
|
|
326
|
+
### 一、先把目录登记上
|
|
327
|
+
|
|
328
|
+
把鼠标停在左侧边栏的任意一个工作区上,那一行的右边会浮出三个小东西。头一个是「更多」,末尾一个是「新会话」,中间那个文件夹,是本插件的。
|
|
329
|
+
|
|
330
|
+

|
|
331
|
+
|
|
332
|
+
它排在中间,不是末尾 —— 末尾那个位置是壳层的,我不去占。人有各人的座位,插件也一样。
|
|
253
333
|
|
|
254
|
-
|
|
334
|
+
点它,「工作区目录设置」就开出来了:
|
|
255
335
|
|
|
256
|
-
-
|
|
336
|
+

|
|
337
|
+
|
|
338
|
+
表格里是已经登记过的目录,一行一个:标签、绝对路径、说明,如此而已。若某个目录在工作区根目录之外,行尾会立一个小小的黄盾牌 —— 这个下面再讲。
|
|
339
|
+
|
|
340
|
+
**路径是不用手打的。** 表单最上头那个带文件夹图标的格子,它本身就是个按钮。点它,系统的目录选择器就开了:
|
|
341
|
+
|
|
342
|
+

|
|
343
|
+
|
|
344
|
+
选中之后,路径落进格子里;标签若还空着,便从目录名里猜一个(`D:\proj\my api` → `my-api`;纯中文的目录名不猜,因为宿主不收,猜了也是白猜)。再补一句说明,点「添加」,完事。标签和说明占同一行,路径在它们上面那一行 —— 三样东西原本挤作一处,那是我的不是。
|
|
345
|
+
|
|
346
|
+
### 二、那个黄盾牌
|
|
347
|
+
|
|
348
|
+
目录若在工作区根目录之外,它就立在那里,不写字。非得你把鼠标放上去,或者用键盘 Tab 把它聚焦了,它才开口:
|
|
349
|
+
|
|
350
|
+

|
|
351
|
+
|
|
352
|
+
说的是:DSH 的沙箱只认工作区根目录这一个可写根;会话权限不是 `danger-full-access` 的时候,根目录之外只能读,要写就得逐次批准。这不是插件不肯通融,是它本来就只有这一条路。
|
|
353
|
+
|
|
354
|
+
所以要图省事,**把工作区建在几个工程的共同父目录上**,让所有标签都落在里头。这是最省事的办法,也是唯一的办法。
|
|
355
|
+
|
|
356
|
+
### 三、开关,一个工作区一个
|
|
357
|
+
|
|
358
|
+
设置 → `workspace+`:
|
|
359
|
+
|
|
360
|
+

|
|
361
|
+
|
|
362
|
+
一个工作区一行,一行一个开关。这个开关管三件事:那一行的目录按钮出不出现、标签表注不注入这个工作区的对话、`workspace_dirs` 认不认这个工作区。
|
|
363
|
+
|
|
364
|
+
默认值并不整齐:**除默认工作区外,都默认是开的**;而默认工作区 —— 也就是 DSH 第一次启动时自己建起来的那一个 —— 默认关着。它属于产品,不属于这个插件,我不便替你做主。要用,把它那一行的开关拨开就是。
|
|
365
|
+
|
|
366
|
+
关掉**不会删掉标签**。哪天想起来了再拨回来,目录还在那里。
|
|
367
|
+
|
|
368
|
+
### 四、在对话里
|
|
369
|
+
|
|
370
|
+
前提是工作区 `D:\proj` 底下挂了 `backend` 和 `docs` 两个标签。于是:
|
|
371
|
+
|
|
372
|
+
- 打一个 `@`,菜单里多出一组「工作区目录」。选 `backend`,输入框里就落下一个 `backend` 的 chip —— 而**送给模型的是那个绝对路径**。这一点要紧:哪怕注入的标签表被上下文压缩挤掉了,模型手里那个路径也还是硬的。
|
|
373
|
+
- 「把 `D:\proj\backend` 记成 backend,说明写后端 API」→ agent 调 `workspace_dirs add`
|
|
257
374
|
- 「现在有哪些标签?」→ `workspace_dirs list`
|
|
258
|
-
-
|
|
259
|
-
- 「删掉 docs
|
|
260
|
-
-
|
|
375
|
+
- 「backend 改指向 `D:\proj\backend-v2`」→ `workspace_dirs update`
|
|
376
|
+
- 「删掉 docs」→ `workspace_dirs remove`
|
|
377
|
+
- 诸事办完,才可以说那句:「backend 加个健康检查接口」。agent 去的是 `D:\proj\backend`,不会因为隔壁站着一个 `backend-legacy` 就摸错了门。
|
|
378
|
+
|
|
379
|
+
标签表是每次请求都注入的,所以上面这些,多数时候你不说,它也晓得。
|
|
261
380
|
|
|
262
381
|
## 自检
|
|
263
382
|
|
|
@@ -267,29 +386,56 @@ dsh plugin --profile desktop remove dsh-workspace-plus
|
|
|
267
386
|
node tools/verify.mjs
|
|
268
387
|
```
|
|
269
388
|
|
|
270
|
-
它会(当前 `
|
|
389
|
+
它会(当前 `387/387 checks passed`,另有 1 项按环境跳过):
|
|
271
390
|
|
|
272
391
|
- 校验 manifest、patch 层、`dsh.client.inject` 的取值;
|
|
273
392
|
- 用临时 `DSH_HOME` 运行宿主半:标签/路径校验、增删改、去重、大小写折叠、损坏文件容错、原子写;
|
|
274
|
-
- 用假的 Cordis ctx
|
|
393
|
+
- 用假的 Cordis ctx 验证各处注册,并**真实执行** `workspace_dirs` 的 list/add/remove;
|
|
275
394
|
- 用假的 `node:http` req/res 真实调用 HTTP 路由的 GET/POST/405/401 分支;
|
|
276
395
|
- 在 `node:vm` 里加载浏览器半,用**最小 DOM 替身**(只实现本插件用到的那几个选择器)真实驱动
|
|
277
396
|
工作区行按钮:挂载前不误报、按行注入、未分组行跳过、重复对账幂等、行移除后回收、
|
|
278
397
|
点击只开弹窗而不冒泡到行、dispose 清理干净;
|
|
279
398
|
- 校验弹窗的外部 store(打开/切换/关闭/退订)与各槽位注册,
|
|
280
399
|
校验行按钮 CSS 与壳层 `.iconButton` 的度量一致(16×16、无底色、hover 只变色),
|
|
281
|
-
|
|
400
|
+
校验样式表在**模块热重载**后仍是一个、且是当前版本的 tag(不会新旧并存),
|
|
401
|
+
校验**按钮的位置**(落在「更多」和「新会话」之间、新会话保持最后一个、被 React 挤走后会自动归位、
|
|
402
|
+
只有一个子节点时退化为排在菜单之后、dispose 后壳层自己的按钮原样不动),
|
|
403
|
+
校验**工作区闸门**(宿主快照标 disabled 后行按钮消失、开关打开后回来、探测失败会重试),
|
|
404
|
+
校验**按工作区设置**(覆盖能关掉一个平时开着的工作区、也能打开默认工作区、覆盖优先于默认值、
|
|
405
|
+
`null` 恢复默认、非法值整批拒绝且一个字节都不写、旧状态文件缺这一层照常工作、
|
|
406
|
+
设置页一个工作区一个开关、按键由宿主下发而不是从路径猜、旧宿主没有 key 时置灰并提示),
|
|
407
|
+
- 校验**弹窗的形状**(卡片被拉宽到 760px、description 与脚注都不再渲染、
|
|
408
|
+
「工作区根目录之外」不再是行内文字而是带 tooltip 的盾牌图标、路径是控件而不是输入框且位于标签行之上、
|
|
409
|
+
路径为空时「添加」禁用),
|
|
282
410
|
校验**目录选择器**(标签猜测始终满足宿主语法、取消不改变任何字段、失败被捕获并给出原因、
|
|
283
|
-
没有挂载 ui-workspace
|
|
284
|
-
|
|
285
|
-
|
|
411
|
+
没有挂载 ui-workspace 时字段自动置灰且不会调用),
|
|
412
|
+
- 校验 **`@` source**(触发器的唯一性、`candidates` 返回 thenable、按标签/说明/路径过滤、
|
|
413
|
+
默认工作区关闭时不出候选、pick 产出的 chip 与它序列化出的绝对路径一致、坏 payload 不插入),
|
|
414
|
+
并用最小 React + Button/Switch/Modal/Tooltip 桩**真实渲染**设置页(每个工作区一个开关 →
|
|
415
|
+
POST 一次按键的 settings op → 共享快照更新)与弹窗/面板的各状态文案。
|
|
286
416
|
|
|
287
417
|
## 故障排查
|
|
288
418
|
|
|
419
|
+
### 输入框打 `@` 有「文件与文件夹」,但没有「工作区目录」
|
|
420
|
+
|
|
421
|
+
**先看 设置 → `workspace+` 里的「输入框 `@` 候选」那一行**,它把三种情况分开了:
|
|
422
|
+
|
|
423
|
+
| 那一行显示 | 含义与处理 |
|
|
424
|
+
| --- | --- |
|
|
425
|
+
| `候选源未注册` | 当前页面跑的还是旧 bundle。**重启 `dsh` 并强制刷新页面**(Ctrl+Shift+R) |
|
|
426
|
+
| `候选源已注册,但还没有可用的标签` | 代码是新的,只是没有可登记目录 —— 去工作区行里加标签;或者这个工作区是默认工作区而开关关着 |
|
|
427
|
+
| `候选源已注册,共 N 个标签` | 代码和数据都就位。若菜单里仍看不到,多半是这一组的行被 `@reference` 的文件/会话组挤到可视区之外,滚动菜单看看 |
|
|
428
|
+
|
|
429
|
+
**为什么必须重启而不能只刷新**:客户端 bundle 的 URL 带一个由 `mtime/ctime/size` 派生的 `rev`,
|
|
430
|
+
而 rev 只在宿主重新组合模块图时才更新(`dsh-client-modules` 的 `rebuilt()`)。文件内容是在**首次 GET**
|
|
431
|
+
时读取并缓存的,所以 rev 不变,刷新只会拿回缓存里的旧 bundle。宿主侧的重载驱动是 `dsh-hmr`,
|
|
432
|
+
你 profile 里它的 `root` 是空的 —— 也就是不监听这个插件目录。
|
|
433
|
+
|
|
289
434
|
### 工作区行上没有出现目录按钮
|
|
290
435
|
|
|
291
436
|
1. 先确认宿主半已加载(见下一条的判据)。
|
|
292
|
-
2.
|
|
437
|
+
2. **这个工作区在 设置 → `workspace+` 里被关着**:默认工作区默认就是关的,其它工作区如果显示关了,
|
|
438
|
+
说明有人(或某个旧开关)把它关掉了 —— 打开它自己那一行即可,无需重启或刷新。
|
|
293
439
|
3. 悬停工作区行才会显示操作区,这是壳层的行为。
|
|
294
440
|
4. 兜底入口会在 1.2s 后出现在侧边栏(`workdirs`);如果它出现了,说明行锚点没匹配上 ——
|
|
295
441
|
多半是 DSH 升级改了行 DOM。此时功能仍然可用(走 `workspace_dirs` 工具或兜底入口),
|