waygraph 0.15.28 → 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.
- package/dist/runner/inpage/banner.js +68 -0
- package/dist/runner/inpage/core.js +267 -0
- package/dist/runner/inpage/device.js +211 -0
- package/dist/runner/inpage/panel-chrome.js +152 -0
- package/dist/runner/inpage/stamp.js +46 -0
- package/dist/runner/inpage/swipe-zoom.js +220 -0
- package/dist/runner/inpage/todos.js +332 -0
- package/dist/runner/overlay-install.js +15 -1269
- package/examples/saucedemo/.waygraph-auto/3f088138.log +0 -0
- package/examples/saucedemo/.waygraph-auto/94221b2b.log +0 -0
- 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, "&")
|
|
157
|
+
.replace(/</g, "<")
|
|
158
|
+
.replace(/>/g, ">")
|
|
159
|
+
.replace(/"/g, """);
|
|
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
|
+
}
|