waygraph 0.15.30 → 0.15.32

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 (47) hide show
  1. package/dist/auto-explore-run.js +9 -6
  2. package/dist/auto-session/session.js +2 -1
  3. package/dist/pilot-overlay/action-ring.d.ts +16 -0
  4. package/dist/pilot-overlay/action-ring.js +67 -0
  5. package/dist/pilot-overlay/fixtures.js +17 -17
  6. package/dist/pilot-overlay/panel.js +3 -3
  7. package/dist/pilot-overlay/shell.js +27 -27
  8. package/dist/pilot-overlay.d.ts +1 -0
  9. package/dist/pilot-overlay.js +1 -0
  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/components/ring.js +5 -0
  27. package/dist/ui/shadow.d.ts +15 -0
  28. package/dist/ui/shadow.js +42 -0
  29. package/examples/saucedemo/.waygraph-auto/1bd7b7bf.log +0 -0
  30. package/examples/saucedemo/.waygraph-auto/2a577961.log +0 -0
  31. package/examples/saucedemo/.waygraph-auto/2c79c73a.log +0 -0
  32. package/examples/saucedemo/.waygraph-auto/2c94a05b.log +0 -0
  33. package/examples/saucedemo/.waygraph-auto/37b56184.log +0 -0
  34. package/examples/saucedemo/.waygraph-auto/401177a3.log +0 -0
  35. package/examples/saucedemo/.waygraph-auto/476cff76.log +0 -0
  36. package/examples/saucedemo/.waygraph-auto/535e8922.json +8 -0
  37. package/examples/saucedemo/.waygraph-auto/535e8922.log +0 -0
  38. package/examples/saucedemo/.waygraph-auto/68fa0682.log +0 -0
  39. package/examples/saucedemo/.waygraph-auto/73a41843.log +0 -0
  40. package/examples/saucedemo/.waygraph-auto/8598b4c0.log +0 -0
  41. package/examples/saucedemo/.waygraph-auto/982138cf.log +0 -0
  42. package/examples/saucedemo/.waygraph-auto/a8645294.log +0 -0
  43. package/examples/saucedemo/.waygraph-auto/bf8693e7.log +0 -0
  44. package/examples/saucedemo/.waygraph-auto/f86224c6.log +0 -0
  45. package/examples/saucedemo/.waygraph-auto/fc917c18.log +0 -0
  46. package/examples/saucedemo/.waygraph-auto/fe83305d.log +0 -0
  47. package/package.json +1 -1
