waygraph 0.16.0 → 0.16.1

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 (107) hide show
  1. package/dist/cli/commands/browser.d.ts +2 -0
  2. package/dist/cli/commands/browser.js +109 -0
  3. package/dist/cli/commands/pilot.d.ts +2 -0
  4. package/dist/cli/commands/pilot.js +102 -0
  5. package/dist/cli/commands/session-ops.d.ts +3 -0
  6. package/dist/cli/commands/session-ops.js +257 -0
  7. package/dist/cli/commands/session.js +20 -461
  8. package/dist/cli/usage.d.ts +1 -1
  9. package/dist/demo/narration/demo-pace.d.ts +1 -1
  10. package/dist/demo/narration/device.d.ts +2 -2
  11. package/dist/demo/narration/merge.d.ts +4 -2
  12. package/dist/demo/narration/merge.js +2 -6
  13. package/dist/demo/narration/narration.d.ts +8 -8
  14. package/dist/demo/narration/todo-dock.d.ts +5 -5
  15. package/dist/demo/overlay/cursor.d.ts +9 -2
  16. package/dist/demo/overlay/cursor.js +1 -1
  17. package/dist/demo/overlay/device-stage.d.ts +15 -2
  18. package/dist/demo/overlay/inpage/banner.d.ts +2 -1
  19. package/dist/demo/overlay/inpage/banner.js +3 -3
  20. package/dist/demo/overlay/inpage/caption.d.ts +1 -1
  21. package/dist/demo/overlay/inpage/core.d.ts +2 -1
  22. package/dist/demo/overlay/inpage/core.js +1 -1
  23. package/dist/demo/overlay/inpage/device-chip.d.ts +1 -1
  24. package/dist/demo/overlay/inpage/device-chip.js +6 -6
  25. package/dist/demo/overlay/inpage/network.d.ts +3 -2
  26. package/dist/demo/overlay/inpage/network.js +1 -12
  27. package/dist/demo/overlay/inpage/panel-chrome.d.ts +1 -1
  28. package/dist/demo/overlay/inpage/panel-chrome.js +1 -1
  29. package/dist/demo/overlay/inpage/stamp.d.ts +1 -1
  30. package/dist/demo/overlay/inpage/stamp.js +1 -1
  31. package/dist/demo/overlay/inpage/swipe-zoom.d.ts +1 -1
  32. package/dist/demo/overlay/inpage/swipe-zoom.js +1 -1
  33. package/dist/demo/overlay/inpage/todos.d.ts +2 -1
  34. package/dist/demo/overlay/inpage/todos.js +1 -2
  35. package/dist/demo/overlay/network-capture.d.ts +2 -2
  36. package/dist/demo/overlay/network-capture.js +1 -1
  37. package/dist/demo/overlay/overlay-stage.d.ts +19 -11
  38. package/dist/demo/overlay/overlay.d.ts +2 -2
  39. package/dist/demo/overlay/rings.d.ts +38 -10
  40. package/dist/demo/overlay/rings.js +21 -47
  41. package/dist/demo/stepper/after.js +9 -19
  42. package/dist/demo/stepper/banner-log.d.ts +4 -6
  43. package/dist/demo/stepper/banner-log.js +3 -1
  44. package/dist/demo/stepper/before.js +3 -3
  45. package/dist/demo/stepper/demo-log.d.ts +26 -9
  46. package/dist/demo/stepper/demo-log.js +9 -26
  47. package/dist/demo/stepper/interaction-patch.d.ts +3 -2
  48. package/dist/demo/stepper/interaction-patch.js +19 -19
  49. package/dist/demo/stepper/panel-after.d.ts +17 -0
  50. package/dist/demo/stepper/panel-after.js +166 -0
  51. package/dist/demo/stepper/panel-before.d.ts +12 -0
  52. package/dist/demo/stepper/panel-before.js +241 -0
  53. package/dist/demo/stepper/slides.d.ts +13 -1
  54. package/dist/demo/stepper/slides.js +3 -3
  55. package/dist/demo/stepper/step-panels.d.ts +57 -9
  56. package/dist/demo/stepper/step-panels.js +10 -427
  57. package/dist/demo/stepper/stepper.js +1 -1
  58. package/dist/demo/stepper/teardown.d.ts +4 -5
  59. package/dist/demo/stepper/teardown.js +4 -5
  60. package/dist/demo/stepper/todo-advance.d.ts +3 -2
  61. package/dist/demo/stepper/todo-advance.js +2 -2
  62. package/dist/demo/stepper/view.d.ts +4 -14
  63. package/dist/demo/types.d.ts +69 -1
  64. package/dist/engine/block/action-page.d.ts +53 -0
  65. package/dist/engine/block/action-page.js +1 -0
  66. package/dist/engine/block/block.d.ts +24 -79
  67. package/dist/engine/block/block.js +2 -77
  68. package/dist/engine/block/branch.d.ts +17 -0
  69. package/dist/engine/block/branch.js +40 -0
  70. package/dist/engine/block/dialog.d.ts +13 -0
  71. package/dist/engine/block/dialog.js +37 -0
  72. package/dist/engine/flow/map-branch.d.ts +9 -0
  73. package/dist/engine/flow/map-branch.js +57 -0
  74. package/dist/engine/flow/map-builder.d.ts +7 -22
  75. package/dist/engine/flow/map-builder.js +6 -145
  76. package/dist/engine/flow/map-steps.d.ts +30 -0
  77. package/dist/engine/flow/map-steps.js +87 -0
  78. package/dist/engine/trait/trait.js +1 -1
  79. package/dist/index.d.ts +13 -9
  80. package/dist/index.js +7 -5
  81. package/dist/live/auto/auto.d.ts +3 -15
  82. package/dist/live/auto/auto.js +10 -420
  83. package/dist/live/auto/explore.d.ts +2 -2
  84. package/dist/live/auto/mem.d.ts +11 -0
  85. package/dist/live/auto/mem.js +85 -0
  86. package/dist/live/auto/menu.d.ts +30 -0
  87. package/dist/live/auto/menu.js +335 -0
  88. package/dist/live/auto-session/auto-session.d.ts +23 -103
  89. package/dist/live/auto-session/auto-session.js +100 -370
  90. package/dist/live/auto-session/console.d.ts +4 -0
  91. package/dist/live/auto-session/console.js +41 -0
  92. package/dist/live/auto-session/files.d.ts +44 -0
  93. package/dist/live/auto-session/files.js +145 -0
  94. package/dist/live/auto-session/helpers.d.ts +5 -2
  95. package/dist/live/auto-session/helpers.js +17 -0
  96. package/dist/live/auto-session/inspect.d.ts +4 -0
  97. package/dist/live/auto-session/inspect.js +75 -0
  98. package/dist/live/auto-session/ipc.d.ts +3 -22
  99. package/dist/live/auto-session/ipc.js +7 -147
  100. package/dist/live/auto-session/open-browser.d.ts +6 -0
  101. package/dist/live/auto-session/open-browser.js +47 -0
  102. package/dist/live/pilot/pilot.d.ts +1 -1
  103. package/dist/live/pilot/pilot.js +2 -1
  104. package/dist/live/traverse/traverse.js +6 -200
  105. package/dist/live/traverse/worker.d.ts +37 -0
  106. package/dist/live/traverse/worker.js +200 -0
  107. package/package.json +5 -1
