dsh-stall-guard 0.1.0 → 0.1.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 CHANGED
@@ -5,6 +5,23 @@
5
5
  格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
6
6
  版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
7
7
 
8
+ ## [0.1.1] - 2026-10-21
9
+
10
+ ### 新增
11
+
12
+ - **气泡文字可关闭**:`supervisor.bubble`。关掉后图标仍会跳动提示有异常,但不显示文字。
13
+ 旧配置文件没有这个键,默认显示气泡。
14
+
15
+ ### 修复
16
+
17
+ - **点气泡打不开面板**:气泡原先在 `<button>` 内部且 `pointer-events:none`,
18
+ 点击会穿透到背后的标题栏(表现为点出「下载 Session 日志」菜单)。
19
+ 现在气泡是按钮的兄弟元素,有自己的点击与键盘激活。
20
+ - **气泡被挤成竖排**:宽度依赖内容,在窄的标题栏槽里被压成每行 3–4 字。
21
+ 改为固定 200px(视口更窄时收窄),最多 4 行后截断。
22
+ - **过时提示文案**:`scenesHint` 仍写着「能自己决定要不要用模型写提示」,
23
+ 但模型功能已删除。改为只描述超时。
24
+
8
25
  ## [0.1.0] - 2026-10-20
9
26
 
10
27
  首个版本。核心用途:**下载卡住时自动掐掉,并把换源提示写给智能体看**。
package/README.md CHANGED
@@ -128,8 +128,12 @@ $env:DSH_PROFILE # 桌面版一般是 desktop
128
128
 
129
129
  ### 显示开关
130
130
 
131
- 面板里「显示监督员」一勾即关,**关掉后守卫照常工作**(下载一样会被掐断、提示一样会写进工具结果),
132
- 只是不再画那个形象。右下角会留一个 14px 的半透明小点,点它随时能打开面板重新打开。
131
+ | 开关 | 作用 |
132
+ |---|---|
133
+ | 显示监督员 | 关掉后不再画形象,但**守卫照常工作**。右下角留一个半透明小点,点它开回面板 |
134
+ | 卡顿时显示文字气泡 | 关掉仍会**跳动**提示有异常,但不显示那句文字 |
135
+
136
+ 点图标或点气泡都能打开面板。
133
137
 
134
138
  ### 图标外观(仅本浏览器)
135
139
 
@@ -258,7 +262,7 @@ $env:DSH_PROFILE # 桌面版一般是 desktop
258
262
  ## 开发
259
263
 
260
264
  ```bash
261
- node --test test/*.test.mjs # 144 个用例
265
+ node --test test/*.test.mjs # 182 个用例
262
266
  ```
263
267
 
264
268
  ## 许可