@@ -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 = document.getElementById("wg-auto-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
- document.querySelectorAll("textarea.wg-mem-input").forEach((ta) => {
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 = document.getElementById("wg-auto-panel");
389
+ let panel = __wgById("wg-auto-panel");
387
390
  if (!panel) {
388
391
  panel = document.createElement("div");
389
- document.documentElement.appendChild(panel);
392
+ __wgAdd(panel);
390
393
  }
391
394
  panel.outerHTML = inner;
392
- const root = document.getElementById("wg-auto-panel");
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(document.querySelectorAll('[data-wg-ui="1"]')).map((el) => {
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 {
@@ -1,6 +1,6 @@
1
1
  // Split out of the former 1,078-line auto-session.ts (see src/ARCHITECTURE.md). Behavior unchanged.
2
2
  import { CONSOLE_LOG_MAX_ENTRIES, FULL_MODE_DEFAULT_DEPTH, FULL_MODE_MAX_NODES, FULL_MODE_MAX_TEXT_LENGTH, TRACE_MAX_STEPS, buildSessionSnapshot, parsePick, stubFilePath, stubPhaseHasContent, walkFullDom } from "./helpers.js";
3
- import { installPersistentPilotOverlay, showPilotActivity, showPilotFixtures, showPilotVision, updatePilotOverlay } from "../pilot-overlay.js";
3
+ import { instrumentPilotActions, installPersistentPilotOverlay, showPilotActivity, showPilotFixtures, showPilotVision, updatePilotOverlay } from "../pilot-overlay.js";
4
4
  import { buildExploreContext, buildExploreMenu } from "../auto-explore.js";
5
5
  import { MemPage } from "../mem-page.js";
6
6
  import { Engine } from "../engine.js";
@@ -92,6 +92,7 @@ export class AutoSession {
92
92
  const context = await browser.newContext(contextOpts);
93
93
  await installPersistentPilotOverlay(context);
94
94
  const page = await context.newPage();
95
+ instrumentPilotActions(page, headless ? 0 : 450);
95
96
  if (startUrl) {
96
97
  await page.goto(startUrl, { waitUntil: "domcontentloaded" }).catch(() => { });
97
98
  await page.waitForLoadState("load").catch(() => { });
@@ -0,0 +1,16 @@
1
+ import type { Page } from "@playwright/test";
2
+ type Box = {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ };
8
+ /** Paint the gray "auto" ring (Playwright is about to act here) at a viewport rectangle. */
9
+ export declare function showPilotRect(page: Page, box: Box, label: string, tone?: string): Promise<void>;
10
+ /**
11
+ * Every Playwright instruction a Block runs against this session's page gets a gray (auto) ring + robot
12
+ * label first, so a human watching a pilot window sees WHAT is being driven. `dwellMs` holds the ring
13
+ * long enough to read before the action fires (0 for headless).
14
+ */
15
+ export declare function instrumentPilotActions(page: Page, dwellMs: number): void;
16
+ export {};
@@ -0,0 +1,67 @@
1
+ import { ensureInstalled } from "./shell.js";
2
+ /** Paint the gray "auto" ring (Playwright is about to act here) at a viewport rectangle. */
3
+ export async function showPilotRect(page, box, label, tone = "auto") {
4
+ await ensureInstalled(page);
5
+ await page
6
+ .evaluate(({ box, label, tone }) => {
7
+ const ring = __wgById("wg-ring");
8
+ const tag = __wgById("wg-ring-label");
9
+ if (!ring || !tag)
10
+ return;
11
+ const w = window;
12
+ if (w.__wgVisionHideTimer)
13
+ clearTimeout(w.__wgVisionHideTimer);
14
+ ring.dataset.tone = tone;
15
+ ring.dataset.size = "md";
16
+ tag.dataset.tone = tone;
17
+ tag.dataset.size = "md";
18
+ tag.dataset.weight = "normal";
19
+ ring.style.left = `${box.x - 3}px`;
20
+ ring.style.top = `${box.y - 3}px`;
21
+ ring.style.width = `${box.width + 6}px`;
22
+ ring.style.height = `${box.height + 6}px`;
23
+ tag.textContent = label;
24
+ tag.style.left = `${Math.max(0, box.x)}px`;
25
+ tag.style.top = `${Math.max(0, box.y - 24)}px`;
26
+ ring.style.opacity = "1";
27
+ tag.style.opacity = "1";
28
+ w.__wgVisionHideTimer = setTimeout(() => {
29
+ ring.style.opacity = "0";
30
+ tag.style.opacity = "0";
31
+ }, 4_000);
32
+ }, { box, label, tone })
33
+ .catch(() => { });
34
+ }
35
+ const LOCATOR_ACTIONS = ["click", "dblclick", "hover", "tap", "fill", "check", "uncheck", "selectOption", "press", "pressSequentially", "setInputFiles"];
36
+ const patched = new WeakSet();
37
+ /**
38
+ * Every Playwright instruction a Block runs against this session's page gets a gray (auto) ring + robot
39
+ * label first, so a human watching a pilot window sees WHAT is being driven. `dwellMs` holds the ring
40
+ * long enough to read before the action fires (0 for headless).
41
+ */
42
+ export function instrumentPilotActions(page, dwellMs) {
43
+ const proto = Object.getPrototypeOf(page.locator("html"));
44
+ if (patched.has(proto))
45
+ return;
46
+ patched.add(proto);
47
+ for (const action of LOCATOR_ACTIONS) {
48
+ const original = proto[action];
49
+ if (typeof original !== "function")
50
+ continue;
51
+ proto[action] = async function (...args) {
52
+ try {
53
+ const box = await this.first().boundingBox({ timeout: 1_500 });
54
+ if (box) {
55
+ const desc = String(this).replace(/^locator\(['"]?|['"]?\)$/g, "").slice(0, 60);
56
+ await showPilotRect(this.page(), box, `${action} ${desc}`);
57
+ if (dwellMs > 0)
58
+ await this.page().waitForTimeout(dwellMs);
59
+ }
60
+ }
61
+ catch {
62
+ /* decoration only - never block the real action */
63
+ }
64
+ return original.apply(this, args);
65
+ };
66
+ }
67
+ }
@@ -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 = document.getElementById("wg-pilot-fx-focus");
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 = document.getElementById("wg-pilot-fx-focus");
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 = document.getElementById("wg-pilot-fx-focus");
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
- document.documentElement.appendChild(veil);
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 = document.getElementById("wg-pilot-fx-zoom");
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
- document.documentElement.appendChild(badge);
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
- document.getElementById("wg-pilot-fx-zoom")?.remove();
146
+ __wgById("wg-pilot-fx-zoom")?.remove();
147
147
  };
148
148
  const setDeviceChip = (label) => {
149
- document.getElementById("wg-pilot-fx-device")?.remove();
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
- document.documentElement.appendChild(chip);
156
+ __wgAdd(chip);
157
157
  };
158
158
  w.__wgPilotFxZoomOutOnHide = zoomOut;
159
159
  const clearFx = () => {
160
- document.querySelectorAll(".wg-pilot-fx-ring, .wg-pilot-fx-label").forEach((el) => el.remove());
161
- document.getElementById("wg-pilot-fx-todos")?.remove();
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
- document.getElementById("wg-pilot-fx-device")?.remove();
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
- document.documentElement.appendChild(ringEl);
210
- document.documentElement.appendChild(labelEl);
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
- document.documentElement.appendChild(dock);
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 = document.getElementById("wg-pilot-fx-todos");
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 = document.querySelector(".wg-pilot-fx-ring");
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 = document.getElementById("wg-pilot-badge");
14
- const current = document.getElementById("wg-pilot-panel-current");
15
- const graphPanel = document.getElementById("wg-pilot-panel-graph");
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 (document.getElementById("wg-pilot-overlay"))
113
+ if (__wgById("wg-pilot-overlay"))
113
114
  return;
114
- const style = document.createElement("style");
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
- document.documentElement.appendChild(root);
129
- document.getElementById("wg-pilot-badge").addEventListener("click", () => {
130
- document.getElementById("wg-pilot-panel").classList.toggle("wg-pilot-open");
126
+ __wgAdd(root);
127
+ __wgById("wg-pilot-badge").addEventListener("click", () => {
128
+ __wgById("wg-pilot-panel").classList.toggle("wg-pilot-open");
131
129
  });
132
- document.getElementById("wg-pilot-view-current").addEventListener("click", () => {
133
- document.getElementById("wg-pilot-view-current").classList.add("wg-pilot-tab-active");
134
- document.getElementById("wg-pilot-view-graph").classList.remove("wg-pilot-tab-active");
135
- document.getElementById("wg-pilot-panel-current").style.display = "";
136
- document.getElementById("wg-pilot-panel-graph").style.display = "none";
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
- document.getElementById("wg-pilot-view-graph").addEventListener("click", () => {
139
- document.getElementById("wg-pilot-view-graph").classList.add("wg-pilot-tab-active");
140
- document.getElementById("wg-pilot-view-current").classList.remove("wg-pilot-tab-active");
141
- document.getElementById("wg-pilot-panel-graph").style.display = "";
142
- document.getElementById("wg-pilot-panel-current").style.display = "none";
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 (!document.getElementById("wg-pilot-activity")) {
142
+ if (!__wgById("wg-pilot-activity")) {
145
143
  const toast = document.createElement("div");
146
144
  toast.id = "wg-pilot-activity";
147
- document.documentElement.appendChild(toast);
145
+ __wgAdd(toast);
148
146
  }
149
- if (!document.getElementById("wg-ring")) {
147
+ if (!__wgById("wg-ring")) {
150
148
  const ring = document.createElement("div");
151
149
  ring.id = "wg-ring";
152
- document.documentElement.appendChild(ring);
150
+ __wgAdd(ring);
153
151
  }
154
- if (!document.getElementById("wg-ring-label")) {
152
+ if (!__wgById("wg-ring-label")) {
155
153
  const ringLabel = document.createElement("div");
156
154
  ringLabel.id = "wg-ring-label";
157
- document.documentElement.appendChild(ringLabel);
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 = document.getElementById("wg-pilot-activity");
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 = document.getElementById("wg-ring");
231
- const tag = document.getElementById("wg-ring-label");
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;
@@ -1,3 +1,4 @@
1
1
  export * from "./pilot-overlay/shell.js";
2
2
  export * from "./pilot-overlay/fixtures.js";
3
3
  export * from "./pilot-overlay/panel.js";
4
+ export * from "./pilot-overlay/action-ring.js";
@@ -2,3 +2,4 @@
2
2
  export * from "./pilot-overlay/shell.js";
3
3
  export * from "./pilot-overlay/fixtures.js";
4
4
  export * from "./pilot-overlay/panel.js";
5
+ export * from "./pilot-overlay/action-ring.js";
@@ -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 = document.getElementById("wg-swipe-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
- document.documentElement.appendChild(layer);
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 = document.getElementById("wg-swipe-layer");
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 = document.getElementById("wg-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
- document.documentElement.appendChild(banner);
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 (!document.getElementById("wg-ring")) {
22
+ if (!__wgById("wg-ring")) {
23
23
  const ring = document.createElement("div");
24
24
  ring.id = "wg-ring";
25
- document.documentElement.appendChild(ring);
25
+ __wgAdd(ring);
26
26
  }
27
- if (!document.getElementById("wg-ring-label")) {
27
+ if (!__wgById("wg-ring-label")) {
28
28
  const ringLabel = document.createElement("div");
29
29
  ringLabel.id = "wg-ring-label";
30
- document.documentElement.appendChild(ringLabel);
30
+ __wgAdd(ringLabel);
31
31
  }
32
- if (!document.getElementById("wg-cursor")) {
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
- document.documentElement.appendChild(cursor);
42
+ __wgAdd(cursor);
43
43
  }
44
- if (!document.getElementById("wg-click-pulse")) {
44
+ if (!__wgById("wg-click-pulse")) {
45
45
  const pulse = document.createElement("div");
46
46
  pulse.id = "wg-click-pulse";
47
- document.documentElement.appendChild(pulse);
47
+ __wgAdd(pulse);
48
48
  }
49
49
  window.__wgMoveCursorTo = (x, y, ms, instant) => {
50
- const cursor = document.getElementById("wg-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 = document.getElementById("wg-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 = document.getElementById("wg-ring");
70
- const ringLabel = document.getElementById("wg-ring-label");
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
- document.querySelectorAll(".wg-todo-dock, #wg-todo-dock").forEach((el) => {
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
- document.querySelectorAll(".wg-todo-dock, #wg-todo-dock").forEach((el) => {
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 = [...document.querySelectorAll(".wg-todo-dock, #wg-todo-dock")].filter(
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 = document.getElementById("wg-click-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 = document.getElementById("wg-ring");
218
- const ringLabel = document.getElementById("wg-ring-label");
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
- document.querySelectorAll(".wg-todo-dock[data-tucked], #wg-todo-dock[data-tucked]").forEach((el) => {
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 = document.getElementById("wg-focus-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 = document.getElementById("wg-focus-veil");
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 = document.getElementById("wg-focus-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
- document.documentElement.appendChild(veil);
257
+ __wgAdd(veil);
258
258
  }
259
259
  const pad = 10;
260
260
  veil.style.left = Math.max(0, box.x - pad) + "px";