waygraph 0.15.30 → 0.15.31
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/dist/auto-explore-run.js +9 -6
- package/dist/pilot-overlay/fixtures.js +17 -17
- package/dist/pilot-overlay/panel.js +3 -3
- package/dist/pilot-overlay/shell.js +27 -27
- package/dist/runner/cursor.js +3 -3
- package/dist/runner/inpage/banner.js +2 -2
- package/dist/runner/inpage/core.js +23 -23
- package/dist/runner/inpage/device.js +11 -11
- package/dist/runner/inpage/panel-chrome.js +3 -3
- package/dist/runner/inpage/stamp.js +2 -2
- package/dist/runner/inpage/swipe-zoom.js +10 -10
- package/dist/runner/inpage/todos.js +6 -6
- package/dist/runner/overlay-css.js +21 -18
- package/dist/runner/overlay-install.js +5 -2
- package/dist/runner/rings.js +1 -1
- package/dist/runner/slides.js +11 -11
- package/dist/runner/step-panels.js +18 -18
- package/dist/runner/teardown.js +8 -8
- package/dist/runner/todo-dock.js +1 -1
- package/dist/step-overlay.js +20 -18
- package/dist/ui/shadow.d.ts +15 -0
- package/dist/ui/shadow.js +42 -0
- package/examples/saucedemo/.waygraph-auto/1bd7b7bf.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2a577961.log +0 -0
- package/examples/saucedemo/.waygraph-auto/401177a3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/68fa0682.log +0 -0
- package/examples/saucedemo/.waygraph-auto/982138cf.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a8645294.log +0 -0
- package/examples/saucedemo/.waygraph-auto/fc917c18.log +0 -0
- package/package.json +1 -1
package/dist/auto-explore-run.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { ensureShadowRoot } from "./ui/shadow.js";
|
|
2
3
|
import { stdin as input, stdout as output } from "node:process";
|
|
3
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
5
|
import { join } from "node:path";
|
|
@@ -168,7 +169,7 @@ export async function ensureMem(entry, mem, cli) {
|
|
|
168
169
|
async function setAutoPanelRunning(page, label) {
|
|
169
170
|
await page
|
|
170
171
|
.evaluate((label) => {
|
|
171
|
-
const panel =
|
|
172
|
+
const panel = __wgById("wg-auto-panel");
|
|
172
173
|
if (!panel)
|
|
173
174
|
return;
|
|
174
175
|
const body = "<h3>waygraph auto</h3><div style='padding:12px 0;color:#c9a6ff'>" +
|
|
@@ -291,9 +292,10 @@ function memKeysForMenu(menu, library) {
|
|
|
291
292
|
async function syncPanelMem(page, mem, keys) {
|
|
292
293
|
if (keys.length === 0)
|
|
293
294
|
return;
|
|
295
|
+
await ensureShadowRoot(page);
|
|
294
296
|
const edits = (await page.evaluate(() => {
|
|
295
297
|
const out = {};
|
|
296
|
-
|
|
298
|
+
__wgQA("textarea.wg-mem-input").forEach((ta) => {
|
|
297
299
|
const key = ta.getAttribute("data-mem-key");
|
|
298
300
|
if (key)
|
|
299
301
|
out[key] = ta.value;
|
|
@@ -382,14 +384,15 @@ async function headfulPick(page, menu, library, mem, waitPick) {
|
|
|
382
384
|
html =
|
|
383
385
|
`<style id="wg-auto-style">${PANEL_CSS}</style><div id="wg-auto-panel">` +
|
|
384
386
|
html.slice(html.indexOf("<h3>"));
|
|
387
|
+
await ensureShadowRoot(page);
|
|
385
388
|
await page.evaluate(`((inner) => {
|
|
386
|
-
let panel =
|
|
389
|
+
let panel = __wgById("wg-auto-panel");
|
|
387
390
|
if (!panel) {
|
|
388
391
|
panel = document.createElement("div");
|
|
389
|
-
|
|
392
|
+
__wgAdd(panel);
|
|
390
393
|
}
|
|
391
394
|
panel.outerHTML = inner;
|
|
392
|
-
const root =
|
|
395
|
+
const root = __wgById("wg-auto-panel");
|
|
393
396
|
if (!root) return;
|
|
394
397
|
window.__wgWirePanelChrome = (el, storageKey, chromeTitle) => {
|
|
395
398
|
if (!el) return;
|
|
@@ -446,7 +449,7 @@ async function headfulPick(page, menu, library, mem, waitPick) {
|
|
|
446
449
|
window.__wgOverlayBeacon =
|
|
447
450
|
window.__wgOverlayBeacon ||
|
|
448
451
|
(() =>
|
|
449
|
-
Array.from(
|
|
452
|
+
Array.from(__wgQA('[data-wg-ui="1"]')).map((el) => {
|
|
450
453
|
const r = el.getBoundingClientRect();
|
|
451
454
|
const st = getComputedStyle(el);
|
|
452
455
|
return {
|
|
@@ -98,22 +98,22 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
98
98
|
if (w.__wgPilotFxHideTimer)
|
|
99
99
|
clearTimeout(w.__wgPilotFxHideTimer);
|
|
100
100
|
const clearFocus = () => {
|
|
101
|
-
const veil =
|
|
101
|
+
const veil = __wgById("wg-pilot-fx-focus");
|
|
102
102
|
if (!veil)
|
|
103
103
|
return;
|
|
104
104
|
veil.classList.remove("wg-in");
|
|
105
105
|
setTimeout(() => {
|
|
106
|
-
const v =
|
|
106
|
+
const v = __wgById("wg-pilot-fx-focus");
|
|
107
107
|
if (v && !v.classList.contains("wg-in"))
|
|
108
108
|
v.remove();
|
|
109
109
|
}, 320);
|
|
110
110
|
};
|
|
111
111
|
const applyFocus = (box) => {
|
|
112
|
-
let veil =
|
|
112
|
+
let veil = __wgById("wg-pilot-fx-focus");
|
|
113
113
|
if (!veil) {
|
|
114
114
|
veil = document.createElement("div");
|
|
115
115
|
veil.id = "wg-pilot-fx-focus";
|
|
116
|
-
|
|
116
|
+
__wgAdd(veil);
|
|
117
117
|
}
|
|
118
118
|
const pad = 10;
|
|
119
119
|
veil.style.left = `${Math.max(0, box.x - pad)}px`;
|
|
@@ -125,11 +125,11 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
125
125
|
};
|
|
126
126
|
const setZoomBadge = (scale) => {
|
|
127
127
|
const level = Number.isFinite(scale) && scale > 0 ? scale : 1;
|
|
128
|
-
let badge =
|
|
128
|
+
let badge = __wgById("wg-pilot-fx-zoom");
|
|
129
129
|
if (!badge) {
|
|
130
130
|
badge = document.createElement("div");
|
|
131
131
|
badge.id = "wg-pilot-fx-zoom";
|
|
132
|
-
|
|
132
|
+
__wgAdd(badge);
|
|
133
133
|
}
|
|
134
134
|
const zoomed = level > 1.001 || level < 0.999;
|
|
135
135
|
badge.dataset.zoomed = zoomed ? "1" : "0";
|
|
@@ -143,27 +143,27 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
143
143
|
root.style.removeProperty("transform");
|
|
144
144
|
root.style.removeProperty("transform-origin");
|
|
145
145
|
root.style.removeProperty("transition");
|
|
146
|
-
|
|
146
|
+
__wgById("wg-pilot-fx-zoom")?.remove();
|
|
147
147
|
};
|
|
148
148
|
const setDeviceChip = (label) => {
|
|
149
|
-
|
|
149
|
+
__wgById("wg-pilot-fx-device")?.remove();
|
|
150
150
|
if (!label)
|
|
151
151
|
return;
|
|
152
152
|
const chip = document.createElement("div");
|
|
153
153
|
chip.id = "wg-pilot-fx-device";
|
|
154
154
|
chip.dataset.preset = label;
|
|
155
155
|
chip.textContent = `device \u00b7 ${label}`;
|
|
156
|
-
|
|
156
|
+
__wgAdd(chip);
|
|
157
157
|
};
|
|
158
158
|
w.__wgPilotFxZoomOutOnHide = zoomOut;
|
|
159
159
|
const clearFx = () => {
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
__wgQA(".wg-pilot-fx-ring, .wg-pilot-fx-label").forEach((el) => el.remove());
|
|
161
|
+
__wgById("wg-pilot-fx-todos")?.remove();
|
|
162
162
|
clearFocus();
|
|
163
163
|
if (w.__wgPilotFxZoomOutOnHide !== false || clearAll)
|
|
164
164
|
clearZoom();
|
|
165
165
|
if (clearAll) {
|
|
166
|
-
|
|
166
|
+
__wgById("wg-pilot-fx-device")?.remove();
|
|
167
167
|
clearZoom();
|
|
168
168
|
}
|
|
169
169
|
};
|
|
@@ -206,8 +206,8 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
206
206
|
if (ring.color) {
|
|
207
207
|
labelEl.style.color = ring.color;
|
|
208
208
|
}
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
__wgAdd(ringEl);
|
|
210
|
+
__wgAdd(labelEl);
|
|
211
211
|
const lw = labelEl.offsetWidth;
|
|
212
212
|
const lh = labelEl.offsetHeight;
|
|
213
213
|
let labelLeft = rect.left - pad;
|
|
@@ -266,18 +266,18 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
266
266
|
more.textContent = "+" + (todos.length - (end - start)) + " more";
|
|
267
267
|
dock.appendChild(more);
|
|
268
268
|
}
|
|
269
|
-
|
|
269
|
+
__wgAdd(dock);
|
|
270
270
|
}
|
|
271
271
|
if (focusBox)
|
|
272
272
|
applyFocus(focusBox);
|
|
273
273
|
// Dim todos under rings / focus so captions stay readable.
|
|
274
|
-
const dockEl =
|
|
274
|
+
const dockEl = __wgById("wg-pilot-fx-todos");
|
|
275
275
|
if (dockEl && todoUi.behindRing !== false && (painted > 0 || focusBox)) {
|
|
276
276
|
dockEl.classList.add("wg-todo-behind");
|
|
277
277
|
}
|
|
278
278
|
// Collision flip: if a painted ring intersects the dock, flip side once.
|
|
279
279
|
if (dockEl && todoUi.collision !== false && painted > 0) {
|
|
280
|
-
const firstRing =
|
|
280
|
+
const firstRing = __wgQ(".wg-pilot-fx-ring");
|
|
281
281
|
if (firstRing) {
|
|
282
282
|
const rr = firstRing.getBoundingClientRect();
|
|
283
283
|
const dr = dockEl.getBoundingClientRect();
|
|
@@ -10,9 +10,9 @@ export async function updatePilotOverlay(page, info) {
|
|
|
10
10
|
await ensureInstalled(page);
|
|
11
11
|
await page
|
|
12
12
|
.evaluate(async ({ sessionId, snapshot, graph }) => {
|
|
13
|
-
const badge =
|
|
14
|
-
const current =
|
|
15
|
-
const graphPanel =
|
|
13
|
+
const badge = __wgById("wg-pilot-badge");
|
|
14
|
+
const current = __wgById("wg-pilot-panel-current");
|
|
15
|
+
const graphPanel = __wgById("wg-pilot-panel-graph");
|
|
16
16
|
if (!badge || !current || !graphPanel)
|
|
17
17
|
return;
|
|
18
18
|
const idPart = sessionId ? `session ${sessionId}` : "no session id";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Split out of the former 950-line pilot-overlay.ts (see src/ARCHITECTURE.md). Behavior unchanged.
|
|
2
2
|
import { SURFACE } from "../ui/tokens.js";
|
|
3
|
+
import { ensureShadowRoot, installShadowRoot } from "../ui/shadow.js";
|
|
3
4
|
import { pilotFxRingCss } from "../ui/components/ring.js";
|
|
4
5
|
import { WAYGRAPH_RING_CSS } from "../highlights.js";
|
|
5
6
|
// Real, direct user request: this overlay's own colors were an improvised
|
|
@@ -109,12 +110,9 @@ ${pilotFxRingCss()}
|
|
|
109
110
|
` + WAYGRAPH_RING_CSS;
|
|
110
111
|
/** Browser-side shell install — runs on every document via context.addInitScript. */
|
|
111
112
|
function installPilotOverlayShell(css) {
|
|
112
|
-
if (
|
|
113
|
+
if (__wgById("wg-pilot-overlay"))
|
|
113
114
|
return;
|
|
114
|
-
|
|
115
|
-
style.id = "wg-pilot-overlay-css";
|
|
116
|
-
style.textContent = css;
|
|
117
|
-
document.documentElement.appendChild(style);
|
|
115
|
+
__wgCss(css, "wg-pilot-overlay-css");
|
|
118
116
|
const root = document.createElement("div");
|
|
119
117
|
root.id = "wg-pilot-overlay";
|
|
120
118
|
root.innerHTML =
|
|
@@ -125,36 +123,36 @@ function installPilotOverlayShell(css) {
|
|
|
125
123
|
'<div id="wg-pilot-panel-current"></div>' +
|
|
126
124
|
'<div id="wg-pilot-panel-graph" style="display:none"></div>' +
|
|
127
125
|
"</div>";
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
__wgAdd(root);
|
|
127
|
+
__wgById("wg-pilot-badge").addEventListener("click", () => {
|
|
128
|
+
__wgById("wg-pilot-panel").classList.toggle("wg-pilot-open");
|
|
131
129
|
});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
130
|
+
__wgById("wg-pilot-view-current").addEventListener("click", () => {
|
|
131
|
+
__wgById("wg-pilot-view-current").classList.add("wg-pilot-tab-active");
|
|
132
|
+
__wgById("wg-pilot-view-graph").classList.remove("wg-pilot-tab-active");
|
|
133
|
+
__wgById("wg-pilot-panel-current").style.display = "";
|
|
134
|
+
__wgById("wg-pilot-panel-graph").style.display = "none";
|
|
137
135
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
__wgById("wg-pilot-view-graph").addEventListener("click", () => {
|
|
137
|
+
__wgById("wg-pilot-view-graph").classList.add("wg-pilot-tab-active");
|
|
138
|
+
__wgById("wg-pilot-view-current").classList.remove("wg-pilot-tab-active");
|
|
139
|
+
__wgById("wg-pilot-panel-graph").style.display = "";
|
|
140
|
+
__wgById("wg-pilot-panel-current").style.display = "none";
|
|
143
141
|
});
|
|
144
|
-
if (!
|
|
142
|
+
if (!__wgById("wg-pilot-activity")) {
|
|
145
143
|
const toast = document.createElement("div");
|
|
146
144
|
toast.id = "wg-pilot-activity";
|
|
147
|
-
|
|
145
|
+
__wgAdd(toast);
|
|
148
146
|
}
|
|
149
|
-
if (!
|
|
147
|
+
if (!__wgById("wg-ring")) {
|
|
150
148
|
const ring = document.createElement("div");
|
|
151
149
|
ring.id = "wg-ring";
|
|
152
|
-
|
|
150
|
+
__wgAdd(ring);
|
|
153
151
|
}
|
|
154
|
-
if (!
|
|
152
|
+
if (!__wgById("wg-ring-label")) {
|
|
155
153
|
const ringLabel = document.createElement("div");
|
|
156
154
|
ringLabel.id = "wg-ring-label";
|
|
157
|
-
|
|
155
|
+
__wgAdd(ringLabel);
|
|
158
156
|
}
|
|
159
157
|
}
|
|
160
158
|
/**
|
|
@@ -163,10 +161,12 @@ function installPilotOverlayShell(css) {
|
|
|
163
161
|
* page is blank or replaced.
|
|
164
162
|
*/
|
|
165
163
|
export async function installPersistentPilotOverlay(context) {
|
|
164
|
+
await context.addInitScript(installShadowRoot);
|
|
166
165
|
await context.addInitScript(installPilotOverlayShell, OVERLAY_CSS);
|
|
167
166
|
}
|
|
168
167
|
/** Idempotent - safe to call before every update, matching narrate mode's own precedent. */
|
|
169
168
|
export async function ensureInstalled(page) {
|
|
169
|
+
await ensureShadowRoot(page);
|
|
170
170
|
await page.evaluate(installPilotOverlayShell, OVERLAY_CSS).catch(() => { });
|
|
171
171
|
}
|
|
172
172
|
/**
|
|
@@ -183,7 +183,7 @@ export async function showPilotActivity(page, text) {
|
|
|
183
183
|
await ensureInstalled(page);
|
|
184
184
|
await page
|
|
185
185
|
.evaluate((text) => {
|
|
186
|
-
const toast =
|
|
186
|
+
const toast = __wgById("wg-pilot-activity");
|
|
187
187
|
if (!toast)
|
|
188
188
|
return;
|
|
189
189
|
toast.textContent = text;
|
|
@@ -227,8 +227,8 @@ export async function showPilotVision(page, selector, label, tone = "auto") {
|
|
|
227
227
|
await ensureInstalled(page);
|
|
228
228
|
await page
|
|
229
229
|
.evaluate(({ selector, label, tone }) => {
|
|
230
|
-
const ring =
|
|
231
|
-
const tag =
|
|
230
|
+
const ring = __wgById("wg-ring");
|
|
231
|
+
const tag = __wgById("wg-ring-label");
|
|
232
232
|
if (!ring || !tag)
|
|
233
233
|
return;
|
|
234
234
|
const w = window;
|
package/dist/runner/cursor.js
CHANGED
|
@@ -48,7 +48,7 @@ export async function playSwipeTrail(page, opts) {
|
|
|
48
48
|
: "left";
|
|
49
49
|
const ms = Math.max(280, Math.min(1400, Number(payload.ms) || 720));
|
|
50
50
|
const label = payload.label || "swipe";
|
|
51
|
-
let layer =
|
|
51
|
+
let layer = __wgById("wg-swipe-layer");
|
|
52
52
|
if (layer) layer.remove();
|
|
53
53
|
layer = document.createElement("div");
|
|
54
54
|
layer.id = "wg-swipe-layer";
|
|
@@ -67,7 +67,7 @@ export async function playSwipeTrail(page, opts) {
|
|
|
67
67
|
"border:1px solid rgba(56,189,248,.55);font:700 12px/1.2 system-ui,sans-serif;";
|
|
68
68
|
layer.appendChild(lab);
|
|
69
69
|
layer.appendChild(finger);
|
|
70
|
-
|
|
70
|
+
__wgAdd(layer);
|
|
71
71
|
const vw = window.innerWidth || 390;
|
|
72
72
|
const vh = window.innerHeight || 844;
|
|
73
73
|
const pad = Math.min(vw, vh) * 0.18;
|
|
@@ -121,7 +121,7 @@ export async function playSwipeTrail(page, opts) {
|
|
|
121
121
|
});
|
|
122
122
|
if (i === steps) {
|
|
123
123
|
setTimeout(() => {
|
|
124
|
-
const el =
|
|
124
|
+
const el = __wgById("wg-swipe-layer");
|
|
125
125
|
if (el) el.remove();
|
|
126
126
|
}, 400);
|
|
127
127
|
}
|
|
@@ -12,7 +12,7 @@ export function installBanner({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
if (title) {
|
|
15
|
-
let banner =
|
|
15
|
+
let banner = __wgById("wg-banner");
|
|
16
16
|
if (!banner) {
|
|
17
17
|
banner = document.createElement("div");
|
|
18
18
|
banner.id = "wg-banner";
|
|
@@ -39,7 +39,7 @@ export function installBanner({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
39
39
|
text.textContent = title;
|
|
40
40
|
banner.appendChild(tag);
|
|
41
41
|
banner.appendChild(text);
|
|
42
|
-
|
|
42
|
+
__wgAdd(banner);
|
|
43
43
|
} else {
|
|
44
44
|
// Fixture / episode title changes every step - update in place
|
|
45
45
|
// (banner is created once; do not leave the first step's text stuck).
|
|
@@ -19,17 +19,17 @@ export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, t
|
|
|
19
19
|
} catch {
|
|
20
20
|
/* private mode / blocked storage - falls back to manual gating */
|
|
21
21
|
}
|
|
22
|
-
if (!
|
|
22
|
+
if (!__wgById("wg-ring")) {
|
|
23
23
|
const ring = document.createElement("div");
|
|
24
24
|
ring.id = "wg-ring";
|
|
25
|
-
|
|
25
|
+
__wgAdd(ring);
|
|
26
26
|
}
|
|
27
|
-
if (!
|
|
27
|
+
if (!__wgById("wg-ring-label")) {
|
|
28
28
|
const ringLabel = document.createElement("div");
|
|
29
29
|
ringLabel.id = "wg-ring-label";
|
|
30
|
-
|
|
30
|
+
__wgAdd(ringLabel);
|
|
31
31
|
}
|
|
32
|
-
if (!
|
|
32
|
+
if (!__wgById("wg-cursor")) {
|
|
33
33
|
const cursor = document.createElement("div");
|
|
34
34
|
cursor.id = "wg-cursor";
|
|
35
35
|
// Dark fill + white stroke, same as help-center-clip-engine's own
|
|
@@ -39,15 +39,15 @@ export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, t
|
|
|
39
39
|
"<svg viewBox='0 0 32 32' width='24' height='24'>" +
|
|
40
40
|
"<path fill='#0C0C1A' stroke='#fff' stroke-width='1.4' stroke-linejoin='round' " +
|
|
41
41
|
"d='M6 3.5l1.4 22.5 5.8-5.4 4.2 9.4 3.6-1.6-4.2-9.2H26z'/></svg>";
|
|
42
|
-
|
|
42
|
+
__wgAdd(cursor);
|
|
43
43
|
}
|
|
44
|
-
if (!
|
|
44
|
+
if (!__wgById("wg-click-pulse")) {
|
|
45
45
|
const pulse = document.createElement("div");
|
|
46
46
|
pulse.id = "wg-click-pulse";
|
|
47
|
-
|
|
47
|
+
__wgAdd(pulse);
|
|
48
48
|
}
|
|
49
49
|
window.__wgMoveCursorTo = (x, y, ms, instant) => {
|
|
50
|
-
const cursor =
|
|
50
|
+
const cursor = __wgById("wg-cursor");
|
|
51
51
|
if (!cursor) return;
|
|
52
52
|
cursor.style.setProperty("--wg-cursor-ms", (ms || 600) + "ms");
|
|
53
53
|
if (instant) {
|
|
@@ -62,12 +62,12 @@ export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, t
|
|
|
62
62
|
cursor.style.opacity = "1";
|
|
63
63
|
};
|
|
64
64
|
window.__wgHideCursor = () => {
|
|
65
|
-
const cursor =
|
|
65
|
+
const cursor = __wgById("wg-cursor");
|
|
66
66
|
if (cursor) cursor.style.opacity = "0";
|
|
67
67
|
};
|
|
68
68
|
window.__wgPositionRing = (box, label, tone, style) => {
|
|
69
|
-
const ring =
|
|
70
|
-
const ringLabel =
|
|
69
|
+
const ring = __wgById("wg-ring");
|
|
70
|
+
const ringLabel = __wgById("wg-ring-label");
|
|
71
71
|
if (!ring || !ringLabel || !box) return;
|
|
72
72
|
const raw = (tone || "planned") + "";
|
|
73
73
|
const t =
|
|
@@ -119,12 +119,12 @@ export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, t
|
|
|
119
119
|
window.__wgTodosSetBehind = (on) => {
|
|
120
120
|
const ui = window.__wgTodoDockUi || {};
|
|
121
121
|
if (ui.behindRing === false) {
|
|
122
|
-
|
|
122
|
+
__wgQA(".wg-todo-dock, #wg-todo-dock").forEach((el) => {
|
|
123
123
|
el.classList.remove("wg-todo-behind");
|
|
124
124
|
});
|
|
125
125
|
return;
|
|
126
126
|
}
|
|
127
|
-
|
|
127
|
+
__wgQA(".wg-todo-dock, #wg-todo-dock").forEach((el) => {
|
|
128
128
|
el.classList.toggle("wg-todo-behind", !!on);
|
|
129
129
|
});
|
|
130
130
|
};
|
|
@@ -145,7 +145,7 @@ export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, t
|
|
|
145
145
|
const r = el.getBoundingClientRect();
|
|
146
146
|
return !(r.right < rx1 || r.left > rx2 || r.bottom < ry1 || r.top > ry2);
|
|
147
147
|
};
|
|
148
|
-
const docks = [...
|
|
148
|
+
const docks = [...__wgQA(".wg-todo-dock, #wg-todo-dock")].filter(
|
|
149
149
|
(el, i, arr) => arr.indexOf(el) === i,
|
|
150
150
|
);
|
|
151
151
|
for (const dock of docks) {
|
|
@@ -194,7 +194,7 @@ export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, t
|
|
|
194
194
|
tick();
|
|
195
195
|
};
|
|
196
196
|
window.__wgClickPulse = (x, y, tone) => {
|
|
197
|
-
const pulse =
|
|
197
|
+
const pulse = __wgById("wg-click-pulse");
|
|
198
198
|
if (!pulse) return;
|
|
199
199
|
const raw = (tone || "planned") + "";
|
|
200
200
|
pulse.dataset.tone =
|
|
@@ -214,14 +214,14 @@ export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, t
|
|
|
214
214
|
// action finishes, not just its first sub-step.
|
|
215
215
|
if (window.__wgNarrateOwnsRing) return;
|
|
216
216
|
if (window.__wgStopRingFollow) window.__wgStopRingFollow();
|
|
217
|
-
const ring =
|
|
218
|
-
const ringLabel =
|
|
217
|
+
const ring = __wgById("wg-ring");
|
|
218
|
+
const ringLabel = __wgById("wg-ring-label");
|
|
219
219
|
if (ring) ring.style.opacity = "0";
|
|
220
220
|
if (ringLabel) ringLabel.style.opacity = "0";
|
|
221
221
|
if (window.__wgClearFocus) window.__wgClearFocus();
|
|
222
222
|
if (window.__wgTodosSetBehind) window.__wgTodosSetBehind(false);
|
|
223
223
|
window.__wgTodoCollisionLocked = false;
|
|
224
|
-
|
|
224
|
+
__wgQA(".wg-todo-dock[data-tucked], #wg-todo-dock[data-tucked]").forEach((el) => {
|
|
225
225
|
delete el.dataset.tucked;
|
|
226
226
|
});
|
|
227
227
|
// Honor zoomOut:false - keep camera until next zoom / desktop clear.
|
|
@@ -236,11 +236,11 @@ export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, t
|
|
|
236
236
|
}
|
|
237
237
|
};
|
|
238
238
|
window.__wgClearFocus = () => {
|
|
239
|
-
const veil =
|
|
239
|
+
const veil = __wgById("wg-focus-veil");
|
|
240
240
|
if (!veil) return;
|
|
241
241
|
veil.classList.remove("wg-in");
|
|
242
242
|
setTimeout(() => {
|
|
243
|
-
const v =
|
|
243
|
+
const v = __wgById("wg-focus-veil");
|
|
244
244
|
if (v && !v.classList.contains("wg-in")) v.remove();
|
|
245
245
|
}, 320);
|
|
246
246
|
};
|
|
@@ -249,12 +249,12 @@ export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, t
|
|
|
249
249
|
if (window.__wgClearFocus) window.__wgClearFocus();
|
|
250
250
|
return;
|
|
251
251
|
}
|
|
252
|
-
let veil =
|
|
252
|
+
let veil = __wgById("wg-focus-veil");
|
|
253
253
|
if (!veil) {
|
|
254
254
|
veil = document.createElement("div");
|
|
255
255
|
veil.id = "wg-focus-veil";
|
|
256
256
|
veil.setAttribute("data-wg-ui", "1");
|
|
257
|
-
|
|
257
|
+
__wgAdd(veil);
|
|
258
258
|
}
|
|
259
259
|
const pad = 10;
|
|
260
260
|
veil.style.left = Math.max(0, box.x - pad) + "px";
|
|
@@ -10,7 +10,7 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
10
10
|
? payload.sync
|
|
11
11
|
: "keep";
|
|
12
12
|
const device = payload && payload.device ? payload.device : null;
|
|
13
|
-
const cursor =
|
|
13
|
+
const cursor = __wgById("wg-cursor");
|
|
14
14
|
const MOUSE_SVG =
|
|
15
15
|
"<svg viewBox='0 0 32 32' width='24' height='24'>" +
|
|
16
16
|
"<path fill='#0C0C1A' stroke='#fff' stroke-width='1.4' stroke-linejoin='round' " +
|
|
@@ -45,12 +45,12 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
const ensureChip = (preset, touch, remain, orientation) => {
|
|
48
|
-
let badge =
|
|
48
|
+
let badge = __wgById("wg-device-badge");
|
|
49
49
|
if (preset === "desktop" && !touch) {
|
|
50
50
|
if (badge) {
|
|
51
51
|
badge.classList.remove("wg-in");
|
|
52
52
|
setTimeout(() => {
|
|
53
|
-
const b =
|
|
53
|
+
const b = __wgById("wg-device-badge");
|
|
54
54
|
if (b) b.remove();
|
|
55
55
|
}, 320);
|
|
56
56
|
}
|
|
@@ -60,7 +60,7 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
60
60
|
badge = document.createElement("div");
|
|
61
61
|
badge.id = "wg-device-badge";
|
|
62
62
|
badge.setAttribute("data-wg-ui", "1");
|
|
63
|
-
|
|
63
|
+
__wgAdd(badge);
|
|
64
64
|
}
|
|
65
65
|
badge.dataset.preset = preset;
|
|
66
66
|
badge.dataset.touch = touch ? "1" : "0";
|
|
@@ -85,7 +85,7 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
85
85
|
badge.classList.add("wg-in");
|
|
86
86
|
};
|
|
87
87
|
const showToast = (preset, touch, title, sub, orientation) => {
|
|
88
|
-
let toast =
|
|
88
|
+
let toast = __wgById("wg-device-toast");
|
|
89
89
|
if (toast && toast._wgTimer) {
|
|
90
90
|
clearTimeout(toast._wgTimer);
|
|
91
91
|
toast._wgTimer = null;
|
|
@@ -94,10 +94,10 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
94
94
|
toast = document.createElement("div");
|
|
95
95
|
toast.id = "wg-device-toast";
|
|
96
96
|
toast.setAttribute("data-wg-ui", "1");
|
|
97
|
-
|
|
97
|
+
__wgAdd(toast);
|
|
98
98
|
}
|
|
99
99
|
// Hide chip while toast is up (same corner).
|
|
100
|
-
const badge =
|
|
100
|
+
const badge = __wgById("wg-device-badge");
|
|
101
101
|
if (badge) badge.classList.remove("wg-in");
|
|
102
102
|
toast.dataset.preset = preset;
|
|
103
103
|
toast.innerHTML =
|
|
@@ -115,7 +115,7 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
115
115
|
toast.classList.add("wg-out");
|
|
116
116
|
toast.classList.remove("wg-in");
|
|
117
117
|
setTimeout(() => {
|
|
118
|
-
const t =
|
|
118
|
+
const t = __wgById("wg-device-toast");
|
|
119
119
|
if (t) t.remove();
|
|
120
120
|
if (preset !== "desktop" || touch) {
|
|
121
121
|
ensureChip(preset, touch, true, orientation);
|
|
@@ -142,13 +142,13 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
142
142
|
"landscape",
|
|
143
143
|
);
|
|
144
144
|
setTimeout(() => {
|
|
145
|
-
const badge =
|
|
145
|
+
const badge = __wgById("wg-device-badge");
|
|
146
146
|
if (badge) badge.remove();
|
|
147
147
|
}, 2600);
|
|
148
148
|
} else {
|
|
149
|
-
const badge =
|
|
149
|
+
const badge = __wgById("wg-device-badge");
|
|
150
150
|
if (badge) badge.remove();
|
|
151
|
-
const toast =
|
|
151
|
+
const toast = __wgById("wg-device-toast");
|
|
152
152
|
if (toast) toast.remove();
|
|
153
153
|
}
|
|
154
154
|
window.__wgDevicePreset = "desktop";
|
|
@@ -69,7 +69,7 @@ export function installPanelChrome({ title, favicon, bannerPos, todoPos, envAuto
|
|
|
69
69
|
} catch {
|
|
70
70
|
/* ignore */
|
|
71
71
|
}
|
|
72
|
-
const runBtn =
|
|
72
|
+
const runBtn = __wgById("wg-run");
|
|
73
73
|
const show = !!hidden && !auto && !!runBtn;
|
|
74
74
|
miniNext.classList.toggle("wg-mini-next-show", show);
|
|
75
75
|
if (runBtn) {
|
|
@@ -125,7 +125,7 @@ export function installPanelChrome({ title, favicon, bannerPos, todoPos, envAuto
|
|
|
125
125
|
miniNext.dataset.wgWired = "1";
|
|
126
126
|
miniNext.addEventListener("click", (e) => {
|
|
127
127
|
e.stopPropagation();
|
|
128
|
-
const runBtn =
|
|
128
|
+
const runBtn = __wgById("wg-run");
|
|
129
129
|
if (runBtn && !runBtn.disabled) {
|
|
130
130
|
runBtn.click();
|
|
131
131
|
return;
|
|
@@ -133,7 +133,7 @@ export function installPanelChrome({ title, favicon, bannerPos, todoPos, envAuto
|
|
|
133
133
|
if (typeof window.__wgNext === "function") window.__wgNext({});
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
|
-
const autoCb =
|
|
136
|
+
const autoCb = __wgById("wg-autoplay-cb");
|
|
137
137
|
if (autoCb && !autoCb.dataset.wgMiniWired) {
|
|
138
138
|
autoCb.dataset.wgMiniWired = "1";
|
|
139
139
|
autoCb.addEventListener("change", () => {
|
|
@@ -20,7 +20,7 @@ export function installStamp({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
20
20
|
el.setAttribute("data-wg-ready", meta.ready === false ? "0" : "1");
|
|
21
21
|
};
|
|
22
22
|
window.__wgOverlayBeacon = () => {
|
|
23
|
-
return Array.from(
|
|
23
|
+
return Array.from(__wgQA("[data-wg-ui=\"1\"]")).map((el) => {
|
|
24
24
|
const r = el.getBoundingClientRect();
|
|
25
25
|
const st = getComputedStyle(el);
|
|
26
26
|
return {
|
|
@@ -41,6 +41,6 @@ export function installStamp({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
41
41
|
};
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
|
-
const existingBanner =
|
|
44
|
+
const existingBanner = __wgById("wg-banner");
|
|
45
45
|
if (existingBanner) window.__wgStampModal(existingBanner, "banner", { ready: true });
|
|
46
46
|
}
|
|
@@ -15,7 +15,7 @@ export function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutopl
|
|
|
15
15
|
o.label ||
|
|
16
16
|
(dir === "left" || dir === "right" ? "swipe" : "swipe") +
|
|
17
17
|
(o.hint ? " \u00b7 " + o.hint : "");
|
|
18
|
-
let layer =
|
|
18
|
+
let layer = __wgById("wg-swipe-layer");
|
|
19
19
|
if (layer && layer._wgTimer) {
|
|
20
20
|
clearTimeout(layer._wgTimer);
|
|
21
21
|
layer._wgTimer = null;
|
|
@@ -31,7 +31,7 @@ export function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutopl
|
|
|
31
31
|
lab.textContent = label;
|
|
32
32
|
layer.appendChild(lab);
|
|
33
33
|
layer.appendChild(finger);
|
|
34
|
-
|
|
34
|
+
__wgAdd(layer);
|
|
35
35
|
const vw = window.innerWidth || 390;
|
|
36
36
|
const vh = window.innerHeight || 844;
|
|
37
37
|
const pad = Math.min(vw, vh) * 0.18;
|
|
@@ -89,7 +89,7 @@ export function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutopl
|
|
|
89
89
|
finger.style.opacity = "0";
|
|
90
90
|
layer.classList.remove("wg-in");
|
|
91
91
|
layer._wgTimer = setTimeout(() => {
|
|
92
|
-
const el =
|
|
92
|
+
const el = __wgById("wg-swipe-layer");
|
|
93
93
|
if (el) el.remove();
|
|
94
94
|
}, 380);
|
|
95
95
|
}
|
|
@@ -108,7 +108,7 @@ export function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutopl
|
|
|
108
108
|
delete el.dataset.wgZoomed;
|
|
109
109
|
});
|
|
110
110
|
// Undo any prior shell-camera experiment - restore device fit scale only.
|
|
111
|
-
const shell =
|
|
111
|
+
const shell = __wgById("wg-device-shell");
|
|
112
112
|
if (shell && shell.dataset.wgCamBase != null) {
|
|
113
113
|
shell.style.transform = shell.dataset.wgCamBase;
|
|
114
114
|
if (shell.dataset.wgCamOrigin) {
|
|
@@ -130,12 +130,12 @@ export function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutopl
|
|
|
130
130
|
window.__wgSetZoomBadge = (scale, sel) => {
|
|
131
131
|
const n = Number(scale);
|
|
132
132
|
const level = Number.isFinite(n) && n > 0 ? n : 1;
|
|
133
|
-
let badge =
|
|
133
|
+
let badge = __wgById("wg-zoom-badge");
|
|
134
134
|
if (!badge) {
|
|
135
135
|
badge = document.createElement("div");
|
|
136
136
|
badge.id = "wg-zoom-badge";
|
|
137
137
|
badge.setAttribute("data-wg-ui", "1");
|
|
138
|
-
|
|
138
|
+
__wgAdd(badge);
|
|
139
139
|
}
|
|
140
140
|
const zoomed = level > 1.001 || level < 0.999;
|
|
141
141
|
badge.dataset.zoomed = zoomed ? "1" : "0";
|
|
@@ -156,12 +156,12 @@ export function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutopl
|
|
|
156
156
|
}
|
|
157
157
|
};
|
|
158
158
|
window.__wgSetTypingBadge = (on, label) => {
|
|
159
|
-
let badge =
|
|
159
|
+
let badge = __wgById("wg-typing-badge");
|
|
160
160
|
if (!on) {
|
|
161
161
|
if (badge) {
|
|
162
162
|
badge.classList.remove("wg-in");
|
|
163
163
|
setTimeout(() => {
|
|
164
|
-
const b =
|
|
164
|
+
const b = __wgById("wg-typing-badge");
|
|
165
165
|
if (b && !b.classList.contains("wg-in")) b.remove();
|
|
166
166
|
}, 220);
|
|
167
167
|
}
|
|
@@ -171,7 +171,7 @@ export function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutopl
|
|
|
171
171
|
badge = document.createElement("div");
|
|
172
172
|
badge.id = "wg-typing-badge";
|
|
173
173
|
badge.setAttribute("data-wg-ui", "1");
|
|
174
|
-
|
|
174
|
+
__wgAdd(badge);
|
|
175
175
|
}
|
|
176
176
|
const text = label ? String(label).slice(0, 40) : "typing";
|
|
177
177
|
badge.innerHTML =
|
|
@@ -196,7 +196,7 @@ export function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutopl
|
|
|
196
196
|
el.style.removeProperty("position");
|
|
197
197
|
delete el.dataset.wgZoomed;
|
|
198
198
|
});
|
|
199
|
-
const shell =
|
|
199
|
+
const shell = __wgById("wg-device-shell");
|
|
200
200
|
if (shell && shell.dataset.wgCamBase != null) {
|
|
201
201
|
shell.style.transform = shell.dataset.wgCamBase;
|
|
202
202
|
shell.style.transformOrigin = shell.dataset.wgCamOrigin || "center center";
|
|
@@ -43,10 +43,10 @@ export function installTodos({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
43
43
|
"_default";
|
|
44
44
|
|
|
45
45
|
// Drop any in-panel leftover (old builds put #wg-todos in .wg-body).
|
|
46
|
-
|
|
46
|
+
__wgQA("#wg-panel #wg-todos").forEach((el) => el.remove());
|
|
47
47
|
|
|
48
48
|
const allDocks = () =>
|
|
49
|
-
[...
|
|
49
|
+
[...__wgQA(".wg-todo-dock, #wg-todo-dock")].filter(
|
|
50
50
|
(el, i, arr) => arr.indexOf(el) === i,
|
|
51
51
|
);
|
|
52
52
|
const relayoutTodoDocks = () => {
|
|
@@ -60,11 +60,11 @@ export function installTodos({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
60
60
|
const findDock = (key) => {
|
|
61
61
|
if (key === "_default") {
|
|
62
62
|
return (
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
__wgQ('.wg-todo-dock[data-wg-todo-key="_default"]') ||
|
|
64
|
+
__wgById("wg-todo-dock")
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
|
-
const all =
|
|
67
|
+
const all = __wgQA(".wg-todo-dock");
|
|
68
68
|
for (let i = 0; i < all.length; i++) {
|
|
69
69
|
if (all[i].getAttribute("data-wg-todo-key") === String(key)) return all[i];
|
|
70
70
|
}
|
|
@@ -141,7 +141,7 @@ export function installTodos({ title, favicon, bannerPos, todoPos, envAutoplay,
|
|
|
141
141
|
/* ignore */
|
|
142
142
|
}
|
|
143
143
|
});
|
|
144
|
-
|
|
144
|
+
__wgAdd(dock);
|
|
145
145
|
} else if (wantPos) {
|
|
146
146
|
dock.dataset.pos = wantPos;
|
|
147
147
|
try {
|
|
@@ -115,24 +115,6 @@ export const RING_CSS =
|
|
|
115
115
|
".wg-todo-dock .wg-todo-dock-title{font:700 11px/1.2 system-ui,sans-serif;color:#c9a6ff;" +
|
|
116
116
|
"letter-spacing:.04em;text-transform:uppercase;margin:0 0 8px;}" +
|
|
117
117
|
".wg-todo-dock .wg-todos-list,#wg-todo-dock #wg-todos,.wg-todo-dock #wg-todos{list-style:none;margin:0;padding:0;background:transparent;border:none;}" +
|
|
118
|
-
// Video device stage: keep recordVideo size fixed; center a device-sized shell.
|
|
119
|
-
"html.wg-video-device-stage{background:#0b1220 !important;}" +
|
|
120
|
-
"html.wg-video-device-stage body{margin:0 !important;min-height:100vh !important;" +
|
|
121
|
-
"display:flex !important;align-items:center !important;justify-content:center !important;" +
|
|
122
|
-
"background:#0b1220 !important;overflow:hidden !important;}" +
|
|
123
|
-
"#wg-device-shell{flex-shrink:0;overflow:auto;background:#fff;" +
|
|
124
|
-
"border-radius:16px;box-shadow:0 24px 80px rgba(0,0,0,.55),0 0 0 1px rgba(255,255,255,.08);" +
|
|
125
|
-
"transform-origin:center center;" +
|
|
126
|
-
/* Force radius clip on all corners (Chrome + transform/scale). */
|
|
127
|
-
"-webkit-mask-image:-webkit-radial-gradient(white,black);" +
|
|
128
|
-
"isolation:isolate;" +
|
|
129
|
-
"transition:border-radius .55s cubic-bezier(.22,1,.36,1),box-shadow .55s ease," +
|
|
130
|
-
"transform .55s cubic-bezier(.22,1,.36,1),width .5s ease,height .5s ease,max-width .5s ease,max-height .5s ease;}" +
|
|
131
|
-
"#wg-device-shell.wg-shell-enter{border-radius:0;box-shadow:none;}" +
|
|
132
|
-
"#wg-device-shell.wg-shell-shutter-out{border-radius:0 !important;" +
|
|
133
|
-
"box-shadow:none !important;outline:none !important;border:none !important;}" +
|
|
134
|
-
"#wg-device-shell.wg-shell-desktop-flat{border-radius:0 !important;box-shadow:none !important;" +
|
|
135
|
-
"outline:none !important;border:none !important;-webkit-mask-image:none;}" +
|
|
136
118
|
// Always-on zoom HUD - bottom-right (top-left is crowded: todos + banners).
|
|
137
119
|
"#wg-zoom-badge{position:fixed;z-index:2147483646;bottom:14px;right:14px;" +
|
|
138
120
|
"display:flex;align-items:center;gap:7px;padding:6px 11px 6px 8px;" +
|
|
@@ -329,3 +311,24 @@ export const WAYGRAPH_FAVICON =
|
|
|
329
311
|
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'>" +
|
|
330
312
|
"<circle cx='16' cy='16' r='14' fill='#7C3AED'/></svg>",
|
|
331
313
|
);
|
|
314
|
+
|
|
315
|
+
// Rules that style the HOST page (device stage) - these must stay in the light DOM, outside the overlay's shadow root.
|
|
316
|
+
export const HOST_CSS =
|
|
317
|
+
// Video device stage: keep recordVideo size fixed; center a device-sized shell.
|
|
318
|
+
"html.wg-video-device-stage{background:#0b1220 !important;}" +
|
|
319
|
+
"html.wg-video-device-stage body{margin:0 !important;min-height:100vh !important;" +
|
|
320
|
+
"display:flex !important;align-items:center !important;justify-content:center !important;" +
|
|
321
|
+
"background:#0b1220 !important;overflow:hidden !important;}" +
|
|
322
|
+
"#wg-device-shell{flex-shrink:0;overflow:auto;background:#fff;" +
|
|
323
|
+
"border-radius:16px;box-shadow:0 24px 80px rgba(0,0,0,.55),0 0 0 1px rgba(255,255,255,.08);" +
|
|
324
|
+
"transform-origin:center center;" +
|
|
325
|
+
/* Force radius clip on all corners (Chrome + transform/scale). */
|
|
326
|
+
"-webkit-mask-image:-webkit-radial-gradient(white,black);" +
|
|
327
|
+
"isolation:isolate;" +
|
|
328
|
+
"transition:border-radius .55s cubic-bezier(.22,1,.36,1),box-shadow .55s ease," +
|
|
329
|
+
"transform .55s cubic-bezier(.22,1,.36,1),width .5s ease,height .5s ease,max-width .5s ease,max-height .5s ease;}" +
|
|
330
|
+
"#wg-device-shell.wg-shell-enter{border-radius:0;box-shadow:none;}" +
|
|
331
|
+
"#wg-device-shell.wg-shell-shutter-out{border-radius:0 !important;" +
|
|
332
|
+
"box-shadow:none !important;outline:none !important;border:none !important;}" +
|
|
333
|
+
"#wg-device-shell.wg-shell-desktop-flat{border-radius:0 !important;box-shadow:none !important;" +
|
|
334
|
+
"outline:none !important;border:none !important;-webkit-mask-image:none;}";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Moved verbatim from the former CHAIN_RUNNER_SCRIPT template string in cli.ts (see src/ARCHITECTURE.md).
|
|
2
2
|
// Runs inside the target project's own waygraph copy - keep it dependency-light and self-contained.
|
|
3
|
-
import { RING_CSS, WAYGRAPH_FAVICON } from "./overlay-css.js";
|
|
3
|
+
import { HOST_CSS, RING_CSS, WAYGRAPH_FAVICON } from "./overlay-css.js";
|
|
4
|
+
import { ensureShadowRoot } from "../ui/shadow.js";
|
|
4
5
|
import { resolveTodoDockUi } from "../highlights.js";
|
|
5
6
|
import { installCore } from "./inpage/core.js";
|
|
6
7
|
import { installSwipeZoom } from "./inpage/swipe-zoom.js";
|
|
@@ -12,7 +13,9 @@ import { installDevice } from "./inpage/device.js";
|
|
|
12
13
|
import { applyVideoDeviceStage } from "./device-stage.js";
|
|
13
14
|
|
|
14
15
|
export async function installOverlay(page, title) {
|
|
15
|
-
await page
|
|
16
|
+
await ensureShadowRoot(page);
|
|
17
|
+
await page.evaluate(([css, key]) => __wgCss(css, key), [RING_CSS, "wg-overlay-css"]).catch(() => {});
|
|
18
|
+
await page.addStyleTag({ content: HOST_CSS }).catch(() => {});
|
|
16
19
|
// Default top-left; override with WAYGRAPH_TITLE_POS=left|center|right.
|
|
17
20
|
// Click cycles left -> center -> right (persisted in localStorage so a
|
|
18
21
|
// navigation that rebuilds the banner keeps the human's last pick).
|
package/dist/runner/rings.js
CHANGED
|
@@ -247,7 +247,7 @@ export async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
247
247
|
return;
|
|
248
248
|
}
|
|
249
249
|
// Fallback for pages that never got installOverlay sync helper.
|
|
250
|
-
const ul =
|
|
250
|
+
const ul = __wgById("wg-todos");
|
|
251
251
|
if (!ul) return;
|
|
252
252
|
ul.innerHTML = todos
|
|
253
253
|
.map((t) => {
|
package/dist/runner/slides.js
CHANGED
|
@@ -66,7 +66,7 @@ export async function presentSlides(page, slides, _gate, opts) {
|
|
|
66
66
|
}
|
|
67
67
|
await page.evaluate(
|
|
68
68
|
(info) => {
|
|
69
|
-
let panel =
|
|
69
|
+
let panel = __wgById("wg-panel");
|
|
70
70
|
const isNew = !panel;
|
|
71
71
|
if (!panel) {
|
|
72
72
|
panel = document.createElement("div");
|
|
@@ -139,7 +139,7 @@ export async function presentSlides(page, slides, _gate, opts) {
|
|
|
139
139
|
">Auto-advancing...</div>" +
|
|
140
140
|
"</div>";
|
|
141
141
|
if (isNew) {
|
|
142
|
-
|
|
142
|
+
__wgAdd(panel);
|
|
143
143
|
requestAnimationFrame(() => panel.classList.add("wg-in"));
|
|
144
144
|
}
|
|
145
145
|
if (window.__wgWirePanelChrome) {
|
|
@@ -172,7 +172,7 @@ export async function presentSlides(page, slides, _gate, opts) {
|
|
|
172
172
|
} catch {
|
|
173
173
|
/* ignore */
|
|
174
174
|
}
|
|
175
|
-
const cb =
|
|
175
|
+
const cb = __wgById("wg-autoplay-cb");
|
|
176
176
|
if (cb) {
|
|
177
177
|
cb.checked = autoNow;
|
|
178
178
|
cb.addEventListener("change", () => {
|
|
@@ -181,17 +181,17 @@ export async function presentSlides(page, slides, _gate, opts) {
|
|
|
181
181
|
} catch {
|
|
182
182
|
/* ignore */
|
|
183
183
|
}
|
|
184
|
-
const manual =
|
|
185
|
-
const auto =
|
|
184
|
+
const manual = __wgById("wg-gate-manual");
|
|
185
|
+
const auto = __wgById("wg-gate-auto");
|
|
186
186
|
if (manual) manual.style.display = cb.checked ? "none" : "";
|
|
187
187
|
if (auto) auto.style.display = cb.checked ? "" : "none";
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
|
-
const manual =
|
|
191
|
-
const auto =
|
|
190
|
+
const manual = __wgById("wg-gate-manual");
|
|
191
|
+
const auto = __wgById("wg-gate-auto");
|
|
192
192
|
if (manual) manual.style.display = autoNow ? "none" : "";
|
|
193
193
|
if (auto) auto.style.display = autoNow ? "" : "none";
|
|
194
|
-
const runBtn =
|
|
194
|
+
const runBtn = __wgById("wg-run");
|
|
195
195
|
if (runBtn) {
|
|
196
196
|
runBtn.removeAttribute("data-wg-acked");
|
|
197
197
|
runBtn.addEventListener("click", (e) => {
|
|
@@ -205,8 +205,8 @@ export async function presentSlides(page, slides, _gate, opts) {
|
|
|
205
205
|
const unlockAt = Date.now() + info.dwellMs;
|
|
206
206
|
const tick = () => {
|
|
207
207
|
const left = Math.max(0, unlockAt - Date.now());
|
|
208
|
-
const hint =
|
|
209
|
-
const btn =
|
|
208
|
+
const hint = __wgById("wg-dwell-hint");
|
|
209
|
+
const btn = __wgById("wg-run");
|
|
210
210
|
if (left <= 0) {
|
|
211
211
|
if (hint) hint.remove();
|
|
212
212
|
if (btn) btn.disabled = false;
|
|
@@ -248,7 +248,7 @@ export async function presentSlides(page, slides, _gate, opts) {
|
|
|
248
248
|
for (;;) {
|
|
249
249
|
const acked = await page
|
|
250
250
|
.evaluate(() => {
|
|
251
|
-
const btn =
|
|
251
|
+
const btn = __wgById("wg-run");
|
|
252
252
|
return !!(btn && btn.getAttribute("data-wg-acked") === "1");
|
|
253
253
|
})
|
|
254
254
|
.catch(() => false);
|
|
@@ -33,7 +33,7 @@ export async function renderBeforeStep(page, info) {
|
|
|
33
33
|
// flash - it only ever fades in ONCE, the first time this page
|
|
34
34
|
// genuinely has no panel yet (a real navigation wiped the whole
|
|
35
35
|
// document, or this is the very first step).
|
|
36
|
-
let panel =
|
|
36
|
+
let panel = __wgById("wg-panel");
|
|
37
37
|
const isNewPanel = !panel;
|
|
38
38
|
if (!panel) {
|
|
39
39
|
panel = document.createElement("div");
|
|
@@ -126,7 +126,7 @@ export async function renderBeforeStep(page, info) {
|
|
|
126
126
|
">Auto-advancing...</div>";
|
|
127
127
|
panel.innerHTML = html;
|
|
128
128
|
if (isNewPanel) {
|
|
129
|
-
|
|
129
|
+
__wgAdd(panel);
|
|
130
130
|
requestAnimationFrame(() => panel.classList.add("wg-in"));
|
|
131
131
|
} else {
|
|
132
132
|
panel.classList.add("wg-in");
|
|
@@ -204,7 +204,7 @@ export async function renderBeforeStep(page, info) {
|
|
|
204
204
|
panel.querySelectorAll("textarea[data-key]").forEach((ta) => {
|
|
205
205
|
ta.addEventListener("input", applyKeyPretty);
|
|
206
206
|
});
|
|
207
|
-
const runBtn =
|
|
207
|
+
const runBtn = __wgById("wg-run");
|
|
208
208
|
if (runBtn) {
|
|
209
209
|
runBtn.addEventListener("click", () => {
|
|
210
210
|
const edits = {};
|
|
@@ -220,7 +220,7 @@ export async function renderBeforeStep(page, info) {
|
|
|
220
220
|
// still always wins over an active autoplay wait, whichever the
|
|
221
221
|
// checkbox says - this is the "hybrid" control: autoplay is a
|
|
222
222
|
// default, not a lockout.
|
|
223
|
-
const cb =
|
|
223
|
+
const cb = __wgById("wg-autoplay-cb");
|
|
224
224
|
if (cb) {
|
|
225
225
|
cb.addEventListener("change", () => {
|
|
226
226
|
try {
|
|
@@ -228,8 +228,8 @@ export async function renderBeforeStep(page, info) {
|
|
|
228
228
|
} catch {
|
|
229
229
|
/* private mode / blocked storage - toggle still works this page */
|
|
230
230
|
}
|
|
231
|
-
const manual =
|
|
232
|
-
const auto =
|
|
231
|
+
const manual = __wgById("wg-gate-manual");
|
|
232
|
+
const auto = __wgById("wg-gate-auto");
|
|
233
233
|
if (manual) manual.style.display = cb.checked ? "none" : "";
|
|
234
234
|
if (auto) auto.style.display = cb.checked ? "" : "none";
|
|
235
235
|
});
|
|
@@ -256,8 +256,8 @@ export async function renderBeforeStep(page, info) {
|
|
|
256
256
|
return {
|
|
257
257
|
ok: !!(panel && textOk && opacityOk),
|
|
258
258
|
beacons,
|
|
259
|
-
hasRun: !!
|
|
260
|
-
readyAttr:
|
|
259
|
+
hasRun: !!__wgById("wg-run"),
|
|
260
|
+
readyAttr: __wgById("wg-panel")?.getAttribute("data-wg-ready") || null,
|
|
261
261
|
};
|
|
262
262
|
})
|
|
263
263
|
.catch((e) => ({ ok: false, reason: String(e && e.message ? e.message : e) }));
|
|
@@ -412,7 +412,7 @@ export async function renderAfterStep(page, info) {
|
|
|
412
412
|
await page
|
|
413
413
|
.evaluate((info) => {
|
|
414
414
|
// Reused in place - see renderBeforeStep's own comment on this.
|
|
415
|
-
let panel =
|
|
415
|
+
let panel = __wgById("wg-panel");
|
|
416
416
|
const isNewPanel = !panel;
|
|
417
417
|
if (!panel) {
|
|
418
418
|
panel = document.createElement("div");
|
|
@@ -497,7 +497,7 @@ export async function renderAfterStep(page, info) {
|
|
|
497
497
|
resultHtml +
|
|
498
498
|
gateHtml;
|
|
499
499
|
if (isNewPanel) {
|
|
500
|
-
|
|
500
|
+
__wgAdd(panel);
|
|
501
501
|
requestAnimationFrame(() => panel.classList.add("wg-in"));
|
|
502
502
|
} else {
|
|
503
503
|
panel.classList.add("wg-in");
|
|
@@ -546,9 +546,9 @@ export async function renderAfterStep(page, info) {
|
|
|
546
546
|
panel.querySelector(".wg-result-json").style.display = wantPretty ? "none" : "block";
|
|
547
547
|
});
|
|
548
548
|
});
|
|
549
|
-
const runBtn =
|
|
549
|
+
const runBtn = __wgById("wg-run");
|
|
550
550
|
if (runBtn) runBtn.addEventListener("click", () => window.__wgNext({}));
|
|
551
|
-
const cb =
|
|
551
|
+
const cb = __wgById("wg-autoplay-cb");
|
|
552
552
|
if (cb) {
|
|
553
553
|
cb.addEventListener("change", () => {
|
|
554
554
|
try {
|
|
@@ -556,8 +556,8 @@ export async function renderAfterStep(page, info) {
|
|
|
556
556
|
} catch {
|
|
557
557
|
/* private mode / blocked storage - toggle still works this page */
|
|
558
558
|
}
|
|
559
|
-
const manual =
|
|
560
|
-
const auto =
|
|
559
|
+
const manual = __wgById("wg-gate-manual");
|
|
560
|
+
const auto = __wgById("wg-gate-auto");
|
|
561
561
|
if (manual) manual.style.display = cb.checked ? "none" : "";
|
|
562
562
|
if (auto) auto.style.display = cb.checked ? "" : "none";
|
|
563
563
|
});
|
|
@@ -579,7 +579,7 @@ async function renderStepError(page, info) {
|
|
|
579
579
|
await page
|
|
580
580
|
.evaluate((info) => {
|
|
581
581
|
// Reused in place - see renderBeforeStep's own comment on this.
|
|
582
|
-
let panel =
|
|
582
|
+
let panel = __wgById("wg-panel");
|
|
583
583
|
const isNewPanel = !panel;
|
|
584
584
|
if (!panel) {
|
|
585
585
|
panel = document.createElement("div");
|
|
@@ -631,7 +631,7 @@ async function renderStepError(page, info) {
|
|
|
631
631
|
"<button id=\"wg-run\" class=\"wg-error-stop\">Stop</button>" +
|
|
632
632
|
"</div>";
|
|
633
633
|
if (isNewPanel) {
|
|
634
|
-
|
|
634
|
+
__wgAdd(panel);
|
|
635
635
|
requestAnimationFrame(() => panel.classList.add("wg-in"));
|
|
636
636
|
} else {
|
|
637
637
|
panel.classList.add("wg-in");
|
|
@@ -651,9 +651,9 @@ async function renderStepError(page, info) {
|
|
|
651
651
|
ready: true,
|
|
652
652
|
});
|
|
653
653
|
}
|
|
654
|
-
const runBtn =
|
|
654
|
+
const runBtn = __wgById("wg-run");
|
|
655
655
|
if (runBtn) runBtn.addEventListener("click", () => window.__wgNext({}));
|
|
656
|
-
const retryBtn =
|
|
656
|
+
const retryBtn = __wgById("wg-error-retry");
|
|
657
657
|
if (retryBtn) retryBtn.addEventListener("click", () => window.__wgNext({ __wgRetry: "1" }));
|
|
658
658
|
}, errorPayload)
|
|
659
659
|
.catch(() => {});
|
package/dist/runner/teardown.js
CHANGED
|
@@ -38,17 +38,17 @@ import { formatHighlightCaption } from "../highlights.js";
|
|
|
38
38
|
export async function markStepRunning(page, _opts) {
|
|
39
39
|
await page
|
|
40
40
|
.evaluate(() => {
|
|
41
|
-
const runBtn =
|
|
41
|
+
const runBtn = __wgById("wg-run");
|
|
42
42
|
if (runBtn) {
|
|
43
43
|
runBtn.disabled = true;
|
|
44
44
|
runBtn.textContent = "Running \u25B6";
|
|
45
45
|
}
|
|
46
|
-
const autoEl =
|
|
46
|
+
const autoEl = __wgById("wg-gate-auto");
|
|
47
47
|
if (autoEl) autoEl.textContent = "Running...";
|
|
48
|
-
|
|
48
|
+
__wgQA("#wg-panel textarea[data-key]").forEach((ta) => {
|
|
49
49
|
ta.disabled = true;
|
|
50
50
|
});
|
|
51
|
-
const panel =
|
|
51
|
+
const panel = __wgById("wg-panel");
|
|
52
52
|
if (panel) {
|
|
53
53
|
panel.classList.add("wg-collapsed");
|
|
54
54
|
panel.setAttribute("data-wg-collapsed", "1");
|
|
@@ -83,13 +83,13 @@ export async function markStepRunning(page, _opts) {
|
|
|
83
83
|
export async function restoreTheaterAfterNavigation(page, todoDockRef, deviceRef, stubBeforeRef) {
|
|
84
84
|
const needs =
|
|
85
85
|
(await page
|
|
86
|
-
.evaluate(() => !window.__wgSyncTodos || !
|
|
86
|
+
.evaluate(() => !window.__wgSyncTodos || !__wgById("wg-ring"))
|
|
87
87
|
.catch(() => true)) || false;
|
|
88
88
|
if (!needs) {
|
|
89
89
|
// Overlay survived (SPA / no full document wipe) - still refresh dock
|
|
90
90
|
// if Node has a carry and the DOM lost .wg-todo-dock.
|
|
91
91
|
const hasDock = await page
|
|
92
|
-
.evaluate(() => !!
|
|
92
|
+
.evaluate(() => !!__wgQ(".wg-todo-dock, #wg-todo-dock"))
|
|
93
93
|
.catch(() => false);
|
|
94
94
|
if (hasDock || !todoDockRef || !todoDockRef.current) return;
|
|
95
95
|
}
|
|
@@ -162,10 +162,10 @@ export async function teardownOverlay(page) {
|
|
|
162
162
|
"wg-device-toast",
|
|
163
163
|
"wg-device-badge",
|
|
164
164
|
]) {
|
|
165
|
-
const el =
|
|
165
|
+
const el = __wgById(id);
|
|
166
166
|
if (el) el.remove();
|
|
167
167
|
}
|
|
168
|
-
|
|
168
|
+
__wgQA(".wg-todo-dock").forEach((el) => el.remove());
|
|
169
169
|
document.documentElement.classList.remove("wg-video-device-stage");
|
|
170
170
|
})
|
|
171
171
|
.catch(() => {});
|
package/dist/runner/todo-dock.js
CHANGED
|
@@ -115,7 +115,7 @@ export async function syncTodoDockAdvance(page, todoDockRef, stubIndex, stub) {
|
|
|
115
115
|
export async function probeTodoDocksOnPage(page) {
|
|
116
116
|
return page
|
|
117
117
|
.evaluate(() => {
|
|
118
|
-
const docks = [...
|
|
118
|
+
const docks = [...__wgQA(".wg-todo-dock, #wg-todo-dock")];
|
|
119
119
|
return docks.map((el) => {
|
|
120
120
|
const r = el.getBoundingClientRect();
|
|
121
121
|
const items = el.querySelectorAll("li");
|
package/dist/step-overlay.js
CHANGED
|
@@ -1,38 +1,40 @@
|
|
|
1
|
+
import { ensureShadowRoot } from "./ui/shadow.js";
|
|
1
2
|
import { composeStepOverlay } from "./ui/compose.js";
|
|
2
3
|
const RING_CSS = composeStepOverlay();
|
|
3
4
|
const FAVICON = "data:image/svg+xml," +
|
|
4
5
|
encodeURIComponent("<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><circle cx='16' cy='16' r='14' fill='#7C3AED'/></svg>");
|
|
5
6
|
export async function installDemoChrome(page, title = "waygraph auto", options = {}) {
|
|
6
7
|
const showBanner = options.banner !== false;
|
|
7
|
-
await page
|
|
8
|
+
await ensureShadowRoot(page);
|
|
9
|
+
await page.evaluate(([css, key]) => __wgCss(css, key), [RING_CSS, "wg-overlay-css"]).catch(() => { });
|
|
8
10
|
await page
|
|
9
11
|
.evaluate(({ title, favicon, showBanner }) => {
|
|
10
|
-
if (!
|
|
12
|
+
if (!__wgById("wg-ring")) {
|
|
11
13
|
const ring = document.createElement("div");
|
|
12
14
|
ring.id = "wg-ring";
|
|
13
|
-
|
|
15
|
+
__wgAdd(ring);
|
|
14
16
|
}
|
|
15
|
-
if (!
|
|
17
|
+
if (!__wgById("wg-ring-label")) {
|
|
16
18
|
const ringLabel = document.createElement("div");
|
|
17
19
|
ringLabel.id = "wg-ring-label";
|
|
18
|
-
|
|
20
|
+
__wgAdd(ringLabel);
|
|
19
21
|
}
|
|
20
|
-
if (!
|
|
22
|
+
if (!__wgById("wg-cursor")) {
|
|
21
23
|
const cursor = document.createElement("div");
|
|
22
24
|
cursor.id = "wg-cursor";
|
|
23
25
|
cursor.innerHTML =
|
|
24
26
|
"<svg viewBox='0 0 32 32' width='24' height='24'>" +
|
|
25
27
|
"<path fill='#0C0C1A' stroke='#fff' stroke-width='1.4' stroke-linejoin='round' " +
|
|
26
28
|
"d='M6 3.5l1.4 22.5 5.8-5.4 4.2 9.4 3.6-1.6-4.2-9.2H26z'/></svg>";
|
|
27
|
-
|
|
29
|
+
__wgAdd(cursor);
|
|
28
30
|
}
|
|
29
|
-
if (!
|
|
31
|
+
if (!__wgById("wg-click-pulse")) {
|
|
30
32
|
const pulse = document.createElement("div");
|
|
31
33
|
pulse.id = "wg-click-pulse";
|
|
32
|
-
|
|
34
|
+
__wgAdd(pulse);
|
|
33
35
|
}
|
|
34
36
|
window.__wgMoveCursorTo = (x, y, ms) => {
|
|
35
|
-
const cursor =
|
|
37
|
+
const cursor = __wgById("wg-cursor");
|
|
36
38
|
if (!cursor)
|
|
37
39
|
return;
|
|
38
40
|
cursor.style.setProperty("--wg-cursor-ms", (ms || 600) + "ms");
|
|
@@ -40,7 +42,7 @@ export async function installDemoChrome(page, title = "waygraph auto", options =
|
|
|
40
42
|
cursor.style.opacity = "1";
|
|
41
43
|
};
|
|
42
44
|
window.__wgClickPulse = (x, y, tone) => {
|
|
43
|
-
const pulse =
|
|
45
|
+
const pulse = __wgById("wg-click-pulse");
|
|
44
46
|
if (!pulse)
|
|
45
47
|
return;
|
|
46
48
|
const raw = (tone || "planned") + "";
|
|
@@ -59,8 +61,8 @@ export async function installDemoChrome(page, title = "waygraph auto", options =
|
|
|
59
61
|
pulse.classList.add("wg-pulse");
|
|
60
62
|
};
|
|
61
63
|
window.__wgPositionRing = (box, label, tone, style) => {
|
|
62
|
-
const ring =
|
|
63
|
-
const ringLabel =
|
|
64
|
+
const ring = __wgById("wg-ring");
|
|
65
|
+
const ringLabel = __wgById("wg-ring-label");
|
|
64
66
|
if (!ring || !ringLabel)
|
|
65
67
|
return;
|
|
66
68
|
const raw = (tone || "planned") + "";
|
|
@@ -92,25 +94,25 @@ export async function installDemoChrome(page, title = "waygraph auto", options =
|
|
|
92
94
|
ringLabel.style.opacity = "1";
|
|
93
95
|
};
|
|
94
96
|
window.__wgHideRing = () => {
|
|
95
|
-
const ring =
|
|
96
|
-
const ringLabel =
|
|
97
|
+
const ring = __wgById("wg-ring");
|
|
98
|
+
const ringLabel = __wgById("wg-ring-label");
|
|
97
99
|
if (ring)
|
|
98
100
|
ring.style.opacity = "0";
|
|
99
101
|
if (ringLabel)
|
|
100
102
|
ringLabel.style.opacity = "0";
|
|
101
103
|
};
|
|
102
|
-
if (showBanner && title && !
|
|
104
|
+
if (showBanner && title && !__wgById("wg-banner")) {
|
|
103
105
|
const banner = document.createElement("div");
|
|
104
106
|
banner.id = "wg-banner";
|
|
105
107
|
banner.innerHTML =
|
|
106
108
|
"<span class='wg-banner-tag'>waygraph auto</span><span>" + title + "</span>";
|
|
107
|
-
|
|
109
|
+
__wgAdd(banner);
|
|
108
110
|
banner.setAttribute("data-wg-ui", "1");
|
|
109
111
|
banner.setAttribute("data-wg-modal", "banner");
|
|
110
112
|
banner.setAttribute("data-wg-ready", "1");
|
|
111
113
|
}
|
|
112
114
|
else {
|
|
113
|
-
const existing =
|
|
115
|
+
const existing = __wgById("wg-banner");
|
|
114
116
|
if (existing) {
|
|
115
117
|
existing.setAttribute("data-wg-ui", "1");
|
|
116
118
|
existing.setAttribute("data-wg-modal", "banner");
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow-DOM mount for every waygraph overlay element. Overlay UI lives in one open shadow root under
|
|
3
|
+
* `<div id="wg-root">`, so the host page's CSS can't restyle it and ours can't leak out.
|
|
4
|
+
*
|
|
5
|
+
* `installShadowRoot` runs INSIDE the page (page.evaluate / addInitScript), so it must stay
|
|
6
|
+
* self-contained. It defines the `__wg*` helpers the overlay code uses in place of `document.*`:
|
|
7
|
+
* lookups check the shadow root first and fall back to the light DOM (the device stage shell and
|
|
8
|
+
* host-page markers such as `[data-wg-zoomed]` intentionally stay in the light DOM).
|
|
9
|
+
*/
|
|
10
|
+
import type { BrowserContext, Page } from "@playwright/test";
|
|
11
|
+
export declare function installShadowRoot(): void;
|
|
12
|
+
/** Idempotent; call before any overlay install on a page. */
|
|
13
|
+
export declare function ensureShadowRoot(page: Page): Promise<void>;
|
|
14
|
+
/** Also define the helpers on every future document (pilot overlay persists across navigations). */
|
|
15
|
+
export declare function installShadowRootEverywhere(context: BrowserContext): Promise<void>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function installShadowRoot() {
|
|
2
|
+
const w = window;
|
|
3
|
+
if (w.__wgById)
|
|
4
|
+
return;
|
|
5
|
+
const root = () => {
|
|
6
|
+
let host = document.getElementById("wg-root");
|
|
7
|
+
if (!host || !host.shadowRoot) {
|
|
8
|
+
host?.remove();
|
|
9
|
+
host = document.createElement("div");
|
|
10
|
+
host.id = "wg-root";
|
|
11
|
+
// No box of its own: children are position:fixed, so the host must never affect page layout.
|
|
12
|
+
host.style.cssText = "display:contents";
|
|
13
|
+
host.attachShadow({ mode: "open" });
|
|
14
|
+
document.documentElement.appendChild(host);
|
|
15
|
+
}
|
|
16
|
+
return host.shadowRoot;
|
|
17
|
+
};
|
|
18
|
+
w.__wgRoot = root;
|
|
19
|
+
w.__wgById = (id) => root().getElementById(id) || document.getElementById(id);
|
|
20
|
+
w.__wgQ = (sel) => root().querySelector(sel) || document.querySelector(sel);
|
|
21
|
+
w.__wgQA = (sel) => Array.from(root().querySelectorAll(sel)).concat(Array.from(document.querySelectorAll(sel)));
|
|
22
|
+
w.__wgAdd = (node) => root().appendChild(node);
|
|
23
|
+
w.__wgCss = (css, key) => {
|
|
24
|
+
const r = root();
|
|
25
|
+
let style = Array.from(r.children).find((c) => c.tagName === "STYLE" && c.getAttribute("data-key") === key);
|
|
26
|
+
if (!style) {
|
|
27
|
+
style = document.createElement("style");
|
|
28
|
+
style.setAttribute("data-key", key);
|
|
29
|
+
r.insertBefore(style, r.firstChild);
|
|
30
|
+
}
|
|
31
|
+
if (style.textContent !== css)
|
|
32
|
+
style.textContent = css;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Idempotent; call before any overlay install on a page. */
|
|
36
|
+
export async function ensureShadowRoot(page) {
|
|
37
|
+
await page.evaluate(installShadowRoot).catch(() => { });
|
|
38
|
+
}
|
|
39
|
+
/** Also define the helpers on every future document (pilot overlay persists across navigations). */
|
|
40
|
+
export async function installShadowRootEverywhere(context) {
|
|
41
|
+
await context.addInitScript(installShadowRoot);
|
|
42
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|