waygraph 0.12.47 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +195 -8
- package/dist/highlights.d.ts +84 -0
- package/dist/highlights.js +214 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +2 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +4 -1
- package/package.json +1 -1
- package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +2 -0
- package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +4 -1
package/dist/cli.js
CHANGED
|
@@ -230,6 +230,8 @@ import {
|
|
|
230
230
|
applyTodoPhase,
|
|
231
231
|
advanceTodoDock,
|
|
232
232
|
completeSequentialTodoDock,
|
|
233
|
+
applyDevicePhase,
|
|
234
|
+
resolveDeviceState,
|
|
233
235
|
} from "waygraph";
|
|
234
236
|
|
|
235
237
|
function walkDir(dir, pattern) {
|
|
@@ -530,6 +532,18 @@ const RING_CSS =
|
|
|
530
532
|
"#wg-todo-dock[data-pos=right]{transform:translateX(calc(100vw - 100% - 28px));}" +
|
|
531
533
|
"#wg-todo-dock[data-pos=left]{transform:translateX(0);}" +
|
|
532
534
|
"#wg-todo-dock #wg-todos{list-style:none;margin:0;padding:0;background:transparent;border:none;}" +
|
|
535
|
+
// Device / touch badge (0.13+) - shows preset + remain|desktop reset.
|
|
536
|
+
"#wg-device-badge{position:fixed;z-index:2147483646;top:14px;right:14px;" +
|
|
537
|
+
"padding:6px 10px;border-radius:999px;background:rgba(20,10,40,.92);color:#f0e8ff;" +
|
|
538
|
+
"border:1px solid rgba(124,58,237,.5);font:700 11px/1.2 system-ui,sans-serif;" +
|
|
539
|
+
"letter-spacing:.04em;text-transform:uppercase;pointer-events:none;" +
|
|
540
|
+
"box-shadow:0 4px 14px rgba(0,0,0,.3);opacity:0;transition:opacity .25s ease;}" +
|
|
541
|
+
"#wg-device-badge.wg-in{opacity:1;}" +
|
|
542
|
+
"#wg-device-badge[data-preset=mobile]{border-color:#3B82F6;}" +
|
|
543
|
+
"#wg-device-badge[data-preset=tablet]{border-color:#22C55E;}" +
|
|
544
|
+
"#wg-device-badge[data-preset=desktop]{border-color:#9CA3AF;}" +
|
|
545
|
+
"#wg-device-badge[data-touch=1]::after{content:' · touch';opacity:.85;}" +
|
|
546
|
+
"#wg-cursor[data-touch=1]{width:28px;height:28px;}" +
|
|
533
547
|
"#wg-todos{list-style:none;margin:0 0 12px;padding:8px 10px;background:#0f0620;border-radius:8px;" +
|
|
534
548
|
"border:1px solid #3a2a60;}" +
|
|
535
549
|
"#wg-todos li{display:flex;gap:8px;align-items:flex-start;margin:0 0 6px;font:600 12.5px/1.35 system-ui,sans-serif;}" +
|
|
@@ -1252,6 +1266,64 @@ async function installOverlay(page, title) {
|
|
|
1252
1266
|
window.__wgStampModal(dock, "todos", { ready: true });
|
|
1253
1267
|
}
|
|
1254
1268
|
};
|
|
1269
|
+
// Device / touch badge (0.13+) - mirror todo sync keep/set/clear.
|
|
1270
|
+
// Payload: { sync, device?, remain? }
|
|
1271
|
+
window.__wgSyncDevice = (payload) => {
|
|
1272
|
+
const sync =
|
|
1273
|
+
payload && (payload.sync === "clear" || payload.sync === "keep" || payload.sync === "set")
|
|
1274
|
+
? payload.sync
|
|
1275
|
+
: "keep";
|
|
1276
|
+
const device = payload && payload.device ? payload.device : null;
|
|
1277
|
+
let badge = document.getElementById("wg-device-badge");
|
|
1278
|
+
const cursor = document.getElementById("wg-cursor");
|
|
1279
|
+
const MOUSE_SVG =
|
|
1280
|
+
"<svg viewBox='0 0 32 32' width='24' height='24'>" +
|
|
1281
|
+
"<path fill='#0C0C1A' stroke='#fff' stroke-width='1.4' stroke-linejoin='round' " +
|
|
1282
|
+
"d='M6 3.5l1.4 22.5 5.8-5.4 4.2 9.4 3.6-1.6-4.2-9.2H26z'/></svg>";
|
|
1283
|
+
const FINGER_SVG =
|
|
1284
|
+
"<svg viewBox='0 0 32 32' width='28' height='28'>" +
|
|
1285
|
+
"<ellipse cx='16' cy='22' rx='7' ry='8' fill='#0C0C1A' stroke='#fff' stroke-width='1.4'/>" +
|
|
1286
|
+
"<rect x='12' y='6' width='8' height='16' rx='4' fill='#0C0C1A' stroke='#fff' stroke-width='1.4'/>" +
|
|
1287
|
+
"</svg>";
|
|
1288
|
+
if (sync === "keep" && !device) return;
|
|
1289
|
+
if (sync === "clear" || (device && device.preset === "desktop" && !device.touchMode)) {
|
|
1290
|
+
if (badge) badge.remove();
|
|
1291
|
+
if (cursor) {
|
|
1292
|
+
cursor.removeAttribute("data-touch");
|
|
1293
|
+
cursor.innerHTML = MOUSE_SVG;
|
|
1294
|
+
}
|
|
1295
|
+
if (sync === "clear" && !device) return;
|
|
1296
|
+
// clear with desktop device: still hide badge (back to main)
|
|
1297
|
+
if (device && device.preset === "desktop" && !device.touchMode) return;
|
|
1298
|
+
}
|
|
1299
|
+
if (!device) return;
|
|
1300
|
+
const preset = device.preset || "desktop";
|
|
1301
|
+
const touch = !!device.touchMode;
|
|
1302
|
+
const remain = payload.remain !== false && sync !== "clear";
|
|
1303
|
+
if (!badge) {
|
|
1304
|
+
badge = document.createElement("div");
|
|
1305
|
+
badge.id = "wg-device-badge";
|
|
1306
|
+
badge.setAttribute("data-wg-ui", "1");
|
|
1307
|
+
document.documentElement.appendChild(badge);
|
|
1308
|
+
}
|
|
1309
|
+
badge.dataset.preset = preset;
|
|
1310
|
+
badge.dataset.touch = touch ? "1" : "0";
|
|
1311
|
+
const label =
|
|
1312
|
+
preset === "desktop" && !touch
|
|
1313
|
+
? "desktop"
|
|
1314
|
+
: preset + (remain ? " · remain" : " · set");
|
|
1315
|
+
badge.textContent = label;
|
|
1316
|
+
badge.classList.add("wg-in");
|
|
1317
|
+
if (cursor) {
|
|
1318
|
+
if (touch) {
|
|
1319
|
+
cursor.setAttribute("data-touch", "1");
|
|
1320
|
+
cursor.innerHTML = FINGER_SVG;
|
|
1321
|
+
} else {
|
|
1322
|
+
cursor.removeAttribute("data-touch");
|
|
1323
|
+
cursor.innerHTML = MOUSE_SVG;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1255
1327
|
},
|
|
1256
1328
|
{ title, favicon: WAYGRAPH_FAVICON, bannerPos, todoPos, envAutoplay },
|
|
1257
1329
|
)
|
|
@@ -1725,6 +1797,7 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1725
1797
|
const defaultHoldMs = opts && opts.defaultHoldMs != null ? opts.defaultHoldMs : null;
|
|
1726
1798
|
const pace = opts && opts.pace !== undefined ? opts.pace : undefined;
|
|
1727
1799
|
let dock = opts && opts.todoDock;
|
|
1800
|
+
const advanceTodos = !(opts && opts.advanceTodos === false);
|
|
1728
1801
|
// Push stubAfter/before dock before the first ring so Method after-panels
|
|
1729
1802
|
// show the updated checklist immediately (not only after the ring cycle).
|
|
1730
1803
|
if (dock) {
|
|
@@ -1751,7 +1824,10 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1751
1824
|
size: h.size,
|
|
1752
1825
|
weight: h.weight,
|
|
1753
1826
|
});
|
|
1754
|
-
|
|
1827
|
+
// Do NOT advanceTodoDock by ring index after stubAfter already set
|
|
1828
|
+
// progress - that reset current back to 0 and the next block looked
|
|
1829
|
+
// blank. Mid-act Method advance still uses syncTodoDockAdvance.
|
|
1830
|
+
if (dock && advanceTodos) {
|
|
1755
1831
|
dock = advanceTodoDock(dock, i);
|
|
1756
1832
|
if (opts && opts.todoDockRef) opts.todoDockRef.current = dock;
|
|
1757
1833
|
await page
|
|
@@ -1759,7 +1835,7 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1759
1835
|
if (window.__wgSyncTodos) window.__wgSyncTodos({ sync: "set", dock: d });
|
|
1760
1836
|
}, dock)
|
|
1761
1837
|
.catch(() => {});
|
|
1762
|
-
} else if ((opts && opts.todos && opts.todos.length) || (h.todos && h.todos.length)) {
|
|
1838
|
+
} else if (!dock && ((opts && opts.todos && opts.todos.length) || (h.todos && h.todos.length))) {
|
|
1763
1839
|
const rows =
|
|
1764
1840
|
opts && opts.todos && opts.todos.length
|
|
1765
1841
|
? opts.todos
|
|
@@ -1863,7 +1939,14 @@ async function renderAfterStep(page, info) {
|
|
|
1863
1939
|
todoDock: info.todoDock,
|
|
1864
1940
|
todos: info.todos,
|
|
1865
1941
|
todoSync: info.todoSync,
|
|
1942
|
+
todoDockRef: info.todoDockRef,
|
|
1943
|
+
// stubAfter already authored progress - do not re-index by ring 0..n
|
|
1944
|
+
advanceTodos: false,
|
|
1866
1945
|
});
|
|
1946
|
+
// After-ring advances may have moved the dock - surface latest for caller.
|
|
1947
|
+
if (info.todoDockRef && info.todoDockRef.current) {
|
|
1948
|
+
info.todoDock = info.todoDockRef.current;
|
|
1949
|
+
}
|
|
1867
1950
|
const afterPayload = { ...info, stepLabel: miniStepLabel(info) };
|
|
1868
1951
|
await page
|
|
1869
1952
|
.evaluate((info) => {
|
|
@@ -2670,6 +2753,49 @@ async function clickPulseAt(page, x, y, tone) {
|
|
|
2670
2753
|
.catch(() => {});
|
|
2671
2754
|
}
|
|
2672
2755
|
|
|
2756
|
+
/**
|
|
2757
|
+
* Apply authored device fixture to the live page (0.13+).
|
|
2758
|
+
* Uses the real setViewportSize (step mode noops block-authored calls).
|
|
2759
|
+
* sync keep with no device = no-op; clear / desktop = restore main feel.
|
|
2760
|
+
*/
|
|
2761
|
+
async function applyDeviceToPage(page, device, sync) {
|
|
2762
|
+
const mode = sync || (device ? "set" : "keep");
|
|
2763
|
+
if (mode === "keep" && !device) return;
|
|
2764
|
+
const d =
|
|
2765
|
+
device ||
|
|
2766
|
+
resolveDeviceState("desktop", false) || {
|
|
2767
|
+
preset: "desktop",
|
|
2768
|
+
viewport: { width: 1280, height: 720, deviceScaleFactor: 1 },
|
|
2769
|
+
isMobile: false,
|
|
2770
|
+
hasTouch: false,
|
|
2771
|
+
touchMode: false,
|
|
2772
|
+
};
|
|
2773
|
+
const realSet =
|
|
2774
|
+
typeof page.__wgRealSetViewportSize === "function"
|
|
2775
|
+
? page.__wgRealSetViewportSize
|
|
2776
|
+
: page.setViewportSize.bind(page);
|
|
2777
|
+
try {
|
|
2778
|
+
await realSet({
|
|
2779
|
+
width: d.viewport.width,
|
|
2780
|
+
height: d.viewport.height,
|
|
2781
|
+
});
|
|
2782
|
+
} catch {
|
|
2783
|
+
/* maximized / null viewport hosts may reject - badge still updates */
|
|
2784
|
+
}
|
|
2785
|
+
await page
|
|
2786
|
+
.evaluate(
|
|
2787
|
+
(payload) => {
|
|
2788
|
+
if (window.__wgSyncDevice) window.__wgSyncDevice(payload);
|
|
2789
|
+
},
|
|
2790
|
+
{
|
|
2791
|
+
sync: mode,
|
|
2792
|
+
device: d,
|
|
2793
|
+
remain: mode === "keep" || mode === "set",
|
|
2794
|
+
},
|
|
2795
|
+
)
|
|
2796
|
+
.catch(() => {});
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2673
2799
|
/**
|
|
2674
2800
|
* True if the engine's own narrate() (waygraph's public export, for a Block
|
|
2675
2801
|
* author explicitly captioning one action) JUST positioned the ring for
|
|
@@ -2686,7 +2812,7 @@ async function wasJustNarrated(page) {
|
|
|
2686
2812
|
.catch(() => false);
|
|
2687
2813
|
}
|
|
2688
2814
|
|
|
2689
|
-
function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef, todoDockRef) {
|
|
2815
|
+
function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef, todoDockRef, deviceRef) {
|
|
2690
2816
|
// Playwright's own slowMo ALREADY pauses after every single low-level
|
|
2691
2817
|
// action it dispatches - and pressSequentially() fires one such action
|
|
2692
2818
|
// PER CHARACTER. Also giving pressSequentially its own fixed delay
|
|
@@ -2850,7 +2976,36 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
2850
2976
|
// mock click visually completes before the real one fires.
|
|
2851
2977
|
await new Promise((res) => setTimeout(res, skipTheater() ? 0 : 500));
|
|
2852
2978
|
}
|
|
2853
|
-
|
|
2979
|
+
// Touch theater (0.13+): prefer Locator.tap / long-press when device
|
|
2980
|
+
// touchMode is on (context launched with hasTouch). gesture on stub
|
|
2981
|
+
// wins: tap | hold | click.
|
|
2982
|
+
let result;
|
|
2983
|
+
const stubsForGesture = (stubBeforeRef && stubBeforeRef.current) || [];
|
|
2984
|
+
let gesture = "click";
|
|
2985
|
+
try {
|
|
2986
|
+
const idx = await matchStubIndexForLocator(page, this, stubsForGesture);
|
|
2987
|
+
const stub = idx >= 0 ? stubsForGesture[idx] : null;
|
|
2988
|
+
if (stub && stub.gesture) gesture = stub.gesture;
|
|
2989
|
+
else if (deviceRef && deviceRef.current && deviceRef.current.touchMode) gesture = "tap";
|
|
2990
|
+
} catch {
|
|
2991
|
+
if (deviceRef && deviceRef.current && deviceRef.current.touchMode) gesture = "tap";
|
|
2992
|
+
}
|
|
2993
|
+
if (gesture === "tap" || gesture === "hold") {
|
|
2994
|
+
try {
|
|
2995
|
+
if (gesture === "hold") {
|
|
2996
|
+
result = await this.tap({
|
|
2997
|
+
...(options || {}),
|
|
2998
|
+
delay: skipTheater() ? 0 : 600,
|
|
2999
|
+
});
|
|
3000
|
+
} else {
|
|
3001
|
+
result = await this.tap(options);
|
|
3002
|
+
}
|
|
3003
|
+
} catch {
|
|
3004
|
+
result = await originalClick.call(this, options);
|
|
3005
|
+
}
|
|
3006
|
+
} else {
|
|
3007
|
+
result = await originalClick.call(this, options);
|
|
3008
|
+
}
|
|
2854
3009
|
await new Promise((res) => setTimeout(res, clickPostPop()));
|
|
2855
3010
|
await hideRing(page);
|
|
2856
3011
|
return result;
|
|
@@ -2960,7 +3115,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
2960
3115
|
const stubBeforeRef = { current: [] };
|
|
2961
3116
|
/** Live todo dock for Method fill/click advance (same carry as lastTodoDock). */
|
|
2962
3117
|
const todoDockRef = { current: undefined };
|
|
2963
|
-
|
|
3118
|
+
/** Live device fixture for touch theater (same carry as lastDevice). */
|
|
3119
|
+
const deviceRef = { current: undefined };
|
|
3120
|
+
instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef, todoDockRef, deviceRef);
|
|
2964
3121
|
// Force the panel checkbox from this process's flags/env at run start.
|
|
2965
3122
|
// installOverlay only seeds localStorage when the key is null (so mid-run
|
|
2966
3123
|
// checkbox clicks survive navigations). Without this, a prior --autoplay
|
|
@@ -2991,7 +3148,8 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
2991
3148
|
// setViewportSize entirely for the duration of step mode. Blocks that
|
|
2992
3149
|
// call it don't need it to actually do anything here - their own
|
|
2993
3150
|
// selectors/layouts still work fine at whatever size the real window
|
|
2994
|
-
// already is.
|
|
3151
|
+
// already is. Authored ctx.device() still uses __wgRealSetViewportSize.
|
|
3152
|
+
page.__wgRealSetViewportSize = page.setViewportSize.bind(page);
|
|
2995
3153
|
page.setViewportSize = async () => {};
|
|
2996
3154
|
let resolveNext = null;
|
|
2997
3155
|
// Render-to-gate race: a slide/step panel's Next button is wired to
|
|
@@ -3089,6 +3247,8 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3089
3247
|
let result;
|
|
3090
3248
|
/** Carry floating todo dock across steps (empty stubBefore must not wipe). */
|
|
3091
3249
|
let lastTodoDock = undefined;
|
|
3250
|
+
/** Carry device / touch fixture across steps (omit = keep, like todos). */
|
|
3251
|
+
let lastDevice = undefined;
|
|
3092
3252
|
for (let i = 0; i < resolved.length; i++) {
|
|
3093
3253
|
const r = resolved[i];
|
|
3094
3254
|
// The block breadcrumb is scoped to THIS step's own episode, not the
|
|
@@ -3182,6 +3342,15 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3182
3342
|
});
|
|
3183
3343
|
lastTodoDock = appliedBefore.dock;
|
|
3184
3344
|
todoDockRef.current = lastTodoDock;
|
|
3345
|
+
const appliedDeviceBefore = applyDevicePhase(lastDevice, {
|
|
3346
|
+
deviceSync: stubBeforePhase.deviceSync,
|
|
3347
|
+
device: stubBeforePhase.device,
|
|
3348
|
+
});
|
|
3349
|
+
lastDevice = appliedDeviceBefore.device;
|
|
3350
|
+
deviceRef.current = lastDevice;
|
|
3351
|
+
if (appliedDeviceBefore.sync !== "keep" || lastDevice) {
|
|
3352
|
+
await applyDeviceToPage(page, lastDevice, appliedDeviceBefore.sync);
|
|
3353
|
+
}
|
|
3185
3354
|
const stubBeforeTodos =
|
|
3186
3355
|
(lastTodoDock && lastTodoDock.groups[0] && lastTodoDock.groups[0].items) ||
|
|
3187
3356
|
stubBeforePhase.todos ||
|
|
@@ -3339,6 +3508,10 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3339
3508
|
})
|
|
3340
3509
|
.catch(() => {});
|
|
3341
3510
|
result = stepOutcome.result;
|
|
3511
|
+
// Mid-act Method fill/click advances live on todoDockRef - fold that
|
|
3512
|
+
// back into lastTodoDock before stubAfter / next-block keep, or the
|
|
3513
|
+
// next step reverts to the pre-act (blank / index-0) checklist.
|
|
3514
|
+
if (todoDockRef.current) lastTodoDock = todoDockRef.current;
|
|
3342
3515
|
// withExpectedFailure last block that SUCCEEDS on the intentional fail
|
|
3343
3516
|
// branch (e.g. submit-login -> LoginPage + error banner). Still show
|
|
3344
3517
|
// stubOnError rings + amber "expected outcome" panel - branching no
|
|
@@ -3403,6 +3576,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3403
3576
|
let afterOverlayTitle = overlayTitle;
|
|
3404
3577
|
let afterTodoPos = overlayTodoPos;
|
|
3405
3578
|
let appliedAfter = { dock: lastTodoDock, sync: "keep" };
|
|
3579
|
+
let appliedDeviceAfter = { device: lastDevice, sync: "keep" };
|
|
3406
3580
|
if (hasAuthoredStubAfter(r.block, result, fixturesAfter)) {
|
|
3407
3581
|
const afterPhase = await runStubPhase(r.block, "stubAfter", {
|
|
3408
3582
|
out: result,
|
|
@@ -3416,6 +3590,15 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3416
3590
|
});
|
|
3417
3591
|
lastTodoDock = appliedAfter.dock;
|
|
3418
3592
|
todoDockRef.current = lastTodoDock;
|
|
3593
|
+
appliedDeviceAfter = applyDevicePhase(lastDevice, {
|
|
3594
|
+
deviceSync: afterPhase.deviceSync,
|
|
3595
|
+
device: afterPhase.device,
|
|
3596
|
+
});
|
|
3597
|
+
lastDevice = appliedDeviceAfter.device;
|
|
3598
|
+
deviceRef.current = lastDevice;
|
|
3599
|
+
if (appliedDeviceAfter.sync !== "keep" || (appliedDeviceAfter.sync === "set" && lastDevice)) {
|
|
3600
|
+
await applyDeviceToPage(page, lastDevice, appliedDeviceAfter.sync);
|
|
3601
|
+
}
|
|
3419
3602
|
stubAfterTodos =
|
|
3420
3603
|
(lastTodoDock && lastTodoDock.groups[0] && lastTodoDock.groups[0].items) ||
|
|
3421
3604
|
afterPhase.todos ||
|
|
@@ -3467,6 +3650,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3467
3650
|
todoDock: lastTodoDock,
|
|
3468
3651
|
todoSync: appliedAfter.sync,
|
|
3469
3652
|
todoId: lastTodoDock && lastTodoDock.id,
|
|
3653
|
+
todoDockRef,
|
|
3470
3654
|
forceCollapsed:
|
|
3471
3655
|
!!process.env.WAYGRAPH_VIDEO ||
|
|
3472
3656
|
process.env.WAYGRAPH_MINI === "1" ||
|
|
@@ -3474,6 +3658,8 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3474
3658
|
? true
|
|
3475
3659
|
: undefined,
|
|
3476
3660
|
});
|
|
3661
|
+
// Ring cycle / stubAfter may have advanced the dock - persist for next block.
|
|
3662
|
+
if (todoDockRef.current) lastTodoDock = todoDockRef.current;
|
|
3477
3663
|
await gate();
|
|
3478
3664
|
}
|
|
3479
3665
|
await teardownOverlay(page);
|
|
@@ -3862,10 +4048,11 @@ async function main() {
|
|
|
3862
4048
|
// Lock DPR so --video-viewport 800x450 stays 800x450 in the .webm
|
|
3863
4049
|
// (HiDPI hosts otherwise scale recordVideo to e.g. 1920x1080).
|
|
3864
4050
|
deviceScaleFactor: 1,
|
|
4051
|
+
hasTouch: true,
|
|
3865
4052
|
}
|
|
3866
4053
|
: step
|
|
3867
|
-
? { baseURL, viewport: null }
|
|
3868
|
-
: { baseURL, viewport: { width: 1280, height: 720 } };
|
|
4054
|
+
? { baseURL, viewport: null, hasTouch: true }
|
|
4055
|
+
: { baseURL, viewport: { width: 1280, height: 720 }, hasTouch: true };
|
|
3869
4056
|
if (videoDir) {
|
|
3870
4057
|
contextOpts.recordVideo = fixedVideoViewport
|
|
3871
4058
|
? { dir: videoDir, size: fixedVideoViewport }
|
package/dist/highlights.d.ts
CHANGED
|
@@ -117,6 +117,57 @@ export type StubPhaseFixtures = {
|
|
|
117
117
|
* Authored control (also click / WAYGRAPH_TODO_POS / --todo-left|right).
|
|
118
118
|
*/
|
|
119
119
|
todoPos?: "left" | "right";
|
|
120
|
+
/**
|
|
121
|
+
* Viewport preset: `mobile` | `tablet` | `desktop` (default / clear).
|
|
122
|
+
* Omit on later blocks = **keep** (same persist pattern as todos).
|
|
123
|
+
*/
|
|
124
|
+
device?: DevicePreset | DeviceState;
|
|
125
|
+
/** Explicit pixel viewport (wins over preset width/height when both set). */
|
|
126
|
+
viewport?: DeviceViewport;
|
|
127
|
+
/** Touch theater + Playwright touchscreen when true. */
|
|
128
|
+
touch?: boolean;
|
|
129
|
+
};
|
|
130
|
+
/** Named viewport presets (0.13+). */
|
|
131
|
+
export type DevicePreset = "mobile" | "tablet" | "desktop";
|
|
132
|
+
/** Explicit viewport box. */
|
|
133
|
+
export type DeviceViewport = {
|
|
134
|
+
width: number;
|
|
135
|
+
height: number;
|
|
136
|
+
deviceScaleFactor?: number;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Carried device fixture (todo-dock shaped persist).
|
|
140
|
+
* Omit on a later step = keep; hideDevice/clearDevice/desktop = clear to desktop.
|
|
141
|
+
*/
|
|
142
|
+
export type DeviceState = {
|
|
143
|
+
preset: DevicePreset;
|
|
144
|
+
viewport: DeviceViewport;
|
|
145
|
+
/** Playwright-ish: treat as mobile layout. */
|
|
146
|
+
isMobile: boolean;
|
|
147
|
+
/** Context needs hasTouch for touchscreen.tap (demo launches with hasTouch). */
|
|
148
|
+
hasTouch: boolean;
|
|
149
|
+
/** Demo gesture theater: finger cursor, tap/hold timing, prefer touchscreen. */
|
|
150
|
+
touchMode: boolean;
|
|
151
|
+
};
|
|
152
|
+
/** Built-in preset sizes (CSS pixels). */
|
|
153
|
+
export declare const DEVICE_PRESETS: Record<DevicePreset, Omit<DeviceState, "touchMode"> & {
|
|
154
|
+
touchMode?: boolean;
|
|
155
|
+
}>;
|
|
156
|
+
export declare function normalizeDevicePreset(raw: unknown): DevicePreset | undefined;
|
|
157
|
+
/** Resolve a preset name or partial state into a full DeviceState. */
|
|
158
|
+
export declare function resolveDeviceState(input: DevicePreset | DeviceState | DeviceViewport | undefined, touchOverride?: boolean): DeviceState | undefined;
|
|
159
|
+
/**
|
|
160
|
+
* Carry-forward for device fixtures (mirror {@link applyTodoPhase}).
|
|
161
|
+
* - clear: hideDevice / clearDevice / device("desktop") with clear intent
|
|
162
|
+
* - set: author set device/viewport/touch this phase
|
|
163
|
+
* - keep: omit - preserve previous (whole episode remain)
|
|
164
|
+
*/
|
|
165
|
+
export declare function applyDevicePhase(prev: DeviceState | undefined, phase: {
|
|
166
|
+
deviceSync?: "set" | "clear" | "keep";
|
|
167
|
+
device?: DeviceState;
|
|
168
|
+
}): {
|
|
169
|
+
device: DeviceState | undefined;
|
|
170
|
+
sync: "set" | "clear" | "keep";
|
|
120
171
|
};
|
|
121
172
|
/** One named highlight slot on a block (selector + caption). */
|
|
122
173
|
export interface WaygraphHighlightStub extends FixtureDurationFields {
|
|
@@ -176,6 +227,13 @@ export interface WaygraphHighlightStub extends FixtureDurationFields {
|
|
|
176
227
|
* for authored stub queues; set false to keep cursor hidden).
|
|
177
228
|
*/
|
|
178
229
|
cursor?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Pointer feel when touchMode is on (0.13+):
|
|
232
|
+
* - `tap` - short press via touchscreen (default in touch mode)
|
|
233
|
+
* - `hold` - long-press (~600ms) then release
|
|
234
|
+
* - `click` - mouse click even in touch mode
|
|
235
|
+
*/
|
|
236
|
+
gesture?: "tap" | "hold" | "click";
|
|
179
237
|
}
|
|
180
238
|
/** Flow fixture patch: label required; selector optional (inherits from stub). */
|
|
181
239
|
export type WaygraphHighlightFixture = Partial<Pick<WaygraphHighlightStub, "selector">> & Required<Pick<WaygraphHighlightStub, "label">> & Pick<WaygraphHighlightStub, "detail" | "tag" | "tone" | "size" | "weight" | "zoom" | "zoomOut" | "focus" | "followMouse" | "follow" | "color" | "cursor"> & FixtureDurationFields;
|
|
@@ -238,6 +296,23 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
238
296
|
hideTodos(): void;
|
|
239
297
|
/** Alias for {@link StubCtx.hideTodos}. */
|
|
240
298
|
clearTodos(): void;
|
|
299
|
+
/**
|
|
300
|
+
* Viewport / device fixture (0.13+). `mobile` | `tablet` | `desktop`.
|
|
301
|
+
* Omit on later blocks = **keep** (same persist as todos). Use
|
|
302
|
+
* {@link StubCtx.hideDevice} / {@link StubCtx.clearDevice} to return to desktop.
|
|
303
|
+
*/
|
|
304
|
+
device(preset: DevicePreset | DeviceState): void;
|
|
305
|
+
/** Explicit CSS-pixel viewport (implies mobile-ish unless desktop-sized). */
|
|
306
|
+
viewport(box: DeviceViewport): void;
|
|
307
|
+
/** Enable / disable touch theater + touchscreen gestures. */
|
|
308
|
+
touch(on?: boolean): void;
|
|
309
|
+
/**
|
|
310
|
+
* Reset to desktop viewport and clear touch mode. Omit device on a later
|
|
311
|
+
* block = **keep**; only hideDevice / clearDevice / device("desktop") clears.
|
|
312
|
+
*/
|
|
313
|
+
hideDevice(): void;
|
|
314
|
+
/** Alias for {@link StubCtx.hideDevice}. */
|
|
315
|
+
clearDevice(): void;
|
|
241
316
|
/** Default camera zoom for rings without their own zoom. */
|
|
242
317
|
zoom(n: number): void;
|
|
243
318
|
/**
|
|
@@ -280,6 +355,15 @@ export type StubPhaseResult = {
|
|
|
280
355
|
* - `keep` - author omitted todos; demo must leave previous dock alone
|
|
281
356
|
*/
|
|
282
357
|
todoSync?: "set" | "clear" | "keep";
|
|
358
|
+
/** Resolved device after this phase (undefined when keep with no prior). */
|
|
359
|
+
device?: DeviceState;
|
|
360
|
+
/**
|
|
361
|
+
* Device intent (mirror todoSync):
|
|
362
|
+
* - `set` - apply {@link device}
|
|
363
|
+
* - `clear` - reset to desktop (hideDevice / clearDevice / device desktop)
|
|
364
|
+
* - `keep` - author omitted device; demo must leave previous alone
|
|
365
|
+
*/
|
|
366
|
+
deviceSync?: "set" | "clear" | "keep";
|
|
283
367
|
zoom?: number;
|
|
284
368
|
zoomOut?: boolean;
|
|
285
369
|
title?: string;
|
package/dist/highlights.js
CHANGED
|
@@ -12,6 +12,123 @@
|
|
|
12
12
|
* All fixture shapes share optional {@link FixtureDurationFields} so demo dwell
|
|
13
13
|
* before advancing is authored the same way on stubs, flow fixtures, and slides.
|
|
14
14
|
*/
|
|
15
|
+
/** Built-in preset sizes (CSS pixels). */
|
|
16
|
+
export const DEVICE_PRESETS = {
|
|
17
|
+
mobile: {
|
|
18
|
+
preset: "mobile",
|
|
19
|
+
viewport: { width: 390, height: 844, deviceScaleFactor: 2 },
|
|
20
|
+
isMobile: true,
|
|
21
|
+
hasTouch: true,
|
|
22
|
+
touchMode: true,
|
|
23
|
+
},
|
|
24
|
+
tablet: {
|
|
25
|
+
preset: "tablet",
|
|
26
|
+
viewport: { width: 768, height: 1024, deviceScaleFactor: 2 },
|
|
27
|
+
isMobile: true,
|
|
28
|
+
hasTouch: true,
|
|
29
|
+
touchMode: true,
|
|
30
|
+
},
|
|
31
|
+
desktop: {
|
|
32
|
+
preset: "desktop",
|
|
33
|
+
viewport: { width: 1280, height: 720, deviceScaleFactor: 1 },
|
|
34
|
+
isMobile: false,
|
|
35
|
+
hasTouch: false,
|
|
36
|
+
touchMode: false,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
export function normalizeDevicePreset(raw) {
|
|
40
|
+
const s = String(raw ?? "")
|
|
41
|
+
.trim()
|
|
42
|
+
.toLowerCase();
|
|
43
|
+
if (s === "mobile" || s === "phone" || s === "m")
|
|
44
|
+
return "mobile";
|
|
45
|
+
if (s === "tablet" || s === "pad" || s === "t")
|
|
46
|
+
return "tablet";
|
|
47
|
+
if (s === "desktop" || s === "desk" || s === "d" || s === "main")
|
|
48
|
+
return "desktop";
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
/** Resolve a preset name or partial state into a full DeviceState. */
|
|
52
|
+
export function resolveDeviceState(input, touchOverride) {
|
|
53
|
+
if (input === undefined)
|
|
54
|
+
return undefined;
|
|
55
|
+
if (typeof input === "string") {
|
|
56
|
+
const p = normalizeDevicePreset(input);
|
|
57
|
+
if (!p)
|
|
58
|
+
return undefined;
|
|
59
|
+
const base = DEVICE_PRESETS[p];
|
|
60
|
+
const touchMode = touchOverride !== undefined ? !!touchOverride : !!base.touchMode;
|
|
61
|
+
return {
|
|
62
|
+
preset: base.preset,
|
|
63
|
+
viewport: { ...base.viewport },
|
|
64
|
+
isMobile: base.isMobile,
|
|
65
|
+
hasTouch: base.hasTouch || touchMode,
|
|
66
|
+
touchMode,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if ("width" in input && "height" in input && !("preset" in input)) {
|
|
70
|
+
const vp = input;
|
|
71
|
+
const touchMode = touchOverride !== undefined ? !!touchOverride : true;
|
|
72
|
+
return {
|
|
73
|
+
preset: "mobile",
|
|
74
|
+
viewport: {
|
|
75
|
+
width: Math.max(200, Math.floor(vp.width)),
|
|
76
|
+
height: Math.max(200, Math.floor(vp.height)),
|
|
77
|
+
...(vp.deviceScaleFactor !== undefined
|
|
78
|
+
? { deviceScaleFactor: vp.deviceScaleFactor }
|
|
79
|
+
: { deviceScaleFactor: 2 }),
|
|
80
|
+
},
|
|
81
|
+
isMobile: true,
|
|
82
|
+
hasTouch: true,
|
|
83
|
+
touchMode,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const d = input;
|
|
87
|
+
const preset = normalizeDevicePreset(d.preset) || "desktop";
|
|
88
|
+
const base = DEVICE_PRESETS[preset];
|
|
89
|
+
const touchMode = touchOverride !== undefined
|
|
90
|
+
? !!touchOverride
|
|
91
|
+
: d.touchMode !== undefined
|
|
92
|
+
? !!d.touchMode
|
|
93
|
+
: !!base.touchMode;
|
|
94
|
+
const viewport = d.viewport
|
|
95
|
+
? {
|
|
96
|
+
width: Math.max(200, Math.floor(d.viewport.width)),
|
|
97
|
+
height: Math.max(200, Math.floor(d.viewport.height)),
|
|
98
|
+
...(d.viewport.deviceScaleFactor !== undefined
|
|
99
|
+
? { deviceScaleFactor: d.viewport.deviceScaleFactor }
|
|
100
|
+
: base.viewport.deviceScaleFactor !== undefined
|
|
101
|
+
? { deviceScaleFactor: base.viewport.deviceScaleFactor }
|
|
102
|
+
: {}),
|
|
103
|
+
}
|
|
104
|
+
: { ...base.viewport };
|
|
105
|
+
return {
|
|
106
|
+
preset,
|
|
107
|
+
viewport,
|
|
108
|
+
isMobile: d.isMobile !== undefined ? !!d.isMobile : base.isMobile,
|
|
109
|
+
hasTouch: d.hasTouch !== undefined ? !!d.hasTouch : base.hasTouch || touchMode,
|
|
110
|
+
touchMode,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Carry-forward for device fixtures (mirror {@link applyTodoPhase}).
|
|
115
|
+
* - clear: hideDevice / clearDevice / device("desktop") with clear intent
|
|
116
|
+
* - set: author set device/viewport/touch this phase
|
|
117
|
+
* - keep: omit - preserve previous (whole episode remain)
|
|
118
|
+
*/
|
|
119
|
+
export function applyDevicePhase(prev, phase) {
|
|
120
|
+
const sync = phase.deviceSync ||
|
|
121
|
+
(phase.device ? "set" : "keep");
|
|
122
|
+
if (sync === "clear") {
|
|
123
|
+
return { device: resolveDeviceState("desktop", false), sync: "clear" };
|
|
124
|
+
}
|
|
125
|
+
if (sync === "set") {
|
|
126
|
+
if (phase.device)
|
|
127
|
+
return { device: phase.device, sync: "set" };
|
|
128
|
+
return { device: resolveDeviceState("desktop", false), sync: "clear" };
|
|
129
|
+
}
|
|
130
|
+
return { device: prev, sync: "keep" };
|
|
131
|
+
}
|
|
15
132
|
/** Apply Flow defaults under per-slot authored values (slot wins when set). */
|
|
16
133
|
export function applyHighlightStyleDefaults(slot, defaults) {
|
|
17
134
|
const tone = slot.tone !== undefined && slot.tone !== ""
|
|
@@ -600,6 +717,57 @@ function createStubCtx(bag, opts) {
|
|
|
600
717
|
bag.todos = [];
|
|
601
718
|
delete bag.todoGroups;
|
|
602
719
|
},
|
|
720
|
+
device(preset) {
|
|
721
|
+
bag.deviceTouched = true;
|
|
722
|
+
bag.hideDevice = false;
|
|
723
|
+
const resolved = resolveDeviceState(preset, bag.touchOverride);
|
|
724
|
+
if (resolved) {
|
|
725
|
+
bag.device = resolved;
|
|
726
|
+
if (resolved.preset === "desktop" && !resolved.touchMode) {
|
|
727
|
+
bag.hideDevice = true;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
viewport(box) {
|
|
732
|
+
bag.deviceTouched = true;
|
|
733
|
+
bag.hideDevice = false;
|
|
734
|
+
const resolved = resolveDeviceState(box, bag.touchOverride);
|
|
735
|
+
if (resolved)
|
|
736
|
+
bag.device = resolved;
|
|
737
|
+
},
|
|
738
|
+
touch(on) {
|
|
739
|
+
bag.deviceTouched = true;
|
|
740
|
+
const enabled = on === undefined ? true : !!on;
|
|
741
|
+
bag.touchOverride = enabled;
|
|
742
|
+
if (enabled)
|
|
743
|
+
bag.hideDevice = false;
|
|
744
|
+
if (bag.device) {
|
|
745
|
+
bag.device = {
|
|
746
|
+
...bag.device,
|
|
747
|
+
touchMode: enabled,
|
|
748
|
+
hasTouch: bag.device.hasTouch || enabled,
|
|
749
|
+
};
|
|
750
|
+
if (enabled)
|
|
751
|
+
bag.hideDevice = false;
|
|
752
|
+
}
|
|
753
|
+
else if (enabled) {
|
|
754
|
+
const base = resolveDeviceState("desktop", true);
|
|
755
|
+
if (base)
|
|
756
|
+
bag.device = { ...base, touchMode: true, hasTouch: true };
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
hideDevice() {
|
|
760
|
+
bag.deviceTouched = true;
|
|
761
|
+
bag.hideDevice = true;
|
|
762
|
+
bag.device = resolveDeviceState("desktop", false);
|
|
763
|
+
bag.touchOverride = false;
|
|
764
|
+
},
|
|
765
|
+
clearDevice() {
|
|
766
|
+
bag.deviceTouched = true;
|
|
767
|
+
bag.hideDevice = true;
|
|
768
|
+
bag.device = resolveDeviceState("desktop", false);
|
|
769
|
+
bag.touchOverride = false;
|
|
770
|
+
},
|
|
603
771
|
zoom(n) {
|
|
604
772
|
if (Number.isFinite(n) && n > 0)
|
|
605
773
|
bag.zoom = n;
|
|
@@ -648,6 +816,27 @@ function createStubCtx(bag, opts) {
|
|
|
648
816
|
bag.hideTodos = !partial.todoGroups || !partial.todoGroups.length;
|
|
649
817
|
bag.todoGroups = partial.todoGroups;
|
|
650
818
|
}
|
|
819
|
+
if (partial.touch !== undefined) {
|
|
820
|
+
bag.deviceTouched = true;
|
|
821
|
+
bag.touchOverride = !!partial.touch;
|
|
822
|
+
}
|
|
823
|
+
if (partial.device !== undefined) {
|
|
824
|
+
bag.deviceTouched = true;
|
|
825
|
+
bag.hideDevice = false;
|
|
826
|
+
const resolved = resolveDeviceState(partial.device, bag.touchOverride);
|
|
827
|
+
if (resolved) {
|
|
828
|
+
bag.device = resolved;
|
|
829
|
+
if (resolved.preset === "desktop" && !resolved.touchMode)
|
|
830
|
+
bag.hideDevice = true;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
if (partial.viewport !== undefined) {
|
|
834
|
+
bag.deviceTouched = true;
|
|
835
|
+
bag.hideDevice = false;
|
|
836
|
+
const resolved = resolveDeviceState(partial.viewport, bag.touchOverride);
|
|
837
|
+
if (resolved)
|
|
838
|
+
bag.device = resolved;
|
|
839
|
+
}
|
|
651
840
|
if (partial.zoom !== undefined && Number.isFinite(partial.zoom) && partial.zoom > 0) {
|
|
652
841
|
bag.zoom = partial.zoom;
|
|
653
842
|
}
|
|
@@ -812,12 +1001,37 @@ export async function runStubPhase(block, phase, opts) {
|
|
|
812
1001
|
: bag.todoTouched && !todoDock
|
|
813
1002
|
? "clear"
|
|
814
1003
|
: "keep";
|
|
1004
|
+
let deviceState;
|
|
1005
|
+
if (bag.hideDevice) {
|
|
1006
|
+
deviceState = resolveDeviceState("desktop", false);
|
|
1007
|
+
}
|
|
1008
|
+
else if (bag.device) {
|
|
1009
|
+
deviceState = bag.touchOverride !== undefined
|
|
1010
|
+
? {
|
|
1011
|
+
...bag.device,
|
|
1012
|
+
touchMode: bag.touchOverride,
|
|
1013
|
+
hasTouch: bag.device.hasTouch || bag.touchOverride,
|
|
1014
|
+
}
|
|
1015
|
+
: bag.device;
|
|
1016
|
+
}
|
|
1017
|
+
else if (bag.touchOverride !== undefined) {
|
|
1018
|
+
deviceState = resolveDeviceState("desktop", bag.touchOverride);
|
|
1019
|
+
}
|
|
1020
|
+
const deviceSync = bag.hideDevice
|
|
1021
|
+
? "clear"
|
|
1022
|
+
: bag.deviceTouched && deviceState
|
|
1023
|
+
? "set"
|
|
1024
|
+
: bag.deviceTouched && !deviceState
|
|
1025
|
+
? "clear"
|
|
1026
|
+
: "keep";
|
|
815
1027
|
return {
|
|
816
1028
|
highlights,
|
|
817
1029
|
todos,
|
|
818
1030
|
todoSync,
|
|
1031
|
+
deviceSync,
|
|
819
1032
|
...(bag.todoIndex !== undefined ? { todoIndex: bag.todoIndex } : {}),
|
|
820
1033
|
...(todoDock ? { todoDock } : {}),
|
|
1034
|
+
...(deviceState ? { device: deviceState } : {}),
|
|
821
1035
|
...(bag.zoom !== undefined ? { zoom: bag.zoom } : {}),
|
|
822
1036
|
...(bag.zoomOut !== undefined ? { zoomOut: bag.zoomOut } : {}),
|
|
823
1037
|
...(bag.title !== undefined && bag.title !== "" ? { title: bag.title } : {}),
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ export { connect, checkpoint } from "./types.js";
|
|
|
3
3
|
export { MemKey, MemPage, key, keyGroup } from "./mem-page.js";
|
|
4
4
|
export { Trait, urlMatches, textEquals, visible } from "./trait.js";
|
|
5
5
|
export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, chainFlow, } from "./engine.js";
|
|
6
|
-
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
7
|
-
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, WaygraphTodoGroup, WaygraphTodoGroupInput, TodoDockState, TodoListStyle, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, } from "./highlights.js";
|
|
6
|
+
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, DEVICE_PRESETS, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
7
|
+
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, WaygraphTodoGroup, WaygraphTodoGroupInput, TodoDockState, TodoListStyle, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, DevicePreset, DeviceViewport, DeviceState, } from "./highlights.js";
|
|
8
8
|
export type { Flow, EngineConfig, ComposedBlock, FastForwardComposedBlock, RunGraphOptions, BlockInfo, NavBlock, NavBlockOptions, NavClickBlock, NavClickBlockOptions, MethodBlock, ActionBlock, PageBlock, PageBlockOptions, PageMethodEntry, EffectBlock, MemEffectBlock, MemNavBlock, } from "./engine.js";
|
|
9
9
|
export type { ActionPage } from "./types.js";
|
|
10
10
|
export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { connect, checkpoint } from "./types.js";
|
|
|
2
2
|
export { MemKey, MemPage, key, keyGroup } from "./mem-page.js";
|
|
3
3
|
export { Trait, urlMatches, textEquals, visible } from "./trait.js";
|
|
4
4
|
export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, chainFlow, } from "./engine.js";
|
|
5
|
-
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
5
|
+
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, DEVICE_PRESETS, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
6
6
|
export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
|
7
7
|
export { isFileSelectToken, parseBlocksSelect, matchBlocksSelect, globToRegExp, } from "./blocks-select.js";
|
|
8
8
|
export { EdgeLeaseCoordinator } from "./traverse-lease.js";
|
package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts
CHANGED
|
@@ -37,6 +37,8 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
37
37
|
// Demo: zoom product -> add (stay zoomed), then badge after act.
|
|
38
38
|
stubBefore: (ctx: StubCtx) => {
|
|
39
39
|
ctx.title("Pick a product");
|
|
40
|
+
// Back to desktop after mobile login (explicit clear - not omit/remain).
|
|
41
|
+
ctx.clearDevice();
|
|
40
42
|
ctx.todoId("saucedemo-cart");
|
|
41
43
|
ctx.todos([
|
|
42
44
|
{ id: "cart-find", text: "Find the product" },
|
|
@@ -51,11 +51,13 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
51
51
|
// sequential highlight queue (appear / dwell / fade), focus + color.
|
|
52
52
|
stubBefore: (ctx: StubCtx) => {
|
|
53
53
|
ctx.title("Signing in");
|
|
54
|
+
// 0.13+: mobile viewport + touch theater; omit on later blocks = remain.
|
|
55
|
+
ctx.device("mobile");
|
|
54
56
|
ctx.todoId("saucedemo-login");
|
|
55
57
|
ctx.todos([
|
|
56
58
|
{ id: "login-user", text: "Enter username" },
|
|
57
59
|
{ id: "login-pass", text: "Enter password" },
|
|
58
|
-
{ id: "login-submit", text: "
|
|
60
|
+
{ id: "login-submit", text: "Tap Login" },
|
|
59
61
|
]);
|
|
60
62
|
ctx.todoIndex(0);
|
|
61
63
|
ctx.zoom(1.35);
|
|
@@ -81,6 +83,7 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
81
83
|
color: "#86efac",
|
|
82
84
|
zoom: 1.55,
|
|
83
85
|
weight: "bold",
|
|
86
|
+
gesture: "tap",
|
|
84
87
|
},
|
|
85
88
|
});
|
|
86
89
|
},
|
package/package.json
CHANGED
package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts
CHANGED
|
@@ -36,6 +36,8 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
36
36
|
// Demo: zoom product -> add (stay zoomed), then badge after act.
|
|
37
37
|
stubBefore: (ctx) => {
|
|
38
38
|
ctx.title("Pick a product");
|
|
39
|
+
// Back to desktop after mobile login (explicit clear - not omit/remain).
|
|
40
|
+
ctx.clearDevice();
|
|
39
41
|
ctx.todoId("saucedemo-cart");
|
|
40
42
|
ctx.todos([
|
|
41
43
|
{ id: "cart-find", text: "Find the product" },
|
|
@@ -51,11 +51,13 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
51
51
|
// sequential highlight queue (appear / dwell / fade), focus + color.
|
|
52
52
|
stubBefore: (ctx: StubCtx) => {
|
|
53
53
|
ctx.title("Signing in");
|
|
54
|
+
// 0.13+: mobile viewport + touch theater; omit on later blocks = remain.
|
|
55
|
+
ctx.device("mobile");
|
|
54
56
|
ctx.todoId("saucedemo-login");
|
|
55
57
|
ctx.todos([
|
|
56
58
|
{ id: "login-user", text: "Enter username" },
|
|
57
59
|
{ id: "login-pass", text: "Enter password" },
|
|
58
|
-
{ id: "login-submit", text: "
|
|
60
|
+
{ id: "login-submit", text: "Tap Login" },
|
|
59
61
|
]);
|
|
60
62
|
ctx.todoIndex(0);
|
|
61
63
|
ctx.zoom(1.35);
|
|
@@ -81,6 +83,7 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
81
83
|
color: "#86efac",
|
|
82
84
|
zoom: 1.55,
|
|
83
85
|
weight: "bold",
|
|
86
|
+
gesture: "tap",
|
|
84
87
|
},
|
|
85
88
|
});
|
|
86
89
|
},
|