waygraph 0.12.44 → 0.12.45
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 +173 -36
- package/dist/highlights.d.ts +134 -4
- package/dist/highlights.js +264 -27
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +6 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +12 -2
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +18 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -226,6 +226,10 @@ import {
|
|
|
226
226
|
formatDemoPaceBadge,
|
|
227
227
|
formatDemoPaceLabel,
|
|
228
228
|
normalizeTodos,
|
|
229
|
+
buildTodoDock,
|
|
230
|
+
applyTodoPhase,
|
|
231
|
+
advanceTodoDock,
|
|
232
|
+
completeSequentialTodoDock,
|
|
229
233
|
} from "waygraph";
|
|
230
234
|
|
|
231
235
|
function walkDir(dir, pattern) {
|
|
@@ -1082,20 +1086,53 @@ async function installOverlay(page, title) {
|
|
|
1082
1086
|
// Floating todo dock - lives outside #wg-panel so --mini / Hide never
|
|
1083
1087
|
// hide the checklist. Click cycles left <-> right with CSS transform.
|
|
1084
1088
|
// Authors set side via ctx.todoPos("left"|"right"); env / --todo-* fallback.
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1089
|
+
// Payload:
|
|
1090
|
+
// { sync: "clear" } -> remove dock
|
|
1091
|
+
// { sync: "keep" } -> leave dock alone (no wipe)
|
|
1092
|
+
// { sync: "set", dock } / { sync:"set", list } -> render / recreate
|
|
1093
|
+
// Compat: bare array = set list (empty array = clear - legacy only).
|
|
1094
|
+
window.__wgSyncTodos = (payload, pos) => {
|
|
1095
|
+
let sync = "set";
|
|
1096
|
+
let list = [];
|
|
1097
|
+
let dockState = null;
|
|
1098
|
+
let wantPos =
|
|
1099
|
+
pos === "left" || pos === "right" ? pos : null;
|
|
1100
|
+
if (Array.isArray(payload)) {
|
|
1101
|
+
list = payload;
|
|
1102
|
+
sync = list.length ? "set" : "clear";
|
|
1103
|
+
} else if (payload && typeof payload === "object") {
|
|
1104
|
+
sync = payload.sync === "clear" || payload.sync === "keep" ? payload.sync : "set";
|
|
1105
|
+
dockState = payload.dock || null;
|
|
1106
|
+
if (payload.pos === "left" || payload.pos === "right") wantPos = payload.pos;
|
|
1107
|
+
if (Array.isArray(payload.list)) list = payload.list;
|
|
1108
|
+
else if (dockState && Array.isArray(dockState.groups)) {
|
|
1109
|
+
// Flatten for legacy callers; full dock rendered below when present.
|
|
1110
|
+
list = [];
|
|
1111
|
+
for (const g of dockState.groups) {
|
|
1112
|
+
if (g && Array.isArray(g.items)) list = list.concat(g.items);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
} else {
|
|
1116
|
+
list = [];
|
|
1117
|
+
sync = "clear";
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1088
1120
|
let dock = document.getElementById("wg-todo-dock");
|
|
1089
1121
|
// Drop any in-panel leftover (old builds put #wg-todos in .wg-body).
|
|
1090
1122
|
document.querySelectorAll("#wg-panel #wg-todos").forEach((el) => el.remove());
|
|
1091
|
-
|
|
1123
|
+
|
|
1124
|
+
// keep + no new payload: do not remove an existing dock (PIA #10).
|
|
1125
|
+
if (sync === "keep") {
|
|
1126
|
+
if (!dockState && !list.length) return;
|
|
1127
|
+
// After navigation the DOM may be gone - recreate from carried dock.
|
|
1128
|
+
sync = "set";
|
|
1129
|
+
}
|
|
1130
|
+
if (sync === "clear" || (sync === "set" && !dockState && !list.length)) {
|
|
1092
1131
|
if (dock) dock.remove();
|
|
1093
1132
|
return;
|
|
1094
1133
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
? pos
|
|
1098
|
-
: null;
|
|
1134
|
+
|
|
1135
|
+
const POS = ["left", "right"];
|
|
1099
1136
|
if (!dock) {
|
|
1100
1137
|
dock = document.createElement("div");
|
|
1101
1138
|
dock.id = "wg-todo-dock";
|
|
@@ -1133,33 +1170,84 @@ async function installOverlay(page, title) {
|
|
|
1133
1170
|
/* ignore */
|
|
1134
1171
|
}
|
|
1135
1172
|
}
|
|
1173
|
+
|
|
1136
1174
|
const esc = (s) =>
|
|
1137
1175
|
String(s)
|
|
1138
1176
|
.replace(/&/g, "&")
|
|
1139
1177
|
.replace(/</g, "<")
|
|
1140
|
-
.replace(/>/g, ">")
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1178
|
+
.replace(/>/g, ">")
|
|
1179
|
+
.replace(/"/g, """);
|
|
1180
|
+
const attrId = (raw) => {
|
|
1181
|
+
const id = raw != null && String(raw).trim() ? String(raw).trim() : "";
|
|
1182
|
+
return id ? ' data-wg-todo-item="' + esc(id) + '"' : "";
|
|
1183
|
+
};
|
|
1184
|
+
const renderItems = (items, style) => {
|
|
1185
|
+
const st = style === "checklist" ? "checklist" : "sequential";
|
|
1186
|
+
return (
|
|
1187
|
+
'<ul id="wg-todos" data-wg-todo-style="' +
|
|
1188
|
+
st +
|
|
1189
|
+
'">' +
|
|
1190
|
+
(items || [])
|
|
1191
|
+
.map((t) => {
|
|
1192
|
+
const cls = t.current
|
|
1193
|
+
? "wg-todo-current"
|
|
1194
|
+
: t.done
|
|
1195
|
+
? "wg-todo-done"
|
|
1196
|
+
: "wg-todo-pending";
|
|
1197
|
+
let mark;
|
|
1198
|
+
if (st === "checklist") {
|
|
1199
|
+
mark = t.done ? "\u2611" : "\u2610";
|
|
1200
|
+
} else {
|
|
1201
|
+
mark = t.done ? "\u2713" : t.current ? "\u2192" : "\u25CB";
|
|
1202
|
+
}
|
|
1203
|
+
const rowId = t.id || t.name || "";
|
|
1204
|
+
return (
|
|
1205
|
+
"<li class=\"" +
|
|
1206
|
+
cls +
|
|
1207
|
+
"\"" +
|
|
1208
|
+
attrId(rowId) +
|
|
1209
|
+
'><span class="wg-todo-mark">' +
|
|
1210
|
+
mark +
|
|
1211
|
+
"</span><span>" +
|
|
1212
|
+
esc(t.text || "") +
|
|
1213
|
+
"</span></li>"
|
|
1214
|
+
);
|
|
1215
|
+
})
|
|
1216
|
+
.join("") +
|
|
1217
|
+
"</ul>"
|
|
1218
|
+
);
|
|
1219
|
+
};
|
|
1220
|
+
|
|
1221
|
+
const dockId =
|
|
1222
|
+
(dockState && dockState.id) ||
|
|
1223
|
+
(payload && !Array.isArray(payload) && payload.todoId) ||
|
|
1224
|
+
"";
|
|
1225
|
+
if (dockId) dock.setAttribute("data-wg-todo-id", String(dockId));
|
|
1226
|
+
else dock.removeAttribute("data-wg-todo-id");
|
|
1227
|
+
|
|
1228
|
+
let html = "";
|
|
1229
|
+
if (dockState && Array.isArray(dockState.groups) && dockState.groups.length) {
|
|
1230
|
+
const dockTitle = dockState.title && String(dockState.title).trim();
|
|
1231
|
+
if (dockTitle) {
|
|
1232
|
+
html += '<div class="wg-todo-dock-title">' + esc(dockTitle) + "</div>";
|
|
1233
|
+
}
|
|
1234
|
+
for (const g of dockState.groups) {
|
|
1235
|
+
const gTitle = g.title && String(g.title).trim();
|
|
1236
|
+
const gid = g.id || g.name || "";
|
|
1237
|
+
html +=
|
|
1238
|
+
'<div class="wg-todo-group"' +
|
|
1239
|
+
(gid ? ' data-wg-todo-group="' + esc(gid) + '"' : "") +
|
|
1240
|
+
">";
|
|
1241
|
+
if (gTitle) {
|
|
1242
|
+
html += '<div class="wg-todo-group-title">' + esc(gTitle) + "</div>";
|
|
1243
|
+
}
|
|
1244
|
+
html += renderItems(g.items, g.style || dockState.style);
|
|
1245
|
+
html += "</div>";
|
|
1246
|
+
}
|
|
1247
|
+
} else {
|
|
1248
|
+
html = renderItems(list, "sequential");
|
|
1249
|
+
}
|
|
1250
|
+
dock.innerHTML = html;
|
|
1163
1251
|
if (window.__wgStampModal) {
|
|
1164
1252
|
window.__wgStampModal(dock, "todos", { ready: true });
|
|
1165
1253
|
}
|
|
@@ -1302,7 +1390,16 @@ async function renderBeforeStep(page, info) {
|
|
|
1302
1390
|
forceCollapsed: info.forceCollapsed === true ? true : undefined,
|
|
1303
1391
|
});
|
|
1304
1392
|
}
|
|
1305
|
-
if (window.__wgSyncTodos)
|
|
1393
|
+
if (window.__wgSyncTodos) {
|
|
1394
|
+
const sync = info.todoSync || (info.todos && info.todos.length ? "set" : "keep");
|
|
1395
|
+
window.__wgSyncTodos({
|
|
1396
|
+
sync: sync,
|
|
1397
|
+
dock: info.todoDock || null,
|
|
1398
|
+
list: info.todos || [],
|
|
1399
|
+
pos: info.todoPos || null,
|
|
1400
|
+
todoId: info.todoId || (info.todoDock && info.todoDock.id) || null,
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1306
1403
|
if (window.__wgStampModal) {
|
|
1307
1404
|
window.__wgStampModal(panel, "panel", {
|
|
1308
1405
|
phase: "before",
|
|
@@ -1840,7 +1937,16 @@ async function renderAfterStep(page, info) {
|
|
|
1840
1937
|
forceCollapsed: info.forceCollapsed === true ? true : undefined,
|
|
1841
1938
|
});
|
|
1842
1939
|
}
|
|
1843
|
-
if (window.__wgSyncTodos)
|
|
1940
|
+
if (window.__wgSyncTodos) {
|
|
1941
|
+
const sync = info.todoSync || (info.todos && info.todos.length ? "set" : "keep");
|
|
1942
|
+
window.__wgSyncTodos({
|
|
1943
|
+
sync: sync,
|
|
1944
|
+
dock: info.todoDock || null,
|
|
1945
|
+
list: info.todos || [],
|
|
1946
|
+
pos: info.todoPos || null,
|
|
1947
|
+
todoId: info.todoId || (info.todoDock && info.todoDock.id) || null,
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1844
1950
|
if (window.__wgStampModal) {
|
|
1845
1951
|
window.__wgStampModal(panel, "panel", {
|
|
1846
1952
|
phase: "after",
|
|
@@ -2908,6 +3014,8 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
2908
3014
|
}
|
|
2909
3015
|
|
|
2910
3016
|
let result;
|
|
3017
|
+
/** Carry floating todo dock across steps (empty stubBefore must not wipe). */
|
|
3018
|
+
let lastTodoDock = undefined;
|
|
2911
3019
|
for (let i = 0; i < resolved.length; i++) {
|
|
2912
3020
|
const r = resolved[i];
|
|
2913
3021
|
// The block breadcrumb is scoped to THIS step's own episode, not the
|
|
@@ -2993,13 +3101,24 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
2993
3101
|
stubBeforeRef.current = stubBeforePhase.highlights.map((h) =>
|
|
2994
3102
|
applyHighlightStyleDefaults(h, flowStyle),
|
|
2995
3103
|
);
|
|
2996
|
-
const
|
|
3104
|
+
const appliedBefore = applyTodoPhase(lastTodoDock, {
|
|
3105
|
+
todoSync: stubBeforePhase.todoSync,
|
|
3106
|
+
todoDock: stubBeforePhase.todoDock,
|
|
3107
|
+
todos: stubBeforePhase.todos,
|
|
3108
|
+
todoPos: stubBeforePhase.todoPos,
|
|
3109
|
+
});
|
|
3110
|
+
lastTodoDock = appliedBefore.dock;
|
|
3111
|
+
const stubBeforeTodos =
|
|
3112
|
+
(lastTodoDock && lastTodoDock.groups[0] && lastTodoDock.groups[0].items) ||
|
|
3113
|
+
stubBeforePhase.todos ||
|
|
3114
|
+
[];
|
|
2997
3115
|
const overlayTitle =
|
|
2998
3116
|
(stubBeforePhase.title && String(stubBeforePhase.title).trim()) ||
|
|
2999
3117
|
(r.episodeTitle && String(r.episodeTitle).trim()) ||
|
|
3000
3118
|
title ||
|
|
3001
3119
|
"waygraph demo";
|
|
3002
|
-
const overlayTodoPos =
|
|
3120
|
+
const overlayTodoPos =
|
|
3121
|
+
(lastTodoDock && lastTodoDock.pos) || stubBeforePhase.todoPos || undefined;
|
|
3003
3122
|
const isNavBlock = r.block.__waygraphKind === "nav";
|
|
3004
3123
|
const autoNow = await currentAutoplay();
|
|
3005
3124
|
// Video / --mini: compact pill. Never pass false - Hide/localStorage wins.
|
|
@@ -3051,6 +3170,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3051
3170
|
stepperMode,
|
|
3052
3171
|
forceCollapsed,
|
|
3053
3172
|
todos: stubBeforeTodos,
|
|
3173
|
+
todoDock: lastTodoDock,
|
|
3174
|
+
todoSync: appliedBefore.sync,
|
|
3175
|
+
todoId: lastTodoDock && lastTodoDock.id,
|
|
3054
3176
|
...paceSpeak,
|
|
3055
3177
|
});
|
|
3056
3178
|
const edits = await gate();
|
|
@@ -3206,16 +3328,28 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3206
3328
|
let stubAfterTodos = [];
|
|
3207
3329
|
let afterOverlayTitle = overlayTitle;
|
|
3208
3330
|
let afterTodoPos = overlayTodoPos;
|
|
3331
|
+
let appliedAfter = { dock: lastTodoDock, sync: "keep" };
|
|
3209
3332
|
if (hasAuthoredStubAfter(r.block, result, fixturesAfter)) {
|
|
3210
3333
|
const afterPhase = await runStubPhase(r.block, "stubAfter", {
|
|
3211
3334
|
out: result,
|
|
3212
3335
|
fixtures: fixturesAfter,
|
|
3213
3336
|
});
|
|
3214
|
-
|
|
3337
|
+
appliedAfter = applyTodoPhase(lastTodoDock, {
|
|
3338
|
+
todoSync: afterPhase.todoSync,
|
|
3339
|
+
todoDock: afterPhase.todoDock,
|
|
3340
|
+
todos: afterPhase.todos,
|
|
3341
|
+
todoPos: afterPhase.todoPos,
|
|
3342
|
+
});
|
|
3343
|
+
lastTodoDock = appliedAfter.dock;
|
|
3344
|
+
stubAfterTodos =
|
|
3345
|
+
(lastTodoDock && lastTodoDock.groups[0] && lastTodoDock.groups[0].items) ||
|
|
3346
|
+
afterPhase.todos ||
|
|
3347
|
+
[];
|
|
3215
3348
|
if (afterPhase.title && String(afterPhase.title).trim()) {
|
|
3216
3349
|
afterOverlayTitle = String(afterPhase.title).trim();
|
|
3217
3350
|
}
|
|
3218
3351
|
if (afterPhase.todoPos) afterTodoPos = afterPhase.todoPos;
|
|
3352
|
+
if (lastTodoDock && lastTodoDock.pos) afterTodoPos = lastTodoDock.pos;
|
|
3219
3353
|
highlights = afterPhase.highlights.map((h) => {
|
|
3220
3354
|
const styled = applyHighlightStyleDefaults(h, r.highlightStyle);
|
|
3221
3355
|
return {
|
|
@@ -3255,6 +3389,9 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
|
|
|
3255
3389
|
episodeTitle: r.episodeTitle,
|
|
3256
3390
|
stepperMode,
|
|
3257
3391
|
todos: stubAfterTodos,
|
|
3392
|
+
todoDock: lastTodoDock,
|
|
3393
|
+
todoSync: appliedAfter.sync,
|
|
3394
|
+
todoId: lastTodoDock && lastTodoDock.id,
|
|
3258
3395
|
forceCollapsed:
|
|
3259
3396
|
!!process.env.WAYGRAPH_VIDEO ||
|
|
3260
3397
|
process.env.WAYGRAPH_MINI === "1" ||
|
package/dist/highlights.d.ts
CHANGED
|
@@ -34,17 +34,75 @@ export type FixtureDurationFields = {
|
|
|
34
34
|
*/
|
|
35
35
|
export type WaygraphTodoItem = {
|
|
36
36
|
text: string;
|
|
37
|
+
/**
|
|
38
|
+
* Stable id for external asserts / redirects (Mailhog, deep links).
|
|
39
|
+
* Stamped as `data-wg-todo-item` on the row.
|
|
40
|
+
*/
|
|
41
|
+
id?: string;
|
|
42
|
+
/** Alias for {@link WaygraphTodoItem.id}. */
|
|
43
|
+
name?: string;
|
|
37
44
|
/** Explicit done. If omitted, derived from todoIndex (index < todoIndex). */
|
|
38
45
|
done?: boolean;
|
|
39
46
|
/** Explicit current. If omitted, derived from todoIndex (index === todoIndex). */
|
|
40
47
|
current?: boolean;
|
|
41
48
|
};
|
|
42
49
|
export type WaygraphTodoInput = string | WaygraphTodoItem;
|
|
50
|
+
/**
|
|
51
|
+
* Dock list presentation:
|
|
52
|
+
* - `sequential` - arrow on current, strike done (demo walkthrough)
|
|
53
|
+
* - `checklist` - checkbox done/pending only (no current arrow; independent checks)
|
|
54
|
+
*/
|
|
55
|
+
export type TodoListStyle = "sequential" | "checklist";
|
|
56
|
+
/** One titled group in the floating todo dock (FR / Scenarios / ACs, etc.). */
|
|
57
|
+
export type WaygraphTodoGroupInput = {
|
|
58
|
+
/** Stable group id -> `data-wg-todo-group`. */
|
|
59
|
+
id?: string;
|
|
60
|
+
/** Alias for {@link WaygraphTodoGroupInput.id}. */
|
|
61
|
+
name?: string;
|
|
62
|
+
/** Group heading shown above the items. */
|
|
63
|
+
title?: string;
|
|
64
|
+
/** Per-group style; falls back to dock default ({@link StubCtx.todoStyle}). */
|
|
65
|
+
style?: TodoListStyle;
|
|
66
|
+
items: readonly WaygraphTodoInput[];
|
|
67
|
+
/** Sequential only: which item is current (0-based). */
|
|
68
|
+
todoIndex?: number;
|
|
69
|
+
};
|
|
70
|
+
export type WaygraphTodoGroup = {
|
|
71
|
+
id?: string;
|
|
72
|
+
title?: string;
|
|
73
|
+
style: TodoListStyle;
|
|
74
|
+
items: WaygraphTodoItem[];
|
|
75
|
+
};
|
|
76
|
+
/** Full floating dock payload passed to the browser. */
|
|
77
|
+
export type TodoDockState = {
|
|
78
|
+
/**
|
|
79
|
+
* Stable dock id for external targeting after redirects
|
|
80
|
+
* (`#wg-todo-dock[data-wg-todo-id="…"]`).
|
|
81
|
+
*/
|
|
82
|
+
id?: string;
|
|
83
|
+
/** Default style for groups that omit their own. */
|
|
84
|
+
style: TodoListStyle;
|
|
85
|
+
/** Single-list title when using {@link StubCtx.todos} + {@link StubCtx.todoTitle}. */
|
|
86
|
+
title?: string;
|
|
87
|
+
groups: WaygraphTodoGroup[];
|
|
88
|
+
pos?: "left" | "right";
|
|
89
|
+
};
|
|
43
90
|
/** Episode-level fixtures authored inside stubBefore/After/OnError(ctx). */
|
|
44
91
|
export type StubPhaseFixtures = {
|
|
45
92
|
todos?: readonly WaygraphTodoInput[];
|
|
46
93
|
/** Which todo is current (0-based). Rows before = done, after = pending. */
|
|
47
94
|
todoIndex?: number;
|
|
95
|
+
/** Heading above a single {@link StubCtx.todos} list. */
|
|
96
|
+
todoTitle?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Stable dock id (`data-wg-todo-id`) for external redirect asserts
|
|
99
|
+
* (e.g. Mailhog -> back to app and still find the checklist).
|
|
100
|
+
*/
|
|
101
|
+
todoId?: string;
|
|
102
|
+
/** `sequential` (default) or `checklist`. */
|
|
103
|
+
todoStyle?: TodoListStyle;
|
|
104
|
+
/** Multiple titled lists (FR / Scenarios / ACs). Wins over bare todos when set. */
|
|
105
|
+
todoGroups?: readonly WaygraphTodoGroupInput[];
|
|
48
106
|
/**
|
|
49
107
|
* Camera zoom (Screen Studio style) - scales the page toward the target /
|
|
50
108
|
* cursor. Not element CSS zoom. Typical `1.25` .. `2`.
|
|
@@ -152,10 +210,34 @@ export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
|
|
|
152
210
|
highlights(slots: HighlightStubPhase): void;
|
|
153
211
|
/** Set / overwrite one named ring. */
|
|
154
212
|
ring(id: string, stub: WaygraphHighlightStub): void;
|
|
155
|
-
/** Episode checklist (demo panel). */
|
|
213
|
+
/** Episode checklist (demo panel). Single list; use {@link StubCtx.todoGroups} for FR/AC packs. */
|
|
156
214
|
todos(items: readonly WaygraphTodoInput[]): void;
|
|
157
215
|
/** Current checklist index (bump for sequential plans). */
|
|
158
216
|
todoIndex(n: number): void;
|
|
217
|
+
/** Heading above the single {@link StubCtx.todos} list. */
|
|
218
|
+
todoTitle(text: string): void;
|
|
219
|
+
/**
|
|
220
|
+
* Stable dock id for external selectors after redirects
|
|
221
|
+
* (`#wg-todo-dock[data-wg-todo-id="ep10-coverage"]`).
|
|
222
|
+
*/
|
|
223
|
+
todoId(id: string): void;
|
|
224
|
+
/**
|
|
225
|
+
* Dock presentation: `sequential` (arrow walkthrough) or `checklist`
|
|
226
|
+
* (independent checkboxes, no current arrow).
|
|
227
|
+
*/
|
|
228
|
+
todoStyle(style: TodoListStyle): void;
|
|
229
|
+
/**
|
|
230
|
+
* Multiple titled lists (e.g. Functional Requirements / Scenarios / ACs).
|
|
231
|
+
* Replaces a bare {@link StubCtx.todos} list when set.
|
|
232
|
+
*/
|
|
233
|
+
todoGroups(groups: readonly WaygraphTodoGroupInput[]): void;
|
|
234
|
+
/**
|
|
235
|
+
* Hide the floating todo dock. Omit todos on a later block = **keep** the
|
|
236
|
+
* previous dock (carry forward). Only hideTodos / clearTodos / todos([]) clears it.
|
|
237
|
+
*/
|
|
238
|
+
hideTodos(): void;
|
|
239
|
+
/** Alias for {@link StubCtx.hideTodos}. */
|
|
240
|
+
clearTodos(): void;
|
|
159
241
|
/** Default camera zoom for rings without their own zoom. */
|
|
160
242
|
zoom(n: number): void;
|
|
161
243
|
/**
|
|
@@ -187,8 +269,17 @@ export type HighlightStubPhaseOrFn<Out extends Checkpoint<string>> = HighlightSt
|
|
|
187
269
|
/** Result of running a stub phase (rings + episode fixtures). */
|
|
188
270
|
export type StubPhaseResult = {
|
|
189
271
|
highlights: ResolvedHighlight[];
|
|
272
|
+
/** Flattened first-group items (compat). Prefer {@link todoDock}. */
|
|
190
273
|
todos: WaygraphTodoItem[];
|
|
191
274
|
todoIndex?: number;
|
|
275
|
+
todoDock?: TodoDockState;
|
|
276
|
+
/**
|
|
277
|
+
* Todo dock intent for this phase:
|
|
278
|
+
* - `set` - replace dock with {@link todoDock}
|
|
279
|
+
* - `clear` - hide dock (author called hideTodos / empty todos())
|
|
280
|
+
* - `keep` - author omitted todos; demo must leave previous dock alone
|
|
281
|
+
*/
|
|
282
|
+
todoSync?: "set" | "clear" | "keep";
|
|
192
283
|
zoom?: number;
|
|
193
284
|
zoomOut?: boolean;
|
|
194
285
|
title?: string;
|
|
@@ -329,12 +420,51 @@ export interface WaygraphHighlight {
|
|
|
329
420
|
tone?: HighlightTone;
|
|
330
421
|
}
|
|
331
422
|
export type ResolvedHighlight = WaygraphHighlightStub;
|
|
423
|
+
/** Normalize checklist dock side. */
|
|
424
|
+
export declare function normalizeTodoPos(raw: unknown): "left" | "right" | undefined;
|
|
425
|
+
/** Normalize dock list style. */
|
|
426
|
+
export declare function normalizeTodoStyle(raw: unknown): TodoListStyle | undefined;
|
|
332
427
|
/**
|
|
333
428
|
* Normalize checklist rows; apply todoIndex for sequential done/current.
|
|
429
|
+
* In checklist mode, todoIndex is ignored unless items omit explicit done.
|
|
334
430
|
*/
|
|
335
|
-
export declare function normalizeTodos(todos: readonly WaygraphTodoInput[] | undefined, todoIndex?: number): WaygraphTodoItem[];
|
|
336
|
-
/**
|
|
337
|
-
export declare function
|
|
431
|
+
export declare function normalizeTodos(todos: readonly WaygraphTodoInput[] | undefined, todoIndex?: number, style?: TodoListStyle): WaygraphTodoItem[];
|
|
432
|
+
/** Build the floating dock from bag / phase fields. */
|
|
433
|
+
export declare function buildTodoDock(opts: {
|
|
434
|
+
todos?: readonly WaygraphTodoInput[] | undefined;
|
|
435
|
+
todoIndex?: number | undefined;
|
|
436
|
+
todoTitle?: string | undefined;
|
|
437
|
+
todoId?: string | undefined;
|
|
438
|
+
todoStyle?: TodoListStyle | undefined;
|
|
439
|
+
todoGroups?: readonly WaygraphTodoGroupInput[] | undefined;
|
|
440
|
+
todoPos?: "left" | "right" | undefined;
|
|
441
|
+
}): TodoDockState | undefined;
|
|
442
|
+
/**
|
|
443
|
+
* Carry-forward rules for the floating todo dock across demo steps.
|
|
444
|
+
* - clear: author called hideTodos / empty todos()
|
|
445
|
+
* - set: author authored a new dock this phase
|
|
446
|
+
* - keep: author omitted todos — preserve previous dock (do not wipe)
|
|
447
|
+
*/
|
|
448
|
+
export declare function applyTodoPhase(prev: TodoDockState | undefined, phase: {
|
|
449
|
+
todoSync?: "set" | "clear" | "keep";
|
|
450
|
+
todoDock?: TodoDockState;
|
|
451
|
+
todos?: WaygraphTodoItem[];
|
|
452
|
+
todoPos?: "left" | "right";
|
|
453
|
+
todoId?: string;
|
|
454
|
+
}): {
|
|
455
|
+
dock: TodoDockState | undefined;
|
|
456
|
+
sync: "set" | "clear" | "keep";
|
|
457
|
+
};
|
|
458
|
+
/**
|
|
459
|
+
* Advance every sequential group to `index` (capped). Checklist groups unchanged.
|
|
460
|
+
* Used while cycling rings / fill/click stubs so the walkthrough moves.
|
|
461
|
+
*/
|
|
462
|
+
export declare function advanceTodoDock(dock: TodoDockState | undefined, index: number): TodoDockState | undefined;
|
|
463
|
+
/**
|
|
464
|
+
* Mark sequential groups complete (all done, none current) - e.g. end of step.
|
|
465
|
+
* Checklist groups keep explicit done flags.
|
|
466
|
+
*/
|
|
467
|
+
export declare function completeSequentialTodoDock(dock: TodoDockState | undefined): TodoDockState | undefined;
|
|
338
468
|
/**
|
|
339
469
|
* Run an open stub lifecycle (preferred) or resolve a static slot map.
|
|
340
470
|
* Returns rings + episode fixtures (todos / todoIndex / zoom).
|
package/dist/highlights.js
CHANGED
|
@@ -293,31 +293,6 @@ function pickStyleFields(base, patch) {
|
|
|
293
293
|
out.weight = normalizeHighlightWeight(weight);
|
|
294
294
|
return out;
|
|
295
295
|
}
|
|
296
|
-
/**
|
|
297
|
-
* Normalize checklist rows; apply todoIndex for sequential done/current.
|
|
298
|
-
*/
|
|
299
|
-
export function normalizeTodos(todos, todoIndex) {
|
|
300
|
-
if (!todos || todos.length === 0)
|
|
301
|
-
return [];
|
|
302
|
-
const idx = todoIndex !== undefined && Number.isFinite(todoIndex) ? Math.floor(todoIndex) : undefined;
|
|
303
|
-
return todos.map((t, i) => {
|
|
304
|
-
const text = typeof t === "string" ? t.trim() : String(t?.text ?? "").trim();
|
|
305
|
-
const item = { text: text || `Item ${i + 1}` };
|
|
306
|
-
if (typeof t === "object" && t) {
|
|
307
|
-
if (t.done !== undefined)
|
|
308
|
-
item.done = !!t.done;
|
|
309
|
-
if (t.current !== undefined)
|
|
310
|
-
item.current = !!t.current;
|
|
311
|
-
}
|
|
312
|
-
if (idx !== undefined) {
|
|
313
|
-
if (item.done === undefined)
|
|
314
|
-
item.done = i < idx;
|
|
315
|
-
if (item.current === undefined)
|
|
316
|
-
item.current = i === idx;
|
|
317
|
-
}
|
|
318
|
-
return item;
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
296
|
function pickZoom(base, patch) {
|
|
322
297
|
const zoom = patch?.zoom !== undefined ? patch.zoom : base?.zoom;
|
|
323
298
|
if (zoom !== undefined && Number.isFinite(zoom) && zoom > 0)
|
|
@@ -394,6 +369,180 @@ export function normalizeTodoPos(raw) {
|
|
|
394
369
|
return "right";
|
|
395
370
|
return undefined;
|
|
396
371
|
}
|
|
372
|
+
/** Normalize dock list style. */
|
|
373
|
+
export function normalizeTodoStyle(raw) {
|
|
374
|
+
const s = String(raw ?? "")
|
|
375
|
+
.trim()
|
|
376
|
+
.toLowerCase();
|
|
377
|
+
if (s === "sequential" || s === "seq" || s === "steps" || s === "step")
|
|
378
|
+
return "sequential";
|
|
379
|
+
if (s === "checklist" || s === "check" || s === "checks" || s === "box")
|
|
380
|
+
return "checklist";
|
|
381
|
+
return undefined;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Normalize checklist rows; apply todoIndex for sequential done/current.
|
|
385
|
+
* In checklist mode, todoIndex is ignored unless items omit explicit done.
|
|
386
|
+
*/
|
|
387
|
+
export function normalizeTodos(todos, todoIndex, style = "sequential") {
|
|
388
|
+
if (!todos || todos.length === 0)
|
|
389
|
+
return [];
|
|
390
|
+
const idx = todoIndex !== undefined && Number.isFinite(todoIndex) ? Math.floor(todoIndex) : undefined;
|
|
391
|
+
return todos.map((t, i) => {
|
|
392
|
+
const text = typeof t === "string" ? t.trim() : String(t?.text ?? "").trim();
|
|
393
|
+
const item = { text: text || `Item ${i + 1}` };
|
|
394
|
+
if (typeof t === "object" && t) {
|
|
395
|
+
if (t.done !== undefined)
|
|
396
|
+
item.done = !!t.done;
|
|
397
|
+
if (t.current !== undefined)
|
|
398
|
+
item.current = !!t.current;
|
|
399
|
+
const idRaw = t.id ?? t.name;
|
|
400
|
+
if (idRaw != null && String(idRaw).trim())
|
|
401
|
+
item.id = String(idRaw).trim();
|
|
402
|
+
}
|
|
403
|
+
if (style === "checklist") {
|
|
404
|
+
if (item.done === undefined)
|
|
405
|
+
item.done = false;
|
|
406
|
+
item.current = false;
|
|
407
|
+
return item;
|
|
408
|
+
}
|
|
409
|
+
if (idx !== undefined) {
|
|
410
|
+
if (item.done === undefined)
|
|
411
|
+
item.done = i < idx;
|
|
412
|
+
if (item.current === undefined)
|
|
413
|
+
item.current = i === idx;
|
|
414
|
+
}
|
|
415
|
+
return item;
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/** Build the floating dock from bag / phase fields. */
|
|
419
|
+
export function buildTodoDock(opts) {
|
|
420
|
+
const style = normalizeTodoStyle(opts.todoStyle) || "sequential";
|
|
421
|
+
const pos = normalizeTodoPos(opts.todoPos);
|
|
422
|
+
const dockId = opts.todoId != null && String(opts.todoId).trim() ? String(opts.todoId).trim() : undefined;
|
|
423
|
+
if (opts.todoGroups && opts.todoGroups.length) {
|
|
424
|
+
const groups = opts.todoGroups
|
|
425
|
+
.map((g) => {
|
|
426
|
+
const gs = normalizeTodoStyle(g.style) || style;
|
|
427
|
+
const items = normalizeTodos(g.items, g.todoIndex, gs);
|
|
428
|
+
if (!items.length)
|
|
429
|
+
return null;
|
|
430
|
+
const group = { style: gs, items };
|
|
431
|
+
if (g.title && String(g.title).trim())
|
|
432
|
+
group.title = String(g.title).trim();
|
|
433
|
+
const gid = g.id ?? g.name;
|
|
434
|
+
if (gid != null && String(gid).trim())
|
|
435
|
+
group.id = String(gid).trim();
|
|
436
|
+
return group;
|
|
437
|
+
})
|
|
438
|
+
.filter((g) => !!g);
|
|
439
|
+
if (!groups.length)
|
|
440
|
+
return undefined;
|
|
441
|
+
const dock = { style, groups };
|
|
442
|
+
if (pos)
|
|
443
|
+
dock.pos = pos;
|
|
444
|
+
if (dockId)
|
|
445
|
+
dock.id = dockId;
|
|
446
|
+
return dock;
|
|
447
|
+
}
|
|
448
|
+
const items = normalizeTodos(opts.todos, opts.todoIndex, style);
|
|
449
|
+
if (!items.length)
|
|
450
|
+
return undefined;
|
|
451
|
+
const group = { style, items };
|
|
452
|
+
if (opts.todoTitle && String(opts.todoTitle).trim()) {
|
|
453
|
+
group.title = String(opts.todoTitle).trim();
|
|
454
|
+
}
|
|
455
|
+
const dock = { style, groups: [group] };
|
|
456
|
+
if (opts.todoTitle && String(opts.todoTitle).trim()) {
|
|
457
|
+
dock.title = String(opts.todoTitle).trim();
|
|
458
|
+
}
|
|
459
|
+
if (pos)
|
|
460
|
+
dock.pos = pos;
|
|
461
|
+
if (dockId)
|
|
462
|
+
dock.id = dockId;
|
|
463
|
+
return dock;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Carry-forward rules for the floating todo dock across demo steps.
|
|
467
|
+
* - clear: author called hideTodos / empty todos()
|
|
468
|
+
* - set: author authored a new dock this phase
|
|
469
|
+
* - keep: author omitted todos — preserve previous dock (do not wipe)
|
|
470
|
+
*/
|
|
471
|
+
export function applyTodoPhase(prev, phase) {
|
|
472
|
+
const sync = phase.todoSync ||
|
|
473
|
+
(phase.todoDock ? "set" : phase.todos && phase.todos.length ? "set" : "keep");
|
|
474
|
+
if (sync === "clear")
|
|
475
|
+
return { dock: undefined, sync: "clear" };
|
|
476
|
+
if (sync === "set") {
|
|
477
|
+
if (phase.todoDock)
|
|
478
|
+
return { dock: phase.todoDock, sync: "set" };
|
|
479
|
+
if (phase.todos && phase.todos.length) {
|
|
480
|
+
const dock = buildTodoDock({
|
|
481
|
+
todos: phase.todos,
|
|
482
|
+
...(phase.todoPos ? { todoPos: phase.todoPos } : {}),
|
|
483
|
+
...(phase.todoId ? { todoId: phase.todoId } : {}),
|
|
484
|
+
});
|
|
485
|
+
return { dock, sync: "set" };
|
|
486
|
+
}
|
|
487
|
+
return { dock: undefined, sync: "clear" };
|
|
488
|
+
}
|
|
489
|
+
return { dock: prev, sync: "keep" };
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Advance every sequential group to `index` (capped). Checklist groups unchanged.
|
|
493
|
+
* Used while cycling rings / fill/click stubs so the walkthrough moves.
|
|
494
|
+
*/
|
|
495
|
+
export function advanceTodoDock(dock, index) {
|
|
496
|
+
if (!dock || !dock.groups.length)
|
|
497
|
+
return dock;
|
|
498
|
+
const i = Number.isFinite(index) ? Math.max(0, Math.floor(index)) : 0;
|
|
499
|
+
return {
|
|
500
|
+
...dock,
|
|
501
|
+
groups: dock.groups.map((g) => {
|
|
502
|
+
if (g.style !== "sequential")
|
|
503
|
+
return g;
|
|
504
|
+
const capped = Math.min(i, Math.max(0, g.items.length - 1));
|
|
505
|
+
const raw = g.items.map((it) => {
|
|
506
|
+
const row = { text: it.text };
|
|
507
|
+
if (it.id)
|
|
508
|
+
row.id = it.id;
|
|
509
|
+
else if (it.name)
|
|
510
|
+
row.id = it.name;
|
|
511
|
+
return row;
|
|
512
|
+
});
|
|
513
|
+
return {
|
|
514
|
+
...g,
|
|
515
|
+
items: normalizeTodos(raw, capped, "sequential"),
|
|
516
|
+
};
|
|
517
|
+
}),
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Mark sequential groups complete (all done, none current) - e.g. end of step.
|
|
522
|
+
* Checklist groups keep explicit done flags.
|
|
523
|
+
*/
|
|
524
|
+
export function completeSequentialTodoDock(dock) {
|
|
525
|
+
if (!dock)
|
|
526
|
+
return dock;
|
|
527
|
+
return {
|
|
528
|
+
...dock,
|
|
529
|
+
groups: dock.groups.map((g) => {
|
|
530
|
+
if (g.style !== "sequential")
|
|
531
|
+
return g;
|
|
532
|
+
return {
|
|
533
|
+
...g,
|
|
534
|
+
items: g.items.map((it) => {
|
|
535
|
+
const row = { text: it.text, done: true, current: false };
|
|
536
|
+
if (it.id)
|
|
537
|
+
row.id = it.id;
|
|
538
|
+
else if (it.name)
|
|
539
|
+
row.id = it.name;
|
|
540
|
+
return row;
|
|
541
|
+
}),
|
|
542
|
+
};
|
|
543
|
+
}),
|
|
544
|
+
};
|
|
545
|
+
}
|
|
397
546
|
function createStubCtx(bag, opts) {
|
|
398
547
|
return {
|
|
399
548
|
out: opts.out,
|
|
@@ -405,11 +554,52 @@ function createStubCtx(bag, opts) {
|
|
|
405
554
|
bag.highlights = { ...bag.highlights, [id]: stub };
|
|
406
555
|
},
|
|
407
556
|
todos(items) {
|
|
557
|
+
bag.todoTouched = true;
|
|
558
|
+
bag.hideTodos = false;
|
|
408
559
|
bag.todos = items;
|
|
560
|
+
delete bag.todoGroups;
|
|
561
|
+
// Empty list = explicit clear (same as hideTodos).
|
|
562
|
+
if (!items || !items.length)
|
|
563
|
+
bag.hideTodos = true;
|
|
409
564
|
},
|
|
410
565
|
todoIndex(n) {
|
|
411
566
|
bag.todoIndex = n;
|
|
412
567
|
},
|
|
568
|
+
todoTitle(text) {
|
|
569
|
+
bag.todoTitle = String(text ?? "");
|
|
570
|
+
},
|
|
571
|
+
todoId(id) {
|
|
572
|
+
bag.todoTouched = true;
|
|
573
|
+
const cleaned = String(id ?? "").trim();
|
|
574
|
+
if (cleaned)
|
|
575
|
+
bag.todoId = cleaned;
|
|
576
|
+
else
|
|
577
|
+
delete bag.todoId;
|
|
578
|
+
},
|
|
579
|
+
todoStyle(style) {
|
|
580
|
+
const s = normalizeTodoStyle(style);
|
|
581
|
+
if (s)
|
|
582
|
+
bag.todoStyle = s;
|
|
583
|
+
},
|
|
584
|
+
todoGroups(groups) {
|
|
585
|
+
bag.todoTouched = true;
|
|
586
|
+
bag.hideTodos = false;
|
|
587
|
+
bag.todoGroups = groups;
|
|
588
|
+
if (!groups || !groups.length)
|
|
589
|
+
bag.hideTodos = true;
|
|
590
|
+
},
|
|
591
|
+
hideTodos() {
|
|
592
|
+
bag.todoTouched = true;
|
|
593
|
+
bag.hideTodos = true;
|
|
594
|
+
bag.todos = [];
|
|
595
|
+
delete bag.todoGroups;
|
|
596
|
+
},
|
|
597
|
+
clearTodos() {
|
|
598
|
+
bag.todoTouched = true;
|
|
599
|
+
bag.hideTodos = true;
|
|
600
|
+
bag.todos = [];
|
|
601
|
+
delete bag.todoGroups;
|
|
602
|
+
},
|
|
413
603
|
zoom(n) {
|
|
414
604
|
if (Number.isFinite(n) && n > 0)
|
|
415
605
|
bag.zoom = n;
|
|
@@ -431,10 +621,33 @@ function createStubCtx(bag, opts) {
|
|
|
431
621
|
set(partial) {
|
|
432
622
|
if (partial.highlights)
|
|
433
623
|
bag.highlights = { ...partial.highlights };
|
|
434
|
-
if (partial.todos !== undefined)
|
|
624
|
+
if (partial.todos !== undefined) {
|
|
625
|
+
bag.todoTouched = true;
|
|
626
|
+
bag.hideTodos = !partial.todos || !partial.todos.length;
|
|
435
627
|
bag.todos = partial.todos;
|
|
628
|
+
if (partial.todoGroups === undefined)
|
|
629
|
+
delete bag.todoGroups;
|
|
630
|
+
}
|
|
436
631
|
if (partial.todoIndex !== undefined)
|
|
437
632
|
bag.todoIndex = partial.todoIndex;
|
|
633
|
+
if (partial.todoTitle !== undefined)
|
|
634
|
+
bag.todoTitle = String(partial.todoTitle ?? "");
|
|
635
|
+
if (partial.todoId !== undefined) {
|
|
636
|
+
bag.todoTouched = true;
|
|
637
|
+
const cleaned = String(partial.todoId ?? "").trim();
|
|
638
|
+
if (cleaned)
|
|
639
|
+
bag.todoId = cleaned;
|
|
640
|
+
else
|
|
641
|
+
delete bag.todoId;
|
|
642
|
+
}
|
|
643
|
+
const ts = normalizeTodoStyle(partial.todoStyle);
|
|
644
|
+
if (ts)
|
|
645
|
+
bag.todoStyle = ts;
|
|
646
|
+
if (partial.todoGroups !== undefined) {
|
|
647
|
+
bag.todoTouched = true;
|
|
648
|
+
bag.hideTodos = !partial.todoGroups || !partial.todoGroups.length;
|
|
649
|
+
bag.todoGroups = partial.todoGroups;
|
|
650
|
+
}
|
|
438
651
|
if (partial.zoom !== undefined && Number.isFinite(partial.zoom) && partial.zoom > 0) {
|
|
439
652
|
bag.zoom = partial.zoom;
|
|
440
653
|
}
|
|
@@ -576,11 +789,35 @@ export async function runStubPhase(block, phase, opts) {
|
|
|
576
789
|
let highlights = mergePhaseMaps(bag.highlights, fixturePhase);
|
|
577
790
|
highlights = applyDefaultZoom(highlights, bag.zoom);
|
|
578
791
|
highlights = applyDefaultZoomOut(highlights, bag.zoomOut);
|
|
579
|
-
const
|
|
792
|
+
const todoDock = bag.hideTodos
|
|
793
|
+
? undefined
|
|
794
|
+
: buildTodoDock({
|
|
795
|
+
todos: bag.todos,
|
|
796
|
+
todoIndex: bag.todoIndex,
|
|
797
|
+
todoTitle: bag.todoTitle,
|
|
798
|
+
todoId: bag.todoId,
|
|
799
|
+
todoStyle: bag.todoStyle,
|
|
800
|
+
todoGroups: bag.todoGroups,
|
|
801
|
+
todoPos: bag.todoPos,
|
|
802
|
+
});
|
|
803
|
+
const todos = todoDock?.groups[0]?.items ||
|
|
804
|
+
(bag.hideTodos
|
|
805
|
+
? []
|
|
806
|
+
: normalizeTodos(bag.todos, bag.todoIndex, normalizeTodoStyle(bag.todoStyle) || "sequential"));
|
|
807
|
+
// Omit todos entirely => keep previous dock. Explicit empty / hideTodos => clear.
|
|
808
|
+
const todoSync = bag.hideTodos
|
|
809
|
+
? "clear"
|
|
810
|
+
: bag.todoTouched && todoDock
|
|
811
|
+
? "set"
|
|
812
|
+
: bag.todoTouched && !todoDock
|
|
813
|
+
? "clear"
|
|
814
|
+
: "keep";
|
|
580
815
|
return {
|
|
581
816
|
highlights,
|
|
582
817
|
todos,
|
|
818
|
+
todoSync,
|
|
583
819
|
...(bag.todoIndex !== undefined ? { todoIndex: bag.todoIndex } : {}),
|
|
820
|
+
...(todoDock ? { todoDock } : {}),
|
|
584
821
|
...(bag.zoom !== undefined ? { zoom: bag.zoom } : {}),
|
|
585
822
|
...(bag.zoomOut !== undefined ? { zoomOut: bag.zoomOut } : {}),
|
|
586
823
|
...(bag.title !== undefined && bag.title !== "" ? { title: bag.title } : {}),
|
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 } from "./trait.js";
|
|
5
5
|
export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, chainFlow, } 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, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
7
|
-
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, } 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, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
7
|
+
export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, WaygraphTodoGroup, WaygraphTodoGroupInput, TodoDockState, TodoListStyle, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, } from "./highlights.js";
|
|
8
8
|
export type { Flow, EngineConfig, ComposedBlock, FastForwardComposedBlock, RunGraphOptions, BlockInfo, NavBlock, NavBlockOptions, NavClickBlock, NavClickBlockOptions, MethodBlock, ActionBlock, PageBlock, PageBlockOptions, PageMethodEntry, EffectBlock, MemEffectBlock, MemNavBlock, } from "./engine.js";
|
|
9
9
|
export type { ActionPage } from "./types.js";
|
|
10
10
|
export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
package/dist/index.js
CHANGED
|
@@ -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 } from "./trait.js";
|
|
4
4
|
export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, chainFlow, } 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, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
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, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, } from "./highlights.js";
|
|
6
6
|
export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
|
|
7
7
|
export { isFileSelectToken, parseBlocksSelect, matchBlocksSelect, globToRegExp, } from "./blocks-select.js";
|
|
8
8
|
export { EdgeLeaseCoordinator } from "./traverse-lease.js";
|
|
@@ -17,7 +17,12 @@ export const NavCartBlock = defineNavClickBlock<CartPage>({
|
|
|
17
17
|
verify: [Trait.url({ pathname: "/cart.html" })],
|
|
18
18
|
stubBefore: (ctx) => {
|
|
19
19
|
ctx.title("Open cart");
|
|
20
|
-
ctx.
|
|
20
|
+
ctx.todoId("saucedemo-cart");
|
|
21
|
+
ctx.todos([
|
|
22
|
+
{ id: "cart-find", text: "Find the product" },
|
|
23
|
+
{ id: "cart-add", text: "Add to cart" },
|
|
24
|
+
{ id: "cart-open", text: "Open cart" },
|
|
25
|
+
]);
|
|
21
26
|
ctx.todoIndex(2);
|
|
22
27
|
ctx.zoom(1.4);
|
|
23
28
|
ctx.zoomOut(false);
|
package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts
CHANGED
|
@@ -36,7 +36,12 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
36
36
|
// Demo: zoom product -> add (stay zoomed), then badge after act.
|
|
37
37
|
stubBefore: (ctx) => {
|
|
38
38
|
ctx.title("Pick a product");
|
|
39
|
-
ctx.
|
|
39
|
+
ctx.todoId("saucedemo-cart");
|
|
40
|
+
ctx.todos([
|
|
41
|
+
{ id: "cart-find", text: "Find the product" },
|
|
42
|
+
{ id: "cart-add", text: "Add to cart" },
|
|
43
|
+
{ id: "cart-open", text: "Open cart" },
|
|
44
|
+
]);
|
|
40
45
|
ctx.todoIndex(0);
|
|
41
46
|
ctx.zoom(1.35);
|
|
42
47
|
ctx.zoomOut(false);
|
|
@@ -63,7 +68,12 @@ export const AddToCartBlock = defineEffectBlock<Checkpoint<string>, ItemInCart>(
|
|
|
63
68
|
},
|
|
64
69
|
stubAfter: (ctx) => {
|
|
65
70
|
ctx.banner("In the cart");
|
|
66
|
-
ctx.
|
|
71
|
+
ctx.todoId("saucedemo-cart");
|
|
72
|
+
ctx.todos([
|
|
73
|
+
{ id: "cart-find", text: "Find the product" },
|
|
74
|
+
{ id: "cart-add", text: "Add to cart" },
|
|
75
|
+
{ id: "cart-open", text: "Open cart" },
|
|
76
|
+
]);
|
|
67
77
|
ctx.todoIndex(1);
|
|
68
78
|
ctx.zoomOut(false);
|
|
69
79
|
ctx.ring("badge", {
|
|
@@ -49,7 +49,12 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
49
49
|
// sequential highlight queue (appear / dwell / fade), focus + color.
|
|
50
50
|
stubBefore: (ctx) => {
|
|
51
51
|
ctx.title("Signing in");
|
|
52
|
-
ctx.
|
|
52
|
+
ctx.todoId("saucedemo-login");
|
|
53
|
+
ctx.todos([
|
|
54
|
+
{ id: "login-user", text: "Enter username" },
|
|
55
|
+
{ id: "login-pass", text: "Enter password" },
|
|
56
|
+
{ id: "login-submit", text: "Click Login" },
|
|
57
|
+
]);
|
|
53
58
|
ctx.todoIndex(0);
|
|
54
59
|
ctx.zoom(1.35);
|
|
55
60
|
ctx.highlights({
|
|
@@ -82,7 +87,12 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
82
87
|
// inventory shelf. Success (LoggedIn) hands off to shopFlow.
|
|
83
88
|
if (ctx.out && ctx.out.__state === "LoginPage") {
|
|
84
89
|
ctx.title("Login blocked");
|
|
85
|
-
ctx.
|
|
90
|
+
ctx.todoId("saucedemo-login");
|
|
91
|
+
ctx.todos([
|
|
92
|
+
{ id: "login-user", text: "Enter username" },
|
|
93
|
+
{ id: "login-pass", text: "Enter password" },
|
|
94
|
+
{ id: "login-submit", text: "Click Login" },
|
|
95
|
+
]);
|
|
86
96
|
ctx.todoIndex(2);
|
|
87
97
|
ctx.ring("error", {
|
|
88
98
|
selector: '[data-test="error"]',
|
|
@@ -95,7 +105,12 @@ export const SubmitLoginActionBlock = defineMethodBlock<LoginPage, LoginSubmitOu
|
|
|
95
105
|
return;
|
|
96
106
|
}
|
|
97
107
|
ctx.banner("Inventory");
|
|
98
|
-
ctx.
|
|
108
|
+
ctx.todoId("saucedemo-login");
|
|
109
|
+
ctx.todos([
|
|
110
|
+
{ id: "login-user", text: "Enter username" },
|
|
111
|
+
{ id: "login-pass", text: "Enter password" },
|
|
112
|
+
{ id: "login-submit", text: "Click Login" },
|
|
113
|
+
]);
|
|
99
114
|
ctx.todoIndex(2);
|
|
100
115
|
// Landed on inventory - hand off to shopFlow for product -> cart zoom.
|
|
101
116
|
ctx.ring("shelf", {
|