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.
Files changed (37) hide show
  1. package/dist/auto-explore-run.js +9 -6
  2. package/dist/pilot-overlay/fixtures.d.ts +81 -0
  3. package/dist/pilot-overlay/fixtures.js +334 -0
  4. package/dist/pilot-overlay/panel.d.ts +34 -0
  5. package/dist/pilot-overlay/panel.js +155 -0
  6. package/dist/pilot-overlay/shell.d.ts +48 -0
  7. package/dist/pilot-overlay/shell.js +276 -0
  8. package/dist/pilot-overlay.d.ts +3 -158
  9. package/dist/pilot-overlay.js +4 -778
  10. package/dist/runner/cursor.js +3 -3
  11. package/dist/runner/inpage/banner.js +2 -2
  12. package/dist/runner/inpage/core.js +23 -23
  13. package/dist/runner/inpage/device.js +11 -11
  14. package/dist/runner/inpage/panel-chrome.js +3 -3
  15. package/dist/runner/inpage/stamp.js +2 -2
  16. package/dist/runner/inpage/swipe-zoom.js +10 -10
  17. package/dist/runner/inpage/todos.js +6 -6
  18. package/dist/runner/overlay-css.js +21 -18
  19. package/dist/runner/overlay-install.js +5 -2
  20. package/dist/runner/rings.js +1 -1
  21. package/dist/runner/slides.js +11 -11
  22. package/dist/runner/step-panels.js +18 -18
  23. package/dist/runner/teardown.js +8 -8
  24. package/dist/runner/todo-dock.js +1 -1
  25. package/dist/step-overlay.js +20 -18
  26. package/dist/ui/shadow.d.ts +15 -0
  27. package/dist/ui/shadow.js +42 -0
  28. package/examples/saucedemo/.waygraph-auto/1bd7b7bf.log +0 -0
  29. package/examples/saucedemo/.waygraph-auto/2a577961.log +0 -0
  30. package/examples/saucedemo/.waygraph-auto/401177a3.log +0 -0
  31. package/examples/saucedemo/.waygraph-auto/68fa0682.log +0 -0
  32. package/examples/saucedemo/.waygraph-auto/982138cf.log +0 -0
  33. package/examples/saucedemo/.waygraph-auto/a8645294.log +0 -0
  34. package/examples/saucedemo/.waygraph-auto/bb0e0e46.log +0 -0
  35. package/examples/saucedemo/.waygraph-auto/e230f24e.log +0 -0
  36. package/examples/saucedemo/.waygraph-auto/fc917c18.log +0 -0
  37. 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 = document.getElementById("wg-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
- document.documentElement.appendChild(panel);
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 = document.getElementById("wg-run");
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 = document.getElementById("wg-autoplay-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 = document.getElementById("wg-gate-manual");
232
- const auto = document.getElementById("wg-gate-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: !!document.getElementById("wg-run"),
260
- readyAttr: document.getElementById("wg-panel")?.getAttribute("data-wg-ready") || null,
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 = document.getElementById("wg-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
- document.documentElement.appendChild(panel);
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 = document.getElementById("wg-run");
549
+ const runBtn = __wgById("wg-run");
550
550
  if (runBtn) runBtn.addEventListener("click", () => window.__wgNext({}));
551
- const cb = document.getElementById("wg-autoplay-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 = document.getElementById("wg-gate-manual");
560
- const auto = document.getElementById("wg-gate-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 = document.getElementById("wg-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
- document.documentElement.appendChild(panel);
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 = document.getElementById("wg-run");
654
+ const runBtn = __wgById("wg-run");
655
655
  if (runBtn) runBtn.addEventListener("click", () => window.__wgNext({}));
656
- const retryBtn = document.getElementById("wg-error-retry");
656
+ const retryBtn = __wgById("wg-error-retry");
657
657
  if (retryBtn) retryBtn.addEventListener("click", () => window.__wgNext({ __wgRetry: "1" }));
658
658
  }, errorPayload)
659
659
  .catch(() => {});
@@ -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 = document.getElementById("wg-run");
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 = document.getElementById("wg-gate-auto");
46
+ const autoEl = __wgById("wg-gate-auto");
47
47
  if (autoEl) autoEl.textContent = "Running...";
48
- document.querySelectorAll("#wg-panel textarea[data-key]").forEach((ta) => {
48
+ __wgQA("#wg-panel textarea[data-key]").forEach((ta) => {
49
49
  ta.disabled = true;
50
50
  });
51
- const panel = document.getElementById("wg-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 || !document.getElementById("wg-ring"))
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(() => !!document.querySelector(".wg-todo-dock, #wg-todo-dock"))
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 = document.getElementById(id);
165
+ const el = __wgById(id);
166
166
  if (el) el.remove();
167
167
  }
168
- document.querySelectorAll(".wg-todo-dock").forEach((el) => el.remove());
168
+ __wgQA(".wg-todo-dock").forEach((el) => el.remove());
169
169
  document.documentElement.classList.remove("wg-video-device-stage");
170
170
  })
171
171
  .catch(() => {});
@@ -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 = [...document.querySelectorAll(".wg-todo-dock, #wg-todo-dock")];
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");
@@ -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.addStyleTag({ content: RING_CSS }).catch(() => { });
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 (!document.getElementById("wg-ring")) {
12
+ if (!__wgById("wg-ring")) {
11
13
  const ring = document.createElement("div");
12
14
  ring.id = "wg-ring";
13
- document.documentElement.appendChild(ring);
15
+ __wgAdd(ring);
14
16
  }
15
- if (!document.getElementById("wg-ring-label")) {
17
+ if (!__wgById("wg-ring-label")) {
16
18
  const ringLabel = document.createElement("div");
17
19
  ringLabel.id = "wg-ring-label";
18
- document.documentElement.appendChild(ringLabel);
20
+ __wgAdd(ringLabel);
19
21
  }
20
- if (!document.getElementById("wg-cursor")) {
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
- document.documentElement.appendChild(cursor);
29
+ __wgAdd(cursor);
28
30
  }
29
- if (!document.getElementById("wg-click-pulse")) {
31
+ if (!__wgById("wg-click-pulse")) {
30
32
  const pulse = document.createElement("div");
31
33
  pulse.id = "wg-click-pulse";
32
- document.documentElement.appendChild(pulse);
34
+ __wgAdd(pulse);
33
35
  }
34
36
  window.__wgMoveCursorTo = (x, y, ms) => {
35
- const cursor = document.getElementById("wg-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 = document.getElementById("wg-click-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 = document.getElementById("wg-ring");
63
- const ringLabel = document.getElementById("wg-ring-label");
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 = document.getElementById("wg-ring");
96
- const ringLabel = document.getElementById("wg-ring-label");
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 && !document.getElementById("wg-banner")) {
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
- document.documentElement.appendChild(banner);
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 = document.getElementById("wg-banner");
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygraph",
3
- "version": "0.15.29",
3
+ "version": "0.15.31",
4
4
  "type": "module",
5
5
  "description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
6
6
  "license": "MIT",