waygraph 0.15.27 → 0.15.29

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.
Files changed (34) hide show
  1. package/dist/highlights/css.js +2 -44
  2. package/dist/pilot-overlay.d.ts +0 -18
  3. package/dist/pilot-overlay.js +27 -32
  4. package/dist/runner/inpage/banner.js +68 -0
  5. package/dist/runner/inpage/core.js +267 -0
  6. package/dist/runner/inpage/device.js +211 -0
  7. package/dist/runner/inpage/panel-chrome.js +152 -0
  8. package/dist/runner/inpage/stamp.js +46 -0
  9. package/dist/runner/inpage/swipe-zoom.js +220 -0
  10. package/dist/runner/inpage/todos.js +332 -0
  11. package/dist/runner/overlay-css.js +7 -18
  12. package/dist/runner/overlay-install.js +15 -1269
  13. package/dist/step-overlay.js +2 -46
  14. package/dist/ui/components/banner.d.ts +2 -0
  15. package/dist/ui/components/banner.js +10 -0
  16. package/dist/ui/components/cursor.d.ts +2 -0
  17. package/dist/ui/components/cursor.js +19 -0
  18. package/dist/ui/components/ring.d.ts +4 -0
  19. package/dist/ui/components/ring.js +44 -0
  20. package/dist/ui/compose.d.ts +4 -0
  21. package/dist/ui/compose.js +7 -0
  22. package/dist/ui/tokens.d.ts +31 -0
  23. package/dist/ui/tokens.js +21 -0
  24. package/examples/saucedemo/.waygraph-auto/2324269a.log +0 -0
  25. package/examples/saucedemo/.waygraph-auto/3f088138.log +0 -0
  26. package/examples/saucedemo/.waygraph-auto/42e014d1.log +0 -0
  27. package/examples/saucedemo/.waygraph-auto/6706ead4.log +0 -0
  28. package/examples/saucedemo/.waygraph-auto/7773a809.log +0 -0
  29. package/examples/saucedemo/.waygraph-auto/94221b2b.log +0 -0
  30. package/examples/saucedemo/.waygraph-auto/c60cc55e.log +0 -0
  31. package/examples/saucedemo/.waygraph-auto/da6ebb31.log +0 -0
  32. package/examples/saucedemo/.waygraph-auto/e03cfd8e.log +0 -0
  33. package/examples/saucedemo/.waygraph-auto/f2943898.log +0 -0
  34. package/package.json +1 -1
