helixlife-v5-cli 1.2.8 → 1.2.9
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 +33 -58
- package/helix-cli.js +7 -2
- package/lib/.cli.runtime.generated.json +10 -1
- package/lib/automation-file-chooser-guard.js +182 -0
- package/lib/automation-overlay.init.js +332 -68
- package/lib/browser-css-cache-guard.init-page.js +49 -0
- package/lib/browser-window-maximize.init-page.js +64 -0
- package/lib/cli.js +316 -14
- package/lib/helix-reload-bypass-cache.run-code.js +7 -0
- package/lib/helix-styles-guard.js +77 -0
- package/lib/hide-nested-skills.js +120 -0
- package/lib/native-mode-patch.js +288 -0
- package/lib/postinstall.js +39 -0
- package/lib/profile-lock-guard.js +187 -0
- package/lib/runtime-config.js +90 -0
- package/lib/token-file.js +189 -0
- package/lib/visual-page-wrap.js +311 -0
- package/lib/visual.js +255 -86
- package/package.json +9 -21
- package/scripts/README.md +81 -0
- package/scripts/{analysis-detail-upload-file.js → analysis/detail-upload-file.js} +1 -1
- package/scripts/{jigsaw-inspect-dom.js → analysis/jigsaw-inspect-dom.js} +15 -15
- package/scripts/{jigsaw-verify-enumerate.js → analysis/jigsaw-verify-enumerate.js} +1 -1
- package/scripts/common/.gitkeep +0 -0
- package/scripts/{chrome-pdf-enumerate-a11y.js → edu/chrome-pdf-enumerate-a11y.js} +1 -1
- package/scripts/{chrome-pdf-goto-page.js → edu/chrome-pdf-goto-page.js} +1 -1
- package/scripts/{chrome-pdf-zoom-out.js → edu/chrome-pdf-zoom-out.js} +1 -1
- package/scripts/{hover-partial-chapter-match.js → edu/hover-partial-chapter-match.js} +1 -1
- package/scripts/maintainer/strip-extra-skills.ps1 +27 -0
- package/scripts/workbench/jova/outline-edit-root-title.js +33 -0
- package/scripts/workbench/jova/outline-keyword-edit.js +122 -0
- package/scripts/workbench/jova/outline-ops.js +344 -0
- package/scripts/workbench/jova/outline-probe-hover.js +56 -0
- package/scripts/workbench/jova/proofread-ai-rewrite-section.js +94 -0
- package/scripts/workbench/jova/proofread-ops.js +261 -0
- package/scripts/workbench/jova/writing-ops.js +376 -0
- package/scripts/workbench/jova/writing-proofread-abstract.js +103 -0
- package/lib/cli.runtime.config.json +0 -5
- package/references/vip.helixlife.cn.site-ops.md +0 -1469
- package/scripts/workbench-jova-outline-regenerate-node.js +0 -173
- package/scripts/workbench-jova-proofread-section.js +0 -262
- package/scripts/workbench-jova-writing-tool.js +0 -308
- /package/scripts/{analysis-detail-upload-both.js → analysis/detail-upload-both.js} +0 -0
- /package/scripts/{jigsaw-verify-bio-tools.js → analysis/jigsaw-verify-bio-tools.js} +0 -0
- /package/scripts/{jigsaw-verify-drag-tool.js → analysis/jigsaw-verify-drag-tool.js} +0 -0
- /package/scripts/{jigsaw-verify-reference-lines.js → analysis/jigsaw-verify-reference-lines.js} +0 -0
|
@@ -7,23 +7,51 @@
|
|
|
7
7
|
(() => {
|
|
8
8
|
if (window.__helix_visual__) return;
|
|
9
9
|
|
|
10
|
-
const VERSION =
|
|
10
|
+
const VERSION = "1.0.0";
|
|
11
11
|
const VISUAL_DEFAULTS = {
|
|
12
12
|
enabled: true,
|
|
13
13
|
durationMs: 420,
|
|
14
|
+
/** true:HUD 与高亮不自动消失(改样式/调试);false:操作结束后按 durationMs 消失 */
|
|
15
|
+
persist: false,
|
|
14
16
|
};
|
|
15
|
-
const VISUAL_STYLE_ID =
|
|
16
|
-
const VISUAL_LAYER_ID =
|
|
17
|
-
const VISUAL_HUD_ID =
|
|
17
|
+
const VISUAL_STYLE_ID = "__helix_visual_style__";
|
|
18
|
+
const VISUAL_LAYER_ID = "__helix_visual_layer__";
|
|
19
|
+
const VISUAL_HUD_ID = "__helix_visual_hud__";
|
|
18
20
|
const VISUAL_TONE_MAP = {
|
|
19
|
-
pending: {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
pending: {
|
|
22
|
+
border: "#faad14",
|
|
23
|
+
fill: "rgba(250, 173, 20, 0.16)",
|
|
24
|
+
pill: "#ad6800",
|
|
25
|
+
text: "#fffbe6",
|
|
26
|
+
statusDot: "#FFA406",
|
|
27
|
+
},
|
|
28
|
+
success: {
|
|
29
|
+
border: "#9747ff",
|
|
30
|
+
fill: "rgba(151, 71, 255, 0.14)",
|
|
31
|
+
pill: "#9747ff",
|
|
32
|
+
text: "#f6ffed",
|
|
33
|
+
statusDot: "#9747ff",
|
|
34
|
+
},
|
|
35
|
+
danger: {
|
|
36
|
+
border: "#FE2C55",
|
|
37
|
+
fill: "rgba(254, 44, 85, 0.14)",
|
|
38
|
+
pill: "#FE2C55",
|
|
39
|
+
text: "#ffe9ee",
|
|
40
|
+
statusDot: "#FE2C55",
|
|
41
|
+
},
|
|
42
|
+
info: {
|
|
43
|
+
border: "#6546e4",
|
|
44
|
+
fill: "rgba(101, 70, 228, 0.14)",
|
|
45
|
+
pill: "#6546e4",
|
|
46
|
+
text: "#f0f5ff",
|
|
47
|
+
statusDot: "#6546e4",
|
|
48
|
+
},
|
|
23
49
|
};
|
|
24
50
|
const visualState = {
|
|
25
51
|
config: { ...VISUAL_DEFAULTS },
|
|
26
52
|
hudTimer: null,
|
|
53
|
+
/** 当前持久选中框(select / 脚本步骤高亮) */
|
|
54
|
+
pinnedBox: null,
|
|
27
55
|
};
|
|
28
56
|
|
|
29
57
|
function clamp(n, min, max) {
|
|
@@ -38,16 +66,25 @@
|
|
|
38
66
|
|
|
39
67
|
function normalizeVisualOptions(options = {}) {
|
|
40
68
|
const base = { ...visualState.config };
|
|
41
|
-
if (!options || typeof options !==
|
|
42
|
-
if (typeof options.enabled ===
|
|
43
|
-
if (options.
|
|
69
|
+
if (!options || typeof options !== "object") return base;
|
|
70
|
+
if (typeof options.enabled === "boolean") base.enabled = options.enabled;
|
|
71
|
+
if (typeof options.persist === "boolean") base.persist = options.persist;
|
|
72
|
+
if (options.durationMs != null)
|
|
73
|
+
base.durationMs = normalizeDuration(options.durationMs, base.durationMs);
|
|
44
74
|
return base;
|
|
45
75
|
}
|
|
46
76
|
|
|
77
|
+
function getDomHost() {
|
|
78
|
+
return document.body || document.documentElement;
|
|
79
|
+
}
|
|
80
|
+
|
|
47
81
|
function ensureVisualRoot() {
|
|
82
|
+
const layerHost = getDomHost();
|
|
83
|
+
if (!layerHost) return null;
|
|
84
|
+
const styleParent = document.head || document.documentElement || layerHost;
|
|
48
85
|
let style = document.getElementById(VISUAL_STYLE_ID);
|
|
49
86
|
if (!style) {
|
|
50
|
-
style = document.createElement(
|
|
87
|
+
style = document.createElement("style");
|
|
51
88
|
style.id = VISUAL_STYLE_ID;
|
|
52
89
|
style.textContent = `
|
|
53
90
|
#${VISUAL_LAYER_ID}{
|
|
@@ -66,27 +103,100 @@
|
|
|
66
103
|
.__helix_visual_badge{
|
|
67
104
|
position:absolute;
|
|
68
105
|
left:0;
|
|
69
|
-
top:-
|
|
106
|
+
top:-40px;
|
|
107
|
+
width: max-content;
|
|
70
108
|
max-width:280px;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
109
|
+
display:inline-flex;
|
|
110
|
+
align-items:center;
|
|
111
|
+
gap:10px;
|
|
112
|
+
box-sizing:border-box;
|
|
113
|
+
padding:7px 10px;
|
|
114
|
+
border-radius:20px;
|
|
115
|
+
background:#19191E;
|
|
116
|
+
box-shadow:0 0 8px rgba(115, 66, 214, 0.8);
|
|
117
|
+
font-weight: 400;
|
|
118
|
+
font-size: 12px;
|
|
119
|
+
color: #EBEBEB;
|
|
120
|
+
}
|
|
121
|
+
.__helix_visual_badge::before{
|
|
122
|
+
content: '';
|
|
123
|
+
position: absolute;
|
|
124
|
+
inset: -1px;
|
|
125
|
+
background: linear-gradient(90deg, rgba(151, 71, 255, 1), rgba(101, 70, 228, 1));
|
|
126
|
+
z-index: -1;
|
|
127
|
+
border-radius: 20px;
|
|
128
|
+
padding: 0px;
|
|
129
|
+
}
|
|
130
|
+
.__helix_visual_badge_text{
|
|
77
131
|
overflow:hidden;
|
|
78
|
-
|
|
132
|
+
text-overflow:ellipsis;
|
|
79
133
|
}
|
|
80
|
-
|
|
134
|
+
.__helix_visual_badge_dot{
|
|
135
|
+
width:6px;
|
|
136
|
+
height:6px;
|
|
137
|
+
border-radius:100%;
|
|
138
|
+
flex-shrink:0;
|
|
139
|
+
}
|
|
140
|
+
#${VISUAL_HUD_ID},
|
|
141
|
+
.__helix_visual_hud_shell{
|
|
81
142
|
position:fixed;
|
|
143
|
+
z-index: 99;
|
|
82
144
|
top:16px;
|
|
83
145
|
right:16px;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
border-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
146
|
+
width:340px;
|
|
147
|
+
min-height:98px;
|
|
148
|
+
box-sizing:border-box;
|
|
149
|
+
padding:1px;
|
|
150
|
+
border-radius:20px;
|
|
151
|
+
background:linear-gradient(90deg, rgba(151, 71, 255, 1), rgba(101, 70, 228, 1));
|
|
152
|
+
box-shadow:0 0 8px rgba(115, 66, 214, 0.8);
|
|
153
|
+
}
|
|
154
|
+
.__helix_visual_hud{
|
|
155
|
+
width:100%;
|
|
156
|
+
min-height:96px;
|
|
157
|
+
box-sizing:border-box;
|
|
158
|
+
border-radius:19px;
|
|
159
|
+
background:#19191E;
|
|
160
|
+
padding:16px 18px;
|
|
161
|
+
color:rgba(255,255,255,0.88);
|
|
162
|
+
font:500 13px/1.4 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
|
163
|
+
}
|
|
164
|
+
.__helix_visual_hud_head{
|
|
165
|
+
display:flex;
|
|
166
|
+
align-items:center;
|
|
167
|
+
justify-content:space-between;
|
|
168
|
+
gap:12px;
|
|
169
|
+
margin-bottom:10px;
|
|
170
|
+
}
|
|
171
|
+
.__helix_visual_hud_action{
|
|
172
|
+
font-weight:600;
|
|
173
|
+
font-size:16px;
|
|
174
|
+
color:#EBEBEB;
|
|
175
|
+
}
|
|
176
|
+
.__helix_visual_hud_status{
|
|
177
|
+
display:inline-flex;
|
|
178
|
+
align-items:center;
|
|
179
|
+
gap:10px;
|
|
180
|
+
font-size:12px;
|
|
181
|
+
font-weight:400;
|
|
182
|
+
color:#EBEBEB;
|
|
183
|
+
white-space:nowrap;
|
|
184
|
+
}
|
|
185
|
+
.__helix_visual_hud_dot{
|
|
186
|
+
width:6px;
|
|
187
|
+
height:6px;
|
|
188
|
+
border-radius:100%;
|
|
189
|
+
flex-shrink:0;
|
|
190
|
+
}
|
|
191
|
+
.__helix_visual_hud_target{
|
|
192
|
+
padding:7px 10px;
|
|
193
|
+
background:rgba(235,235,235,0.05);
|
|
194
|
+
border-radius:10px;
|
|
195
|
+
display:flex;
|
|
196
|
+
align-items:center;
|
|
197
|
+
font-weight:400;
|
|
198
|
+
font-size:12px;
|
|
199
|
+
color:#B2B1B1;
|
|
90
200
|
}
|
|
91
201
|
@keyframes __helix_visual_pulse{
|
|
92
202
|
0%{transform:scale(.985);opacity:.2}
|
|
@@ -94,13 +204,13 @@
|
|
|
94
204
|
100%{transform:scale(1);opacity:1}
|
|
95
205
|
}
|
|
96
206
|
`;
|
|
97
|
-
|
|
207
|
+
styleParent.appendChild(style);
|
|
98
208
|
}
|
|
99
209
|
let layer = document.getElementById(VISUAL_LAYER_ID);
|
|
100
210
|
if (!layer) {
|
|
101
|
-
layer = document.createElement(
|
|
211
|
+
layer = document.createElement("div");
|
|
102
212
|
layer.id = VISUAL_LAYER_ID;
|
|
103
|
-
|
|
213
|
+
layerHost.appendChild(layer);
|
|
104
214
|
}
|
|
105
215
|
return layer;
|
|
106
216
|
}
|
|
@@ -110,74 +220,226 @@
|
|
|
110
220
|
}
|
|
111
221
|
|
|
112
222
|
function removeLater(el, durationMs) {
|
|
113
|
-
if (!el) return;
|
|
223
|
+
if (!el || visualState.config.persist) return;
|
|
114
224
|
const ms = normalizeDuration(durationMs);
|
|
115
225
|
window.setTimeout(() => {
|
|
116
226
|
if (el && el.parentNode) el.remove();
|
|
117
227
|
}, ms);
|
|
118
228
|
}
|
|
119
229
|
|
|
120
|
-
function
|
|
121
|
-
const
|
|
230
|
+
function appendBadge(box, spec, label, badgeTop) {
|
|
231
|
+
const badge = document.createElement("div");
|
|
232
|
+
badge.className = "__helix_visual_badge";
|
|
233
|
+
badge.style.top = badgeTop + "px";
|
|
234
|
+
|
|
235
|
+
const dot = document.createElement("span");
|
|
236
|
+
dot.className = "__helix_visual_badge_dot";
|
|
237
|
+
dot.style.background = spec.statusDot || spec.border;
|
|
238
|
+
|
|
239
|
+
const text = document.createElement("span");
|
|
240
|
+
text.className = "__helix_visual_badge_text";
|
|
241
|
+
text.textContent = label;
|
|
242
|
+
|
|
243
|
+
badge.appendChild(dot);
|
|
244
|
+
badge.appendChild(text);
|
|
245
|
+
box.appendChild(badge);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function createHighlightBox(layer, spec, label, rect) {
|
|
249
|
+
const box = document.createElement("div");
|
|
250
|
+
box.className = "__helix_visual_box";
|
|
251
|
+
box.style.left = rect.left + "px";
|
|
252
|
+
box.style.top = rect.top + "px";
|
|
253
|
+
box.style.width = rect.width + "px";
|
|
254
|
+
box.style.height = rect.height + "px";
|
|
255
|
+
// box.style.border = `2px solid ${spec.border}`;
|
|
256
|
+
// box.style.background = spec.fill;
|
|
257
|
+
// box.style.boxShadow = `0 0 0 1px ${spec.border}33, 0 12px 28px ${spec.border}22`;
|
|
258
|
+
box.style.border = "none";
|
|
259
|
+
box.style.boxShadow = "0 0 0 6px rgba(159, 71, 255, 0.2)";
|
|
260
|
+
if (label) {
|
|
261
|
+
appendBadge(box, spec, label, rect.top < 38 ? rect.height + 8 : -45);
|
|
262
|
+
}
|
|
263
|
+
layer.appendChild(box);
|
|
264
|
+
return box;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function populateHudCard(inner, options = {}) {
|
|
268
|
+
const {
|
|
269
|
+
action = "",
|
|
270
|
+
target = "",
|
|
271
|
+
status = "pending",
|
|
272
|
+
detail = "",
|
|
273
|
+
} = options;
|
|
274
|
+
const spec = toneSpec(status);
|
|
275
|
+
inner.replaceChildren();
|
|
276
|
+
|
|
277
|
+
const head = document.createElement("div");
|
|
278
|
+
head.className = "__helix_visual_hud_head";
|
|
279
|
+
|
|
280
|
+
const actionEl = document.createElement("span");
|
|
281
|
+
actionEl.className = "__helix_visual_hud_action";
|
|
282
|
+
actionEl.textContent = action;
|
|
283
|
+
|
|
284
|
+
const statusEl = document.createElement("span");
|
|
285
|
+
statusEl.className = "__helix_visual_hud_status";
|
|
286
|
+
|
|
287
|
+
const dot = document.createElement("span");
|
|
288
|
+
dot.className = "__helix_visual_hud_dot";
|
|
289
|
+
dot.style.background = spec.statusDot || spec.border;
|
|
290
|
+
|
|
291
|
+
const statusText = document.createElement("span");
|
|
292
|
+
statusText.textContent = detail;
|
|
293
|
+
|
|
294
|
+
statusEl.appendChild(dot);
|
|
295
|
+
statusEl.appendChild(statusText);
|
|
296
|
+
head.appendChild(actionEl);
|
|
297
|
+
head.appendChild(statusEl);
|
|
298
|
+
inner.appendChild(head);
|
|
299
|
+
|
|
300
|
+
const targetEl = document.createElement("div");
|
|
301
|
+
targetEl.className = "__helix_visual_hud_target";
|
|
302
|
+
const targetText = String(target || "").trim();
|
|
303
|
+
if (targetText) {
|
|
304
|
+
targetEl.textContent = targetText;
|
|
305
|
+
} else {
|
|
306
|
+
targetEl.textContent = "执行中…";
|
|
307
|
+
}
|
|
308
|
+
inner.appendChild(targetEl);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function createHudCard(options = {}) {
|
|
312
|
+
const shell = document.createElement("div");
|
|
313
|
+
shell.className = "__helix_visual_hud_shell";
|
|
314
|
+
const inner = document.createElement("div");
|
|
315
|
+
inner.className = "__helix_visual_hud";
|
|
316
|
+
populateHudCard(inner, options);
|
|
317
|
+
shell.appendChild(inner);
|
|
318
|
+
return shell;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function rectWithInset(rect, inset = 0) {
|
|
322
|
+
return {
|
|
323
|
+
left: Math.max(4, rect.left - inset),
|
|
324
|
+
top: Math.max(4, rect.top - inset),
|
|
325
|
+
width: Math.max(18, rect.width + inset * 2),
|
|
326
|
+
height: Math.max(18, rect.height + inset * 2),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function unpinSelection() {
|
|
331
|
+
if (visualState.pinnedBox && visualState.pinnedBox.parentNode) {
|
|
332
|
+
visualState.pinnedBox.remove();
|
|
333
|
+
}
|
|
334
|
+
visualState.pinnedBox = null;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* 持久选中态:紫色边框 + 角标标签,并同步右上角 HUD(用于 run-code 脚本步骤)。
|
|
339
|
+
*/
|
|
340
|
+
function selectElement(el, options = {}) {
|
|
341
|
+
const {
|
|
342
|
+
tone = "pending",
|
|
343
|
+
label = "",
|
|
344
|
+
action = "",
|
|
345
|
+
target = "",
|
|
346
|
+
detail = "",
|
|
347
|
+
inset = 4,
|
|
348
|
+
} = options;
|
|
122
349
|
if (!el || !visualState.config.enabled) return false;
|
|
123
350
|
const rect = el.getBoundingClientRect();
|
|
124
351
|
if (!rect || rect.width <= 0 || rect.height <= 0) return false;
|
|
125
352
|
const layer = ensureVisualRoot();
|
|
353
|
+
if (!layer) return false;
|
|
354
|
+
unpinSelection();
|
|
126
355
|
const spec = toneSpec(tone);
|
|
127
|
-
const box =
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
box.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
badge.style.top = (rect.top < 38 ? rect.height + 8 : -28) + 'px';
|
|
143
|
-
box.appendChild(badge);
|
|
356
|
+
const box = createHighlightBox(
|
|
357
|
+
layer,
|
|
358
|
+
spec,
|
|
359
|
+
label,
|
|
360
|
+
rectWithInset(rect, inset),
|
|
361
|
+
);
|
|
362
|
+
box.dataset.helixPinned = "1";
|
|
363
|
+
visualState.pinnedBox = box;
|
|
364
|
+
if (action || target || detail || label) {
|
|
365
|
+
showHud({
|
|
366
|
+
action: action || "步骤",
|
|
367
|
+
target: target || label,
|
|
368
|
+
status: tone,
|
|
369
|
+
detail: detail || label || "执行中…",
|
|
370
|
+
});
|
|
144
371
|
}
|
|
145
|
-
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function flashElement(el, options = {}) {
|
|
376
|
+
const {
|
|
377
|
+
tone = "info",
|
|
378
|
+
label = "",
|
|
379
|
+
durationMs,
|
|
380
|
+
inset = 0,
|
|
381
|
+
action = "",
|
|
382
|
+
target = "",
|
|
383
|
+
detail = "",
|
|
384
|
+
} = options;
|
|
385
|
+
if (!el || !visualState.config.enabled) return false;
|
|
386
|
+
const rect = el.getBoundingClientRect();
|
|
387
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return false;
|
|
388
|
+
const layer = ensureVisualRoot();
|
|
389
|
+
if (!layer) return false;
|
|
390
|
+
const spec = toneSpec(tone);
|
|
391
|
+
const box = createHighlightBox(
|
|
392
|
+
layer,
|
|
393
|
+
spec,
|
|
394
|
+
label,
|
|
395
|
+
rectWithInset(rect, inset),
|
|
396
|
+
);
|
|
146
397
|
removeLater(box, durationMs);
|
|
147
398
|
return true;
|
|
148
399
|
}
|
|
149
400
|
|
|
150
401
|
function showHud(options = {}) {
|
|
151
|
-
const {
|
|
402
|
+
const {
|
|
403
|
+
action = "",
|
|
404
|
+
target = "",
|
|
405
|
+
status = "pending",
|
|
406
|
+
detail = "",
|
|
407
|
+
durationMs,
|
|
408
|
+
} = options;
|
|
152
409
|
if (!visualState.config.enabled) return false;
|
|
153
410
|
const layer = ensureVisualRoot();
|
|
411
|
+
if (!layer) return false;
|
|
154
412
|
let hud = document.getElementById(VISUAL_HUD_ID);
|
|
155
413
|
if (!hud) {
|
|
156
|
-
hud =
|
|
414
|
+
hud = createHudCard({ action, target, status, detail });
|
|
157
415
|
hud.id = VISUAL_HUD_ID;
|
|
158
416
|
layer.appendChild(hud);
|
|
417
|
+
} else {
|
|
418
|
+
const inner = hud.querySelector(".__helix_visual_hud");
|
|
419
|
+
if (inner) populateHudCard(inner, { action, target, status, detail });
|
|
159
420
|
}
|
|
160
|
-
|
|
161
|
-
const lines = [];
|
|
162
|
-
if (action) lines.push(action);
|
|
163
|
-
if (target) lines.push(target);
|
|
164
|
-
if (detail) lines.push(detail);
|
|
165
|
-
hud.textContent = lines.join('\n');
|
|
166
|
-
hud.style.border = `1px solid ${spec.border}`;
|
|
167
|
-
hud.style.background = spec.fill.replace(/0\.\d+\)$/, '0.92)');
|
|
168
|
-
hud.style.color = spec.pill;
|
|
421
|
+
if (visualState.config.persist) return true;
|
|
169
422
|
if (visualState.hudTimer) clearTimeout(visualState.hudTimer);
|
|
170
|
-
visualState.hudTimer = window.setTimeout(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
423
|
+
visualState.hudTimer = window.setTimeout(
|
|
424
|
+
() => {
|
|
425
|
+
if (hud && hud.parentNode) hud.remove();
|
|
426
|
+
visualState.hudTimer = null;
|
|
427
|
+
},
|
|
428
|
+
normalizeDuration(
|
|
429
|
+
durationMs,
|
|
430
|
+
Math.max(900, visualState.config.durationMs * 2),
|
|
431
|
+
),
|
|
432
|
+
);
|
|
174
433
|
return true;
|
|
175
434
|
}
|
|
176
435
|
|
|
177
436
|
function cleanupVisualArtifacts() {
|
|
437
|
+
unpinSelection();
|
|
178
438
|
const layer = document.getElementById(VISUAL_LAYER_ID);
|
|
179
439
|
if (!layer) return;
|
|
180
|
-
Array.from(layer.querySelectorAll(
|
|
440
|
+
Array.from(layer.querySelectorAll(".__helix_visual_box")).forEach((el) =>
|
|
441
|
+
el.remove(),
|
|
442
|
+
);
|
|
181
443
|
const hud = document.getElementById(VISUAL_HUD_ID);
|
|
182
444
|
if (hud) hud.remove();
|
|
183
445
|
if (visualState.hudTimer) {
|
|
@@ -187,7 +449,7 @@
|
|
|
187
449
|
}
|
|
188
450
|
|
|
189
451
|
function setVisualOptions(options = {}) {
|
|
190
|
-
if (!options || typeof options !==
|
|
452
|
+
if (!options || typeof options !== "object") {
|
|
191
453
|
visualState.config = { ...VISUAL_DEFAULTS };
|
|
192
454
|
cleanupVisualArtifacts();
|
|
193
455
|
return { ok: true, data: { ...visualState.config } };
|
|
@@ -201,6 +463,8 @@
|
|
|
201
463
|
version: () => VERSION,
|
|
202
464
|
setVisualOptions,
|
|
203
465
|
flash: flashElement,
|
|
466
|
+
select: selectElement,
|
|
467
|
+
unpin: unpinSelection,
|
|
204
468
|
showHud,
|
|
205
469
|
cleanup: cleanupVisualArtifacts,
|
|
206
470
|
__meta: { version: VERSION, loadedAt: new Date().toISOString() },
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { HELIX_HOST_RE } = require('./helix-styles-guard');
|
|
4
|
+
|
|
5
|
+
const GUARD_KEY = '__helixCssCacheGuard__';
|
|
6
|
+
|
|
7
|
+
async function hasBrokenHelixStylesheet(page) {
|
|
8
|
+
return page.evaluate(() => {
|
|
9
|
+
const links = [...document.querySelectorAll('link[rel="stylesheet"]')];
|
|
10
|
+
const appCss = links.filter((l) => l.href.includes('/static/css/'));
|
|
11
|
+
if (appCss.length === 0) return false;
|
|
12
|
+
return appCss.some((l) => {
|
|
13
|
+
try {
|
|
14
|
+
return !l.sheet;
|
|
15
|
+
} catch {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function cssCacheGuard({ page }) {
|
|
23
|
+
if (page[GUARD_KEY]) return;
|
|
24
|
+
page[GUARD_KEY] = true;
|
|
25
|
+
|
|
26
|
+
const maybeFix = async () => {
|
|
27
|
+
try {
|
|
28
|
+
const url = page.url();
|
|
29
|
+
if (!HELIX_HOST_RE.test(url)) return;
|
|
30
|
+
const broken = await hasBrokenHelixStylesheet(page);
|
|
31
|
+
if (!broken) return;
|
|
32
|
+
if (page[`${GUARD_KEY}Reloaded`]) return;
|
|
33
|
+
page[`${GUARD_KEY}Reloaded`] = true;
|
|
34
|
+
await page.reload({ waitUntil: 'networkidle', bypassCache: true });
|
|
35
|
+
} catch {
|
|
36
|
+
// 页面已关闭或正在导航
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
page.on('load', () => {
|
|
41
|
+
void maybeFix();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (page.url() && page.url() !== 'about:blank') {
|
|
45
|
+
await maybeFix();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { default: cssCacheGuard };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 有头会话启动后通过 CDP 将首个浏览器窗口最大化(配合 launchOptions --start-maximized)。
|
|
5
|
+
* initPage 在 Playwright CLI 每个新标签页/窗口初始化时调用;后续页面不再强制最大化,
|
|
6
|
+
* 新窗口则复制已有窗口的位置与尺寸,避免用户手动调整窗口后新开页又变全屏。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
async function getWindowId(page) {
|
|
10
|
+
const cdp = await page.context().newCDPSession(page);
|
|
11
|
+
const { windowId } = await cdp.send('Browser.getWindowForTarget');
|
|
12
|
+
return { cdp, windowId };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function maximizeBrowserWindow({ page }) {
|
|
16
|
+
try {
|
|
17
|
+
const context = page.context();
|
|
18
|
+
const pages = context.pages();
|
|
19
|
+
|
|
20
|
+
if (pages.length > 1) {
|
|
21
|
+
await syncWindowBoundsFromExisting(page, pages);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { cdp, windowId } = await getWindowId(page);
|
|
26
|
+
await cdp.send('Browser.setWindowBounds', {
|
|
27
|
+
windowId,
|
|
28
|
+
bounds: { windowState: 'maximized' },
|
|
29
|
+
});
|
|
30
|
+
} catch {
|
|
31
|
+
// Chromium channel unavailable or CDP unsupported — rely on --start-maximized.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 新标签/新窗口:同窗口内无需处理;独立 popup 窗口则复制参考窗口 bounds。
|
|
37
|
+
*/
|
|
38
|
+
async function syncWindowBoundsFromExisting(page, pages) {
|
|
39
|
+
const refPage = pages.find((p) => p !== page && !p.isClosed()) ?? pages[0];
|
|
40
|
+
if (!refPage || refPage === page) return;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const { windowId: newWindowId } = await getWindowId(page);
|
|
44
|
+
const { windowId: refWindowId } = await getWindowId(refPage);
|
|
45
|
+
|
|
46
|
+
if (newWindowId === refWindowId) return;
|
|
47
|
+
|
|
48
|
+
const cdpRef = await page.context().newCDPSession(refPage);
|
|
49
|
+
const { bounds } = await cdpRef.send('Browser.getWindowBounds', {
|
|
50
|
+
windowId: refWindowId,
|
|
51
|
+
});
|
|
52
|
+
if (!bounds) return;
|
|
53
|
+
|
|
54
|
+
const cdpNew = await page.context().newCDPSession(page);
|
|
55
|
+
await cdpNew.send('Browser.setWindowBounds', {
|
|
56
|
+
windowId: newWindowId,
|
|
57
|
+
bounds,
|
|
58
|
+
});
|
|
59
|
+
} catch {
|
|
60
|
+
// CDP unsupported or race during close — ignore.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { default: maximizeBrowserWindow };
|