@@ -1,13 +1,26 @@
1
+ import type { DeviceState } from "../../engine/stub/viewport.js";
1
2
  import type { WgPage } from "../types.js";
3
+ interface Size {
4
+ width: number;
5
+ height: number;
6
+ }
7
+ /** How a device-shell change animates: clear it, shutter out to desktop, enter in, or stay flat. */
8
+ interface DeviceStageOptions {
9
+ clear?: boolean;
10
+ shutterOut?: boolean;
11
+ enterIn?: boolean;
12
+ desktopFlat?: boolean;
13
+ }
2
14
  /**
3
15
  * Video recording locks Playwright viewport to recordVideo.size. Shrinking
4
16
  * setViewportSize to mobile/tablet letterboxes the .webm top-left. Instead keep
5
17
  * the stage size and center a device-sized #wg-device-shell inside the frame.
6
18
  */
7
- export declare function applyVideoDeviceStage(page: WgPage, target: any, stage: any, opts: any): Promise<void>;
19
+ export declare function applyVideoDeviceStage(page: WgPage, target: Size, stage: Size, opts?: DeviceStageOptions): Promise<void>;
8
20
  /**
9
21
  * Apply authored device fixture to the live page (0.13+).
10
22
  * Seamless viewport lerp + centered window + toast on set/clear.
11
23
  * With --video: keep recordVideo stage size and center a device shell in-frame.
12
24
  */
