@bud.sh/app 0.5.1 → 0.6.0
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 +41 -31
- package/dist/client/assets/bud/design.js +1 -1
- package/dist/client/assets/canvas-Bih9EdKJ.js +1 -0
- package/dist/client/assets/difference-worker-Bzg-rizy.js +8 -0
- package/dist/client/assets/dist-CdE5wBkg.js +5 -0
- package/dist/client/assets/files-1dLuoM2f.js +1 -0
- package/dist/client/assets/i18n-CkO6VFI0.js +1 -0
- package/dist/client/assets/index-Cn2mxroU.css +2 -0
- package/dist/client/assets/{index-Dn2pfmXf.js → index-DtISWZp9.js} +2 -2
- package/dist/client/assets/pages-BJ-tNJ4J.js +1 -0
- package/dist/client/assets/{project-branches-CMGRmrqr.js → project-branches-Ce3rXrkc.js} +1 -1
- package/dist/client/assets/project-comments-CPUONfs8.js +1 -0
- package/dist/client/assets/project-thread-links-C-TGkq59.js +1 -0
- package/dist/client/assets/{project-threads-ZbKDh78d.js → project-threads-Bv7IDBgi.js} +105 -105
- package/dist/client/assets/project-work-DAE9-olg.js +1 -0
- package/dist/client/assets/{task-design-BPpc4Gd7.js → task-design-CwySIAOl.js} +3 -3
- package/dist/client/assets/tasks-KWsCLi_h.js +3 -0
- package/dist/client/assets/wasm-inline-DSUyFn1L.js +1 -0
- package/dist/client/assets/workspace-CVdcfaaB.js +2 -0
- package/dist/client/index.html +3 -3
- package/dist/hosted/assets/{canvas-BYTOp1p7.js → canvas-AII16n9i.js} +97 -6
- package/dist/hosted/assets/{comment-data-Cn5y2VxV.js → comment-data-BXfMElfx.js} +1998 -381
- package/dist/hosted/assets/dist-BCSTeNf4.js +4785 -0
- package/dist/hosted/assets/{files-B1P2_24q.js → files-D_1yeAVO.js} +10 -5
- package/dist/hosted/assets/{pages-B0ZlVdci.js → pages-CN_SbKWA.js} +3 -3
- package/dist/hosted/assets/{project-branches-DlCWmfRo.js → project-branches-Bxp0DCwg.js} +1 -1
- package/dist/hosted/assets/{project-thread-links-BZXQ200n.js → project-thread-links-KqQoKBLE.js} +76 -4
- package/dist/{node-host/assets/screen-DapOVrga.js → hosted/assets/screen-DpAcDUKR.js} +25 -1
- package/dist/hosted/assets/tasks-D66LlwxM.js +1423 -0
- package/dist/hosted/assets/wasm-inline-Dl9tqabx.js +4 -0
- package/dist/hosted/hosted.js +7885 -6219
- package/dist/node/assets/Stream-CAm1eJ2n.js +16604 -0
- package/dist/{server/assets/bridge-B_Blt55E.js → node/assets/bridge-DQSBYHmw.js} +124 -290
- package/dist/node/assets/config-9_jWwNBh.js +1067 -0
- package/dist/node/assets/load-config.server-CMVtRVmO.js +261 -0
- package/dist/node/assets/module-3GgtT4Xs.js +308 -0
- package/dist/node/assets/server-DTQFuoX2.js +846 -0
- package/dist/node/assets/src-D-kG1GxB.js +2 -0
- package/dist/node/assets/{src-CerrKjID.js → src-Dwe9APUx.js} +93 -3
- package/dist/node/assets/src-DysjfbM8.js +8592 -0
- package/dist/node/bridge.js +1 -845
- package/dist/node/cli.js +206 -0
- package/dist/node/design.js +811 -8
- package/dist/node/index.js +2 -739
- package/dist/node/plugins.js +5711 -0
- package/dist/node/server.js +1 -6746
- package/dist/node/vite.js +121 -0
- package/dist/{server/assets/canvas-CkMK4hoN.js → node-host/assets/canvas-BeDoufy1.js} +97 -7
- package/dist/node-host/assets/{comment-data-yfWEdDQL.js → comment-data-DE2T9yTj.js} +1974 -355
- package/dist/node-host/assets/dist-BCSTeNf4.js +4785 -0
- package/dist/node-host/assets/{files-anHi-apm.js → files-Dl-PTS09.js} +11 -6
- package/dist/node-host/assets/{pages-CW4aF6dK.js → pages-DL2sk6_U.js} +3 -3
- package/dist/node-host/assets/{project-branches-D7rVCxQX.js → project-branches-DGp5ELct.js} +1 -1
- package/dist/{server/assets/project-thread-links-lLS_rq7c.js → node-host/assets/project-thread-links-CgBV3Jcm.js} +76 -5
- package/dist/{hosted/assets/screen-CfdC3NbR.js → node-host/assets/screen-B55yyVfb.js} +25 -1
- package/dist/node-host/assets/tasks-DZRNkoxe.js +1425 -0
- package/dist/node-host/assets/wasm-inline-Dl9tqabx.js +4 -0
- package/dist/node-host/node-host.js +6127 -4438
- package/dist/server/assets/{branch-previews-ClkoJ0lM.js → branch-previews-DedrpnVR.js} +1555 -673
- package/dist/{node-host/assets/canvas-DxmcFZCh.js → server/assets/canvas-CJWQ-jjv.js} +98 -6
- package/dist/server/assets/{comment-data-NLeHG89b.js → comment-data-Rg42ToTW.js} +1660 -26
- package/dist/server/assets/dist-BCSTeNf4.js +4785 -0
- package/dist/server/assets/{events-iHpLGr3k.js → events-BH_rAu7O.js} +155 -20
- package/dist/server/assets/{files-CRdl489E.js → files-CPJqKeMo.js} +12 -7
- package/dist/server/assets/{listener-BAYn9Q-u.js → listener-D61O6PzF.js} +708 -12
- package/dist/server/assets/{pages-DXVlJlBC.js → pages-KJY_Ba92.js} +4 -4
- package/dist/server/assets/{project-branches-CwTsf6WQ.js → project-branches-qG9ACZvo.js} +2 -2
- package/dist/{node-host/assets/project-thread-links-m-OLpsD9.js → server/assets/project-thread-links-CqK0lqCF.js} +77 -4
- package/dist/{node-host/assets/tasks-request-B0aeNocI.js → server/assets/project-work-DoIwxWMm.js} +87 -2
- package/dist/server/assets/run-BJ8r5jSh.js +572 -0
- package/dist/server/assets/{screen-B1zH_1O3.js → screen-CBrHPFug.js} +26 -2
- package/dist/server/assets/{server-DxK-UB_Z.js → server-Dkbp5stG.js} +1521 -793
- package/dist/server/assets/{services-BRrgXYlT.js → services-BTfig8A8.js} +234 -997
- package/dist/server/assets/tasks-Bgu5Lg9Y.js +1427 -0
- package/dist/server/assets/wasm-inline-Dl9tqabx.js +4 -0
- package/dist/server/capture.js +1 -2
- package/dist/server/migration.js +569 -12
- package/dist/server/runtime.js +7 -5
- package/dist/server/server.js +3 -3
- package/dist/types/assets/{config-DIC2cf_S.d.ts → config-CvcYmE2s.d.ts} +188 -50
- package/dist/types/assets/{contract-CAT-XFNq.d.ts → contract-B7yuuqSn.d.ts} +44 -2
- package/dist/types/assets/{items-CaVkByHk.d.ts → items-BQdaVksf.d.ts} +12 -1
- package/dist/types/assets/memory-state-BBW-FAVA.d.ts +236 -0
- package/dist/types/assets/picker-Be5Gx4WR.d.ts +9 -0
- package/dist/types/assets/{task-design-xJOhRNWh.d.ts → task-design-BDHKFHpp.d.ts} +3 -1
- package/dist/types/bridge.d.ts +1 -1
- package/dist/types/companion.d.ts +3 -3
- package/dist/types/design.d.ts +2 -2
- package/dist/types/hosted.d.ts +3 -3
- package/dist/types/index.d.ts +2 -2
- package/dist/types/node-host.d.ts +2 -2
- package/dist/types/plugins.d.ts +445 -0
- package/dist/types/screen.d.ts +2 -2
- package/dist/types/server.d.ts +2 -228
- package/dist/types/vite.d.ts +16 -0
- package/package.json +19 -3
- package/dist/client/assets/canvas-8GXJIuDK.js +0 -1
- package/dist/client/assets/difference-worker-C99ychYw.js +0 -8
- package/dist/client/assets/files-DkWt1l9H.js +0 -1
- package/dist/client/assets/i18n-hH_Ba5QO.js +0 -1
- package/dist/client/assets/index-tpHYVXma.css +0 -2
- package/dist/client/assets/pages-BVd6MQmu.js +0 -1
- package/dist/client/assets/project-comments-D1IIQwsW.js +0 -1
- package/dist/client/assets/project-thread-links-DKwe_SYb.js +0 -1
- package/dist/client/assets/project-work-D4DZqBCZ.js +0 -1
- package/dist/client/assets/tasks-Cn0VOZ9Z.js +0 -1
- package/dist/client/assets/tasks-request-BLyOzokZ.js +0 -1
- package/dist/client/assets/workspace-DGErsSg6.js +0 -2
- package/dist/hosted/assets/tasks-_yKh2Z6q.js +0 -649
- package/dist/hosted/assets/tasks-request-CFPLvLc7.js +0 -113
- package/dist/node-host/assets/tasks-BBK_CybR.js +0 -651
- package/dist/server/assets/project-work-CSZlCwH-.js +0 -88
- package/dist/server/assets/tasks-CGdz-uox.js +0 -652
- package/dist/server/assets/tasks-request-D2NBjCTl.js +0 -115
- package/dist/types/assets/picker-D1Bw3ndx.d.ts +0 -14
package/dist/node/bridge.js
CHANGED
|
@@ -1,845 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* The page side of every picker: Bud's DOM picker composed with a framework adapter. It runs in
|
|
4
|
-
* the project's pages as module text, so it must not reference anything outside its own body.
|
|
5
|
-
* @param adapter - The configured picker's page module, or null for the DOM picker alone.
|
|
6
|
-
* @param name - The picker's name, recorded on every anchor it describes.
|
|
7
|
-
* @returns The picker's page operations.
|
|
8
|
-
*/
|
|
9
|
-
function pickerRuntime(adapter, name) {
|
|
10
|
-
let blocks = "article, section, li, figure, form, header, footer, nav, aside, main, dialog, [data-block], [data-component], [role=\"listitem\"], [role=\"region\"], [role=\"dialog\"]";
|
|
11
|
-
let ignored = "[data-react-grab], [data-bud-picker], script, style, link, meta, noscript";
|
|
12
|
-
let active = null;
|
|
13
|
-
function clip(text, length) {
|
|
14
|
-
return text.length > length ? text.slice(0, length) : text;
|
|
15
|
-
}
|
|
16
|
-
function normalize(text) {
|
|
17
|
-
return (text ?? "").replace(/\s+/g, " ").trim();
|
|
18
|
-
}
|
|
19
|
-
function textOf(element) {
|
|
20
|
-
return clip(normalize(element instanceof HTMLElement ? element.innerText : element.textContent), 1e3);
|
|
21
|
-
}
|
|
22
|
-
function fingerprint(element) {
|
|
23
|
-
let value = `${element.localName}\u0000${textOf(element)}`;
|
|
24
|
-
let hash = 2166136261;
|
|
25
|
-
for (let index = 0; index < value.length; index++) {
|
|
26
|
-
hash ^= value.charCodeAt(index);
|
|
27
|
-
hash = Math.imul(hash, 16777619) >>> 0;
|
|
28
|
-
}
|
|
29
|
-
return hash.toString(16).padStart(8, "0");
|
|
30
|
-
}
|
|
31
|
-
function stableId(element) {
|
|
32
|
-
let id = element.id;
|
|
33
|
-
if (!id || /[:\d]/.test(id) || id.length > 64) return null;
|
|
34
|
-
return document.querySelectorAll(`#${CSS.escape(id)}`).length === 1 ? id : null;
|
|
35
|
-
}
|
|
36
|
-
function segment(element) {
|
|
37
|
-
let id = stableId(element);
|
|
38
|
-
if (id) return `#${CSS.escape(id)}`;
|
|
39
|
-
let tag = element.localName;
|
|
40
|
-
let parent = element.parentElement;
|
|
41
|
-
if (!parent) return tag;
|
|
42
|
-
let same = [...parent.children].filter((child) => child.localName === tag);
|
|
43
|
-
return same.length > 1 ? `${tag}:nth-of-type(${same.indexOf(element) + 1})` : tag;
|
|
44
|
-
}
|
|
45
|
-
function selectorOf(element) {
|
|
46
|
-
let parts = [];
|
|
47
|
-
for (let node = element; node; node = node.parentElement) {
|
|
48
|
-
if (node === document.documentElement) break;
|
|
49
|
-
let part = segment(node);
|
|
50
|
-
parts.unshift(part);
|
|
51
|
-
if (part.startsWith("#") || node === document.body) break;
|
|
52
|
-
}
|
|
53
|
-
return clip(parts.join(" > "), 4096);
|
|
54
|
-
}
|
|
55
|
-
function breadcrumbOf(element) {
|
|
56
|
-
let parts = [];
|
|
57
|
-
for (let node = element; node && parts.length < 5; node = node.parentElement) {
|
|
58
|
-
if (node === document.body || node === document.documentElement) break;
|
|
59
|
-
parts.unshift(node.localName);
|
|
60
|
-
}
|
|
61
|
-
return clip(parts.join(" > ") || element.localName, 4096);
|
|
62
|
-
}
|
|
63
|
-
function labelOf(element) {
|
|
64
|
-
return clip(normalize(element.getAttribute("aria-label") || element.getAttribute("alt") || element.getAttribute("title")) || clip(textOf(element), 80) || element.localName, 500);
|
|
65
|
-
}
|
|
66
|
-
function query(selector) {
|
|
67
|
-
try {
|
|
68
|
-
return document.querySelector(selector);
|
|
69
|
-
} catch {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
function box(element) {
|
|
74
|
-
let rect = element.getBoundingClientRect();
|
|
75
|
-
return {
|
|
76
|
-
left: rect.left + scrollX,
|
|
77
|
-
top: rect.top + scrollY,
|
|
78
|
-
width: rect.width,
|
|
79
|
-
height: rect.height
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
function fraction(offset, size) {
|
|
83
|
-
return size > 0 ? Math.min(1, Math.max(0, offset / size)) : .5;
|
|
84
|
-
}
|
|
85
|
-
function elementAt(clientX, clientY) {
|
|
86
|
-
for (let element of document.elementsFromPoint(clientX, clientY)) {
|
|
87
|
-
if (element === document.documentElement || element.closest(ignored)) continue;
|
|
88
|
-
return element;
|
|
89
|
-
}
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
async function blockOf(element) {
|
|
93
|
-
return (adapter?.block ? await Promise.resolve(adapter.block(element)).catch(() => null) : null) ?? element.closest(blocks);
|
|
94
|
-
}
|
|
95
|
-
async function anchor(element, x, y) {
|
|
96
|
-
let rect = box(element);
|
|
97
|
-
let around = await blockOf(element);
|
|
98
|
-
let described = adapter?.describe ? await Promise.resolve(adapter.describe(element)).catch(() => null) : null;
|
|
99
|
-
let block = null;
|
|
100
|
-
if (around) {
|
|
101
|
-
let outer = box(around);
|
|
102
|
-
block = {
|
|
103
|
-
selector: selectorOf(around),
|
|
104
|
-
label: labelOf(around),
|
|
105
|
-
tagName: around.localName,
|
|
106
|
-
offsetX: fraction(x - outer.left, outer.width),
|
|
107
|
-
offsetY: fraction(y - outer.top, outer.height)
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
return {
|
|
111
|
-
selector: selectorOf(element),
|
|
112
|
-
breadcrumb: breadcrumbOf(element),
|
|
113
|
-
contentHash: fingerprint(element),
|
|
114
|
-
label: described?.label ? clip(described.label, 500) : labelOf(element),
|
|
115
|
-
tagName: clip(element.localName, 100),
|
|
116
|
-
text: textOf(element),
|
|
117
|
-
offsetX: fraction(x - rect.left, rect.width),
|
|
118
|
-
offsetY: fraction(y - rect.top, rect.height),
|
|
119
|
-
block,
|
|
120
|
-
source: described?.source ?? null,
|
|
121
|
-
picker: name
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
function alike(a, b) {
|
|
125
|
-
let words = (text) => new Set(text.toLowerCase().split(/\s+/).filter(Boolean));
|
|
126
|
-
let left = words(a);
|
|
127
|
-
let right = words(b);
|
|
128
|
-
let shared = [...left].filter((word) => right.has(word)).length;
|
|
129
|
-
let all = (/* @__PURE__ */ new Set([...left, ...right])).size;
|
|
130
|
-
return all === 0 || shared / all >= .5;
|
|
131
|
-
}
|
|
132
|
-
function find(stored) {
|
|
133
|
-
let candidate = query(stored.selector);
|
|
134
|
-
let same = (element) => element.localName === stored.tagName && (!stored.text || fingerprint(element) === stored.contentHash);
|
|
135
|
-
if (candidate && same(candidate)) return {
|
|
136
|
-
element: candidate,
|
|
137
|
-
offsetX: stored.offsetX,
|
|
138
|
-
offsetY: stored.offsetY
|
|
139
|
-
};
|
|
140
|
-
if (stored.text) {
|
|
141
|
-
let moved = [...document.getElementsByTagName(stored.tagName)].filter((element) => !element.closest(ignored) && same(element));
|
|
142
|
-
let container = stored.block ? query(stored.block.selector) : null;
|
|
143
|
-
let inside = container ? moved.filter((element) => container.contains(element)) : [];
|
|
144
|
-
let found = moved.length === 1 ? moved[0] : inside.length === 1 ? inside[0] : null;
|
|
145
|
-
if (found) return {
|
|
146
|
-
element: found,
|
|
147
|
-
offsetX: stored.offsetX,
|
|
148
|
-
offsetY: stored.offsetY
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
let block = stored.block ? query(stored.block.selector) : null;
|
|
152
|
-
if (block && block.localName === stored.block.tagName && alike(labelOf(block), stored.block.label)) return {
|
|
153
|
-
element: block,
|
|
154
|
-
offsetX: stored.block.offsetX,
|
|
155
|
-
offsetY: stored.block.offsetY,
|
|
156
|
-
block: true
|
|
157
|
-
};
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
return {
|
|
161
|
-
async describe(x, y) {
|
|
162
|
-
let from = {
|
|
163
|
-
left: scrollX,
|
|
164
|
-
top: scrollY
|
|
165
|
-
};
|
|
166
|
-
scrollTo({
|
|
167
|
-
left: 0,
|
|
168
|
-
top: Math.max(0, y - innerHeight / 2),
|
|
169
|
-
behavior: "instant"
|
|
170
|
-
});
|
|
171
|
-
try {
|
|
172
|
-
let element = elementAt(x - scrollX, y - scrollY);
|
|
173
|
-
return element ? await anchor(element, x, y) : null;
|
|
174
|
-
} finally {
|
|
175
|
-
scrollTo({
|
|
176
|
-
...from,
|
|
177
|
-
behavior: "instant"
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
async locate(anchors) {
|
|
182
|
-
return anchors.map(({ id, element }) => {
|
|
183
|
-
let found = find(element);
|
|
184
|
-
if (!found) return {
|
|
185
|
-
id,
|
|
186
|
-
match: "missing",
|
|
187
|
-
x: null,
|
|
188
|
-
y: null
|
|
189
|
-
};
|
|
190
|
-
let rect = box(found.element);
|
|
191
|
-
return {
|
|
192
|
-
id,
|
|
193
|
-
match: "block" in found ? "block" : "element",
|
|
194
|
-
x: rect.left + found.offsetX * rect.width,
|
|
195
|
-
y: rect.top + found.offsetY * rect.height
|
|
196
|
-
};
|
|
197
|
-
});
|
|
198
|
-
},
|
|
199
|
-
pick() {
|
|
200
|
-
active?.abort();
|
|
201
|
-
return new Promise((resolve) => {
|
|
202
|
-
let controller = new AbortController();
|
|
203
|
-
let outline = document.createElement("div");
|
|
204
|
-
outline.setAttribute("data-bud-picker", "");
|
|
205
|
-
outline.style.cssText = "position:fixed;z-index:2147483647;pointer-events:none;border:2px solid #7c3aed;border-radius:4px;background:rgb(124 58 237 / 0.08);display:none;box-sizing:border-box";
|
|
206
|
-
let done = false;
|
|
207
|
-
let finish = (value) => {
|
|
208
|
-
if (done) return;
|
|
209
|
-
done = true;
|
|
210
|
-
controller.abort();
|
|
211
|
-
outline.remove();
|
|
212
|
-
if (active === session) active = null;
|
|
213
|
-
resolve(value);
|
|
214
|
-
};
|
|
215
|
-
let session = { abort: () => finish(null) };
|
|
216
|
-
active = session;
|
|
217
|
-
let signal = controller.signal;
|
|
218
|
-
if (adapter?.pick) {
|
|
219
|
-
adapter.pick(signal).then(async (element) => {
|
|
220
|
-
if (!element || done) return finish(null);
|
|
221
|
-
let rect = box(element);
|
|
222
|
-
finish(await anchor(element, rect.left + rect.width / 2, rect.top + rect.height / 2));
|
|
223
|
-
}).catch(() => finish(null));
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
document.documentElement.append(outline);
|
|
227
|
-
let hovered = null;
|
|
228
|
-
let options = {
|
|
229
|
-
capture: true,
|
|
230
|
-
signal
|
|
231
|
-
};
|
|
232
|
-
addEventListener("pointermove", (event) => {
|
|
233
|
-
hovered = elementAt(event.clientX, event.clientY);
|
|
234
|
-
if (!hovered) return void (outline.style.display = "none");
|
|
235
|
-
let rect = hovered.getBoundingClientRect();
|
|
236
|
-
Object.assign(outline.style, {
|
|
237
|
-
display: "block",
|
|
238
|
-
left: `${rect.left}px`,
|
|
239
|
-
top: `${rect.top}px`,
|
|
240
|
-
width: `${rect.width}px`,
|
|
241
|
-
height: `${rect.height}px`
|
|
242
|
-
});
|
|
243
|
-
}, options);
|
|
244
|
-
for (let type of [
|
|
245
|
-
"pointerdown",
|
|
246
|
-
"mousedown",
|
|
247
|
-
"pointerup",
|
|
248
|
-
"mouseup"
|
|
249
|
-
]) addEventListener(type, (event) => event.preventDefault(), options);
|
|
250
|
-
addEventListener("click", (event) => {
|
|
251
|
-
event.preventDefault();
|
|
252
|
-
event.stopPropagation();
|
|
253
|
-
let element = elementAt(event.clientX, event.clientY);
|
|
254
|
-
if (!element) return;
|
|
255
|
-
anchor(element, event.clientX + scrollX, event.clientY + scrollY).then(finish);
|
|
256
|
-
}, options);
|
|
257
|
-
addEventListener("keydown", (event) => {
|
|
258
|
-
if (event.key !== "Escape") return;
|
|
259
|
-
event.preventDefault();
|
|
260
|
-
finish(null);
|
|
261
|
-
}, options);
|
|
262
|
-
});
|
|
263
|
-
},
|
|
264
|
-
cancel() {
|
|
265
|
-
active?.abort();
|
|
266
|
-
},
|
|
267
|
-
async index(options = {}) {
|
|
268
|
-
let limit = options.maxElements ?? 2e4;
|
|
269
|
-
let mark = options.mark ?? null;
|
|
270
|
-
let page = globalThis;
|
|
271
|
-
let from = {
|
|
272
|
-
left: scrollX,
|
|
273
|
-
top: scrollY
|
|
274
|
-
};
|
|
275
|
-
let priming = page.__budPriming;
|
|
276
|
-
page.__budPriming = true;
|
|
277
|
-
try {
|
|
278
|
-
scrollTo({
|
|
279
|
-
left: 0,
|
|
280
|
-
top: 0,
|
|
281
|
-
behavior: "instant"
|
|
282
|
-
});
|
|
283
|
-
let round = (value) => Math.round(value * 100) / 100;
|
|
284
|
-
let intersect = (a, b) => {
|
|
285
|
-
let left = Math.max(a[0], b[0]);
|
|
286
|
-
let top = Math.max(a[1], b[1]);
|
|
287
|
-
let right = Math.min(a[0] + a[2], b[0] + b[2]);
|
|
288
|
-
let bottom = Math.min(a[1] + a[3], b[1] + b[3]);
|
|
289
|
-
return [
|
|
290
|
-
left,
|
|
291
|
-
top,
|
|
292
|
-
Math.max(0, right - left),
|
|
293
|
-
Math.max(0, bottom - top)
|
|
294
|
-
];
|
|
295
|
-
};
|
|
296
|
-
let everywhere = [
|
|
297
|
-
-1e6,
|
|
298
|
-
-1e6,
|
|
299
|
-
2e6,
|
|
300
|
-
2e6
|
|
301
|
-
];
|
|
302
|
-
let found = [];
|
|
303
|
-
let indexes = /* @__PURE__ */ new Map();
|
|
304
|
-
let clips = /* @__PURE__ */ new Map();
|
|
305
|
-
for (let element of document.body?.querySelectorAll("*") ?? []) {
|
|
306
|
-
if (found.length >= limit) break;
|
|
307
|
-
if (element.closest(ignored)) continue;
|
|
308
|
-
let style = getComputedStyle(element);
|
|
309
|
-
let rect = element.getBoundingClientRect();
|
|
310
|
-
let own = [
|
|
311
|
-
rect.left + scrollX,
|
|
312
|
-
rect.top + scrollY,
|
|
313
|
-
rect.width,
|
|
314
|
-
rect.height
|
|
315
|
-
];
|
|
316
|
-
let container = element.parentElement;
|
|
317
|
-
let clip = everywhere;
|
|
318
|
-
if (style.position !== "fixed") for (let node = container; node; node = node.parentElement) {
|
|
319
|
-
let known = clips.get(node);
|
|
320
|
-
if (known) {
|
|
321
|
-
clip = known;
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
clips.set(element, style.overflowX !== "visible" || style.overflowY !== "visible" ? intersect(clip, own) : clip);
|
|
326
|
-
if (rect.width <= 0 || rect.height <= 0) continue;
|
|
327
|
-
let box = intersect(own, clip);
|
|
328
|
-
let parent = -1;
|
|
329
|
-
for (let node = container; node && parent < 0; node = node.parentElement) parent = indexes.get(node) ?? -1;
|
|
330
|
-
indexes.set(element, found.length);
|
|
331
|
-
found.push({
|
|
332
|
-
element,
|
|
333
|
-
box: [
|
|
334
|
-
round(box[0]),
|
|
335
|
-
round(box[1]),
|
|
336
|
-
round(box[2]),
|
|
337
|
-
round(box[3])
|
|
338
|
-
],
|
|
339
|
-
parent,
|
|
340
|
-
inert: style.pointerEvents === "none" || style.visibility !== "visible"
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
let sources = [];
|
|
344
|
-
let known = /* @__PURE__ */ new Map();
|
|
345
|
-
let elements = [];
|
|
346
|
-
for (let [index, { element, box, parent, inert }] of found.entries()) {
|
|
347
|
-
let around = await blockOf(element);
|
|
348
|
-
let described = adapter?.describe ? await Promise.resolve(adapter.describe(element)).catch(() => null) : null;
|
|
349
|
-
let source = -1;
|
|
350
|
-
if (described?.source) {
|
|
351
|
-
let key = JSON.stringify(described.source);
|
|
352
|
-
source = known.get(key) ?? -1;
|
|
353
|
-
if (source < 0) {
|
|
354
|
-
source = sources.push(described.source) - 1;
|
|
355
|
-
known.set(key, source);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
if (mark) element.setAttribute(mark, String(index));
|
|
359
|
-
elements.push({
|
|
360
|
-
box,
|
|
361
|
-
parent,
|
|
362
|
-
rank: inert ? -1 : 0,
|
|
363
|
-
selector: selectorOf(element),
|
|
364
|
-
breadcrumb: breadcrumbOf(element),
|
|
365
|
-
contentHash: fingerprint(element),
|
|
366
|
-
label: described?.label ? clip(described.label, 500) : labelOf(element),
|
|
367
|
-
tagName: clip(element.localName, 100),
|
|
368
|
-
text: textOf(element),
|
|
369
|
-
block: around ? indexes.get(around) ?? -1 : -1,
|
|
370
|
-
source
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
return {
|
|
374
|
-
version: 2,
|
|
375
|
-
picker: name,
|
|
376
|
-
width: document.documentElement.clientWidth,
|
|
377
|
-
height: Math.min(document.documentElement.scrollHeight, 1e5),
|
|
378
|
-
elements,
|
|
379
|
-
sources
|
|
380
|
-
};
|
|
381
|
-
} finally {
|
|
382
|
-
scrollTo({
|
|
383
|
-
...from,
|
|
384
|
-
behavior: "instant"
|
|
385
|
-
});
|
|
386
|
-
page.__budPriming = priming;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
//#endregion
|
|
392
|
-
//#region ../picker/src/lib/module.ts
|
|
393
|
-
/** The picker module's ID in the project's dev server, served by Bud's Vite plugin. */
|
|
394
|
-
var PICKER_MODULE_ID = "\0bud-picker";
|
|
395
|
-
/** Where pages import the picker module from. */
|
|
396
|
-
var PICKER_MODULE_URL = "/@id/__x00__bud-picker";
|
|
397
|
-
/** Where a picker script leaves the picker in a page that has no module to import it from. */
|
|
398
|
-
var PICKER_GLOBAL = "__budPicker";
|
|
399
|
-
/** A built-in picker's adapter module, which imports the framework from the project. */
|
|
400
|
-
var PICKER_ADAPTER_MODULE_ID = "\0bud-picker-adapter";
|
|
401
|
-
/**
|
|
402
|
-
* The picker module's text: Bud's DOM picker composed with the configured adapter.
|
|
403
|
-
* @param name - The picker's name, recorded on every anchor.
|
|
404
|
-
* @param adapter - Where the adapter's page module is imported from, or null for the DOM picker.
|
|
405
|
-
* @returns ES module text exporting describe, locate, pick and cancel.
|
|
406
|
-
*/
|
|
407
|
-
function pickerModule(name, adapter) {
|
|
408
|
-
return [
|
|
409
|
-
adapter ? `import adapter from ${JSON.stringify(adapter)}` : "const adapter = null",
|
|
410
|
-
`const picker = (${pickerRuntime.toString()})(adapter ?? null, ${JSON.stringify(name)})`,
|
|
411
|
-
"export const describe = picker.describe",
|
|
412
|
-
"export const locate = picker.locate",
|
|
413
|
-
"export const pick = picker.pick",
|
|
414
|
-
"export const cancel = picker.cancel",
|
|
415
|
-
"export const index = picker.index"
|
|
416
|
-
].join("\n");
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* The picker as a classic script, for pages no Bud Vite plugin serves, such as builds of a
|
|
420
|
-
* branch: running it leaves the picker on `window.__budPicker` (once) and evaluates to it.
|
|
421
|
-
* @param name - The picker's name, recorded on every anchor.
|
|
422
|
-
* @param adapter - An expression evaluating to the adapter, needing nothing from the page's
|
|
423
|
-
* modules; null for the DOM picker.
|
|
424
|
-
* @returns Script text.
|
|
425
|
-
*/
|
|
426
|
-
function pickerScript(name, adapter) {
|
|
427
|
-
return `(globalThis.${PICKER_GLOBAL} ??= (${pickerRuntime.toString()})(${adapter ?? "null"}, ${JSON.stringify(name)}))`;
|
|
428
|
-
}
|
|
429
|
-
//#endregion
|
|
430
|
-
//#region ../picker-react-grab/src/lib/adapter.ts
|
|
431
|
-
/**
|
|
432
|
-
* The page side of `bud:react` and `bud:react-grab`: an element's component and source come from
|
|
433
|
-
* React's development build, its block is the component's outermost element, and with React Grab
|
|
434
|
-
* its own selection picks. It runs in the project's pages as module or script text, so it must not
|
|
435
|
-
* reference anything outside its own body. The line comes from React's own stack, mapped through
|
|
436
|
-
* the module's source map, so it is exact whichever React Grab a project runs; React Grab's own
|
|
437
|
-
* answer fills in what that can't find. Production builds keep no source, so only the DOM picker's
|
|
438
|
-
* own description remains.
|
|
439
|
-
* @param grab - The project's `react-grab` module, or null to read React alone. Importing it
|
|
440
|
-
* starts React Grab if it isn't yet.
|
|
441
|
-
* @param helpers - Resolves file names and source map positions.
|
|
442
|
-
* @returns The picker adapter.
|
|
443
|
-
*/
|
|
444
|
-
function reactGrabAdapter(grab, helpers) {
|
|
445
|
-
let { sourceFile, projectFrames, sourceMapLookup } = helpers;
|
|
446
|
-
let plugin = "bud:picker";
|
|
447
|
-
let maps = /* @__PURE__ */ new Map();
|
|
448
|
-
let host = 5;
|
|
449
|
-
function fiberOf(element) {
|
|
450
|
-
let key = Object.keys(element).find((name) => name.startsWith("__reactFiber$"));
|
|
451
|
-
return key ? element[key] ?? null : null;
|
|
452
|
-
}
|
|
453
|
-
function outermost(component) {
|
|
454
|
-
let node = component.child;
|
|
455
|
-
while (node && node.tag !== host) node = node.child;
|
|
456
|
-
return node?.stateNode instanceof Element ? node.stateNode : null;
|
|
457
|
-
}
|
|
458
|
-
function sourceMap(url) {
|
|
459
|
-
let cached = maps.get(url);
|
|
460
|
-
if (cached) return cached;
|
|
461
|
-
cached = fetch(url).then((response) => response.ok ? response.text() : "").then(async (text) => {
|
|
462
|
-
let reference = /\/\/[#@] sourceMappingURL=(\S+)\s*$/.exec(text)?.[1];
|
|
463
|
-
let address = new URL(reference ?? `${url.replace(/[?#].*$/, "")}.map`, url);
|
|
464
|
-
if (address.protocol !== "data:" && address.origin !== location.origin) return null;
|
|
465
|
-
let response = await fetch(address);
|
|
466
|
-
let map = response.ok ? await response.json() : null;
|
|
467
|
-
let lookup = map && typeof map === "object" ? sourceMapLookup(map) : null;
|
|
468
|
-
return lookup ? {
|
|
469
|
-
lookup,
|
|
470
|
-
base: address.protocol === "data:" ? url : address.href
|
|
471
|
-
} : null;
|
|
472
|
-
}).catch(() => null);
|
|
473
|
-
maps.set(url, cached);
|
|
474
|
-
if (maps.size > 50) maps.delete(maps.keys().next().value);
|
|
475
|
-
return cached;
|
|
476
|
-
}
|
|
477
|
-
function servedPath(source, base, stack) {
|
|
478
|
-
if (source.startsWith("/") && !source.startsWith("//")) return sourceFile(source, stack, location.origin);
|
|
479
|
-
let url = new URL(source, base);
|
|
480
|
-
return url.origin === location.origin && !url.pathname.startsWith("/@") && !url.pathname.includes("/node_modules/") ? url.pathname : null;
|
|
481
|
-
}
|
|
482
|
-
async function written(stack) {
|
|
483
|
-
let frames = projectFrames(stack, location.origin);
|
|
484
|
-
for (let frame of frames) {
|
|
485
|
-
let map = await sourceMap(frame.url);
|
|
486
|
-
let position = map ? map.lookup(frame.line, frame.column) : {
|
|
487
|
-
source: frame.url,
|
|
488
|
-
line: frame.line,
|
|
489
|
-
column: frame.column
|
|
490
|
-
};
|
|
491
|
-
if (!position) continue;
|
|
492
|
-
let file = servedPath(position.source, map?.base ?? frame.url, stack);
|
|
493
|
-
if (file) return {
|
|
494
|
-
name: frame.name,
|
|
495
|
-
file,
|
|
496
|
-
line: position.line,
|
|
497
|
-
column: position.column
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
return frames[0] ? {
|
|
501
|
-
name: frames[0].name,
|
|
502
|
-
file: null,
|
|
503
|
-
line: null,
|
|
504
|
-
column: null
|
|
505
|
-
} : null;
|
|
506
|
-
}
|
|
507
|
-
function ownerName(element) {
|
|
508
|
-
let type = fiberOf(element)?._debugOwner?.type;
|
|
509
|
-
let name = type && typeof type === "object" ? type.displayName ?? type.name : null;
|
|
510
|
-
return typeof name === "string" && name ? name : null;
|
|
511
|
-
}
|
|
512
|
-
let adapter = {
|
|
513
|
-
async describe(element) {
|
|
514
|
-
let api = grab?.getGlobalApi() ?? null;
|
|
515
|
-
let stack = fiberOf(element)?._debugStack?.stack ?? "";
|
|
516
|
-
let mapped = stack ? await written(stack).catch(() => null) : null;
|
|
517
|
-
let found = !mapped?.file && api ? await api.getSource(element).catch(() => null) : null;
|
|
518
|
-
let component = mapped?.name ?? found?.componentName ?? api?.getDisplayName(element) ?? ownerName(element);
|
|
519
|
-
if (!mapped?.file && !found && !component) return null;
|
|
520
|
-
if (mapped?.file) return { source: {
|
|
521
|
-
component: component || null,
|
|
522
|
-
file: mapped.file,
|
|
523
|
-
line: mapped.line,
|
|
524
|
-
column: mapped.column
|
|
525
|
-
} };
|
|
526
|
-
let file = found?.filePath ? sourceFile(found.filePath, stack, location.origin) : null;
|
|
527
|
-
let line = found?.lineNumber && found.lineNumber > 0 ? found.lineNumber : null;
|
|
528
|
-
let column = found?.columnNumber != null && found.columnNumber >= 0 ? found.columnNumber + 1 : null;
|
|
529
|
-
return { source: {
|
|
530
|
-
component: component || null,
|
|
531
|
-
file,
|
|
532
|
-
line,
|
|
533
|
-
column
|
|
534
|
-
} };
|
|
535
|
-
},
|
|
536
|
-
block(element) {
|
|
537
|
-
for (let owner = fiberOf(element)?._debugOwner; owner; owner = owner._debugOwner) {
|
|
538
|
-
let outer = outermost(owner);
|
|
539
|
-
if (outer && outer !== document.body && outer.contains(element)) return outer;
|
|
540
|
-
}
|
|
541
|
-
return null;
|
|
542
|
-
}
|
|
543
|
-
};
|
|
544
|
-
if (!grab) return adapter;
|
|
545
|
-
return {
|
|
546
|
-
...adapter,
|
|
547
|
-
pick(signal) {
|
|
548
|
-
let api = grab.getGlobalApi();
|
|
549
|
-
if (!api || signal.aborted) return Promise.resolve(null);
|
|
550
|
-
return new Promise((resolve) => {
|
|
551
|
-
let settled = false;
|
|
552
|
-
let tidy = () => {
|
|
553
|
-
api.unregisterPlugin(plugin);
|
|
554
|
-
if (api.isActive()) api.deactivate();
|
|
555
|
-
};
|
|
556
|
-
let finish = (element, during) => {
|
|
557
|
-
if (settled) return;
|
|
558
|
-
settled = true;
|
|
559
|
-
signal.removeEventListener("abort", cancel);
|
|
560
|
-
resolve(element);
|
|
561
|
-
if (during) setTimeout(tidy, 1e3);
|
|
562
|
-
else tidy();
|
|
563
|
-
};
|
|
564
|
-
let cancel = () => finish(null, false);
|
|
565
|
-
signal.addEventListener("abort", cancel, { once: true });
|
|
566
|
-
api.unregisterPlugin(plugin);
|
|
567
|
-
api.registerPlugin({
|
|
568
|
-
name: plugin,
|
|
569
|
-
theme: { grabbedBoxes: { enabled: false } },
|
|
570
|
-
hooks: {
|
|
571
|
-
onElementSelect(element) {
|
|
572
|
-
finish(element, true);
|
|
573
|
-
return true;
|
|
574
|
-
},
|
|
575
|
-
onDeactivate: () => finish(null, true)
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
api.activate();
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
//#endregion
|
|
584
|
-
//#region ../picker-react-grab/src/lib/source-map.ts
|
|
585
|
-
/**
|
|
586
|
-
* The frames of React's stack for an element that may have run in the project's own code, in
|
|
587
|
-
* order: the first one whose source is the project's is the component that rendered it.
|
|
588
|
-
* Dependencies the dev server serves apart, Vite's own modules and other origins are left out; a
|
|
589
|
-
* build bundles dependencies with the project, so their frames stay for their source maps to
|
|
590
|
-
* tell apart. It runs in the project's pages as module text, so it must not reference anything
|
|
591
|
-
* outside its own body.
|
|
592
|
-
* @param stack - React's stack for the element, as `Error.stack` text.
|
|
593
|
-
* @param origin - The page's origin.
|
|
594
|
-
* @returns The frames, possibly none.
|
|
595
|
-
*/
|
|
596
|
-
function projectFrames(stack, origin) {
|
|
597
|
-
let frames = [];
|
|
598
|
-
for (let text of stack.split("\n")) {
|
|
599
|
-
let match = /^\s*at (?:(.+?) \()?(https?:\/\/[^\s()]+?):(\d+):(\d+)\)?\s*$/.exec(text);
|
|
600
|
-
if (!match) continue;
|
|
601
|
-
let url = new URL(match[2]);
|
|
602
|
-
if (url.origin !== origin || url.pathname.startsWith("/@") || url.pathname.includes("/node_modules/")) continue;
|
|
603
|
-
let name = match[1]?.replace(/^Object\./, "") ?? null;
|
|
604
|
-
frames.push({
|
|
605
|
-
name,
|
|
606
|
-
url: url.href,
|
|
607
|
-
line: Number(match[3]),
|
|
608
|
-
column: Number(match[4])
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
return frames;
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* Decodes a module's source map once, for looking up many positions in it. It runs in the
|
|
615
|
-
* project's pages as module text, so it must not reference anything outside its own body.
|
|
616
|
-
* @param map - The source map, as parsed JSON.
|
|
617
|
-
* @returns Maps a generated line and column, counted from one, to the original position; null
|
|
618
|
-
* for a map that can't be read.
|
|
619
|
-
*/
|
|
620
|
-
function sourceMapLookup(map) {
|
|
621
|
-
if (typeof map.mappings !== "string" || !Array.isArray(map.sources)) return null;
|
|
622
|
-
let sources = map.sources;
|
|
623
|
-
let root = typeof map.sourceRoot === "string" && map.sourceRoot ? map.sourceRoot : "";
|
|
624
|
-
let digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
625
|
-
let lines = [];
|
|
626
|
-
let source = 0;
|
|
627
|
-
let sourceLine = 0;
|
|
628
|
-
let sourceColumn = 0;
|
|
629
|
-
for (let text of map.mappings.split(";")) {
|
|
630
|
-
let generated = 0;
|
|
631
|
-
let segments = [];
|
|
632
|
-
for (let segment of text.split(",")) {
|
|
633
|
-
if (!segment) continue;
|
|
634
|
-
let values = [];
|
|
635
|
-
let value = 0;
|
|
636
|
-
let shift = 0;
|
|
637
|
-
for (let character of segment) {
|
|
638
|
-
let digit = digits.indexOf(character);
|
|
639
|
-
if (digit < 0) return null;
|
|
640
|
-
value += (digit & 31) << shift;
|
|
641
|
-
if (digit & 32) shift += 5;
|
|
642
|
-
else {
|
|
643
|
-
values.push(value & 1 ? -(value >>> 1) : value >>> 1);
|
|
644
|
-
value = 0;
|
|
645
|
-
shift = 0;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
generated += values[0] ?? 0;
|
|
649
|
-
if (values.length < 4) continue;
|
|
650
|
-
source += values[1];
|
|
651
|
-
sourceLine += values[2];
|
|
652
|
-
sourceColumn += values[3];
|
|
653
|
-
segments.push([
|
|
654
|
-
generated,
|
|
655
|
-
source,
|
|
656
|
-
sourceLine,
|
|
657
|
-
sourceColumn
|
|
658
|
-
]);
|
|
659
|
-
}
|
|
660
|
-
lines.push(segments);
|
|
661
|
-
}
|
|
662
|
-
return (line, column) => {
|
|
663
|
-
let found = null;
|
|
664
|
-
for (let segment of lines[line - 1] ?? []) if (segment[0] <= column - 1) found = segment;
|
|
665
|
-
let name = found ? sources[found[1]] : null;
|
|
666
|
-
if (!found || typeof name !== "string") return null;
|
|
667
|
-
return {
|
|
668
|
-
source: root ? `${root.replace(/\/$/, "")}/${name}` : name,
|
|
669
|
-
line: found[2] + 1,
|
|
670
|
-
column: found[3] + 1
|
|
671
|
-
};
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
//#endregion
|
|
675
|
-
//#region ../picker-react-grab/src/lib/source.ts
|
|
676
|
-
/**
|
|
677
|
-
* The project path of a file React Grab names. React Grab maps React's stack through the dev
|
|
678
|
-
* server's source maps, whose sources are relative to the module that names them (Vite writes
|
|
679
|
-
* `card.tsx` for `/src/components/card.tsx`). The module's address is still in React's own stack,
|
|
680
|
-
* so the file resolves against the first frame from the page's origin that shares its name. It runs
|
|
681
|
-
* in the project's pages as module text, so it must not reference anything outside its own body.
|
|
682
|
-
* @param file - The file React Grab reported: a name, a relative or absolute path, or a URL.
|
|
683
|
-
* @param stack - React's stack for the element, as `Error.stack` text.
|
|
684
|
-
* @param origin - The page's origin; frames from elsewhere are never the project's.
|
|
685
|
-
* @returns A path as the dev server serves it, such as `/src/components/card.tsx`, or null.
|
|
686
|
-
*/
|
|
687
|
-
function sourceFile(file, stack, origin) {
|
|
688
|
-
let served = (url) => url.origin === origin && !url.pathname.startsWith("/@") && !url.pathname.includes("/node_modules/") && !/[\\\u0000-\u001f\u007f]/.test(url.pathname);
|
|
689
|
-
if (/^https?:\/\//.test(file)) {
|
|
690
|
-
let url = new URL(file);
|
|
691
|
-
return served(url) ? url.pathname : null;
|
|
692
|
-
}
|
|
693
|
-
let name = file.split("/").pop();
|
|
694
|
-
for (let line of stack.split("\n")) {
|
|
695
|
-
let match = /(https?:\/\/[^\s()]+?)(?::\d+){1,2}\)?\s*$/.exec(line);
|
|
696
|
-
if (!match) continue;
|
|
697
|
-
let frame = new URL(match[1]);
|
|
698
|
-
if (!served(frame) || frame.pathname.split("/").pop() !== name) continue;
|
|
699
|
-
if (file.startsWith("/")) return file.endsWith(frame.pathname) ? frame.pathname : null;
|
|
700
|
-
let resolved = new URL(file, frame);
|
|
701
|
-
return served(resolved) ? resolved.pathname : null;
|
|
702
|
-
}
|
|
703
|
-
return null;
|
|
704
|
-
}
|
|
705
|
-
function helpers() {
|
|
706
|
-
return [
|
|
707
|
-
"{",
|
|
708
|
-
` sourceFile: ${sourceFile.toString()},`,
|
|
709
|
-
` projectFrames: ${projectFrames.toString()},`,
|
|
710
|
-
` sourceMapLookup: ${sourceMapLookup.toString()},`,
|
|
711
|
-
"}"
|
|
712
|
-
].join("\n");
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* The adapter's page module text. It imports `react-grab` from the project, so React Grab must be
|
|
716
|
-
* installed there; the dev server serves the same instance the project's pages use.
|
|
717
|
-
* @returns ES module text whose default export is the picker adapter.
|
|
718
|
-
*/
|
|
719
|
-
function reactGrabAdapterModule() {
|
|
720
|
-
return ["import * as grab from 'react-grab'", `export default (${reactGrabAdapter.toString()})(grab, ${helpers()})`].join("\n");
|
|
721
|
-
}
|
|
722
|
-
/**
|
|
723
|
-
* An expression that builds the React adapter without React Grab, for module or script text.
|
|
724
|
-
* It needs nothing from the project, so pages without a dev server can run it too.
|
|
725
|
-
* @returns JavaScript expression text evaluating to the picker adapter.
|
|
726
|
-
*/
|
|
727
|
-
function reactAdapterExpression() {
|
|
728
|
-
return `(${reactGrabAdapter.toString()})(null, ${helpers()})`;
|
|
729
|
-
}
|
|
730
|
-
//#endregion
|
|
731
|
-
//#region src/bridge.ts
|
|
732
|
-
/**
|
|
733
|
-
* Hosts add this to the project's own pages during development, so Bud's Pages browser can follow
|
|
734
|
-
* a page it shows in a cross-origin frame. It does nothing outside a frame, answers only its
|
|
735
|
-
* parent, and reports where the page is. It receives nothing but back, forward, reload and the
|
|
736
|
-
* picker's requests: `bud:pick` runs the project's picker and answers `bud:picked` with the
|
|
737
|
-
* element, where it is on the page and the page's width; `bud:pick-cancel` ends a pick.
|
|
738
|
-
*/
|
|
739
|
-
var bridgeScript = `(() => {
|
|
740
|
-
if (window.parent === window || window.__budBridge) return
|
|
741
|
-
window.__budBridge = true
|
|
742
|
-
let parentOrigin = null
|
|
743
|
-
let send = () => {
|
|
744
|
-
if (!parentOrigin) return
|
|
745
|
-
window.parent.postMessage(
|
|
746
|
-
{
|
|
747
|
-
type: 'bud:location',
|
|
748
|
-
path: location.pathname + location.search + location.hash,
|
|
749
|
-
title: document.title,
|
|
750
|
-
},
|
|
751
|
-
parentOrigin,
|
|
752
|
-
)
|
|
753
|
-
}
|
|
754
|
-
addEventListener('message', (event) => {
|
|
755
|
-
if (event.source !== window.parent) return
|
|
756
|
-
let type = event.data && event.data.type
|
|
757
|
-
if (type === 'bud:hello') {
|
|
758
|
-
parentOrigin = event.origin
|
|
759
|
-
send()
|
|
760
|
-
} else if (event.origin !== parentOrigin) return
|
|
761
|
-
else if (type === 'bud:back') history.back()
|
|
762
|
-
else if (type === 'bud:forward') history.forward()
|
|
763
|
-
else if (type === 'bud:reload') location.reload()
|
|
764
|
-
else if (type === 'bud:pick') pick(event.data.id, event.origin)
|
|
765
|
-
else if (type === 'bud:pick-cancel') picker().then((module) => module.cancel(), () => {})
|
|
766
|
-
})
|
|
767
|
-
let picker = () => import(${JSON.stringify(PICKER_MODULE_URL)})
|
|
768
|
-
// The element a person picks, its point in page pixels and where it shows in the viewport, for
|
|
769
|
-
// the parent that asked.
|
|
770
|
-
let pick = async (id, origin) => {
|
|
771
|
-
if (typeof id !== 'string' || id.length > 64) return
|
|
772
|
-
let anchor = null
|
|
773
|
-
let point = null
|
|
774
|
-
let view = null
|
|
775
|
-
try {
|
|
776
|
-
anchor = await (await picker()).pick()
|
|
777
|
-
} catch {}
|
|
778
|
-
try {
|
|
779
|
-
let element = anchor && document.querySelector(anchor.selector)
|
|
780
|
-
if (element) {
|
|
781
|
-
let rect = element.getBoundingClientRect()
|
|
782
|
-
point = {
|
|
783
|
-
x: rect.left + scrollX + anchor.offsetX * rect.width,
|
|
784
|
-
y: rect.top + scrollY + anchor.offsetY * rect.height,
|
|
785
|
-
width: document.documentElement.clientWidth,
|
|
786
|
-
}
|
|
787
|
-
view = {
|
|
788
|
-
x: rect.left + anchor.offsetX * rect.width,
|
|
789
|
-
y: rect.top + anchor.offsetY * rect.height,
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
} catch {}
|
|
793
|
-
window.parent.postMessage(
|
|
794
|
-
{
|
|
795
|
-
type: 'bud:picked',
|
|
796
|
-
id,
|
|
797
|
-
path: location.pathname + location.search + location.hash,
|
|
798
|
-
anchor,
|
|
799
|
-
point,
|
|
800
|
-
view,
|
|
801
|
-
},
|
|
802
|
-
origin,
|
|
803
|
-
)
|
|
804
|
-
}
|
|
805
|
-
for (let method of ['pushState', 'replaceState']) {
|
|
806
|
-
let original = history[method]
|
|
807
|
-
history[method] = function (...args) {
|
|
808
|
-
let result = original.apply(this, args)
|
|
809
|
-
queueMicrotask(send)
|
|
810
|
-
return result
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
addEventListener('popstate', send)
|
|
814
|
-
addEventListener('hashchange', send)
|
|
815
|
-
addEventListener('load', send)
|
|
816
|
-
new MutationObserver(send).observe(document.head || document.documentElement, {
|
|
817
|
-
subtree: true,
|
|
818
|
-
childList: true,
|
|
819
|
-
characterData: true,
|
|
820
|
-
})
|
|
821
|
-
})()`;
|
|
822
|
-
/**
|
|
823
|
-
* The page modules a project's picker runs as, by module ID. Hosts serve them to the project's
|
|
824
|
-
* own pages during development; they hold no Bud state, only the picker's page code.
|
|
825
|
-
* @param picker - The project's configured picker.
|
|
826
|
-
* @returns Module text by ID, the picker module first.
|
|
827
|
-
*/
|
|
828
|
-
function pickerModules(picker) {
|
|
829
|
-
if (picker.name === "bud:react-grab" || picker.name === "bud:react") return /* @__PURE__ */ new Map([[PICKER_MODULE_ID, pickerModule(picker.name, PICKER_ADAPTER_MODULE_ID)], [PICKER_ADAPTER_MODULE_ID, picker.name === "bud:react-grab" ? reactGrabAdapterModule() : `export default ${reactAdapterExpression()}`]]);
|
|
830
|
-
let adapter = picker.module ? `/${picker.module.slice(2)}` : null;
|
|
831
|
-
return /* @__PURE__ */ new Map([[PICKER_MODULE_ID, pickerModule(picker.name, adapter)]]);
|
|
832
|
-
}
|
|
833
|
-
/**
|
|
834
|
-
* The picker as a classic script, for pages no Bud Vite plugin serves, such as a branch's build.
|
|
835
|
-
* React pickers read React's development build without React Grab, which such pages can't import;
|
|
836
|
-
* a project's own picker module needs its dev server, so the DOM picker stands in for it.
|
|
837
|
-
* @param picker - The project's configured picker.
|
|
838
|
-
* @returns Script text that leaves the picker on `window.__budPicker`.
|
|
839
|
-
*/
|
|
840
|
-
function pickerPageScript(picker) {
|
|
841
|
-
if (picker.name === "bud:react-grab" || picker.name === "bud:react") return pickerScript(picker.name, reactAdapterExpression());
|
|
842
|
-
return pickerScript(picker.module ? "bud:dom" : picker.name, null);
|
|
843
|
-
}
|
|
844
|
-
//#endregion
|
|
845
|
-
export { PICKER_MODULE_ID, PICKER_MODULE_URL, bridgeScript, pickerModules, pickerPageScript };
|
|
1
|
+
export { i as PICKER_MODULE_ID, a as PICKER_MODULE_URL, t as bridgeScript, n as pickerModules, r as pickerPageScript } from "./assets/bridge-DQSBYHmw.js";
|