waygraph 0.12.14 → 0.12.16
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 +6 -0
- package/dist/cli.js +144 -34
- package/dist/engine.d.ts +17 -0
- package/dist/engine.js +25 -0
- package/dist/highlights.d.ts +64 -2
- package/dist/highlights.js +126 -15
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/step-overlay.d.ts +1 -1
- package/dist/step-overlay.js +53 -11
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -233,6 +233,12 @@ Sign In narration.
|
|
|
233
233
|
snapshot, partitions edges by hash, and claims via in-process edge leases (audit under
|
|
234
234
|
`.waygraph-traverse/`). `--session inherit` is refused when `parallel > 1`. See
|
|
235
235
|
`docs/proposals/traverse-ffcompose-rfc.md`.
|
|
236
|
+
|
|
237
|
+
**Richer demo narration:** automation rings are **gray**; authored stubs/slides can use
|
|
238
|
+
`tone: "info" | "warning" | "danger" | "success"` (iconified captions), plus
|
|
239
|
+
`size: "sm" | "md" | "lg"` and `weight: "normal" | "bold"`. Episode pacing:
|
|
240
|
+
`withDemoPace(flow, "fast" | "slow" | "normal" | "blitz")` or `withBlockPace(block, ...)`
|
|
241
|
+
(FFCompose stays blitz). Living objectives: `docs/proposals/OBJECTIVES.md`.
|
|
236
242
|
`spawnTab(entry, page, mem)` drives a genuinely separate second tab through its own
|
|
237
243
|
Block/Flow, in the same browser context an existing `page` already belongs to - the
|
|
238
244
|
pattern `observe()` could already reach for (it's the only phase allowed to touch
|
package/dist/cli.js
CHANGED
|
@@ -211,6 +211,11 @@ import {
|
|
|
211
211
|
formatHighlightCaption,
|
|
212
212
|
hasAuthoredStubAfter,
|
|
213
213
|
hasAuthoredStubOnError,
|
|
214
|
+
normalizeHighlightTone,
|
|
215
|
+
normalizeDemoPace,
|
|
216
|
+
normalizeHighlightSize,
|
|
217
|
+
normalizeHighlightWeight,
|
|
218
|
+
resolveStepDemoPace,
|
|
214
219
|
} from "waygraph";
|
|
215
220
|
|
|
216
221
|
function walkDir(dir, pattern) {
|
|
@@ -399,9 +404,13 @@ const RING_CSS =
|
|
|
399
404
|
"border:2.5px solid #7C3AED;border-radius:10px;" +
|
|
400
405
|
"box-shadow:0 0 0 4px rgba(124,58,237,.16);transition:opacity .3s ease,border-color .15s,box-shadow .15s;}" +
|
|
401
406
|
// Planned (stubs / fixtures / YAP / instruction.highlights) = purple.
|
|
402
|
-
// Automation (verify fallback, unmatched fill/click) =
|
|
403
|
-
// can ignore engine checks and
|
|
404
|
-
"#wg-ring[data-tone=auto]{border-color:#
|
|
407
|
+
// Automation (verify fallback, unmatched fill/click) = gray - operators
|
|
408
|
+
// can ignore engine checks and watch purple + semantic tones.
|
|
409
|
+
"#wg-ring[data-tone=auto]{border-color:#9CA3AF;box-shadow:0 0 0 4px rgba(156,163,175,.28);}" +
|
|
410
|
+
"#wg-ring[data-tone=info]{border-color:#3B82F6;box-shadow:0 0 0 4px rgba(59,130,246,.22);}" +
|
|
411
|
+
"#wg-ring[data-tone=warning]{border-color:#EAB308;box-shadow:0 0 0 4px rgba(234,179,8,.22);}" +
|
|
412
|
+
"#wg-ring[data-tone=danger]{border-color:#EF4444;box-shadow:0 0 0 4px rgba(239,68,68,.22);}" +
|
|
413
|
+
"#wg-ring[data-tone=success]{border-color:#22C55E;box-shadow:0 0 0 4px rgba(34,197,94,.22);}" +
|
|
405
414
|
// A real element, not a ::after pseudo-element - a pseudo-element's
|
|
406
415
|
// position is CSS-relative to the ring's own box (left:0 always meant
|
|
407
416
|
// "the ring's own left edge"), so it had no way to be clamped back onto
|
|
@@ -413,7 +422,18 @@ const RING_CSS =
|
|
|
413
422
|
"#wg-ring-label{position:fixed;z-index:2147483646;pointer-events:none;opacity:0;" +
|
|
414
423
|
"max-width:min(360px,70vw);white-space:normal;padding:6px 10px;border-radius:7px;background:#7C3AED;color:#fff;" +
|
|
415
424
|
"font:600 12px/1.35 system-ui,sans-serif;transition:opacity .3s ease,background .15s,color .15s;}" +
|
|
416
|
-
"#wg-ring-label[data-tone=auto]{background:#
|
|
425
|
+
"#wg-ring-label[data-tone=auto]{background:#6B7280;color:#fff;}" +
|
|
426
|
+
"#wg-ring-label[data-tone=info]{background:#2563EB;color:#fff;}" +
|
|
427
|
+
"#wg-ring-label[data-tone=warning]{background:#EAB308;color:#1c1917;}" +
|
|
428
|
+
"#wg-ring-label[data-tone=danger]{background:#DC2626;color:#fff;}" +
|
|
429
|
+
"#wg-ring-label[data-tone=success]{background:#16A34A;color:#fff;}" +
|
|
430
|
+
// size: ring pad + label font; weight: label boldness
|
|
431
|
+
"#wg-ring[data-size=sm]{border-width:1.5px;border-radius:8px;}" +
|
|
432
|
+
"#wg-ring[data-size=lg]{border-width:4px;border-radius:12px;}" +
|
|
433
|
+
"#wg-ring-label[data-size=sm]{font-size:10px;line-height:1.25;padding:4px 7px;border-radius:5px;}" +
|
|
434
|
+
"#wg-ring-label[data-size=lg]{font-size:16px;line-height:1.35;padding:8px 14px;border-radius:9px;max-width:min(480px,85vw);}" +
|
|
435
|
+
"#wg-ring-label[data-weight=bold]{font-weight:800;}" +
|
|
436
|
+
"#wg-ring-label[data-weight=normal]{font-weight:600;}" +
|
|
417
437
|
// Mouse cursor icon that travels to a target before it's acted on, plus a
|
|
418
438
|
// quick expanding ripple at the moment of a click - same idea as
|
|
419
439
|
// help-center-clip-engine's #clip-cursor/#clip-ring (video-pipeline). The
|
|
@@ -431,7 +451,11 @@ const RING_CSS =
|
|
|
431
451
|
"#wg-click-pulse{position:fixed;z-index:2147483647;width:14px;height:14px;" +
|
|
432
452
|
"margin-left:-7px;margin-top:-7px;border-radius:50%;pointer-events:none;opacity:0;" +
|
|
433
453
|
"border:2px solid #7C3AED;background:rgba(124,58,237,.25);}" +
|
|
434
|
-
"#wg-click-pulse[data-tone=auto]{border-color:#
|
|
454
|
+
"#wg-click-pulse[data-tone=auto]{border-color:#9CA3AF;background:rgba(156,163,175,.28);}" +
|
|
455
|
+
"#wg-click-pulse[data-tone=info]{border-color:#3B82F6;background:rgba(59,130,246,.28);}" +
|
|
456
|
+
"#wg-click-pulse[data-tone=warning]{border-color:#EAB308;background:rgba(234,179,8,.28);}" +
|
|
457
|
+
"#wg-click-pulse[data-tone=danger]{border-color:#EF4444;background:rgba(239,68,68,.28);}" +
|
|
458
|
+
"#wg-click-pulse[data-tone=success]{border-color:#22C55E;background:rgba(34,197,94,.28);}" +
|
|
435
459
|
"#wg-click-pulse.wg-pulse{animation:wg-pulse .5s ease-out;}" +
|
|
436
460
|
"@keyframes wg-pulse{0%{opacity:.9;transform:scale(.4);}100%{opacity:0;transform:scale(2.4);}}" +
|
|
437
461
|
"#wg-panel{position:fixed;z-index:2147483647;left:50%;bottom:12px;transform:translateX(-50%);" +
|
|
@@ -647,20 +671,34 @@ async function installOverlay(page, title) {
|
|
|
647
671
|
const cursor = document.getElementById("wg-cursor");
|
|
648
672
|
if (cursor) cursor.style.opacity = "0";
|
|
649
673
|
};
|
|
650
|
-
window.__wgPositionRing = (box, label, tone) => {
|
|
674
|
+
window.__wgPositionRing = (box, label, tone, style) => {
|
|
651
675
|
const ring = document.getElementById("wg-ring");
|
|
652
676
|
const ringLabel = document.getElementById("wg-ring-label");
|
|
653
677
|
if (!ring || !ringLabel) return;
|
|
654
|
-
const
|
|
678
|
+
const raw = (tone || "planned") + "";
|
|
679
|
+
const t =
|
|
680
|
+
raw === "auto" || raw === "info" || raw === "warning" || raw === "danger" || raw === "success"
|
|
681
|
+
? raw
|
|
682
|
+
: "planned";
|
|
683
|
+
const st = style && typeof style === "object" ? style : {};
|
|
684
|
+
const sizeRaw = (st.size || "md") + "";
|
|
685
|
+
const size =
|
|
686
|
+
sizeRaw === "sm" || sizeRaw === "lg" ? sizeRaw : "md";
|
|
687
|
+
const weightRaw = (st.weight || "normal") + "";
|
|
688
|
+
const weight = weightRaw === "bold" ? "bold" : "normal";
|
|
655
689
|
ring.dataset.tone = t;
|
|
656
690
|
ringLabel.dataset.tone = t;
|
|
691
|
+
ring.dataset.size = size;
|
|
692
|
+
ringLabel.dataset.size = size;
|
|
693
|
+
ringLabel.dataset.weight = weight;
|
|
694
|
+
const pad = size === "sm" ? 3 : size === "lg" ? 10 : 6;
|
|
657
695
|
const margin = 6;
|
|
658
696
|
const vw = window.innerWidth;
|
|
659
697
|
const vh = window.innerHeight;
|
|
660
|
-
let left = box.x -
|
|
661
|
-
let top = box.y -
|
|
662
|
-
const width = box.width +
|
|
663
|
-
const height = box.height +
|
|
698
|
+
let left = box.x - pad;
|
|
699
|
+
let top = box.y - pad;
|
|
700
|
+
const width = box.width + pad * 2;
|
|
701
|
+
const height = box.height + pad * 2;
|
|
664
702
|
if (left < margin) left = margin;
|
|
665
703
|
if (top < margin) top = margin;
|
|
666
704
|
if (left + width > vw - margin) left = Math.max(margin, vw - margin - width);
|
|
@@ -688,7 +726,11 @@ async function installOverlay(page, title) {
|
|
|
688
726
|
window.__wgClickPulse = (x, y, tone) => {
|
|
689
727
|
const pulse = document.getElementById("wg-click-pulse");
|
|
690
728
|
if (!pulse) return;
|
|
691
|
-
|
|
729
|
+
const raw = (tone || "planned") + "";
|
|
730
|
+
pulse.dataset.tone =
|
|
731
|
+
raw === "auto" || raw === "info" || raw === "warning" || raw === "danger" || raw === "success"
|
|
732
|
+
? raw
|
|
733
|
+
: "planned";
|
|
692
734
|
pulse.style.left = x + "px";
|
|
693
735
|
pulse.style.top = y + "px";
|
|
694
736
|
pulse.classList.remove("wg-pulse");
|
|
@@ -1035,7 +1077,10 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1035
1077
|
try {
|
|
1036
1078
|
const box = await page.locator(h.selector).first().boundingBox();
|
|
1037
1079
|
if (box) {
|
|
1038
|
-
await showRing(page, box, h.label, h.tone || "planned"
|
|
1080
|
+
await showRing(page, box, h.label, h.tone || "planned", {
|
|
1081
|
+
size: h.size,
|
|
1082
|
+
weight: h.weight,
|
|
1083
|
+
});
|
|
1039
1084
|
const authored = resolveFixtureDwellMs(h, { gatesFast });
|
|
1040
1085
|
const legacyMs = i === list.length - 1 ? 200 : 900;
|
|
1041
1086
|
const holdMs =
|
|
@@ -1057,7 +1102,10 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
|
|
|
1057
1102
|
if (window.__wgHideRing) window.__wgHideRing();
|
|
1058
1103
|
return;
|
|
1059
1104
|
}
|
|
1060
|
-
window.__wgPositionRing(el.getBoundingClientRect(), h.label, h.tone || "planned"
|
|
1105
|
+
window.__wgPositionRing(el.getBoundingClientRect(), h.label, h.tone || "planned", {
|
|
1106
|
+
size: h.size || "md",
|
|
1107
|
+
weight: h.weight || "normal",
|
|
1108
|
+
});
|
|
1061
1109
|
};
|
|
1062
1110
|
reposition();
|
|
1063
1111
|
window.__wgRingTrack = reposition;
|
|
@@ -1303,7 +1351,7 @@ function resolveDeclaredHighlights(block, resultTag) {
|
|
|
1303
1351
|
if (!Array.isArray(highlights)) return [];
|
|
1304
1352
|
return highlights.filter((h) => h && typeof h.selector === "string" && typeof h.label === "string").map((h) => ({
|
|
1305
1353
|
...h,
|
|
1306
|
-
tone: h.tone
|
|
1354
|
+
tone: normalizeHighlightTone(h.tone),
|
|
1307
1355
|
}));
|
|
1308
1356
|
}
|
|
1309
1357
|
|
|
@@ -1402,13 +1450,17 @@ async function markStepRunning(page, _opts) {
|
|
|
1402
1450
|
.catch(() => {});
|
|
1403
1451
|
}
|
|
1404
1452
|
|
|
1405
|
-
async function showRing(page, box, label, tone) {
|
|
1453
|
+
async function showRing(page, box, label, tone, style) {
|
|
1454
|
+
const size = normalizeHighlightSize(style && style.size);
|
|
1455
|
+
const weight = normalizeHighlightWeight(style && style.weight);
|
|
1406
1456
|
await page
|
|
1407
1457
|
.evaluate(
|
|
1408
|
-
({ box, label, tone }) => {
|
|
1409
|
-
if (window.__wgPositionRing)
|
|
1458
|
+
({ box, label, tone, size, weight }) => {
|
|
1459
|
+
if (window.__wgPositionRing) {
|
|
1460
|
+
window.__wgPositionRing(box, label, tone || "planned", { size, weight });
|
|
1461
|
+
}
|
|
1410
1462
|
},
|
|
1411
|
-
{ box, label, tone: tone || "planned" },
|
|
1463
|
+
{ box, label, tone: tone || "planned", size, weight },
|
|
1412
1464
|
)
|
|
1413
1465
|
.catch(() => {});
|
|
1414
1466
|
}
|
|
@@ -1438,14 +1490,24 @@ async function matchStubForLocator(page, locator, stubs) {
|
|
|
1438
1490
|
|
|
1439
1491
|
async function captionForLocator(page, locator, stubs, fallback) {
|
|
1440
1492
|
const matched = await matchStubForLocator(page, locator, stubs);
|
|
1441
|
-
if (matched)
|
|
1442
|
-
|
|
1493
|
+
if (matched) {
|
|
1494
|
+
return {
|
|
1495
|
+
label: formatHighlightCaption(matched),
|
|
1496
|
+
tone: normalizeHighlightTone(matched.tone),
|
|
1497
|
+
size: normalizeHighlightSize(matched.size),
|
|
1498
|
+
weight: normalizeHighlightWeight(matched.weight),
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
return { label: fallback, tone: "auto", size: "md", weight: "normal" };
|
|
1443
1502
|
}
|
|
1444
1503
|
|
|
1445
1504
|
async function dwellMatchedStub(page, locator, stubs, pacing) {
|
|
1446
1505
|
const matched = await matchStubForLocator(page, locator, stubs);
|
|
1447
1506
|
if (!matched) return;
|
|
1448
|
-
const ms = resolveFixtureDwellMs(matched, {
|
|
1507
|
+
const ms = resolveFixtureDwellMs(matched, {
|
|
1508
|
+
gatesFast: !!(pacing && pacing.gatesFast),
|
|
1509
|
+
pace: pacing && pacing.demoPace,
|
|
1510
|
+
});
|
|
1449
1511
|
if (ms != null && ms > 0) {
|
|
1450
1512
|
await new Promise((res) => setTimeout(res, ms));
|
|
1451
1513
|
}
|
|
@@ -1466,22 +1528,30 @@ async function presentSlides(page, slides, _gate, opts) {
|
|
|
1466
1528
|
const episodeNumber = opts && opts.episodeNumber;
|
|
1467
1529
|
const episodeTitle = (opts && opts.episodeTitle) || "";
|
|
1468
1530
|
const gatesFast = !!(opts && opts.fast);
|
|
1531
|
+
const demoPace = normalizeDemoPace((opts && opts.pace) || (gatesFast ? "fast" : "normal"));
|
|
1469
1532
|
const autoplayMs =
|
|
1470
1533
|
opts && opts.autoplayMs
|
|
1471
1534
|
? Number(opts.autoplayMs)
|
|
1472
1535
|
: process.env.WAYGRAPH_AUTOPLAY_MS
|
|
1473
1536
|
? Number(process.env.WAYGRAPH_AUTOPLAY_MS)
|
|
1474
|
-
:
|
|
1537
|
+
: demoPace === "slow"
|
|
1538
|
+
? 3500
|
|
1539
|
+
: 1800;
|
|
1475
1540
|
for (let i = 0; i < slides.length; i++) {
|
|
1476
1541
|
const s = slides[i];
|
|
1477
1542
|
const caption = formatHighlightCaption(s);
|
|
1543
|
+
const slideTone = normalizeHighlightTone(s.tone);
|
|
1544
|
+
const slideStyle = {
|
|
1545
|
+
size: normalizeHighlightSize(s.size),
|
|
1546
|
+
weight: normalizeHighlightWeight(s.weight),
|
|
1547
|
+
};
|
|
1478
1548
|
const isLast = i === slides.length - 1;
|
|
1479
|
-
const dwellMs = resolveFixtureDwellMs(s, { gatesFast });
|
|
1549
|
+
const dwellMs = resolveFixtureDwellMs(s, { gatesFast, pace: demoPace });
|
|
1480
1550
|
await installOverlay(page, title);
|
|
1481
1551
|
if (s.selector) {
|
|
1482
1552
|
try {
|
|
1483
1553
|
const box = await page.locator(s.selector).first().boundingBox();
|
|
1484
|
-
if (box) await showRing(page, box, caption,
|
|
1554
|
+
if (box) await showRing(page, box, caption, slideTone, slideStyle);
|
|
1485
1555
|
} catch {
|
|
1486
1556
|
await hideRing(page);
|
|
1487
1557
|
}
|
|
@@ -1793,7 +1863,10 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
1793
1863
|
const stubs = (stubBeforeRef && stubBeforeRef.current) || [];
|
|
1794
1864
|
const cap = await captionForLocator(page, this, stubs, fallback);
|
|
1795
1865
|
await moveCursorTo(page, box, cursorMs(500));
|
|
1796
|
-
await showRing(page, box, cap.label, cap.tone
|
|
1866
|
+
await showRing(page, box, cap.label, cap.tone, {
|
|
1867
|
+
size: cap.size,
|
|
1868
|
+
weight: cap.weight,
|
|
1869
|
+
});
|
|
1797
1870
|
await dwellMatchedStub(page, this, stubs, pacing);
|
|
1798
1871
|
await new Promise((res) => setTimeout(res, skipTheater() ? 0 : 200));
|
|
1799
1872
|
} else if (box) {
|
|
@@ -1865,7 +1938,10 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBefore
|
|
|
1865
1938
|
const cap = await captionForLocator(page, this, stubs, fallback);
|
|
1866
1939
|
clickTone = cap.tone;
|
|
1867
1940
|
clickPoint = await moveCursorTo(page, box, cursorMs(600));
|
|
1868
|
-
await showRing(page, box, cap.label, cap.tone
|
|
1941
|
+
await showRing(page, box, cap.label, cap.tone, {
|
|
1942
|
+
size: cap.size,
|
|
1943
|
+
weight: cap.weight,
|
|
1944
|
+
});
|
|
1869
1945
|
await dwellMatchedStub(page, this, stubs, pacing);
|
|
1870
1946
|
// "pop for a few seconds" - Dan's own phrase, matching the
|
|
1871
1947
|
// zsign demo-engine's ring-before-click pattern in
|
|
@@ -1996,7 +2072,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
1996
2072
|
// inside the FF (that was the "30s login / cursor gone but still slow" bug).
|
|
1997
2073
|
const demoFast = process.env.WAYGRAPH_DEMO_FAST === "1";
|
|
1998
2074
|
const stepperMode = process.env.WAYGRAPH_STEPPER === "full" ? "full" : "carousel";
|
|
1999
|
-
const pacing = { gatesFast: demoFast, skipTheater: false };
|
|
2075
|
+
const pacing = { gatesFast: demoFast, gatesSlow: false, skipTheater: false, demoPace: "normal" };
|
|
2000
2076
|
const stubBeforeRef = { current: [] };
|
|
2001
2077
|
instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef);
|
|
2002
2078
|
// Force the panel checkbox from this process's flags/env at run start.
|
|
@@ -2087,7 +2163,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
2087
2163
|
? Math.min(80, autoplayMs)
|
|
2088
2164
|
: pacing.gatesFast
|
|
2089
2165
|
? Math.min(400, autoplayMs)
|
|
2090
|
-
:
|
|
2166
|
+
: pacing.gatesSlow
|
|
2167
|
+
? Math.max(autoplayMs * 2, 3500)
|
|
2168
|
+
: autoplayMs;
|
|
2091
2169
|
let elapsed = 0;
|
|
2092
2170
|
for (;;) {
|
|
2093
2171
|
// Re-read the checkbox EVERY loop tick, not once up front - a human
|
|
@@ -2171,10 +2249,33 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
2171
2249
|
await resetPageState(context, page, baseURL);
|
|
2172
2250
|
}
|
|
2173
2251
|
// gatesFast / skipTheater: opaque FF, former-FF inners (--ff-disabled/--ff-expand),
|
|
2174
|
-
// or
|
|
2175
|
-
|
|
2252
|
+
// WAYGRAPH_FAST_BLOCKS, or withDemoPace/withBlockPace. --fast alone = shorter
|
|
2253
|
+
// gates only (keeps theater). Episode pace: block > flow > CLI --fast.
|
|
2254
|
+
const stepPace = resolveStepDemoPace({
|
|
2255
|
+
blockPace: r.block.demoPace,
|
|
2256
|
+
flowPace: r.demoPace,
|
|
2257
|
+
fastForward: !!r.block.fastForward,
|
|
2258
|
+
wasFastForward: !!r.wasFastForward,
|
|
2259
|
+
});
|
|
2260
|
+
const cliFast = demoFast || fastBlockNames.has(r.block.name);
|
|
2261
|
+
pacing.demoPace =
|
|
2262
|
+
stepPace === "normal" && cliFast
|
|
2263
|
+
? fastBlockNames.has(r.block.name)
|
|
2264
|
+
? "blitz"
|
|
2265
|
+
: "fast"
|
|
2266
|
+
: stepPace;
|
|
2267
|
+
pacing.gatesFast =
|
|
2268
|
+
pacing.demoPace === "blitz" ||
|
|
2269
|
+
pacing.demoPace === "fast" ||
|
|
2270
|
+
!!r.block.fastForward ||
|
|
2271
|
+
!!r.wasFastForward ||
|
|
2272
|
+
demoFast;
|
|
2273
|
+
pacing.gatesSlow = pacing.demoPace === "slow";
|
|
2176
2274
|
pacing.skipTheater =
|
|
2177
|
-
|
|
2275
|
+
pacing.demoPace === "blitz" ||
|
|
2276
|
+
fastBlockNames.has(r.block.name) ||
|
|
2277
|
+
!!r.block.fastForward ||
|
|
2278
|
+
!!r.wasFastForward;
|
|
2178
2279
|
const fixtures = r.highlightFixtures;
|
|
2179
2280
|
stubBeforeRef.current = resolveHighlightSlots(r.block, "stubBefore", { fixtures });
|
|
2180
2281
|
const isNavBlock = r.block.__waygraphKind === "nav";
|
|
@@ -2273,7 +2374,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
2273
2374
|
label: formatHighlightCaption(h),
|
|
2274
2375
|
duration: h.duration,
|
|
2275
2376
|
fastMode: h.fastMode,
|
|
2276
|
-
tone:
|
|
2377
|
+
tone: normalizeHighlightTone(h.tone),
|
|
2378
|
+
size: normalizeHighlightSize(h.size),
|
|
2379
|
+
weight: normalizeHighlightWeight(h.weight),
|
|
2277
2380
|
}));
|
|
2278
2381
|
await cycleHighlightRings(page, errHighlights, !!pacing.gatesFast, {
|
|
2279
2382
|
defaultHoldMs: 2000,
|
|
@@ -2333,6 +2436,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
2333
2436
|
title,
|
|
2334
2437
|
blockName: r.block.name,
|
|
2335
2438
|
fast: pacing.gatesFast,
|
|
2439
|
+
pace: pacing.demoPace,
|
|
2336
2440
|
episodeNumber: r.episodeNumber,
|
|
2337
2441
|
episodeTitle: r.episodeTitle,
|
|
2338
2442
|
autoplayMs,
|
|
@@ -2346,7 +2450,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
2346
2450
|
label: formatHighlightCaption(h),
|
|
2347
2451
|
duration: h.duration,
|
|
2348
2452
|
fastMode: h.fastMode,
|
|
2349
|
-
tone:
|
|
2453
|
+
tone: normalizeHighlightTone(h.tone),
|
|
2454
|
+
size: normalizeHighlightSize(h.size),
|
|
2455
|
+
weight: normalizeHighlightWeight(h.weight),
|
|
2350
2456
|
}),
|
|
2351
2457
|
);
|
|
2352
2458
|
} else {
|
|
@@ -2531,6 +2637,7 @@ async function main() {
|
|
|
2531
2637
|
exportName: bi.name,
|
|
2532
2638
|
seedMem: idx === 0 ? seedMem : undefined,
|
|
2533
2639
|
highlightFixtures: flow.highlightFixtures,
|
|
2640
|
+
demoPace: flow.demoPace,
|
|
2534
2641
|
expectedFailureReason:
|
|
2535
2642
|
flow.expectedFailureReason && idx === blockInfos.length - 1
|
|
2536
2643
|
? flow.expectedFailureReason
|
|
@@ -2599,6 +2706,7 @@ async function main() {
|
|
|
2599
2706
|
episodeTitle: flow.title || seg.ref,
|
|
2600
2707
|
expectedFailureReason: flow.expectedFailureReason,
|
|
2601
2708
|
highlightFixtures: flow.highlightFixtures,
|
|
2709
|
+
demoPace: flow.demoPace,
|
|
2602
2710
|
seedMem: () => seedMemForFlow(mem, flow.blocks(), seg.json, seg.ref),
|
|
2603
2711
|
});
|
|
2604
2712
|
} else {
|
|
@@ -2608,6 +2716,7 @@ async function main() {
|
|
|
2608
2716
|
episodeNumber: undefined,
|
|
2609
2717
|
episodeTitle: undefined,
|
|
2610
2718
|
highlightFixtures: undefined,
|
|
2719
|
+
demoPace: undefined,
|
|
2611
2720
|
seedMem: () => seedMemForBlock(mem, r, seg.json),
|
|
2612
2721
|
});
|
|
2613
2722
|
}
|
|
@@ -2632,6 +2741,7 @@ async function main() {
|
|
|
2632
2741
|
episodeNumber: meta.episodeNumber,
|
|
2633
2742
|
episodeTitle: meta.episodeTitle,
|
|
2634
2743
|
highlightFixtures: meta.highlightFixtures,
|
|
2744
|
+
demoPace: meta.demoPace,
|
|
2635
2745
|
expectedFailureReason:
|
|
2636
2746
|
meta.expectedFailureReason && remainingInFlow === 1 ? meta.expectedFailureReason : undefined,
|
|
2637
2747
|
seedMem: isFirstOfSegment ? meta.seedMem : undefined,
|
package/dist/engine.d.ts
CHANGED
|
@@ -186,6 +186,11 @@ export interface Flow<Out extends Checkpoint<string>> {
|
|
|
186
186
|
* block.name. `run()` never reads this; step/`demo` merges onto block stubs.
|
|
187
187
|
*/
|
|
188
188
|
readonly highlightFixtures?: import("./highlights.js").HighlightFixtureMap;
|
|
189
|
+
/**
|
|
190
|
+
* Demo pacing for this Flow's episode (`withDemoPace`).
|
|
191
|
+
* `run()` ignores; step/demo applies per Block in the episode.
|
|
192
|
+
*/
|
|
193
|
+
readonly demoPace?: import("./highlights.js").DemoPace;
|
|
189
194
|
/**
|
|
190
195
|
* This Flow's constituent Blocks, in order between `start`/`end`, as plain
|
|
191
196
|
* data - for introspection/visualization tools, without running anything.
|
|
@@ -253,6 +258,18 @@ export declare function withSessionReset<Out extends Checkpoint<string>>(flow: F
|
|
|
253
258
|
* @example const ticket123 = withTitle(loginFlow, "Ticket #123 - Sales rep signs in");
|
|
254
259
|
*/
|
|
255
260
|
export declare function withTitle<Out extends Checkpoint<string>>(flow: Flow<Out>, title: string): Flow<Out>;
|
|
261
|
+
/**
|
|
262
|
+
* Demo pacing for a whole Flow episode (fast review vs slow gap/wrong walkthrough).
|
|
263
|
+
* Non-destructive. `fastForwardComposeBlock` is always blitz for its own step;
|
|
264
|
+
* use this when an entire episode should be fast or slow.
|
|
265
|
+
* @example withDemoPace(withTitle(gapFlow, "Episode 2 - gaps"), "slow")
|
|
266
|
+
*/
|
|
267
|
+
export declare function withDemoPace<Out extends Checkpoint<string>>(flow: Flow<Out>, pace: import("./highlights.js").DemoPace): Flow<Out>;
|
|
268
|
+
/**
|
|
269
|
+
* Demo pacing for one Block or compose unit (not FF - use fastForwardComposeBlock for blitz).
|
|
270
|
+
* @example withBlockPace(composeBlock("gap-review", [...]), "slow")
|
|
271
|
+
*/
|
|
272
|
+
export declare function withBlockPace<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, pace: import("./highlights.js").DemoPace): Block<In, Out>;
|
|
256
273
|
/**
|
|
257
274
|
* Marks a Flow as one that's SUPPOSED to throw - e.g. a locked-out user's
|
|
258
275
|
* login attempt genuinely failing, demonstrating the app's own real
|
package/dist/engine.js
CHANGED
|
@@ -351,6 +351,31 @@ export function withTitle(flow, title) {
|
|
|
351
351
|
modBlockVerify: (block, nameOrIndex, newCheck) => withTitle(flow.modBlockVerify(block, nameOrIndex, newCheck), title),
|
|
352
352
|
};
|
|
353
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Demo pacing for a whole Flow episode (fast review vs slow gap/wrong walkthrough).
|
|
356
|
+
* Non-destructive. `fastForwardComposeBlock` is always blitz for its own step;
|
|
357
|
+
* use this when an entire episode should be fast or slow.
|
|
358
|
+
* @example withDemoPace(withTitle(gapFlow, "Episode 2 - gaps"), "slow")
|
|
359
|
+
*/
|
|
360
|
+
export function withDemoPace(flow, pace) {
|
|
361
|
+
const demoPace = pace;
|
|
362
|
+
return {
|
|
363
|
+
...flow,
|
|
364
|
+
demoPace,
|
|
365
|
+
withBlockVerify: (block, verify) => withDemoPace(flow.withBlockVerify(block, verify), demoPace),
|
|
366
|
+
modBlockVerify: (block, nameOrIndex, newCheck) => withDemoPace(flow.modBlockVerify(block, nameOrIndex, newCheck), demoPace),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Demo pacing for one Block or compose unit (not FF - use fastForwardComposeBlock for blitz).
|
|
371
|
+
* @example withBlockPace(composeBlock("gap-review", [...]), "slow")
|
|
372
|
+
*/
|
|
373
|
+
export function withBlockPace(block, pace) {
|
|
374
|
+
return {
|
|
375
|
+
...block,
|
|
376
|
+
demoPace: pace,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
354
379
|
/**
|
|
355
380
|
* Marks a Flow as one that's SUPPOSED to throw - e.g. a locked-out user's
|
|
356
381
|
* login attempt genuinely failing, demonstrating the app's own real
|
package/dist/highlights.d.ts
CHANGED
|
@@ -33,9 +33,26 @@ export interface WaygraphHighlightStub extends FixtureDurationFields {
|
|
|
33
33
|
detail?: string;
|
|
34
34
|
/** Short badge, e.g. AC / BUG / GATE / YAP. */
|
|
35
35
|
tag?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Demo ring color + icon:
|
|
38
|
+
* - `planned` purple (authored narration, default)
|
|
39
|
+
* - `auto` gray (engine automation / verify fallback)
|
|
40
|
+
* - `info` blue · `warning` yellow · `danger` red · `success` green
|
|
41
|
+
*/
|
|
42
|
+
tone?: HighlightTone;
|
|
43
|
+
/**
|
|
44
|
+
* Ring padding + label font scale: `sm` | `md` (default) | `lg`.
|
|
45
|
+
* Aliases: small/medium/large, s/m/l.
|
|
46
|
+
*/
|
|
47
|
+
size?: HighlightSize;
|
|
48
|
+
/**
|
|
49
|
+
* Label font weight: `normal` (default) | `bold`.
|
|
50
|
+
* Aliases: regular, strong.
|
|
51
|
+
*/
|
|
52
|
+
weight?: HighlightWeight;
|
|
36
53
|
}
|
|
37
54
|
/** Flow fixture patch: label required; selector optional (inherits from stub). */
|
|
38
|
-
export type WaygraphHighlightFixture = Partial<Pick<WaygraphHighlightStub, "selector">> & Required<Pick<WaygraphHighlightStub, "label">> & Pick<WaygraphHighlightStub, "detail" | "tag"> & FixtureDurationFields;
|
|
55
|
+
export type WaygraphHighlightFixture = Partial<Pick<WaygraphHighlightStub, "selector">> & Required<Pick<WaygraphHighlightStub, "label">> & Pick<WaygraphHighlightStub, "detail" | "tag" | "tone" | "size" | "weight"> & FixtureDurationFields;
|
|
39
56
|
export type HighlightStubPhase = Record<string, WaygraphHighlightStub>;
|
|
40
57
|
export type HighlightStubPhaseOrFn<Out extends Checkpoint<string>> = HighlightStubPhase | ((out: Out) => HighlightStubPhase);
|
|
41
58
|
/**
|
|
@@ -49,11 +66,42 @@ export interface WaygraphSlide extends FixtureDurationFields {
|
|
|
49
66
|
tag?: string;
|
|
50
67
|
/** Optional ring while this slide is showing. */
|
|
51
68
|
selector?: string;
|
|
69
|
+
tone?: HighlightTone;
|
|
70
|
+
size?: HighlightSize;
|
|
71
|
+
weight?: HighlightWeight;
|
|
52
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Demo ring tones (iconified).
|
|
75
|
+
* Automation (`auto`) is gray so authored planned/info/warning/danger/success stand out.
|
|
76
|
+
*/
|
|
77
|
+
export type HighlightTone = "planned" | "auto" | "info" | "warning" | "danger" | "success";
|
|
78
|
+
/** Ring + label scale. */
|
|
79
|
+
export type HighlightSize = "sm" | "md" | "lg";
|
|
80
|
+
/** Label font weight. */
|
|
81
|
+
export type HighlightWeight = "normal" | "bold";
|
|
82
|
+
/** Map author aliases (`error` -> danger) to a canonical tone. */
|
|
83
|
+
export declare function normalizeHighlightTone(raw: unknown): HighlightTone;
|
|
84
|
+
/** Map size aliases to sm/md/lg. */
|
|
85
|
+
export declare function normalizeHighlightSize(raw: unknown): HighlightSize;
|
|
86
|
+
/** Map weight aliases to normal/bold. */
|
|
87
|
+
export declare function normalizeHighlightWeight(raw: unknown): HighlightWeight;
|
|
88
|
+
/** Prefix label with a short ASCII icon for semantic tones. */
|
|
89
|
+
export declare function toneIconPrefix(tone: HighlightTone): string;
|
|
90
|
+
/**
|
|
91
|
+
* Demo pacing for a Flow episode or a Block/compose group.
|
|
92
|
+
* - `blitz` - FF-like: skip theater + short gates (same as fastForwardComposeBlock)
|
|
93
|
+
* - `fast` - shorter gates, keep cursor theater
|
|
94
|
+
* - `normal` - default demo pace
|
|
95
|
+
* - `slow` - longer autoplay + fixture dwell (gaps / wrongs review)
|
|
96
|
+
*/
|
|
97
|
+
export type DemoPace = "blitz" | "fast" | "normal" | "slow";
|
|
98
|
+
export declare function normalizeDemoPace(raw: unknown): DemoPace;
|
|
53
99
|
/** Default fixture dwell when `duration: true`. */
|
|
54
100
|
export declare const FIXTURE_DURATION_MS_DEFAULT = 2000;
|
|
55
101
|
/** Default fixture dwell under `--fast` when duration is enabled. */
|
|
56
102
|
export declare const FIXTURE_DURATION_FAST_MS_DEFAULT = 600;
|
|
103
|
+
/** Default fixture dwell under demo pace `slow` (gaps / wrongs review). */
|
|
104
|
+
export declare const FIXTURE_DURATION_SLOW_MS_DEFAULT = 4000;
|
|
57
105
|
/** @deprecated Prefer FIXTURE_DURATION_MS_DEFAULT */
|
|
58
106
|
export declare const SLIDE_DURATION_MS_DEFAULT = 2000;
|
|
59
107
|
/** @deprecated Prefer FIXTURE_DURATION_FAST_MS_DEFAULT */
|
|
@@ -64,11 +112,23 @@ export declare const SLIDE_DURATION_FAST_MS_DEFAULT = 600;
|
|
|
64
112
|
*/
|
|
65
113
|
export declare function resolveFixtureDwellMs(fixture: FixtureDurationFields, opts?: {
|
|
66
114
|
gatesFast?: boolean;
|
|
115
|
+
pace?: DemoPace;
|
|
67
116
|
}): number | null;
|
|
68
117
|
/** @deprecated Prefer {@link resolveFixtureDwellMs} */
|
|
69
118
|
export declare function resolveSlideDwellMs(slide: FixtureDurationFields, opts?: {
|
|
70
119
|
gatesFast?: boolean;
|
|
120
|
+
pace?: DemoPace;
|
|
71
121
|
}): number | null;
|
|
122
|
+
/**
|
|
123
|
+
* Effective demo pace for a step: block override > flow episode > normal.
|
|
124
|
+
* Opaque FF / wasFastForward always win as blitz.
|
|
125
|
+
*/
|
|
126
|
+
export declare function resolveStepDemoPace(opts: {
|
|
127
|
+
blockPace?: DemoPace | string | null;
|
|
128
|
+
flowPace?: DemoPace | string | null;
|
|
129
|
+
fastForward?: boolean;
|
|
130
|
+
wasFastForward?: boolean;
|
|
131
|
+
}): DemoPace;
|
|
72
132
|
export type WaygraphSlidesOrFn<Out extends Checkpoint<string>> = readonly WaygraphSlide[] | ((out: Out) => readonly WaygraphSlide[]);
|
|
73
133
|
/** Demo stub phase names (block instruction + flow fixtures). */
|
|
74
134
|
export type HighlightStubPhaseName = "stubBefore" | "stubAfter" | "stubOnError";
|
|
@@ -85,6 +145,7 @@ export type HighlightFixtureMap = Record<string, {
|
|
|
85
145
|
export interface WaygraphHighlight {
|
|
86
146
|
selector: string;
|
|
87
147
|
label: string;
|
|
148
|
+
tone?: HighlightTone;
|
|
88
149
|
}
|
|
89
150
|
export type ResolvedHighlight = WaygraphHighlightStub;
|
|
90
151
|
/**
|
|
@@ -104,12 +165,13 @@ export declare function resolveSlides(block: Block<any, any>, opts?: {
|
|
|
104
165
|
out?: Checkpoint<string>;
|
|
105
166
|
fixtures?: HighlightFixtureMap;
|
|
106
167
|
}): WaygraphSlide[];
|
|
107
|
-
/** Caption for ring / slide UI (label|caption + optional detail/tag). */
|
|
168
|
+
/** Caption for ring / slide UI (label|caption + optional detail/tag + tone icon). */
|
|
108
169
|
export declare function formatHighlightCaption(h: {
|
|
109
170
|
label?: string;
|
|
110
171
|
caption?: string;
|
|
111
172
|
detail?: string;
|
|
112
173
|
tag?: string;
|
|
174
|
+
tone?: HighlightTone | string;
|
|
113
175
|
}): string;
|
|
114
176
|
/**
|
|
115
177
|
* True when the block authored a non-empty stubAfter (after resolve + shim),
|
package/dist/highlights.js
CHANGED
|
@@ -8,10 +8,76 @@
|
|
|
8
8
|
* All fixture shapes share optional {@link FixtureDurationFields} so demo dwell
|
|
9
9
|
* before advancing is authored the same way on stubs, flow fixtures, and slides.
|
|
10
10
|
*/
|
|
11
|
+
const TONE_ICONS = {
|
|
12
|
+
planned: "",
|
|
13
|
+
auto: "",
|
|
14
|
+
info: "i",
|
|
15
|
+
warning: "!",
|
|
16
|
+
danger: "x",
|
|
17
|
+
success: "+",
|
|
18
|
+
};
|
|
19
|
+
/** Map author aliases (`error` -> danger) to a canonical tone. */
|
|
20
|
+
export function normalizeHighlightTone(raw) {
|
|
21
|
+
if (typeof raw !== "string")
|
|
22
|
+
return "planned";
|
|
23
|
+
const t = raw.trim().toLowerCase();
|
|
24
|
+
if (t === "auto" || t === "automation" || t === "engine")
|
|
25
|
+
return "auto";
|
|
26
|
+
if (t === "info" || t === "blue")
|
|
27
|
+
return "info";
|
|
28
|
+
if (t === "warning" || t === "warn" || t === "yellow")
|
|
29
|
+
return "warning";
|
|
30
|
+
if (t === "danger" || t === "error" || t === "red")
|
|
31
|
+
return "danger";
|
|
32
|
+
if (t === "success" || t === "ok" || t === "green")
|
|
33
|
+
return "success";
|
|
34
|
+
if (t === "planned" || t === "purple" || t === "authored")
|
|
35
|
+
return "planned";
|
|
36
|
+
return "planned";
|
|
37
|
+
}
|
|
38
|
+
/** Map size aliases to sm/md/lg. */
|
|
39
|
+
export function normalizeHighlightSize(raw) {
|
|
40
|
+
if (typeof raw !== "string")
|
|
41
|
+
return "md";
|
|
42
|
+
const t = raw.trim().toLowerCase();
|
|
43
|
+
if (t === "sm" || t === "s" || t === "small" || t === "tiny")
|
|
44
|
+
return "sm";
|
|
45
|
+
if (t === "lg" || t === "l" || t === "large" || t === "big" || t === "xl")
|
|
46
|
+
return "lg";
|
|
47
|
+
return "md";
|
|
48
|
+
}
|
|
49
|
+
/** Map weight aliases to normal/bold. */
|
|
50
|
+
export function normalizeHighlightWeight(raw) {
|
|
51
|
+
if (typeof raw !== "string")
|
|
52
|
+
return "normal";
|
|
53
|
+
const t = raw.trim().toLowerCase();
|
|
54
|
+
if (t === "bold" || t === "strong" || t === "heavy" || t === "b")
|
|
55
|
+
return "bold";
|
|
56
|
+
return "normal";
|
|
57
|
+
}
|
|
58
|
+
/** Prefix label with a short ASCII icon for semantic tones. */
|
|
59
|
+
export function toneIconPrefix(tone) {
|
|
60
|
+
const icon = TONE_ICONS[tone];
|
|
61
|
+
return icon ? `[${icon}] ` : "";
|
|
62
|
+
}
|
|
63
|
+
export function normalizeDemoPace(raw) {
|
|
64
|
+
if (typeof raw !== "string")
|
|
65
|
+
return "normal";
|
|
66
|
+
const t = raw.trim().toLowerCase();
|
|
67
|
+
if (t === "blitz" || t === "ff")
|
|
68
|
+
return "blitz";
|
|
69
|
+
if (t === "fast" || t === "quick")
|
|
70
|
+
return "fast";
|
|
71
|
+
if (t === "slow" || t === "careful" || t === "review")
|
|
72
|
+
return "slow";
|
|
73
|
+
return "normal";
|
|
74
|
+
}
|
|
11
75
|
/** Default fixture dwell when `duration: true`. */
|
|
12
76
|
export const FIXTURE_DURATION_MS_DEFAULT = 2000;
|
|
13
77
|
/** Default fixture dwell under `--fast` when duration is enabled. */
|
|
14
78
|
export const FIXTURE_DURATION_FAST_MS_DEFAULT = 600;
|
|
79
|
+
/** Default fixture dwell under demo pace `slow` (gaps / wrongs review). */
|
|
80
|
+
export const FIXTURE_DURATION_SLOW_MS_DEFAULT = 4000;
|
|
15
81
|
/** @deprecated Prefer FIXTURE_DURATION_MS_DEFAULT */
|
|
16
82
|
export const SLIDE_DURATION_MS_DEFAULT = FIXTURE_DURATION_MS_DEFAULT;
|
|
17
83
|
/** @deprecated Prefer FIXTURE_DURATION_FAST_MS_DEFAULT */
|
|
@@ -23,23 +89,40 @@ export const SLIDE_DURATION_FAST_MS_DEFAULT = FIXTURE_DURATION_FAST_MS_DEFAULT;
|
|
|
23
89
|
export function resolveFixtureDwellMs(fixture, opts) {
|
|
24
90
|
if (fixture.duration === undefined || fixture.duration === false)
|
|
25
91
|
return null;
|
|
26
|
-
const
|
|
92
|
+
const pace = normalizeDemoPace(opts?.pace ?? (opts?.gatesFast ? "fast" : "normal"));
|
|
27
93
|
const fastMs = typeof fixture.fastMode === "number" && Number.isFinite(fixture.fastMode) && fixture.fastMode >= 0
|
|
28
94
|
? fixture.fastMode
|
|
29
95
|
: FIXTURE_DURATION_FAST_MS_DEFAULT;
|
|
30
|
-
if (fast)
|
|
96
|
+
if (pace === "blitz" || pace === "fast" || opts?.gatesFast)
|
|
31
97
|
return fastMs;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
98
|
+
const normalMs = fixture.duration === true
|
|
99
|
+
? FIXTURE_DURATION_MS_DEFAULT
|
|
100
|
+
: typeof fixture.duration === "number" && Number.isFinite(fixture.duration) && fixture.duration >= 0
|
|
101
|
+
? fixture.duration
|
|
102
|
+
: FIXTURE_DURATION_MS_DEFAULT;
|
|
103
|
+
if (pace === "slow")
|
|
104
|
+
return Math.max(normalMs, FIXTURE_DURATION_SLOW_MS_DEFAULT);
|
|
105
|
+
return normalMs;
|
|
38
106
|
}
|
|
39
107
|
/** @deprecated Prefer {@link resolveFixtureDwellMs} */
|
|
40
108
|
export function resolveSlideDwellMs(slide, opts) {
|
|
41
109
|
return resolveFixtureDwellMs(slide, opts);
|
|
42
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Effective demo pace for a step: block override > flow episode > normal.
|
|
113
|
+
* Opaque FF / wasFastForward always win as blitz.
|
|
114
|
+
*/
|
|
115
|
+
export function resolveStepDemoPace(opts) {
|
|
116
|
+
if (opts.fastForward || opts.wasFastForward)
|
|
117
|
+
return "blitz";
|
|
118
|
+
if (opts.blockPace != null && opts.blockPace !== "") {
|
|
119
|
+
return normalizeDemoPace(opts.blockPace);
|
|
120
|
+
}
|
|
121
|
+
if (opts.flowPace != null && opts.flowPace !== "") {
|
|
122
|
+
return normalizeDemoPace(opts.flowPace);
|
|
123
|
+
}
|
|
124
|
+
return "normal";
|
|
125
|
+
}
|
|
43
126
|
function isStubPhase(v) {
|
|
44
127
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
45
128
|
}
|
|
@@ -53,10 +136,24 @@ function pickDurationFields(base, patch) {
|
|
|
53
136
|
out.fastMode = fastMode;
|
|
54
137
|
return out;
|
|
55
138
|
}
|
|
139
|
+
function pickStyleFields(base, patch) {
|
|
140
|
+
const out = {};
|
|
141
|
+
const tone = patch?.tone !== undefined ? patch.tone : base?.tone;
|
|
142
|
+
const size = patch?.size !== undefined ? patch.size : base?.size;
|
|
143
|
+
const weight = patch?.weight !== undefined ? patch.weight : base?.weight;
|
|
144
|
+
if (tone !== undefined)
|
|
145
|
+
out.tone = normalizeHighlightTone(tone);
|
|
146
|
+
if (size !== undefined)
|
|
147
|
+
out.size = normalizeHighlightSize(size);
|
|
148
|
+
if (weight !== undefined)
|
|
149
|
+
out.weight = normalizeHighlightWeight(weight);
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
56
152
|
function mergeSlot(base, patch) {
|
|
57
153
|
if (!base && !patch)
|
|
58
154
|
return null;
|
|
59
155
|
const dwell = pickDurationFields(base, patch);
|
|
156
|
+
const style = pickStyleFields(base, patch);
|
|
60
157
|
if (!base && patch) {
|
|
61
158
|
if (!patch.selector)
|
|
62
159
|
return null;
|
|
@@ -65,11 +162,18 @@ function mergeSlot(base, patch) {
|
|
|
65
162
|
label: patch.label,
|
|
66
163
|
...(patch.detail ? { detail: patch.detail } : {}),
|
|
67
164
|
...(patch.tag ? { tag: patch.tag } : {}),
|
|
165
|
+
...style,
|
|
68
166
|
...dwell,
|
|
69
167
|
};
|
|
70
168
|
}
|
|
71
|
-
if (base && !patch)
|
|
72
|
-
return {
|
|
169
|
+
if (base && !patch) {
|
|
170
|
+
return {
|
|
171
|
+
...base,
|
|
172
|
+
...(base.tone ? { tone: normalizeHighlightTone(base.tone) } : {}),
|
|
173
|
+
...(base.size ? { size: normalizeHighlightSize(base.size) } : {}),
|
|
174
|
+
...(base.weight ? { weight: normalizeHighlightWeight(base.weight) } : {}),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
73
177
|
return {
|
|
74
178
|
selector: patch.selector ?? base.selector,
|
|
75
179
|
label: patch.label,
|
|
@@ -79,6 +183,7 @@ function mergeSlot(base, patch) {
|
|
|
79
183
|
? { detail: base.detail }
|
|
80
184
|
: {}),
|
|
81
185
|
...(patch.tag !== undefined ? { tag: patch.tag } : base.tag ? { tag: base.tag } : {}),
|
|
186
|
+
...style,
|
|
82
187
|
...dwell,
|
|
83
188
|
};
|
|
84
189
|
}
|
|
@@ -88,7 +193,11 @@ function phaseFromLegacyHighlights(highlights) {
|
|
|
88
193
|
return out;
|
|
89
194
|
highlights.forEach((h, i) => {
|
|
90
195
|
if (h && typeof h.selector === "string" && typeof h.label === "string") {
|
|
91
|
-
out[String(i)] = {
|
|
196
|
+
out[String(i)] = {
|
|
197
|
+
selector: h.selector,
|
|
198
|
+
label: h.label,
|
|
199
|
+
...(h.tone ? { tone: normalizeHighlightTone(h.tone) } : {}),
|
|
200
|
+
};
|
|
92
201
|
}
|
|
93
202
|
});
|
|
94
203
|
return out;
|
|
@@ -169,13 +278,15 @@ export function resolveSlides(block, opts) {
|
|
|
169
278
|
return [];
|
|
170
279
|
return raw.filter((s) => s && typeof s.caption === "string" && s.caption.length > 0);
|
|
171
280
|
}
|
|
172
|
-
/** Caption for ring / slide UI (label|caption + optional detail/tag). */
|
|
281
|
+
/** Caption for ring / slide UI (label|caption + optional detail/tag + tone icon). */
|
|
173
282
|
export function formatHighlightCaption(h) {
|
|
174
|
-
const
|
|
283
|
+
const tone = normalizeHighlightTone(h.tone);
|
|
284
|
+
const icon = toneIconPrefix(tone);
|
|
285
|
+
const primary = (h.label ?? h.caption ?? "").trim();
|
|
175
286
|
const tag = h.tag ? `[${h.tag}] ` : "";
|
|
176
287
|
if (h.detail)
|
|
177
|
-
return `${tag}${primary} - ${h.detail}`;
|
|
178
|
-
return `${tag}${primary}`;
|
|
288
|
+
return `${icon}${tag}${primary} - ${h.detail}`;
|
|
289
|
+
return `${icon}${tag}${primary}`;
|
|
179
290
|
}
|
|
180
291
|
/**
|
|
181
292
|
* True when the block authored a non-empty stubAfter (after resolve + shim),
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ export type { Checkpoint, Instruction, Block, DefinedBlock, Start, WaygraphInsta
|
|
|
2
2
|
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
|
-
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, withExpectedFailure, withHighlightFixtures, chainFlow, } from "./engine.js";
|
|
6
|
-
export { resolveHighlightSlots, resolveSlides, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
7
|
-
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, } from "./highlights.js";
|
|
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, withExpectedFailure, withHighlightFixtures, chainFlow, } from "./engine.js";
|
|
6
|
+
export { resolveHighlightSlots, resolveSlides, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, toneIconPrefix, resolveStepDemoPace, 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, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, DemoPace, } 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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
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
|
-
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, withExpectedFailure, withHighlightFixtures, chainFlow, } from "./engine.js";
|
|
5
|
-
export { resolveHighlightSlots, resolveSlides, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
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, withExpectedFailure, withHighlightFixtures, chainFlow, } from "./engine.js";
|
|
5
|
+
export { resolveHighlightSlots, resolveSlides, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, toneIconPrefix, resolveStepDemoPace, 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/dist/step-overlay.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { MemPage } from "./mem-page.js";
|
|
|
3
3
|
export declare function installDemoChrome(page: Page, title?: string, options?: {
|
|
4
4
|
banner?: boolean;
|
|
5
5
|
}): Promise<void>;
|
|
6
|
-
/** Demo cursor + ring on fill/click during auto explore headful runs (automation =
|
|
6
|
+
/** Demo cursor + ring on fill/click during auto explore headful runs (automation = gray). */
|
|
7
7
|
export declare function instrumentInteractionHighlighting(page: Page, mem: MemPage, slowMo: number): void;
|
|
8
8
|
declare global {
|
|
9
9
|
interface Window {
|
package/dist/step-overlay.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
const RING_CSS = "#wg-ring{position:fixed;z-index:2147483646;pointer-events:none;opacity:0;" +
|
|
2
2
|
"border:2.5px solid #7C3AED;border-radius:10px;box-shadow:0 0 0 4px rgba(124,58,237,.16);" +
|
|
3
3
|
"transition:opacity .3s ease,border-color .15s,box-shadow .15s;}" +
|
|
4
|
-
|
|
4
|
+
// Automation = gray; semantic tones for authored highlights.
|
|
5
|
+
"#wg-ring[data-tone=auto]{border-color:#9CA3AF;box-shadow:0 0 0 4px rgba(156,163,175,.28);}" +
|
|
6
|
+
"#wg-ring[data-tone=info]{border-color:#3B82F6;box-shadow:0 0 0 4px rgba(59,130,246,.22);}" +
|
|
7
|
+
"#wg-ring[data-tone=warning]{border-color:#EAB308;box-shadow:0 0 0 4px rgba(234,179,8,.22);}" +
|
|
8
|
+
"#wg-ring[data-tone=danger]{border-color:#EF4444;box-shadow:0 0 0 4px rgba(239,68,68,.22);}" +
|
|
9
|
+
"#wg-ring[data-tone=success]{border-color:#22C55E;box-shadow:0 0 0 4px rgba(34,197,94,.22);}" +
|
|
5
10
|
"#wg-ring-label{position:fixed;z-index:2147483646;pointer-events:none;opacity:0;" +
|
|
6
11
|
"white-space:nowrap;padding:4px 9px;border-radius:7px;background:#7C3AED;color:#fff;" +
|
|
7
12
|
"font:600 12px/1.2 system-ui,sans-serif;transition:opacity .3s ease,background .15s,color .15s;}" +
|
|
8
|
-
"#wg-ring-label[data-tone=auto]{background:#
|
|
13
|
+
"#wg-ring-label[data-tone=auto]{background:#6B7280;color:#fff;}" +
|
|
14
|
+
"#wg-ring-label[data-tone=info]{background:#2563EB;color:#fff;}" +
|
|
15
|
+
"#wg-ring-label[data-tone=warning]{background:#EAB308;color:#1c1917;}" +
|
|
16
|
+
"#wg-ring-label[data-tone=danger]{background:#DC2626;color:#fff;}" +
|
|
17
|
+
"#wg-ring-label[data-tone=success]{background:#16A34A;color:#fff;}" +
|
|
18
|
+
"#wg-ring[data-size=sm]{border-width:1.5px;border-radius:8px;}" +
|
|
19
|
+
"#wg-ring[data-size=lg]{border-width:4px;border-radius:12px;}" +
|
|
20
|
+
"#wg-ring-label[data-size=sm]{font-size:10px;line-height:1.25;padding:4px 7px;border-radius:5px;}" +
|
|
21
|
+
"#wg-ring-label[data-size=lg]{font-size:16px;line-height:1.35;padding:8px 14px;border-radius:9px;}" +
|
|
22
|
+
"#wg-ring-label[data-weight=bold]{font-weight:800;}" +
|
|
23
|
+
"#wg-ring-label[data-weight=normal]{font-weight:600;}" +
|
|
9
24
|
"#wg-cursor{position:fixed;z-index:2147483647;width:24px;height:24px;pointer-events:none;" +
|
|
10
25
|
"left:0;top:0;opacity:0;margin:0;" +
|
|
11
26
|
"transition:transform var(--wg-cursor-ms,600ms) cubic-bezier(.22,1,.36,1),opacity .2s ease;" +
|
|
@@ -13,7 +28,11 @@ const RING_CSS = "#wg-ring{position:fixed;z-index:2147483646;pointer-events:none
|
|
|
13
28
|
"#wg-click-pulse{position:fixed;z-index:2147483647;width:14px;height:14px;" +
|
|
14
29
|
"margin-left:-7px;margin-top:-7px;border-radius:50%;pointer-events:none;opacity:0;" +
|
|
15
30
|
"border:2px solid #7C3AED;background:rgba(124,58,237,.25);}" +
|
|
16
|
-
"#wg-click-pulse[data-tone=auto]{border-color:#
|
|
31
|
+
"#wg-click-pulse[data-tone=auto]{border-color:#9CA3AF;background:rgba(156,163,175,.28);}" +
|
|
32
|
+
"#wg-click-pulse[data-tone=info]{border-color:#3B82F6;background:rgba(59,130,246,.28);}" +
|
|
33
|
+
"#wg-click-pulse[data-tone=warning]{border-color:#EAB308;background:rgba(234,179,8,.28);}" +
|
|
34
|
+
"#wg-click-pulse[data-tone=danger]{border-color:#EF4444;background:rgba(239,68,68,.28);}" +
|
|
35
|
+
"#wg-click-pulse[data-tone=success]{border-color:#22C55E;background:rgba(34,197,94,.28);}" +
|
|
17
36
|
"#wg-click-pulse.wg-pulse{animation:wg-pulse .5s ease-out;}" +
|
|
18
37
|
"@keyframes wg-pulse{0%{opacity:.9;transform:scale(.4);}100%{opacity:0;transform:scale(2.4);}}" +
|
|
19
38
|
"#wg-banner{position:fixed;z-index:2147483647;top:14px;left:14px;" +
|
|
@@ -65,25 +84,48 @@ export async function installDemoChrome(page, title = "waygraph auto", options =
|
|
|
65
84
|
const pulse = document.getElementById("wg-click-pulse");
|
|
66
85
|
if (!pulse)
|
|
67
86
|
return;
|
|
68
|
-
|
|
87
|
+
const raw = (tone || "planned") + "";
|
|
88
|
+
pulse.dataset.tone =
|
|
89
|
+
raw === "auto" ||
|
|
90
|
+
raw === "info" ||
|
|
91
|
+
raw === "warning" ||
|
|
92
|
+
raw === "danger" ||
|
|
93
|
+
raw === "success"
|
|
94
|
+
? raw
|
|
95
|
+
: "planned";
|
|
69
96
|
pulse.style.left = x + "px";
|
|
70
97
|
pulse.style.top = y + "px";
|
|
71
98
|
pulse.classList.remove("wg-pulse");
|
|
72
99
|
void pulse.offsetWidth;
|
|
73
100
|
pulse.classList.add("wg-pulse");
|
|
74
101
|
};
|
|
75
|
-
window.__wgPositionRing = (box, label, tone) => {
|
|
102
|
+
window.__wgPositionRing = (box, label, tone, style) => {
|
|
76
103
|
const ring = document.getElementById("wg-ring");
|
|
77
104
|
const ringLabel = document.getElementById("wg-ring-label");
|
|
78
105
|
if (!ring || !ringLabel)
|
|
79
106
|
return;
|
|
80
|
-
const
|
|
107
|
+
const raw = (tone || "planned") + "";
|
|
108
|
+
const t = raw === "auto" ||
|
|
109
|
+
raw === "info" ||
|
|
110
|
+
raw === "warning" ||
|
|
111
|
+
raw === "danger" ||
|
|
112
|
+
raw === "success"
|
|
113
|
+
? raw
|
|
114
|
+
: "planned";
|
|
115
|
+
const sizeRaw = ((style && style.size) || "md") + "";
|
|
116
|
+
const size = sizeRaw === "sm" || sizeRaw === "lg" ? sizeRaw : "md";
|
|
117
|
+
const weightRaw = ((style && style.weight) || "normal") + "";
|
|
118
|
+
const weight = weightRaw === "bold" ? "bold" : "normal";
|
|
81
119
|
ring.dataset.tone = t;
|
|
82
120
|
ringLabel.dataset.tone = t;
|
|
83
|
-
ring.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
121
|
+
ring.dataset.size = size;
|
|
122
|
+
ringLabel.dataset.size = size;
|
|
123
|
+
ringLabel.dataset.weight = weight;
|
|
124
|
+
const pad = size === "sm" ? 3 : size === "lg" ? 10 : 6;
|
|
125
|
+
ring.style.left = box.x - pad + "px";
|
|
126
|
+
ring.style.top = box.y - pad + "px";
|
|
127
|
+
ring.style.width = box.width + pad * 2 + "px";
|
|
128
|
+
ring.style.height = box.height + pad * 2 + "px";
|
|
87
129
|
ring.style.opacity = "1";
|
|
88
130
|
ringLabel.textContent = label;
|
|
89
131
|
ringLabel.style.left = box.x + "px";
|
|
@@ -136,7 +178,7 @@ async function clickPulseAt(page, x, y, tone = "auto") {
|
|
|
136
178
|
.evaluate(({ x, y, tone }) => window.__wgClickPulse?.(x, y, tone), { x, y, tone })
|
|
137
179
|
.catch(() => { });
|
|
138
180
|
}
|
|
139
|
-
/** Demo cursor + ring on fill/click during auto explore headful runs (automation =
|
|
181
|
+
/** Demo cursor + ring on fill/click during auto explore headful runs (automation = gray). */
|
|
140
182
|
export function instrumentInteractionHighlighting(page, mem, slowMo) {
|
|
141
183
|
const typeDelay = () => (slowMo ? 0 : 30);
|
|
142
184
|
const clickPrePop = () => (slowMo ? 300 : 700);
|
package/dist/types.d.ts
CHANGED
|
@@ -209,6 +209,11 @@ export interface Block<In extends Checkpoint<string>, Out extends Checkpoint<str
|
|
|
209
209
|
* @example requires: [LoginCreds.key]
|
|
210
210
|
*/
|
|
211
211
|
requires?: readonly MemKey<any>[];
|
|
212
|
+
/**
|
|
213
|
+
* Demo pacing for this Block or compose group (`withBlockPace`).
|
|
214
|
+
* Demo/step only - `runGraph` ignores. Prefer `fastForwardComposeBlock` for blitz.
|
|
215
|
+
*/
|
|
216
|
+
demoPace?: import("./highlights.js").DemoPace;
|
|
212
217
|
/**
|
|
213
218
|
* Optional. When this Block's real choices depend on the live page
|
|
214
219
|
* (which of several items to add to cart, which of several rows to
|