waygraph 0.16.1 → 0.16.4
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/README.md +12 -0
- package/dist/analysis/practices-check.d.ts +1 -1
- package/dist/analysis/practices-check.js +36 -0
- package/dist/beta.d.ts +6 -0
- package/dist/beta.js +5 -0
- package/dist/cli/commands/codemod-flows.js +2 -2
- package/dist/cli/commands/codemod-stubs.js +132 -2
- package/dist/cli/commands/pilot.js +3 -16
- package/dist/cli/commands/session-ops.js +26 -1
- package/dist/cli/flags.d.ts +1 -1
- package/dist/cli/flags.js +1 -1
- package/dist/cli/run-chain.js +2 -2
- package/dist/cli/usage.js +6 -0
- package/dist/cli/util.js +1 -1
- package/dist/demo/narration/demo-pace.d.ts +1 -1
- package/dist/demo/narration/demo-pace.js +1 -1
- package/dist/demo/narration/merge.d.ts +1 -1
- package/dist/demo/narration/merge.js +1 -1
- package/dist/demo/narration/narration.d.ts +2 -6
- package/dist/demo/narration/narration.js +5 -17
- package/dist/demo/narration/stub-ctx.js +126 -124
- package/dist/demo/overlay/cursor.js +1 -1
- package/dist/demo/overlay/inpage/banner.js +5 -0
- package/dist/demo/overlay/inpage/core.js +1 -6
- package/dist/demo/overlay/inpage/network.js +1 -1
- package/dist/demo/overlay/inpage/todos.js +0 -15
- package/dist/demo/overlay/network-capture.js +2 -2
- package/dist/demo/overlay/overlay-css.js +19 -76
- package/dist/demo/overlay/overlay-stage.js +1 -1
- package/dist/demo/overlay/overlay.js +3 -2
- package/dist/demo/overlay/phase-fixtures.js +5 -5
- package/dist/demo/overlay/pilot/shell.js +7 -5
- package/dist/demo/overlay/rings.d.ts +0 -8
- package/dist/demo/overlay/rings.js +0 -15
- package/dist/demo/overlay/ui/components/ring.js +2 -2
- package/dist/demo/overlay/ui/overlay.css +136 -33
- package/dist/demo/overlay/ui/shadow.js +69 -2
- package/dist/demo/stepper/after.js +4 -17
- package/dist/demo/stepper/before.js +3 -1
- package/dist/demo/stepper/demo-log.js +8 -8
- package/dist/demo/stepper/interaction-patch.js +3 -15
- package/dist/demo/stepper/panel-after.js +1 -1
- package/dist/demo/stepper/slides.d.ts +3 -0
- package/dist/demo/stepper/slides.js +18 -2
- package/dist/demo/stepper/stepper.js +1 -1
- package/dist/demo/stepper/teardown.js +0 -1
- package/dist/demo/verbose-log.js +1 -1
- package/dist/engine/block/block.d.ts +1 -12
- package/dist/engine/block/kind.d.ts +3 -0
- package/dist/engine/block/kind.js +11 -0
- package/dist/engine/block/kinds/method.d.ts +0 -2
- package/dist/engine/block/kinds/method.js +0 -1
- package/dist/engine/block/kinds/nav.js +1 -1
- package/dist/engine/errors.d.ts +12 -0
- package/dist/engine/errors.js +12 -0
- package/dist/engine/read/read-block.d.ts +61 -0
- package/dist/engine/read/read-block.js +94 -0
- package/dist/engine/read/read-key.d.ts +32 -0
- package/dist/engine/read/read-key.js +34 -0
- package/dist/engine/read/read-page.d.ts +19 -0
- package/dist/engine/read/read-page.js +50 -0
- package/dist/engine/read/readers.d.ts +82 -0
- package/dist/engine/read/readers.js +43 -0
- package/dist/engine/read/run-read.d.ts +17 -0
- package/dist/engine/read/run-read.js +156 -0
- package/dist/engine/run/connect.js +3 -2
- package/dist/engine/run/events.d.ts +28 -10
- package/dist/engine/run/events.js +52 -38
- package/dist/engine/run/run.d.ts +1 -19
- package/dist/engine/run/run.js +21 -97
- package/dist/engine/stub/ring.d.ts +2 -4
- package/dist/engine/stub/ring.js +1 -0
- package/dist/engine/stub/stub.d.ts +38 -25
- package/dist/index.d.ts +9 -7
- package/dist/index.js +11 -6
- package/dist/live/auto/auto.js +2 -3
- package/dist/live/auto/menu.js +3 -2
- package/dist/live/auto-session/auto-session.d.ts +9 -2
- package/dist/live/auto-session/auto-session.js +23 -20
- package/dist/live/auto-session/feed.d.ts +16 -0
- package/dist/live/auto-session/feed.js +71 -0
- package/dist/live/auto-session/helpers.d.ts +7 -0
- package/dist/live/auto-session/helpers.js +9 -0
- package/dist/live/auto-session/ipc.d.ts +15 -47
- package/dist/live/auto-session/ipc.js +34 -4
- package/dist/live/auto-session/protocol.d.ts +127 -0
- package/dist/live/auto-session/protocol.js +6 -0
- package/dist/live/auto-session/read.d.ts +25 -0
- package/dist/live/auto-session/read.js +50 -0
- package/dist/live/traverse/worker.d.ts +1 -0
- package/dist/live/traverse/worker.js +6 -1
- package/dist/manifest/manifest.d.ts +22 -2
- package/dist/manifest/manifest.js +27 -1
- package/dist/manifest/project.d.ts +10 -1
- package/dist/manifest/project.js +9 -12
- package/dist/manifest/read-blocks.d.ts +2 -0
- package/dist/manifest/read-blocks.js +23 -0
- package/dist/manifest/scan.d.ts +3 -2
- package/dist/manifest/scan.js +8 -4
- package/dist/runner/plan/find.js +2 -2
- package/dist/runner/plan/plan.js +1 -1
- package/dist/runner/plan/seed-mem.js +5 -5
- package/dist/runner/plan/spec.js +1 -1
- package/dist/runner/runner.d.ts +0 -1
- package/dist/runner/runner.js +6 -6
- package/dist/runtime/full-launch.d.ts +2 -1
- package/dist/runtime/full-launch.js +29 -27
- package/dist/runtime/run-log.d.ts +2 -0
- package/dist/runtime/run-log.js +13 -6
- package/examples/saucedemo/README.md +2 -2
- package/examples/saucedemo/src/blocks/SITE-MAP.md +3 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.read.block.ts +26 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
- package/examples/saucedemo/src/blocks/saucedemo-web/header.read.block.ts +25 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory/inventory.read.block.ts +28 -0
- package/examples/saucedemo/src/flows/api-demo.flow.ts +1 -1
- package/examples/saucedemo/src/flows/cart-bulk.flow.ts +1 -1
- package/examples/saucedemo/src/flows/cart-continue.flow.ts +1 -1
- package/examples/saucedemo/src/flows/catalog-logout.flow.ts +1 -1
- package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +3 -2
- package/examples/saucedemo/src/flows/checkout.flow.ts +13 -12
- package/examples/saucedemo/src/flows/login.flow.ts +1 -1
- package/examples/saucedemo/src/flows/shop.flow.ts +3 -2
- package/examples/saucedemo/src/flows/stub-on-error.flow.ts +1 -1
- package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +1 -1
- package/examples/saucedemo/src/states/checkout.reads.ts +9 -0
- package/examples/saucedemo/src/stubs/device-showcase.ts +70 -76
- package/examples/saucedemo/src/stubs/finish-order.ts +38 -0
- package/examples/saucedemo/src/stubs/login.ts +5 -5
- package/package.json +5 -1
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +2 -34
- package/templates/quickstart/src/flows/api-demo.flow.ts +1 -1
- package/templates/quickstart/src/flows/cart-bulk.flow.ts +1 -1
- package/templates/quickstart/src/flows/cart-continue.flow.ts +1 -1
- package/templates/quickstart/src/flows/catalog-logout.flow.ts +1 -1
- package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +3 -2
- package/templates/quickstart/src/flows/checkout.flow.ts +13 -12
- package/templates/quickstart/src/flows/login.flow.ts +1 -1
- package/templates/quickstart/src/flows/shop.flow.ts +3 -2
- package/templates/quickstart/src/flows/stub-on-error.flow.ts +1 -1
- package/templates/quickstart/src/flows/viewer-blocked.flow.ts +1 -1
- package/templates/quickstart/src/stubs/device-showcase.ts +70 -76
- package/templates/quickstart/src/stubs/finish-order.ts +38 -0
- package/templates/quickstart/src/stubs/login.ts +5 -5
- package/templates/scaffold/src/flows/(external)/hoppscotch/auth.flow.ts +1 -1
- package/templates/scaffold/src/flows/(external)/hoppscotch/get.flow.ts +1 -1
- package/templates/scaffold/src/flows/(external)/hoppscotch/post.flow.ts +1 -1
- package/templates/scaffold/src/flows/(user-stories)/email-verification.flow.ts +4 -4
- package/templates/scaffold/src/flows/README.md +1 -1
- package/templates/scaffold/src/flows/home/cart.flow.ts +7 -7
- package/templates/scaffold/src/flows/home/hello.flow.ts +10 -7
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +1 -9
- package/templates/skills/waygraph-convention.skill.md +10 -2
- package/templates/skills/waygraph-pilot.skill.md +7 -0
|
@@ -13,8 +13,10 @@ import { ignorePageGone } from "../../../engine/page-gone.js";
|
|
|
13
13
|
// re-invented) - genuinely the same visual language, not just the ring CSS
|
|
14
14
|
// this file already shared via WAYGRAPH_RING_CSS.
|
|
15
15
|
const OVERLAY_CSS = `
|
|
16
|
+
/* Every element here sits in a slot of the overlay layout (ui/css/layout.css) - bottom-right, bottom-left, top-right -
|
|
17
|
+
or on its stage (the spotlight); none is positioned by hand. */
|
|
16
18
|
#wg-pilot-overlay {
|
|
17
|
-
|
|
19
|
+
z-index: var(--wg-z-top); display: flex; flex-direction: column; align-items: flex-end;
|
|
18
20
|
font: 12px/1.4 ui-monospace, "SF Mono", Consolas, monospace;
|
|
19
21
|
color: #fff; pointer-events: auto;
|
|
20
22
|
}
|
|
@@ -56,7 +58,7 @@ const OVERLAY_CSS = `
|
|
|
56
58
|
#wg-pilot-panel-graph .wg-pilot-graph-edge { padding: 2px 0 2px 14px; border-left: 2px solid rgba(255,255,255,.15); font-size: 11px; }
|
|
57
59
|
#wg-pilot-panel-graph .wg-pilot-graph-edge .wg-pilot-kind { color: #c9a6ff; }
|
|
58
60
|
#wg-pilot-activity {
|
|
59
|
-
|
|
61
|
+
z-index: var(--wg-z-chrome);
|
|
60
62
|
font: 12px/1.4 ui-monospace, "SF Mono", Consolas, monospace;
|
|
61
63
|
background: ${SURFACE}; color: #fff; padding: 6px 10px; border-radius: 8px;
|
|
62
64
|
box-shadow: 0 2px 8px rgba(0,0,0,0.25); white-space: nowrap; pointer-events: none;
|
|
@@ -68,12 +70,12 @@ const OVERLAY_CSS = `
|
|
|
68
70
|
#wg-banner all come from the ONE shared bundle ensureShadowRoot installs (ui/css/*.css) - not
|
|
69
71
|
redefined here. */
|
|
70
72
|
/* Spotlight (demo focus:true) - dim page, cutout around target. */
|
|
71
|
-
#wg-pilot-fx-focus{position:fixed;z-index:
|
|
73
|
+
#wg-pilot-fx-focus{position:fixed;z-index:var(--wg-z-focus);pointer-events:none;
|
|
72
74
|
border-radius:12px;box-shadow:0 0 0 9999px rgba(8,4,20,.62);
|
|
73
75
|
opacity:0;transition:opacity .3s ease,left .3s ease,top .3s ease,width .3s ease,height .3s ease;}
|
|
74
76
|
#wg-pilot-fx-focus.wg-in{opacity:1;}
|
|
75
77
|
/* Zoom HUD (demo parity - badge only; no CSS scale of the page). */
|
|
76
|
-
#wg-pilot-fx-zoom{
|
|
78
|
+
#wg-pilot-fx-zoom{z-index:var(--wg-z-chrome);
|
|
77
79
|
display:flex;align-items:center;gap:7px;padding:6px 11px 6px 8px;
|
|
78
80
|
border-radius:999px;background:${SURFACE};color:#f0e8ff;
|
|
79
81
|
border:1px solid rgba(250,204,21,.6);box-shadow:0 6px 18px rgba(0,0,0,.4);
|
|
@@ -83,7 +85,7 @@ const OVERLAY_CSS = `
|
|
|
83
85
|
#wg-pilot-fx-zoom .wg-pilot-fx-zoom-ico svg{width:14px;height:14px;display:block;}
|
|
84
86
|
#wg-pilot-fx-zoom .wg-pilot-fx-zoom-val{color:#fde68a;font-variant-numeric:tabular-nums;font-weight:800;min-width:3.2em;}
|
|
85
87
|
#wg-pilot-fx-zoom[data-zoomed="1"]{border-color:#fbbf24;}
|
|
86
|
-
#wg-pilot-fx-device{
|
|
88
|
+
#wg-pilot-fx-device{z-index:var(--wg-z-chrome);
|
|
87
89
|
padding:8px 12px;border-radius:10px;background:${SURFACE};color:#f0e8ff;
|
|
88
90
|
border:1px solid rgba(124,58,237,.55);font:700 12px/1.2 system-ui,sans-serif;
|
|
89
91
|
pointer-events:none;box-shadow:0 6px 18px rgba(0,0,0,.35);}
|
|
@@ -51,11 +51,3 @@ export declare function captionForLocator(page: WgPage, locator: Locator, stubs:
|
|
|
51
51
|
}>;
|
|
52
52
|
export declare function dwellMatchedStub(page: WgPage, locator: Locator, stubs: readonly ResolvedHighlight[] | undefined, pacing: Pick<Pacing, "gatesFast" | "demoPace"> | undefined): Promise<void>;
|
|
53
53
|
export declare function hideRing(page: WgPage): Promise<void>;
|
|
54
|
-
/**
|
|
55
|
-
* True if the engine's own narrate() (waygraph's public export, for a Block
|
|
56
|
-
* author explicitly captioning one action) JUST positioned the ring for
|
|
57
|
-
* THIS action, moments ago - the automatic per-fill/per-click narration
|
|
58
|
-
* below should not immediately overwrite an author's own explicit caption
|
|
59
|
-
* with its generic guess.
|
|
60
|
-
*/
|
|
61
|
-
export declare function wasJustNarrated(page: WgPage): Promise<boolean>;
|
|
@@ -405,18 +405,3 @@ export async function hideRing(page) {
|
|
|
405
405
|
})
|
|
406
406
|
.catch(ignorePageGone);
|
|
407
407
|
}
|
|
408
|
-
/**
|
|
409
|
-
* True if the engine's own narrate() (waygraph's public export, for a Block
|
|
410
|
-
* author explicitly captioning one action) JUST positioned the ring for
|
|
411
|
-
* THIS action, moments ago - the automatic per-fill/per-click narration
|
|
412
|
-
* below should not immediately overwrite an author's own explicit caption
|
|
413
|
-
* with its generic guess.
|
|
414
|
-
*/
|
|
415
|
-
export async function wasJustNarrated(page) {
|
|
416
|
-
return page
|
|
417
|
-
.evaluate(() => {
|
|
418
|
-
const w = window;
|
|
419
|
-
return typeof w.__wgLastNarrate === "number" && Date.now() - w.__wgLastNarrate < 500;
|
|
420
|
-
})
|
|
421
|
-
.catch(() => false);
|
|
422
|
-
}
|
|
@@ -11,13 +11,13 @@ function toneRules(sel, decl, only) {
|
|
|
11
11
|
/** The highlight ring + its label (`#wg-ring`, `#wg-ring-label`): tone / size / weight variants. */
|
|
12
12
|
export function ringCss() {
|
|
13
13
|
const p = TONES.planned;
|
|
14
|
-
return ("#wg-ring{position:fixed;z-index:2147483646;pointer-events:none;opacity:0;" +
|
|
14
|
+
return ("#wg-ring{position:fixed;z-index:var(--wg-z-ring,2147483646);pointer-events:none;opacity:0;" +
|
|
15
15
|
`border:2.5px solid ${p.ring};border-radius:10px;` +
|
|
16
16
|
// Opacity fade only - NEVER transition border-color/box-shadow: tone swaps must snap.
|
|
17
17
|
`box-shadow:0 0 0 4px ${p.glow};transition:opacity .3s ease;}` +
|
|
18
18
|
toneRules("#wg-ring", (t) => `border-color:${t.ring};box-shadow:0 0 0 4px ${t.glow};`) +
|
|
19
19
|
// A real element (not ::after) so JS can measure it and clamp it back onto the screen.
|
|
20
|
-
"#wg-ring-label{position:fixed;z-index:2147483646;pointer-events:none;opacity:0;" +
|
|
20
|
+
"#wg-ring-label{position:fixed;z-index:var(--wg-z-ring,2147483646);pointer-events:none;opacity:0;" +
|
|
21
21
|
`max-width:min(360px,70vw);white-space:normal;padding:6px 10px;border-radius:7px;background:${p.labelBg};color:${p.labelFg};` +
|
|
22
22
|
"font:600 12px/1.35 system-ui,sans-serif;transition:opacity .3s ease;}" +
|
|
23
23
|
"#wg-ring-label::after{content:attr(data-label);}" +
|
|
@@ -31,16 +31,21 @@
|
|
|
31
31
|
--wg-font: system-ui, sans-serif;
|
|
32
32
|
--wg-font-mono: ui-monospace, "SF Mono", Consolas, monospace;
|
|
33
33
|
|
|
34
|
-
/* Stacking
|
|
35
|
-
|
|
36
|
-
--wg-z-
|
|
37
|
-
--wg-z-
|
|
38
|
-
--wg-z-
|
|
39
|
-
--wg-z-
|
|
40
|
-
--wg-z-
|
|
41
|
-
/*
|
|
42
|
-
--wg-z-
|
|
43
|
-
|
|
34
|
+
/* Stacking. All overlay UI lives in one fixed parent (#wg-ui, layout.css) that sits above the host page with the one
|
|
35
|
+
max z-index; these values only order the layers inside it, lowest first. */
|
|
36
|
+
--wg-z-ui: var(--layer-important, 2147483647);
|
|
37
|
+
--wg-z-focus: 1; /* the spotlight veil; chrome stepping back while a ring is the focus */
|
|
38
|
+
--wg-z-chrome: 2; /* banner, todo docks, caption, chips, network tab */
|
|
39
|
+
--wg-z-dock: 2;
|
|
40
|
+
--wg-z-banner: 2;
|
|
41
|
+
--wg-z-fx: 4; /* rings and their labels: a highlight is never covered by chrome */
|
|
42
|
+
--wg-z-ring: 4;
|
|
43
|
+
--wg-z-top: 6; /* the step panel and other controls a person clicks */
|
|
44
|
+
--wg-z-toast: 7; /* short-lived notices (device change) */
|
|
45
|
+
--wg-z-cursor: 8; /* the cursor, click pulse and swipe trail: the pointer is always on top */
|
|
46
|
+
/* Spacing of the chrome layout: between items in a slot, and from the viewport edge. */
|
|
47
|
+
--wg-gap: 10px;
|
|
48
|
+
--wg-edge: 14px;
|
|
44
49
|
--wg-ease: var(--ease-3, cubic-bezier(.22,1,.36,1));
|
|
45
50
|
--wg-shadow-dock: var(--shadow-3, 0 2px 10px rgba(0,0,0,.3));
|
|
46
51
|
--wg-shadow-banner: var(--shadow-4, 0 8px 20px rgba(0,0,0,.3));
|
|
@@ -50,6 +55,74 @@
|
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
|
|
58
|
+
/*
|
|
59
|
+
* The overlay's one layout. Every piece of waygraph UI lives in #wg-ui, a fixed layer over the page, in one of two
|
|
60
|
+
* places:
|
|
61
|
+
*
|
|
62
|
+
* #wg-stage things anchored to the page: ring, label, cursor, click pulse, spotlight veil, swipe trail. They follow
|
|
63
|
+
* a target's coordinates (position: fixed against the viewport).
|
|
64
|
+
* #wg-chrome everything else, placed in a slot. Nothing in it is positioned by hand: a slot is a flex column with
|
|
65
|
+
* a gap, so what shares a place stacks instead of overlapping, and a slot never grows past its row.
|
|
66
|
+
*
|
|
67
|
+
* top-left | top-center | top-right banner (by its side) · device chip / toast
|
|
68
|
+
* left | | right todo docks (by side)
|
|
69
|
+
* bottom-left | bottom-center | bottom-right network tab · typing, caption, step panel · zoom
|
|
70
|
+
*
|
|
71
|
+
* The slot an element goes in is decided once, in ui/shadow.ts (by id and data-pos), and a data-pos change moves it.
|
|
72
|
+
* Each row lays out on its own, so a wide step panel never squeezes the side docks.
|
|
73
|
+
*/
|
|
74
|
+
#wg-ui { position: fixed; inset: 0; z-index: var(--wg-z-ui); pointer-events: none; }
|
|
75
|
+
#wg-stage { display: contents; }
|
|
76
|
+
#wg-chrome {
|
|
77
|
+
position: absolute; inset: 0; box-sizing: border-box;
|
|
78
|
+
display: flex; flex-direction: column; gap: var(--wg-gap); padding: var(--wg-edge);
|
|
79
|
+
}
|
|
80
|
+
.wg-row { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: var(--wg-gap); min-height: 0; }
|
|
81
|
+
.wg-row-top { flex: 0 0 auto; align-items: start; }
|
|
82
|
+
.wg-row-middle { flex: 1 1 0; display: flex; justify-content: space-between; align-items: stretch; }
|
|
83
|
+
.wg-row-bottom { flex: 0 1 auto; align-items: end; max-height: 62vh; }
|
|
84
|
+
.wg-slot {
|
|
85
|
+
display: flex; flex-direction: column; gap: var(--wg-gap);
|
|
86
|
+
min-width: 0; min-height: 0; max-height: 100%; max-width: 100%;
|
|
87
|
+
}
|
|
88
|
+
.wg-slot[data-slot$="left"] { align-items: flex-start; }
|
|
89
|
+
.wg-slot[data-slot$="center"] { align-items: center; }
|
|
90
|
+
.wg-slot[data-slot$="right"] { align-items: flex-end; }
|
|
91
|
+
.wg-row-bottom .wg-slot { justify-content: flex-end; }
|
|
92
|
+
/* A slotted element shrinks (and scrolls inside) rather than spilling out of its slot: a min-width of its own (the
|
|
93
|
+
network tab's 260px) must not push it past the slot into its neighbour. */
|
|
94
|
+
.wg-slot > * { position: relative; flex: 0 1 auto; box-sizing: border-box; pointer-events: auto; }
|
|
95
|
+
/* The slot's limits win over an element's own sizing (an #id rule's min-width / max-height), hence the id here. */
|
|
96
|
+
#wg-chrome .wg-slot > * { min-width: 0; min-height: 0; max-width: 100%; max-height: 100%; }
|
|
97
|
+
|
|
98
|
+
/* Order inside a shared slot: what is being typed, what the narration says, then the controls. */
|
|
99
|
+
#wg-typing-badge { order: 1; }
|
|
100
|
+
#wg-caption-bar { order: 2; }
|
|
101
|
+
#wg-panel, #wg-auto-panel { order: 3; }
|
|
102
|
+
#wg-device-toast { order: 1; }
|
|
103
|
+
#wg-device-badge, #wg-pilot-fx-device { order: 2; }
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* Chrome that comes and goes takes no space while hidden (an empty gap above the step panel would read as a bug), and
|
|
107
|
+
* still fades: display switches with the transition (allow-discrete), and @starting-style is where the fade starts.
|
|
108
|
+
*/
|
|
109
|
+
#wg-caption-bar, #wg-typing-badge, #wg-network-log, #wg-device-badge {
|
|
110
|
+
transition-property: opacity, transform, display; transition-behavior: allow-discrete;
|
|
111
|
+
}
|
|
112
|
+
#wg-caption-bar:not(.wg-in), #wg-typing-badge:not(.wg-in), #wg-network-log:not(.wg-in), #wg-device-badge:not(.wg-in) {
|
|
113
|
+
display: none;
|
|
114
|
+
}
|
|
115
|
+
@starting-style {
|
|
116
|
+
#wg-caption-bar.wg-in, #wg-typing-badge.wg-in, #wg-network-log.wg-in, #wg-device-badge.wg-in { opacity: 0; }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Narrow viewports: the bottom row's three slots stack (network, then the step panel full width, then zoom). */
|
|
120
|
+
@media (max-width: 640px) {
|
|
121
|
+
.wg-row-bottom { grid-template-columns: minmax(0, 1fr); }
|
|
122
|
+
.wg-slot[data-slot="bottom-center"] { align-items: stretch; }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
53
126
|
/*
|
|
54
127
|
* ONE ring implementation, shared by every surface (demo/auto's singleton #wg-ring, and Pilot's
|
|
55
128
|
* multiple simultaneous fixture rings) - `.wg-ring-el`/`.wg-ring-el-label` is the real class every
|
|
@@ -110,14 +183,14 @@
|
|
|
110
183
|
|
|
111
184
|
/* Travelling mouse cursor + click ripple (#wg-cursor, #wg-click-pulse). */
|
|
112
185
|
#wg-cursor {
|
|
113
|
-
position: fixed; z-index: var(--wg-z-
|
|
186
|
+
position: fixed; z-index: var(--wg-z-cursor); width: 24px; height: 24px; pointer-events: none;
|
|
114
187
|
left: 0; top: 0; opacity: 0; margin: 0;
|
|
115
188
|
/* Travel time is JS-driven via --wg-cursor-ms (a hardcoded duration would make speed a no-op). */
|
|
116
189
|
transition: transform var(--wg-cursor-ms, 600ms) var(--wg-ease), opacity .2s ease;
|
|
117
190
|
filter: drop-shadow(0 2px 4px rgba(12,12,26,.4));
|
|
118
191
|
}
|
|
119
192
|
#wg-click-pulse {
|
|
120
|
-
position: fixed; z-index: var(--wg-z-
|
|
193
|
+
position: fixed; z-index: var(--wg-z-cursor); width: 14px; height: 14px;
|
|
121
194
|
margin-left: -7px; margin-top: -7px; border-radius: 50%; pointer-events: none; opacity: 0;
|
|
122
195
|
border: 2px solid var(--wg-tone-ring, var(--wg-tone-planned-ring)); background: var(--wg-tone-pulse, var(--wg-tone-planned-glow));
|
|
123
196
|
}
|
|
@@ -130,19 +203,16 @@
|
|
|
130
203
|
@keyframes wg-pulse { 0% { opacity: .9; transform: scale(.4); } 100% { opacity: 0; transform: scale(2.4); } }
|
|
131
204
|
|
|
132
205
|
|
|
133
|
-
/* Top title banner (#wg-banner)
|
|
134
|
-
* card
|
|
135
|
-
*
|
|
206
|
+
/* Top title banner (#wg-banner), in the top-left / top-center / top-right slot by its data-pos (layout.css). Sits
|
|
207
|
+
* below rings and labels (--wg-z-banner): a title card never shadows a highlight. Moves to another slot when a ring
|
|
208
|
+
* overlaps it (inpage/banner.ts's __wgBannerAvoidRing) unless authored off. */
|
|
136
209
|
#wg-banner {
|
|
137
|
-
|
|
210
|
+
z-index: var(--wg-z-banner);
|
|
138
211
|
background: var(--wg-surface); color: #fff; border-radius: var(--wg-radius-lg); padding: 10px 16px;
|
|
139
212
|
font: 14px/1.4 var(--wg-font); box-shadow: var(--wg-shadow-banner);
|
|
140
|
-
border: 1px solid rgba(124,58,237,.4); cursor: pointer;
|
|
141
|
-
transition:
|
|
213
|
+
border: 1px solid rgba(124,58,237,.4); cursor: pointer; max-width: min(92vw, 520px);
|
|
214
|
+
transition: opacity .3s var(--wg-ease);
|
|
142
215
|
}
|
|
143
|
-
#wg-banner[data-pos=left] { left: 14px; right: auto; transform: none; }
|
|
144
|
-
#wg-banner[data-pos=center] { left: 50%; right: auto; transform: translateX(-50%); }
|
|
145
|
-
#wg-banner[data-pos=right] { right: 14px; left: auto; transform: none; }
|
|
146
216
|
/* Same "auto opacity" step-back the todo dock already has (.wg-todo-behind) while a ring caption
|
|
147
217
|
is the current focus - added via __wgBannerSetBehind, not just the collision-driven move. */
|
|
148
218
|
#wg-banner.wg-banner-behind { opacity: .42; }
|
|
@@ -161,14 +231,11 @@
|
|
|
161
231
|
* duplicate of this file's own rules, is consolidated.
|
|
162
232
|
*/
|
|
163
233
|
#wg-pilot-fx-todos {
|
|
164
|
-
|
|
165
|
-
overflow: auto; font: 12px/1.4 ui-sans-serif, system-ui, sans-serif; color: #fff;
|
|
234
|
+
z-index: var(--wg-z-dock); width: min(280px, 42vw); overflow: auto; font: 12px/1.4 ui-sans-serif, system-ui, sans-serif; color: #fff;
|
|
166
235
|
background: var(--wg-surface); border-radius: var(--wg-radius-md); padding: 10px 12px;
|
|
167
236
|
box-shadow: var(--wg-shadow-dock); pointer-events: auto; transition: opacity .25s ease;
|
|
168
237
|
}
|
|
169
238
|
#wg-pilot-fx-todos.wg-todo-behind { z-index: var(--wg-z-focus); opacity: .42; pointer-events: none; }
|
|
170
|
-
#wg-pilot-fx-todos[data-pos=left] { left: 14px; right: auto; }
|
|
171
|
-
#wg-pilot-fx-todos[data-pos=right] { right: 14px; left: auto; }
|
|
172
239
|
#wg-pilot-fx-todos .wg-pilot-fx-todo-title {
|
|
173
240
|
font: 700 11px/1.2 system-ui, sans-serif; color: var(--wg-accent-soft);
|
|
174
241
|
letter-spacing: .04em; text-transform: uppercase; margin: 0 0 8px;
|
|
@@ -199,19 +266,55 @@
|
|
|
199
266
|
* chrome, this narrates the PAGE itself so it reads as a caption card, not more chrome).
|
|
200
267
|
*/
|
|
201
268
|
#wg-caption-bar {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
z-index:2147483647 - always on top) - without it the pill sat right over the caption card,
|
|
205
|
-
both centered at the bottom of the viewport (see #wg-typing-badge's own bottom:88px, same
|
|
206
|
-
reason). */
|
|
207
|
-
padding: 0 6% 64px; display: flex; justify-content: center;
|
|
269
|
+
/* In the bottom-center slot, above the step panel (layout.css): the slot's gap keeps them apart. */
|
|
270
|
+
z-index: var(--wg-z-chrome); display: flex; justify-content: center; max-width: min(820px, 92vw);
|
|
208
271
|
pointer-events: none; opacity: 0; transform: translateY(6px);
|
|
209
|
-
transition:
|
|
272
|
+
transition-duration: .3s; transition-timing-function: ease;
|
|
210
273
|
}
|
|
211
274
|
#wg-caption-bar.wg-in { opacity: 1; transform: translateY(0); }
|
|
212
275
|
#wg-caption-bar .wg-caption-text {
|
|
213
276
|
font: 500 15px/1.4 var(--wg-font); color: #1c1917; text-align: center;
|
|
214
|
-
|
|
277
|
+
text-wrap: balance;
|
|
215
278
|
background: #fff; border-radius: var(--wg-radius-lg);
|
|
216
279
|
padding: 10px 18px; box-shadow: var(--wg-shadow-banner);
|
|
217
280
|
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
/* The network tab: real page traffic (network-capture.ts) and defineApiBlock's own calls, bottom-left (the one corner
|
|
284
|
+
* free of the zoom HUD and the typing chip). Hidden until ctx.ui.showNetworkTab(); stays up once shown. In the shared
|
|
285
|
+
* bundle, not the runner CSS: a navigation rebuilds the panel the moment the shadow root is back, before any later
|
|
286
|
+
* stylesheet, and without its CSS it painted unstyled and visible, then faded out. */
|
|
287
|
+
#wg-network-log{z-index:var(--wg-z-chrome);min-width:260px;max-width:min(92vw,420px);max-height:min(60vh,420px);display:flex;flex-direction:column;border-radius:10px;background:var(--wg-surface);color:#f0e8ff;border:1px solid rgba(124,58,237,.45);box-shadow:0 8px 22px rgba(0,0,0,.4);font:12px/1.4 ui-monospace,"SF Mono",Consolas,monospace;opacity:0;transform:translateY(8px);transition-duration:.25s;transition-timing-function:ease;overflow:hidden;}
|
|
288
|
+
#wg-network-log.wg-in{opacity:1;transform:translateY(0);}
|
|
289
|
+
#wg-network-log.wg-net-collapsed .wg-net-list{display:none;}
|
|
290
|
+
#wg-network-log .wg-net-header{display:flex;align-items:center;gap:6px;padding:8px 10px;border-bottom:1px solid rgba(124,58,237,.25);cursor:default;flex:0 0 auto;}
|
|
291
|
+
#wg-network-log .wg-net-title{font:700 10px/1.2 system-ui,sans-serif;color:#c9a6ff;letter-spacing:.06em;text-transform:uppercase;flex:1 1 auto;}
|
|
292
|
+
#wg-network-log .wg-net-count{font:700 10px/1.2 system-ui,sans-serif;color:#9CA3AF;background:rgba(124,58,237,.22);border-radius:999px;padding:1px 7px;}
|
|
293
|
+
#wg-network-log .wg-net-collapse{flex:0 0 auto;width:18px;height:18px;border-radius:5px;border:none;background:rgba(124,58,237,.28);color:#e8dcff;font:700 13px/1 system-ui,sans-serif;cursor:pointer;pointer-events:auto;}
|
|
294
|
+
#wg-network-log .wg-net-collapse:hover{background:rgba(124,58,237,.42);}
|
|
295
|
+
#wg-network-log .wg-net-list{list-style:none;margin:0;padding:6px 8px;display:flex;flex-direction:column;gap:2px;overflow-y:auto;pointer-events:auto;}
|
|
296
|
+
#wg-network-log .wg-net-row{display:flex;align-items:baseline;gap:7px;opacity:0;padding:3px 4px;border-radius:5px;cursor:pointer;transform:translateX(-4px);transition:opacity .2s ease,transform .2s ease;}
|
|
297
|
+
#wg-network-log .wg-net-row:hover,#wg-network-log .wg-net-row.wg-net-open{background:rgba(124,58,237,.18);}
|
|
298
|
+
#wg-network-log .wg-net-row.wg-in{opacity:1;transform:translateX(0);}
|
|
299
|
+
#wg-network-log .wg-net-method{flex:0 0 auto;font-weight:800;font-size:10px;min-width:38px;}
|
|
300
|
+
#wg-network-log .wg-net-method-get{color:#3B82F6;}
|
|
301
|
+
#wg-network-log .wg-net-method-post{color:#22C55E;}
|
|
302
|
+
#wg-network-log .wg-net-method-put{color:#F97316;}
|
|
303
|
+
#wg-network-log .wg-net-method-delete{color:#EF4444;}
|
|
304
|
+
#wg-network-log .wg-net-method-other{color:#9CA3AF;}
|
|
305
|
+
#wg-network-log .wg-net-path{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#e0e0f0;}
|
|
306
|
+
#wg-network-log .wg-net-status{flex:0 0 auto;font-weight:700;color:#22C55E;}
|
|
307
|
+
#wg-network-log .wg-net-row[data-ok="0"] .wg-net-status{color:#EF4444;}
|
|
308
|
+
#wg-network-log .wg-net-ms{flex:0 0 auto;color:#9CA3AF;font-size:10px;}
|
|
309
|
+
#wg-network-log .wg-net-detail{list-style:none;background:rgba(0,0,0,.28);border-radius:6px;padding:8px;margin:2px 0 6px;}
|
|
310
|
+
#wg-network-log .wg-net-detail-section{margin-bottom:8px;}
|
|
311
|
+
#wg-network-log .wg-net-detail-section:last-child{margin-bottom:0;}
|
|
312
|
+
#wg-network-log .wg-net-detail-section h4{margin:0 0 4px;font:700 9px/1.2 system-ui,sans-serif;color:#9CA3AF;letter-spacing:.06em;text-transform:uppercase;}
|
|
313
|
+
#wg-network-log .wg-net-headers{width:100%;border-collapse:collapse;font-size:11px;}
|
|
314
|
+
#wg-network-log .wg-net-headers td{padding:1px 6px 1px 0;vertical-align:top;word-break:break-all;}
|
|
315
|
+
#wg-network-log .wg-net-headers td:first-child{color:#c9a6ff;white-space:nowrap;}
|
|
316
|
+
#wg-network-log .wg-net-body{margin:0;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:160px;overflow-y:auto;color:#e0e0f0;}
|
|
317
|
+
#wg-network-log .wg-net-curl{color:#7dd3fc;}
|
|
318
|
+
#wg-network-log .wg-net-empty{font-size:11px;color:#6b7280;font-style:italic;}
|
|
319
|
+
#wg-network-log .wg-net-copy{margin-top:5px;padding:3px 9px;border-radius:5px;border:1px solid rgba(124,58,237,.5);background:rgba(124,58,237,.22);color:#e8dcff;font:700 10px system-ui,sans-serif;cursor:pointer;}
|
|
320
|
+
#wg-network-log .wg-net-copy:hover{background:rgba(124,58,237,.38);}
|
|
@@ -15,18 +15,85 @@ export function installShadowRoot(overlaySheet) {
|
|
|
15
15
|
host?.remove();
|
|
16
16
|
host = document.createElement("div");
|
|
17
17
|
host.id = "wg-root";
|
|
18
|
-
// No box of its own:
|
|
18
|
+
// No box of its own: everything inside is in the fixed #wg-ui layer, so the host never affects page layout.
|
|
19
19
|
host.style.cssText = "display:contents";
|
|
20
20
|
host.attachShadow({ mode: "open" });
|
|
21
21
|
document.documentElement.appendChild(host);
|
|
22
22
|
}
|
|
23
23
|
return host.shadowRoot;
|
|
24
24
|
};
|
|
25
|
+
// The one layout (ui/css/layout.css): #wg-ui holds the stage (anchored things) and the chrome's slots. Where a chrome
|
|
26
|
+
// element goes is decided here, once, by its id and data-pos; anything not in the table is anchored (the stage).
|
|
27
|
+
const SLOTS = ["top-left", "top-center", "top-right", "left", "right", "bottom-left", "bottom-center", "bottom-right"];
|
|
28
|
+
const SLOT_OF = {
|
|
29
|
+
"wg-typing-badge": "bottom-center",
|
|
30
|
+
"wg-caption-bar": "bottom-center",
|
|
31
|
+
"wg-panel": "bottom-center",
|
|
32
|
+
"wg-auto-panel": "bottom-center",
|
|
33
|
+
"wg-network-log": "bottom-left",
|
|
34
|
+
"wg-pilot-activity": "bottom-left",
|
|
35
|
+
"wg-zoom-badge": "bottom-right",
|
|
36
|
+
"wg-pilot-fx-zoom": "bottom-right",
|
|
37
|
+
"wg-pilot-overlay": "bottom-right",
|
|
38
|
+
"wg-device-toast": "top-right",
|
|
39
|
+
"wg-device-badge": "top-right",
|
|
40
|
+
"wg-pilot-fx-device": "top-right",
|
|
41
|
+
};
|
|
42
|
+
const slotOf = (el) => {
|
|
43
|
+
if (!(el instanceof HTMLElement))
|
|
44
|
+
return undefined;
|
|
45
|
+
const pos = el.dataset.pos;
|
|
46
|
+
if (el.id === "wg-banner")
|
|
47
|
+
return pos === "center" || pos === "right" ? `top-${pos}` : "top-left";
|
|
48
|
+
if (el.classList.contains("wg-todo-dock") || el.id === "wg-todo-dock" || el.id === "wg-pilot-fx-todos")
|
|
49
|
+
return pos === "right" ? "right" : "left";
|
|
50
|
+
return SLOT_OF[el.id];
|
|
51
|
+
};
|
|
52
|
+
const ui = () => {
|
|
53
|
+
const r = root();
|
|
54
|
+
let layer = r.getElementById("wg-ui");
|
|
55
|
+
if (layer)
|
|
56
|
+
return layer;
|
|
57
|
+
layer = document.createElement("div");
|
|
58
|
+
layer.id = "wg-ui";
|
|
59
|
+
const stage = document.createElement("div");
|
|
60
|
+
stage.id = "wg-stage";
|
|
61
|
+
const chrome = document.createElement("div");
|
|
62
|
+
chrome.id = "wg-chrome";
|
|
63
|
+
for (const [row, slots] of [["top", SLOTS.slice(0, 3)], ["middle", SLOTS.slice(3, 5)], ["bottom", SLOTS.slice(5)]]) {
|
|
64
|
+
const rowEl = document.createElement("div");
|
|
65
|
+
rowEl.className = `wg-row wg-row-${row}`;
|
|
66
|
+
for (const name of slots) {
|
|
67
|
+
const slot = document.createElement("div");
|
|
68
|
+
slot.className = "wg-slot";
|
|
69
|
+
slot.dataset.slot = name;
|
|
70
|
+
rowEl.appendChild(slot);
|
|
71
|
+
}
|
|
72
|
+
chrome.appendChild(rowEl);
|
|
73
|
+
}
|
|
74
|
+
layer.append(stage, chrome);
|
|
75
|
+
r.appendChild(layer);
|
|
76
|
+
// A data-pos change (banner click, a dock flipping away from a ring) moves the element to its new slot.
|
|
77
|
+
new MutationObserver((records) => {
|
|
78
|
+
for (const rec of records)
|
|
79
|
+
if (rec.target instanceof Element && rec.target.parentElement?.classList.contains("wg-slot"))
|
|
80
|
+
place(rec.target);
|
|
81
|
+
}).observe(chrome, { subtree: true, attributes: true, attributeFilter: ["data-pos"] });
|
|
82
|
+
return layer;
|
|
83
|
+
};
|
|
84
|
+
const place = (node) => {
|
|
85
|
+
const layer = ui();
|
|
86
|
+
const slot = node instanceof Element ? slotOf(node) : undefined;
|
|
87
|
+
const parent = slot ? layer.querySelector(`[data-slot="${slot}"]`) : layer.querySelector("#wg-stage");
|
|
88
|
+
if (node.parentNode !== parent)
|
|
89
|
+
parent.appendChild(node);
|
|
90
|
+
return node;
|
|
91
|
+
};
|
|
25
92
|
w.__wgRoot = root;
|
|
26
93
|
w.__wgById = (id) => root().getElementById(id) || document.getElementById(id);
|
|
27
94
|
w.__wgQ = (sel) => root().querySelector(sel) || document.querySelector(sel);
|
|
28
95
|
w.__wgQA = (sel) => Array.from(root().querySelectorAll(sel)).concat(Array.from(document.querySelectorAll(sel)));
|
|
29
|
-
w.__wgAdd =
|
|
96
|
+
w.__wgAdd = place;
|
|
30
97
|
w.__wgCss = (css, key) => {
|
|
31
98
|
const r = root();
|
|
32
99
|
let style = Array.from(r.children).find((c) => c.tagName === "STYLE" && c.getAttribute("data-key") === key);
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { envFlag } from "../../engine/env.js";
|
|
2
|
-
import { hasAuthoredStubAfter,
|
|
2
|
+
import { hasAuthoredStubAfter, runStubPhase } from "../narration/narration.js";
|
|
3
3
|
import { applyHighlightStyleDefaults, formatHighlightCaption } from "../narration/style.js";
|
|
4
4
|
import { logBannerDom, probeBanner } from "./banner-log.js";
|
|
5
5
|
import { logDocks } from "./before.js";
|
|
6
6
|
import { demoLog, logStubPhaseFixtures, logTodoDockFull, paceSpeakFields, summarizeDevice } from "./demo-log.js";
|
|
7
7
|
import { renderAfterStep } from "./panel-after.js";
|
|
8
|
-
import {
|
|
8
|
+
import { showSlides } from "./slides.js";
|
|
9
9
|
import { extractVerifyHighlights } from "./step-panels.js";
|
|
10
10
|
import { forceCollapsed } from "./view.js";
|
|
11
11
|
const quiet = () => envFlag("WAYGRAPH_JSON");
|
|
12
12
|
export async function showAfter(ctx, view, result) {
|
|
13
13
|
const { stage, refs } = ctx;
|
|
14
|
-
await showSlides(ctx, view, result);
|
|
15
14
|
const after = hasAuthoredStubAfter(view.step.block, result) ? await narrateAfter(ctx, view, result) : undefined;
|
|
15
|
+
if (after)
|
|
16
|
+
await showSlides(ctx, view, after.phase.slides);
|
|
16
17
|
const applied = after ? await stage.applyPhase(after.phase) : { appliedTodo: { sync: "keep" }, appliedDevice: { sync: "keep" } };
|
|
17
18
|
await renderAfterStep(ctx.page, {
|
|
18
19
|
index: view.index,
|
|
@@ -62,20 +63,6 @@ export async function showAfter(ctx, view, result) {
|
|
|
62
63
|
}
|
|
63
64
|
await ctx.gate.wait();
|
|
64
65
|
}
|
|
65
|
-
async function showSlides(ctx, view, result) {
|
|
66
|
-
const slides = resolveSlides(view.step.block, { out: result }).map((s) => applyHighlightStyleDefaults(s, view.step.highlightStyle));
|
|
67
|
-
if (slides.length === 0)
|
|
68
|
-
return;
|
|
69
|
-
await presentSlides(ctx.page, slides, {
|
|
70
|
-
title: ctx.title,
|
|
71
|
-
blockName: view.step.block.name,
|
|
72
|
-
fast: ctx.pacing.gatesFast,
|
|
73
|
-
pace: ctx.pacing.demoPace,
|
|
74
|
-
episodeNumber: view.step.episodeNumber,
|
|
75
|
-
episodeTitle: view.step.episodeTitle,
|
|
76
|
-
autoplayMs: ctx.gate.autoplayMs,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
66
|
/** Runs the Block's stubAfter; its rings carry the flow's highlight style and a caption. */
|
|
80
67
|
async function narrateAfter(ctx, view, result) {
|
|
81
68
|
const style = view.step.highlightStyle;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Before a demo step runs: play the Block's stubBefore narration (rings, todos, device, title, caption) and show the
|
|
1
|
+
// Before a demo step runs: play the Block's stubBefore narration (slides, 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
3
|
import { envFlag } from "../../engine/env.js";
|
|
4
4
|
import { runStubPhase } from "../narration/narration.js";
|
|
@@ -6,6 +6,7 @@ import { applyHighlightStyleDefaults, formatHighlightCaption } from "../narratio
|
|
|
6
6
|
import { logBannerDom, probeBanner } from "./banner-log.js";
|
|
7
7
|
import { demoLog, logStubPhaseFixtures, logTodoDockFull, paceSpeakFields, summarizeDevice } from "./demo-log.js";
|
|
8
8
|
import { renderBeforeStep } from "./panel-before.js";
|
|
9
|
+
import { showSlides } from "./slides.js";
|
|
9
10
|
import { probeTodoDocksOnPage } from "./todo-advance.js";
|
|
10
11
|
import { forceCollapsed } from "./view.js";
|
|
11
12
|
const quiet = () => envFlag("WAYGRAPH_JSON");
|
|
@@ -13,6 +14,7 @@ export async function showBefore(ctx, view) {
|
|
|
13
14
|
const { stage } = ctx;
|
|
14
15
|
const phase = await narrateBefore(ctx, view);
|
|
15
16
|
const { appliedTodo } = await stage.applyPhase(phase, { reapplyDeviceOnKeep: true });
|
|
17
|
+
await showSlides(ctx, view, phase.slides);
|
|
16
18
|
if (phase.title && String(phase.title).trim())
|
|
17
19
|
ctx.stickyTitle = String(phase.title).trim();
|
|
18
20
|
view.overlayTitle = ctx.stickyTitle || (view.step.episodeTitle && String(view.step.episodeTitle).trim()) || ctx.title || "waygraph demo";
|
|
@@ -177,13 +177,13 @@ export function logStubPhaseFixtures(phaseName, phase) {
|
|
|
177
177
|
return;
|
|
178
178
|
const tag = "stub." + phaseName;
|
|
179
179
|
if (phase.title)
|
|
180
|
-
demoFixture(tag, "ctx.title(...)", JSON.stringify(phase.title));
|
|
180
|
+
demoFixture(tag, "ctx.ui.title(...)", JSON.stringify(phase.title));
|
|
181
181
|
if (phase.titlePos)
|
|
182
|
-
demoFixture(tag, "ctx.titlePos(...)", JSON.stringify(phase.titlePos));
|
|
182
|
+
demoFixture(tag, "ctx.ui.titlePos(...)", JSON.stringify(phase.titlePos));
|
|
183
183
|
if (phase.bannerUi)
|
|
184
|
-
demoFixture(tag, "ctx.bannerUi(...)", JSON.stringify(phase.bannerUi));
|
|
184
|
+
demoFixture(tag, "ctx.ui.bannerUi(...)", JSON.stringify(phase.bannerUi));
|
|
185
185
|
if (phase.deviceSync && phase.deviceSync !== "keep") {
|
|
186
|
-
demoFixture(tag, phase.deviceSync === "clear" ? "ctx.hideDevice()" : "ctx.device(...)", phase.device
|
|
186
|
+
demoFixture(tag, phase.deviceSync === "clear" ? "ctx.ui.hideDevice()" : "ctx.ui.device(...)", phase.device
|
|
187
187
|
? {
|
|
188
188
|
preset: phase.device.preset,
|
|
189
189
|
w: phase.device.viewport && phase.device.viewport.width,
|
|
@@ -194,11 +194,11 @@ export function logStubPhaseFixtures(phaseName, phase) {
|
|
|
194
194
|
: phase.deviceSync);
|
|
195
195
|
}
|
|
196
196
|
if (phase.zoom != null)
|
|
197
|
-
demoFixture(tag, "ctx.zoom(" + phase.zoom + ")", phase.zoom + "x");
|
|
197
|
+
demoFixture(tag, "ctx.ui.zoom(" + phase.zoom + ")", phase.zoom + "x");
|
|
198
198
|
if (phase.zoomOut !== undefined)
|
|
199
|
-
demoFixture(tag, "ctx.zoomOut(" + !!phase.zoomOut + ")", !!phase.zoomOut);
|
|
199
|
+
demoFixture(tag, "ctx.ui.zoomOut(" + !!phase.zoomOut + ")", !!phase.zoomOut);
|
|
200
200
|
if (phase.todoSync && phase.todoSync !== "keep") {
|
|
201
|
-
demoFixture(tag, phase.todoSync === "clear" ? "ctx.hideTodos() / clear" : "ctx.todos(...)", phase.todoDock
|
|
201
|
+
demoFixture(tag, phase.todoSync === "clear" ? "ctx.ui.hideTodos() / clear" : "ctx.ui.todos(...)", phase.todoDock
|
|
202
202
|
? {
|
|
203
203
|
id: phase.todoDock.id,
|
|
204
204
|
pos: phase.todoDock.pos,
|
|
@@ -208,7 +208,7 @@ export function logStubPhaseFixtures(phaseName, phase) {
|
|
|
208
208
|
: phase.todoSync);
|
|
209
209
|
}
|
|
210
210
|
phase.highlights.forEach((h, i) => {
|
|
211
|
-
demoFixture(tag, "ctx.ring
|
|
211
|
+
demoFixture(tag, "ctx.ui.ring[" + i + "]", {
|
|
212
212
|
selector: h.selector,
|
|
213
213
|
label: h.label,
|
|
214
214
|
detail: h.detail,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { installOverlay } from "../overlay/overlay.js";
|
|
2
|
-
import { applyHighlightZoom, captionForLocator, dwellMatchedStub, ensureLocatorInView, hideRing, matchStubIndexForLocator, showRing
|
|
2
|
+
import { applyHighlightZoom, captionForLocator, dwellMatchedStub, ensureLocatorInView, hideRing, matchStubIndexForLocator, showRing } from "../overlay/rings.js";
|
|
3
3
|
import { syncTodoDockAdvance } from "./todo-advance.js";
|
|
4
4
|
import { clickPulseAt, moveCursorTo } from "../overlay/cursor.js";
|
|
5
5
|
import { demoHighlight } from "./demo-log.js";
|
|
@@ -109,7 +109,7 @@ export function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stu
|
|
|
109
109
|
catch {
|
|
110
110
|
/* ignore */
|
|
111
111
|
}
|
|
112
|
-
if (box
|
|
112
|
+
if (box) {
|
|
113
113
|
// Only trust the "last mem.get()" as THIS fill's source if it
|
|
114
114
|
// happened recently - a stale read from several actions ago is
|
|
115
115
|
// more likely unrelated than actually describing this field.
|
|
@@ -148,11 +148,6 @@ export function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stu
|
|
|
148
148
|
if (cap && cap.label)
|
|
149
149
|
typeLabel = String(cap.label).slice(0, 40);
|
|
150
150
|
}
|
|
151
|
-
else if (box) {
|
|
152
|
-
// Ring/caption already handled by narrate() - still move the
|
|
153
|
-
// cursor there, just skip re-showing the ring with a generic label.
|
|
154
|
-
await moveCursorTo(page, box, cursorMs(200));
|
|
155
|
-
}
|
|
156
151
|
await page
|
|
157
152
|
.evaluate((label) => {
|
|
158
153
|
if (window.__wgSetTypingBadge)
|
|
@@ -191,8 +186,7 @@ export function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stu
|
|
|
191
186
|
// own click scroll is instant and reads as a teleport under --fast.
|
|
192
187
|
await ensureLocatorInView(locator, { instant: skipTheater() });
|
|
193
188
|
const box = await locator.boundingBox();
|
|
194
|
-
|
|
195
|
-
if (box && !narrated) {
|
|
189
|
+
if (box) {
|
|
196
190
|
let fallback = "click";
|
|
197
191
|
try {
|
|
198
192
|
const text = (await locator.textContent())?.trim();
|
|
@@ -241,12 +235,6 @@ export function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stu
|
|
|
241
235
|
// services/help-center-clip-engine's video-pipeline.
|
|
242
236
|
await new Promise((res) => setTimeout(res, clickPrePop()));
|
|
243
237
|
}
|
|
244
|
-
else if (box) {
|
|
245
|
-
// Ring/caption already handled by narrate() - still move the
|
|
246
|
-
// cursor + pulse the click point, just skip re-showing the ring.
|
|
247
|
-
clickPoint = await moveCursorTo(page, box, cursorMs(200));
|
|
248
|
-
clickTone = "planned";
|
|
249
|
-
}
|
|
250
238
|
if (clickPoint) {
|
|
251
239
|
await clickPulseAt(page, clickPoint.x, clickPoint.y, clickTone);
|
|
252
240
|
// clickPulseAt only triggers the CSS animation class - it doesn't
|
|
@@ -21,7 +21,7 @@ export async function renderAfterStep(page, info) {
|
|
|
21
21
|
// Cycle through EVERY declared/recovered highlight in order, each shown
|
|
22
22
|
// long enough to actually register - "it highlights something [...] then
|
|
23
23
|
// it highlights something [else] and next," not just the first one.
|
|
24
|
-
// duration / fastMode on stubAfter
|
|
24
|
+
// duration / fastMode on stubAfter rings override the default
|
|
25
25
|
// 900ms / 200ms holds when set.
|
|
26
26
|
await cycleHighlightRings(page, highlights, gatesFast, {
|
|
27
27
|
pace: info.pace,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { WgPage } from "../types.js";
|
|
2
2
|
import { type WaygraphSlide } from "../../engine/stub/ring.js";
|
|
3
3
|
import type { DemoPace } from "../../engine/stub/pace.js";
|
|
4
|
+
import type { StepperContext, StepView } from "./view.js";
|
|
4
5
|
/** Where the slides sit (title, Block, episode) and how fast they advance. */
|
|
5
6
|
export interface SlidesOptions {
|
|
6
7
|
title?: string | undefined;
|
|
@@ -21,3 +22,5 @@ export interface SlidesOptions {
|
|
|
21
22
|
* doing nothing. In-page data-wg-acked + autoplay poll is the source of truth.
|
|
22
23
|
*/
|
|
23
24
|
export declare function presentSlides(page: WgPage, slides: readonly WaygraphSlide[], opts?: SlidesOptions): Promise<void>;
|
|
25
|
+
/** Plays a stub phase's `ctx.ui.slides(...)` for the step on screen, in the flow's highlight style. */
|
|
26
|
+
export declare function showSlides(ctx: StepperContext, view: StepView, slides: readonly WaygraphSlide[]): Promise<void>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeDemoPace, resolveFixtureDwellMs } from "../narration/demo-pace.js";
|
|
2
|
-
import { formatHighlightCaption } from "../narration/style.js";
|
|
3
2
|
import { normalizeHighlightSize, normalizeHighlightTone, normalizeHighlightWeight } from "../../engine/stub/ring.js";
|
|
4
3
|
import { installOverlay } from "../overlay/overlay.js";
|
|
5
4
|
import { applyHighlightZoom, ensureSelectorInView, hideRing, showRing } from "../overlay/rings.js";
|
|
6
|
-
import { demoPaceKind } from "./demo-log.js";
|
|
5
|
+
import { demoLog, demoPaceKind } from "./demo-log.js";
|
|
7
6
|
import { envFlag, envNumber, envString } from "../../engine/env.js";
|
|
7
|
+
import { applyHighlightStyleDefaults, formatHighlightCaption } from "../narration/style.js";
|
|
8
8
|
/**
|
|
9
9
|
* Multi-step yap captions - not block lifecycle. Each slide waits for Next
|
|
10
10
|
* (or auto-next), optional ring via slide.selector.
|
|
@@ -231,3 +231,19 @@ export async function presentSlides(page, slides, opts) {
|
|
|
231
231
|
}
|
|
232
232
|
await hideRing(page);
|
|
233
233
|
}
|
|
234
|
+
/** Plays a stub phase's `ctx.ui.slides(...)` for the step on screen, in the flow's highlight style. */
|
|
235
|
+
export async function showSlides(ctx, view, slides) {
|
|
236
|
+
if (slides.length === 0)
|
|
237
|
+
return;
|
|
238
|
+
if (!envFlag("WAYGRAPH_JSON"))
|
|
239
|
+
demoLog(` slides ${slides.length}: ` + slides.map((s) => JSON.stringify(s.caption)).join(" | "));
|
|
240
|
+
await presentSlides(ctx.page, slides.map((s) => applyHighlightStyleDefaults(s, view.step.highlightStyle)), {
|
|
241
|
+
title: ctx.title,
|
|
242
|
+
blockName: view.step.block.name,
|
|
243
|
+
fast: ctx.pacing.gatesFast,
|
|
244
|
+
pace: ctx.pacing.demoPace,
|
|
245
|
+
episodeNumber: view.step.episodeNumber,
|
|
246
|
+
episodeTitle: view.step.episodeTitle,
|
|
247
|
+
autoplayMs: ctx.gate.autoplayMs,
|
|
248
|
+
});
|
|
249
|
+
}
|