waygraph 0.15.29 → 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.d.ts +81 -0
- package/dist/pilot-overlay/fixtures.js +334 -0
- package/dist/pilot-overlay/panel.d.ts +34 -0
- package/dist/pilot-overlay/panel.js +155 -0
- package/dist/pilot-overlay/shell.d.ts +48 -0
- package/dist/pilot-overlay/shell.js +276 -0
- package/dist/pilot-overlay.d.ts +3 -158
- package/dist/pilot-overlay.js +4 -778
- 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/bb0e0e46.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e230f24e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/fc917c18.log +0 -0
- package/package.json +1 -1
|
@@ -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
|
|
File without changes
|
|
File without changes
|