dsh-stall-guard 0.1.4 → 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 +24 -0
- package/README.md +12 -1
- package/client.js +90 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@
|
|
|
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
|
+
|
|
8
32
|
## [0.1.4] - 2026-10-21
|
|
9
33
|
|
|
10
34
|
### 修复
|
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
|
}),
|
|
@@ -1223,12 +1275,22 @@ window.__ModuleLoader__.load({
|
|
|
1223
1275
|
const [bubbleBelow, setBubbleBelow] = useState(false);
|
|
1224
1276
|
const appearance = useAppearance();
|
|
1225
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);
|
|
1226
1281
|
|
|
1227
1282
|
useEffect(() => store.start(), []);
|
|
1228
1283
|
|
|
1229
1284
|
const config = snapshot.config;
|
|
1230
1285
|
const supervisorOff = config !== null && config.supervisor.show === false;
|
|
1231
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]);
|
|
1232
1294
|
const display = config === null ? 'avatar' : config.supervisor.display;
|
|
1233
1295
|
const message = snapshot.state === null ? '' : snapshot.state.supervisor.message;
|
|
1234
1296
|
const bubbleOn = config === null ? true : config.supervisor.bubble !== false;
|
|
@@ -1305,7 +1367,9 @@ window.__ModuleLoader__.load({
|
|
|
1305
1367
|
top: appearance.y < 0 ? 'auto' : appearance.y + 'px',
|
|
1306
1368
|
right: appearance.x < 0 ? '14px' : 'auto',
|
|
1307
1369
|
bottom: appearance.y < 0 ? '96px' : 'auto',
|
|
1308
|
-
|
|
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,
|
|
1309
1373
|
}
|
|
1310
1374
|
: undefined;
|
|
1311
1375
|
|
|
@@ -1334,7 +1398,32 @@ window.__ModuleLoader__.load({
|
|
|
1334
1398
|
display === 'dot' ? h('span', { className: 'dsg-dot' }) : null,
|
|
1335
1399
|
display === 'icon' ? h(ShieldGlyph, { size: Math.round(size * 0.64) }) : null,
|
|
1336
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,
|
|
1337
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
|
+
|
|
1338
1427
|
/* The bubble is a SIBLING of the button, not a child: interactive content inside a
|
|
1339
1428
|
* button is invalid and browsers route its clicks unpredictably, and content painted
|
|
1340
1429
|
* outside the button box is not part of its hit area. */
|
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",
|