waygraph 0.12.30 → 0.12.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -274,12 +274,12 @@ async function findBlock(projectDir, ref) {
274
274
  if (byRuntimeName.length === 1) return byRuntimeName[0];
275
275
  if (byRuntimeName.length > 1) {
276
276
  throw new Error(
277
- "waygraph chain: \\"" + ref + "\\" matches " + byRuntimeName.length + " Blocks by name - " +
277
+ "waygraph chain: \\\"" + ref + "\\\" matches " + byRuntimeName.length + " Blocks by name - " +
278
278
  byRuntimeName.map((b) => b.exportName + " (" + b.file + ")").join(", ") +
279
279
  ". Use the export name to disambiguate.",
280
280
  );
281
281
  }
282
- throw new Error("waygraph chain: no Block named \\"" + ref + "\\" found under " + projectDir);
282
+ throw new Error("waygraph chain: no Block named \\\"" + ref + "\\\" found under " + projectDir);
283
283
  }
284
284
 
285
285
  /**
@@ -316,7 +316,7 @@ function parseChainSpec(spec) {
316
316
  const m = /^([A-Za-z_$][\\w-]*)\\s*(?:\\(([\\s\\S]*)\\))?$/.exec(s);
317
317
  if (!m) {
318
318
  throw new Error(
319
- "waygraph chain: could not parse segment \\"" + s + "\\" - expected \\"blockName\\" or \\"blockName({...json...})\\"",
319
+ "waygraph chain: could not parse segment \\\"" + s + "\\\" - expected \\\"blockName\\\" or \\\"blockName({...json...})\\\"",
320
320
  );
321
321
  }
322
322
  const ref = m[1];
@@ -329,7 +329,7 @@ function seedMemFromRequires(mem, requires, json, label) {
329
329
  if (requires.length === 0) {
330
330
  if (json !== undefined) {
331
331
  throw new Error(
332
- "waygraph chain: \\"" + label + "\\" takes no input (empty requires) but got a payload: " + json,
332
+ "waygraph chain: \\\"" + label + "\\\" takes no input (empty requires) but got a payload: " + json,
333
333
  );
334
334
  }
335
335
  return;
@@ -341,7 +341,7 @@ function seedMemFromRequires(mem, requires, json, label) {
341
341
  json = fromEnv;
342
342
  } else {
343
343
  throw new Error(
344
- "waygraph chain: \\"" + label + "\\" requires " + requires.map((k) => k.name).join(", ") +
344
+ "waygraph chain: \\\"" + label + "\\\" requires " + requires.map((k) => k.name).join(", ") +
345
345
  " - give a JSON payload (inline blockName({...}) or --data '{...}')",
346
346
  );
347
347
  }
@@ -350,7 +350,7 @@ function seedMemFromRequires(mem, requires, json, label) {
350
350
  try {
351
351
  parsed = JSON.parse(json);
352
352
  } catch (err) {
353
- throw new Error("waygraph chain: \\"" + label + "\\" payload is not valid JSON - " + String(err));
353
+ throw new Error("waygraph chain: \\\"" + label + "\\\" payload is not valid JSON - " + String(err));
354
354
  }
355
355
  // Keyed-by-name when every require name is a top-level key (preferred for
356
356
  // --data and for {"saucedemo.credentials":{...}} even with one require).
@@ -367,13 +367,13 @@ function seedMemFromRequires(mem, requires, json, label) {
367
367
  }
368
368
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
369
369
  throw new Error(
370
- "waygraph chain: \\"" + label + "\\" requires " + requires.length + " keys (" +
370
+ "waygraph chain: \\\"" + label + "\\\" requires " + requires.length + " keys (" +
371
371
  requires.map((k) => k.name).join(", ") + ") - payload must be an object keyed by each key's name",
372
372
  );
373
373
  }
374
374
  for (const k of requires) {
375
375
  if (!(k.name in parsed)) {
376
- throw new Error("waygraph chain: \\"" + label + "\\" payload is missing required key \\"" + k.name + "\\"");
376
+ throw new Error("waygraph chain: \\\"" + label + "\\\" payload is missing required key \\\"" + k.name + "\\\"");
377
377
  }
378
378
  mem.set(k, parsed[k.name]);
379
379
  }
@@ -415,7 +415,7 @@ const RING_CSS =
415
415
  "box-shadow:0 0 0 4px rgba(124,58,237,.16);" +
416
416
  "transition:opacity .32s ease,left .28s ease,top .28s ease,width .28s ease,height .28s ease,border-color .15s,box-shadow .32s ease;}" +
417
417
  // Unquoted [data-focus=1]: this lives inside CHAIN_RUNNER_SCRIPT's template
418
- // literal - a \" here would be eaten by the outer template and break the
418
+ // literal - a \\" here would be eaten by the outer template and break the
419
419
  // generated .mjs. CSS allows unquoted attr values for identifiers.
420
420
  "#wg-ring[data-focus=1]{box-shadow:0 0 0 4px rgba(124,58,237,.2),0 0 0 9999px rgba(15,6,32,.55);}" +
421
421
  "#wg-ring[data-tone=auto][data-focus=1]{box-shadow:0 0 0 4px rgba(156,163,175,.28),0 0 0 9999px rgba(15,6,32,.55);}" +
@@ -527,6 +527,24 @@ const RING_CSS =
527
527
  "#wg-todos .wg-todo-done{color:#6ee7b7;text-decoration:line-through;opacity:.85;}" +
528
528
  "#wg-todos .wg-todo-current{color:#fff;}" +
529
529
  "#wg-todos .wg-todo-pending{color:#9a7ad1;}" +
530
+ "#wg-todo-modal{position:fixed;z-index:2147483647;left:14px;bottom:14px;min-width:240px;max-width:min(360px,92vw);" +
531
+ "padding:14px 16px 12px;background:rgba(18,8,36,.94);color:#f0e8ff;" +
532
+ "border:1px solid rgba(124,58,237,.55);border-radius:12px;" +
533
+ "box-shadow:0 12px 40px rgba(0,0,0,.45);backdrop-filter:blur(8px);" +
534
+ "opacity:0;transform:translateY(8px);pointer-events:none;" +
535
+ "transition:opacity .28s ease,transform .28s ease;}" +
536
+ "#wg-todo-modal.wg-todo-on{opacity:1;transform:none;pointer-events:auto;}" +
537
+ "#wg-todo-modal .wg-todo-head{font:700 11px/1.2 system-ui,sans-serif;color:#c9a6ff;" +
538
+ "letter-spacing:.04em;text-transform:uppercase;margin:0 0 10px;}" +
539
+ "#wg-todo-modal ul{list-style:none;margin:0;padding:0;}" +
540
+ "#wg-todo-modal li{display:flex;gap:10px;align-items:flex-start;margin:0 0 8px;" +
541
+ "font:600 14px/1.4 system-ui,sans-serif;}" +
542
+ "#wg-todo-modal li:last-child{margin-bottom:0;}" +
543
+ "#wg-todo-modal .wg-todo-mark{flex:0 0 auto;width:1.2em;text-align:center;font-size:15px;}" +
544
+ "#wg-todo-modal .wg-todo-done{color:#6ee7b7;text-decoration:line-through;opacity:.8;}" +
545
+ "#wg-todo-modal .wg-todo-current{color:#fff;}" +
546
+ "#wg-todo-modal .wg-todo-current .wg-todo-mark{color:#c9a6ff;}" +
547
+ "#wg-todo-modal .wg-todo-pending{color:#9a7ad1;}" +
530
548
  "#wg-progress{height:4px;background:#2a1650;border-radius:2px;margin:0 0 12px;overflow:hidden;}" +
531
549
  "#wg-progress-bar{height:100%;background:#7C3AED;border-radius:2px;transition:width .3s ease;}" +
532
550
  // A real tab bar for episodes - the currently-active episode reads as
@@ -707,11 +725,6 @@ async function installOverlay(page, title) {
707
725
  cursor.style.transform = "translate(" + x + "px," + y + "px)";
708
726
  }
709
727
  cursor.style.opacity = "1";
710
- // Screen Studio: pan the zoomed camera with the cursor.
711
- const cam = window.__wgCamera;
712
- if (cam && cam.scale > 1.001 && window.__wgSetCamera) {
713
- window.__wgSetCamera(x, y, cam.scale, instant ? 0 : travel);
714
- }
715
728
  };
716
729
  window.__wgHideCursor = () => {
717
730
  const cursor = document.getElementById("wg-cursor");
@@ -801,12 +814,14 @@ async function installOverlay(page, title) {
801
814
  };
802
815
  // Screen Studio-style camera: scale <body>, keep overlays on <html> fixed.
803
816
  window.__wgCamera = { scale: 1, cx: 0, cy: 0 };
804
- window.__wgSetCamera = (cx, cy, scale, ms) => {
817
+ window.__wgSetCamera = (fx, fy, scale, ms) => {
805
818
  const body = document.body;
806
819
  if (!body) return;
807
820
  const s = Math.max(1, Math.min(Number(scale) || 1, 2.75));
808
- window.__wgCamera = { scale: s, cx, cy };
809
- const dur = ms != null && Number(ms) >= 0 ? Number(ms) : 500;
821
+ const vw = window.innerWidth;
822
+ const vh = window.innerHeight;
823
+ window.__wgCamera = { scale: s, cx: fx, cy: fy };
824
+ const dur = ms != null && Number(ms) >= 0 ? Number(ms) : 650;
810
825
  body.style.transition =
811
826
  dur <= 0
812
827
  ? "none"
@@ -816,10 +831,57 @@ async function installOverlay(page, title) {
816
831
  body.style.transform = "";
817
832
  return;
818
833
  }
834
+ // One transform: scale AND bring focus to viewport center.
819
835
  body.style.transform =
820
- "translate(" + cx + "px," + cy + "px) scale(" + s + ") translate(" + -cx + "px," + -cy + "px)";
836
+ "translate(" + vw / 2 + "px," + vh / 2 + "px) scale(" + s + ") translate(" + -fx + "px," + -fy + "px)";
821
837
  };
822
- window.__wgResetCamera = () => {
838
+ window.__wgSetTodos = (todos) => {
839
+ let modal = document.getElementById("wg-todo-modal");
840
+ if (!todos || !todos.length) {
841
+ if (modal) {
842
+ modal.classList.remove("wg-todo-on");
843
+ modal.innerHTML = "";
844
+ }
845
+ return;
846
+ }
847
+ if (!modal) {
848
+ modal = document.createElement("div");
849
+ modal.id = "wg-todo-modal";
850
+ document.documentElement.appendChild(modal);
851
+ }
852
+ const esc = (s) =>
853
+ String(s)
854
+ .replace(/&/g, "&amp;")
855
+ .replace(/</g, "&lt;")
856
+ .replace(/>/g, "&gt;");
857
+ const rows = todos
858
+ .map((t) => {
859
+ const cls = t.current
860
+ ? "wg-todo-current"
861
+ : t.done
862
+ ? "wg-todo-done"
863
+ : "wg-todo-pending";
864
+ const mark = t.done
865
+ ? String.fromCharCode(0x2713)
866
+ : t.current
867
+ ? String.fromCharCode(0x2192)
868
+ : String.fromCharCode(0x25cb);
869
+ return (
870
+ '<li class="' +
871
+ cls +
872
+ '"><span class="wg-todo-mark">' +
873
+ mark +
874
+ '</span><span>' +
875
+ esc(t.text || "") +
876
+ '</span></li>'
877
+ );
878
+ })
879
+ .join("");
880
+ modal.innerHTML =
881
+ '<div class="wg-todo-head">Checklist</div><ul>' + rows + '</ul>';
882
+ modal.classList.add("wg-todo-on");
883
+ };
884
+ window.__wgResetCamera = () => {
823
885
  window.__wgSetCamera(window.innerWidth / 2, window.innerHeight / 2, 1);
824
886
  };
825
887
  window.__wgClearZoom = () => {
@@ -1008,7 +1070,7 @@ async function renderBeforeStep(page, info) {
1008
1070
  // active, the episode 2 tab is inactive"). Absent entirely for an ad
1009
1071
  // hoc block chain with no named Flows.
1010
1072
  const episodesHtml = info.allEpisodes && info.allEpisodes.length > 0
1011
- ? "<div id=\\"wg-episodes\\">" +
1073
+ ? "<div id=\\\"wg-episodes\\\">" +
1012
1074
  info.allEpisodes
1013
1075
  .map((e) => {
1014
1076
  const cls = e.episodeNumber < info.episodeNumber ? "wg-ep-done"
@@ -1019,7 +1081,7 @@ async function renderBeforeStep(page, info) {
1019
1081
  const enteredCls = info.justEnteredEpisode && e.episodeNumber === info.episodeNumber
1020
1082
  ? " wg-ep-entered"
1021
1083
  : "";
1022
- return "<span class=\\"wg-ep-tab " + cls + enteredCls + "\\">Episode " + e.episodeNumber + ": " +
1084
+ return "<span class=\\\"wg-ep-tab " + cls + enteredCls + "\\\">Episode " + e.episodeNumber + ": " +
1023
1085
  esc(e.episodeTitle || "") + "</span>";
1024
1086
  })
1025
1087
  .join("") +
@@ -1035,40 +1097,66 @@ async function renderBeforeStep(page, info) {
1035
1097
  .map((name, idx) => {
1036
1098
  const cls = idx < info.moduleIndex ? "wg-mod-done" : idx === info.moduleIndex ? "wg-mod-current" : "wg-mod-upcoming";
1037
1099
  const desc = info.allDescriptions && info.allDescriptions[idx];
1038
- const titleAttr = desc ? " title=\\"" + esc(desc) + "\\"" : "";
1039
- return "<span class=\\"wg-mod " + cls + "\\"" + titleAttr + ">" + name + "</span>";
1100
+ const titleAttr = desc ? " title=\\\"" + esc(desc) + "\\\"" : "";
1101
+ return "<span class=\\\"wg-mod " + cls + "\\\"" + titleAttr + ">" + name + "</span>";
1040
1102
  })
1041
1103
  .join("");
1042
1104
  const narrationHtml = info.description
1043
- ? "<div class=\\"wg-narration\\">" + esc(info.description) + "</div>"
1105
+ ? "<div class=\\\"wg-narration\\\">" + esc(info.description) + "</div>"
1044
1106
  : "";
1045
1107
  const paceHtml =
1046
1108
  info.paceBadge || info.paceLabel
1047
- ? "<div class=\\"wg-pace\\" data-pace-kind=\\"" +
1109
+ ? "<div class=\\\"wg-pace\\\" data-pace-kind=\\\"" +
1048
1110
  esc(info.paceKind || "normal") +
1049
- "\\"><span class=\\"wg-pace-badge\\">" +
1111
+ "\\\"><span class=\\\"wg-pace-badge\\\">" +
1050
1112
  esc(info.paceBadge || "1x") +
1051
1113
  "</span><span>" +
1052
1114
  esc(info.paceLabel || "") +
1053
1115
  "</span></div>"
1054
1116
  : "";
1055
1117
  let html =
1056
- "<div id=\\"wg-progress\\"><div id=\\"wg-progress-bar\\" style=\\"width:" + pct + "%\\"></div></div>" +
1118
+ "<div id=\\\"wg-progress\\\"><div id=\\\"wg-progress-bar\\\" style=\\\"width:" + pct + "%\\\"></div></div>" +
1057
1119
  episodesHtml +
1058
1120
  paceHtml +
1059
- "<div id=\\"wg-modules\\" class=\\"" + modulesClass + "\\">" + modulesHtml + "</div>" +
1121
+ "<div id=\\\"wg-modules\\\" class=\\\"" + modulesClass + "\\\">" + modulesHtml + "</div>" +
1060
1122
  "<h3>Step " + (info.index + 1) + " / " + info.total + " - " + info.blockName + "</h3>" +
1061
1123
  narrationHtml +
1062
- todosHtmlFromInfo(info, esc);
1124
+ ((() => {
1125
+ const list = info.todos || [];
1126
+ if (!list.length) return "";
1127
+ return (
1128
+ '<ul id="wg-todos">' +
1129
+ list
1130
+ .map((t) => {
1131
+ const cls = t.current ? "wg-todo-current" : t.done ? "wg-todo-done" : "wg-todo-pending";
1132
+ const mark = t.done
1133
+ ? String.fromCharCode(0x2713)
1134
+ : t.current
1135
+ ? String.fromCharCode(0x2192)
1136
+ : String.fromCharCode(0x25cb);
1137
+ return (
1138
+ '<li class="' +
1139
+ cls +
1140
+ '"><span class="wg-todo-mark">' +
1141
+ mark +
1142
+ '</span><span>' +
1143
+ esc(t.text || "") +
1144
+ '</span></li>'
1145
+ );
1146
+ })
1147
+ .join("") +
1148
+ '</ul>'
1149
+ );
1150
+ })());
1063
1151
  if (info.keys.length === 0) {
1064
- html += "<div class=\\"wg-key\\">(no MemKeys required)</div>";
1152
+ html += "<div class=\\\"wg-key\\\">(no MemKeys required)</div>";
1065
1153
  }
1066
1154
  for (const k of info.keys) {
1067
1155
  html +=
1068
- "<div class=\\"wg-key\\"><label>" + k.name + "</label>" +
1069
- "<textarea data-key=\\"" + k.name + "\\" rows=\\"2\\">" +
1156
+ "<div class=\\\"wg-key\\\"><label>" + k.name + "</label>" +
1157
+ "<textarea data-key=\\\"" + k.name + "\\\" rows=\\\"2\\\">" +
1070
1158
  k.value.replace(/</g, "&lt;") + "</textarea>" +
1071
- "<div class=\\"wg-key-pretty\\" data-pretty-for=\\"" + esc(k.name) + "\\"></div>" +
1159
+ "<div class=\\\"wg-key-pretty\\\" data-pretty-for=\\\"" + esc(k.name) + "\\\"></div>" +
1072
1160
  "</div>";
1073
1161
  }
1074
1162
  let autoNow = false;
@@ -1078,12 +1166,12 @@ async function renderBeforeStep(page, info) {
1078
1166
  /* private mode / blocked storage - defaults to manual */
