waygraph 0.12.30 → 0.12.31

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
@@ -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,8 +831,51 @@ 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)";
837
+ };
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 ? "\u2713" : t.current ? "\u2192" : "\u25CB";
865
+ return (
866
+ "<li class=\"" +
867
+ cls +
868
+ "\"><span class=\"wg-todo-mark\">" +
869
+ mark +
870
+ "</span><span>" +
871
+ esc(t.text || "") +
872
+ "</span></li>"
873
+ );
874
+ })
875
+ .join("");
876
+ modal.innerHTML =
877
+ "<div class=\"wg-todo-head\">Checklist</div><ul>" + rows + "</ul>";
878
+ modal.classList.add("wg-todo-on");
821
879
  };
822
880
  window.__wgResetCamera = () => {
823
881
  window.__wgSetCamera(window.innerWidth / 2, window.innerHeight / 2, 1);
@@ -1059,7 +1117,29 @@ async function renderBeforeStep(page, info) {
1059
1117
  "<div id=\\"wg-modules\\" class=\\"" + modulesClass + "\\">" + modulesHtml + "</div>" +
1060
1118
  "<h3>Step " + (info.index + 1) + " / " + info.total + " - " + info.blockName + "</h3>" +
1061
1119
  narrationHtml +
1062
- todosHtmlFromInfo(info, esc);
1120
+ ((() => {
1121
+ const list = info.todos || [];
1122
+ if (!list.length) return "";
1123
+ return (
1124
+ "<ul id=\"wg-todos\">" +
1125
+ list
1126
+ .map((t) => {
1127
+ const cls = t.current ? "wg-todo-current" : t.done ? "wg-todo-done" : "wg-todo-pending";
1128
+ const mark = t.done ? "\u2713" : t.current ? "\u2192" : "\u25CB";
1129
+ return (
1130
+ "<li class=\"" +
1131
+ cls +
1132
+ "\"><span class=\"wg-todo-mark\">" +
1133
+ mark +
1134
+ "</span><span>" +
1135
+ esc(t.text || "") +
1136
+ "</span></li>"
1137
+ );
1138
+ })
1139
+ .join("") +
1140
+ "</ul>"
1141
+ );
1142
+ })());
1063
1143
  if (info.keys.length === 0) {
1064
1144
  html += "<div class=\\"wg-key\\">(no MemKeys required)</div>";
1065
1145
  }
@@ -1237,9 +1317,9 @@ async function ensureSelectorInView(page, selector) {
1237
1317
  }
1238
1318
  }
1239
1319
 
1240
- async function applyCameraZoom(page, box, zoom) {
1320
+ async function applyCameraZoom(page, box, zoom, ms) {
1241
1321
  const z = zoom != null && Number(zoom) > 1 ? Number(zoom) : 1;
1242
- if (!box) {
1322
+ if (!box || z <= 1) {
1243
1323
  await page.evaluate(() => {
1244
1324
  if (window.__wgResetCamera) window.__wgResetCamera();
1245
1325
  }).catch(() => {});
@@ -1247,10 +1327,19 @@ async function applyCameraZoom(page, box, zoom) {
1247
1327
  }
1248
1328
  const cx = box.x + box.width / 2;
1249
1329
  const cy = box.y + box.height / 2;
1330
+ const dur = ms != null ? ms : 650;
1250
1331
  await page
1251
- .evaluate(({ cx, cy, scale }) => {
1252
- if (window.__wgSetCamera) window.__wgSetCamera(cx, cy, scale);
1253
- }, { cx, cy, scale: z })
1332
+ .evaluate(({ cx, cy, scale, dur }) => {
1333
+ if (window.__wgSetCamera) window.__wgSetCamera(cx, cy, scale, dur);
1334
+ }, { cx, cy, scale: z, dur })
1335
+ .catch(() => {});
1336
+ }
1337
+
1338
+ async function setDemoTodos(page, todos) {
1339
+ await page
1340
+ .evaluate((list) => {
1341
+ if (window.__wgSetTodos) window.__wgSetTodos(list || []);
1342
+ }, todos && todos.length ? todos : [])
1254
1343
  .catch(() => {});
1255
1344
  }
1256
1345
 
@@ -1355,15 +1444,27 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1355
1444
  const box = await page.locator(h.selector).first().boundingBox();
1356
1445
  if (!box) continue;
1357
1446
  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;
1447
+ if (opts && opts.todos && opts.todos.length) {
1448
+ const rows = opts.todos.map((t, ti) => ({
1449
+ text: t.text,
1450
+ done: ti < i,
1451
+ current: ti === i,
1452
+ }));
1453
+ await setDemoTodos(page, rows);
1454
+ } else if (h.todos && h.todos.length) {
1455
+ await setDemoTodos(page, normalizeTodos(h.todos, h.todoIndex));
1456
+ }
1457
+ const cx = box.x + box.width / 2;
1458
+ const cy = box.y + box.height / 2;
1459
+ const z = h.zoom != null && Number(h.zoom) > 1 ? Number(h.zoom) : 1;
1460
+ const camMs = 700;
1461
+ // One-shot camera: zoom + center together (no zoom-then-align).
1462
+ await applyCameraZoom(page, box, z, camMs);
1463
+ if (h.cursor !== false) {
1363
1464
  await page
1364
- .evaluate(({ cx, cy }) => {
1365
- if (window.__wgMoveCursorTo) window.__wgMoveCursorTo(cx, cy, 500, false);
1366
- }, { cx, cy })
1465
+ .evaluate(({ cx, cy, camMs }) => {
1466
+ if (window.__wgMoveCursorTo) window.__wgMoveCursorTo(cx, cy, camMs, false);
1467
+ }, { cx, cy, camMs })
1367
1468
  .catch(() => {});
1368
1469
  }
1369
1470
  await showRing(page, box, h.label, h.tone || "planned", {
@@ -1372,9 +1473,10 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1372
1473
  focus: !!h.focus,
1373
1474
  color: h.color || h.labelColor || h.textColor,
1374
1475
  });
1375
- // Live follow while this ring is up (seamless, not taped).
1476
+ await new Promise((res) => setTimeout(res, camMs));
1477
+ // Live ring track only - camera stays locked (no per-frame re-pan).
1376
1478
  await page
1377
- .evaluate(({ sel, label, tone, size, weight, focus, color, zoom }) => {
1479
+ .evaluate(({ sel, label, tone, size, weight, focus, color }) => {
1378
1480
  const tick = () => {
1379
1481
  const el = document.querySelector(sel);
1380
1482
  if (!el) {
@@ -1386,26 +1488,10 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1386
1488
  if (window.__wgPositionRing) {
1387
1489
  window.__wgPositionRing(box, label, tone || "planned", { size, weight, focus, color });
1388
1490
  }
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
1491
  window.__wgRingRaf = requestAnimationFrame(tick);
1405
1492
  };
1406
1493
  window.__wgRingRaf = requestAnimationFrame(tick);
1407
- const onScroll = () => {};
1408
- window.__wgRingTrack = onScroll;
1494
+ window.__wgRingTrack = () => {};
1409
1495
  }, {
1410
1496
  sel: h.selector,
1411
1497
  label: h.label,
@@ -1414,47 +1500,9 @@ async function cycleHighlightRings(page, highlights, gatesFast, opts) {
1414
1500
  weight: h.weight || "normal",
1415
1501
  focus: !!h.focus,
1416
1502
  color: h.color || h.labelColor || h.textColor || "",
1417
- zoom: h.zoom != null && h.zoom > 1 ? h.zoom : 0,
1418
1503
  })
1419
1504
  .catch(() => {});
1420
1505
 
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
1506
  // Appear settle
1459
1507
  await new Promise((res) => setTimeout(res, fadeMs));
1460
1508
  const authored = resolveFixtureDwellMs(h, { gatesFast, pace });
@@ -1585,7 +1633,29 @@ async function renderAfterStep(page, info) {
1585
1633
  paceHtml +
1586
1634
  "<div id=\\"wg-modules\\" class=\\"" + modulesClass + "\\">" + modulesHtml + "</div>" +
1587
1635
  "<h3>" + heading + "</h3>" +
1588
- todosHtmlFromInfo(info, escA) +
1636
+ ((() => {
1637
+ const list = info.todos || [];
1638
+ if (!list.length) return "";
1639
+ return (
1640
+ "<ul id=\"wg-todos\">" +
1641
+ list
1642
+ .map((t) => {
1643
+ const cls = t.current ? "wg-todo-current" : t.done ? "wg-todo-done" : "wg-todo-pending";
1644
+ const mark = t.done ? "\u2713" : t.current ? "\u2192" : "\u25CB";
1645
+ return (
1646
+ "<li class=\"" +
1647
+ cls +
1648
+ "\"><span class=\"wg-todo-mark\">" +
1649
+ mark +
1650
+ "</span><span>" +
1651
+ escA(t.text || "") +
1652
+ "</span></li>"
1653
+ );
1654
+ })
1655
+ .join("") +
1656
+ "</ul>"
1657
+ );
1658
+ })()) +
1589
1659
  resultHtml +
1590
1660
  gateHtml;
1591
1661
  if (isNewPanel) {
@@ -2173,7 +2243,7 @@ async function teardownOverlay(page) {
2173
2243
  window.__wgRingTrack = null;
2174
2244
  }
2175
2245
  window.__wgNarrateOwnsRing = false;
2176
- for (const id of ["wg-panel", "wg-ring", "wg-ring-label", "wg-cursor", "wg-click-pulse", "wg-banner"]) {
2246
+ for (const id of ["wg-panel", "wg-ring", "wg-ring-label", "wg-cursor", "wg-click-pulse", "wg-banner", "wg-todo-modal"]) {
2177
2247
  const el = document.getElementById(id);
2178
2248
  if (el) el.remove();
2179
2249
  }
@@ -2747,6 +2817,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
2747
2817
  todos: stubBeforeTodos,
2748
2818
  ...paceSpeak,
2749
2819
  });
2820
+ await setDemoTodos(page, stubBeforeTodos);
2750
2821
  const edits = await gate();
2751
2822
  for (const k of requires) {
2752
2823
  if (edits[k.name] !== undefined) {
@@ -2934,6 +3005,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
2934
3005
  });
2935
3006
  stubAfterTodos = afterPhase.todos || [];
2936
3007
  await setDemoBannerTitle(page, afterPhase.title);
3008
+ await setDemoTodos(page, afterPhase.todos || []);
2937
3009
  highlights = afterPhase.highlights.map((h) => {
2938
3010
  const styled = applyHighlightStyleDefaults(h, r.highlightStyle);
2939
3011
  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.31",
4
4
  "type": "module",
5
5
  "description": "A typed graph of reusable Blocks for driving a browser through E2E flows.",
6
6
  "license": "MIT",