package/client.js CHANGED
@@ -189,7 +189,7 @@ window.__ModuleLoader__.load({
189
189
  autoSwitch: 'Auto-switch fetch/search on timeout',
190
190
  sectionPresets: 'Preset text (sent to the agent)',
191
191
  sectionScenes: 'Each feature, configured separately',
192
- scenesHint: 'Turn one off without affecting the others. Each has its own timeout and its own choice about the supervisor model.',
192
+ scenesHint: 'Turn one off without affecting the others. Each has its own timeout.',
193
193
  sceneSeconds: 'Timeout (s)',
194
194
  presetsHint: 'Write the exact text the agent reads when a stall is reported. Placeholders are filled in automatically; an empty box uses the built-in wording.',
195
195
  presetSceneDownload: 'Download',
@@ -207,6 +207,8 @@ window.__ModuleLoader__.load({
207
207
  sectionSupervisor: 'Supervisor',
208
208
  showSupervisor: 'Show the supervisor',
209
209
  showSupervisorHint: 'Off hides the figure but keeps the guard running; a small tab stays in the corner so you can switch it back.',
210
+ showBubble: 'Show the text bubble on a stall',
211
+ showBubbleHint: 'Off keeps the bouncing animation but hides the sentence. Click the figure or the bubble to open settings.',
210
212
  supervisorHiddenHint: 'Supervisor hidden — click to open settings',
211
213
  appearanceLabel: 'Icon appearance (this browser only)',
212
214
  iconSize: 'Size',
@@ -279,7 +281,7 @@ window.__ModuleLoader__.load({
279
281
  discardNo: '\u7ee7\u7eed\u7f16\u8f91',
280
282
  sectionPresets: '\u9884\u8bbe\u6587\u5b57\uff08\u53d1\u7ed9\u667a\u80fd\u4f53\uff09',
281
283
  sectionScenes: '\u6bcf\u4e2a\u529f\u80fd\u72ec\u7acb\u914d\u7f6e',
282
- scenesHint: '\u5173\u6389\u4e00\u4e2a\u4e0d\u5f71\u54cd\u5176\u5b83\u3002\u6bcf\u4e2a\u7c7b\u578b\u6709\u81ea\u5df1\u7684\u8d85\u65f6\uff0c\u4e5f\u80fd\u81ea\u5df1\u51b3\u5b9a\u8981\u4e0d\u8981\u7528\u6a21\u578b\u5199\u63d0\u793a\u3002',
284
+ scenesHint: '\u5173\u6389\u4e00\u4e2a\u4e0d\u5f71\u54cd\u5176\u5b83\u3002\u6bcf\u4e2a\u7c7b\u578b\u6709\u81ea\u5df1\u7684\u8d85\u65f6\u3002',
283
285
  sceneSeconds: '\u8d85\u65f6\uff08\u79d2\uff09',
284
286
  presetsHint: '\u76f4\u63a5\u5199\u4f60\u8981\u53d1\u7ed9\u667a\u80fd\u4f53\u7684\u6587\u5b57\uff0c\u5360\u4f4d\u7b26\u4f1a\u81ea\u52a8\u66ff\u6362\uff1b\u7a7a\u7740 = \u7528\u5185\u7f6e\u6587\u6848\u3002',
285
287
  presetSceneDownload: '\u4e0b\u8f7d',
@@ -296,6 +298,8 @@ window.__ModuleLoader__.load({
296
298
  downloadSeconds: '\u4e0b\u8f7d\u8d85\u65f6\uff08\u79d2\uff09',
297
299
  showSupervisor: '\u663e\u793a\u76d1\u7763\u5458',
298
300
  showSupervisorHint: '\u5173\u6389\u540e\u4e0d\u518d\u663e\u793a\u5f62\u8c61\uff0c\u4f46\u5b88\u536b\u4ecd\u5728\u8fd0\u884c\uff1b\u53f3\u4e0b\u89d2\u4f1a\u7559\u4e00\u4e2a\u5c0f\u70b9\uff0c\u70b9\u5b83\u53ef\u4ee5\u518d\u6253\u5f00\u3002',
301
+ showBubble: '\u5361\u987f\u65f6\u663e\u793a\u6587\u5b57\u6c14\u6ce1',
302
+ showBubbleHint: '\u5173\u6389\u4ecd\u4f1a\u8df3\u52a8\uff0c\u4f46\u4e0d\u663e\u793a\u6587\u5b57\u3002\u70b9\u56fe\u6807\u6216\u6c14\u6ce1\u90fd\u80fd\u6253\u5f00\u8bbe\u7f6e\u3002',
299
303
  supervisorHiddenHint: '\u76d1\u7763\u5458\u5df2\u9690\u85cf \u2014 \u70b9\u51fb\u6253\u5f00\u8bbe\u7f6e',
300
304
  appearanceLabel: '\u56fe\u6807\u5916\u89c2\uff08\u4ec5\u672c\u6d4f\u89c8\u5668\uff09',
301
305
  iconSize: '\u5927\u5c0f',
@@ -496,6 +500,7 @@ window.__ModuleLoader__.load({
496
500
  presets: normalizePresets(body.presets),
497
501
  supervisor: {
498
502
  show: sup.show !== false,
503
+ bubble: sup.bubble !== false,
499
504
  display: pick(sup.display, DISPLAYS, 'avatar'),
500
505
  },
501
506
  };
@@ -858,6 +863,7 @@ window.__ModuleLoader__.load({
858
863
  },
859
864
  supervisor: {
860
865
  show: source.supervisor.show !== false,
866
+ bubble: source.supervisor.bubble !== false,
861
867
  display: source.supervisor.display,
862
868
  },
863
869
  };
@@ -905,6 +911,7 @@ window.__ModuleLoader__.load({
905
911
  },
906
912
  supervisor: {
907
913
  show: draft.supervisor.show !== false,
914
+ bubble: draft.supervisor.bubble !== false,
908
915
  display: pick(draft.supervisor.display, DISPLAYS, 'avatar'),
909
916
  },
910
917
  };
@@ -920,7 +927,7 @@ window.__ModuleLoader__.load({
920
927
  */
921
928
  const CSS = [
922
929
  /* ---- supervisor seat ---- */
923
- '.dsg-seat{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;pointer-events:auto}',
930
+ '.dsg-seat{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;pointer-events:auto;overflow:visible}',
924
931
  '.dsg-orb{box-sizing:border-box;display:inline-flex;position:relative;align-items:center;justify-content:center;margin:0;padding:0;border:none;background:transparent;font-family:inherit;cursor:pointer;overflow:visible;border-radius:var(--dsw-radius-sm,6px);pointer-events:auto}',
925
932
  '.dsg-orb:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:2px}',
926
933
  '.dsg-orb[data-display="dot"]{width:22px;height:22px}',
@@ -937,11 +944,14 @@ window.__ModuleLoader__.load({
937
944
  '.dsg-orb[data-mode="idle"] .dsg-avatar{opacity:.72;animation:none}',
938
945
  '.dsg-orb[data-mode="watching"] .dsg-avatar{animation:dsg-sway 3.2s ease-in-out infinite}',
939
946
  '.dsg-orb[data-mode="alert"] .dsg-avatar{animation:dsg-bounce-big .85s ease-in-out infinite}',
940
- '.dsg-bubble{position:absolute;right:0;bottom:calc(100% + 7px);z-index:5;box-sizing:border-box;max-width:230px;min-width:64px;padding:4px 8px;border:1px solid var(--dsw-alias-border-l1,var(--dsh-border,rgba(255,255,255,.12)));border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-overlay,var(--dsh-surface,rgba(20,22,28,.96)));box-shadow:var(--dsw-elevation-soft,0 4px 18px rgba(0,0,0,.2));color:var(--dsw-alias-label-primary,var(--dsh-text-primary,#e6e6e6));font-size:11px;line-height:15px;text-align:left;white-space:normal;word-break:break-word;pointer-events:none;animation:dsg-bubble-in .16s ease-out both}',
947
+ '.dsg-bubble{position:absolute;right:0;bottom:calc(100% + 7px);z-index:5;box-sizing:border-box;width:200px;max-width:calc(100vw - 40px);padding:4px 8px;border:1px solid var(--dsw-alias-border-l1,var(--dsh-border,rgba(255,255,255,.12)));border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-overlay,var(--dsh-surface,rgba(20,22,28,.96)));box-shadow:var(--dsw-elevation-soft,0 4px 18px rgba(0,0,0,.2));color:var(--dsw-alias-label-primary,var(--dsh-text-primary,#e6e6e6));font-size:11px;line-height:15px;text-align:left;white-space:normal;overflow-wrap:break-word;word-break:normal;max-height:120px;overflow:hidden;pointer-events:auto;cursor:pointer;animation:dsg-bubble-in .16s ease-out both}',
941
948
  /* The header sits at the very top of the frame, so a bubble above the head
942
949
  * would be clipped by the viewport; the seat measures once and flips it
943
950
  * below when there is no room. */
944
951
  '.dsg-bubble[data-place="below"]{bottom:auto;top:calc(100% + 7px)}',
952
+ '.dsg-bubble:hover{border-color:var(--dsw-alias-brand-primary,#4d6bfe)}',
953
+ /* Hidden state: the bubble is not rendered at all, so there is nothing to dim. */
954
+
945
955
  /* ---- overlay panel ---- */
946
956
  '.dsg-panel{position:fixed;top:56px;right:14px;left:auto;z-index:70;box-sizing:border-box;width:min(430px,calc(100vw - 40px));max-height:min(74vh,660px);overflow:hidden;pointer-events:auto;display:flex;flex-direction:column;gap:0;padding:12px;border:1px solid var(--dsw-alias-border-l1,var(--dsh-border,rgba(255,255,255,.12)));border-radius:var(--dsw-radius-panel,12px);background:var(--dsw-alias-bg-overlay,var(--dsh-surface,rgba(20,22,28,.96)));box-shadow:var(--dsw-elevation-soft,0 8px 28px rgba(0,0,0,.22));color:var(--dsw-alias-label-primary,var(--dsh-text-primary,#e6e6e6));font:inherit;font-size:12px;line-height:18px}',
947
957
  '.dsg-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;flex:0 0 auto;padding-bottom:8px}',
@@ -1207,7 +1217,8 @@ window.__ModuleLoader__.load({
1207
1217
  const mode = snapshot.state === null ? 'idle' : snapshot.state.supervisor.mode;
1208
1218
  const display = config === null ? 'avatar' : config.supervisor.display;
1209
1219
  const message = snapshot.state === null ? '' : snapshot.state.supervisor.message;
1210
- const showBubble = !supervisorOff && display === 'avatar' && mode === 'alert' && message !== '';
1220
+ const bubbleOn = config === null ? true : config.supervisor.bubble !== false;
1221
+ const showBubble = !supervisorOff && bubbleOn && display === 'avatar' && mode === 'alert' && message !== '';
1211
1222
 
1212
1223
  /* The seat can live in the top bar or float anywhere; a bubble above the head
1213
1224
  * would be cut off by the viewport when it sits near the top, so measure once
@@ -1297,14 +1308,30 @@ window.__ModuleLoader__.load({
1297
1308
  display === 'dot' ? h('span', { className: 'dsg-dot' }) : null,
1298
1309
  display === 'icon' ? h(ShieldGlyph, { size: Math.round(size * 0.64) }) : null,
1299
1310
  display === 'avatar' ? h(WhaleGlyph, { size: Math.round(size * 0.82), mode }) : null,
1300
- showBubble
1301
- ? h('span', {
1302
- className: 'dsg-bubble',
1303
- 'data-place': bubbleBelow ? 'below' : 'above',
1304
- role: 'status',
1305
- }, message)
1306
- : null,
1307
1311
  ),
1312
+ /* The bubble is a SIBLING of the button, not a child: interactive content inside a
1313
+ * button is invalid and browsers route its clicks unpredictably, and content painted
1314
+ * outside the button box is not part of its hit area. */
1315
+ showBubble
1316
+ ? h('span', {
1317
+ className: 'dsg-bubble',
1318
+ 'data-place': bubbleBelow ? 'below' : 'above',
1319
+ role: 'button',
1320
+ tabIndex: 0,
1321
+ title: t('openPanel'),
1322
+ onClick: (event) => {
1323
+ event.stopPropagation();
1324
+ store.activate();
1325
+ },
1326
+ onKeyDown: (event) => {
1327
+ if (event.key === 'Enter' || event.key === ' ') {
1328
+ event.preventDefault();
1329
+ event.stopPropagation();
1330
+ store.activate();
1331
+ }
1332
+ },
1333
+ }, message)
1334
+ : null,
1308
1335
  );
1309
1336
  }
1310
1337
 
@@ -1701,6 +1728,18 @@ window.__ModuleLoader__.load({
1701
1728
  h('span', null, t('showSupervisor')),
1702
1729
  ),
1703
1730
  h('p', { className: 'dsg-status' }, t('showSupervisorHint')),
1731
+ h('label', { className: 'dsg-check' },
1732
+ h('input', {
1733
+ type: 'checkbox',
1734
+ checked: draft.supervisor.bubble !== false,
1735
+ onChange: (event) => {
1736
+ const checked = event.target.checked;
1737
+ edit((next) => { next.supervisor = Object.assign({}, next.supervisor, { bubble: checked }); });
1738
+ },
1739
+ }),
1740
+ h('span', null, t('showBubble')),
1741
+ ),
1742
+ h('p', { className: 'dsg-status' }, t('showBubbleHint')),
1704
1743
  h('div', { className: 'dsg-label' }, t('displayLabel')),
1705
1744
  h('div', { className: 'dsg-choices' },
1706
1745
  DISPLAYS.map((value) => h('label', {
package/lib/config.js CHANGED
@@ -48,6 +48,11 @@ export function defaults() {
48
48
  supervisor: {
49
49
  /** Whether the supervisor is drawn at all. Off still serves the API and the panel. */
50
50
  show: true,
51
+ /**
52
+ * Whether the alert bubble shows its sentence. Off keeps the animation only, so a
53
+ * user who finds the text intrusive still sees that something is wrong.
54
+ */
55
+ bubble: true,
51
56
  display: DEFAULT_DISPLAY,
52
57
  },
53
58
  };
@@ -113,6 +118,7 @@ export function validateConfig(raw) {
113
118
  const rawSupervisor = input.supervisor && typeof input.supervisor === 'object' ? input.supervisor : {};
114
119
 
115
120
  const show = rawSupervisor.show === undefined ? base.supervisor.show : Boolean(rawSupervisor.show);
121
+ const bubble = rawSupervisor.bubble === undefined ? base.supervisor.bubble : Boolean(rawSupervisor.bubble);
116
122
 
117
123
  let display = base.supervisor.display;
118
124
  if (rawSupervisor.display !== undefined) {
@@ -142,6 +148,7 @@ export function validateConfig(raw) {
142
148
  presets: presetResult.presets,
143
149
  supervisor: {
144
150
  show,
151
+ bubble,
145
152
  display,
146
153
  },
147
154
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-stall-guard",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "DSH 卡顿守卫:下载类命令(git clone / pnpm install / pip / curl)超时后自动中止,并把「换个下载源重试」的提示写进工具结果给智能体看,解决 GitHub 在国内网络不稳导致智能体一直干等的问题。另可按工具类型自定义超时秒数,并附一个只在卡顿时唤醒的 DeepSeek 监督员。",
5
5
  "keywords": [
6
6
  "dsh",