@@ -0,0 +1,332 @@
1
+ // In-page installer (runs inside the browser via page.evaluate): must stay self-contained - no module-scope closure.
2
+ // Split out of the former single installOverlay evaluate (see src/ARCHITECTURE.md). Shares state only via window.__wg*.
3
+ /* eslint-disable no-unused-vars */
4
+ export function installTodos({ title, favicon, bannerPos, todoPos, envAutoplay, todoDockUi }) {
5
+ // Floating todo dock - lives outside #wg-panel so --mini / Hide never
6
+ // hide the checklist. Click cycles left <-> right with CSS transform.
7
+ // Authors set side via ctx.todoPos("left"|"right"); env / --todo-* fallback.
8
+ // Payload:
9
+ // { sync: "clear" } -> remove dock
10
+ // { sync: "keep" } -> leave dock alone (no wipe)
11
+ // { sync: "set", dock } / { sync:"set", list } -> render / recreate
12
+ // Compat: bare array = set list (empty array = clear - legacy only).
13
+ window.__wgSyncTodos = (payload, pos) => {
14
+ const syncPayload = payload;
15
+ let sync = "set";
16
+ let list = [];
17
+ let dockState = null;
18
+ let wantPos =
19
+ pos === "left" || pos === "right" ? pos : null;
20
+ if (Array.isArray(payload)) {
21
+ list = payload;
22
+ sync = list.length ? "set" : "clear";
23
+ } else if (payload && typeof payload === "object") {
24
+ sync = payload.sync === "clear" || payload.sync === "keep" ? payload.sync : "set";
25
+ dockState = payload.dock || null;
26
+ if (payload.pos === "left" || payload.pos === "right") wantPos = payload.pos;
27
+ if (Array.isArray(payload.list)) list = payload.list;
28
+ else if (dockState && Array.isArray(dockState.groups)) {
29
+ // Flatten for legacy callers; full dock rendered below when present.
30
+ list = [];
31
+ for (const g of dockState.groups) {
32
+ if (g && Array.isArray(g.items)) list = list.concat(g.items);
33
+ }
34
+ }
35
+ } else {
36
+ list = [];
37
+ sync = "clear";
38
+ }
39
+
40
+ let dockKey =
41
+ (dockState && dockState.id && String(dockState.id).trim()) ||
42
+ (payload && !Array.isArray(payload) && payload.todoId && String(payload.todoId).trim()) ||
43
+ "_default";
44
+
45
+ // Drop any in-panel leftover (old builds put #wg-todos in .wg-body).
46
+ document.querySelectorAll("#wg-panel #wg-todos").forEach((el) => el.remove());
47
+
48
+ const allDocks = () =>
49
+ [...document.querySelectorAll(".wg-todo-dock, #wg-todo-dock")].filter(
50
+ (el, i, arr) => arr.indexOf(el) === i,
51
+ );
52
+ const relayoutTodoDocks = () => {
53
+ const docks = allDocks();
54
+ let top = 72;
55
+ for (const el of docks) {
56
+ el.style.top = top + "px";
57
+ top += Math.max(48, el.getBoundingClientRect().height) + 10;
58
+ }
59
+ };
60
+ const findDock = (key) => {
61
+ if (key === "_default") {
62
+ return (
63
+ document.querySelector('.wg-todo-dock[data-wg-todo-key="_default"]') ||
64
+ document.getElementById("wg-todo-dock")
65
+ );
66
+ }
67
+ const all = document.querySelectorAll(".wg-todo-dock");
68
+ for (let i = 0; i < all.length; i++) {
69
+ if (all[i].getAttribute("data-wg-todo-key") === String(key)) return all[i];
70
+ }
71
+ return null;
72
+ };
73
+
74
+ // keep + no new payload: do not remove existing docks (PIA #10).
75
+ if (sync === "keep") {
76
+ if (!dockState && !list.length) return;
77
+ // After navigation the DOM may be gone - recreate from carried dock.
78
+ sync = "set";
79
+ }
80
+ // Default replace=true: one checklist. parallel docks only when replace:false.
81
+ const replace =
82
+ !(payload && !Array.isArray(payload) && payload.replace === false) &&
83
+ !(payload && !Array.isArray(payload) && payload.parallel === true);
84
+ const hasDock =
85
+ !!(dockState && Array.isArray(dockState.groups) && dockState.groups.length);
86
+ // Explicit clear only - never treat "set + dock + empty list" as clear
87
+ // (that thrash was marks -> blank in a few ms).
88
+ if (sync === "clear" || (sync === "set" && !hasDock && !list.length)) {
89
+ const clearId =
90
+ (payload && !Array.isArray(payload) && payload.todoId && String(payload.todoId).trim()) ||
91
+ (dockState && dockState.id && String(dockState.id).trim()) ||
92
+ "";
93
+ if (clearId && !replace) {
94
+ const el = findDock(clearId);
95
+ if (el) el.remove();
96
+ } else {
97
+ allDocks().forEach((el) => el.remove());
98
+ }
99
+ relayoutTodoDocks();
100
+ return;
101
+ }
102
+
103
+ if (sync === "set" && replace) {
104
+ // Drop sibling docks so we never stack duplicates.
105
+ for (const el of allDocks()) {
106
+ const key = el.getAttribute("data-wg-todo-key") || "_default";
107
+ if (key !== dockKey) el.remove();
108
+ }
109
+ }
110
+
111
+ const POS = ["left", "right"];
112
+ let dock = findDock(dockKey);
113
+ if (!dock) {
114
+ dock = document.createElement("div");
115
+ dock.className = "wg-todo-dock";
116
+ dock.setAttribute("data-wg-todo-key", dockKey);
117
+ if (dockKey === "_default") dock.id = "wg-todo-dock";
118
+ else dock.id = "wg-todo-dock--" + dockKey.replace(/[^a-zA-Z0-9_-]/g, "-");
119
+ dock.setAttribute("data-wg-ui", "1");
120
+ dock.setAttribute("data-wg-modal", "todos");
121
+ dock.setAttribute("data-wg-ready", "1");
122
+ let saved = null;
123
+ try {
124
+ saved = localStorage.getItem("wg-todo-pos");
125
+ } catch {
126
+ /* ignore */
127
+ }
128
+ const start =
129
+ wantPos ||
130
+ (saved && POS.includes(saved) ? saved : todoPos === "right" ? "right" : "left");
131
+ dock.dataset.pos = start;
132
+ dock.title = "Click to move checklist: left / right";
133
+ dock.addEventListener("click", (e) => {
134
+ e.stopPropagation();
135
+ const i = POS.indexOf(dock.dataset.pos || "left");
136
+ const next = POS[(i + 1) % POS.length];
137
+ dock.dataset.pos = next;
138
+ try {
139
+ localStorage.setItem("wg-todo-pos", next);
140
+ } catch {
141
+ /* ignore */
142
+ }
143
+ });
144
+ document.documentElement.appendChild(dock);
145
+ } else if (wantPos) {
146
+ dock.dataset.pos = wantPos;
147
+ try {
148
+ localStorage.setItem("wg-todo-pos", wantPos);
149
+ } catch {
150
+ /* ignore */
151
+ }
152
+ }
153
+
154
+ const esc = (s) =>
155
+ String(s)
156
+ .replace(/&/g, "&amp;")
157
+ .replace(/</g, "&lt;")
158
+ .replace(/>/g, "&gt;")
159
+ .replace(/"/g, "&quot;");
160
+ const attrId = (raw) => {
161
+ const id = raw != null && String(raw).trim() ? String(raw).trim() : "";
162
+ return id ? ' data-wg-todo-item="' + esc(id) + '"' : "";
163
+ };
164
+ // Compact + cap: >cap rows folds overflow; hover shows up to expandCap; "+N more" pins expand.
165
+ const ui = (payload && !Array.isArray(payload) && payload.ui) || window.__wgTodoDockUi || {};
166
+ const COMPACT_AT = Number(ui.cap) > 0 ? Number(ui.cap) : 5;
167
+ const WINDOW = COMPACT_AT;
168
+ const EXPAND_MAX = Number(ui.expandCap) > 0 ? Number(ui.expandCap) : 14;
169
+ const compactEnabled = ui.compact !== false;
170
+ const flattenItems = () => {
171
+ if (dockState && Array.isArray(dockState.groups) && dockState.groups.length) {
172
+ const out = [];
173
+ for (const g of dockState.groups) {
174
+ for (const t of g.items || []) out.push({ ...t, __style: g.style || dockState.style });
175
+ }
176
+ return out;
177
+ }
178
+ return (list || []).map((t) => ({ ...t, __style: "sequential" }));
179
+ };
180
+ const windowBounds = (items, cap) => {
181
+ const n = items.length;
182
+ if (n <= cap) return { start: 0, end: n };
183
+ let cur = items.findIndex((t) => t.current);
184
+ if (cur < 0) cur = items.findIndex((t) => !t.done);
185
+ if (cur < 0) cur = 0;
186
+ let start = Math.max(0, cur - Math.floor((cap - 1) / 2));
187
+ let end = Math.min(n, start + cap);
188
+ start = Math.max(0, end - cap);
189
+ return { start, end };
190
+ };
191
+ const renderItems = (items, style, foldMeta, indexOffset) => {
192
+ const offset = indexOffset || 0;
193
+ const rawStyle = String(style || "sequential").toLowerCase();
194
+ const st =
195
+ rawStyle === "checklist"
196
+ ? "checklist"
197
+ : rawStyle === "bullets" ||
198
+ rawStyle === "bullet" ||
199
+ rawStyle === "list" ||
200
+ rawStyle === "ul" ||
201
+ rawStyle === "points" ||
202
+ rawStyle === "plain"
203
+ ? "bullets"
204
+ : "sequential";
205
+ const foldStart = foldMeta ? foldMeta.foldStart : -1;
206
+ const foldEnd = foldMeta ? foldMeta.foldEnd : -1;
207
+ const deepStart = foldMeta ? foldMeta.deepStart : -1;
208
+ const deepEnd = foldMeta ? foldMeta.deepEnd : -1;
209
+ return (
210
+ '<ul class="wg-todos-list" data-wg-todo-style="' +
211
+ st +
212
+ '">' +
213
+ (items || [])
214
+ .map((t, idx) => {
215
+ const gi = offset + idx;
216
+ const clsBase =
217
+ st === "bullets"
218
+ ? t.done
219
+ ? "wg-todo-done"
220
+ : "wg-todo-pending"
221
+ : t.current
222
+ ? "wg-todo-current"
223
+ : t.done
224
+ ? "wg-todo-done"
225
+ : "wg-todo-pending";
226
+ let foldCls = "";
227
+ if (foldStart >= 0 && (gi < foldStart || gi >= foldEnd)) foldCls += " wg-todo-fold";
228
+ if (deepStart >= 0 && (gi < deepStart || gi >= deepEnd)) foldCls += " wg-todo-fold-deep";
229
+ let mark;
230
+ if (st === "checklist") {
231
+ mark = t.done ? "☑" : "☐";
232
+ } else if (st === "bullets") {
233
+ mark = "•";
234
+ } else {
235
+ mark = t.done ? "✓" : t.current ? "→" : "○";
236
+ }
237
+ const rowId = t.id || t.name || "";
238
+ return (
239
+ '<li class="' +
240
+ clsBase +
241
+ foldCls +
242
+ '"' +
243
+ attrId(rowId) +
244
+ '><span class="wg-todo-mark">' +
245
+ mark +
246
+ "</span><span>" +
247
+ esc(t.text || "") +
248
+ "</span></li>"
249
+ );
250
+ })
251
+ .join("") +
252
+ "</ul>"
253
+ );
254
+ };
255
+
256
+ if (dockKey !== "_default") dock.setAttribute("data-wg-todo-id", dockKey);
257
+ else dock.removeAttribute("data-wg-todo-id");
258
+
259
+ const flat = flattenItems();
260
+ const compact = compactEnabled && flat.length > COMPACT_AT;
261
+ const win = windowBounds(flat, WINDOW);
262
+ const expand = windowBounds(flat, EXPAND_MAX);
263
+ const foldMeta = compact
264
+ ? {
265
+ foldStart: win.start,
266
+ foldEnd: win.end,
267
+ deepStart: expand.start,
268
+ deepEnd: expand.end,
269
+ }
270
+ : null;
271
+ const hiddenCompact = compact ? flat.length - (win.end - win.start) : 0;
272
+ const hiddenExpand = compact ? flat.length - (expand.end - expand.start) : 0;
273
+
274
+ let html = "";
275
+ if (dockState && Array.isArray(dockState.groups) && dockState.groups.length) {
276
+ const dockTitle = dockState.title && String(dockState.title).trim();
277
+ if (dockTitle) {
278
+ html += '<div class="wg-todo-dock-title">' + esc(dockTitle) + "</div>";
279
+ }
280
+ let globalIdx = 0;
281
+ for (const g of dockState.groups) {
282
+ const gTitle = g.title && String(g.title).trim();
283
+ const gid = g.id || g.name || "";
284
+ html +=
285
+ '<div class="wg-todo-group"' +
286
+ (gid ? ' data-wg-todo-group="' + esc(gid) + '"' : "") +
287
+ ">";
288
+ if (gTitle) {
289
+ html += '<div class="wg-todo-group-title">' + esc(gTitle) + "</div>";
290
+ }
291
+ const gItems = g.items || [];
292
+ html += renderItems(gItems, g.style || dockState.style, foldMeta, globalIdx);
293
+ html += "</div>";
294
+ globalIdx += gItems.length;
295
+ }
296
+ } else {
297
+ html = renderItems(list, "sequential", foldMeta, 0);
298
+ }
299
+ if (compact && hiddenCompact > 0) {
300
+ const n = dock.dataset.expanded === "1" ? hiddenExpand : hiddenCompact;
301
+ if (n > 0) {
302
+ html +=
303
+ '<div class="wg-todo-more" data-wg-todo-more="1" title="Click to expand / collapse">' +
304
+ "+" +
305
+ n +
306
+ " more</div>";
307
+ }
308
+ }
309
+ if (compact) dock.dataset.compact = "1";
310
+ else {
311
+ delete dock.dataset.compact;
312
+ delete dock.dataset.expanded;
313
+ }
314
+ dock.innerHTML = html;
315
+ const moreBtn = dock.querySelector("[data-wg-todo-more]");
316
+ if (moreBtn) {
317
+ moreBtn.addEventListener("click", (e) => {
318
+ e.stopPropagation();
319
+ if (dock.dataset.expanded === "1") delete dock.dataset.expanded;
320
+ else dock.dataset.expanded = "1";
321
+ if (window.__wgSyncTodos && dock._wgLastPayload) {
322
+ window.__wgSyncTodos(dock._wgLastPayload);
323
+ }
324
+ });
325
+ }
326
+ dock._wgLastPayload = syncPayload;
327
+ relayoutTodoDocks();
328
+ if (window.__wgStampModal) {
329
+ window.__wgStampModal(dock, "todos", { ready: true });
330
+ }
331
+ };
332
+ }
@@ -1,6 +1,8 @@
1
1
  // Moved verbatim from the former CHAIN_RUNNER_SCRIPT template string in cli.ts (see src/ARCHITECTURE.md).
2
2
  // Runs inside the target project's own waygraph copy - keep it dependency-light and self-contained.
3
3
  import { WAYGRAPH_RING_CSS } from "../highlights.js";
4
+ import { cursorCss } from "../ui/components/cursor.js";
5
+ import { SURFACE } from "../ui/tokens.js";
4
6
 
5
7
  // ---------------------------------------------------------------------------
6
8
  // WAYGRAPH_STEP=1 - human-verification overlay: one Block at a time, a
@@ -26,20 +28,7 @@ export const RING_CSS =
26
28
  // Travel duration is JS-driven per call via --wg-cursor-ms, same reason
27
29
  // the clip engine's own comment gives: a hardcoded CSS duration would
28
30
  // make the speed param a no-op.
29
- "#wg-cursor{position:fixed;z-index:2147483647;width:24px;height:24px;pointer-events:none;" +
30
- "left:0;top:0;opacity:0;margin:0;" +
31
- "transition:transform var(--wg-cursor-ms,600ms) cubic-bezier(.22,1,.36,1),opacity .2s ease;" +
32
- "filter:drop-shadow(0 2px 4px rgba(12,12,26,.4));}" +
33
- "#wg-click-pulse{position:fixed;z-index:2147483647;width:14px;height:14px;" +
34
- "margin-left:-7px;margin-top:-7px;border-radius:50%;pointer-events:none;opacity:0;" +
35
- "border:2px solid #7C3AED;background:rgba(124,58,237,.25);}" +
36
- "#wg-click-pulse[data-tone=auto]{border-color:#9CA3AF;background:rgba(156,163,175,.28);}" +
37
- "#wg-click-pulse[data-tone=info]{border-color:#3B82F6;background:rgba(59,130,246,.28);}" +
38
- "#wg-click-pulse[data-tone=warning]{border-color:#EAB308;background:rgba(234,179,8,.28);}" +
39
- "#wg-click-pulse[data-tone=danger]{border-color:#EF4444;background:rgba(239,68,68,.28);}" +
40
- "#wg-click-pulse[data-tone=success]{border-color:#22C55E;background:rgba(34,197,94,.28);}" +
41
- "#wg-click-pulse.wg-pulse{animation:wg-pulse .5s ease-out;}" +
42
- "@keyframes wg-pulse{0%{opacity:.9;transform:scale(.4);}100%{opacity:0;transform:scale(2.4);}}" +
31
+ cursorCss() +
43
32
  // Spotlight: dim everything except the highlight target (focus: true).
44
33
  "#wg-focus-veil{position:fixed;z-index:2147483644;pointer-events:none;" +
45
34
  "border-radius:12px;box-shadow:0 0 0 9999px rgba(8,4,20,.62);" +
@@ -62,7 +51,7 @@ export const RING_CSS =
62
51
  "#wg-swipe-layer.wg-in .wg-swipe-label{opacity:1;}" +
63
52
  "#wg-panel{position:fixed;z-index:2147483647;left:50%;bottom:12px;transform:translateX(-50%);" +
64
53
  "max-width:min(92vw,640px);max-height:calc(100vh - 24px);overflow-y:auto;box-sizing:border-box;" +
65
- "background:rgba(20,10,40,.94);color:#fff;border-radius:14px;" +
54
+ "background:" + SURFACE + ";color:#fff;border-radius:14px;" +
66
55
  "padding:16px 20px;font:14px/1.4 system-ui,sans-serif;box-shadow:0 12px 30px rgba(0,0,0,.35);" +
67
56
  // .06s was tuned back when the panel was removed and recreated on
68
57
  // EVERY step - fast was the only way to avoid feeling laggy. Now that
@@ -106,7 +95,7 @@ export const RING_CSS =
106
95
  // Click a dock to slide left <-> right (WAYGRAPH_TODO_POS / --todo-left|right).
107
96
  ".wg-todo-dock,#wg-todo-dock{position:fixed;z-index:2147483646;top:72px;left:14px;" +
108
97
  "width:min(280px,42vw);max-height:calc(100vh - 100px);overflow:auto;box-sizing:border-box;" +
109
- "padding:10px 12px;background:rgba(20,10,40,.94);color:#fff;border-radius:12px;" +
98
+ "padding:10px 12px;background:" + SURFACE + ";color:#fff;border-radius:12px;" +
110
99
  "border:1px solid rgba(124,58,237,.45);box-shadow:0 8px 24px rgba(0,0,0,.35);" +
111
100
  "pointer-events:auto;cursor:pointer;" +
112
101
  "transition:transform .4s cubic-bezier(.22,1,.36,1),top .35s ease,opacity .25s ease,z-index 0s;transform:translateX(0);}" +
@@ -147,7 +136,7 @@ export const RING_CSS =
147
136
  // Always-on zoom HUD - bottom-right (top-left is crowded: todos + banners).
148
137
  "#wg-zoom-badge{position:fixed;z-index:2147483646;bottom:14px;right:14px;" +
149
138
  "display:flex;align-items:center;gap:7px;padding:6px 11px 6px 8px;" +
150
- "border-radius:999px;background:rgba(20,10,40,.94);color:#f0e8ff;" +
139
+ "border-radius:999px;background:" + SURFACE + ";color:#f0e8ff;" +
151
140
  "border:1px solid rgba(250,204,21,.6);box-shadow:0 6px 18px rgba(0,0,0,.4);" +
152
141
  "font:700 12px/1.2 system-ui,sans-serif;pointer-events:none;" +
153
142
  "opacity:1;transform:translateY(0);}" +
@@ -255,7 +244,7 @@ export const RING_CSS =
255
244
  "#wg-modules .wg-mod-current{background:#7C3AED;color:#fff;}" +
256
245
  "#wg-modules .wg-mod-upcoming{background:transparent;color:#5a4a80;border:1px solid #3a2a60;}" +
257
246
  "#wg-banner{position:fixed;z-index:2147483647;top:14px;max-width:320px;" +
258
- "background:rgba(20,10,40,.94);color:#fff;border-radius:12px;padding:10px 16px;" +
247
+ "background:" + SURFACE + ";color:#fff;border-radius:12px;padding:10px 16px;" +
259
248
  "font:14px/1.4 system-ui,sans-serif;box-shadow:0 8px 20px rgba(0,0,0,.3);" +
260
249
  "border:1px solid rgba(124,58,237,.4);cursor:pointer;user-select:none;}" +
261
250
  "#wg-banner[data-pos=left]{left:14px;right:auto;transform:none;}" +