waygraph 0.10.8 → 0.11.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/README.md +3 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +331 -44
- package/dist/engine.d.ts +23 -0
- package/dist/engine.js +21 -0
- package/dist/highlights.d.ts +76 -0
- package/dist/highlights.js +136 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/types.d.ts +26 -15
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +29 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +8 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +6 -0
- package/examples/saucedemo/src/flows/checkout.flow.ts +61 -22
- package/package.json +1 -1
- package/templates/quickstart/package.json +1 -1
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +29 -1
- package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +8 -1
- package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +6 -0
- package/templates/quickstart/src/flows/checkout.flow.ts +61 -22
- package/templates/scaffold/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Primary verbs (less is more):
|
|
6
6
|
* auto Explore picker; `.flow.ts` / `--blocks From To` = run that path
|
|
7
|
-
* demo Watch (step overlay); `--blocks` `--data` `--auto-next` `--
|
|
7
|
+
* demo Watch (step overlay); `--blocks` `--data` `--auto-next` `--fast` `--full`
|
|
8
8
|
* run Execute; `--blocks` `--data` `--non-headless` `--video`
|
|
9
9
|
*
|
|
10
10
|
* Also: list / nav / validate / check / graph / init / try
|
|
@@ -200,6 +200,12 @@ const CHAIN_RUNNER_SCRIPT = `
|
|
|
200
200
|
import { readdirSync, mkdirSync } from "node:fs";
|
|
201
201
|
import { join } from "node:path";
|
|
202
202
|
import { tmpdir } from "node:os";
|
|
203
|
+
import {
|
|
204
|
+
resolveHighlightSlots,
|
|
205
|
+
resolveSlides,
|
|
206
|
+
formatHighlightCaption,
|
|
207
|
+
hasAuthoredStubAfter,
|
|
208
|
+
} from "waygraph";
|
|
203
209
|
|
|
204
210
|
function walkDir(dir, pattern) {
|
|
205
211
|
const results = [];
|
|
@@ -395,8 +401,8 @@ const RING_CSS =
|
|
|
395
401
|
// pushed back onto screen, same width, never shrunk. See
|
|
396
402
|
// window.__wgPositionRing in installOverlay.
|
|
397
403
|
"#wg-ring-label{position:fixed;z-index:2147483646;pointer-events:none;opacity:0;" +
|
|
398
|
-
"white-space:
|
|
399
|
-
"font:600 12px/1.
|
|
404
|
+
"max-width:min(360px,70vw);white-space:normal;padding:6px 10px;border-radius:7px;background:#7C3AED;color:#fff;" +
|
|
405
|
+
"font:600 12px/1.35 system-ui,sans-serif;transition:opacity .3s ease;}" +
|
|
400
406
|
// Mouse cursor icon that travels to a target before it's acted on, plus a
|
|
401
407
|
// quick expanding ripple at the moment of a click - same idea as
|
|
402
408
|
// help-center-clip-engine's #clip-cursor/#clip-ring (video-pipeline). The
|
|
@@ -469,12 +475,16 @@ const RING_CSS =
|
|
|
469
475
|
"@keyframes wg-ep-enter{0%{box-shadow:0 0 0 0 rgba(124,58,237,.55);}" +
|
|
470
476
|
"100%{box-shadow:0 0 0 10px rgba(124,58,237,0);}}" +
|
|
471
477
|
"#wg-episodes .wg-ep-entered{border-radius:6px;animation:wg-ep-enter 1.1s ease-out;}" +
|
|
472
|
-
//
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
"#wg-modules{
|
|
477
|
-
"
|
|
478
|
+
// Block strip: carousel (default) or classic wrap (--full).
|
|
479
|
+
"#wg-modules.wg-modules-carousel{display:flex;flex-wrap:nowrap;gap:8px;margin:0 0 10px;" +
|
|
480
|
+
"overflow-x:auto;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;" +
|
|
481
|
+
"padding:4px 2px 8px;scrollbar-width:thin;}" +
|
|
482
|
+
"#wg-modules.wg-modules-carousel .wg-mod{flex:0 0 auto;scroll-snap-align:center;" +
|
|
483
|
+
"padding:6px 12px;border-radius:999px;font:600 12px system-ui,sans-serif;}" +
|
|
484
|
+
"#wg-modules.wg-modules-carousel .wg-mod-current{transform:scale(1.06);" +
|
|
485
|
+
"box-shadow:0 0 0 2px rgba(124,58,237,.45);}" +
|
|
486
|
+
"#wg-modules.wg-modules-full{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 10px;}" +
|
|
487
|
+
"#wg-modules.wg-modules-full .wg-mod{padding:3px 9px;border-radius:6px;font:600 11px system-ui,sans-serif;}" +
|
|
478
488
|
"#wg-modules .wg-mod-done{background:#2a1650;color:#9a7ad1;}" +
|
|
479
489
|
"#wg-modules .wg-mod-current{background:#7C3AED;color:#fff;}" +
|
|
480
490
|
"#wg-modules .wg-mod-upcoming{background:transparent;color:#5a4a80;border:1px solid #3a2a60;}" +
|
|
@@ -525,22 +535,23 @@ const RING_CSS =
|
|
|
525
535
|
"#wg-panel .wg-expected-heading{color:#ffcf7a;}" +
|
|
526
536
|
"#wg-panel .wg-expected-reason{font:600 12.5px/1.5 system-ui,sans-serif;background:#2a2410;" +
|
|
527
537
|
"color:#ffe6ae;border-radius:8px;padding:10px;margin:0 0 8px;}" +
|
|
528
|
-
// Hide / Show chrome
|
|
538
|
+
// Hide / Show chrome: collapsed = compact "N / M · block" pill (not empty chrome).
|
|
529
539
|
"#wg-panel .wg-chrome{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0 0 8px;}" +
|
|
530
540
|
"#wg-panel .wg-chrome-title{font:700 11px/1.2 system-ui,sans-serif;color:#c9a6ff;" +
|
|
531
541
|
"letter-spacing:.04em;text-transform:uppercase;}" +
|
|
532
542
|
"#wg-panel button.wg-hide-btn{margin:0;padding:4px 10px;font:600 11px system-ui,sans-serif;" +
|
|
533
543
|
"background:#3a2a60;color:#e8dcff;border:1px solid #5b3aa8;border-radius:6px;cursor:pointer;}" +
|
|
534
544
|
"#wg-panel button.wg-hide-btn:hover{background:#4b2a80;}" +
|
|
535
|
-
"#wg-panel.wg-collapsed{width:auto;max-width:
|
|
545
|
+
"#wg-panel.wg-collapsed{width:auto;max-width:92vw;padding:8px 12px;max-height:none;overflow:hidden;}" +
|
|
536
546
|
"#wg-panel.wg-collapsed .wg-body{display:none;}" +
|
|
537
547
|
"#wg-panel.wg-collapsed .wg-chrome{margin:0;}" +
|
|
548
|
+
"#wg-panel.wg-collapsed .wg-chrome-title{font:700 13px/1.25 system-ui,sans-serif;color:#fff;" +
|
|
549
|
+
"letter-spacing:0;text-transform:none;}" +
|
|
538
550
|
"@media (max-width:640px){" +
|
|
539
551
|
"#wg-panel{left:8px;right:8px;bottom:8px;transform:none;max-width:none;width:auto;" +
|
|
540
552
|
"max-height:min(55vh,calc(100vh - 16px));padding:12px 14px;border-radius:12px;}" +
|
|
541
553
|
"#wg-panel.wg-collapsed{left:50%;right:auto;transform:translateX(-50%);width:auto;}" +
|
|
542
554
|
"#wg-episodes{overflow-x:auto;-webkit-overflow-scrolling:touch;flex-wrap:nowrap;}" +
|
|
543
|
-
"#wg-modules{gap:4px;}" +
|
|
544
555
|
"#wg-panel .wg-narration{font-size:13px;}" +
|
|
545
556
|
"#wg-panel button{width:100%;}" +
|
|
546
557
|
"#wg-panel .wg-error-actions{flex-direction:column;}" +
|
|
@@ -736,8 +747,11 @@ async function installOverlay(page, title) {
|
|
|
736
747
|
}
|
|
737
748
|
if (iconLink.href !== favicon) iconLink.href = favicon;
|
|
738
749
|
// Hide/Show for #wg-panel - call after every panel.innerHTML refresh.
|
|
739
|
-
|
|
750
|
+
// Collapsed title shows "N / M · block" (compact progress). forceCollapsed
|
|
751
|
+
// is for auto-next navs / video - does not overwrite the user's Hide preference.
|
|
752
|
+
window.__wgWirePanelChrome = (panel, storageKey, chromeTitle, opts) => {
|
|
740
753
|
if (!panel) return;
|
|
754
|
+
opts = opts || {};
|
|
741
755
|
if (!panel.querySelector(":scope > .wg-chrome")) {
|
|
742
756
|
const body = document.createElement("div");
|
|
743
757
|
body.className = "wg-body";
|
|
@@ -757,31 +771,56 @@ async function installOverlay(page, title) {
|
|
|
757
771
|
panel.appendChild(chrome);
|
|
758
772
|
panel.appendChild(body);
|
|
759
773
|
}
|
|
760
|
-
const
|
|
774
|
+
const stepLabel = opts.stepLabel || panel.dataset.wgStepLabel || "";
|
|
775
|
+
if (stepLabel) panel.dataset.wgStepLabel = stepLabel;
|
|
776
|
+
const apply = (hidden, persist) => {
|
|
761
777
|
panel.classList.toggle("wg-collapsed", hidden);
|
|
762
778
|
const t = panel.querySelector("[data-wg-toggle]");
|
|
779
|
+
const titleEl = panel.querySelector(".wg-chrome-title");
|
|
780
|
+
const label = panel.dataset.wgStepLabel || stepLabel;
|
|
781
|
+
if (titleEl) {
|
|
782
|
+
titleEl.textContent = hidden
|
|
783
|
+
? (label || chromeTitle || "waygraph demo")
|
|
784
|
+
: (chromeTitle || "waygraph demo");
|
|
785
|
+
}
|
|
763
786
|
if (t) t.textContent = hidden ? "Show" : "Hide";
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
787
|
+
if (persist !== false) {
|
|
788
|
+
try {
|
|
789
|
+
localStorage.setItem(storageKey, hidden ? "1" : "0");
|
|
790
|
+
} catch {
|
|
791
|
+
/* private mode */
|
|
792
|
+
}
|
|
768
793
|
}
|
|
769
794
|
};
|
|
770
795
|
let hidden = false;
|
|
771
|
-
|
|
772
|
-
hidden =
|
|
773
|
-
}
|
|
774
|
-
|
|
796
|
+
if (opts.forceCollapsed === true) {
|
|
797
|
+
hidden = true;
|
|
798
|
+
} else if (opts.forceCollapsed === false) {
|
|
799
|
+
hidden = false;
|
|
800
|
+
} else {
|
|
801
|
+
try {
|
|
802
|
+
hidden = localStorage.getItem(storageKey) === "1";
|
|
803
|
+
} catch {
|
|
804
|
+
/* ignore */
|
|
805
|
+
}
|
|
775
806
|
}
|
|
776
|
-
apply(hidden);
|
|
807
|
+
apply(hidden, opts.forceCollapsed === true ? false : true);
|
|
777
808
|
const toggle = panel.querySelector("[data-wg-toggle]");
|
|
778
809
|
if (toggle && !toggle.dataset.wgWired) {
|
|
779
810
|
toggle.dataset.wgWired = "1";
|
|
780
811
|
toggle.addEventListener("click", (e) => {
|
|
781
812
|
e.stopPropagation();
|
|
782
|
-
apply(!panel.classList.contains("wg-collapsed"));
|
|
813
|
+
apply(!panel.classList.contains("wg-collapsed"), true);
|
|
783
814
|
});
|
|
784
815
|
}
|
|
816
|
+
const cur = panel.querySelector("#wg-modules .wg-mod-current");
|
|
817
|
+
if (cur && typeof cur.scrollIntoView === "function") {
|
|
818
|
+
try {
|
|
819
|
+
cur.scrollIntoView({ inline: "center", block: "nearest", behavior: "smooth" });
|
|
820
|
+
} catch {
|
|
821
|
+
cur.scrollIntoView(false);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
785
824
|
};
|
|
786
825
|
},
|
|
787
826
|
{ title, favicon: WAYGRAPH_FAVICON, bannerPos, envAutoplay },
|
|
@@ -850,6 +889,7 @@ async function renderBeforeStep(page, info) {
|
|
|
850
889
|
// step's position WITHIN that subset, not the whole chain's index -
|
|
851
890
|
// "in episode 2 it shows 2nd to the last... it should start from the
|
|
852
891
|
// first block" (Dan).
|
|
892
|
+
const modulesClass = info.stepperMode === "full" ? "wg-modules-full" : "wg-modules-carousel";
|
|
853
893
|
const modulesHtml = info.allNames
|
|
854
894
|
.map((name, idx) => {
|
|
855
895
|
const cls = idx < info.moduleIndex ? "wg-mod-done" : idx === info.moduleIndex ? "wg-mod-current" : "wg-mod-upcoming";
|
|
@@ -864,7 +904,7 @@ async function renderBeforeStep(page, info) {
|
|
|
864
904
|
let html =
|
|
865
905
|
"<div id=\\"wg-progress\\"><div id=\\"wg-progress-bar\\" style=\\"width:" + pct + "%\\"></div></div>" +
|
|
866
906
|
episodesHtml +
|
|
867
|
-
"<div id=\\"wg-modules\\">" + modulesHtml + "</div>" +
|
|
907
|
+
"<div id=\\"wg-modules\\" class=\\"" + modulesClass + "\\">" + modulesHtml + "</div>" +
|
|
868
908
|
"<h3>Step " + (info.index + 1) + " / " + info.total + " - " + info.blockName + "</h3>" +
|
|
869
909
|
narrationHtml;
|
|
870
910
|
if (info.keys.length === 0) {
|
|
@@ -897,7 +937,12 @@ async function renderBeforeStep(page, info) {
|
|
|
897
937
|
document.documentElement.appendChild(panel);
|
|
898
938
|
requestAnimationFrame(() => panel.classList.add("wg-in"));
|
|
899
939
|
}
|
|
900
|
-
if (window.__wgWirePanelChrome)
|
|
940
|
+
if (window.__wgWirePanelChrome) {
|
|
941
|
+
window.__wgWirePanelChrome(panel, "wg-panel-hidden", "waygraph demo", {
|
|
942
|
+
stepLabel: (info.index + 1) + " / " + info.total + (info.blockName ? " · " + info.blockName : ""),
|
|
943
|
+
forceCollapsed: info.forceCollapsed === true ? true : info.forceCollapsed === false ? false : undefined,
|
|
944
|
+
});
|
|
945
|
+
}
|
|
901
946
|
// Live, human-readable preview of what a MemKey's raw JSON will
|
|
902
947
|
// actually write - object fields become "Field: value" lines
|
|
903
948
|
// (camelCase split the same way state tags already are); a
|
|
@@ -1040,6 +1085,7 @@ async function renderAfterStep(page, info) {
|
|
|
1040
1085
|
: "Step " + (info.index + 1) + " / " + info.total + " - " + info.blockName + " done";
|
|
1041
1086
|
const buttonLabel = info.isLast ? "Finish" : "Next \\u25B6";
|
|
1042
1087
|
const escA = (s) => String(s).replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
1088
|
+
const modulesClass = info.stepperMode === "full" ? "wg-modules-full" : "wg-modules-carousel";
|
|
1043
1089
|
const modulesHtml = info.allNames
|
|
1044
1090
|
.map((name, idx) => {
|
|
1045
1091
|
const cls = idx <= info.moduleIndex ? "wg-mod-done" : "wg-mod-upcoming";
|
|
@@ -1094,7 +1140,7 @@ async function renderAfterStep(page, info) {
|
|
|
1094
1140
|
panel.innerHTML =
|
|
1095
1141
|
"<div id=\\"wg-progress\\"><div id=\\"wg-progress-bar\\" style=\\"width:" + pct + "%\\"></div></div>" +
|
|
1096
1142
|
episodesHtml +
|
|
1097
|
-
"<div id=\\"wg-modules\\">" + modulesHtml + "</div>" +
|
|
1143
|
+
"<div id=\\"wg-modules\\" class=\\"" + modulesClass + "\\">" + modulesHtml + "</div>" +
|
|
1098
1144
|
"<h3>" + heading + "</h3>" +
|
|
1099
1145
|
resultHtml +
|
|
1100
1146
|
gateHtml;
|
|
@@ -1102,7 +1148,12 @@ async function renderAfterStep(page, info) {
|
|
|
1102
1148
|
document.documentElement.appendChild(panel);
|
|
1103
1149
|
requestAnimationFrame(() => panel.classList.add("wg-in"));
|
|
1104
1150
|
}
|
|
1105
|
-
if (window.__wgWirePanelChrome)
|
|
1151
|
+
if (window.__wgWirePanelChrome) {
|
|
1152
|
+
window.__wgWirePanelChrome(panel, "wg-panel-hidden", "waygraph demo", {
|
|
1153
|
+
stepLabel: (info.index + 1) + " / " + info.total + (info.blockName ? " · " + info.blockName : ""),
|
|
1154
|
+
forceCollapsed: info.forceCollapsed === true ? true : info.forceCollapsed === false ? false : undefined,
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1106
1157
|
panel.querySelectorAll(".wg-toggle-btn").forEach((btn) => {
|
|
1107
1158
|
btn.addEventListener("click", () => {
|
|
1108
1159
|
const wantPretty = btn.getAttribute("data-mode") === "pretty";
|
|
@@ -1169,6 +1220,7 @@ async function renderStepError(page, info) {
|
|
|
1169
1220
|
.join("") +
|
|
1170
1221
|
"</div>"
|
|
1171
1222
|
: "";
|
|
1223
|
+
const modulesClass = info.stepperMode === "full" ? "wg-modules-full" : "wg-modules-carousel";
|
|
1172
1224
|
const modulesHtml = info.allNames
|
|
1173
1225
|
.map((name, idx) => {
|
|
1174
1226
|
const cls = idx === info.moduleIndex ? "wg-mod-current" : idx < info.moduleIndex ? "wg-mod-done" : "wg-mod-upcoming";
|
|
@@ -1186,7 +1238,7 @@ async function renderStepError(page, info) {
|
|
|
1186
1238
|
: "";
|
|
1187
1239
|
panel.innerHTML =
|
|
1188
1240
|
episodesHtml +
|
|
1189
|
-
"<div id=\\"wg-modules\\">" + modulesHtml + "</div>" +
|
|
1241
|
+
"<div id=\\"wg-modules\\" class=\\"" + modulesClass + "\\">" + modulesHtml + "</div>" +
|
|
1190
1242
|
"<h3 class=\\"" + (isExpected ? "wg-expected-heading" : "wg-error-heading") + "\\">" + headingText + "</h3>" +
|
|
1191
1243
|
reasonHtml +
|
|
1192
1244
|
"<div class=\\"wg-error-msg\\">" + esc(info.message) + "</div>" +
|
|
@@ -1198,7 +1250,12 @@ async function renderStepError(page, info) {
|
|
|
1198
1250
|
document.documentElement.appendChild(panel);
|
|
1199
1251
|
requestAnimationFrame(() => panel.classList.add("wg-in"));
|
|
1200
1252
|
}
|
|
1201
|
-
if (window.__wgWirePanelChrome)
|
|
1253
|
+
if (window.__wgWirePanelChrome) {
|
|
1254
|
+
window.__wgWirePanelChrome(panel, "wg-panel-hidden", "waygraph demo", {
|
|
1255
|
+
stepLabel: (info.index + 1) + " / " + info.total + (info.blockName ? " · " + info.blockName : ""),
|
|
1256
|
+
forceCollapsed: info.forceCollapsed === true ? true : info.forceCollapsed === false ? false : undefined,
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1202
1259
|
const runBtn = document.getElementById("wg-run");
|
|
1203
1260
|
if (runBtn) runBtn.addEventListener("click", () => window.__wgNext({}));
|
|
1204
1261
|
const retryBtn = document.getElementById("wg-error-retry");
|
|
@@ -1308,6 +1365,9 @@ async function markStepRunning(page, opts) {
|
|
|
1308
1365
|
panel.classList.add("wg-collapsed");
|
|
1309
1366
|
const toggle = panel.querySelector("[data-wg-toggle]");
|
|
1310
1367
|
if (toggle) toggle.textContent = "Show";
|
|
1368
|
+
const titleEl = panel.querySelector(".wg-chrome-title");
|
|
1369
|
+
const label = panel.dataset.wgStepLabel;
|
|
1370
|
+
if (titleEl && label) titleEl.textContent = label;
|
|
1311
1371
|
}
|
|
1312
1372
|
}
|
|
1313
1373
|
}, { autoCollapsePanel: !!(opts && opts.autoCollapsePanel) })
|
|
@@ -1325,6 +1385,162 @@ async function showRing(page, box, label) {
|
|
|
1325
1385
|
.catch(() => {});
|
|
1326
1386
|
}
|
|
1327
1387
|
|
|
1388
|
+
/** Match a locator to a stubBefore slot by overlapping bounding boxes. */
|
|
1389
|
+
async function captionForLocator(page, locator, stubs, fallback) {
|
|
1390
|
+
if (!stubs || stubs.length === 0) return fallback;
|
|
1391
|
+
const box = await locator.boundingBox().catch(() => null);
|
|
1392
|
+
if (!box) return fallback;
|
|
1393
|
+
for (const s of stubs) {
|
|
1394
|
+
try {
|
|
1395
|
+
const b = await page.locator(s.selector).first().boundingBox();
|
|
1396
|
+
if (
|
|
1397
|
+
b &&
|
|
1398
|
+
Math.abs(b.x - box.x) < 4 &&
|
|
1399
|
+
Math.abs(b.y - box.y) < 4 &&
|
|
1400
|
+
Math.abs(b.width - box.width) < 8
|
|
1401
|
+
) {
|
|
1402
|
+
return formatHighlightCaption(s);
|
|
1403
|
+
}
|
|
1404
|
+
} catch {
|
|
1405
|
+
/* skip */
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return fallback;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* Multi-step yap captions - not block lifecycle. Each slide waits for Next
|
|
1413
|
+
* (or auto-next), optional ring via slide.selector.
|
|
1414
|
+
*/
|
|
1415
|
+
async function presentSlides(page, slides, gate, opts) {
|
|
1416
|
+
const title = (opts && opts.title) || "waygraph demo";
|
|
1417
|
+
const blockName = (opts && opts.blockName) || "";
|
|
1418
|
+
for (let i = 0; i < slides.length; i++) {
|
|
1419
|
+
const s = slides[i];
|
|
1420
|
+
const caption = formatHighlightCaption(s);
|
|
1421
|
+
const isLast = i === slides.length - 1;
|
|
1422
|
+
await installOverlay(page, title);
|
|
1423
|
+
if (s.selector) {
|
|
1424
|
+
try {
|
|
1425
|
+
const box = await page.locator(s.selector).first().boundingBox();
|
|
1426
|
+
if (box) await showRing(page, box, caption);
|
|
1427
|
+
} catch {
|
|
1428
|
+
await hideRing(page);
|
|
1429
|
+
}
|
|
1430
|
+
} else {
|
|
1431
|
+
await hideRing(page);
|
|
1432
|
+
}
|
|
1433
|
+
await page
|
|
1434
|
+
.evaluate(
|
|
1435
|
+
(info) => {
|
|
1436
|
+
let panel = document.getElementById("wg-panel");
|
|
1437
|
+
const isNew = !panel;
|
|
1438
|
+
if (!panel) {
|
|
1439
|
+
panel = document.createElement("div");
|
|
1440
|
+
panel.id = "wg-panel";
|
|
1441
|
+
}
|
|
1442
|
+
panel.classList.remove("wg-collapsed", "wg-error", "wg-expected");
|
|
1443
|
+
const esc = (t) =>
|
|
1444
|
+
String(t).replace(/&/g, "&").replace(/</g, "<").replace(/"/g, """);
|
|
1445
|
+
panel.innerHTML =
|
|
1446
|
+
"<div class=\\"wg-chrome\\"><span class=\\"wg-chrome-title\\">waygraph demo</span>" +
|
|
1447
|
+
"<button type=\\"button\\" class=\\"wg-hide-btn\\" data-wg-toggle=\\"1\\">Hide</button></div>" +
|
|
1448
|
+
"<div class=\\"wg-body\\">" +
|
|
1449
|
+
"<h3>Slide " +
|
|
1450
|
+
(info.index + 1) +
|
|
1451
|
+
" / " +
|
|
1452
|
+
info.total +
|
|
1453
|
+
(info.blockName ? " · " + esc(info.blockName) : "") +
|
|
1454
|
+
"</h3>" +
|
|
1455
|
+
(info.tag
|
|
1456
|
+
? "<div class=\\"wg-episode\\" style=\\"border:none;padding:0;margin:0 0 6px;font-size:11px;color:#c9a6ff\\">" +
|
|
1457
|
+
esc(info.tag) +
|
|
1458
|
+
"</div>"
|
|
1459
|
+
: "") +
|
|
1460
|
+
"<div class=\\"wg-narration\\">" +
|
|
1461
|
+
esc(info.caption) +
|
|
1462
|
+
"</div>" +
|
|
1463
|
+
(info.detail
|
|
1464
|
+
? "<div class=\\"wg-result-pretty\\" style=\\"margin:0 0 12px;color:#f0e8ff\\">" +
|
|
1465
|
+
esc(info.detail) +
|
|
1466
|
+
"</div>"
|
|
1467
|
+
: "") +
|
|
1468
|
+
"<div class=\\"wg-autoplay-row\\"><label><input type=\\"checkbox\\" id=\\"wg-autoplay-cb\\"" +
|
|
1469
|
+
(info.autoNow ? " checked" : "") +
|
|
1470
|
+
"> Auto-advance</label></div>" +
|
|
1471
|
+
"<div id=\\"wg-gate-manual\\"" +
|
|
1472
|
+
(info.autoNow ? " style=\\"display:none\\"" : "") +
|
|
1473
|
+
"><button id=\\"wg-run\\">" +
|
|
1474
|
+
(info.isLast ? "Continue \\u25B6" : "Next slide \\u25B6") +
|
|
1475
|
+
"</button></div>" +
|
|
1476
|
+
"<div id=\\"wg-gate-auto\\" class=\\"wg-auto\\"" +
|
|
1477
|
+
(info.autoNow ? "" : " style=\\"display:none\\"") +
|
|
1478
|
+
">Auto-advancing...</div>" +
|
|
1479
|
+
"</div>";
|
|
1480
|
+
if (isNew) {
|
|
1481
|
+
document.documentElement.appendChild(panel);
|
|
1482
|
+
requestAnimationFrame(() => panel.classList.add("wg-in"));
|
|
1483
|
+
}
|
|
1484
|
+
if (window.__wgWirePanelChrome) {
|
|
1485
|
+
window.__wgWirePanelChrome(panel, "wg-panel-hidden", "waygraph demo", {
|
|
1486
|
+
stepLabel:
|
|
1487
|
+
"Slide " +
|
|
1488
|
+
(info.index + 1) +
|
|
1489
|
+
" / " +
|
|
1490
|
+
info.total +
|
|
1491
|
+
(info.blockName ? " · " + info.blockName : ""),
|
|
1492
|
+
forceCollapsed: false,
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
const cb = document.getElementById("wg-autoplay-cb");
|
|
1496
|
+
if (cb) {
|
|
1497
|
+
cb.addEventListener("change", () => {
|
|
1498
|
+
try {
|
|
1499
|
+
localStorage.setItem("wg-autoplay", cb.checked ? "1" : "0");
|
|
1500
|
+
} catch {
|
|
1501
|
+
/* ignore */
|
|
1502
|
+
}
|
|
1503
|
+
const manual = document.getElementById("wg-gate-manual");
|
|
1504
|
+
const auto = document.getElementById("wg-gate-auto");
|
|
1505
|
+
if (manual) manual.style.display = cb.checked ? "none" : "";
|
|
1506
|
+
if (auto) auto.style.display = cb.checked ? "" : "none";
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
const runBtn = document.getElementById("wg-run");
|
|
1510
|
+
if (runBtn) runBtn.addEventListener("click", () => window.__wgNext({}));
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
index: i,
|
|
1514
|
+
total: slides.length,
|
|
1515
|
+
caption: s.caption,
|
|
1516
|
+
detail: s.detail || "",
|
|
1517
|
+
tag: s.tag || "",
|
|
1518
|
+
blockName,
|
|
1519
|
+
isLast,
|
|
1520
|
+
autoNow: false,
|
|
1521
|
+
},
|
|
1522
|
+
)
|
|
1523
|
+
.catch(() => {});
|
|
1524
|
+
await page
|
|
1525
|
+
.evaluate(() => {
|
|
1526
|
+
try {
|
|
1527
|
+
const on = localStorage.getItem("wg-autoplay") === "1";
|
|
1528
|
+
const cb = document.getElementById("wg-autoplay-cb");
|
|
1529
|
+
if (cb) cb.checked = on;
|
|
1530
|
+
const manual = document.getElementById("wg-gate-manual");
|
|
1531
|
+
const auto = document.getElementById("wg-gate-auto");
|
|
1532
|
+
if (manual) manual.style.display = on ? "none" : "";
|
|
1533
|
+
if (auto) auto.style.display = on ? "" : "none";
|
|
1534
|
+
} catch {
|
|
1535
|
+
/* ignore */
|
|
1536
|
+
}
|
|
1537
|
+
})
|
|
1538
|
+
.catch(() => {});
|
|
1539
|
+
await gate();
|
|
1540
|
+
}
|
|
1541
|
+
await hideRing(page);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1328
1544
|
async function hideRing(page) {
|
|
1329
1545
|
await page
|
|
1330
1546
|
.evaluate(() => {
|
|
@@ -1401,7 +1617,7 @@ async function wasJustNarrated(page) {
|
|
|
1401
1617
|
.catch(() => false);
|
|
1402
1618
|
}
|
|
1403
1619
|
|
|
1404
|
-
function instrumentInteractionHighlighting(page, mem, slowMo, pacing) {
|
|
1620
|
+
function instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef) {
|
|
1405
1621
|
// Playwright's own slowMo ALREADY pauses after every single low-level
|
|
1406
1622
|
// action it dispatches - and pressSequentially() fires one such action
|
|
1407
1623
|
// PER CHARACTER. Also giving pressSequentially its own fixed delay
|
|
@@ -1415,6 +1631,7 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing) {
|
|
|
1415
1631
|
// per-block (WAYGRAPH_FAST_BLOCKS), and the Locator.fill/click patches
|
|
1416
1632
|
// below are installed once on the shared prototype but invoked once per
|
|
1417
1633
|
// real interaction, long after this closure was created.
|
|
1634
|
+
// stubBeforeRef.current = resolved stubBefore slots for the active block.
|
|
1418
1635
|
const typeDelay = () => (pacing.fast ? 0 : slowMo ? 0 : 30);
|
|
1419
1636
|
// Click is a single action, not per-character, so it doesn't compound
|
|
1420
1637
|
// the same way - but slowMo still adds its own pause around the actual
|
|
@@ -1451,9 +1668,11 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing) {
|
|
|
1451
1668
|
// Only trust the "last mem.get()" as THIS fill's source if it
|
|
1452
1669
|
// happened recently - a stale read from several actions ago is
|
|
1453
1670
|
// more likely unrelated than actually describing this field.
|
|
1454
|
-
const
|
|
1671
|
+
const fallback = memTrack.lastKeyName && Date.now() - memTrack.at < 3000
|
|
1455
1672
|
? "from mem: " + memTrack.lastKeyName
|
|
1456
1673
|
: "writing from mem";
|
|
1674
|
+
const stubs = (stubBeforeRef && stubBeforeRef.current) || [];
|
|
1675
|
+
const label = await captionForLocator(page, this, stubs, fallback);
|
|
1457
1676
|
await moveCursorTo(page, box, cursorMs(500));
|
|
1458
1677
|
await showRing(page, box, label);
|
|
1459
1678
|
await new Promise((res) => setTimeout(res, pacing.fast ? 0 : 200));
|
|
@@ -1506,10 +1725,10 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing) {
|
|
|
1506
1725
|
const box = await this.boundingBox();
|
|
1507
1726
|
const narrated = box ? await wasJustNarrated(page) : false;
|
|
1508
1727
|
if (box && !narrated) {
|
|
1509
|
-
let
|
|
1728
|
+
let fallback = "click";
|
|
1510
1729
|
try {
|
|
1511
1730
|
const text = (await this.textContent())?.trim();
|
|
1512
|
-
if (text && text.length > 0 && text.length <= 30)
|
|
1731
|
+
if (text && text.length > 0 && text.length <= 30) fallback = text;
|
|
1513
1732
|
} catch {
|
|
1514
1733
|
// element has no simple text (an icon button, say) - generic label is fine
|
|
1515
1734
|
}
|
|
@@ -1517,10 +1736,12 @@ function instrumentInteractionHighlighting(page, mem, slowMo, pacing) {
|
|
|
1517
1736
|
// (set on page by runStepMode just before act).
|
|
1518
1737
|
try {
|
|
1519
1738
|
const navLabel = await page.evaluate(() => window.__wgPendingNavClickLabel || null);
|
|
1520
|
-
if (navLabel)
|
|
1739
|
+
if (navLabel) fallback = String(navLabel);
|
|
1521
1740
|
} catch {
|
|
1522
1741
|
/* ignore */
|
|
1523
1742
|
}
|
|
1743
|
+
const stubs = (stubBeforeRef && stubBeforeRef.current) || [];
|
|
1744
|
+
const label = await captionForLocator(page, this, stubs, fallback);
|
|
1524
1745
|
clickPoint = await moveCursorTo(page, box, cursorMs(600));
|
|
1525
1746
|
await showRing(page, box, label);
|
|
1526
1747
|
// "pop for a few seconds" - Dan's own phrase, matching the
|
|
@@ -1644,8 +1865,13 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
1644
1865
|
// own added dwell while the rest of the chain keeps the full theatrical
|
|
1645
1866
|
// pace. Playwright's own slowMo is process-wide and untouched by this -
|
|
1646
1867
|
// only OUR added pauses (ring pop, cursor travel, typing delay) shrink.
|
|
1647
|
-
|
|
1648
|
-
|
|
1868
|
+
// --fast / WAYGRAPH_DEMO_FAST: every step uses fast interaction pacing + shorter gates.
|
|
1869
|
+
// WAYGRAPH_FAST_BLOCKS still names per-block overrides when --fast is off.
|
|
1870
|
+
const demoFast = process.env.WAYGRAPH_DEMO_FAST === "1";
|
|
1871
|
+
const stepperMode = process.env.WAYGRAPH_STEPPER === "full" ? "full" : "carousel";
|
|
1872
|
+
const pacing = { fast: demoFast };
|
|
1873
|
+
const stubBeforeRef = { current: [] };
|
|
1874
|
+
instrumentInteractionHighlighting(page, mem, slowMo, pacing, stubBeforeRef);
|
|
1649
1875
|
// Force the panel checkbox from this process's flags/env at run start.
|
|
1650
1876
|
// installOverlay only seeds localStorage when the key is null (so mid-run
|
|
1651
1877
|
// checkbox clicks survive navigations). Without this, a prior --autoplay
|
|
@@ -1689,7 +1915,11 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
1689
1915
|
// which way the checkbox is set. That's the hybrid Dan asked for: some
|
|
1690
1916
|
// steps auto-advance, some get a manual click, toggled as the demo goes,
|
|
1691
1917
|
// not fixed for the whole run from a single env var.
|
|
1692
|
-
const autoplayMs = process.env.WAYGRAPH_AUTOPLAY_MS
|
|
1918
|
+
const autoplayMs = process.env.WAYGRAPH_AUTOPLAY_MS
|
|
1919
|
+
? Number(process.env.WAYGRAPH_AUTOPLAY_MS)
|
|
1920
|
+
: demoFast
|
|
1921
|
+
? 450
|
|
1922
|
+
: 1800;
|
|
1693
1923
|
const currentAutoplay = () =>
|
|
1694
1924
|
page
|
|
1695
1925
|
.evaluate(() => {
|
|
@@ -1789,7 +2019,14 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
1789
2019
|
if ((clearSession || r.resetSession) && i > 0) {
|
|
1790
2020
|
await resetPageState(context, page, baseURL);
|
|
1791
2021
|
}
|
|
1792
|
-
pacing.fast = fastBlockNames.has(r.block.name);
|
|
2022
|
+
pacing.fast = demoFast || fastBlockNames.has(r.block.name);
|
|
2023
|
+
const fixtures = r.highlightFixtures;
|
|
2024
|
+
stubBeforeRef.current = resolveHighlightSlots(r.block, "stubBefore", { fixtures });
|
|
2025
|
+
const isNavBlock = r.block.__waygraphKind === "nav";
|
|
2026
|
+
const autoNow = await currentAutoplay();
|
|
2027
|
+
// Auto-next: tuck stepper on NavBlocks so the page transition fills the frame.
|
|
2028
|
+
// Video recording still collapses every step.
|
|
2029
|
+
const forceCollapsed = !!process.env.WAYGRAPH_VIDEO || (autoNow && isNavBlock);
|
|
1793
2030
|
const requires = r.block.requires ?? [];
|
|
1794
2031
|
const keys = requires.map((k) => {
|
|
1795
2032
|
let value = "<not yet set>";
|
|
@@ -1814,6 +2051,8 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
1814
2051
|
title,
|
|
1815
2052
|
episodeNumber: r.episodeNumber,
|
|
1816
2053
|
episodeTitle: r.episodeTitle,
|
|
2054
|
+
stepperMode,
|
|
2055
|
+
forceCollapsed,
|
|
1817
2056
|
});
|
|
1818
2057
|
const edits = await gate();
|
|
1819
2058
|
for (const k of requires) {
|
|
@@ -1826,7 +2065,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
1826
2065
|
}
|
|
1827
2066
|
}
|
|
1828
2067
|
const recordingVideo = !!process.env.WAYGRAPH_VIDEO;
|
|
1829
|
-
const autoCollapsePanel = recordingVideo || (await currentAutoplay());
|
|
2068
|
+
const autoCollapsePanel = recordingVideo || (await currentAutoplay() && isNavBlock) || forceCollapsed;
|
|
1830
2069
|
await markStepRunning(page, { autoCollapsePanel });
|
|
1831
2070
|
// NavBlock click-nav: label the upcoming Locator.click demo cursor as
|
|
1832
2071
|
// "nav: <block>" so pia/demo watchers see cursor+pulse on click nav
|
|
@@ -1876,6 +2115,8 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
1876
2115
|
episodeNumber: r.episodeNumber,
|
|
1877
2116
|
episodeTitle: r.episodeTitle,
|
|
1878
2117
|
expectedFailureReason: r.expectedFailureReason,
|
|
2118
|
+
stepperMode,
|
|
2119
|
+
forceCollapsed: false,
|
|
1879
2120
|
});
|
|
1880
2121
|
const errEdits = await gate();
|
|
1881
2122
|
if (errEdits && errEdits.__wgRetry) {
|
|
@@ -1904,7 +2145,26 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
1904
2145
|
})
|
|
1905
2146
|
.catch(() => {});
|
|
1906
2147
|
result = stepOutcome.result;
|
|
1907
|
-
const
|
|
2148
|
+
const fixturesAfter = r.highlightFixtures;
|
|
2149
|
+
const slides = resolveSlides(r.block, { out: result, fixtures: fixturesAfter });
|
|
2150
|
+
if (slides.length > 0) {
|
|
2151
|
+
await presentSlides(page, slides, gate, {
|
|
2152
|
+
title,
|
|
2153
|
+
blockName: r.block.name,
|
|
2154
|
+
fast: pacing.fast,
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
let highlights;
|
|
2158
|
+
if (hasAuthoredStubAfter(r.block, result, fixturesAfter)) {
|
|
2159
|
+
highlights = resolveHighlightSlots(r.block, "stubAfter", { out: result, fixtures: fixturesAfter }).map(
|
|
2160
|
+
(h) => ({
|
|
2161
|
+
selector: h.selector,
|
|
2162
|
+
label: formatHighlightCaption(h),
|
|
2163
|
+
}),
|
|
2164
|
+
);
|
|
2165
|
+
} else {
|
|
2166
|
+
highlights = extractVerifyHighlights(r.block, result.__state);
|
|
2167
|
+
}
|
|
1908
2168
|
await renderAfterStep(page, {
|
|
1909
2169
|
index: i,
|
|
1910
2170
|
total: resolved.length,
|
|
@@ -1920,6 +2180,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
1920
2180
|
title,
|
|
1921
2181
|
episodeNumber: r.episodeNumber,
|
|
1922
2182
|
episodeTitle: r.episodeTitle,
|
|
2183
|
+
stepperMode,
|
|
2184
|
+
// After a nav under auto-next, stay compact until the next action block expands.
|
|
2185
|
+
forceCollapsed: !!process.env.WAYGRAPH_VIDEO || ((await currentAutoplay()) && isNavBlock),
|
|
1923
2186
|
});
|
|
1924
2187
|
await gate();
|
|
1925
2188
|
}
|
|
@@ -2052,6 +2315,7 @@ async function main() {
|
|
|
2052
2315
|
block: bi.block,
|
|
2053
2316
|
exportName: bi.name,
|
|
2054
2317
|
seedMem: idx === 0 ? seedMem : undefined,
|
|
2318
|
+
highlightFixtures: flow.highlightFixtures,
|
|
2055
2319
|
expectedFailureReason:
|
|
2056
2320
|
flow.expectedFailureReason && idx === blockInfos.length - 1
|
|
2057
2321
|
? flow.expectedFailureReason
|
|
@@ -2113,6 +2377,7 @@ async function main() {
|
|
|
2113
2377
|
episodeNumber: episodeCounter,
|
|
2114
2378
|
episodeTitle: flow.title || seg.ref,
|
|
2115
2379
|
expectedFailureReason: flow.expectedFailureReason,
|
|
2380
|
+
highlightFixtures: flow.highlightFixtures,
|
|
2116
2381
|
seedMem: () => seedMemForFlow(mem, flow.blocks(), seg.json, seg.ref),
|
|
2117
2382
|
});
|
|
2118
2383
|
} else {
|
|
@@ -2121,6 +2386,7 @@ async function main() {
|
|
|
2121
2386
|
flowMeta.push({
|
|
2122
2387
|
episodeNumber: undefined,
|
|
2123
2388
|
episodeTitle: undefined,
|
|
2389
|
+
highlightFixtures: undefined,
|
|
2124
2390
|
seedMem: () => seedMemForBlock(mem, r, seg.json),
|
|
2125
2391
|
});
|
|
2126
2392
|
}
|
|
@@ -2142,6 +2408,7 @@ async function main() {
|
|
|
2142
2408
|
resetSession: bi.resetSessionBefore === true,
|
|
2143
2409
|
episodeNumber: meta.episodeNumber,
|
|
2144
2410
|
episodeTitle: meta.episodeTitle,
|
|
2411
|
+
highlightFixtures: meta.highlightFixtures,
|
|
2145
2412
|
expectedFailureReason:
|
|
2146
2413
|
meta.expectedFailureReason && remainingInFlow === 1 ? meta.expectedFailureReason : undefined,
|
|
2147
2414
|
seedMem: isFirstOfSegment ? meta.seedMem : undefined,
|
|
@@ -2840,6 +3107,12 @@ function parseRunFlags(argv) {
|
|
|
2840
3107
|
else if (a === "--auto-play-video") {
|
|
2841
3108
|
out.autoPlayVideo = true;
|
|
2842
3109
|
}
|
|
3110
|
+
else if (a === "--fast") {
|
|
3111
|
+
out.fast = true;
|
|
3112
|
+
}
|
|
3113
|
+
else if (a === "--full") {
|
|
3114
|
+
out.fullStepper = true;
|
|
3115
|
+
}
|
|
2843
3116
|
else if (a === "--non-headless") {
|
|
2844
3117
|
out.nonHeadless = true;
|
|
2845
3118
|
}
|
|
@@ -2945,6 +3218,10 @@ function applyRunFlags(flags, opts) {
|
|
|
2945
3218
|
if (flags.step === undefined)
|
|
2946
3219
|
flags.step = true;
|
|
2947
3220
|
}
|
|
3221
|
+
if ((flags.fast || flags.fullStepper) && !opts?.allowDemoUi) {
|
|
3222
|
+
console.error("waygraph: --fast / --full are demo-only flags");
|
|
3223
|
+
process.exit(1);
|
|
3224
|
+
}
|
|
2948
3225
|
if (flags.step === true) {
|
|
2949
3226
|
process.env.WAYGRAPH_STEP = "1";
|
|
2950
3227
|
process.env.WAYGRAPH_HEADED = "1";
|
|
@@ -2980,6 +3257,12 @@ function applyRunFlags(flags, opts) {
|
|
|
2980
3257
|
if (flags.data !== undefined) {
|
|
2981
3258
|
process.env.WAYGRAPH_DATA = flags.data;
|
|
2982
3259
|
}
|
|
3260
|
+
if (flags.fast) {
|
|
3261
|
+
process.env.WAYGRAPH_DEMO_FAST = "1";
|
|
3262
|
+
}
|
|
3263
|
+
if (flags.fullStepper) {
|
|
3264
|
+
process.env.WAYGRAPH_STEPPER = "full";
|
|
3265
|
+
}
|
|
2983
3266
|
}
|
|
2984
3267
|
/**
|
|
2985
3268
|
* BASE_URL when neither --base-url nor WAYGRAPH_BASE_URL is set:
|
|
@@ -3135,6 +3418,8 @@ Primary (less is more):
|
|
|
3135
3418
|
waygraph demo [--blocks <flow|file|spec>] Watch with step overlay (QA path)
|
|
3136
3419
|
--data '{...}' Mem seed JSON (or inline flow({...}))
|
|
3137
3420
|
--auto-next Auto-advance steps (alias: --autoplay)
|
|
3421
|
+
--fast Faster transitions + shorter auto-next gates
|
|
3422
|
+
--full Classic wrap-all block chips (default: carousel)
|
|
3138
3423
|
--auto-play-video QA: --auto-next + --video (+ step)
|
|
3139
3424
|
--video-viewport WxH Recording size (default demo: 1920x1080)
|
|
3140
3425
|
--title / --base-url
|
|
@@ -3155,7 +3440,8 @@ Examples:
|
|
|
3155
3440
|
waygraph list # file → export map
|
|
3156
3441
|
waygraph run src/flows/shop.flow.ts --data '{...}'
|
|
3157
3442
|
waygraph run --blocks shopFlow --non-headless --video
|
|
3158
|
-
waygraph demo --blocks src/flows/cart-bulk.flow.ts --auto-next
|
|
3443
|
+
waygraph demo --blocks src/flows/cart-bulk.flow.ts --auto-next --fast
|
|
3444
|
+
waygraph demo src/flows/shop.flow.ts --full
|
|
3159
3445
|
waygraph try auto
|
|
3160
3446
|
waygraph try auto:cli
|
|
3161
3447
|
waygraph auto src/flows/shop.flow.ts --data '{...}' # run by file (same as run)
|
|
@@ -3168,6 +3454,7 @@ Aliases (compat): \`chain <spec>\` -> run --blocks; \`chain auto A B\` -> auto -
|
|
|
3168
3454
|
|
|
3169
3455
|
Project path optional (defaults to cwd). Flags beat WAYGRAPH_* env.
|
|
3170
3456
|
\`run\`/\`demo\`/\`auto\`: Flow export, .flow.ts path, or "a then b" chain (auto also explores).
|
|
3457
|
+
Hide stepper = compact "N / M · block" pill. Auto-next auto-hides on NavBlocks.
|
|
3171
3458
|
`);
|
|
3172
3459
|
process.exit(0);
|
|
3173
3460
|
}
|
|
@@ -3282,13 +3569,13 @@ async function main() {
|
|
|
3282
3569
|
}
|
|
3283
3570
|
case "demo": {
|
|
3284
3571
|
const flags = parseRunFlags(args.slice(1));
|
|
3285
|
-
applyRunFlags(flags, { allowAutoPlayVideo: true });
|
|
3572
|
+
applyRunFlags(flags, { allowAutoPlayVideo: true, allowDemoUi: true });
|
|
3286
3573
|
const spec = resolveSpec(flags);
|
|
3287
3574
|
if (!spec) {
|
|
3288
3575
|
console.error("waygraph demo: missing flow/spec — e.g.\n" +
|
|
3289
3576
|
" waygraph demo src/flows/shop.flow.ts\n" +
|
|
3290
3577
|
" waygraph demo --blocks shopFlow\n" +
|
|
3291
|
-
" Flags: --blocks --data --auto-next --auto-play-video --title --base-url --video");
|
|
3578
|
+
" Flags: --blocks --data --auto-next --fast --full --auto-play-video --title --base-url --video");
|
|
3292
3579
|
process.exit(1);
|
|
3293
3580
|
}
|
|
3294
3581
|
const proj = flags.blocks
|