13
- export declare function applyDeviceToPage(page: WgPage, device: any, sync: any): Promise<void>;
25
+ export declare function applyDeviceToPage(page: WgPage, device: DeviceState | null | undefined, sync?: string): Promise<void>;
26
+ export {};
@@ -1 +1,2 @@
1
- export declare function installBanner({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi, bannerUi, tag: bannerTag }: any): void;
1
+ import type { OverlayInstallArgs } from "../../types.js";
2
+ export declare function installBanner({ title, favicon, bannerPos, bannerUi, tag: bannerTag }: OverlayInstallArgs): void;
@@ -1,4 +1,4 @@
1
- export function installBanner({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi, bannerUi, tag: bannerTag }) {
1
+ export function installBanner({ title, favicon, bannerPos, bannerUi, tag: bannerTag }) {
2
2
  const POSITIONS = ["left", "center", "right"];
3
3
  const applyPos = (el, pos) => {
4
4
  el.dataset.pos = pos;
@@ -22,7 +22,7 @@ export function installBanner({ title, favicon, bannerPos, todoPos, envAutoplay,
22
22
  catch {
23
23
  /* ignore */
24
24
  }
25
- const startPos = saved && POSITIONS.includes(saved) ? saved : bannerPos;
25
+ const startPos = saved && POSITIONS.includes(saved) ? saved : bannerPos || "left";
26
26
  applyPos(banner, startPos);
27
27
  banner.title = "Click to move: top left / center / right";
28
28
  banner.addEventListener("click", (e) => {
@@ -155,6 +155,6 @@ export function installBanner({ title, favicon, bannerPos, todoPos, envAutoplay,
155
155
  iconLink.rel = "icon";
156
156
  document.head.appendChild(iconLink);
157
157
  }
158
- if (iconLink.href !== favicon)
158
+ if (favicon && iconLink.href !== favicon)
159
159
  iconLink.href = favicon;
160
160
  }
@@ -1,2 +1,2 @@
1
- export declare function showCaption(text: any): void;
1
+ export declare function showCaption(text: string): void;
2
2
  export declare function hideCaption(): void;
@@ -1 +1,2 @@
1
- export declare function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }: any): void;
1
+ import type { OverlayInstallArgs } from "../../types.js";
2
+ export declare function installCore({ envAutoplay, todoDockUi }: OverlayInstallArgs): void;
@@ -1,4 +1,4 @@
1
- export function installCore({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }) {
1
+ export function installCore({ envAutoplay, todoDockUi }) {
2
2
  window.__wgTodoDockUi = todoDockUi || {
3
3
  compact: true,
4
4
  cap: 5,
@@ -1 +1 @@
1
- export declare function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }: any): void;
1
+ export declare function installDevice(): void;
@@ -1,5 +1,5 @@
1
1
  // In-page (runs inside the browser via page.evaluate, must stay self-contained): the device toast and chip.
2
- export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }) {
2
+ export function installDevice() {
3
3
  // Device toast + chip (0.13.2+) - announce on set/clear; quiet chip on keep.
4
4
  // Payload: { sync, device?, remain?, announce? }
5
5
  window.__wgSyncDevice = (payload) => {
@@ -114,7 +114,7 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
114
114
  (device && device.preset === "desktop" && !device.touchMode);
115
115
  if (isDesktopClear) {
116
116
  setCursor(false);
117
- const announce = payload.announce !== false && sync !== "keep";
117
+ const announce = payload?.announce !== false && sync !== "keep";
118
118
  if (announce) {
119
119
  const vp = device && device.viewport ? device.viewport : { width: 1280, height: 720 };
120
120
  showToast("desktop", false, "Back to desktop", vp.width + "×" + vp.height + " · mouse", "landscape");
@@ -144,8 +144,8 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
144
144
  return;
145
145
  const preset = device.preset || "desktop";
146
146
  const touch = !!device.touchMode;
147
- const remain = payload.remain !== false && sync !== "clear";
148
- const vp = device.viewport || {};
147
+ const remain = payload?.remain !== false;
148
+ const vp = device.viewport || { width: 0, height: 0 };
149
149
  const orient = device.orientation === "landscape" || device.orientation === "portrait"
150
150
  ? device.orientation
151
151
  : vp.height > vp.width
@@ -163,8 +163,8 @@ export function installDevice({ title, favicon, bannerPos, todoPos, envAutoplay,
163
163
  document.documentElement.dataset.wgTouch = touch ? "1" : "0";
164
164
  if (window.__wgEnsureZoomBadge)
165
165
  window.__wgEnsureZoomBadge();
166
- const announce = payload.announce === true ||
167
- (payload.announce !== false && (sync === "set" || sync === "clear") && changed);
166
+ const announce = payload?.announce === true ||
167
+ (payload?.announce !== false && sync === "set" && changed);
168
168
  const size = (vp.width || "?") +
169
169
  "×" +
170
170
  (vp.height || "?") +
@@ -1,9 +1,10 @@
1
- export declare function addNetworkEntry(entry: any): void;
1
+ import type { NetworkEntry } from "../../types.js";
2
+ export declare function addNetworkEntry(entry: NetworkEntry): void;
2
3
  /**
3
4
  * Shows or hides the network-tab panel - capture keeps happening either way (addNetworkEntry
4
5
  * always builds the DOM row; this only toggles whether the panel itself is visible). Safe to call
5
6
  * before any entry exists yet - it just remembers the flag for addNetworkEntry to check later.
6
7
  */
7
- export declare function setNetworkTabVisible(visible: any): void;
8
+ export declare function setNetworkTabVisible(visible: boolean): void;
8
9
  /** Clears every captured entry and removes the panel - a fresh navigation/session starting over. */
9
10
  export declare function clearNetworkLog(): void;
@@ -1,14 +1,3 @@
1
- // In-page installer (page.evaluate target) - must stay self-contained, no module-scope closure
2
- // (page.evaluate serializes ONLY the passed function's own source, not sibling top-level
3
- // functions in this file - every helper below is nested inside addNetworkEntry itself for exactly
4
- // that reason; a row's click handler still works after addNetworkEntry returns because it's a
5
- // closure over ITS OWN invocation's nested helpers, not a reference to module scope).
6
- //
7
- // A real network-tab overlay (`#wg-network-log`): the DevTools Network panel itself can't be
8
- // embedded (it lives in browser chrome, not reachable from automation), but every entry's real
9
- // data is - this renders that, on-page: method, url, status, and (click a row) request/response
10
- // headers + bodies + a "copy as cURL" command, a Postman-style inspector for both real page
11
- // traffic (installNetworkCapture, network-capture.js) and defineApiBlock's own page.request calls.
12
1
  export function addNetworkEntry(entry) {
13
2
  // No-op outside a demo/pilot/auto run - ensureShadowRoot() (and its __wgById/__wgAdd globals)
14
3
  // is never installed for a plain `waygraph run`/Playwright test, and this must not throw there.
@@ -113,7 +102,7 @@ export function addNetworkEntry(entry) {
113
102
  row.dataset.ok = entry.ok ? "1" : "0";
114
103
  let path = entry.url;
115
104
  try {
116
- const u = new URL(entry.url);
105
+ const u = new URL(String(entry.url));
117
106
  path = u.pathname + u.search;
118
107
  }
119
108
  catch {
@@ -1 +1 @@
1
- export declare function installPanelChrome({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }: any): void;
1
+ export declare function installPanelChrome(): void;
@@ -1,5 +1,5 @@
1
1
  // In-page (runs inside the browser via page.evaluate, must stay self-contained): the step panel's Hide/Show chrome and mini Next.
2
- export function installPanelChrome({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }) {
2
+ export function installPanelChrome() {
3
3
  // Hide/Show for #wg-panel - call after every panel content refresh.
4
4
  // Collapsed = "N / M · block" + Next (when not auto). Never pass
5
5
  // forceCollapsed:false - omit so Hide/localStorage wins.
@@ -1 +1 @@
1
- export declare function installStamp({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }: any): void;
1
+ export declare function installStamp(): void;
@@ -1,5 +1,5 @@
1
1
  // In-page (runs inside the browser via page.evaluate, must stay self-contained): readiness stamps agents read to know a panel painted.
2
- export function installStamp({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }) {
2
+ export function installStamp() {
3
3
  // Blind-agent overlay beacons: every modal root gets data-wg-ui /
4
4
  // data-wg-modal / data-wg-ready so tests can fail on a blank panel.
5
5
  window.__wgStampModal = (el, kind, meta) => {
@@ -1 +1 @@
1
- export declare function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }: any): void;
1
+ export declare function installSwipeZoom(): void;
@@ -1,5 +1,5 @@
1
1
  // In-page (runs inside the browser via page.evaluate, must stay self-contained): swipe trails and ring zoom.
2
- export function installSwipeZoom({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }) {
2
+ export function installSwipeZoom() {
3
3
  /**
4
4
  * Finger swipe trail for touch theater (orientation rotate / device change).
5
5
  * opts: { dir: 'left'|'right'|'up'|'down', label?, ms? }
@@ -1 +1,2 @@
1
- export declare function installTodos({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }: any): void;
1
+ import type { OverlayInstallArgs } from "../../types.js";
2
+ export declare function installTodos({ todoPos }: OverlayInstallArgs): void;
@@ -1,5 +1,4 @@
1
- // In-page (runs inside the browser via page.evaluate, must stay self-contained): the todo dock(s).
2
- export function installTodos({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }) {
1
+ export function installTodos({ todoPos }) {
3
2
  // Floating todo dock - lives outside #wg-panel so --mini / Hide never
4
3
  // hide the checklist. Click cycles left <-> right with CSS transform.
5
4
  // Authors set side via ctx.todos(id, items, { pos }); env / --todo-* fallback.
@@ -1,4 +1,4 @@
1
- import type { WgPage } from "../types.js";
1
+ import type { NetworkEntry, WgPage } from "../types.js";
2
2
  /**
3
3
  * Records one entry AND paints it live - the single place that touches page.__wgNetworkHistory,
4
4
  * so every caller (this file's own listeners, defineApiBlock) stays in sync automatically. History
@@ -7,7 +7,7 @@ import type { WgPage } from "../types.js";
7
7
  * this, the "network tab" reset to empty on every step that navigated - not what a persistent
8
8
  * Network tab is supposed to do.
9
9
  */
10
- export declare function recordNetworkEntry(page: WgPage, entry: any): Promise<void | undefined>;
10
+ export declare function recordNetworkEntry(page: WgPage, entry: NetworkEntry): Promise<void | undefined>;
11
11
  /**
12
12
  * Replays whatever history survived the last navigation back into a freshly (re)installed panel -
13
13
  * call this after ensureShadowRoot, every step, same as the rest of the overlay's own per-step
@@ -119,7 +119,7 @@ export function installNetworkCapture(page) {
119
119
  status: 0,
120
120
  ok: false,
121
121
  reqBody: request.postData() || undefined,
122
- resBody: request.failure() ? request.failure().errorText : undefined,
122
+ resBody: request.failure()?.errorText,
123
123
  });
124
124
  });
125
125
  }
@@ -1,22 +1,30 @@
1
- import type { WgPage } from "../types.js";
2
- export declare function createOverlayStage(page: WgPage, { todoDockRef, deviceRef }: any): {
1
+ import type { BannerUiState, StepperRefs, WgPage } from "../types.js";
2
+ import type { StubPhaseResult } from "../../engine/stub/stub.js";
3
+ import type { TodoDockState, TodoDockUiOpts } from "../../engine/stub/todos.js";
4
+ import type { DeviceState } from "../../engine/stub/viewport.js";
5
+ export declare function createOverlayStage(page: WgPage, { todoDockRef, deviceRef }: Pick<StepperRefs, never> & {
6
+ todoDockRef: StepperRefs["todoDock"];
7
+ deviceRef: StepperRefs["device"];
8
+ }): {
3
9
  state: {
4
- lastTodoDock: any;
5
- lastDevice: any;
6
- lastBannerUi: Record<string, any>;
10
+ lastTodoDock: TodoDockState | undefined;
11
+ lastDevice: DeviceState | undefined;
12
+ lastBannerUi: BannerUiState;
7
13
  };
8
- pushTodoDockUi: (patch: any) => Promise<void>;
9
- rememberTodoDock: (dock: any, sync: any, parallel: any) => void;
10
- syncAllTodoDocks: (opts: any) => Promise<void>;
11
- applyPhase: (phase: any, { reapplyDeviceOnKeep }?: {
14
+ pushTodoDockUi: (patch: TodoDockUiOpts | undefined) => Promise<void>;
15
+ rememberTodoDock: (dock: TodoDockState | undefined, sync: "set" | "clear" | "keep", parallel: boolean) => void;
16
+ syncAllTodoDocks: (opts?: {
17
+ parallel?: boolean;
18
+ }) => Promise<void>;
19
+ applyPhase: (phase: StubPhaseResult, { reapplyDeviceOnKeep }?: {
12
20
  reapplyDeviceOnKeep?: boolean | undefined;
13
21
  }) => Promise<{
14
22
  appliedTodo: {
15
- dock: import("../../index.js").TodoDockState | undefined;
23
+ dock: TodoDockState | undefined;
16
24
  sync: "set" | "clear" | "keep";
17
25
  };
18
26
  appliedDevice: {
19
- device: import("../../index.js").DeviceState | undefined;
27
+ device: DeviceState | undefined;
20
28
  sync: "set" | "clear" | "keep";
21
29
  };
22
30
  }>;
@@ -1,4 +1,4 @@
1
- import type { WgPage } from "../types.js";
1
+ import type { BannerUiState, WgPage } from "../types.js";
2
2
  /**
3
3
  * `ui` = authored banner UX from the stub phase: { pos, collision, hidden } (all optional).
4
4
  * `caption` = bottom subtitle-bar text (ctx.caption()/ctx.hideCaption()) - `undefined` leaves it
@@ -7,4 +7,4 @@ import type { WgPage } from "../types.js";
7
7
  * `undefined` leaves it alone (default state is hidden, set the very first time restoreNetworkHistory
8
8
  * builds a panel); capture itself is unconditional and unaffected by this either way.
9
9
  */
10
- export declare function installOverlay(page: WgPage, title?: any, ui?: any, caption?: any, networkTabVisible?: any): Promise<void>;
10
+ export declare function installOverlay(page: WgPage, title?: string, ui?: BannerUiState | null, caption?: string, networkTabVisible?: boolean): Promise<void>;
@@ -1,5 +1,9 @@
1
1
  import type { WgBox, WgPage } from "../types.js";
2
2
  import type { Locator } from "@playwright/test";
3
+ import type { ResolvedHighlight } from "../../engine/stub/ring.js";
4
+ import type { DemoPace } from "../../engine/stub/pace.js";
5
+ import type { TodoDockState, WaygraphTodoItem } from "../../engine/stub/todos.js";
6
+ import type { Pacing } from "../stepper/pacing.js";
3
7
  /**
4
8
  * Scroll a locator into view for demo theater.
5
9
  * Prefer CSS smooth scroll + scrollend wait. Do NOT call Playwright's
@@ -8,20 +12,44 @@ import type { Locator } from "@playwright/test";
8
12
  * noticeable under --fast when dwell is shorter.
9
13
  * Pass opts.instant=true only for skipTheater / blitz.
10
14
  */
11
- export declare function ensureLocatorInView(locator: Locator, opts: any): Promise<void>;
12
- export declare function ensureSelectorInView(page: WgPage, selector: any, opts?: any): Promise<void>;
13
- export declare function applyHighlightZoom(page: WgPage, selector: any, zoom: any, zoomOut: any): Promise<void>;
14
- export declare function cycleHighlightRings(page: WgPage, highlights: any, gatesFast: any, opts: any): Promise<void>;
15
- export declare function showRing(page: WgPage, box: WgBox, label: any, tone: any, style: any): Promise<void>;
16
- /** 0-based stub index for Method fill/click todo advance; -1 if none. */
17
- export declare function matchStubIndexForLocator(page: WgPage, locator: Locator, stubs: any): Promise<number>;
18
- export declare function captionForLocator(page: WgPage, locator: Locator, stubs: any, fallback: any): Promise<{
19
- label: any;
15
+ export declare function ensureLocatorInView(locator: Locator, opts?: {
16
+ instant?: boolean;
17
+ }): Promise<void>;
18
+ export declare function ensureSelectorInView(page: WgPage, selector: string, opts?: {
19
+ instant?: boolean;
20
+ }): Promise<void>;
21
+ export declare function applyHighlightZoom(page: WgPage, selector: string | undefined, zoom: number | undefined, zoomOut: boolean | undefined): Promise<void>;
22
+ /** How `cycleHighlightRings` plays a step's rings: hold times, pace, and the todo dock it moves along. */
23
+ export interface CycleRingsOptions {
24
+ /** Hold for each ring when it sets none (the failure path holds longer). */
25
+ defaultHoldMs?: number;
26
+ pace?: DemoPace | undefined;
27
+ todoDock?: TodoDockState | undefined;
28
+ todos?: readonly WaygraphTodoItem[] | undefined;
29
+ todoSync?: "set" | "clear" | "keep" | undefined;
30
+ todoDockRef?: {
31
+ current: TodoDockState | undefined;
32
+ } | undefined;
33
+ /** False when the stub already set the dock's progress. */
34
+ advanceTodos?: boolean;
35
+ stepLabel?: string;
36
+ }
37
+ export declare function cycleHighlightRings(page: WgPage, highlights: readonly ResolvedHighlight[] | undefined, gatesFast: boolean, opts?: CycleRingsOptions): Promise<void>;
38
+ export declare function showRing(page: WgPage, box: WgBox, label: string, tone: string | undefined, style?: {
39
+ size?: string | undefined;
40
+ weight?: string | undefined;
41
+ selector?: string | undefined;
42
+ focus?: boolean;
43
+ }): Promise<void>;
44
+ /** The stubBefore ring a locator is, by overlapping bounding boxes: its index, or -1. For a Method's todo advance. */
45
+ export declare function matchStubIndexForLocator(page: WgPage, locator: Locator, stubs: readonly ResolvedHighlight[] | undefined): Promise<number>;
46
+ export declare function captionForLocator(page: WgPage, locator: Locator, stubs: readonly ResolvedHighlight[] | undefined, fallback: string): Promise<{
47
+ label: string;
20
48
  tone: string;
21
49
  size: string;
22
50
  weight: string;
23
51
  }>;
24
- export declare function dwellMatchedStub(page: WgPage, locator: Locator, stubs: any, pacing: any): Promise<void>;
52
+ export declare function dwellMatchedStub(page: WgPage, locator: Locator, stubs: readonly ResolvedHighlight[] | undefined, pacing: Pick<Pacing, "gatesFast" | "demoPace"> | undefined): Promise<void>;
25
53
  export declare function hideRing(page: WgPage): Promise<void>;
26
54
  /**
27
55
  * True if the engine's own narrate() (waygraph's public export, for a Block
@@ -1,11 +1,11 @@
1
1
  import { ANSI, ansiPaint, demoFixture, demoHighlight, demoLog } from "../stepper/demo-log.js";
2
2
  import { playSwipeTrail } from "./cursor.js";
3
- import { advanceTodoDock, normalizeTodos } from "../narration/todo-dock.js";
3
+ import { advanceTodoDock } from "../narration/todo-dock.js";
4
4
  import { formatHighlightCaption } from "../narration/style.js";
5
5
  import { normalizeHighlightSize, normalizeHighlightTone, normalizeHighlightWeight } from "../../engine/stub/ring.js";
6
6
  import { resolveFixtureDwellMs } from "../narration/demo-pace.js";
7
7
  import { envNumber } from "../../engine/env.js";
8
- import { ignorePageGone } from "../../engine/page-gone.js";
8
+ import { ignorePageGone, ignoreTimeout } from "../../engine/page-gone.js";
9
9
  /**
10
10
  * Scroll a locator into view for demo theater.
11
11
  * Prefer CSS smooth scroll + scrollend wait. Do NOT call Playwright's
@@ -184,8 +184,7 @@ export async function cycleHighlightRings(page, highlights, gatesFast, opts) {
184
184
  .catch(ignorePageGone);
185
185
  }
186
186
  let lastRingEndedAt = null;
187
- for (let i = 0; i < list.length; i++) {
188
- const h = list[i];
187
+ for (const [i, h] of list.entries()) {
189
188
  const ringStarted = Date.now();
190
189
  try {
191
190
  await ensureSelectorInView(page, h.selector);
@@ -200,13 +199,13 @@ export async function cycleHighlightRings(page, highlights, gatesFast, opts) {
200
199
  // "does it exist right now" check - ensureSelectorInView above already gave the page a real
201
200
  // moment to settle.
202
201
  const hLoc = page.locator(h.selector).first();
203
- const box = (await hLoc.count().catch(() => 0)) > 0
202
+ const box = ((await hLoc.count().catch(ignorePageGone)) ?? 0) > 0
204
203
  ? await hLoc
205
204
  .evaluate((el) => {
206
205
  const r = el.getBoundingClientRect();
207
206
  return { x: r.x, y: r.y, width: r.width, height: r.height };
208
207
  })
209
- .catch(() => null)
208
+ .catch(ignorePageGone)
210
209
  : null;
211
210
  if (box && box.width > 0 && box.height > 0) {
212
211
  await showRing(page, box, h.label, h.tone || "planned", {
@@ -230,10 +229,8 @@ export async function cycleHighlightRings(page, highlights, gatesFast, opts) {
230
229
  }, dock)
231
230
  .catch(ignorePageGone);
232
231
  }
233
- else if (!dock && ((opts && opts.todos && opts.todos.length) || (h.todos && h.todos.length))) {
234
- const rows = opts && opts.todos && opts.todos.length
235
- ? opts.todos
236
- : normalizeTodos(h.todos, h.todoIndex);
232
+ else if (!dock && opts?.todos?.length) {
233
+ const rows = opts.todos;
237
234
  await page
238
235
  .evaluate((todos) => {
239
236
  if (window.__wgSyncTodos)
@@ -273,16 +270,16 @@ export async function cycleHighlightRings(page, highlights, gatesFast, opts) {
273
270
  }
274
271
  }
275
272
  catch (err) {
276
- demoHighlight(h || { selector: h && h.selector, label: "(error)" }, "SKIP " + String(err instanceof Error ? err.message : err).slice(0, 80));
273
+ demoHighlight(h, "SKIP " + String(err instanceof Error ? err.message : err).slice(0, 80));
277
274
  }
278
275
  }
279
- if (list.length > 0) {
280
- const last = list[list.length - 1];
276
+ const last = list.at(-1);
277
+ if (last) {
281
278
  // Resolved through Playwright's own locator (>> piercing / :has-text() / :visible support),
282
279
  // not handed to the page as a string for a raw DOM querySelector to fail silently on - see
283
280
  // showRing's own comment (same fix, same reason) and __wgFollowRing's in core.js.
284
281
  const handle = last.selector
285
- ? await page.locator(last.selector).first().elementHandle({ timeout: 100 }).catch(() => null)
282
+ ? await page.locator(last.selector).first().elementHandle({ timeout: 100 }).catch(ignoreTimeout)
286
283
  : null;
287
284
  await page
288
285
  .evaluate(({ h, el }) => {
@@ -333,7 +330,7 @@ export async function showRing(page, box, label, tone, style) {
333
330
  // syntax, invisible to document.querySelector. The resolved element handle is passed straight
334
331
  // into evaluate (Playwright unwraps it to the real DOM node inside the page), so __wgFollowRing's
335
332
  // per-frame RAF loop tracks that exact element directly - no repeated selector re-resolution.
336
- const handle = selector ? await page.locator(selector).first().elementHandle({ timeout: 100 }).catch(() => null) : null;
333
+ const handle = selector ? await page.locator(selector).first().elementHandle({ timeout: 100 }).catch(ignoreTimeout) : null;
337
334
  await page
338
335
  .evaluate(({ box, label, tone, size, weight, el, focus }) => {
339
336
  if (el && window.__wgFollowRing) {
@@ -352,46 +349,18 @@ export async function showRing(page, box, label, tone, style) {
352
349
  }, { box, label, tone: tone || "planned", size, weight, el: handle, focus })
353
350
  .catch(ignorePageGone);
354
351
  }
355
- /** Match a locator to a stubBefore slot by overlapping bounding boxes. */
356
- async function matchStubForLocator(page, locator, stubs) {
357
- if (!stubs || stubs.length === 0)
358
- return null;
359
- const box = await locator.boundingBox().catch(() => null);
360
- if (!box)
361
- return null;
362
- for (const s of stubs) {
363
- try {
364
- const b = await page.locator(s.selector).first().boundingBox();
365
- if (b &&
366
- Math.abs(b.x - box.x) < 4 &&
367
- Math.abs(b.y - box.y) < 4 &&
368
- Math.abs(b.width - box.width) < 8) {
369
- return s;
370
- }
371
- }
372
- catch {
373
- // A stub selector Playwright can't parse can't be this element either.
374
- }
375
- }
376
- return null;
377
- }
378
- /** 0-based stub index for Method fill/click todo advance; -1 if none. */
352
+ /** The stubBefore ring a locator is, by overlapping bounding boxes: its index, or -1. For a Method's todo advance. */
379
353
  export async function matchStubIndexForLocator(page, locator, stubs) {
380
354
  if (!stubs || stubs.length === 0)
381
355
  return -1;
382
- const box = await locator.boundingBox().catch(() => null);
356
+ const box = await locator.boundingBox().catch(ignorePageGone);
383
357
  if (!box)
384
358
  return -1;
385
359
  for (let i = 0; i < stubs.length; i++) {
386
- const s = stubs[i];
387
360
  try {
388
- const b = await page.locator(s.selector).first().boundingBox();
389
- if (b &&
390
- Math.abs(b.x - box.x) < 4 &&
391
- Math.abs(b.y - box.y) < 4 &&
392
- Math.abs(b.width - box.width) < 8) {
361
+ const b = await page.locator(stubs[i].selector).first().boundingBox();
362
+ if (b && Math.abs(b.x - box.x) < 4 && Math.abs(b.y - box.y) < 4 && Math.abs(b.width - box.width) < 8)
393
363
  return i;
394
- }
395
364
  }
396
365
  catch {
397
366
  // A stub selector Playwright can't parse can't be this element either.
@@ -399,6 +368,11 @@ export async function matchStubIndexForLocator(page, locator, stubs) {
399
368
  }
400
369
  return -1;
401
370
  }
371
+ /** The stubBefore ring a locator is, or null. */
372
+ async function matchStubForLocator(page, locator, stubs) {
373
+ const i = await matchStubIndexForLocator(page, locator, stubs);
374
+ return i >= 0 && stubs ? stubs[i] : null;
375
+ }
402
376
  export async function captionForLocator(page, locator, stubs, fallback) {
403
377
  const matched = await matchStubForLocator(page, locator, stubs);
404
378
  if (matched) {
@@ -1,12 +1,13 @@
1
+ import { envFlag } from "../../engine/env.js";
1
2
  import { hasAuthoredStubAfter, resolveSlides, runStubPhase } from "../narration/narration.js";
2
3
  import { applyHighlightStyleDefaults, formatHighlightCaption } from "../narration/style.js";
3
- import { demoLog, logStubPhaseFixtures, logTodoDockFull, paceSpeakFields, summarizeDevice } from "./demo-log.js";
4
- import { extractVerifyHighlights, renderAfterStep } from "./step-panels.js";
5
- import { presentSlides } from "./slides.js";
6
4
  import { logBannerDom, probeBanner } from "./banner-log.js";
7
5
  import { logDocks } from "./before.js";
6
+ import { demoLog, logStubPhaseFixtures, logTodoDockFull, paceSpeakFields, summarizeDevice } from "./demo-log.js";
7
+ import { renderAfterStep } from "./panel-after.js";
8
+ import { presentSlides } from "./slides.js";
9
+ import { extractVerifyHighlights } from "./step-panels.js";
8
10
  import { forceCollapsed } from "./view.js";
9
- import { envFlag } from "../../engine/env.js";
10
11
  const quiet = () => envFlag("WAYGRAPH_JSON");
11
12
  export async function showAfter(ctx, view, result) {
12
13
  const { stage, refs } = ctx;
@@ -65,7 +66,7 @@ async function showSlides(ctx, view, result) {
65
66
  const slides = resolveSlides(view.step.block, { out: result }).map((s) => applyHighlightStyleDefaults(s, view.step.highlightStyle));
66
67
  if (slides.length === 0)
67
68
  return;
68
- await presentSlides(ctx.page, slides, ctx.gate.wait, {
69
+ await presentSlides(ctx.page, slides, {
69
70
  title: ctx.title,
70
71
  blockName: view.step.block.name,
71
72
  fast: ctx.pacing.gatesFast,
@@ -89,21 +90,10 @@ async function narrateAfter(ctx, view, result) {
89
90
  }),
90
91
  });
91
92
  const highlights = phase.highlights.map((h) => {
93
+ // The ring's look and timing only; its todo, gesture and cursor belong to stubBefore.
92
94
  const styled = applyHighlightStyleDefaults(h, style);
93
- return {
94
- selector: styled.selector,
95
- label: formatHighlightCaption(styled),
96
- duration: styled.duration,
97
- fastMode: styled.fastMode,
98
- tone: styled.tone,
99
- size: styled.size,
100
- weight: styled.weight,
101
- zoom: styled.zoom,
102
- zoomOut: styled.zoomOut,
103
- focus: !!styled.focus,
104
- color: styled.color,
105
- detail: styled.detail,
106
- };
95
+ const { tag: _tag, cursor: _cursor, todo: _todo, slotId: _slotId, gesture: _gesture, ...ring } = styled;
96
+ return { ...ring, label: formatHighlightCaption(styled), focus: !!styled.focus };
107
97
  });
108
98
  return { phase, highlights };
109
99
  }
@@ -1,6 +1,7 @@
1
- import type { WgPage } from "../types.js";
1
+ import type { BannerUiState, WgPage } from "../types.js";
2
+ import type { StubPhaseResult } from "../../engine/stub/stub.js";
2
3
  /** Authored banner UX from a stub phase -> the { pos, collision, hidden } shape installOverlay takes. */
3
- export declare function bannerUiFromPhase(phase: any): Record<string, any>;
4
+ export declare function bannerUiFromPhase(phase: Pick<StubPhaseResult, "titlePos" | "bannerUi"> | null | undefined): BannerUiState;
4
5
  /** Read the live banner: text, position, whether it was moved by ring collision; drains the move log. */
5
6
  export declare function probeBanner(page: WgPage): Promise<{
6
7
  present: boolean;
@@ -30,8 +31,5 @@ export declare function probeBanner(page: WgPage): Promise<{
30
31
  w: number;
31
32
  h: number;
32
33
  log: any;
33
- } | {
34
- present: boolean;
35
- log: never[];
36
34
  }>;
37
- export declare function logBannerDom(tag: any, p: any): void;
35
+ export declare function logBannerDom(tag: string, p: Awaited<ReturnType<typeof probeBanner>> | null | undefined): void;
@@ -1,4 +1,5 @@
1
1
  import { demoLog } from "./demo-log.js";
2
+ import { ignorePageGone } from "../../engine/page-gone.js";
2
3
  /** Authored banner UX from a stub phase -> the { pos, collision, hidden } shape installOverlay takes. */
3
4
  export function bannerUiFromPhase(phase) {
4
5
  const ui = {};
@@ -41,7 +42,8 @@ export async function probeBanner(page) {
41
42
  log,
42
43
  };
43
44
  })
44
- .catch(() => ({ present: false, log: [] }));
45
+ .catch(ignorePageGone)
46
+ .then((probe) => probe ?? { present: false, log: [] });
45
47
  }
46
48
  export function logBannerDom(tag, p) {
47
49
  if (!p)
@@ -1,13 +1,13 @@
1
1
  // Before a demo step runs: play the Block's stubBefore narration (rings, todos, device, title, caption) and show the
2
2
  // step's panel, then log what was painted so an agent without eyes can check it.
3
+ import { envFlag } from "../../engine/env.js";
3
4
  import { runStubPhase } from "../narration/narration.js";
4
5
  import { applyHighlightStyleDefaults, formatHighlightCaption } from "../narration/style.js";
6
+ import { logBannerDom, probeBanner } from "./banner-log.js";
5
7
  import { demoLog, logStubPhaseFixtures, logTodoDockFull, paceSpeakFields, summarizeDevice } from "./demo-log.js";
6
- import { renderBeforeStep } from "./step-panels.js";
8
+ import { renderBeforeStep } from "./panel-before.js";
7
9
  import { probeTodoDocksOnPage } from "./todo-advance.js";
8
- import { logBannerDom, probeBanner } from "./banner-log.js";
9
10
  import { forceCollapsed } from "./view.js";
10
- import { envFlag } from "../../engine/env.js";
11
11
  const quiet = () => envFlag("WAYGRAPH_JSON");
12
12
  export async function showBefore(ctx, view) {
13
13
  const { stage } = ctx;