waygraph 0.15.21 → 0.15.23

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.
@@ -0,0 +1,272 @@
1
+ // Moved verbatim from the former CHAIN_RUNNER_SCRIPT template string in cli.ts (see src/ARCHITECTURE.md).
2
+ // Runs inside the target project's own waygraph copy - keep it dependency-light and self-contained.
3
+ import { demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, formatHighlightCaption, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightTone, normalizeHighlightWeight, resolveFixtureDwellMs } from "../highlights.js";
4
+ import { installOverlay } from "./overlay-install.js";
5
+ import { applyHighlightZoom, ensureSelectorInView, hideRing, showRing } from "./rings.js";
6
+ import { demoPaceKind } from "./demo-log.js";
7
+
8
+ /**
9
+ * Multi-step yap captions - not block lifecycle. Each slide waits for Next
10
+ * (or auto-next), optional ring via slide.selector.
11
+ *
12
+ * Advance wait does NOT use exposeFunction/gate(): after a real navigation
13
+ * (e.g. finish-order -> checkout-complete) the panel can render while
14
+ * __wgNext is momentarily wedged, which left "Next slide" looking live but
15
+ * doing nothing. In-page data-wg-acked + autoplay poll is the source of truth.
16
+ */
17
+ export async function presentSlides(page, slides, _gate, opts) {
18
+ const title = (opts && opts.title) || "waygraph demo";
19
+ const blockName = (opts && opts.blockName) || "";
20
+ const episodeNumber = opts && opts.episodeNumber;
21
+ const episodeTitle = (opts && opts.episodeTitle) || "";
22
+ const gatesFast = !!(opts && opts.fast);
23
+ const demoPace = normalizeDemoPace((opts && opts.pace) || (gatesFast ? "fast" : "normal"));
24
+ const baseAutoplay =
25
+ opts && opts.autoplayMs
26
+ ? Number(opts.autoplayMs)
27
+ : process.env.WAYGRAPH_AUTOPLAY_MS
28
+ ? Number(process.env.WAYGRAPH_AUTOPLAY_MS)
29
+ : 1800;
30
+ const autoplayMs = demoPaceGateMs(demoPace, baseAutoplay);
31
+ for (let i = 0; i < slides.length; i++) {
32
+ const s = slides[i];
33
+ const caption = formatHighlightCaption(s);
34
+ const slideTone = normalizeHighlightTone(s.tone);
35
+ const slideStyle = {
36
+ size: normalizeHighlightSize(s.size),
37
+ weight: normalizeHighlightWeight(s.weight),
38
+ };
39
+ const isLast = i === slides.length - 1;
40
+ const dwellMs = resolveFixtureDwellMs(s, { gatesFast, pace: demoPace });
41
+ await installOverlay(page, title);
42
+ if (s.selector) {
43
+ try {
44
+ await ensureSelectorInView(page, s.selector);
45
+ await applyHighlightZoom(page, s.selector, s.zoom, s.zoomOut);
46
+ const box = await page
47
+ .evaluate((sel) => {
48
+ const el = document.querySelector(sel);
49
+ if (!el) return null;
50
+ const r = el.getBoundingClientRect();
51
+ return { x: r.x, y: r.y, width: r.width, height: r.height };
52
+ }, s.selector)
53
+ .catch(() => null);
54
+ if (box && box.width > 0 && box.height > 0) {
55
+ await showRing(page, box, caption, slideTone, {
56
+ ...slideStyle,
57
+ selector: s.selector,
58
+ focus: !!s.focus,
59
+ });
60
+ }
61
+ } catch {
62
+ await hideRing(page);
63
+ }
64
+ } else {
65
+ await hideRing(page);
66
+ }
67
+ await page.evaluate(
68
+ (info) => {
69
+ let panel = document.getElementById("wg-panel");
70
+ const isNew = !panel;
71
+ if (!panel) {
72
+ panel = document.createElement("div");
73
+ panel.id = "wg-panel";
74
+ }
75
+ panel.classList.remove("wg-error", "wg-expected");
76
+ // Do not clear wg-collapsed - WirePanelChrome honors Hide (localStorage)
77
+ // unless forceCollapsed:true (--mini / --video).
78
+ const esc = (t) =>
79
+ String(t).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
80
+ const episodeLine =
81
+ info.episodeNumber !== undefined && info.episodeNumber !== null
82
+ ? "<div class=\"wg-episode\">Episode " +
83
+ info.episodeNumber +
84
+ (info.episodeTitle ? ": " + esc(info.episodeTitle) : "") +
85
+ " · yap slides</div>"
86
+ : "";
87
+ const paceHtml =
88
+ info.paceBadge || info.paceLabel
89
+ ? "<div class=\"wg-pace\" data-pace-kind=\"" +
90
+ esc(info.paceKind || "normal") +
91
+ "\"><span class=\"wg-pace-badge\">" +
92
+ esc(info.paceBadge || "1x") +
93
+ "</span><span>" +
94
+ esc(info.paceLabel || "") +
95
+ "</span></div>"
96
+ : "";
97
+ panel.innerHTML =
98
+ "<div class=\"wg-chrome\"><span class=\"wg-chrome-title\">waygraph demo</span>" +
99
+ "<button type=\"button\" class=\"wg-hide-btn\" data-wg-toggle=\"1\">Hide</button></div>" +
100
+ "<div class=\"wg-body\">" +
101
+ episodeLine +
102
+ paceHtml +
103
+ "<h3>Slide " +
104
+ (info.index + 1) +
105
+ " / " +
106
+ info.total +
107
+ (info.blockName ? " · " + esc(info.blockName) : "") +
108
+ "</h3>" +
109
+ (info.tag
110
+ ? "<div style=\"border:none;padding:0;margin:0 0 6px;font-size:11px;color:#c9a6ff\">" +
111
+ esc(info.tag) +
112
+ "</div>"
113
+ : "") +
114
+ "<div class=\"wg-narration\">" +
115
+ esc(info.caption) +
116
+ "</div>" +
117
+ (info.detail
118
+ ? "<div class=\"wg-result-pretty\" style=\"margin:0 0 12px;color:#f0e8ff\">" +
119
+ esc(info.detail) +
120
+ "</div>"
121
+ : "") +
122
+ (info.dwellMs != null
123
+ ? "<div class=\"wg-auto\" id=\"wg-dwell-hint\" style=\"margin:0 0 8px\">Hold " +
124
+ (info.dwellMs / 1000).toFixed(1) +
125
+ "s before next...</div>"
126
+ : "") +
127
+ "<div class=\"wg-autoplay-row\"><label><input type=\"checkbox\" id=\"wg-autoplay-cb\"" +
128
+ (info.autoNow ? " checked" : "") +
129
+ "> Auto-advance</label></div>" +
130
+ "<div id=\"wg-gate-manual\"" +
131
+ (info.autoNow ? " style=\"display:none\"" : "") +
132
+ "><button type=\"button\" id=\"wg-run\"" +
133
+ (info.dwellMs != null ? " disabled" : "") +
134
+ ">" +
135
+ (info.isLast ? "Continue \u25B6" : "Next slide \u25B6") +
136
+ "</button></div>" +
137
+ "<div id=\"wg-gate-auto\" class=\"wg-auto\"" +
138
+ (info.autoNow ? "" : " style=\"display:none\"") +
139
+ ">Auto-advancing...</div>" +
140
+ "</div>";
141
+ if (isNew) {
142
+ document.documentElement.appendChild(panel);
143
+ requestAnimationFrame(() => panel.classList.add("wg-in"));
144
+ }
145
+ if (window.__wgWirePanelChrome) {
146
+ window.__wgWirePanelChrome(panel, "wg-panel-hidden", "waygraph demo", {
147
+ stepLabel:
148
+ (info.episodeNumber !== undefined && info.episodeNumber !== null
149
+ ? "Ep " + info.episodeNumber + " \u00b7 "
150
+ : "") +
151
+ "Slide " +
152
+ (info.index + 1) +
153
+ " / " +
154
+ info.total +
155
+ (info.blockName ? " \u00b7 " + info.blockName : ""),
156
+ // Video / --mini: compact pill. Else omit so Hide sticks.
157
+ forceCollapsed: info.forceCollapsed === true ? true : undefined,
158
+ });
159
+ }
160
+ if (window.__wgStampModal) {
161
+ window.__wgStampModal(panel, "panel", {
162
+ phase: "slide",
163
+ step: info.index + 1,
164
+ total: info.total,
165
+ block: info.blockName || "",
166
+ ready: true,
167
+ });
168
+ }
169
+ let autoNow = false;
170
+ try {
171
+ autoNow = localStorage.getItem("wg-autoplay") === "1";
172
+ } catch {
173
+ /* ignore */
174
+ }
175
+ const cb = document.getElementById("wg-autoplay-cb");
176
+ if (cb) {
177
+ cb.checked = autoNow;
178
+ cb.addEventListener("change", () => {
179
+ try {
180
+ localStorage.setItem("wg-autoplay", cb.checked ? "1" : "0");
181
+ } catch {
182
+ /* ignore */
183
+ }
184
+ const manual = document.getElementById("wg-gate-manual");
185
+ const auto = document.getElementById("wg-gate-auto");
186
+ if (manual) manual.style.display = cb.checked ? "none" : "";
187
+ if (auto) auto.style.display = cb.checked ? "" : "none";
188
+ });
189
+ }
190
+ const manual = document.getElementById("wg-gate-manual");
191
+ const auto = document.getElementById("wg-gate-auto");
192
+ if (manual) manual.style.display = autoNow ? "none" : "";
193
+ if (auto) auto.style.display = autoNow ? "" : "none";
194
+ const runBtn = document.getElementById("wg-run");
195
+ if (runBtn) {
196
+ runBtn.removeAttribute("data-wg-acked");
197
+ runBtn.addEventListener("click", (e) => {
198
+ e.preventDefault();
199
+ e.stopPropagation();
200
+ if (runBtn.disabled) return;
201
+ runBtn.setAttribute("data-wg-acked", "1");
202
+ });
203
+ }
204
+ if (info.dwellMs != null && info.dwellMs > 0) {
205
+ const unlockAt = Date.now() + info.dwellMs;
206
+ const tick = () => {
207
+ const left = Math.max(0, unlockAt - Date.now());
208
+ const hint = document.getElementById("wg-dwell-hint");
209
+ const btn = document.getElementById("wg-run");
210
+ if (left <= 0) {
211
+ if (hint) hint.remove();
212
+ if (btn) btn.disabled = false;
213
+ return;
214
+ }
215
+ if (hint) hint.textContent = "Hold " + (left / 1000).toFixed(1) + "s before next...";
216
+ setTimeout(tick, 100);
217
+ };
218
+ setTimeout(tick, 100);
219
+ }
220
+ },
221
+ {
222
+ index: i,
223
+ total: slides.length,
224
+ caption: s.caption,
225
+ detail: s.detail || "",
226
+ tag: s.tag || "",
227
+ blockName,
228
+ isLast,
229
+ autoNow: false,
230
+ dwellMs,
231
+ episodeNumber: episodeNumber !== undefined ? episodeNumber : null,
232
+ episodeTitle,
233
+ paceBadge: formatDemoPaceBadge(demoPace, baseAutoplay),
234
+ paceLabel: formatDemoPaceLabel(demoPace, baseAutoplay),
235
+ paceKind: demoPaceKind(demoPace),
236
+ forceCollapsed:
237
+ !!process.env.WAYGRAPH_VIDEO ||
238
+ process.env.WAYGRAPH_MINI === "1" ||
239
+ process.env.WAYGRAPH_STEPPER_MINI === "1"
240
+ ? true
241
+ : undefined,
242
+ },
243
+ );
244
+ const started = Date.now();
245
+ // When duration is set: floor both manual Next and auto-next to dwellMs.
246
+ // When unset: legacy - auto uses autoplayMs, manual Next is immediate.
247
+ const autoWaitMs = dwellMs != null ? dwellMs : autoplayMs;
248
+ for (;;) {
249
+ const acked = await page
250
+ .evaluate(() => {
251
+ const btn = document.getElementById("wg-run");
252
+ return !!(btn && btn.getAttribute("data-wg-acked") === "1");
253
+ })
254
+ .catch(() => false);
255
+ if (acked) {
256
+ if (dwellMs == null || Date.now() - started >= dwellMs) break;
257
+ }
258
+ const auto = await page
259
+ .evaluate(() => {
260
+ try {
261
+ return localStorage.getItem("wg-autoplay") === "1";
262
+ } catch {
263
+ return false;
264
+ }
265
+ })
266
+ .catch(() => false);
267
+ if (auto && Date.now() - started >= autoWaitMs) break;
268
+ await new Promise((res) => setTimeout(res, 100));
269
+ }
270
+ }
271
+ await hideRing(page);
272
+ }