dsh-panel-control 0.4.0 → 0.7.2
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 +105 -202
- package/client.js +725 -28
- package/docs/discussion-bundle-evaluated-twice.md +100 -0
- package/docs/sidebar-layout.md +40 -0
- package/index.js +191 -15
- package/package.json +2 -2
- package/scripts/host-smoke.mjs +45 -2
- package/scripts/layout-smoke.mjs +48 -0
- package/scripts/read-page-trace.mjs +104 -0
package/README.md
CHANGED
|
@@ -1,202 +1,105 @@
|
|
|
1
|
-
# dsh-panel-control · 侧栏面板管理
|
|
2
|
-
|
|
3
|
-
管理
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
- 同一组缺失**只提示一次**(记在 `localStorage` 的 `dsh.panel-control.notice-nagged`),
|
|
108
|
-
刷新后还在就保持安静,诊断里仍能看到 `本页缺失` / `已提示过`。
|
|
109
|
-
- 文案改成「刷新后仍然缺:重启 DSH 才会应用新的插件清单」——这才是真正有效的动作。
|
|
110
|
-
- 缺口修复后自动清除记忆,下次真出现新缺口还会提示。
|
|
111
|
-
|
|
112
|
-
## 0.4.0:不再有任何"等一会儿"的机制
|
|
113
|
-
|
|
114
|
-
前面几版为了对付"启动后入口不见了",加过看门狗、自愈改写、延时判断。这些都是**事后补偿**,代价是行为不可预测(改写自己的脚本会让加载器热替换,顺手把邻居插件的重复注册打挂)。0.4.0 把它们全部删掉了:
|
|
115
|
-
|
|
116
|
-
- 没有定时器、没有自查重试、不会改写任何文件。Host 半只做三件事:装路由、读一次插件图并记一行结论、做一次"我这个包的脚本取得到吗"的自检(都是**启动瞬间**完成的同步调用)。
|
|
117
|
-
- Client 半和别的插件一样:`dsh.client.immediately = true`,页面启动时随清单一起加载,立刻把入口挂到侧栏。
|
|
118
|
-
- 安装方式也建议和别的插件一致:用 **npm 安装**(`npm i dsh-panel-control`),而不是 `link:` 本地目录。`link:` 指向的是一个我随时在改的开发目录,任何一次编辑都会让页面手里的版本号作废;装成包之后文件是静止的,不存在这个窗口。
|
|
119
|
-
|
|
120
|
-
## 为什么重启后偶尔看不到入口(0.3.16)
|
|
121
|
-
|
|
122
|
-
DSH 给每个插件的脚本算一个**版本号**,来源是文件的 `mtime / ctime / 大小`,注册表只认它自己在组装那一刻读到的那个版本。页面拿到的是**启动时那份清单**,只要 `client.js` 在清单发出去之后被重写过(编辑器保存、`git` 换行归一化、或者早先那个"自愈"改写),页面手里的版本号就成了作废的号,请求回 404,而页面**不会报错**——入口就这么没了。
|
|
123
|
-
|
|
124
|
-
0.3.16 的处理:
|
|
125
|
-
|
|
126
|
-
- Host 半只对本插件包装注册表的资源查询:页面若拿旧版本号来要,先用**本进程发过的同一版本字节**回答(缓存最近 5 个版本),再退到"用当前版本回答",其余请求原样放行,卸载时撤掉包装。别家插件的行为一点不改。
|
|
127
|
-
- 早先的"自愈"(改写 `client.js` 强制热加载)**默认关闭**:它会重跑所有插件的注册,邻居不幂等就会丢入口——`dsh-workspace-control` 就是这么被弄掉的。需要时用 `DPC_NUDGE=on` 显式打开。
|
|
128
|
-
- `host.log` 每次 apply 打两行结论:`graph: lists|does not list <包名>` 和 `graph: own bundle at rev … answers 200|404`,一次就能分清是"没进清单"还是"清单有但取不到"。
|
|
129
|
-
|
|
130
|
-
## 代码审查后的加固(0.3.11)
|
|
131
|
-
|
|
132
|
-
一轮完整审查(正确性 / 安全 / 可维护性 / 性能)之后改掉的东西,按主题记在这里,免得以后又踩回去。
|
|
133
|
-
|
|
134
|
-
**安全**
|
|
135
|
-
|
|
136
|
-
- 路由只认自己这一页:以前只校验「Host 和 Origin 都是 loopback」,于是本机任何一个 dev server 的页面(`http://127.0.0.1:3000`)都能调 `save` / `classify` / `report`——改布局、烧模型额度。现在要求 **端口也一致**,`Sec-Fetch-Site` 不是 `same-origin`/`none` 的一律 403,`dsh-app:`/`file:` 两种桌面端协议放行。
|
|
137
|
-
- 体积上限移到解析之前:先 `request.text()` 量长度再看,超限直接 413(以前是先 `json()` 整个解析完再比,等于没有上限)。
|
|
138
|
-
|
|
139
|
-
**正确性**
|
|
140
|
-
|
|
141
|
-
- `mapButtons`:入口与行按钮的映射先按 `aria-label` 认,只有「剩余数量相等且标签逐一相符(或按钮根本没有标签)」才按位置补。以前无条件按位置兜底,一旦头部插入一行就整体错位,隐藏会隐藏错行。
|
|
142
|
-
- `hydratePrefs`:冷启动读 Host 期间用户改过的设置不再被回来的旧快照覆盖(`commitRevision` 比对)。
|
|
143
|
-
- `classifyNow`:并发闸(`classifyToken`),连点「重新分类」时只有最新一次的结果会落地。
|
|
144
|
-
- `autoFiled`:只记这次真正归了组的条目,模型漏掉的下次还能补。
|
|
145
|
-
- 写盘串行化(`writeChain`):两个窗口同时保存不再抢同一个 `.tmp`。
|
|
146
|
-
- 热替换后重置 `clientModules` 缓存,避免拿到上一代的图。
|
|
147
|
-
|
|
148
|
-
**资源与性能**
|
|
149
|
-
|
|
150
|
-
- `sanitize` 给所有数组/字典设上限并去重(order/hidden 200、autoFiled 400、分组 24、assign 400、名称 40 字):256KB 的恶意文件不再能变出几千个分隔节点。
|
|
151
|
-
- `MutationObserver` 只在找到列表之后盯那个列表,并给 `apply` 加 100ms 最小间隔——聊天流式输出不再每帧触发全量重排。
|
|
152
|
-
|
|
153
|
-
**可维护性**
|
|
154
|
-
|
|
155
|
-
- 常量具名(`SAVE_DEBOUNCE_MS` / `AUTO_DELAY_MS` / `AUTO_RETRY_MS` / `ORDER_UNMANAGED_TAIL`)。
|
|
156
|
-
- 入口行与分组行的拖拽逻辑合并成 `dragProps()`,两边只传提交函数。
|
|
157
|
-
- `flatten()` 缺语言时回退到另一种语言,不再渲染 `undefined`。
|
|
158
|
-
- Host 的失败按 `code` 本地化(超时 / 模型失败 / 回答不可解析),不再把英文原文拼进中文界面。
|
|
159
|
-
- `checkInventory` 5 秒节流(窗口每次聚焦都会触发)。
|
|
160
|
-
- 页面列改成 `max-width:1056px; margin-inline:auto`:窄窗口下不再横向溢出(以前 `50% - 480px` 的算法在窄容器里会把列推到屏幕外,右侧按钮点不到)。
|
|
161
|
-
|
|
162
|
-
回归测试:`npm run check`(Host 半 17 项,含同机异端口 / `same-site` / 自定义协议 / 超大 body 四条安全用例),`.scratch/harness.html` 37 项(新增 `mapButtons` 四条与 `sanitize` 上限四条)。
|
|
163
|
-
|
|
164
|
-
## 让 DSH 崩掉的两件事
|
|
165
|
-
|
|
166
|
-
这两条都是真崩过的,不是推测。
|
|
167
|
-
|
|
168
|
-
**一、半装的插件是致命的。** `dsh.profile.bundles` 里写了一个名字,但 `dependencies` 里没有、`node_modules` 里也没有可解析的包,Host 启动时就会去加载这个不存在的包;这个失败发生在 Cordis 的逐个插件错误边界之外,于是整个 Host 进程直接退出——不是「这个插件没加载」,是「应用打不开」。手改 `bundles` 最容易造成这个状态。
|
|
169
|
-
|
|
170
|
-
所以安装拆成顺序固定、每步都校验的三步(依赖 → 链接 → 清单),任何一步失败就整体回滚:
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
npm run enable # 装进 desktop profile,最后校验整份清单
|
|
174
|
-
npm run enable -- --disable # 三步一起撤掉
|
|
175
|
-
npm run doctor # 只检查:每个 bundle 是否都能解析
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
(这三个脚本随包发布,装成 npm 包后也能直接跑;`.scratch/harness.html` 只在仓库里。)
|
|
179
|
-
|
|
180
|
-
`doctor` 也会揪出别的断链插件(指向已删除目录的 `link:` 依赖、名字与 `package.json` 不符的包),它只读真实 profile,不改任何东西。
|
|
181
|
-
|
|
182
|
-
**二、Node 定时器里的回调没有错误边界。** `setTimeout` / `Promise` 回调不在 Cordis 的 `apply` 调用栈里,那里抛出的异常没人接,就是进程级 fatal。第一个版本正是在 20 秒看门狗里调了一个已被删掉的函数,启动 20 秒后必崩,日志里只留一行「客户端半没上报」。
|
|
183
|
-
|
|
184
|
-
现在每个延迟回调都自己包 `try/catch`,调用点再加一层 `.catch(...)`;`npm run check` 里的 `scripts/host-smoke.mjs` 会劫持 `setTimeout` 并把回调**真的执行一遍**,再驱动 `inventory` / `report` / 403 三条路由。对着 0.3.6 那份代码跑,它会清清楚楚地报:
|
|
185
|
-
|
|
186
|
-
```
|
|
187
|
-
FAIL timer(20000ms) survived: watchFirstClientBoot is not defined
|
|
188
|
-
FAIL inventory answers: handle is not defined
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
npm run smoke # 当前代码:全绿
|
|
193
|
-
npm run smoke -- <文件> # 跑别的副本,用来复现回归
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## 边界
|
|
197
|
-
|
|
198
|
-
- 自动分类会把**入口名称**(如「插件市场」「工作区管理」,不含任何会话内容、路径或凭据)发给当前 profile 配置的模型服务商;不想发就别开这个开关,它的结果也可以在面板里随手改。
|
|
199
|
-
- 分组是"建议":模型只提出组名与归属,你可以在每行右侧的下拉里改成别的组,或直接改组名。
|
|
200
|
-
- 宿主若改动 `panelList` 的 class 命名或行结构,需要同步更新选择器;已做多级回退,但仍是 DOM 适配层。
|
|
201
|
-
- 隐藏/分组只影响本机这份侧栏渲染,不改动任何插件自身配置。
|
|
202
|
-
- 插件销毁时移除注入的 `style` 节点、observer 与轮询定时器;布局存在 `~/.dsh/panel-control/prefs.json`,重装或重启后接着用(想清空就在面板里点「恢复默认」,或直接删掉这个文件)。
|
|
1
|
+
# dsh-panel-control · 侧栏面板管理
|
|
2
|
+
|
|
3
|
+
管理 DeepSeek Harness 侧栏顶部那一列插件入口(插件、插件市场、自动化任务、灵枢、工作区管理……):排序、隐藏、分组、折叠;导航项变多时,还会托住下方的会话与工作区列表。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- **排序** — 拖动行首把手任意换位;一键按名称 A→Z;一键恢复注册时的默认顺序。
|
|
8
|
+
- **隐藏** — 行右侧的眼睛图标。隐藏项仍留在列表里,随时放回;不影响插件是否加载,也不写插件自己的配置。
|
|
9
|
+
- **分组** — 新建分组(如「创作」「系统」)并把入口分进去,侧栏会在组与组之间插入带标题的分隔线。组的先后由设置里的分组列表拖动决定,与注册顺序、分类结果无关。
|
|
10
|
+
- **分组折叠** — 点分组标题收起该组,只留标题与计数;`Enter` / 空格同样可切换,状态写进配置。侧栏收成图标条时不显示标题,也不折叠。
|
|
11
|
+
- **侧栏溢出兜底** — 导航项变多时,官方侧栏会把下方的会话/工作区列表压到 0 高且无法滚动([讨论 #12083](https://github.com/deepseek-ai/deepseek-harness/discussions/12083))。本插件给导航列表加上限高与滚动,给下方区域留最小高度。
|
|
12
|
+
- **自动分类** — 用当前 profile 的默认模型读入口名称,自动分成 2–4 组。只处理没见过的入口,不会改动你排好的顺序;随时可手动「重新分类」。
|
|
13
|
+
- **面板管理自己不可隐藏** — 避免把自己锁在门外;未接管的节点(如「更多」)始终排在最后。
|
|
14
|
+
|
|
15
|
+
## 界面
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
侧栏分组后的效果:
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
两个入口:侧栏导航里的「面板管理」行,以及 **设置 → 面板管理**(同一套管理界面整块嵌在设置页)。
|
|
24
|
+
|
|
25
|
+
## 安装
|
|
26
|
+
|
|
27
|
+
从 npm 安装(推荐):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd C:/Users/<you>/.dsh/profiles/desktop
|
|
31
|
+
npm install dsh-panel-control@0.7.2
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
把包名加进同一目录 `package.json` 的 `dsh.profile.bundles`:
|
|
35
|
+
|
|
36
|
+
```jsonc
|
|
37
|
+
"dsh": { "profile": { "bundles": [ ..., "dsh-panel-control" ] } }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`npm install` 会把包(含 `client.js` 与 `cordis.patch.yml`)放进 profile 的 `node_modules`,不需要再建目录链接。
|
|
41
|
+
|
|
42
|
+
本地开发用 `link:` 指向工作副本,再建目录链接(免管理员):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cd C:/Users/<you>/.dsh/profiles/desktop
|
|
46
|
+
cmd //c mklink /J node_modules\dsh-panel-control D:\Github\dsh-panel-control
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
装好后需要一次页面重载才会加载新的 bundle(界面里的刷新按钮,或重启 DSH)。
|
|
50
|
+
|
|
51
|
+
## 侧栏布局与折叠
|
|
52
|
+
|
|
53
|
+
官方 `dsh-client-ui-sidebar` 把导航列表设为 `flex: none`,把下方区域设为 `flex: 1; overflow: hidden`。导航项一多,列表独占高度,下方被压成 0 高且没有可滚动容器——滚轮看起来"失灵",其实是没东西可滚。
|
|
54
|
+
|
|
55
|
+
本插件不改宿主代码,只从三个属性出发写 CSS:
|
|
56
|
+
|
|
57
|
+
| 属性 | 加在哪 | 作用 |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| `data-dpc-list` | 导航列表容器 | `overflow-y: auto` + 高度上限 `max(120px, var(--dpc-panellist-max, 45vh))` |
|
|
60
|
+
| `data-dpc-unbounded` | 同上(选择「不限」时) | 取消高度上限,列表按内容撑开 |
|
|
61
|
+
| `data-dpc-region` | 列表下方的会话/工作区区域 | 保留最小高度 `min(120px, 18vh)` |
|
|
62
|
+
|
|
63
|
+
设置页「侧栏布局」卡片里可以调整:
|
|
64
|
+
|
|
65
|
+
- 高度兜底开关,以及 35 / 45 / 55 / 不限 四档上限;
|
|
66
|
+
- 分组折叠开关,以及「全部收起 / 全部展开」;
|
|
67
|
+
- 折叠状态按分组 id 记在配置里,重启后保留。
|
|
68
|
+
|
|
69
|
+
## 配置与存储
|
|
70
|
+
|
|
71
|
+
布局写在 Host 侧:`~/.dsh/panel-control/prefs.json`(`~` 是这个 profile 的用户目录)。包含顺序、隐藏项、分组、折叠状态、布局开关与分类结果。
|
|
72
|
+
|
|
73
|
+
页面 origin 是 Host 的 loopback 端口,每次启动都会换端口,`localStorage` 会因此"看起来每次清空"——所以真实存储放在 Host 侧,`localStorage` 只留镜像;读不到 Host 时才回退到镜像。配置按安装维度生效,不跟随工作区。
|
|
74
|
+
|
|
75
|
+
## 排错
|
|
76
|
+
|
|
77
|
+
- **侧栏没有「面板管理」**:先重载一次页面。仍没有,看 `~/.dsh/panel-control/host.log` 与 `client.log`。
|
|
78
|
+
- **插件管理显示运行,界面却没变化**:页面多半还在用旧的 bundle revision(刚改过 `client.js` 时常见),重载一次即可。
|
|
79
|
+
- **改了代码没反应**:确认 `BUILD_STAMP` 与 `package.json` 的版本一致,并跑一遍 `npm run check`。
|
|
80
|
+
|
|
81
|
+
## 开发与测试
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm run check # 语法检查 + 宿主路由 smoke + 布局/版本静态断言
|
|
85
|
+
npm run smoke # 只跑宿主路由用例
|
|
86
|
+
npm run doctor # 体检 profile 安装状态
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- `.scratch/harness.html`:引擎测试页(伪造 DOM 与 ctx),覆盖排序、隐藏、分组、折叠、脏数据清洗。
|
|
90
|
+
- `scripts/host-smoke.mjs`:驱动宿主侧路由,覆盖设置读写、分类请求与 bundle 兜底。
|
|
91
|
+
- `scripts/layout-smoke.mjs`:断言侧栏布局的 CSS 钩子与关键数值,并校验 `BUILD_STAMP` 与版本号一致。
|
|
92
|
+
|
|
93
|
+
进一步材料:[docs/sidebar-layout.md](docs/sidebar-layout.md)(布局与折叠实现)、[docs/ui-design-notes.md](docs/ui-design-notes.md)(界面规范与踩坑)、[docs/discussion-bundle-evaluated-twice.md](docs/discussion-bundle-evaluated-twice.md)(bundle 被求值两次的事故复盘)。
|
|
94
|
+
|
|
95
|
+
## 边界
|
|
96
|
+
|
|
97
|
+
- 自动分类会把入口名称发给当前 profile 配置的模型服务商;不想要就关掉开关,分组仍可手动编辑。
|
|
98
|
+
- 分类结果只是建议,可以整体重排或改名。
|
|
99
|
+
- 依赖宿主侧栏的 DOM 结构(`[class*="panelList"]` 与面板按钮的 `aria-label`),官方大改版时需要跟进。
|
|
100
|
+
- 只写 `style.order`、`style.display` 与 `data-dpc-*` 属性,不移动 React 节点,不改宿主代码。
|
|
101
|
+
- 排序、隐藏、分组都只影响本机这份安装。
|
|
102
|
+
|
|
103
|
+
## 许可
|
|
104
|
+
|
|
105
|
+
MIT
|