1079
1167
  }
1080
1168
  html +=
1081
- "<div class=\\"wg-autoplay-row\\"><label><input type=\\"checkbox\\" id=\\"wg-autoplay-cb\\"" +
1169
+ "<div class=\\\"wg-autoplay-row\\\"><label><input type=\\\"checkbox\\\" id=\\\"wg-autoplay-cb\\\"" +
1082
1170
  (autoNow ? " checked" : "") +
1083
1171
  "> Auto-advance</label></div>" +
1084
- "<div id=\\"wg-gate-manual\\"" + (autoNow ? " style=\\"display:none\\"" : "") +
1085
- "><button id=\\"wg-run\\">Run this step \\u25B6</button></div>" +
1086
- "<div id=\\"wg-gate-auto\\" class=\\"wg-auto\\"" + (autoNow ? "" : " style=\\"display:none\\"") +
1172
+ "<div id=\\\"wg-gate-manual\\\"" + (autoNow ? " style=\\\"display:none\\\"" : "") +
1173
+ "><button id=\\\"wg-run\\\">Run this step \\u25B6</button></div>" +
1174
+ "<div id=\\\"wg-gate-auto\\\" class=\\\"wg-auto\\\"" + (autoNow ? "" : " style=\\\"display:none\\\"") +
1087
1175
  ">Auto-advancing...</div>";
