dsh-sidecard-ask 1.1.0 → 1.2.1
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/CHANGELOG.md +49 -0
- package/README.md +71 -6
- package/client.js +156 -23
- package/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.1
|
|
4
|
+
|
|
5
|
+
**主题:核对 DSH 0.2.0-rc.1 的适配性,并修掉由此暴露的一个真实缺陷。**
|
|
6
|
+
|
|
7
|
+
核对(本机运行中的就是 0.2.0-rc.1,`@deepseek-ai/dsh` 的 `next` 通道):
|
|
8
|
+
|
|
9
|
+
- 包内容层面:`node tools/compat-probe.mjs 0.1.7-rc.2 0.2.0-rc.1` → 16 项能力**逐项一致**,没有任何依赖被移除或改名;
|
|
10
|
+
- 运行实例上:宿主半 `v1.2.0` 正常(`sideEngine`、provider `spawn,fork`、`capabilities.parent` 均可用);
|
|
11
|
+
客户端半在 `shell.overlay` / `conversation.input.right` 都已注册;`standardProps` 仍含 `sessionId` 与 `inputActions`;
|
|
12
|
+
真机端到端作答 `start→delta×31→status→done`(1.36 s)通过。新增的 `sessions.fork`/`uiWorkspace.forkSession`
|
|
13
|
+
可选参数 `onCreated` 是纯增量,不影响既有调用。
|
|
14
|
+
|
|
15
|
+
**修掉的缺陷**:`sidebar.right.pane.tab` 的运行清单里只有 `dsh-better-sidebar:sidecard-ask:card`,
|
|
16
|
+
没有本插件原生右侧栏的 `sidecard-ask:card`——两个适配器用了同一个 tab kind,而 better-sidebar 会把每个 tab 描述符
|
|
17
|
+
镜像注册进**同一个**原生注册表(band `extension`),该注册表对同 band 同 kind 的二次注册抛错,后注册的一方静默失败。
|
|
18
|
+
|
|
19
|
+
- better-sidebar 承载面改用独立类型 `sidecard-ask:card:workbench`(`CARD_KIND_BETTER`),两端不再重叠;
|
|
20
|
+
- `contract-test` 新增"surface kinds must not collide"一节:**在测试里模拟该注册表的重复 kind 抛错规则**
|
|
21
|
+
与 better-sidebar 的镜像注册行为,若两者再共用 kind,测试立即失败;
|
|
22
|
+
- 设置页自检行改为同时报告**两个适配器**(原先原生适配器的错误在界面上不可见,只能靠翻槽位清单发现)。
|
|
23
|
+
|
|
24
|
+
自测 289 → **295 项**(verify 53 / contract 124 / smoke 118)。
|
|
25
|
+
|
|
26
|
+
## 1.2.0
|
|
27
|
+
|
|
28
|
+
**主题:核对并加固与 dsh-better-sidebar 0.22.1 的适配。**
|
|
29
|
+
|
|
30
|
+
核对方式(可复现):把 0.22.0 与 0.22.1 的发布产物都拉下来解包,逐文件 SHA256 比对。结论:
|
|
31
|
+
|
|
32
|
+
- 消费端契约 `lib/types/client/service.d.ts` **除 `SIDEBAR_SERVICE_VERSION` 常量外逐字节相同**;
|
|
33
|
+
`SIDEBAR_FEATURES`、`dsh.client.inject`、peer 依赖均未变;
|
|
34
|
+
- 0.22.1 唯一的大体量改动是它**自己** `src/client/native/index.ts` 的健壮性修复(`disposeSafely` +
|
|
35
|
+
槽位注册失败时回滚已注册的 tab 类型),不涉及本插件调用的任何字段;
|
|
36
|
+
- 本插件用到的 `registerTab / openTab(seed, scope) / closeTab / features / version` 全部仍在。
|
|
37
|
+
|
|
38
|
+
即 **0.22.1 下无需改动即兼容**。据此做了三处加固:
|
|
39
|
+
|
|
40
|
+
1. **按能力而非版本判断**:适配器仅在 `features` 含 `tabMeta` 时可用(该能力自 better-sidebar 0.12 起提供,
|
|
41
|
+
更早的版本会打开一个读不到 `meta.cardId` 的空 tab);不满足时 `auto` 跳到原生右侧栏/内置浮层,
|
|
42
|
+
并在自检里给出 `no-tab-meta`。
|
|
43
|
+
2. **原生承载面注册回滚**:先注册 tab 类型再注册槽位,槽位注册抛错时释放已占用的类型 id
|
|
44
|
+
(正是 0.22.1 在自己原生胶水里修的同类问题,否则该 kind 会永久占用、渲染宿主"无实现"的空面)。
|
|
45
|
+
3. **运行时可见**:设置页「运行自检」新增一行,显示侧边卡片插件**自己报告的 `version` 与能力项数**,
|
|
46
|
+
以及本插件对它的判定(可用 / 版本过旧 / 未检测到)。
|
|
47
|
+
|
|
48
|
+
自测 276 → **289 项**(verify 53 / contract 118 / smoke 118),新增用例覆盖:0.22.1 形状的服务被接受、
|
|
49
|
+
`openTab(seed, scope)` 的字段与作用域、缺 `tabMeta` 时被拒绝并落到浮层、缺 `registerTab` 时被拒绝、
|
|
50
|
+
槽位注册失败时类型被释放且适配器转为不可用。
|
|
51
|
+
|
|
3
52
|
## 1.1.0
|
|
4
53
|
|
|
5
54
|
**改名:`dsh-selection-followup` → `dsh-sidecard-ask`**(显示名「划词追问」→「侧边卡片追问」)。
|
package/README.md
CHANGED
|
@@ -152,14 +152,66 @@ plugin_manager(action="install_bundle", target="D:\\Users\\34332\\AI\\dsh-sideca
|
|
|
152
152
|
|
|
153
153
|
| 顺序 | 承载面 | 依赖 | 失败时 |
|
|
154
154
|
|---|---|---|---|
|
|
155
|
-
| 1 | DSH 原生右侧栏 | `ctx.sidebarRightTabs`(注册 tab 类型)+ `ctx.sidebarRight`(`openTab`),并占用槽位 `sidebar.right.pane.tab` / `…tab.title` | 记 `surfaces.native.error
|
|
156
|
-
| 2 | **dsh-better-sidebar**(侧边卡片插件) | 客户端服务 `ctx.betterSidebar`:`registerTab` + `openTab(seed, scope)`;卡片 id 走 `tab.meta.cardId
|
|
155
|
+
| 1 | DSH 原生右侧栏 | `ctx.sidebarRightTabs`(注册 tab 类型)+ `ctx.sidebarRight`(`openTab`),并占用槽位 `sidebar.right.pane.tab` / `…tab.title` | 记 `surfaces.native.error`,落到下一层;**类型注册会回滚**(见下) |
|
|
156
|
+
| 2 | **dsh-better-sidebar**(侧边卡片插件) | 客户端服务 `ctx.betterSidebar`:`registerTab` + `openTab(seed, scope)`;卡片 id 走 `tab.meta.cardId`,因此**要求其 `features` 含 `tabMeta`** | 同上(缺 `tabMeta` 时记 `no-tab-meta` 并跳过,而不是开一个空 tab) |
|
|
157
157
|
| 3 | **内置浮层卡片**(默认兜底) | 只需要 `shell.overlay` 槽位 | ——(这是保底面,永远可用) |
|
|
158
158
|
|
|
159
159
|
- 装了 `dsh-better-sidebar`:卡片以它的 tab 形式出现在它的面板里,关闭卡片会同时 `closeTab`,不残留空 tab。
|
|
160
160
|
- 没装:自动使用内置浮层卡片(右下角卡片栈),功能完全一致——**这条路径是本插件的默认与保底路径**。
|
|
161
161
|
- 强制指定了一个不可用的承载面:回退到内置浮层,并在卡片上标注「已回退」。
|
|
162
162
|
|
|
163
|
+
### 5.1 与 dsh-better-sidebar 0.22.1 的适配核对(2026-09-28)
|
|
164
|
+
|
|
165
|
+
做法:把 0.22.0 与 0.22.1 的发布产物都拉下来、解包、逐文件比对(`npm pack` + SHA256)。
|
|
166
|
+
|
|
167
|
+
| 检查项 | 结果 |
|
|
168
|
+
|---|---|
|
|
169
|
+
| 消费端契约 `lib/types/client/service.d.ts` | **除 `SIDEBAR_SERVICE_VERSION` 常量外逐字节相同** → `BetterSidebarService` 的方法/参数/返回类型没变 |
|
|
170
|
+
| `SIDEBAR_FEATURES`(能力清单) | **未变**(`tabMeta` 等仍在;契约承诺"Features are never removed") |
|
|
171
|
+
| `dsh.client.inject` / peer 依赖 | 未变(仍是 locale / ui-slots / ui-conversation / ui-sidebar-right / client-modules) |
|
|
172
|
+
| `src/client/native/index.ts`(0.22.1 唯一大体量改动) | 是它**自己原生胶水的健壮性修复**:`disposeSafely` + 槽位注册失败时回滚已注册的 tab 类型;不涉及我们调用的任何字段 |
|
|
173
|
+
| 本插件实际用到的面 | `registerTab({id,title,description,order,dedupeKey,component})`、`openTab({type,id,title,meta},{sessionId})`、`closeTab(id)`、`features`、`version` —— 全部仍在 |
|
|
174
|
+
|
|
175
|
+
**因此 0.22.1 下适配为「无需改动即兼容」**;本轮据此做了三处加固:
|
|
176
|
+
|
|
177
|
+
1. **能力门控而非版本判断**:适配器只在 `features` 含 `tabMeta` 时可用(0.12 起才有,旧版会开出空 tab);
|
|
178
|
+
不满足时 `auto` 直接跳到原生右侧栏/内置浮层,设置页自检里写明原因(`no-tab-meta`)。
|
|
179
|
+
2. **原生承载面类型注册回滚**:先注册 tab 类型、再注册槽位;槽位注册抛错(reload/teardown 期的失活 context)时
|
|
180
|
+
**释放已占用的类型 id**——这正是 0.22.1 在自己原生胶水里修的同一类问题(否则该 kind 会永久占用并显示宿主"没有实现"的空面)。
|
|
181
|
+
3. **运行时可见**:设置页「运行自检」新增一行,直接显示**侧边卡片插件自己的 `version` 与能力项数**,
|
|
182
|
+
以及本插件对它的判定(可用 / 版本过旧 / 未检测到),不再依赖 package.json 的声明。
|
|
183
|
+
|
|
184
|
+
> 注意:`version` 是加载到浏览器里的那份模块报告的版本。页面刷新后即可用它确认"跑的是不是 0.22.1"。
|
|
185
|
+
|
|
186
|
+
### 5.2 与 DSH 0.2.0-rc.1 的适配核对(2026-09-29)
|
|
187
|
+
|
|
188
|
+
本机实际运行的就是 **0.2.0-rc.1**(`@deepseek-ai/dsh` 的 `next` 通道;`latest` 仍是 0.1.7-rc.2)。核对分两层:
|
|
189
|
+
|
|
190
|
+
**① 包内容层面**(`node tools/compat-probe.mjs 0.1.7-rc.2 0.2.0-rc.1`):
|
|
191
|
+
16 项能力**逐项与 0.1.7-rc.2 完全一致**——`shell.overlay`、`conversation.input.right`、`settings.section`、
|
|
192
|
+
`data-slot` 锚点、原生右侧栏、`sessions.using/retain`、归档门、`agent/assistant-stream`、子代理、`tools.schemas`、
|
|
193
|
+
`webServer` 路由……全部 ✅。
|
|
194
|
+
|
|
195
|
+
**② 在运行中的实例上实测**:
|
|
196
|
+
|
|
197
|
+
| 检查 | 结果 |
|
|
198
|
+
|---|---|
|
|
199
|
+
| 宿主半是否加载 | ✅ `/sidecard-ask/api/state` 返回 `dsh-sidecard-ask v1.2.0`,`sideEngine=true`(provider `spawn,fork`)、`capabilities.parent` 正常 |
|
|
200
|
+
| 客户端半是否注册 | ✅ `shell.overlay` 有 `sidecard-ask`(order 45、active)、`conversation.input.right` 有 `sidecard-ask-session-probe`、`settings.section` 有设置页 |
|
|
201
|
+
| 依赖的槽位与标准 prop | ✅ `conversation.input.right` 仍是 list/session,`standardProps` 仍含 **`sessionId`** 与 **`inputActions`** |
|
|
202
|
+
| 依赖的客户端服务 | ✅ `sessions`(`retain`/`using`/`scope`)、`slots`、`locale`、`layout`、`theme` 全在;`sessions.fork`/`uiWorkspace.forkSession` 只是**新增**了可选 `onCreated`,不影响既有调用 |
|
|
203
|
+
| 端到端作答 | ✅ 真机 `start→delta×31→status→done`(1.36 s,`streamSource=frames`),只读工具白名单照常生效 |
|
|
204
|
+
|
|
205
|
+
**③ 由此发现并修掉的真实缺陷(1.2.1)**:运行中的 `sidebar.right.pane.tab` 清单里只有
|
|
206
|
+
`dsh-better-sidebar:sidecard-ask:card`,**没有我的原生 `sidecard-ask:card`**。原因是两个适配器用了**同一个 tab kind**:
|
|
207
|
+
better-sidebar 会把收到的每个 tab 描述符**镜像注册进同一个原生注册表**(`sidebarRightTabs.register`,band `extension`),
|
|
208
|
+
而该注册表对*同 band 同 kind 的二次注册直接抛错*(源码注释:*"Everything else colliding on a kind throws"*)——
|
|
209
|
+
后注册的那一方就此失败,而且**在界面上不可见**。修法:
|
|
210
|
+
|
|
211
|
+
1. better-sidebar 承载面改用独立类型 **`sidecard-ask:card:workbench`**,两端 kind 不再重叠;
|
|
212
|
+
2. `contract-test` **模拟该注册表的"重复 kind 抛错"规则**,此类冲突今后会直接让测试失败;
|
|
213
|
+
3. 设置页自检行改为**同时报告两个适配器**的状态(此前原生适配器的错误压根看不到)。
|
|
214
|
+
|
|
163
215
|
---
|
|
164
216
|
|
|
165
217
|
## 六、未知 DSH API:占位接口与替换方式
|
|
@@ -330,7 +382,7 @@ node test/smoke-test.mjs # 端到端:流式/截断/取消/持久化/失
|
|
|
330
382
|
```
|
|
331
383
|
|
|
332
384
|
三个脚本都以 `process.exitCode` 反映结果,失败会列出具体条目;测试会把 `DSH_HOME` 指向临时目录,不会污染真实配置。
|
|
333
|
-
当前规模:verify 53 项 + contract
|
|
385
|
+
当前规模:verify 53 项 + contract 118 项 + smoke 118 项 = **289 项全部通过**。
|
|
334
386
|
|
|
335
387
|
### 10.2 版本能力探测(§7 矩阵的来源)
|
|
336
388
|
|
|
@@ -410,14 +462,27 @@ Host: ctx.subagents.start('spawn')
|
|
|
410
462
|
## 十三、发布流程
|
|
411
463
|
|
|
412
464
|
```powershell
|
|
413
|
-
npm whoami
|
|
465
|
+
npm whoami # 先确认登录态;本机用的是 granular Publish token
|
|
414
466
|
node test/verify.mjs; node test/contract-test.mjs; node test/smoke-test.mjs
|
|
415
|
-
npm publish --access public
|
|
467
|
+
npm publish --access public # 1.0.1 那次被 npm 暂存(staged)后才转正;再遇到可用 --otp=<验证码>
|
|
468
|
+
git push origin main # 仓库:https://github.com/HERO476/dsh-sidecard-ask
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
改名/迁移时的额外两步(1.1.0 实际做过):
|
|
472
|
+
|
|
473
|
+
```powershell
|
|
474
|
+
plugin_manager(action="remove_bundle", target="<旧包名>") # 卸旧 bundle(同时移除 profile 里的 link)
|
|
475
|
+
plugin_manager(action="install_bundle", target="D:\...\<新目录>")
|
|
476
|
+
npm deprecate "<旧包名>@*" "Renamed to <新包名> - <原因>" # 旧名指路,避免别人再装到废弃名
|
|
416
477
|
```
|
|
417
478
|
|
|
418
479
|
版本号同时出现在三处,必须一致:`package.json` 的 `version`、`index.js` 的 `PLUGIN_VERSION`、`client.js` 的 `api.version`
|
|
419
480
|
(`test/verify.mjs` 会断言前两处;第三处在契约测试中同样被断言)。
|
|
420
481
|
|
|
482
|
+
**发布前必查"地址类信息"**(1.0.0 的教训):包名不能只查 npm —— 还要查 GitHub 仓库名是否已被占用,以及
|
|
483
|
+
[`awesome-dsh-plugin` 收录索引](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/tree/main/data/plugins)
|
|
484
|
+
里是否已有同名插件(本插件第一次改名就是因为漏查了后者)。
|
|
485
|
+
|
|
421
486
|
---
|
|
422
487
|
|
|
423
488
|
## 十四、已知限制(含未在本机验证的部分)
|
|
@@ -435,5 +500,5 @@ npm publish --access public
|
|
|
435
500
|
本仓库当前文件比运行中的宿主模块新(1.0.1 的「归档父会话不再一刀切 / 持久化 chunk 流式源 / 能力门控 / 路由降级」),
|
|
436
501
|
**重启后**这些改动才生效;`/state` 的 `version` 与 `capabilities.parent` 字段可以直接确认是否已加载新版。
|
|
437
502
|
6. **除 0.1.7-rc.2 外,其余版本只做了产物层验证**:见 §7.3——插件在 0.1.2-rc.1 … 0.1.6-alpha.2 上安装启动过**没有**得到验证,
|
|
438
|
-
验证到的是"这些版本缺哪些 API + 插件对每个缺失都有分支",分支本身由
|
|
503
|
+
验证到的是"这些版本缺哪些 API + 插件对每个缺失都有分支",分支本身由 289 项自测覆盖。
|
|
439
504
|
|
package/client.js
CHANGED
|
@@ -42,9 +42,27 @@ window.__ModuleLoader__.load({
|
|
|
42
42
|
settings: 'sidecard-ask-settings',
|
|
43
43
|
sessionProbe: 'sidecard-ask-session-probe',
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Tab kind served by the DSH native right rail (also its implementation id).
|
|
47
|
+
*/
|
|
46
48
|
const CARD_KIND = 'sidecard-ask:card'
|
|
47
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Tab type served by the dsh-better-sidebar plugin.
|
|
52
|
+
*
|
|
53
|
+
* It MUST differ from {@link CARD_KIND}: better-sidebar mirrors every tab
|
|
54
|
+
* descriptor it is given into the SAME native registry
|
|
55
|
+
* (`sidebarRightTabs.register`, id `dsh-better-sidebar:<id>`, band
|
|
56
|
+
* `extension`), and that registry THROWS when a kind already has a
|
|
57
|
+
* registration in the same band ("Everything else colliding on a kind
|
|
58
|
+
* throws"). Sharing one kind made the two adapters collide: whichever
|
|
59
|
+
* registered second failed — observed live on 0.2.0-rc.1, where the pane
|
|
60
|
+
* listed `dsh-better-sidebar:sidecard-ask:card` and no native
|
|
61
|
+
* `sidecard-ask:card` at all. `test/contract-test.mjs` now emulates that
|
|
62
|
+
* registry rule so the collision cannot come back.
|
|
63
|
+
*/
|
|
64
|
+
const CARD_KIND_BETTER = 'sidecard-ask:card:workbench'
|
|
65
|
+
|
|
48
66
|
/**
|
|
49
67
|
* Client-side mirror of the Host `DEFAULT_CONFIG`. The client needs values
|
|
50
68
|
* before `/state` answers (first paint) and when the Host is unreachable;
|
|
@@ -167,6 +185,10 @@ window.__ModuleLoader__.load({
|
|
|
167
185
|
diagZones: '区域锚点(data-slot)',
|
|
168
186
|
diagZonesMissing: '缺失,区域过滤已停用',
|
|
169
187
|
diagSlots: '槽位落点',
|
|
188
|
+
diagSidecard: '侧边卡片插件(dsh-better-sidebar)',
|
|
189
|
+
diagFeatures: '项能力',
|
|
190
|
+
diagNoTabMeta: '版本过旧:没有 tabMeta 能力,已跳过它改用其它承载面',
|
|
191
|
+
diagNativeKind: 'kind',
|
|
170
192
|
toolGuardUnavailable: '该 provider 不支持工具白名单,本次作答继承了当前会话的工具',
|
|
171
193
|
toolGuardInherited: '按配置继承当前会话的工具',
|
|
172
194
|
diagUnreachable: '宿主接口不可达:{msg}',
|
|
@@ -266,6 +288,10 @@ window.__ModuleLoader__.load({
|
|
|
266
288
|
diagZones: 'Zone anchors (data-slot)',
|
|
267
289
|
diagZonesMissing: 'missing — region filtering disabled',
|
|
268
290
|
diagSlots: 'Slot landing',
|
|
291
|
+
diagSidecard: 'Side-card plugin (dsh-better-sidebar)',
|
|
292
|
+
diagFeatures: 'capabilities',
|
|
293
|
+
diagNoTabMeta: 'too old: no tabMeta capability, skipped in favour of another surface',
|
|
294
|
+
diagNativeKind: 'kind',
|
|
269
295
|
toolGuardUnavailable: 'This provider supports no tool allow-list, so the answer inherited the session tools',
|
|
270
296
|
toolGuardInherited: 'Inherits the session tools, as configured',
|
|
271
297
|
diagUnreachable: 'Host API unreachable: {msg}',
|
|
@@ -859,21 +885,44 @@ window.__ModuleLoader__.load({
|
|
|
859
885
|
error: null,
|
|
860
886
|
dispose: null,
|
|
861
887
|
features: [],
|
|
888
|
+
/**
|
|
889
|
+
* Why this adapter is (not) usable, in the settings page's own words.
|
|
890
|
+
* `null` means "usable"; a string explains the refusal.
|
|
891
|
+
*/
|
|
892
|
+
reason: 'not-detected',
|
|
893
|
+
/**
|
|
894
|
+
* Reported by the plugin itself (`BetterSidebarService.version`), so the
|
|
895
|
+
* settings page can say which side-card plugin version is really in use.
|
|
896
|
+
*/
|
|
897
|
+
version: null,
|
|
898
|
+
/**
|
|
899
|
+
* Usable only when the service advertises the `tabMeta` capability: the
|
|
900
|
+
* card id travels in `tab.meta`, and `SIDEBAR_FEATURES` has listed
|
|
901
|
+
* `tabMeta` since v0.12.0 with newer versions only ever ADDING features
|
|
902
|
+
* ("Features are never removed"). A pre-0.12 instance would open an
|
|
903
|
+
* empty tab, so it is skipped and the ladder continues to the native
|
|
904
|
+
* right rail or the built-in flow card instead.
|
|
905
|
+
*/
|
|
862
906
|
available() {
|
|
863
|
-
|
|
907
|
+
if (this.service === null || this.error !== null) return false
|
|
908
|
+
return Array.isArray(this.features) && this.features.includes('tabMeta')
|
|
864
909
|
},
|
|
865
910
|
open(card) {
|
|
866
911
|
if (!this.available() || typeof this.service.openTab !== 'function') return false
|
|
867
|
-
const tabId = `${
|
|
912
|
+
const tabId = `${CARD_KIND_BETTER}:${card.id}`
|
|
913
|
+
// `meta` is the transport for the card id (feature `tabMeta`), and
|
|
914
|
+
// `dedupeKey` on our descriptor is what collapses repeat opens onto
|
|
915
|
+
// the same tab; both are part of the stable consumer contract
|
|
916
|
+
// (`lib/types/client/service.d.ts`, unchanged across 0.22.0 → 0.22.1).
|
|
868
917
|
this.service.openTab(
|
|
869
|
-
{ type:
|
|
918
|
+
{ type: CARD_KIND_BETTER, id: tabId, title: card.question.slice(0, 32), meta: { cardId: card.id } },
|
|
870
919
|
store.state.sessionId === null ? undefined : { sessionId: store.state.sessionId },
|
|
871
920
|
)
|
|
872
921
|
return true
|
|
873
922
|
},
|
|
874
923
|
close(card) {
|
|
875
924
|
if (!this.available() || typeof this.service.closeTab !== 'function') return false
|
|
876
|
-
this.service.closeTab(`${
|
|
925
|
+
this.service.closeTab(`${CARD_KIND_BETTER}:${card.id}`)
|
|
877
926
|
return true
|
|
878
927
|
},
|
|
879
928
|
},
|
|
@@ -1523,6 +1572,9 @@ window.__ModuleLoader__.load({
|
|
|
1523
1572
|
h('div', { key: 'session' }, `${t('diagSession')}: ${state.sessionId ?? t('none')}`),
|
|
1524
1573
|
h('div', { key: 'zones' }, `${t('diagZones')}: ${state.zoneAnchors === false ? t('diagZonesMissing') : t('yes')}`),
|
|
1525
1574
|
h('div', { key: 'slots' }, `${t('diagSlots')}: ${Object.entries(state.slots).map(([k, v]) => `${k}=${v}`).join(' · ')}`),
|
|
1575
|
+
// The side-card plugin's own report: which version is loaded and
|
|
1576
|
+
// whether it advertises the capability this adapter needs.
|
|
1577
|
+
h('div', { key: 'sidecard' }, `${t('diagSidecard')}: ${describeSurfaces()}`),
|
|
1526
1578
|
h('div', { key: 'path' }, `${t('settingsTitle')} → ${diag.provenance?.persistedPath ?? ''}`),
|
|
1527
1579
|
])
|
|
1528
1580
|
: null)
|
|
@@ -1549,6 +1601,39 @@ window.__ModuleLoader__.load({
|
|
|
1549
1601
|
return capturedComposerActions === null ? null : { actions: capturedComposerActions }
|
|
1550
1602
|
}
|
|
1551
1603
|
|
|
1604
|
+
/**
|
|
1605
|
+
* One line describing how each optional side-card surface resolved, for the
|
|
1606
|
+
* settings page's capability check. Both adapters are reported, because a
|
|
1607
|
+
* failing NATIVE registration was previously invisible here (it only showed
|
|
1608
|
+
* up as a missing entry in the pane's own slot inventory).
|
|
1609
|
+
* @returns {string} human-readable state.
|
|
1610
|
+
*/
|
|
1611
|
+
function describeSurfaces() {
|
|
1612
|
+
const native = surfaces.native.available()
|
|
1613
|
+
? `${t('yes')} · ${t('diagNativeKind')}=${CARD_KIND}`
|
|
1614
|
+
: `${t('no')}${surfaces.native.error === null ? '' : `(${surfaces.native.error})`}`
|
|
1615
|
+
return `${t('surfaceNative')}: ${native} / ${t('surfaceBetter')}: ${describeSidecardAdapter()}`
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
/**
|
|
1619
|
+
* The optional side-card plugin's own report: which version is loaded and
|
|
1620
|
+
* whether it advertises the capability this adapter needs.
|
|
1621
|
+
* @returns {string} human-readable state.
|
|
1622
|
+
*/
|
|
1623
|
+
function describeSidecardAdapter() {
|
|
1624
|
+
const better = surfaces.better
|
|
1625
|
+
if (better.error !== null) return `${t('no')}(${better.error})`
|
|
1626
|
+
if (better.version === null && better.service === null) return t('none')
|
|
1627
|
+
const version = better.version ?? '?'
|
|
1628
|
+
if (better.available()) {
|
|
1629
|
+
return `v${version} · ${better.features.length} ${t('diagFeatures')} · ${t('yes')}`
|
|
1630
|
+
}
|
|
1631
|
+
if (better.reason === 'no-tab-meta') {
|
|
1632
|
+
return `v${version} · ${t('diagNoTabMeta')}`
|
|
1633
|
+
}
|
|
1634
|
+
return `v${version} · ${better.reason ?? t('no')}`
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1552
1637
|
// ─────────────────────────────────────────────────────────────────────
|
|
1553
1638
|
// §10 plugin face
|
|
1554
1639
|
// ─────────────────────────────────────────────────────────────────────
|
|
@@ -2099,6 +2184,18 @@ window.__ModuleLoader__.load({
|
|
|
2099
2184
|
if (registry === null || registry === undefined) return
|
|
2100
2185
|
surfaces.native.registry = registry
|
|
2101
2186
|
surfaces.native.controller = controller ?? null
|
|
2187
|
+
// Register the tab TYPE first, then its body/title slots — and
|
|
2188
|
+
// roll the type back if a slot refuses to register.
|
|
2189
|
+
//
|
|
2190
|
+
// The host takes the id the moment `register` returns and refuses
|
|
2191
|
+
// a second registration of the same id; a slot registration that
|
|
2192
|
+
// throws (an already-inactive context during a reload/disposal)
|
|
2193
|
+
// would then leave the kind taken for the rest of the page's life,
|
|
2194
|
+
// rendering the host's "nothing can view this" face forever. This
|
|
2195
|
+
// mirrors what dsh-better-sidebar 0.22.1 fixed in its own native
|
|
2196
|
+
// glue (`disposeSafely` + partial-set release in
|
|
2197
|
+
// `src/client/native/index.ts`).
|
|
2198
|
+
let releaseType = null
|
|
2102
2199
|
if (typeof registry.register === 'function') {
|
|
2103
2200
|
const off = registry.register({
|
|
2104
2201
|
id: CARD_KIND,
|
|
@@ -2111,20 +2208,40 @@ window.__ModuleLoader__.load({
|
|
|
2111
2208
|
description: () => t('settingsDesc'),
|
|
2112
2209
|
}],
|
|
2113
2210
|
})
|
|
2114
|
-
|
|
2211
|
+
releaseType = typeof off === 'function' ? off : null
|
|
2212
|
+
surfaces.native.dispose = releaseType
|
|
2213
|
+
}
|
|
2214
|
+
const slotDisposers = []
|
|
2215
|
+
try {
|
|
2216
|
+
slotDisposers.push(ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
|
|
2217
|
+
name: 'sidebar.right.pane.tab',
|
|
2218
|
+
key: CARD_KIND,
|
|
2219
|
+
inject: sessionId => ({ sessionId }),
|
|
2220
|
+
}, (props) => h(CardHost, { props, actions }))))
|
|
2221
|
+
slotDisposers.push(ctx.slots.inject('sidebar.right.pane.tab.title', () => ctx.slots.register({
|
|
2222
|
+
name: 'sidebar.right.pane.tab.title',
|
|
2223
|
+
key: CARD_KIND,
|
|
2224
|
+
}, () => h('span', null, t('answerTitle')))))
|
|
2225
|
+
} catch (error) {
|
|
2226
|
+
for (const dispose of slotDisposers) {
|
|
2227
|
+
try {
|
|
2228
|
+
dispose()
|
|
2229
|
+
} catch { /* releasing must not mask the failure */ }
|
|
2230
|
+
}
|
|
2231
|
+
if (releaseType !== null) {
|
|
2232
|
+
try {
|
|
2233
|
+
releaseType()
|
|
2234
|
+
} catch { /* releasing must not mask the failure */ }
|
|
2235
|
+
surfaces.native.dispose = null
|
|
2236
|
+
}
|
|
2237
|
+
surfaces.native.registry = null
|
|
2238
|
+
surfaces.native.controller = null
|
|
2239
|
+
throw error
|
|
2115
2240
|
}
|
|
2116
|
-
const offBody = ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
|
|
2117
|
-
name: 'sidebar.right.pane.tab',
|
|
2118
|
-
key: CARD_KIND,
|
|
2119
|
-
inject: sessionId => ({ sessionId }),
|
|
2120
|
-
}, (props) => h(CardHost, { props, actions })))
|
|
2121
|
-
const offTitle = ctx.slots.inject('sidebar.right.pane.tab.title', () => ctx.slots.register({
|
|
2122
|
-
name: 'sidebar.right.pane.tab.title',
|
|
2123
|
-
key: CARD_KIND,
|
|
2124
|
-
}, () => h('span', null, t('answerTitle'))))
|
|
2125
2241
|
disposers.push(() => {
|
|
2126
|
-
|
|
2127
|
-
|
|
2242
|
+
for (const dispose of slotDisposers) {
|
|
2243
|
+
try { dispose() } catch { /* already disposed */ }
|
|
2244
|
+
}
|
|
2128
2245
|
try { surfaces.native.dispose?.() } catch { /* already disposed */ }
|
|
2129
2246
|
})
|
|
2130
2247
|
} catch (error) {
|
|
@@ -2137,9 +2254,15 @@ window.__ModuleLoader__.load({
|
|
|
2137
2254
|
}
|
|
2138
2255
|
|
|
2139
2256
|
// The dsh-better-sidebar plugin, when installed: its client service
|
|
2140
|
-
// registers a tab type and opens one per card.
|
|
2141
|
-
//
|
|
2142
|
-
//
|
|
2257
|
+
// registers a tab type and opens one per card.
|
|
2258
|
+
//
|
|
2259
|
+
// Verified against the published consumer contract: between 0.22.0 and
|
|
2260
|
+
// 0.22.1 `lib/types/client/service.d.ts` is byte-identical apart from the
|
|
2261
|
+
// version constant, and 0.22.1's `src/client/native/index.ts` changes are
|
|
2262
|
+
// robustness fixes in ITS own native glue — no field we use changed. The
|
|
2263
|
+
// `features` array is the forward-compatible half of that contract
|
|
2264
|
+
// ("Features are never removed"), so the adapter gates on `tabMeta`
|
|
2265
|
+
// instead of on a version number.
|
|
2143
2266
|
try {
|
|
2144
2267
|
ctx.inject(['betterSidebar'], (injected) => {
|
|
2145
2268
|
try {
|
|
@@ -2147,9 +2270,18 @@ window.__ModuleLoader__.load({
|
|
|
2147
2270
|
if (service === null || service === undefined) return
|
|
2148
2271
|
const features = Array.isArray(service.features) ? service.features : []
|
|
2149
2272
|
surfaces.better.features = features
|
|
2150
|
-
|
|
2273
|
+
surfaces.better.version = typeof service.version === 'string' ? service.version : null
|
|
2274
|
+
surfaces.better.reason = features.includes('tabMeta') ? null : 'no-tab-meta'
|
|
2275
|
+
if (typeof service.registerTab !== 'function') {
|
|
2276
|
+
surfaces.better.reason = 'no-register-tab'
|
|
2277
|
+
return
|
|
2278
|
+
}
|
|
2151
2279
|
const off = service.registerTab({
|
|
2152
|
-
|
|
2280
|
+
// A DIFFERENT type from the native rail's: better-sidebar mirrors
|
|
2281
|
+
// this descriptor into `sidebarRightTabs` as an `extension`-band
|
|
2282
|
+
// registration, and a second registration of the same kind in
|
|
2283
|
+
// that band throws (see CARD_KIND_BETTER).
|
|
2284
|
+
id: CARD_KIND_BETTER,
|
|
2153
2285
|
title: () => t('answerTitle'),
|
|
2154
2286
|
description: () => t('settingsDesc'),
|
|
2155
2287
|
order: 120,
|
|
@@ -2241,13 +2373,14 @@ window.__ModuleLoader__.load({
|
|
|
2241
2373
|
* `window` and exercises these without a browser.
|
|
2242
2374
|
*/
|
|
2243
2375
|
api: Object.freeze({
|
|
2244
|
-
version: '1.1
|
|
2376
|
+
version: '1.2.1',
|
|
2245
2377
|
/** Read-only state accessor for diagnostics and the test harness. */
|
|
2246
2378
|
snapshot: () => store.state,
|
|
2247
2379
|
pure: Object.freeze({
|
|
2248
2380
|
CLIENT_DEFAULTS,
|
|
2249
2381
|
IDS,
|
|
2250
2382
|
CARD_KIND,
|
|
2383
|
+
CARD_KIND_BETTER,
|
|
2251
2384
|
parseSseBlock,
|
|
2252
2385
|
truncateSelection,
|
|
2253
2386
|
parseShortcut,
|
package/index.js
CHANGED
|
@@ -42,7 +42,7 @@ export const name = 'dsh-sidecard-ask'
|
|
|
42
42
|
export const inject = ['webServer']
|
|
43
43
|
|
|
44
44
|
/** Version of this plugin (kept in step with package.json by test/verify.mjs). */
|
|
45
|
-
export const PLUGIN_VERSION = '1.1
|
|
45
|
+
export const PLUGIN_VERSION = '1.2.1'
|
|
46
46
|
|
|
47
47
|
/** Route prefix of the plugin's own API. */
|
|
48
48
|
export const ROUTE_PREFIX = '/sidecard-ask/api'
|