dsh-stall-guard 0.1.3 → 0.1.5
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 +38 -0
- package/README.md +12 -1
- package/client.js +124 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,44 @@
|
|
|
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.5] - 2026-10-21
|
|
9
|
+
|
|
10
|
+
### 界面
|
|
11
|
+
|
|
12
|
+
- **浮动模式变成鱼缸**:图标外框现在是一缸水 —— 半透明蓝色水体、顶部水面亮线、
|
|
13
|
+
缓慢左右摆动的波光、向下扫过的波纹,以及向上浮动的水泡。
|
|
14
|
+
全部用 CSS 关键帧实现,空闲时不占用 JavaScript;`prefers-reduced-motion` 下自动停止。
|
|
15
|
+
- **卡顿水花**:警报时一圈水花炸开 + 5 滴水珠飞散,播一次后自动清除,不会循环闪。
|
|
16
|
+
- **图标恢复为最初的圆鲸鱼**:圆身体、翘尾鳍、顶鳍、大眼与微笑。这是用户偏好的形态,
|
|
17
|
+
之前两次重画(侧游小鱼、官方剪影风)都已撤掉。仅补上尾鳍/顶鳍的动画钩子。
|
|
18
|
+
- 尾鳍随状态摆动:闲置慢摆(5.4 秒)、监视中小幅(3.2 秒)、卡顿急摆(0.3 秒)。
|
|
19
|
+
- 水泡改为按比例尺寸,小图标下也看得见。
|
|
20
|
+
|
|
21
|
+
### 修复
|
|
22
|
+
|
|
23
|
+
- **切到浮动后图标消失**:标题栏座位的守卫 `supervisorOff || floating` 把 overlay 座位
|
|
24
|
+
也挡掉了(它收到的 floating 恒为 true)。现区分「本座位该画的形态」与「用户选择的形态」。
|
|
25
|
+
- **拖动图标会带动标题栏**:浮动态改由 `shell.overlay` 渲染,脱离文档流。
|
|
26
|
+
- **调整大小时看不到实时预览**:设置面板从标题栏下方展开(`top:118px`),不再压住图标。
|
|
27
|
+
外观状态统一走 `useAppearance()`(`useSyncExternalStore`),三个消费点同步重绘。
|
|
28
|
+
- **气泡点击会打开面板**:改为关闭提示(`acknowledge`)。点鱼图标才是打开设置。
|
|
29
|
+
- **气泡被挤成竖排**:固定宽度 200px,最多 4 行。
|
|
30
|
+
- **气泡在 `<button>` 内部**:移到按钮外作为兄弟节点,独立处理点击与键盘激活。
|
|
31
|
+
|
|
32
|
+
## [0.1.4] - 2026-10-21
|
|
33
|
+
|
|
34
|
+
### 修复
|
|
35
|
+
|
|
36
|
+
- **切到「浮动」后图标消失**:0.1.3 把浮动态拆到 overlay 渲染时,标题栏座位的守卫
|
|
37
|
+
`if (supervisorOff || floating) return null` 同时也把 overlay 座位挡掉了 ——
|
|
38
|
+
因为 overlay 座位收到的 `floating` 恒为 true。结果是图标彻底不画。
|
|
39
|
+
现在区分「这个座位要画的形态(forcedFloat)」与「用户选择的形态(wantsFloat)」:
|
|
40
|
+
标题栏在 `wantsFloat && !forcedFloat` 时让位,overlay 座位永不让位。
|
|
41
|
+
- **两个座位可能同时画图标**:overlay 座位现在只在 `place === 'float'` 时渲染。
|
|
42
|
+
- 外观状态改为共享 `useAppearance()` 钩子(`useSyncExternalStore`),
|
|
43
|
+
三个消费点(标题栏、浮动态、面板)对同一份记录同步重绘,
|
|
44
|
+
修掉面板改尺寸时图标不实时变化的问题。
|
|
45
|
+
|
|
8
46
|
## [0.1.3] - 2026-10-21
|
|
9
47
|
|
|
10
48
|
### 修复
|
package/README.md
CHANGED
|
@@ -124,7 +124,18 @@ $env:DSH_PROFILE # 桌面版一般是 desktop
|
|
|
124
124
|
|---|---|---|
|
|
125
125
|
| `dot` | 状态栏 6px 半透明点 | 变红脉冲 |
|
|
126
126
|
| `icon` | 静止图标 | 弹跳 + 发光 |
|
|
127
|
-
| `avatar` |
|
|
127
|
+
| `avatar` | 闭眼圆鲸鱼 | 变红、尾鳍急摆、水花炸开 |
|
|
128
|
+
|
|
129
|
+
### 鱼缸(浮动模式)
|
|
130
|
+
|
|
131
|
+
把图标设为「浮动」后,它外面会套一个鱼缸:
|
|
132
|
+
|
|
133
|
+
- 半透明蓝色水体、顶部水面亮线(呼吸)、缓慢左右摆的波光、向下扫的波纹
|
|
134
|
+
- 5 颗按比例缩放的**上浮水泡**
|
|
135
|
+
- 卡顿时一圈**水花炸开 + 5 滴水珠飞散**,播一次就停,不循环闪
|
|
136
|
+
|
|
137
|
+
全部是 CSS 关键帧,空闲时不跑 JavaScript;系统开启「减少动态效果」时自动停止。
|
|
138
|
+
图标本身也有动作:尾鳍闲置慢摆、监视中小幅、卡顿急摆。
|
|
128
139
|
|
|
129
140
|
### 显示开关
|
|
130
141
|
|
package/client.js
CHANGED
|
@@ -931,6 +931,9 @@ window.__ModuleLoader__.load({
|
|
|
931
931
|
/* ---- supervisor seat ---- */
|
|
932
932
|
'.dsg-seat{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;pointer-events:auto;overflow:visible}',
|
|
933
933
|
'.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}',
|
|
934
|
+
/* The floating figure is a tank: clip the water to its frame and stack the whale above it. */
|
|
935
|
+
'.dsg-orb[data-floating="yes"]{overflow:hidden}',
|
|
936
|
+
'.dsg-orb[data-floating="yes"]>svg{position:relative;z-index:1}',
|
|
934
937
|
'.dsg-orb:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:2px}',
|
|
935
938
|
'.dsg-orb[data-display="dot"]{width:22px;height:22px}',
|
|
936
939
|
'.dsg-orb[data-display="icon"]{width:24px;height:24px}',
|
|
@@ -1052,6 +1055,37 @@ window.__ModuleLoader__.load({
|
|
|
1052
1055
|
'@keyframes dsg-glow{0%,100%{filter:drop-shadow(0 0 0 rgba(236,19,19,0))}50%{filter:drop-shadow(0 0 5px rgba(236,19,19,.85))}}',
|
|
1053
1056
|
'@keyframes dsg-sway{0%,100%{transform:rotate(-6deg)}50%{transform:rotate(6deg)}}',
|
|
1054
1057
|
'@keyframes dsg-bounce-big{0%,100%{transform:translateY(0) scale(1)}30%{transform:translateY(-8px) scale(1.06)}60%{transform:translateY(0) scale(1)}80%{transform:translateY(-3px) scale(1)}}',
|
|
1058
|
+
/* ---- aquarium: the floating posture reads as a fish tank ---- */
|
|
1059
|
+
/* Three layers behind the whale: a translucent water body, a caustic shimmer that
|
|
1060
|
+
* suggests light through water, and bubbles rising on their own keyframes. All are
|
|
1061
|
+
* pointer-events:none so the tank never eats a click meant for the figure. */
|
|
1062
|
+
'.dsg-tank{position:absolute;inset:0;display:block;pointer-events:none;border-radius:var(--dsw-radius-sm,10px);overflow:hidden;background:linear-gradient(180deg,rgba(86,140,220,.28) 0%,rgba(40,88,170,.42) 60%,rgba(18,46,110,.5) 100%);border:1px solid rgba(150,200,255,.35);box-shadow:inset 0 2px 10px rgba(0,20,60,.45),0 2px 12px rgba(0,0,0,.32)}',
|
|
1063
|
+
'.dsg-tank::before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(ellipse 60% 40% at 50% 0%,rgba(190,225,255,.35),transparent 70%);mix-blend-mode:screen;animation:dsg-caustic 7s ease-in-out infinite}',
|
|
1064
|
+
'.dsg-tank::after{content:"";position:absolute;left:0;right:0;top:-40%;height:34%;pointer-events:none;background:linear-gradient(180deg,transparent,rgba(150,205,255,.22),transparent);animation:dsg-wave 5.5s linear infinite}',
|
|
1065
|
+
'.dsg-tank-waterline{position:absolute;left:0;right:0;top:0;height:2px;pointer-events:none;background:linear-gradient(90deg,transparent,rgba(190,230,255,.75),transparent);opacity:.85;animation:dsg-waterline 3.6s ease-in-out infinite}',
|
|
1066
|
+
'.dsg-tank-bubble{position:absolute;bottom:6%;border-radius:50%;pointer-events:none;background:radial-gradient(circle at 32% 28%,rgba(255,255,255,.9),rgba(180,220,255,.35) 55%,rgba(120,180,255,.18) 100%);box-shadow:0 0 3px rgba(180,220,255,.5);animation:dsg-rise linear infinite}',
|
|
1067
|
+
'@keyframes dsg-rise{0%{transform:translateY(0) scale(.7);opacity:0}12%{opacity:.85}70%{opacity:.7}100%{transform:translateY(-260%) scale(1.05);opacity:0}}',
|
|
1068
|
+
'@keyframes dsg-caustic{0%,100%{opacity:.5;transform:translateX(-4%)}50%{opacity:.85;transform:translateX(4%)}}',
|
|
1069
|
+
'@keyframes dsg-wave{0%{transform:translateY(0)}100%{transform:translateY(320%)}}',
|
|
1070
|
+
'@keyframes dsg-waterline{0%,100%{opacity:.55}50%{opacity:1}}',
|
|
1071
|
+
/* One-shot splash for an alarm: droplets fly outward, then the class is removed. */
|
|
1072
|
+
'.dsg-splash{position:absolute;inset:0;pointer-events:none;border-radius:50%;border:2px solid rgba(190,232,255,.85);opacity:0;animation:dsg-splash-out .62s ease-out forwards}',
|
|
1073
|
+
'.dsg-splash-drop{position:absolute;left:50%;top:50%;width:3px;height:3px;border-radius:50%;pointer-events:none;background:rgba(215,240,255,.95);animation:dsg-drop-out .62s ease-out forwards}',
|
|
1074
|
+
'@keyframes dsg-splash-out{0%{opacity:.9;transform:scale(.35);border-width:3px}100%{opacity:0;transform:scale(1.15);border-width:1px}}',
|
|
1075
|
+
'@keyframes dsg-drop-out{0%{opacity:1;transform:translate(-50%,-50%) scale(1)}100%{opacity:0;transform:translate(calc(-50% + var(--dx)),calc(-50% + var(--dy))) scale(.4)}}',
|
|
1076
|
+
'@media (prefers-reduced-motion:reduce){.dsg-tank::before,.dsg-tank::after,.dsg-tank-waterline,.dsg-tank-bubble,.dsg-splash,.dsg-splash-drop{animation:none}}',
|
|
1077
|
+
/* ---- the whale: gentle when calm, a flick when alarmed ---- */
|
|
1078
|
+
'.dsg-avatar{overflow:visible}',
|
|
1079
|
+
'.dsg-avatar[data-mode="idle"] .dsg-whale-tail{animation:dsg-whale-drift 5.4s ease-in-out infinite;transform-origin:19.6px 13px}',
|
|
1080
|
+
'.dsg-avatar[data-mode="watching"] .dsg-whale-tail{animation:dsg-whale-swish 3.2s ease-in-out infinite;transform-origin:19.6px 13px}',
|
|
1081
|
+
'.dsg-avatar[data-mode="alert"] .dsg-whale-tail{animation:dsg-whale-flick .3s ease-in-out infinite;transform-origin:19.6px 13px}',
|
|
1082
|
+
'.dsg-avatar[data-mode="watching"] .dsg-whale-fin{animation:dsg-fin-wave 2.6s ease-in-out infinite;transform-origin:14px 8px}',
|
|
1083
|
+
'.dsg-avatar[data-mode="alert"] .dsg-whale-fin{animation:dsg-fin-wave .8s ease-in-out infinite;transform-origin:14px 8px}',
|
|
1084
|
+
'@keyframes dsg-whale-drift{0%,100%{transform:rotate(-4deg)}50%{transform:rotate(5deg)}}',
|
|
1085
|
+
'@keyframes dsg-whale-swish{0%,100%{transform:rotate(-8deg)}50%{transform:rotate(9deg)}}',
|
|
1086
|
+
'@keyframes dsg-whale-flick{0%,100%{transform:rotate(-22deg) scaleY(1)}50%{transform:rotate(22deg) scaleY(.85)}}',
|
|
1087
|
+
'@keyframes dsg-fin-wave{0%,100%{transform:rotate(-4deg)}50%{transform:rotate(6deg)}}',
|
|
1088
|
+
'@media (prefers-reduced-motion:reduce){.dsg-whale-tail,.dsg-whale-fin{animation:none!important}}',
|
|
1055
1089
|
'@keyframes dsg-bubble-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}',
|
|
1056
1090
|
'@media (prefers-reduced-motion:reduce){.dsg-orb[data-mode] .dsg-dot,.dsg-orb[data-mode] .dsg-glyph,.dsg-orb[data-mode] .dsg-avatar,.dsg-bubble{animation:none}}',
|
|
1057
1091
|
].join('\n');
|
|
@@ -1101,6 +1135,21 @@ window.__ModuleLoader__.load({
|
|
|
1101
1135
|
* 28px original DeepSeek-blue whale. Eyes are closed with a line while the
|
|
1102
1136
|
* supervisor is idle and open dots otherwise; nothing external is loaded.
|
|
1103
1137
|
*/
|
|
1138
|
+
/**
|
|
1139
|
+
* A fish, drawn for the tank.
|
|
1140
|
+
*
|
|
1141
|
+
* Three moods, driven by `mode`:
|
|
1142
|
+
* idle - slow bob, closed eye, tail drifting
|
|
1143
|
+
* watching - upright, eye open, gentle fin motion
|
|
1144
|
+
* alert - red body, mouth open, fast tail flick and a small scale burst
|
|
1145
|
+
*/
|
|
1146
|
+
/**
|
|
1147
|
+
* The whale, as originally drawn.
|
|
1148
|
+
*
|
|
1149
|
+
* A plump 28x28 whale: round body, raised tail fluke, top fin, one eye, a small smile.
|
|
1150
|
+
* The user preferred this silhouette over later redraws, so it is kept verbatim; only
|
|
1151
|
+
* class names are added to the two fins so the tank animations can target them.
|
|
1152
|
+
*/
|
|
1104
1153
|
function WhaleGlyph(props) {
|
|
1105
1154
|
const size = props.size || 28;
|
|
1106
1155
|
const asleep = props.mode === 'idle';
|
|
@@ -1115,14 +1164,17 @@ window.__ModuleLoader__.load({
|
|
|
1115
1164
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
1116
1165
|
'aria-hidden': 'true',
|
|
1117
1166
|
focusable: 'false',
|
|
1167
|
+
'data-mode': props.mode || 'idle',
|
|
1118
1168
|
},
|
|
1119
1169
|
/* tail fluke */
|
|
1120
1170
|
h('path', {
|
|
1171
|
+
className: 'dsg-whale-tail',
|
|
1121
1172
|
d: 'M19.6 12.4c2.9-3.4 5.1-3.1 5.1.5 0 3.3-2.4 3.7-5.1.6z',
|
|
1122
1173
|
fill: fin,
|
|
1123
1174
|
}),
|
|
1124
1175
|
/* top fin */
|
|
1125
1176
|
h('path', {
|
|
1177
|
+
className: 'dsg-whale-fin',
|
|
1126
1178
|
d: 'M11.8 6.4c2.3-2.5 4.4-2.5 4.8 1.1-2.1 1-3.9.6-4.8-1.1z',
|
|
1127
1179
|
fill: BLUE_LIGHT,
|
|
1128
1180
|
}),
|
|
@@ -1188,6 +1240,19 @@ window.__ModuleLoader__.load({
|
|
|
1188
1240
|
}
|
|
1189
1241
|
}
|
|
1190
1242
|
|
|
1243
|
+
/**
|
|
1244
|
+
* Subscribe to the appearance record.
|
|
1245
|
+
*
|
|
1246
|
+
* The record lives outside the polled snapshot, so it needs its own subscription. A
|
|
1247
|
+
* shared hook keeps every consumer honest: an appearance change re-renders all of them.
|
|
1248
|
+
*/
|
|
1249
|
+
function useAppearance() {
|
|
1250
|
+
return useSyncExternalStore(
|
|
1251
|
+
(listener) => store.subscribeAppearance(listener),
|
|
1252
|
+
() => store.getAppearance(),
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1191
1256
|
function useTranslate(props) {
|
|
1192
1257
|
return props.t !== undefined && typeof props.t === 'function' ? props.t : (boundT || ((key) => key));
|
|
1193
1258
|
}
|
|
@@ -1208,15 +1273,24 @@ window.__ModuleLoader__.load({
|
|
|
1208
1273
|
const snapshot = useStore();
|
|
1209
1274
|
const orbRef = useRef(null);
|
|
1210
1275
|
const [bubbleBelow, setBubbleBelow] = useState(false);
|
|
1211
|
-
const
|
|
1276
|
+
const appearance = useAppearance();
|
|
1212
1277
|
const [dragging, setDragging] = useState(false);
|
|
1278
|
+
/* The splash is a one-shot: raised when the alarm starts, cleared on animation end. */
|
|
1279
|
+
const [splash, setSplash] = useState(false);
|
|
1280
|
+
const previousModeRef = useRef(null);
|
|
1213
1281
|
|
|
1214
1282
|
useEffect(() => store.start(), []);
|
|
1215
|
-
useEffect(() => store.subscribeAppearance(setAppearance), []);
|
|
1216
1283
|
|
|
1217
1284
|
const config = snapshot.config;
|
|
1218
1285
|
const supervisorOff = config !== null && config.supervisor.show === false;
|
|
1219
1286
|
const mode = snapshot.state === null ? 'idle' : snapshot.state.supervisor.mode;
|
|
1287
|
+
/* Raised once per alarm. Declared after `mode` on purpose: a hook that reads a
|
|
1288
|
+
* `const` before its declaration is a TDZ error that takes the whole module down. */
|
|
1289
|
+
useEffect(() => {
|
|
1290
|
+
if (previousModeRef.current === mode) return;
|
|
1291
|
+
previousModeRef.current = mode;
|
|
1292
|
+
if (mode === 'alert') setSplash(true);
|
|
1293
|
+
}, [mode]);
|
|
1220
1294
|
const display = config === null ? 'avatar' : config.supervisor.display;
|
|
1221
1295
|
const message = snapshot.state === null ? '' : snapshot.state.supervisor.message;
|
|
1222
1296
|
const bubbleOn = config === null ? true : config.supervisor.bubble !== false;
|
|
@@ -1238,8 +1312,16 @@ window.__ModuleLoader__.load({
|
|
|
1238
1312
|
|
|
1239
1313
|
/* Float placement: drag moves the control, and the position is clamped into the
|
|
1240
1314
|
* viewport on release so a window resize cannot strand it off-screen. */
|
|
1241
|
-
|
|
1242
|
-
|
|
1315
|
+
/**
|
|
1316
|
+
* Which posture this render draws.
|
|
1317
|
+
*
|
|
1318
|
+
* The overlay seat forces floating; the header seat reads the user's setting. The two
|
|
1319
|
+
* must be distinguished, because the header seat has to bail out when the chosen
|
|
1320
|
+
* posture is floating — while the overlay seat must not bail out for the same reason.
|
|
1321
|
+
*/
|
|
1322
|
+
const forcedFloat = props.forcePlace === 'float';
|
|
1323
|
+
const wantsFloat = appearance.place === 'float';
|
|
1324
|
+
const floating = forcedFloat || wantsFloat;
|
|
1243
1325
|
const startDrag = useCallback((event) => {
|
|
1244
1326
|
if (!floating) return;
|
|
1245
1327
|
if (event.button !== undefined && event.button !== 0) return;
|
|
@@ -1270,10 +1352,11 @@ window.__ModuleLoader__.load({
|
|
|
1270
1352
|
event.preventDefault();
|
|
1271
1353
|
}, [floating]);
|
|
1272
1354
|
|
|
1273
|
-
//
|
|
1274
|
-
//
|
|
1275
|
-
//
|
|
1276
|
-
if (supervisorOff
|
|
1355
|
+
// The header seat renders nothing when the figure is hidden OR when the overlay owns
|
|
1356
|
+
// it — a floating figure inside the header's flow would push that row around as it
|
|
1357
|
+
// moved. The overlay seat asked for this render, so it never bails out here.
|
|
1358
|
+
if (supervisorOff) return null;
|
|
1359
|
+
if (wantsFloat && !forcedFloat) return null;
|
|
1277
1360
|
|
|
1278
1361
|
const label = t('supervisor') + ' — ' + t(MODE_KEY[mode]);
|
|
1279
1362
|
const size = appearance.size;
|
|
@@ -1284,7 +1367,9 @@ window.__ModuleLoader__.load({
|
|
|
1284
1367
|
top: appearance.y < 0 ? 'auto' : appearance.y + 'px',
|
|
1285
1368
|
right: appearance.x < 0 ? '14px' : 'auto',
|
|
1286
1369
|
bottom: appearance.y < 0 ? '96px' : 'auto',
|
|
1287
|
-
|
|
1370
|
+
/* Above the settings panel (70): the figure must stay visible and draggable
|
|
1371
|
+
* even when it is dragged under the panel. */
|
|
1372
|
+
zIndex: 75,
|
|
1288
1373
|
}
|
|
1289
1374
|
: undefined;
|
|
1290
1375
|
|
|
@@ -1313,7 +1398,32 @@ window.__ModuleLoader__.load({
|
|
|
1313
1398
|
display === 'dot' ? h('span', { className: 'dsg-dot' }) : null,
|
|
1314
1399
|
display === 'icon' ? h(ShieldGlyph, { size: Math.round(size * 0.64) }) : null,
|
|
1315
1400
|
display === 'avatar' ? h(WhaleGlyph, { size: Math.round(size * 0.82), mode }) : null,
|
|
1401
|
+
/* One-shot splash per alarm. `onAnimationEnd` clears it so it never loops. */
|
|
1402
|
+
splash
|
|
1403
|
+
? h('span', { className: 'dsg-splash', 'aria-hidden': 'true',
|
|
1404
|
+
onAnimationEnd: () => setSplash(false),
|
|
1405
|
+
},
|
|
1406
|
+
h('span', { className: 'dsg-splash-drop', style: { '--dx': '-14px', '--dy': '-16px' } }),
|
|
1407
|
+
h('span', { className: 'dsg-splash-drop', style: { '--dx': '13px', '--dy': '-18px' } }),
|
|
1408
|
+
h('span', { className: 'dsg-splash-drop', style: { '--dx': '-16px', '--dy': '8px' } }),
|
|
1409
|
+
h('span', { className: 'dsg-splash-drop', style: { '--dx': '17px', '--dy': '6px' } }),
|
|
1410
|
+
h('span', { className: 'dsg-splash-drop', style: { '--dx': '0px', '--dy': '-22px' } }),
|
|
1411
|
+
)
|
|
1412
|
+
: null,
|
|
1316
1413
|
),
|
|
1414
|
+
/* Aquarium: only in the floating posture, where the rounded frame reads as a tank.
|
|
1415
|
+
* Everything here paints behind the whale and ignores pointer events. */
|
|
1416
|
+
floating
|
|
1417
|
+
? h('span', { className: 'dsg-tank', 'aria-hidden': 'true' },
|
|
1418
|
+
h('span', { className: 'dsg-tank-waterline' }),
|
|
1419
|
+
h('span', { className: 'dsg-tank-bubble', style: { width: '15%', height: '15%', left: '16%', animationDuration: '4.2s', animationDelay: '0s' } }),
|
|
1420
|
+
h('span', { className: 'dsg-tank-bubble', style: { width: '11%', height: '11%', left: '33%', animationDuration: '5.6s', animationDelay: '1.1s' } }),
|
|
1421
|
+
h('span', { className: 'dsg-tank-bubble', style: { width: '18%', height: '18%', left: '51%', animationDuration: '4.8s', animationDelay: '0.4s' } }),
|
|
1422
|
+
h('span', { className: 'dsg-tank-bubble', style: { width: '10%', height: '10%', left: '69%', animationDuration: '6.2s', animationDelay: '2.0s' } }),
|
|
1423
|
+
h('span', { className: 'dsg-tank-bubble', style: { width: '14%', height: '14%', left: '83%', animationDuration: '5.1s', animationDelay: '1.6s' } })
|
|
1424
|
+
)
|
|
1425
|
+
: null,
|
|
1426
|
+
|
|
1317
1427
|
/* The bubble is a SIBLING of the button, not a child: interactive content inside a
|
|
1318
1428
|
* button is invalid and browsers route its clicks unpredictably, and content painted
|
|
1319
1429
|
* outside the button box is not part of its hit area. */
|
|
@@ -1354,9 +1464,13 @@ window.__ModuleLoader__.load({
|
|
|
1354
1464
|
function FloatingSeat(props) {
|
|
1355
1465
|
const t = useTranslate(props);
|
|
1356
1466
|
const snapshot = useStore();
|
|
1467
|
+
/* The overlay draws the figure only in the floating posture. Without this check both
|
|
1468
|
+
* seats would render it, and the header would show a second copy. */
|
|
1469
|
+
const appearance = useAppearance();
|
|
1357
1470
|
useEffect(() => store.start(), []);
|
|
1358
1471
|
if (snapshot.config === null) return null;
|
|
1359
1472
|
if (snapshot.config.supervisor.show === false) return null;
|
|
1473
|
+
if (appearance.place !== 'float') return null;
|
|
1360
1474
|
return h(SupervisorSeat, { t, forcePlace: 'float' });
|
|
1361
1475
|
}
|
|
1362
1476
|
|
|
@@ -1401,8 +1515,7 @@ window.__ModuleLoader__.load({
|
|
|
1401
1515
|
const presetLocale = hostLocale() === 'en' ? 'en' : 'zh';
|
|
1402
1516
|
/* Appearance is browser-local, so the panel mirrors it separately from the
|
|
1403
1517
|
* Host draft; saving the config must not overwrite it and vice versa. */
|
|
1404
|
-
const
|
|
1405
|
-
useEffect(() => store.subscribeAppearance(setLocalAppearance), []);
|
|
1518
|
+
const localAppearance = useAppearance();
|
|
1406
1519
|
const [dirty, setDirty] = useState(false);
|
|
1407
1520
|
const [status, setStatus] = useState({ kind: 'idle', text: '' });
|
|
1408
1521
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-stall-guard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "DSH 卡顿守卫:下载类命令(git clone / pnpm install / pip / curl)超时后自动中止,并把「换个下载源重试」的提示写进工具结果给智能体看,解决 GitHub 在国内网络不稳导致智能体一直干等的问题。另可按工具类型自定义超时秒数,并附一个只在卡顿时唤醒的 DeepSeek 监督员。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|