1088
1176
  panel.innerHTML = html;
1089
1177
  if (isNewPanel) {
@@ -1116,7 +1204,7 @@ async function renderBeforeStep(page, info) {
1116
1204
  return Object.entries(parsed)
1117
1205
  .map(([field, value]) => {
1118
1206
  const label = field.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (c) => c.toUpperCase());
1119
- return "<div class=\\"wg-key-pretty-row\\"><span class=\\"wg-key-pretty-label\\">" +
1207
+ return "<div class=\\\"wg-key-pretty-row\\\"><span class=\\\"wg-key-pretty-label\\\">" +
1120
1208
  esc(label) + ":</span> " + esc(String(value)) + "</div>";
1121
1209
  })
1122
1210
  .join("");
@@ -1125,7 +1213,7 @@ async function renderBeforeStep(page, info) {
1125
1213
  const prettyOn = window.__wgPretty !== false;
1126
1214
  panel.querySelectorAll("textarea[data-key]").forEach((ta) => {
1127
1215
  const key = ta.getAttribute("data-key");
1128
- const previewEl = panel.querySelector(".wg-key-pretty[data-pretty-for=\\"" + key + "\\"]");
1216
+ const previewEl = panel.querySelector(".wg-key-pretty[data-pretty-for=\\\"" + key + "\\\"]");
1129
1217
  if (!previewEl) return;
1130
1218
  previewEl.style.display = prettyOn ? "" : "none";
1131
1219
  if (prettyOn) previewEl.innerHTML = prettyMemValue(ta.value);
@@ -1237,9 +1325,9 @@ async function ensureSelectorInView(page, selector) {
1237
1325
  }
1238
1326
  }
1239
1327
 
1240
- async function applyCameraZoom(page, box, zoom) {
1328
+ async function applyCameraZoom(page, box, zoom, ms) {
1241
1329
  const z = zoom != null && Number(zoom) > 1 ? Number(zoom) : 1;
1242
- if (!box) {
1330
+ if (!box || z <= 1) {
1243
1331
  await page.evaluate(() => {
1244
1332
  if (window.__wgResetCamera) window.__wgResetCamera();
1245
1333
  }).catch(() => {});
@@ -1247,10 +1335,19 @@ async function applyCameraZoom(page, box, zoom) {
1247
1335
  }
1248
1336
  const cx = box.x + box.width / 2;
1249
1337
  const cy = box.y + box.height / 2;
1338
+ const dur = ms != null ? ms : 650;
1250
1339
  await page
1251
- .evaluate(({ cx, cy, scale }) => {
1252
- if (window.__wgSetCamera) window.__wgSetCamera(cx, cy, scale);
1253
- }, { cx, cy, scale: z })
1340
+ .evaluate(({ cx, cy, scale, dur }) => {
1341
+ if (window.__wgSetCamera) window.__wgSetCamera(cx, cy, scale, dur);
1342
+ }, { cx, cy, scale: z, dur })
1343
+ .catch(() => {});
1344
+ }
1345
+
1346
+ async function setDemoTodos(page, todos) {
1347
+ await page
1348
+ .evaluate((list) => {
1349
+ if (window.__wgSetTodos) window.__wgSetTodos(list || []);
1350
+ }, todos && todos.length ? todos : [])
1254
1351
  .catch(() => {});
1255
1352
  }
1256
1353
 
@@ -1355,15 +1452,27 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1355
1452
  const box = await page.locator(h.selector).first().boundingBox();
1356
1453
  if (!box) continue;
1357
1454
  await updateHlProgress(i);
1358
- await applyCameraZoom(page, box, h.zoom);
1359
- const useCursor = h.cursor !== false;
1360
- if (useCursor) {
1361
- const cx = box.x + box.width / 2;
1362
- const cy = box.y + box.height / 2;
1455
+ if (opts && opts.todos && opts.todos.length) {
1456
+ const rows = opts.todos.map((t, ti) => ({
1457
+ text: t.text,
1458
+ done: ti < i,
1459
+ current: ti === i,
1460
+ }));
1461
+ await setDemoTodos(page, rows);
1462
+ } else if (h.todos && h.todos.length) {
1463
+ await setDemoTodos(page, normalizeTodos(h.todos, h.todoIndex));
1464
+ }
1465
+ const cx = box.x + box.width / 2;
1466
+ const cy = box.y + box.height / 2;
1467
+ const z = h.zoom != null && Number(h.zoom) > 1 ? Number(h.zoom) : 1;
1468
+ const camMs = 700;
1469
+ // One-shot camera: zoom + center together (no zoom-then-align).
1470
+ await applyCameraZoom(page, box, z, camMs);
1471
+ if (h.cursor !== false) {
1363
1472
  await page
1364
- .evaluate(({ cx, cy }) => {
1365
- if (window.__wgMoveCursorTo) window.__wgMoveCursorTo(cx, cy, 500, false);
1366
- }, { cx, cy })
1473
+ .evaluate(({ cx, cy, camMs }) => {
1474
+ if (window.__wgMoveCursorTo) window.__wgMoveCursorTo(cx, cy, camMs, false);
1475
+ }, { cx, cy, camMs })
1367
1476
  .catch(() => {});
1368
1477
  }
1369
1478
  await showRing(page, box, h.label, h.tone || "planned", {
@@ -1372,9 +1481,10 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1372
1481
  focus: !!h.focus,
1373
1482
  color: h.color || h.labelColor || h.textColor,
1374
1483
  });
1375
- // Live follow while this ring is up (seamless, not taped).
1484
+ await new Promise((res) => setTimeout(res, camMs));
1485
+ // Live ring track only - camera stays locked (no per-frame re-pan).
1376
1486
  await page
1377
- .evaluate(({ sel, label, tone, size, weight, focus, color, zoom }) => {
1487
+ .evaluate(({ sel, label, tone, size, weight, focus, color }) => {
1378
1488
  const tick = () => {
1379
1489
  const el = document.querySelector(sel);
1380
1490
  if (!el) {
@@ -1386,26 +1496,10 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1386
1496
  if (window.__wgPositionRing) {
1387
1497
  window.__wgPositionRing(box, label, tone || "planned", { size, weight, focus, color });
1388
1498
  }
1389
- if (zoom && zoom > 1 && window.__wgSetCamera) {
1390
- // Prefer cursor tip when visible (Screen Studio follow-mouse);
1391
- // else keep the ring target centered.
1392
- let cx = box.x + box.width / 2;
1393
- let cy = box.y + box.height / 2;
1394
- const cur = document.getElementById("wg-cursor");
1395
- if (cur && cur.style.opacity !== "0") {
1396
- const m = /translate\(([-\d.]+)px,\s*([-\d.]+)px\)/.exec(cur.style.transform || "");
1397
- if (m) {
1398
- cx = Number(m[1]);
1399
- cy = Number(m[2]);
1400
- }
1401
- }
1402
- window.__wgSetCamera(cx, cy, zoom, 0);
1403
- }
1404
1499
  window.__wgRingRaf = requestAnimationFrame(tick);
1405
1500
  };
1406
1501
  window.__wgRingRaf = requestAnimationFrame(tick);
1407
- const onScroll = () => {};
1408
- window.__wgRingTrack = onScroll;
1502
+ window.__wgRingTrack = () => {};
1409
1503
  }, {
1410
1504
  sel: h.selector,
1411
1505
  label: h.label,
@@ -1414,47 +1508,9 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1414
1508
  weight: h.weight || "normal",
1415
1509
  focus: !!h.focus,
1416
1510
  color: h.color || h.labelColor || h.textColor || "",
1417
- zoom: h.zoom != null && h.zoom > 1 ? h.zoom : 0,
1418
1511
  })
1419
1512
  .catch(() => {});
1420
1513
 
1421
- if ((opts && opts.todos && opts.todos.length) || (h.todos && h.todos.length)) {
1422
- const rows =
1423
- opts && opts.todos && opts.todos.length
1424
- ? opts.todos
1425
- : normalizeTodos(h.todos, h.todoIndex);
1426
- await page
1427
- .evaluate((todos) => {
1428
- const ul = document.getElementById("wg-todos");
1429
- if (!ul) return;
1430
- ul.innerHTML = todos
1431
- .map((t) => {
1432
- const cls = t.current
1433
- ? "wg-todo-current"
1434
- : t.done
1435
- ? "wg-todo-done"
1436
- : "wg-todo-pending";
1437
- const mark = t.done ? "\\u2713" : t.current ? "\\u2192" : "\\u25CB";
1438
- const esc = (s) =>
1439
- String(s)
1440
- .replace(/&/g, "&amp;")
1441
- .replace(/</g, "&lt;")
1442
- .replace(/>/g, "&gt;");
1443
- return (
1444
- "<li class=\\"" +
1445
- cls +
1446
- "\\"><span class=\\"wg-todo-mark\\">" +
1447
- mark +
1448
- "</span><span>" +
1449
- esc(t.text || "") +
1450
- "</span></li>"
1451
- );
1452
- })
1453
- .join("");
1454
- }, rows)
1455
- .catch(() => {});
1456
- }
1457
-
1458
1514
  // Appear settle
1459
1515
  await new Promise((res) => setTimeout(res, fadeMs));
1460
1516
  const authored = resolveFixtureDwellMs(h, { gatesFast, pace });
@@ -1522,8 +1578,8 @@ async function renderAfterStep(page, info) {
1522
1578
  .map((name, idx) => {
1523
1579
  const cls = idx <= info.moduleIndex ? "wg-mod-done" : "wg-mod-upcoming";
1524
1580
  const desc = info.allDescriptions && info.allDescriptions[idx];
1525
- const titleAttr = desc ? " title=\\"" + escA(desc) + "\\"" : "";
1526
- return "<span class=\\"wg-mod " + cls + "\\"" + titleAttr + ">" + name + "</span>";
1581
+ const titleAttr = desc ? " title=\\\"" + escA(desc) + "\\\"" : "";
1582
+ return "<span class=\\\"wg-mod " + cls + "\\\"" + titleAttr + ">" + name + "</span>";
1527
1583
  })
1528
1584
  .join("");
1529
1585
  // QA-friendly by default ("LoginSuccess" -> "Login Success") - raw
@@ -1534,13 +1590,13 @@ async function renderAfterStep(page, info) {
1534
1590
  .replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2") || info.resultTag;
1535
1591
  const pretty = window.__wgPretty !== false;
1536
1592
  const resultHtml =
1537
- "<div class=\\"wg-toggle\\">" +
1538
- "<button class=\\"wg-toggle-btn" + (pretty ? " wg-active" : "") + "\\" data-mode=\\"pretty\\">Pretty</button>" +
1539
- "<button class=\\"wg-toggle-btn" + (pretty ? "" : " wg-active") + "\\" data-mode=\\"json\\">JSON</button>" +
1593
+ "<div class=\\\"wg-toggle\\\">" +
1594
+ "<button class=\\\"wg-toggle-btn" + (pretty ? " wg-active" : "") + "\\\" data-mode=\\\"pretty\\\">Pretty</button>" +
1595
+ "<button class=\\\"wg-toggle-btn" + (pretty ? "" : " wg-active") + "\\\" data-mode=\\\"json\\\">JSON</button>" +
1540
1596
  "</div>" +
1541
- "<div class=\\"wg-result wg-result-pretty\\" style=\\"display:" + (pretty ? "block" : "none") + "\\">" +
1597
+ "<div class=\\\"wg-result wg-result-pretty\\\" style=\\\"display:" + (pretty ? "block" : "none") + "\\\">" +
1542
1598
  prettyText + "</div>" +
1543
- "<div class=\\"wg-result wg-result-json\\" style=\\"display:" + (pretty ? "none" : "block") + "\\">" +
1599
+ "<div class=\\\"wg-result wg-result-json\\\" style=\\\"display:" + (pretty ? "none" : "block") + "\\\">" +
1544
1600
  info.resultTag + "</div>";
1545
1601
  let autoNow = false;
1546
1602
  try {
@@ -1549,21 +1605,21 @@ async function renderAfterStep(page, info) {
1549
1605
  /* private mode / blocked storage - defaults to manual */
1550
1606
  }
1551
1607
  const gateHtml =
1552
- "<div class=\\"wg-autoplay-row\\"><label><input type=\\"checkbox\\" id=\\"wg-autoplay-cb\\"" +
1608
+ "<div class=\\\"wg-autoplay-row\\\"><label><input type=\\\"checkbox\\\" id=\\\"wg-autoplay-cb\\\"" +
1553
1609
  (autoNow ? " checked" : "") +
1554
1610
  "> Auto-advance</label></div>" +
1555
- "<div id=\\"wg-gate-manual\\"" + (autoNow ? " style=\\"display:none\\"" : "") +
1556
- "><button id=\\"wg-run\\">" + buttonLabel + "</button></div>" +
1557
- "<div id=\\"wg-gate-auto\\" class=\\"wg-auto\\"" + (autoNow ? "" : " style=\\"display:none\\"") +
1611
+ "<div id=\\\"wg-gate-manual\\\"" + (autoNow ? " style=\\\"display:none\\\"" : "") +
1612
+ "><button id=\\\"wg-run\\\">" + buttonLabel + "</button></div>" +
1613
+ "<div id=\\\"wg-gate-auto\\\" class=\\\"wg-auto\\\"" + (autoNow ? "" : " style=\\\"display:none\\\"") +
1558
1614
  ">Auto-advancing...</div>";
1559
1615
  const episodesHtml = info.allEpisodes && info.allEpisodes.length > 0
1560
- ? "<div id=\\"wg-episodes\\">" +
1616
+ ? "<div id=\\\"wg-episodes\\\">" +
1561
1617
  info.allEpisodes
1562
1618
  .map((e) => {
1563
1619
  const cls = e.episodeNumber < info.episodeNumber ? "wg-ep-done"
1564
1620
  : e.episodeNumber === info.episodeNumber ? "wg-ep-current"
1565
1621
  : "wg-ep-upcoming";
1566
- return "<span class=\\"wg-ep-tab " + cls + "\\">Episode " + e.episodeNumber + ": " +
1622
+ return "<span class=\\\"wg-ep-tab " + cls + "\\\">Episode " + e.episodeNumber + ": " +
1567
1623
  escA(e.episodeTitle || "") + "</span>";
1568
1624
  })
1569
1625
  .join("") +
@@ -1571,21 +1627,47 @@ async function renderAfterStep(page, info) {
1571
1627
  : "";
1572
1628
  const paceHtml =
1573
1629
  info.paceBadge || info.paceLabel
1574
- ? "<div class=\\"wg-pace\\" data-pace-kind=\\"" +
1630
+ ? "<div class=\\\"wg-pace\\\" data-pace-kind=\\\"" +
1575
1631
  escA(info.paceKind || "normal") +
1576
- "\\"><span class=\\"wg-pace-badge\\">" +
1632
+ "\\\"><span class=\\\"wg-pace-badge\\\">" +
1577
1633
  escA(info.paceBadge || "1x") +
1578
1634
  "</span><span>" +
1579
1635
  escA(info.paceLabel || "") +
1580
1636
  "</span></div>"
1581
1637
  : "";
1582
1638
  panel.innerHTML =
1583
- "<div id=\\"wg-progress\\"><div id=\\"wg-progress-bar\\" style=\\"width:" + pct + "%\\"></div></div>" +
1639
+ "<div id=\\\"wg-progress\\\"><div id=\\\"wg-progress-bar\\\" style=\\\"width:" + pct + "%\\\"></div></div>" +
1584
1640
  episodesHtml +
1585
1641
  paceHtml +
1586
- "<div id=\\"wg-modules\\" class=\\"" + modulesClass + "\\">" + modulesHtml + "</div>" +
1642
+ "<div id=\\\"wg-modules\\\" class=\\\"" + modulesClass + "\\\">" + modulesHtml + "</div>" +
1587
1643
  "<h3>" + heading + "</h3>" +
1588
- todosHtmlFromInfo(info, escA) +
1644
+ ((() => {
1645
+ const list = info.todos || [];
1646
+ if (!list.length) return "";
1647
+ return (
1648
+ '<ul id="wg-todos">' +
1649
+ list
1650
+ .map((t) => {
1651
+ const cls = t.current ? "wg-todo-current" : t.done ? "wg-todo-done" : "wg-todo-pending";
1652
+ const mark = t.done
1653
+ ? String.fromCharCode(0x2713)
1654
+ : t.current
1655
+ ? String.fromCharCode(0x2192)
1656
+ : String.fromCharCode(0x25cb);
1657
+ return (
1658
+ '<li class="' +
1659
+ cls +
1660
+ '"><span class="wg-todo-mark">' +
1661
+ mark +
1662
+ '</span><span>' +
1663
+ escA(t.text || "") +
1664
+ '</span></li>'
1665
+ );
1666
+ })
1667
+ .join("") +
1668
+ '</ul>'
1669
+ );
1670
+ })()) +
1589
1671
  resultHtml +
1590
1672
  gateHtml;
1591
1673
  if (isNewPanel) {
@@ -1652,13 +1734,13 @@ async function renderStepError(page, info) {
1652
1734
  if (!isNewPanel) panel.classList.add("wg-in");
1653
1735
  const esc = (s) => String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
1654
1736
  const episodesHtml = info.allEpisodes && info.allEpisodes.length > 0
1655
- ? "<div id=\\"wg-episodes\\">" +
1737
+ ? "<div id=\\\"wg-episodes\\\">" +
1656
1738
  info.allEpisodes
1657
1739
  .map((e) => {
1658
1740
  const cls = e.episodeNumber < info.episodeNumber ? "wg-ep-done"
1659
1741
  : e.episodeNumber === info.episodeNumber ? "wg-ep-current"
1660
1742
  : "wg-ep-upcoming";
1661
- return "<span class=\\"wg-ep-tab " + cls + "\\">Episode " + e.episodeNumber + ": " +
1743
+ return "<span class=\\\"wg-ep-tab " + cls + "\\\">Episode " + e.episodeNumber + ": " +
1662
1744
  esc(e.episodeTitle || "") + "</span>";
1663
1745
  })
1664
1746
  .join("") +
@@ -1669,8 +1751,8 @@ async function renderStepError(page, info) {
1669
1751
  .map((name, idx) => {
1670
1752
  const cls = idx === info.moduleIndex ? "wg-mod-current" : idx < info.moduleIndex ? "wg-mod-done" : "wg-mod-upcoming";
1671
1753
  const desc = info.allDescriptions && info.allDescriptions[idx];
1672
- const titleAttr = desc ? " title=\\"" + esc(desc) + "\\"" : "";
1673
- return "<span class=\\"wg-mod " + cls + "\\"" + titleAttr + ">" + name + "</span>";
1754
+ const titleAttr = desc ? " title=\\\"" + esc(desc) + "\\\"" : "";
1755
+ return "<span class=\\\"wg-mod " + cls + "\\\"" + titleAttr + ">" + name + "</span>";
1674
1756
  })
1675
1757
  .join("");
1676
1758
  const retryLabel = info.episodeNumber !== undefined ? "Retry Episode " + info.episodeNumber : "Retry";
@@ -1678,17 +1760,17 @@ async function renderStepError(page, info) {
1678
1760
  ? "Expected outcome - Step " + (info.index + 1) + " / " + info.total + " - " + info.blockName + " failed as intended"
1679
1761
  : "Step " + (info.index + 1) + " / " + info.total + " - " + info.blockName + " threw";
1680
1762
  const reasonHtml = isExpected
1681
- ? "<div class=\\"wg-expected-reason\\">" + esc(info.expectedFailureReason) + "</div>"
1763
+ ? "<div class=\\\"wg-expected-reason\\\">" + esc(info.expectedFailureReason) + "</div>"
1682
1764
  : "";
1683
1765
  panel.innerHTML =
1684
1766
  episodesHtml +
1685
- "<div id=\\"wg-modules\\" class=\\"" + modulesClass + "\\">" + modulesHtml + "</div>" +
1686
- "<h3 class=\\"" + (isExpected ? "wg-expected-heading" : "wg-error-heading") + "\\">" + headingText + "</h3>" +
1767
+ "<div id=\\\"wg-modules\\\" class=\\\"" + modulesClass + "\\\">" + modulesHtml + "</div>" +
1768
+ "<h3 class=\\\"" + (isExpected ? "wg-expected-heading" : "wg-error-heading") + "\\\">" + headingText + "</h3>" +
1687
1769
  reasonHtml +
1688
- "<div class=\\"wg-error-msg\\">" + esc(info.message) + "</div>" +
1689
- "<div class=\\"wg-error-actions\\">" +
1690
- "<button id=\\"wg-error-retry\\" class=\\"wg-error-retry\\">" + esc(retryLabel) + "</button>" +
1691
- "<button id=\\"wg-run\\" class=\\"wg-error-stop\\">Stop</button>" +
1770
+ "<div class=\\\"wg-error-msg\\\">" + esc(info.message) + "</div>" +
1771
+ "<div class=\\\"wg-error-actions\\\">" +
1772
+ "<button id=\\\"wg-error-retry\\\" class=\\\"wg-error-retry\\\">" + esc(retryLabel) + "</button>" +
1773
+ "<button id=\\\"wg-run\\\" class=\\\"wg-error-stop\\\">Stop</button>" +
1692
1774
  "</div>";
1693
1775
  if (isNewPanel) {
1694
1776
  document.documentElement.appendChild(panel);
@@ -1952,25 +2034,25 @@ async function presentSlides(page, slides, _gate, opts) {
1952
2034
  String(t).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
1953
2035
  const episodeLine =
1954
2036
  info.episodeNumber !== undefined && info.episodeNumber !== null
1955
- ? "<div class=\\"wg-episode\\">Episode " +
2037
+ ? "<div class=\\\"wg-episode\\\">Episode " +
1956
2038
  info.episodeNumber +
1957
2039
  (info.episodeTitle ? ": " + esc(info.episodeTitle) : "") +
1958
2040
  " · yap slides</div>"
1959
2041
  : "";
1960
2042
  const paceHtml =
1961
2043
  info.paceBadge || info.paceLabel
1962
- ? "<div class=\\"wg-pace\\" data-pace-kind=\\"" +
2044
+ ? "<div class=\\\"wg-pace\\\" data-pace-kind=\\\"" +
1963
2045
  esc(info.paceKind || "normal") +
1964
- "\\"><span class=\\"wg-pace-badge\\">" +
2046
+ "\\\"><span class=\\\"wg-pace-badge\\\">" +
1965
2047
  esc(info.paceBadge || "1x") +
1966
2048
  "</span><span>" +
1967
2049
  esc(info.paceLabel || "") +
1968
2050
  "</span></div>"
1969
2051
  : "";
1970
2052
  panel.innerHTML =
1971
- "<div class=\\"wg-chrome\\"><span class=\\"wg-chrome-title\\">waygraph demo</span>" +
1972
- "<button type=\\"button\\" class=\\"wg-hide-btn\\" data-wg-toggle=\\"1\\">Hide</button></div>" +
1973
- "<div class=\\"wg-body\\">" +
2053
+ "<div class=\\\"wg-chrome\\\"><span class=\\\"wg-chrome-title\\\">waygraph demo</span>" +
2054
+ "<button type=\\\"button\\\" class=\\\"wg-hide-btn\\\" data-wg-toggle=\\\"1\\\">Hide</button></div>" +
2055
+ "<div class=\\\"wg-body\\\">" +
1974
2056
  episodeLine +
1975
2057
  paceHtml +
1976
2058
  "<h3>Slide " +
@@ -1980,35 +2062,35 @@ async function presentSlides(page, slides, _gate, opts) {
1980
2062
  (info.blockName ? " · " + esc(info.blockName) : "") +
1981
2063
  "</h3>" +
1982
2064
  (info.tag
1983
- ? "<div style=\\"border:none;padding:0;margin:0 0 6px;font-size:11px;color:#c9a6ff\\">" +
2065
+ ? "<div style=\\\"border:none;padding:0;margin:0 0 6px;font-size:11px;color:#c9a6ff\\\">" +
1984
2066
  esc(info.tag) +
1985
2067
  "</div>"
1986
2068
  : "") +
1987
- "<div class=\\"wg-narration\\">" +
2069
+ "<div class=\\\"wg-narration\\\">" +
1988
2070
  esc(info.caption) +
1989
2071
  "</div>" +
1990
2072
  (info.detail
1991
- ? "<div class=\\"wg-result-pretty\\" style=\\"margin:0 0 12px;color:#f0e8ff\\">" +
2073
+ ? "<div class=\\\"wg-result-pretty\\\" style=\\\"margin:0 0 12px;color:#f0e8ff\\\">" +
1992
2074
  esc(info.detail) +
1993
2075
  "</div>"
1994
2076
  : "") +
1995
2077
  (info.dwellMs != null
1996
- ? "<div class=\\"wg-auto\\" id=\\"wg-dwell-hint\\" style=\\"margin:0 0 8px\\">Hold " +
2078
+ ? "<div class=\\\"wg-auto\\\" id=\\\"wg-dwell-hint\\\" style=\\\"margin:0 0 8px\\\">Hold " +
1997
2079
  (info.dwellMs / 1000).toFixed(1) +
1998
2080
  "s before next...</div>"
1999
2081
  : "") +
2000
- "<div class=\\"wg-autoplay-row\\"><label><input type=\\"checkbox\\" id=\\"wg-autoplay-cb\\"" +
2082
+ "<div class=\\\"wg-autoplay-row\\\"><label><input type=\\\"checkbox\\\" id=\\\"wg-autoplay-cb\\\"" +
2001
2083
  (info.autoNow ? " checked" : "") +
2002
2084
  "> Auto-advance</label></div>" +
2003
- "<div id=\\"wg-gate-manual\\"" +
2004
- (info.autoNow ? " style=\\"display:none\\"" : "") +
2005
- "><button type=\\"button\\" id=\\"wg-run\\"" +
2085
+ "<div id=\\\"wg-gate-manual\\\"" +
2086
+ (info.autoNow ? " style=\\\"display:none\\\"" : "") +
2087
+ "><button type=\\\"button\\\" id=\\\"wg-run\\\"" +
2006
2088
  (info.dwellMs != null ? " disabled" : "") +
2007
2089
  ">" +
2008
2090
  (info.isLast ? "Continue \\u25B6" : "Next slide \\u25B6") +
2009
2091
  "</button></div>" +
2010
- "<div id=\\"wg-gate-auto\\" class=\\"wg-auto\\"" +
2011
- (info.autoNow ? "" : " style=\\"display:none\\"") +
2092
+ "<div id=\\\"wg-gate-auto\\\" class=\\\"wg-auto\\\"" +
2093
+ (info.autoNow ? "" : " style=\\\"display:none\\\"") +
2012
2094
  ">Auto-advancing...</div>" +
2013
2095
  "</div>";
2014
2096
  if (isNew) {
@@ -2173,7 +2255,7 @@ async function teardownOverlay(page) {
2173
2255
  window.__wgRingTrack = null;
2174
2256
  }
2175
2257
  window.__wgNarrateOwnsRing = false;
2176
- for (const id of ["wg-panel", "wg-ring", "wg-ring-label", "wg-cursor", "wg-click-pulse", "wg-banner"]) {
2258
+ for (const id of ["wg-panel", "wg-ring", "wg-ring-label", "wg-cursor", "wg-click-pulse", "wg-banner", "wg-todo-modal"]) {
2177
2259
  const el = document.getElementById(id);
2178
2260
  if (el) el.remove();
2179
2261
  }
@@ -2747,6 +2829,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
2747
2829
  todos: stubBeforeTodos,
2748
2830
  ...paceSpeak,
2749
2831
  });
2832
+ await setDemoTodos(page, stubBeforeTodos);
2750
2833
  const edits = await gate();
2751
2834
  for (const k of requires) {
2752
2835
  if (edits[k.name] !== undefined) {
@@ -2934,6 +3017,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
2934
3017
  });
2935
3018
  stubAfterTodos = afterPhase.todos || [];
2936
3019
  await setDemoBannerTitle(page, afterPhase.title);
3020
+ await setDemoTodos(page, afterPhase.todos || []);
2937
3021
  highlights = afterPhase.highlights.map((h) => {
2938
3022
  const styled = applyHighlightStyleDefaults(h, r.highlightStyle);
2939
3023
  return {
package/dist/engine.d.ts CHANGED
@@ -544,6 +544,57 @@ export declare class Engine {
544
544
  Block<K, L>,
545
545
  EndMarker
546
546
  ]): Flow<L>;
547
+ defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>, J extends Checkpoint<string>, K extends Checkpoint<string>, L extends Checkpoint<string>, M extends Checkpoint<string>>(blocks: readonly [
548
+ StartMarker,
549
+ Block<S, B>,
550
+ Block<B, C>,
551
+ Block<C, D>,
552
+ Block<D, E>,
553
+ Block<E, F>,
554
+ Block<F, G>,
555
+ Block<G, H>,
556
+ Block<H, I>,
557
+ Block<I, J>,
558
+ Block<J, K>,
559
+ Block<K, L>,
560
+ Block<L, M>,
561
+ EndMarker
562
+ ]): Flow<M>;
563
+ defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>, J extends Checkpoint<string>, K extends Checkpoint<string>, L extends Checkpoint<string>, M extends Checkpoint<string>, N extends Checkpoint<string>>(blocks: readonly [
564
+ StartMarker,
565
+ Block<S, B>,
566
+ Block<B, C>,
567
+ Block<C, D>,
568
+ Block<D, E>,
569
+ Block<E, F>,
570
+ Block<F, G>,
571
+ Block<G, H>,
572
+ Block<H, I>,
573
+ Block<I, J>,
574
+ Block<J, K>,
575
+ Block<K, L>,
576
+ Block<L, M>,
577
+ Block<M, N>,
578
+ EndMarker
579
+ ]): Flow<N>;
580
+ defineFlow<B extends Checkpoint<string>, C extends Checkpoint<string>, D extends Checkpoint<string>, E extends Checkpoint<string>, F extends Checkpoint<string>, G extends Checkpoint<string>, H extends Checkpoint<string>, I extends Checkpoint<string>, J extends Checkpoint<string>, K extends Checkpoint<string>, L extends Checkpoint<string>, M extends Checkpoint<string>, N extends Checkpoint<string>, O extends Checkpoint<string>>(blocks: readonly [
581
+ StartMarker,
582
+ Block<S, B>,
583
+ Block<B, C>,
584
+ Block<C, D>,
585
+ Block<D, E>,
586
+ Block<E, F>,
587
+ Block<F, G>,
588
+ Block<G, H>,
589
+ Block<H, I>,
590
+ Block<I, J>,
591
+ Block<J, K>,
592
+ Block<K, L>,
593
+ Block<L, M>,
594
+ Block<M, N>,
595
+ Block<N, O>,
596
+ EndMarker
597
+ ]): Flow<O>;
547
598
  }
548
599
  /**
549
600
  * Attaches routing to `block` without touching its act/observe/resolve/verify -
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waygraph",
3
- "version": "0.12.30",
3
+ "version": "0.12.32",
4
4
  "type": "module",
5
5
  "description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
6
6
  "license": "MIT",