waygraph 0.15.7 → 0.15.8
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 +15 -0
- package/dist/cli.js +205 -9
- package/dist/highlights.d.ts +52 -0
- package/dist/highlights.js +67 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/pilot-overlay.d.ts +6 -1
- package/dist/pilot-overlay.js +47 -4
- package/package.json +1 -1
- package/templates/skills/waygraph-pilot.skill.md +1 -0
package/README.md
CHANGED
|
@@ -1015,6 +1015,7 @@ Flows are files (0.10.5+):
|
|
|
1015
1015
|
| Manual watch | `waygraph demo src/flows/shop.flow.ts` |
|
|
1016
1016
|
| Auto-advance | `waygraph demo --blocks shopFlow --auto-next` (nav auto-hides strip) |
|
|
1017
1017
|
| Fast / full strip | `waygraph demo ... --fast` (shorter gates; keeps cursor) · `--full` (classic chips; default = carousel) |
|
|
1018
|
+
| Todo dock UX | `waygraph demo ... --todo-smart` (default) · `--todo-full` (no compact/collision/behind) |
|
|
1018
1019
|
| QA watch + record | `waygraph demo --blocks shopFlow --auto-play-video` |
|
|
1019
1020
|
| Ad-hoc Blocks | `waygraph run --blocks "login then nav-cart" --data '{...}'` |
|
|
1020
1021
|
| Headed execute | `waygraph run --blocks shopFlow --non-headless --video` |
|
|
@@ -1040,6 +1041,20 @@ Flows are files (0.10.5+):
|
|
|
1040
1041
|
`--auto-next` (alias `--autoplay`) = panel Auto-advance. `--auto-play-video` is **demo
|
|
1041
1042
|
only**. `chain` remains a compat alias for `run --blocks` / `auto --blocks`.
|
|
1042
1043
|
|
|
1044
|
+
### Todo dock UI (demo, 0.15.8+)
|
|
1045
|
+
|
|
1046
|
+
Long FR/AC checklists used to bury highlight rings. **Smart defaults are on:**
|
|
1047
|
+
|
|
1048
|
+
| Behavior | Default | Opt out |
|
|
1049
|
+
|----------|---------|---------|
|
|
1050
|
+
| Compact fold (~5 rows around current; hover expands; `+N more`) | on | `ctx.todoDockFull()` / `ctx.todoDockUi({ compact: false })` / `--todo-full` / `WAYGRAPH_TODO_UI=full` |
|
|
1051
|
+
| Collision flip (dock L/R when a ring overlaps) | on | `ctx.todoDockUi({ collision: false })` / `WAYGRAPH_TODO_COLLISION=0` |
|
|
1052
|
+
| Behind ring (dim + lower z-index while a ring is up) | on | `ctx.todoDockUi({ behindRing: false })` / `WAYGRAPH_TODO_BEHIND=0` |
|
|
1053
|
+
|
|
1054
|
+
Per-knob env also: `WAYGRAPH_TODO_COMPACT`, `WAYGRAPH_TODO_CAP` (default 5),
|
|
1055
|
+
`WAYGRAPH_TODO_EXPAND_CAP` (default 14). Author API: `ctx.todoDockUi({ ... })`.
|
|
1056
|
+
Pilot `auto highlight` accepts the same knobs as `todoUi` on the JSON body.
|
|
1057
|
+
|
|
1043
1058
|
Run `waygraph --help` (or any subcommand with no args) for the full flag reference kept
|
|
1044
1059
|
in `src/cli.ts`'s own `usage()` - that's the source of truth for flags, this table is the
|
|
1045
1060
|
scannable summary.
|
package/dist/cli.js
CHANGED
|
@@ -235,6 +235,7 @@ import {
|
|
|
235
235
|
completeSequentialTodoDock,
|
|
236
236
|
applyDevicePhase,
|
|
237
237
|
resolveDeviceState,
|
|
238
|
+
resolveTodoDockUi,
|
|
238
239
|
WAYGRAPH_RING_CSS,
|
|
239
240
|
} from "waygraph";
|
|
240
241
|
|
|
@@ -517,9 +518,22 @@ const RING_CSS =
|
|
|
517
518
|
"padding:10px 12px;background:rgba(20,10,40,.94);color:#fff;border-radius:12px;" +
|
|
518
519
|
"border:1px solid rgba(124,58,237,.45);box-shadow:0 8px 24px rgba(0,0,0,.35);" +
|
|
519
520
|
"pointer-events:auto;cursor:pointer;" +
|
|
520
|
-
"transition:transform .4s cubic-bezier(.22,1,.36,1),top .35s ease;transform:translateX(0);}" +
|
|
521
|
+
"transition:transform .4s cubic-bezier(.22,1,.36,1),top .35s ease,opacity .25s ease,z-index 0s;transform:translateX(0);}" +
|
|
521
522
|
".wg-todo-dock[data-pos=right],#wg-todo-dock[data-pos=right]{transform:translateX(calc(100vw - 100% - 28px));}" +
|
|
522
523
|
".wg-todo-dock[data-pos=left],#wg-todo-dock[data-pos=left]{transform:translateX(0);}" +
|
|
524
|
+
/* While a highlight ring is up: sit under the ring so captions stay readable. */
|
|
525
|
+
".wg-todo-dock.wg-todo-behind,#wg-todo-dock.wg-todo-behind{z-index:2147483643;opacity:.42;}" +
|
|
526
|
+
/* Collision tuck: shrink so a mid-page ring can still breathe. */
|
|
527
|
+
".wg-todo-dock[data-tucked=\\"1\\"],#wg-todo-dock[data-tucked=\\"1\\"]{max-height:min(28vh,220px);}" +
|
|
528
|
+
/* Compact + cap: fold overflow rows; hover / data-expanded reveals more. */
|
|
529
|
+
".wg-todo-dock[data-compact=\\"1\\"]:not(:hover):not([data-expanded=\\"1\\"]) li.wg-todo-fold{display:none;}" +
|
|
530
|
+
".wg-todo-dock[data-compact=\\"1\\"]:is(:hover,[data-expanded=\\"1\\"]) li.wg-todo-fold-deep{display:none;}" +
|
|
531
|
+
".wg-todo-dock .wg-todo-more{display:flex;align-items:center;gap:6px;margin-top:6px;padding:5px 8px;" +
|
|
532
|
+
"border-radius:8px;background:rgba(124,58,237,.22);border:1px solid rgba(124,58,237,.4);" +
|
|
533
|
+
"font:700 11px/1.2 system-ui,sans-serif;color:#e8dcff;cursor:pointer;user-select:none;}" +
|
|
534
|
+
".wg-todo-dock .wg-todo-more:hover{background:rgba(124,58,237,.35);}" +
|
|
535
|
+
".wg-todo-dock .wg-todo-dock-title{font:700 11px/1.2 system-ui,sans-serif;color:#c9a6ff;" +
|
|
536
|
+
"letter-spacing:.04em;text-transform:uppercase;margin:0 0 8px;}" +
|
|
523
537
|
".wg-todo-dock .wg-todos-list,#wg-todo-dock #wg-todos,.wg-todo-dock #wg-todos{list-style:none;margin:0;padding:0;background:transparent;border:none;}" +
|
|
524
538
|
// Video device stage: keep recordVideo size fixed; center a device-sized shell.
|
|
525
539
|
"html.wg-video-device-stage{background:#0b1220 !important;}" +
|
|
@@ -745,9 +759,17 @@ async function installOverlay(page, title) {
|
|
|
745
759
|
const envTodoPos = (process.env.WAYGRAPH_TODO_POS || "left").toLowerCase();
|
|
746
760
|
const todoPos = envTodoPos === "right" ? "right" : "left";
|
|
747
761
|
const envAutoplay = process.env.WAYGRAPH_AUTOPLAY === "1";
|
|
762
|
+
const todoDockUi = resolveTodoDockUi();
|
|
748
763
|
await page
|
|
749
764
|
.evaluate(
|
|
750
|
-
({ title, favicon, bannerPos, todoPos, envAutoplay }) => {
|
|
765
|
+
({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }) => {
|
|
766
|
+
window.__wgTodoDockUi = todoDockUi || {
|
|
767
|
+
compact: true,
|
|
768
|
+
cap: 5,
|
|
769
|
+
expandCap: 14,
|
|
770
|
+
collision: true,
|
|
771
|
+
behindRing: true,
|
|
772
|
+
};
|
|
751
773
|
// Seed the live autoplay toggle from the env default on first ever
|
|
752
774
|
// load only - a real navigation re-runs this, and re-stamping here
|
|
753
775
|
// would silently undo a human's mid-run checkbox click.
|
|
@@ -851,6 +873,57 @@ async function installOverlay(page, title) {
|
|
|
851
873
|
if (labelLeft < margin) labelLeft = margin;
|
|
852
874
|
ringLabel.style.left = labelLeft + "px";
|
|
853
875
|
ringLabel.style.top = labelTop + "px";
|
|
876
|
+
if (window.__wgTodosSetBehind) window.__wgTodosSetBehind(true);
|
|
877
|
+
if (window.__wgTodosAvoidRingCollision) window.__wgTodosAvoidRingCollision(box);
|
|
878
|
+
};
|
|
879
|
+
/** Dim/lower floating todo docks while a ring caption is the focus. */
|
|
880
|
+
window.__wgTodosSetBehind = (on) => {
|
|
881
|
+
const ui = window.__wgTodoDockUi || {};
|
|
882
|
+
if (ui.behindRing === false) {
|
|
883
|
+
document.querySelectorAll(".wg-todo-dock, #wg-todo-dock").forEach((el) => {
|
|
884
|
+
el.classList.remove("wg-todo-behind");
|
|
885
|
+
});
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
document.querySelectorAll(".wg-todo-dock, #wg-todo-dock").forEach((el) => {
|
|
889
|
+
el.classList.toggle("wg-todo-behind", !!on);
|
|
890
|
+
});
|
|
891
|
+
};
|
|
892
|
+
/**
|
|
893
|
+
* If the active ring box intersects a todo dock, flip L/R once.
|
|
894
|
+
* Locked until hideRing so follow-rAF does not thrash.
|
|
895
|
+
*/
|
|
896
|
+
window.__wgTodosAvoidRingCollision = (box) => {
|
|
897
|
+
const ui = window.__wgTodoDockUi || {};
|
|
898
|
+
if (ui.collision === false) return;
|
|
899
|
+
if (!box || window.__wgTodoCollisionLocked) return;
|
|
900
|
+
const pad = 10;
|
|
901
|
+
const rx1 = box.x - pad;
|
|
902
|
+
const ry1 = box.y - pad;
|
|
903
|
+
const rx2 = box.x + box.width + pad;
|
|
904
|
+
const ry2 = box.y + box.height + pad;
|
|
905
|
+
const overlaps = (el) => {
|
|
906
|
+
const r = el.getBoundingClientRect();
|
|
907
|
+
return !(r.right < rx1 || r.left > rx2 || r.bottom < ry1 || r.top > ry2);
|
|
908
|
+
};
|
|
909
|
+
const docks = [...document.querySelectorAll(".wg-todo-dock, #wg-todo-dock")].filter(
|
|
910
|
+
(el, i, arr) => arr.indexOf(el) === i,
|
|
911
|
+
);
|
|
912
|
+
for (const dock of docks) {
|
|
913
|
+
if (!overlaps(dock)) continue;
|
|
914
|
+
const cur = dock.dataset.pos === "right" ? "right" : "left";
|
|
915
|
+
dock.dataset.pos = cur === "right" ? "left" : "right";
|
|
916
|
+
try {
|
|
917
|
+
localStorage.setItem("wg-todo-pos", dock.dataset.pos);
|
|
918
|
+
} catch {
|
|
919
|
+
/* ignore */
|
|
920
|
+
}
|
|
921
|
+
// Still colliding after flip - tuck (shorter max-height).
|
|
922
|
+
if (overlaps(dock)) dock.dataset.tucked = "1";
|
|
923
|
+
else delete dock.dataset.tucked;
|
|
924
|
+
window.__wgTodoCollisionLocked = true;
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
854
927
|
};
|
|
855
928
|
/** Live-follow a selector with rAF (accurate under device shell scale). */
|
|
856
929
|
window.__wgStopRingFollow = () => {
|
|
@@ -907,6 +980,11 @@ async function installOverlay(page, title) {
|
|
|
907
980
|
if (ring) ring.style.opacity = "0";
|
|
908
981
|
if (ringLabel) ringLabel.style.opacity = "0";
|
|
909
982
|
if (window.__wgClearFocus) window.__wgClearFocus();
|
|
983
|
+
if (window.__wgTodosSetBehind) window.__wgTodosSetBehind(false);
|
|
984
|
+
window.__wgTodoCollisionLocked = false;
|
|
985
|
+
document.querySelectorAll(".wg-todo-dock[data-tucked], #wg-todo-dock[data-tucked]").forEach((el) => {
|
|
986
|
+
delete el.dataset.tucked;
|
|
987
|
+
});
|
|
910
988
|
// Honor zoomOut:false - keep camera until next zoom / desktop clear.
|
|
911
989
|
const forceClear = opts && opts.clearZoom === true;
|
|
912
990
|
const skipClear = opts && opts.clearZoom === false;
|
|
@@ -1422,6 +1500,7 @@ async function installOverlay(page, title) {
|
|
|
1422
1500
|
// { sync: "set", dock } / { sync:"set", list } -> render / recreate
|
|
1423
1501
|
// Compat: bare array = set list (empty array = clear - legacy only).
|
|
1424
1502
|
window.__wgSyncTodos = (payload, pos) => {
|
|
1503
|
+
const syncPayload = payload;
|
|
1425
1504
|
let sync = "set";
|
|
1426
1505
|
let list = [];
|
|
1427
1506
|
let dockState = null;
|
|
@@ -1571,7 +1650,35 @@ async function installOverlay(page, title) {
|
|
|
1571
1650
|
const id = raw != null && String(raw).trim() ? String(raw).trim() : "";
|
|
1572
1651
|
return id ? ' data-wg-todo-item="' + esc(id) + '"' : "";
|
|
1573
1652
|
};
|
|
1574
|
-
|
|
1653
|
+
// Compact + cap: >cap rows folds overflow; hover shows up to expandCap; "+N more" pins expand.
|
|
1654
|
+
const ui = (payload && !Array.isArray(payload) && payload.ui) || window.__wgTodoDockUi || {};
|
|
1655
|
+
const COMPACT_AT = Number(ui.cap) > 0 ? Number(ui.cap) : 5;
|
|
1656
|
+
const WINDOW = COMPACT_AT;
|
|
1657
|
+
const EXPAND_MAX = Number(ui.expandCap) > 0 ? Number(ui.expandCap) : 14;
|
|
1658
|
+
const compactEnabled = ui.compact !== false;
|
|
1659
|
+
const flattenItems = () => {
|
|
1660
|
+
if (dockState && Array.isArray(dockState.groups) && dockState.groups.length) {
|
|
1661
|
+
const out = [];
|
|
1662
|
+
for (const g of dockState.groups) {
|
|
1663
|
+
for (const t of g.items || []) out.push({ ...t, __style: g.style || dockState.style });
|
|
1664
|
+
}
|
|
1665
|
+
return out;
|
|
1666
|
+
}
|
|
1667
|
+
return (list || []).map((t) => ({ ...t, __style: "sequential" }));
|
|
1668
|
+
};
|
|
1669
|
+
const windowBounds = (items, cap) => {
|
|
1670
|
+
const n = items.length;
|
|
1671
|
+
if (n <= cap) return { start: 0, end: n };
|
|
1672
|
+
let cur = items.findIndex((t) => t.current);
|
|
1673
|
+
if (cur < 0) cur = items.findIndex((t) => !t.done);
|
|
1674
|
+
if (cur < 0) cur = 0;
|
|
1675
|
+
let start = Math.max(0, cur - Math.floor((cap - 1) / 2));
|
|
1676
|
+
let end = Math.min(n, start + cap);
|
|
1677
|
+
start = Math.max(0, end - cap);
|
|
1678
|
+
return { start, end };
|
|
1679
|
+
};
|
|
1680
|
+
const renderItems = (items, style, foldMeta, indexOffset) => {
|
|
1681
|
+
const offset = indexOffset || 0;
|
|
1575
1682
|
const rawStyle = String(style || "sequential").toLowerCase();
|
|
1576
1683
|
const st =
|
|
1577
1684
|
rawStyle === "checklist"
|
|
@@ -1584,13 +1691,18 @@ async function installOverlay(page, title) {
|
|
|
1584
1691
|
rawStyle === "plain"
|
|
1585
1692
|
? "bullets"
|
|
1586
1693
|
: "sequential";
|
|
1694
|
+
const foldStart = foldMeta ? foldMeta.foldStart : -1;
|
|
1695
|
+
const foldEnd = foldMeta ? foldMeta.foldEnd : -1;
|
|
1696
|
+
const deepStart = foldMeta ? foldMeta.deepStart : -1;
|
|
1697
|
+
const deepEnd = foldMeta ? foldMeta.deepEnd : -1;
|
|
1587
1698
|
return (
|
|
1588
1699
|
'<ul class="wg-todos-list" data-wg-todo-style="' +
|
|
1589
1700
|
st +
|
|
1590
1701
|
'">' +
|
|
1591
1702
|
(items || [])
|
|
1592
|
-
.map((t) => {
|
|
1593
|
-
const
|
|
1703
|
+
.map((t, idx) => {
|
|
1704
|
+
const gi = offset + idx;
|
|
1705
|
+
const clsBase =
|
|
1594
1706
|
st === "bullets"
|
|
1595
1707
|
? t.done
|
|
1596
1708
|
? "wg-todo-done"
|
|
@@ -1600,6 +1712,9 @@ async function installOverlay(page, title) {
|
|
|
1600
1712
|
: t.done
|
|
1601
1713
|
? "wg-todo-done"
|
|
1602
1714
|
: "wg-todo-pending";
|
|
1715
|
+
let foldCls = "";
|
|
1716
|
+
if (foldStart >= 0 && (gi < foldStart || gi >= foldEnd)) foldCls += " wg-todo-fold";
|
|
1717
|
+
if (deepStart >= 0 && (gi < deepStart || gi >= deepEnd)) foldCls += " wg-todo-fold-deep";
|
|
1603
1718
|
let mark;
|
|
1604
1719
|
if (st === "checklist") {
|
|
1605
1720
|
mark = t.done ? "\u2611" : "\u2610";
|
|
@@ -1611,7 +1726,8 @@ async function installOverlay(page, title) {
|
|
|
1611
1726
|
const rowId = t.id || t.name || "";
|
|
1612
1727
|
return (
|
|
1613
1728
|
'<li class="' +
|
|
1614
|
-
|
|
1729
|
+
clsBase +
|
|
1730
|
+
foldCls +
|
|
1615
1731
|
'"' +
|
|
1616
1732
|
attrId(rowId) +
|
|
1617
1733
|
'><span class="wg-todo-mark">' +
|
|
@@ -1629,12 +1745,28 @@ async function installOverlay(page, title) {
|
|
|
1629
1745
|
if (dockKey !== "_default") dock.setAttribute("data-wg-todo-id", dockKey);
|
|
1630
1746
|
else dock.removeAttribute("data-wg-todo-id");
|
|
1631
1747
|
|
|
1748
|
+
const flat = flattenItems();
|
|
1749
|
+
const compact = compactEnabled && flat.length > COMPACT_AT;
|
|
1750
|
+
const win = windowBounds(flat, WINDOW);
|
|
1751
|
+
const expand = windowBounds(flat, EXPAND_MAX);
|
|
1752
|
+
const foldMeta = compact
|
|
1753
|
+
? {
|
|
1754
|
+
foldStart: win.start,
|
|
1755
|
+
foldEnd: win.end,
|
|
1756
|
+
deepStart: expand.start,
|
|
1757
|
+
deepEnd: expand.end,
|
|
1758
|
+
}
|
|
1759
|
+
: null;
|
|
1760
|
+
const hiddenCompact = compact ? flat.length - (win.end - win.start) : 0;
|
|
1761
|
+
const hiddenExpand = compact ? flat.length - (expand.end - expand.start) : 0;
|
|
1762
|
+
|
|
1632
1763
|
let html = "";
|
|
1633
1764
|
if (dockState && Array.isArray(dockState.groups) && dockState.groups.length) {
|
|
1634
1765
|
const dockTitle = dockState.title && String(dockState.title).trim();
|
|
1635
1766
|
if (dockTitle) {
|
|
1636
1767
|
html += '<div class="wg-todo-dock-title">' + esc(dockTitle) + "</div>";
|
|
1637
1768
|
}
|
|
1769
|
+
let globalIdx = 0;
|
|
1638
1770
|
for (const g of dockState.groups) {
|
|
1639
1771
|
const gTitle = g.title && String(g.title).trim();
|
|
1640
1772
|
const gid = g.id || g.name || "";
|
|
@@ -1645,13 +1777,42 @@ async function installOverlay(page, title) {
|
|
|
1645
1777
|
if (gTitle) {
|
|
1646
1778
|
html += '<div class="wg-todo-group-title">' + esc(gTitle) + "</div>";
|
|
1647
1779
|
}
|
|
1648
|
-
|
|
1780
|
+
const gItems = g.items || [];
|
|
1781
|
+
html += renderItems(gItems, g.style || dockState.style, foldMeta, globalIdx);
|
|
1649
1782
|
html += "</div>";
|
|
1783
|
+
globalIdx += gItems.length;
|
|
1650
1784
|
}
|
|
1651
1785
|
} else {
|
|
1652
|
-
html = renderItems(list, "sequential");
|
|
1786
|
+
html = renderItems(list, "sequential", foldMeta, 0);
|
|
1787
|
+
}
|
|
1788
|
+
if (compact && hiddenCompact > 0) {
|
|
1789
|
+
const n = dock.dataset.expanded === "1" ? hiddenExpand : hiddenCompact;
|
|
1790
|
+
if (n > 0) {
|
|
1791
|
+
html +=
|
|
1792
|
+
'<div class="wg-todo-more" data-wg-todo-more="1" title="Click to expand / collapse">' +
|
|
1793
|
+
"+" +
|
|
1794
|
+
n +
|
|
1795
|
+
" more</div>";
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
if (compact) dock.dataset.compact = "1";
|
|
1799
|
+
else {
|
|
1800
|
+
delete dock.dataset.compact;
|
|
1801
|
+
delete dock.dataset.expanded;
|
|
1653
1802
|
}
|
|
1654
1803
|
dock.innerHTML = html;
|
|
1804
|
+
const moreBtn = dock.querySelector("[data-wg-todo-more]");
|
|
1805
|
+
if (moreBtn) {
|
|
1806
|
+
moreBtn.addEventListener("click", (e) => {
|
|
1807
|
+
e.stopPropagation();
|
|
1808
|
+
if (dock.dataset.expanded === "1") delete dock.dataset.expanded;
|
|
1809
|
+
else dock.dataset.expanded = "1";
|
|
1810
|
+
if (window.__wgSyncTodos && dock._wgLastPayload) {
|
|
1811
|
+
window.__wgSyncTodos(dock._wgLastPayload);
|
|
1812
|
+
}
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
dock._wgLastPayload = syncPayload;
|
|
1655
1816
|
relayoutTodoDocks();
|
|
1656
1817
|
if (window.__wgStampModal) {
|
|
1657
1818
|
window.__wgStampModal(dock, "todos", { ready: true });
|
|
@@ -1864,7 +2025,7 @@ async function installOverlay(page, title) {
|
|
|
1864
2025
|
}
|
|
1865
2026
|
};
|
|
1866
2027
|
},
|
|
1867
|
-
{ title, favicon: WAYGRAPH_FAVICON, bannerPos, todoPos, envAutoplay },
|
|
2028
|
+
{ title, favicon: WAYGRAPH_FAVICON, bannerPos, todoPos, envAutoplay, todoDockUi },
|
|
1868
2029
|
)
|
|
1869
2030
|
.catch(() => {});
|
|
1870
2031
|
// Navigations wipe #wg-device-shell - rebuild when video stage + device are live.
|
|
@@ -4970,8 +5131,21 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
4970
5131
|
let result;
|
|
4971
5132
|
/** Carry floating todo dock across steps (empty stubBefore must not wipe). */
|
|
4972
5133
|
let lastTodoDock = undefined;
|
|
5134
|
+
/** Author todoDockUi patches (merged); resolved against env each step. */
|
|
5135
|
+
let lastTodoDockUiPatch = undefined;
|
|
4973
5136
|
/** Multi-todo: every dock keyed by todoId (or "_default"), survives navigation. */
|
|
4974
5137
|
const todoDockById = new Map();
|
|
5138
|
+
const pushTodoDockUi = async (patch) => {
|
|
5139
|
+
if (patch && typeof patch === "object") {
|
|
5140
|
+
lastTodoDockUiPatch = { ...(lastTodoDockUiPatch || {}), ...patch };
|
|
5141
|
+
}
|
|
5142
|
+
const ui = resolveTodoDockUi(lastTodoDockUiPatch);
|
|
5143
|
+
await page
|
|
5144
|
+
.evaluate((u) => {
|
|
5145
|
+
window.__wgTodoDockUi = u;
|
|
5146
|
+
}, ui)
|
|
5147
|
+
.catch(() => {});
|
|
5148
|
+
};
|
|
4975
5149
|
const dockRegistryKey = (dock) =>
|
|
4976
5150
|
dock && dock.id && String(dock.id).trim() ? String(dock.id).trim() : "_default";
|
|
4977
5151
|
const rememberTodoDock = (dock, sync, parallel) => {
|
|
@@ -5104,6 +5278,11 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
5104
5278
|
const fixtures = r.highlightFixtures;
|
|
5105
5279
|
const flowStyle = r.highlightStyle;
|
|
5106
5280
|
const stubBeforePhase = await runStubPhase(r.block, "stubBefore", { fixtures, mem });
|
|
5281
|
+
if (stubBeforePhase.todoDockUi) {
|
|
5282
|
+
await pushTodoDockUi(stubBeforePhase.todoDockUi);
|
|
5283
|
+
} else {
|
|
5284
|
+
await pushTodoDockUi(undefined);
|
|
5285
|
+
}
|
|
5107
5286
|
stubBeforeRef.current = stubBeforePhase.highlights.map((h) =>
|
|
5108
5287
|
applyHighlightStyleDefaults(h, flowStyle),
|
|
5109
5288
|
);
|
|
@@ -5422,6 +5601,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
5422
5601
|
fixtures: fixturesAfter,
|
|
5423
5602
|
mem,
|
|
5424
5603
|
});
|
|
5604
|
+
if (afterPhase.todoDockUi) {
|
|
5605
|
+
await pushTodoDockUi(afterPhase.todoDockUi);
|
|
5606
|
+
}
|
|
5425
5607
|
logStubPhaseFixtures("after", {
|
|
5426
5608
|
...afterPhase,
|
|
5427
5609
|
highlights: (afterPhase.highlights || []).map((h) => {
|
|
@@ -6604,6 +6786,12 @@ function parseRunFlags(argv) {
|
|
|
6604
6786
|
else if (a === "--todo-right") {
|
|
6605
6787
|
out.todoPos = "right";
|
|
6606
6788
|
}
|
|
6789
|
+
else if (a === "--todo-full" || a === "--no-todo-smart") {
|
|
6790
|
+
out.todoUiFull = true;
|
|
6791
|
+
}
|
|
6792
|
+
else if (a === "--todo-smart") {
|
|
6793
|
+
out.todoUiSmart = true;
|
|
6794
|
+
}
|
|
6607
6795
|
else if (a === "--ff-expand") {
|
|
6608
6796
|
out.ffExpand = true;
|
|
6609
6797
|
}
|
|
@@ -6782,6 +6970,12 @@ function applyRunFlags(flags, opts) {
|
|
|
6782
6970
|
if (flags.todoPos === "left" || flags.todoPos === "right") {
|
|
6783
6971
|
process.env.WAYGRAPH_TODO_POS = flags.todoPos;
|
|
6784
6972
|
}
|
|
6973
|
+
if (flags.todoUiFull) {
|
|
6974
|
+
process.env.WAYGRAPH_TODO_UI = "full";
|
|
6975
|
+
}
|
|
6976
|
+
else if (flags.todoUiSmart) {
|
|
6977
|
+
process.env.WAYGRAPH_TODO_UI = "smart";
|
|
6978
|
+
}
|
|
6785
6979
|
if (flags.ffExpand) {
|
|
6786
6980
|
process.env.WAYGRAPH_FF_EXPAND = "1";
|
|
6787
6981
|
}
|
|
@@ -7030,6 +7224,8 @@ Primary (less is more):
|
|
|
7030
7224
|
--full Classic wrap-all block chips (default: carousel)
|
|
7031
7225
|
--mini Force compact mini panel (Hide pill + Next); alias --stepper-mini
|
|
7032
7226
|
--todo-left|--todo-right Floating checklist dock side (also ctx.todoPos / WAYGRAPH_TODO_POS)
|
|
7227
|
+
--todo-full Opt out: full checklist, no compact/collision/behind-ring
|
|
7228
|
+
--todo-smart Opt in: compact + collision + behind-ring (default)
|
|
7033
7229
|
--ff-expand Expand fastForwardComposeBlock inners as separate steps
|
|
7034
7230
|
--ff-disabled Dispute: expand FF (alias --no-ff); blitz kept on those inners
|
|
7035
7231
|
--auto-play-video Unattended + recorded: --auto-next + --video (+ step); headless
|
package/dist/highlights.d.ts
CHANGED
|
@@ -105,6 +105,41 @@ export type WaygraphTodosOpts = {
|
|
|
105
105
|
*/
|
|
106
106
|
parallel?: boolean;
|
|
107
107
|
};
|
|
108
|
+
/**
|
|
109
|
+
* Floating todo-dock UX knobs (0.15.8+). Defaults are **smart on** so long
|
|
110
|
+
* FR/AC checklists do not bury highlight rings. Opt out per phase or globally.
|
|
111
|
+
*
|
|
112
|
+
* Defaults: `compact`, `collision`, `behindRing` = true; `cap` = 5; `expandCap` = 14.
|
|
113
|
+
*
|
|
114
|
+
* Global: `WAYGRAPH_TODO_UI=full` / `waygraph demo --todo-full` turns smart off.
|
|
115
|
+
* Per-knob env: `WAYGRAPH_TODO_COMPACT=0`, `WAYGRAPH_TODO_COLLISION=0`,
|
|
116
|
+
* `WAYGRAPH_TODO_BEHIND=0`, `WAYGRAPH_TODO_CAP`, `WAYGRAPH_TODO_EXPAND_CAP`.
|
|
117
|
+
*/
|
|
118
|
+
export type TodoDockUiOpts = {
|
|
119
|
+
/** Fold long lists around the current row (+N more / hover). Default true. */
|
|
120
|
+
compact?: boolean;
|
|
121
|
+
/** Visible rows when compact. Default 5. */
|
|
122
|
+
cap?: number;
|
|
123
|
+
/** Visible rows on hover / pinned expand. Default 14. */
|
|
124
|
+
expandCap?: number;
|
|
125
|
+
/** Flip dock L/R when an active ring overlaps it. Default true. */
|
|
126
|
+
collision?: boolean;
|
|
127
|
+
/** Dim + lower z-index while a highlight ring is up. Default true. */
|
|
128
|
+
behindRing?: boolean;
|
|
129
|
+
};
|
|
130
|
+
/** Resolved todo-dock UX (all fields present). */
|
|
131
|
+
export type TodoDockUiResolved = {
|
|
132
|
+
compact: boolean;
|
|
133
|
+
cap: number;
|
|
134
|
+
expandCap: number;
|
|
135
|
+
collision: boolean;
|
|
136
|
+
behindRing: boolean;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Resolve todo-dock UX from env + optional author patch.
|
|
140
|
+
* `WAYGRAPH_TODO_UI=full|off|legacy` forces compact/collision/behindRing off.
|
|
141
|
+
*/
|
|
142
|
+
export declare function resolveTodoDockUi(patch?: TodoDockUiOpts | null, env?: NodeJS.ProcessEnv): TodoDockUiResolved;
|
|
108
143
|
/** Episode-level fixtures authored inside stubBefore/After/OnError(ctx). */
|
|
109
144
|
export type StubPhaseFixtures = {
|
|
110
145
|
todos?: readonly WaygraphTodoInput[];
|
|
@@ -135,6 +170,11 @@ export type StubPhaseFixtures = {
|
|
|
135
170
|
* Authored control (also click / WAYGRAPH_TODO_POS / --todo-left|right).
|
|
136
171
|
*/
|
|
137
172
|
todoPos?: "left" | "right";
|
|
173
|
+
/**
|
|
174
|
+
* Todo-dock UX (compact / collision / behind-ring). Merged onto env defaults.
|
|
175
|
+
* See {@link TodoDockUiOpts}.
|
|
176
|
+
*/
|
|
177
|
+
todoDockUi?: TodoDockUiOpts;
|
|
138
178
|
/**
|
|
139
179
|
* Viewport preset: `mobile` | `tablet` | `desktop` (default / clear).
|
|
140
180
|
* Omit on later blocks = **keep** (same persist pattern as todos).
|
|
@@ -403,6 +443,16 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
403
443
|
* code; click / env / --todo-left|right are fallbacks.
|
|
404
444
|
*/
|
|
405
445
|
todoPos(side: "left" | "right"): void;
|
|
446
|
+
/**
|
|
447
|
+
* Todo-dock UX: compact fold, collision flip, dim-behind-ring.
|
|
448
|
+
* Defaults are smart-on; pass false fields to opt out for this phase.
|
|
449
|
+
* @example
|
|
450
|
+
* ctx.todoDockUi({ compact: false }); // always show full list
|
|
451
|
+
* ctx.todoDockFull(); // shorthand: compact+collision+behindRing off
|
|
452
|
+
*/
|
|
453
|
+
todoDockUi(opts: TodoDockUiOpts): void;
|
|
454
|
+
/** Shorthand: full checklist, no compact/collision/behind-ring for this phase. */
|
|
455
|
+
todoDockFull(): void;
|
|
406
456
|
/** Batch-set rings + episode fixtures. */
|
|
407
457
|
set(partial: {
|
|
408
458
|
highlights?: HighlightStubPhase;
|
|
@@ -430,6 +480,8 @@ export type StubPhaseResult = {
|
|
|
430
480
|
* - `keep` - author omitted todos; demo must leave previous dock alone
|
|
431
481
|
*/
|
|
432
482
|
todoSync?: "set" | "clear" | "keep";
|
|
483
|
+
/** Todo-dock UX for this phase (when author called {@link StubCtx.todoDockUi}). */
|
|
484
|
+
todoDockUi?: TodoDockUiOpts;
|
|
433
485
|
/** Resolved device after this phase (undefined when keep with no prior). */
|
|
434
486
|
device?: DeviceState;
|
|
435
487
|
/**
|
package/dist/highlights.js
CHANGED
|
@@ -12,6 +12,57 @@
|
|
|
12
12
|
* All fixture shapes share optional {@link FixtureDurationFields} so demo dwell
|
|
13
13
|
* before advancing is authored the same way on stubs, flow fixtures, and slides.
|
|
14
14
|
*/
|
|
15
|
+
function envFlagBool(env, key, fallback) {
|
|
16
|
+
const v = env[key];
|
|
17
|
+
if (v === undefined || v === "")
|
|
18
|
+
return fallback;
|
|
19
|
+
const s = String(v).trim().toLowerCase();
|
|
20
|
+
if (s === "0" || s === "false" || s === "off" || s === "no")
|
|
21
|
+
return false;
|
|
22
|
+
if (s === "1" || s === "true" || s === "on" || s === "yes")
|
|
23
|
+
return true;
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
function envFlagInt(env, key, fallback) {
|
|
27
|
+
const n = Number(env[key]);
|
|
28
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve todo-dock UX from env + optional author patch.
|
|
32
|
+
* `WAYGRAPH_TODO_UI=full|off|legacy` forces compact/collision/behindRing off.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveTodoDockUi(patch, env = process.env) {
|
|
35
|
+
const mode = String(env.WAYGRAPH_TODO_UI || "smart")
|
|
36
|
+
.trim()
|
|
37
|
+
.toLowerCase();
|
|
38
|
+
const full = mode === "full" || mode === "off" || mode === "0" || mode === "legacy" || mode === "classic";
|
|
39
|
+
const base = {
|
|
40
|
+
compact: full ? false : envFlagBool(env, "WAYGRAPH_TODO_COMPACT", true),
|
|
41
|
+
collision: full ? false : envFlagBool(env, "WAYGRAPH_TODO_COLLISION", true),
|
|
42
|
+
behindRing: full ? false : envFlagBool(env, "WAYGRAPH_TODO_BEHIND", true),
|
|
43
|
+
cap: envFlagInt(env, "WAYGRAPH_TODO_CAP", 5),
|
|
44
|
+
expandCap: envFlagInt(env, "WAYGRAPH_TODO_EXPAND_CAP", 14),
|
|
45
|
+
};
|
|
46
|
+
if (base.expandCap < base.cap)
|
|
47
|
+
base.expandCap = base.cap;
|
|
48
|
+
if (!patch)
|
|
49
|
+
return base;
|
|
50
|
+
const cap = patch.cap !== undefined && Number.isFinite(patch.cap)
|
|
51
|
+
? Math.max(1, Math.floor(Number(patch.cap)))
|
|
52
|
+
: base.cap;
|
|
53
|
+
let expandCap = patch.expandCap !== undefined && Number.isFinite(patch.expandCap)
|
|
54
|
+
? Math.max(1, Math.floor(Number(patch.expandCap)))
|
|
55
|
+
: base.expandCap;
|
|
56
|
+
if (expandCap < cap)
|
|
57
|
+
expandCap = cap;
|
|
58
|
+
return {
|
|
59
|
+
compact: patch.compact !== undefined ? !!patch.compact : base.compact,
|
|
60
|
+
collision: patch.collision !== undefined ? !!patch.collision : base.collision,
|
|
61
|
+
behindRing: patch.behindRing !== undefined ? !!patch.behindRing : base.behindRing,
|
|
62
|
+
cap,
|
|
63
|
+
expandCap,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
15
66
|
/** Built-in preset sizes (CSS pixels) — stored in portrait for mobile/tablet. */
|
|
16
67
|
export const DEVICE_PRESETS = {
|
|
17
68
|
mobile: {
|
|
@@ -966,6 +1017,18 @@ function createStubCtx(bag, opts) {
|
|
|
966
1017
|
if (p)
|
|
967
1018
|
bag.todoPos = p;
|
|
968
1019
|
},
|
|
1020
|
+
todoDockUi(opts) {
|
|
1021
|
+
if (!opts || typeof opts !== "object")
|
|
1022
|
+
return;
|
|
1023
|
+
bag.todoDockUi = { ...(bag.todoDockUi || {}), ...opts };
|
|
1024
|
+
},
|
|
1025
|
+
todoDockFull() {
|
|
1026
|
+
bag.todoDockUi = {
|
|
1027
|
+
compact: false,
|
|
1028
|
+
collision: false,
|
|
1029
|
+
behindRing: false,
|
|
1030
|
+
};
|
|
1031
|
+
},
|
|
969
1032
|
set(partial) {
|
|
970
1033
|
if (partial.highlights)
|
|
971
1034
|
bag.highlights = { ...partial.highlights };
|
|
@@ -1040,6 +1103,9 @@ function createStubCtx(bag, opts) {
|
|
|
1040
1103
|
const tp = normalizeTodoPos(partial.todoPos);
|
|
1041
1104
|
if (tp)
|
|
1042
1105
|
bag.todoPos = tp;
|
|
1106
|
+
if (partial.todoDockUi !== undefined && partial.todoDockUi && typeof partial.todoDockUi === "object") {
|
|
1107
|
+
bag.todoDockUi = { ...(bag.todoDockUi || {}), ...partial.todoDockUi };
|
|
1108
|
+
}
|
|
1043
1109
|
},
|
|
1044
1110
|
};
|
|
1045
1111
|
}
|
|
@@ -1237,6 +1303,7 @@ export async function runStubPhase(block, phase, opts) {
|
|
|
1237
1303
|
...(bag.zoomOut !== undefined ? { zoomOut: bag.zoomOut } : {}),
|
|
1238
1304
|
...(bag.title !== undefined && bag.title !== "" ? { title: bag.title } : {}),
|
|
1239
1305
|
...(bag.todoPos !== undefined ? { todoPos: bag.todoPos } : {}),
|
|
1306
|
+
...(bag.todoDockUi !== undefined ? { todoDockUi: bag.todoDockUi } : {}),
|
|
1240
1307
|
...(bag.todoParallel === true ? { todoParallel: true } : {}),
|
|
1241
1308
|
};
|
|
1242
1309
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ export { connect, checkpoint } from "./types.js";
|
|
|
3
3
|
export { MemKey, MemPage, key, keyGroup } from "./mem-page.js";
|
|
4
4
|
export { Trait, urlMatches, textEquals, visible, frameVisible, frameTextEquals, frameContainsText } from "./trait.js";
|
|
5
5
|
export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, defineAssertBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, withStubBefore, withStubAfter, withStubOnError, chainFlow, defineLayout, map, MapBuilder, } from "./engine.js";
|
|
6
|
-
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, normalizeDeviceOrientation, applyOrientation, orientationFromViewport, DEVICE_PRESETS, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, WAYGRAPH_RING_CSS, } from "./highlights.js";
|
|
7
|
-
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, WaygraphTodoGroup, WaygraphTodoGroupInput, WaygraphTodosOpts, TodoDockState, TodoListStyle, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, DevicePreset, DeviceViewport, DeviceState, DeviceOrientation, } from "./highlights.js";
|
|
6
|
+
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, normalizeDeviceOrientation, applyOrientation, orientationFromViewport, DEVICE_PRESETS, resolveTodoDockUi, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, WAYGRAPH_RING_CSS, } from "./highlights.js";
|
|
7
|
+
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, WaygraphTodoGroup, WaygraphTodoGroupInput, WaygraphTodosOpts, TodoDockUiOpts, TodoDockUiResolved, TodoDockState, TodoListStyle, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, DevicePreset, DeviceViewport, DeviceState, DeviceOrientation, } from "./highlights.js";
|
|
8
8
|
export type { Flow, EngineConfig, ComposedBlock, FastForwardComposedBlock, RunGraphOptions, BlockInfo, NavBlock, NavBlockOptions, NavClickBlock, NavClickBlockOptions, MethodBlock, ActionBlock, AssertBlock, AssertBlockOptions, PageBlock, PageBlockOptions, PageMethodEntry, EffectBlock, MemEffectBlock, MemNavBlock, Layout, LayoutOptions, MapBuilderOptions, } from "./engine.js";
|
|
9
9
|
export type { ActionPage } from "./types.js";
|
|
10
10
|
export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { connect, checkpoint } from "./types.js";
|
|
|
2
2
|
export { MemKey, MemPage, key, keyGroup } from "./mem-page.js";
|
|
3
3
|
export { Trait, urlMatches, textEquals, visible, frameVisible, frameTextEquals, frameContainsText } from "./trait.js";
|
|
4
4
|
export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, defineAssertBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, withStubBefore, withStubAfter, withStubOnError, chainFlow, defineLayout, map, MapBuilder, } from "./engine.js";
|
|
5
|
-
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, normalizeDeviceOrientation, applyOrientation, orientationFromViewport, DEVICE_PRESETS, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, WAYGRAPH_RING_CSS, } from "./highlights.js";
|
|
5
|
+
export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, normalizeDeviceOrientation, applyOrientation, orientationFromViewport, DEVICE_PRESETS, resolveTodoDockUi, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, WAYGRAPH_RING_CSS, } from "./highlights.js";
|
|
6
6
|
export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
|
7
7
|
export { isFileSelectToken, parseBlocksSelect, matchBlocksSelect, globToRegExp, } from "./blocks-select.js";
|
|
8
8
|
export { EdgeLeaseCoordinator } from "./traverse-lease.js";
|
package/dist/pilot-overlay.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import type { Page } from "@playwright/test";
|
|
20
20
|
import type { SessionSnapshot } from "./auto-session.js";
|
|
21
|
-
import { type HighlightTone, type DevicePreset, type DeviceState } from "./highlights.js";
|
|
21
|
+
import { type HighlightTone, type DevicePreset, type DeviceState, type TodoDockUiOpts } from "./highlights.js";
|
|
22
22
|
/**
|
|
23
23
|
* Bottom-left transient toast, separate from the persistent bottom-right
|
|
24
24
|
* badge - real, direct user request: something that visibly flags "a
|
|
@@ -97,6 +97,11 @@ export type PilotHighlightFixtures = {
|
|
|
97
97
|
todoTitle?: string;
|
|
98
98
|
/** Todo dock side. Default `right`. */
|
|
99
99
|
todoPos?: "left" | "right";
|
|
100
|
+
/**
|
|
101
|
+
* Todo-dock UX (compact / collision / behind-ring). Defaults smart-on;
|
|
102
|
+
* pass false fields to opt out. See {@link TodoDockUiOpts}.
|
|
103
|
+
*/
|
|
104
|
+
todoUi?: TodoDockUiOpts;
|
|
100
105
|
/**
|
|
101
106
|
* How long fixtures stay visible (ms). `0` = until the next highlight /
|
|
102
107
|
* clear. Default `30000` (30s) unless specified.
|
package/dist/pilot-overlay.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WAYGRAPH_RING_CSS, normalizeHighlightTone, normalizeHighlightSize, normalizeHighlightWeight, formatHighlightCaption, resolveDeviceState, } from "./highlights.js";
|
|
1
|
+
import { WAYGRAPH_RING_CSS, normalizeHighlightTone, normalizeHighlightSize, normalizeHighlightWeight, formatHighlightCaption, resolveDeviceState, resolveTodoDockUi, } from "./highlights.js";
|
|
2
2
|
// Real, direct user request: this overlay's own colors were an improvised
|
|
3
3
|
// purple/white scheme (#a78bfa / #1a1033) that didn't match anything else -
|
|
4
4
|
// "our design here seems like barely shares with the ui on the stepper
|
|
@@ -88,11 +88,13 @@ const OVERLAY_CSS = `
|
|
|
88
88
|
.wg-pilot-fx-label[data-size=lg]{font-size:16px;padding:8px 14px;}
|
|
89
89
|
.wg-pilot-fx-label[data-weight=bold]{font-weight:800;}
|
|
90
90
|
#wg-pilot-fx-todos{
|
|
91
|
-
position:fixed;z-index:2147483645;top:72px;max-width:280px;
|
|
91
|
+
position:fixed;z-index:2147483645;top:72px;max-width:280px;max-height:calc(100vh - 100px);overflow:auto;
|
|
92
92
|
font:12px/1.4 ui-sans-serif,system-ui,sans-serif;color:#fff;
|
|
93
93
|
background:rgba(20,10,40,.94);border-radius:10px;padding:10px 12px;
|
|
94
|
-
box-shadow:0 2px 10px rgba(0,0,0,.3);pointer-events:
|
|
94
|
+
box-shadow:0 2px 10px rgba(0,0,0,.3);pointer-events:auto;
|
|
95
|
+
transition:opacity .25s ease;
|
|
95
96
|
}
|
|
97
|
+
#wg-pilot-fx-todos.wg-todo-behind{z-index:2147483643;opacity:.42;pointer-events:none;}
|
|
96
98
|
#wg-pilot-fx-todos[data-pos=left]{left:14px;right:auto;}
|
|
97
99
|
#wg-pilot-fx-todos[data-pos=right]{right:14px;left:auto;}
|
|
98
100
|
#wg-pilot-fx-todos .wg-pilot-fx-todo-title{font-weight:700;color:#c9a6ff;margin-bottom:6px;}
|
|
@@ -100,6 +102,9 @@ const OVERLAY_CSS = `
|
|
|
100
102
|
#wg-pilot-fx-todos li{margin:3px 0;color:#ddd;}
|
|
101
103
|
#wg-pilot-fx-todos li.wg-pilot-fx-todo-done{color:#888;text-decoration:line-through;}
|
|
102
104
|
#wg-pilot-fx-todos li.wg-pilot-fx-todo-now{color:#fff;font-weight:700;}
|
|
105
|
+
#wg-pilot-fx-todos[data-compact="1"]:not(:hover) li.wg-todo-fold{display:none;}
|
|
106
|
+
#wg-pilot-fx-todos .wg-todo-more{margin-top:6px;padding:4px 8px;border-radius:8px;
|
|
107
|
+
background:rgba(124,58,237,.22);font:700 11px/1.2 system-ui,sans-serif;color:#e8dcff;}
|
|
103
108
|
/* Spotlight (demo focus:true) - dim page, cutout around target. */
|
|
104
109
|
#wg-pilot-fx-focus{position:fixed;z-index:2147483644;pointer-events:none;
|
|
105
110
|
border-radius:12px;box-shadow:0 0 0 9999px rgba(8,4,20,.62);
|
|
@@ -318,6 +323,7 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
318
323
|
const todoIndex = fixtures.todoIndex ?? 0;
|
|
319
324
|
const todoTitle = fixtures.todoTitle ?? "Plan";
|
|
320
325
|
const todoPos = fixtures.todoPos === "left" ? "left" : "right";
|
|
326
|
+
const todoUi = resolveTodoDockUi(fixtures.todoUi ?? null);
|
|
321
327
|
// Effective zoom: phase zoom, else last ring that authored zoom.
|
|
322
328
|
let zoom = fixtures.clear === true
|
|
323
329
|
? 1
|
|
@@ -368,7 +374,7 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
368
374
|
? fixtures.device
|
|
369
375
|
: fixtures.device.preset || "device";
|
|
370
376
|
return page
|
|
371
|
-
.evaluate(({ rings, todos, todoIndex, todoTitle, todoPos, holdMs, zoom, zoomSelector, zoomOut, clearAll, deviceLabel, }) => {
|
|
377
|
+
.evaluate(({ rings, todos, todoIndex, todoTitle, todoPos, holdMs, zoom, zoomSelector, zoomOut, clearAll, deviceLabel, todoUi, }) => {
|
|
372
378
|
const w = window;
|
|
373
379
|
if (w.__wgPilotFxHideTimer)
|
|
374
380
|
clearTimeout(w.__wgPilotFxHideTimer);
|
|
@@ -511,6 +517,17 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
511
517
|
title.className = "wg-pilot-fx-todo-title";
|
|
512
518
|
title.textContent = todoTitle;
|
|
513
519
|
dock.appendChild(title);
|
|
520
|
+
const WINDOW = todoUi.cap > 0 ? todoUi.cap : 5;
|
|
521
|
+
const compact = todoUi.compact !== false && todos.length > WINDOW;
|
|
522
|
+
if (compact)
|
|
523
|
+
dock.dataset.compact = "1";
|
|
524
|
+
let start = 0;
|
|
525
|
+
let end = todos.length;
|
|
526
|
+
if (compact) {
|
|
527
|
+
start = Math.max(0, todoIndex - Math.floor((WINDOW - 1) / 2));
|
|
528
|
+
end = Math.min(todos.length, start + WINDOW);
|
|
529
|
+
start = Math.max(0, end - WINDOW);
|
|
530
|
+
}
|
|
514
531
|
const ol = document.createElement("ol");
|
|
515
532
|
todos.forEach((text, i) => {
|
|
516
533
|
const li = document.createElement("li");
|
|
@@ -519,13 +536,38 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
519
536
|
li.className = "wg-pilot-fx-todo-done";
|
|
520
537
|
else if (i === todoIndex)
|
|
521
538
|
li.className = "wg-pilot-fx-todo-now";
|
|
539
|
+
if (compact && (i < start || i >= end))
|
|
540
|
+
li.classList.add("wg-todo-fold");
|
|
522
541
|
ol.appendChild(li);
|
|
523
542
|
});
|
|
524
543
|
dock.appendChild(ol);
|
|
544
|
+
if (compact && todos.length - (end - start) > 0) {
|
|
545
|
+
const more = document.createElement("div");
|
|
546
|
+
more.className = "wg-todo-more";
|
|
547
|
+
more.textContent = "+" + (todos.length - (end - start)) + " more";
|
|
548
|
+
dock.appendChild(more);
|
|
549
|
+
}
|
|
525
550
|
document.documentElement.appendChild(dock);
|
|
526
551
|
}
|
|
527
552
|
if (focusBox)
|
|
528
553
|
applyFocus(focusBox);
|
|
554
|
+
// Dim todos under rings / focus so captions stay readable.
|
|
555
|
+
const dockEl = document.getElementById("wg-pilot-fx-todos");
|
|
556
|
+
if (dockEl && todoUi.behindRing !== false && (painted > 0 || focusBox)) {
|
|
557
|
+
dockEl.classList.add("wg-todo-behind");
|
|
558
|
+
}
|
|
559
|
+
// Collision flip: if a painted ring intersects the dock, flip side once.
|
|
560
|
+
if (dockEl && todoUi.collision !== false && painted > 0) {
|
|
561
|
+
const firstRing = document.querySelector(".wg-pilot-fx-ring");
|
|
562
|
+
if (firstRing) {
|
|
563
|
+
const rr = firstRing.getBoundingClientRect();
|
|
564
|
+
const dr = dockEl.getBoundingClientRect();
|
|
565
|
+
const hit = !(rr.right < dr.left || rr.left > dr.right || rr.bottom < dr.top || rr.top > dr.bottom);
|
|
566
|
+
if (hit) {
|
|
567
|
+
dockEl.dataset.pos = dockEl.dataset.pos === "right" ? "left" : "right";
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
529
571
|
if (zoom > 1.001 && zoomSelector) {
|
|
530
572
|
const target = document.querySelector(zoomSelector);
|
|
531
573
|
if (target) {
|
|
@@ -567,6 +609,7 @@ export async function showPilotFixtures(page, fixtures) {
|
|
|
567
609
|
zoomOut,
|
|
568
610
|
clearAll: fixtures.clear === true,
|
|
569
611
|
deviceLabel,
|
|
612
|
+
todoUi,
|
|
570
613
|
})
|
|
571
614
|
.catch(() => ({ painted: 0, missing: rings.map((r) => r.selector) }));
|
|
572
615
|
}
|
package/package.json
CHANGED
|
@@ -39,6 +39,7 @@ waygraph auto highlight <sessionId> '{"clear":true}'
|
|
|
39
39
|
Supported fields (demo parity):
|
|
40
40
|
- `rings[]`: `selector`, `label`, `detail`, `tag`, `tone`, `size`, `weight`, `color`, `zoom`, `zoomOut`, `focus`
|
|
41
41
|
- `todos` / `todoIndex` / `todoTitle` / `todoPos` (`right` default | `left`)
|
|
42
|
+
- `todoUi`: `{ compact?, cap?, expandCap?, collision?, behindRing? }` — same dock UX as demo (defaults smart-on; see README "Todo dock UI")
|
|
42
43
|
- `zoom` / `zoomSelector` / `zoomOut` - scroll + zoom badge (not CSS page scale)
|
|
43
44
|
- `device`: `mobile` | `tablet` | `desktop` (Playwright viewport)
|
|
44
45
|
- `holdMs`: ms to keep fixtures; `0` = until the next highlight / clear (default 30000)
|