@nimrobo/wand-web 0.1.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/dist/client.js ADDED
@@ -0,0 +1,1559 @@
1
+ "use strict";
2
+ (() => {
3
+ // node_modules/html-to-image/es/util.js
4
+ function resolveUrl(url, baseUrl) {
5
+ if (url.match(/^[a-z]+:\/\//i)) {
6
+ return url;
7
+ }
8
+ if (url.match(/^\/\//)) {
9
+ return window.location.protocol + url;
10
+ }
11
+ if (url.match(/^[a-z]+:/i)) {
12
+ return url;
13
+ }
14
+ const doc = document.implementation.createHTMLDocument();
15
+ const base = doc.createElement("base");
16
+ const a = doc.createElement("a");
17
+ doc.head.appendChild(base);
18
+ doc.body.appendChild(a);
19
+ if (baseUrl) {
20
+ base.href = baseUrl;
21
+ }
22
+ a.href = url;
23
+ return a.href;
24
+ }
25
+ var uuid = /* @__PURE__ */ (() => {
26
+ let counter = 0;
27
+ const random = () => (
28
+ // eslint-disable-next-line no-bitwise
29
+ `0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4)
30
+ );
31
+ return () => {
32
+ counter += 1;
33
+ return `u${random()}${counter}`;
34
+ };
35
+ })();
36
+ function toArray(arrayLike) {
37
+ const arr = [];
38
+ for (let i = 0, l = arrayLike.length; i < l; i++) {
39
+ arr.push(arrayLike[i]);
40
+ }
41
+ return arr;
42
+ }
43
+ var styleProps = null;
44
+ function getStyleProperties(options = {}) {
45
+ if (styleProps) {
46
+ return styleProps;
47
+ }
48
+ if (options.includeStyleProperties) {
49
+ styleProps = options.includeStyleProperties;
50
+ return styleProps;
51
+ }
52
+ styleProps = toArray(window.getComputedStyle(document.documentElement));
53
+ return styleProps;
54
+ }
55
+ function px(node, styleProperty) {
56
+ const win = node.ownerDocument.defaultView || window;
57
+ const val = win.getComputedStyle(node).getPropertyValue(styleProperty);
58
+ return val ? parseFloat(val.replace("px", "")) : 0;
59
+ }
60
+ function getNodeWidth(node) {
61
+ const leftBorder = px(node, "border-left-width");
62
+ const rightBorder = px(node, "border-right-width");
63
+ return node.clientWidth + leftBorder + rightBorder;
64
+ }
65
+ function getNodeHeight(node) {
66
+ const topBorder = px(node, "border-top-width");
67
+ const bottomBorder = px(node, "border-bottom-width");
68
+ return node.clientHeight + topBorder + bottomBorder;
69
+ }
70
+ function getImageSize(targetNode, options = {}) {
71
+ const width = options.width || getNodeWidth(targetNode);
72
+ const height = options.height || getNodeHeight(targetNode);
73
+ return { width, height };
74
+ }
75
+ function getPixelRatio() {
76
+ let ratio;
77
+ let FINAL_PROCESS;
78
+ try {
79
+ FINAL_PROCESS = process;
80
+ } catch (e) {
81
+ }
82
+ const val = FINAL_PROCESS && FINAL_PROCESS.env ? FINAL_PROCESS.env.devicePixelRatio : null;
83
+ if (val) {
84
+ ratio = parseInt(val, 10);
85
+ if (Number.isNaN(ratio)) {
86
+ ratio = 1;
87
+ }
88
+ }
89
+ return ratio || window.devicePixelRatio || 1;
90
+ }
91
+ var canvasDimensionLimit = 16384;
92
+ function checkCanvasDimensions(canvas) {
93
+ if (canvas.width > canvasDimensionLimit || canvas.height > canvasDimensionLimit) {
94
+ if (canvas.width > canvasDimensionLimit && canvas.height > canvasDimensionLimit) {
95
+ if (canvas.width > canvas.height) {
96
+ canvas.height *= canvasDimensionLimit / canvas.width;
97
+ canvas.width = canvasDimensionLimit;
98
+ } else {
99
+ canvas.width *= canvasDimensionLimit / canvas.height;
100
+ canvas.height = canvasDimensionLimit;
101
+ }
102
+ } else if (canvas.width > canvasDimensionLimit) {
103
+ canvas.height *= canvasDimensionLimit / canvas.width;
104
+ canvas.width = canvasDimensionLimit;
105
+ } else {
106
+ canvas.width *= canvasDimensionLimit / canvas.height;
107
+ canvas.height = canvasDimensionLimit;
108
+ }
109
+ }
110
+ }
111
+ function createImage(url) {
112
+ return new Promise((resolve, reject) => {
113
+ const img = new Image();
114
+ img.onload = () => {
115
+ img.decode().then(() => {
116
+ requestAnimationFrame(() => resolve(img));
117
+ });
118
+ };
119
+ img.onerror = reject;
120
+ img.crossOrigin = "anonymous";
121
+ img.decoding = "async";
122
+ img.src = url;
123
+ });
124
+ }
125
+ async function svgToDataURL(svg) {
126
+ return Promise.resolve().then(() => new XMLSerializer().serializeToString(svg)).then(encodeURIComponent).then((html) => `data:image/svg+xml;charset=utf-8,${html}`);
127
+ }
128
+ async function nodeToDataURL(node, width, height) {
129
+ const xmlns = "http://www.w3.org/2000/svg";
130
+ const svg = document.createElementNS(xmlns, "svg");
131
+ const foreignObject = document.createElementNS(xmlns, "foreignObject");
132
+ svg.setAttribute("width", `${width}`);
133
+ svg.setAttribute("height", `${height}`);
134
+ svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
135
+ foreignObject.setAttribute("width", "100%");
136
+ foreignObject.setAttribute("height", "100%");
137
+ foreignObject.setAttribute("x", "0");
138
+ foreignObject.setAttribute("y", "0");
139
+ foreignObject.setAttribute("externalResourcesRequired", "true");
140
+ svg.appendChild(foreignObject);
141
+ foreignObject.appendChild(node);
142
+ return svgToDataURL(svg);
143
+ }
144
+ var isInstanceOfElement = (node, instance) => {
145
+ if (node instanceof instance)
146
+ return true;
147
+ const nodePrototype = Object.getPrototypeOf(node);
148
+ if (nodePrototype === null)
149
+ return false;
150
+ return nodePrototype.constructor.name === instance.name || isInstanceOfElement(nodePrototype, instance);
151
+ };
152
+
153
+ // node_modules/html-to-image/es/clone-pseudos.js
154
+ function formatCSSText(style) {
155
+ const content = style.getPropertyValue("content");
156
+ return `${style.cssText} content: '${content.replace(/'|"/g, "")}';`;
157
+ }
158
+ function formatCSSProperties(style, options) {
159
+ return getStyleProperties(options).map((name) => {
160
+ const value = style.getPropertyValue(name);
161
+ const priority = style.getPropertyPriority(name);
162
+ return `${name}: ${value}${priority ? " !important" : ""};`;
163
+ }).join(" ");
164
+ }
165
+ function getPseudoElementStyle(className, pseudo, style, options) {
166
+ const selector = `.${className}:${pseudo}`;
167
+ const cssText = style.cssText ? formatCSSText(style) : formatCSSProperties(style, options);
168
+ return document.createTextNode(`${selector}{${cssText}}`);
169
+ }
170
+ function clonePseudoElement(nativeNode, clonedNode, pseudo, options) {
171
+ const style = window.getComputedStyle(nativeNode, pseudo);
172
+ const content = style.getPropertyValue("content");
173
+ if (content === "" || content === "none") {
174
+ return;
175
+ }
176
+ const className = uuid();
177
+ try {
178
+ clonedNode.className = `${clonedNode.className} ${className}`;
179
+ } catch (err) {
180
+ return;
181
+ }
182
+ const styleElement = document.createElement("style");
183
+ styleElement.appendChild(getPseudoElementStyle(className, pseudo, style, options));
184
+ clonedNode.appendChild(styleElement);
185
+ }
186
+ function clonePseudoElements(nativeNode, clonedNode, options) {
187
+ clonePseudoElement(nativeNode, clonedNode, ":before", options);
188
+ clonePseudoElement(nativeNode, clonedNode, ":after", options);
189
+ }
190
+
191
+ // node_modules/html-to-image/es/mimes.js
192
+ var WOFF = "application/font-woff";
193
+ var JPEG = "image/jpeg";
194
+ var mimes = {
195
+ woff: WOFF,
196
+ woff2: WOFF,
197
+ ttf: "application/font-truetype",
198
+ eot: "application/vnd.ms-fontobject",
199
+ png: "image/png",
200
+ jpg: JPEG,
201
+ jpeg: JPEG,
202
+ gif: "image/gif",
203
+ tiff: "image/tiff",
204
+ svg: "image/svg+xml",
205
+ webp: "image/webp"
206
+ };
207
+ function getExtension(url) {
208
+ const match = /\.([^./]*?)$/g.exec(url);
209
+ return match ? match[1] : "";
210
+ }
211
+ function getMimeType(url) {
212
+ const extension = getExtension(url).toLowerCase();
213
+ return mimes[extension] || "";
214
+ }
215
+
216
+ // node_modules/html-to-image/es/dataurl.js
217
+ function getContentFromDataUrl(dataURL) {
218
+ return dataURL.split(/,/)[1];
219
+ }
220
+ function isDataUrl(url) {
221
+ return url.search(/^(data:)/) !== -1;
222
+ }
223
+ function makeDataUrl(content, mimeType) {
224
+ return `data:${mimeType};base64,${content}`;
225
+ }
226
+ async function fetchAsDataURL(url, init, process2) {
227
+ const res = await fetch(url, init);
228
+ if (res.status === 404) {
229
+ throw new Error(`Resource "${res.url}" not found`);
230
+ }
231
+ const blob = await res.blob();
232
+ return new Promise((resolve, reject) => {
233
+ const reader = new FileReader();
234
+ reader.onerror = reject;
235
+ reader.onloadend = () => {
236
+ try {
237
+ resolve(process2({ res, result: reader.result }));
238
+ } catch (error) {
239
+ reject(error);
240
+ }
241
+ };
242
+ reader.readAsDataURL(blob);
243
+ });
244
+ }
245
+ var cache = {};
246
+ function getCacheKey(url, contentType, includeQueryParams) {
247
+ let key = url.replace(/\?.*/, "");
248
+ if (includeQueryParams) {
249
+ key = url;
250
+ }
251
+ if (/ttf|otf|eot|woff2?/i.test(key)) {
252
+ key = key.replace(/.*\//, "");
253
+ }
254
+ return contentType ? `[${contentType}]${key}` : key;
255
+ }
256
+ async function resourceToDataURL(resourceUrl, contentType, options) {
257
+ const cacheKey = getCacheKey(resourceUrl, contentType, options.includeQueryParams);
258
+ if (cache[cacheKey] != null) {
259
+ return cache[cacheKey];
260
+ }
261
+ if (options.cacheBust) {
262
+ resourceUrl += (/\?/.test(resourceUrl) ? "&" : "?") + (/* @__PURE__ */ new Date()).getTime();
263
+ }
264
+ let dataURL;
265
+ try {
266
+ const content = await fetchAsDataURL(resourceUrl, options.fetchRequestInit, ({ res, result }) => {
267
+ if (!contentType) {
268
+ contentType = res.headers.get("Content-Type") || "";
269
+ }
270
+ return getContentFromDataUrl(result);
271
+ });
272
+ dataURL = makeDataUrl(content, contentType);
273
+ } catch (error) {
274
+ dataURL = options.imagePlaceholder || "";
275
+ let msg = `Failed to fetch resource: ${resourceUrl}`;
276
+ if (error) {
277
+ msg = typeof error === "string" ? error : error.message;
278
+ }
279
+ if (msg) {
280
+ console.warn(msg);
281
+ }
282
+ }
283
+ cache[cacheKey] = dataURL;
284
+ return dataURL;
285
+ }
286
+
287
+ // node_modules/html-to-image/es/clone-node.js
288
+ async function cloneCanvasElement(canvas) {
289
+ const dataURL = canvas.toDataURL();
290
+ if (dataURL === "data:,") {
291
+ return canvas.cloneNode(false);
292
+ }
293
+ return createImage(dataURL);
294
+ }
295
+ async function cloneVideoElement(video, options) {
296
+ if (video.currentSrc) {
297
+ const canvas = document.createElement("canvas");
298
+ const ctx = canvas.getContext("2d");
299
+ canvas.width = video.clientWidth;
300
+ canvas.height = video.clientHeight;
301
+ ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
302
+ const dataURL2 = canvas.toDataURL();
303
+ return createImage(dataURL2);
304
+ }
305
+ const poster = video.poster;
306
+ const contentType = getMimeType(poster);
307
+ const dataURL = await resourceToDataURL(poster, contentType, options);
308
+ return createImage(dataURL);
309
+ }
310
+ async function cloneIFrameElement(iframe, options) {
311
+ var _a;
312
+ try {
313
+ if ((_a = iframe === null || iframe === void 0 ? void 0 : iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.body) {
314
+ return await cloneNode(iframe.contentDocument.body, options, true);
315
+ }
316
+ } catch (_b) {
317
+ }
318
+ return iframe.cloneNode(false);
319
+ }
320
+ async function cloneSingleNode(node, options) {
321
+ if (isInstanceOfElement(node, HTMLCanvasElement)) {
322
+ return cloneCanvasElement(node);
323
+ }
324
+ if (isInstanceOfElement(node, HTMLVideoElement)) {
325
+ return cloneVideoElement(node, options);
326
+ }
327
+ if (isInstanceOfElement(node, HTMLIFrameElement)) {
328
+ return cloneIFrameElement(node, options);
329
+ }
330
+ return node.cloneNode(isSVGElement(node));
331
+ }
332
+ var isSlotElement = (node) => node.tagName != null && node.tagName.toUpperCase() === "SLOT";
333
+ var isSVGElement = (node) => node.tagName != null && node.tagName.toUpperCase() === "SVG";
334
+ async function cloneChildren(nativeNode, clonedNode, options) {
335
+ var _a, _b;
336
+ if (isSVGElement(clonedNode)) {
337
+ return clonedNode;
338
+ }
339
+ let children = [];
340
+ if (isSlotElement(nativeNode) && nativeNode.assignedNodes) {
341
+ children = toArray(nativeNode.assignedNodes());
342
+ } else if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && ((_a = nativeNode.contentDocument) === null || _a === void 0 ? void 0 : _a.body)) {
343
+ children = toArray(nativeNode.contentDocument.body.childNodes);
344
+ } else {
345
+ children = toArray(((_b = nativeNode.shadowRoot) !== null && _b !== void 0 ? _b : nativeNode).childNodes);
346
+ }
347
+ if (children.length === 0 || isInstanceOfElement(nativeNode, HTMLVideoElement)) {
348
+ return clonedNode;
349
+ }
350
+ await children.reduce((deferred, child) => deferred.then(() => cloneNode(child, options)).then((clonedChild) => {
351
+ if (clonedChild) {
352
+ clonedNode.appendChild(clonedChild);
353
+ }
354
+ }), Promise.resolve());
355
+ return clonedNode;
356
+ }
357
+ function cloneCSSStyle(nativeNode, clonedNode, options) {
358
+ const targetStyle = clonedNode.style;
359
+ if (!targetStyle) {
360
+ return;
361
+ }
362
+ const sourceStyle = window.getComputedStyle(nativeNode);
363
+ if (sourceStyle.cssText) {
364
+ targetStyle.cssText = sourceStyle.cssText;
365
+ targetStyle.transformOrigin = sourceStyle.transformOrigin;
366
+ } else {
367
+ getStyleProperties(options).forEach((name) => {
368
+ let value = sourceStyle.getPropertyValue(name);
369
+ if (name === "font-size" && value.endsWith("px")) {
370
+ const reducedFont = Math.floor(parseFloat(value.substring(0, value.length - 2))) - 0.1;
371
+ value = `${reducedFont}px`;
372
+ }
373
+ if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && name === "display" && value === "inline") {
374
+ value = "block";
375
+ }
376
+ if (name === "d" && clonedNode.getAttribute("d")) {
377
+ value = `path(${clonedNode.getAttribute("d")})`;
378
+ }
379
+ targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));
380
+ });
381
+ }
382
+ }
383
+ function cloneInputValue(nativeNode, clonedNode) {
384
+ if (isInstanceOfElement(nativeNode, HTMLTextAreaElement)) {
385
+ clonedNode.innerHTML = nativeNode.value;
386
+ }
387
+ if (isInstanceOfElement(nativeNode, HTMLInputElement)) {
388
+ clonedNode.setAttribute("value", nativeNode.value);
389
+ }
390
+ }
391
+ function cloneSelectValue(nativeNode, clonedNode) {
392
+ if (isInstanceOfElement(nativeNode, HTMLSelectElement)) {
393
+ const clonedSelect = clonedNode;
394
+ const selectedOption = Array.from(clonedSelect.children).find((child) => nativeNode.value === child.getAttribute("value"));
395
+ if (selectedOption) {
396
+ selectedOption.setAttribute("selected", "");
397
+ }
398
+ }
399
+ }
400
+ function decorate(nativeNode, clonedNode, options) {
401
+ if (isInstanceOfElement(clonedNode, Element)) {
402
+ cloneCSSStyle(nativeNode, clonedNode, options);
403
+ clonePseudoElements(nativeNode, clonedNode, options);
404
+ cloneInputValue(nativeNode, clonedNode);
405
+ cloneSelectValue(nativeNode, clonedNode);
406
+ }
407
+ return clonedNode;
408
+ }
409
+ async function ensureSVGSymbols(clone, options) {
410
+ const uses = clone.querySelectorAll ? clone.querySelectorAll("use") : [];
411
+ if (uses.length === 0) {
412
+ return clone;
413
+ }
414
+ const processedDefs = {};
415
+ for (let i = 0; i < uses.length; i++) {
416
+ const use = uses[i];
417
+ const id = use.getAttribute("xlink:href");
418
+ if (id) {
419
+ const exist = clone.querySelector(id);
420
+ const definition = document.querySelector(id);
421
+ if (!exist && definition && !processedDefs[id]) {
422
+ processedDefs[id] = await cloneNode(definition, options, true);
423
+ }
424
+ }
425
+ }
426
+ const nodes = Object.values(processedDefs);
427
+ if (nodes.length) {
428
+ const ns = "http://www.w3.org/1999/xhtml";
429
+ const svg = document.createElementNS(ns, "svg");
430
+ svg.setAttribute("xmlns", ns);
431
+ svg.style.position = "absolute";
432
+ svg.style.width = "0";
433
+ svg.style.height = "0";
434
+ svg.style.overflow = "hidden";
435
+ svg.style.display = "none";
436
+ const defs = document.createElementNS(ns, "defs");
437
+ svg.appendChild(defs);
438
+ for (let i = 0; i < nodes.length; i++) {
439
+ defs.appendChild(nodes[i]);
440
+ }
441
+ clone.appendChild(svg);
442
+ }
443
+ return clone;
444
+ }
445
+ async function cloneNode(node, options, isRoot) {
446
+ if (!isRoot && options.filter && !options.filter(node)) {
447
+ return null;
448
+ }
449
+ return Promise.resolve(node).then((clonedNode) => cloneSingleNode(clonedNode, options)).then((clonedNode) => cloneChildren(node, clonedNode, options)).then((clonedNode) => decorate(node, clonedNode, options)).then((clonedNode) => ensureSVGSymbols(clonedNode, options));
450
+ }
451
+
452
+ // node_modules/html-to-image/es/embed-resources.js
453
+ var URL_REGEX = /url\((['"]?)([^'"]+?)\1\)/g;
454
+ var URL_WITH_FORMAT_REGEX = /url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g;
455
+ var FONT_SRC_REGEX = /src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;
456
+ function toRegex(url) {
457
+ const escaped = url.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
458
+ return new RegExp(`(url\\(['"]?)(${escaped})(['"]?\\))`, "g");
459
+ }
460
+ function parseURLs(cssText) {
461
+ const urls = [];
462
+ cssText.replace(URL_REGEX, (raw, quotation, url) => {
463
+ urls.push(url);
464
+ return raw;
465
+ });
466
+ return urls.filter((url) => !isDataUrl(url));
467
+ }
468
+ async function embed(cssText, resourceURL, baseURL, options, getContentFromUrl) {
469
+ try {
470
+ const resolvedURL = baseURL ? resolveUrl(resourceURL, baseURL) : resourceURL;
471
+ const contentType = getMimeType(resourceURL);
472
+ let dataURL;
473
+ if (getContentFromUrl) {
474
+ const content = await getContentFromUrl(resolvedURL);
475
+ dataURL = makeDataUrl(content, contentType);
476
+ } else {
477
+ dataURL = await resourceToDataURL(resolvedURL, contentType, options);
478
+ }
479
+ return cssText.replace(toRegex(resourceURL), `$1${dataURL}$3`);
480
+ } catch (error) {
481
+ }
482
+ return cssText;
483
+ }
484
+ function filterPreferredFontFormat(str, { preferredFontFormat }) {
485
+ return !preferredFontFormat ? str : str.replace(FONT_SRC_REGEX, (match) => {
486
+ while (true) {
487
+ const [src, , format] = URL_WITH_FORMAT_REGEX.exec(match) || [];
488
+ if (!format) {
489
+ return "";
490
+ }
491
+ if (format === preferredFontFormat) {
492
+ return `src: ${src};`;
493
+ }
494
+ }
495
+ });
496
+ }
497
+ function shouldEmbed(url) {
498
+ return url.search(URL_REGEX) !== -1;
499
+ }
500
+ async function embedResources(cssText, baseUrl, options) {
501
+ if (!shouldEmbed(cssText)) {
502
+ return cssText;
503
+ }
504
+ const filteredCSSText = filterPreferredFontFormat(cssText, options);
505
+ const urls = parseURLs(filteredCSSText);
506
+ return urls.reduce((deferred, url) => deferred.then((css) => embed(css, url, baseUrl, options)), Promise.resolve(filteredCSSText));
507
+ }
508
+
509
+ // node_modules/html-to-image/es/embed-images.js
510
+ async function embedProp(propName, node, options) {
511
+ var _a;
512
+ const propValue = (_a = node.style) === null || _a === void 0 ? void 0 : _a.getPropertyValue(propName);
513
+ if (propValue) {
514
+ const cssString = await embedResources(propValue, null, options);
515
+ node.style.setProperty(propName, cssString, node.style.getPropertyPriority(propName));
516
+ return true;
517
+ }
518
+ return false;
519
+ }
520
+ async function embedBackground(clonedNode, options) {
521
+ ;
522
+ await embedProp("background", clonedNode, options) || await embedProp("background-image", clonedNode, options);
523
+ await embedProp("mask", clonedNode, options) || await embedProp("-webkit-mask", clonedNode, options) || await embedProp("mask-image", clonedNode, options) || await embedProp("-webkit-mask-image", clonedNode, options);
524
+ }
525
+ async function embedImageNode(clonedNode, options) {
526
+ const isImageElement = isInstanceOfElement(clonedNode, HTMLImageElement);
527
+ if (!(isImageElement && !isDataUrl(clonedNode.src)) && !(isInstanceOfElement(clonedNode, SVGImageElement) && !isDataUrl(clonedNode.href.baseVal))) {
528
+ return;
529
+ }
530
+ const url = isImageElement ? clonedNode.src : clonedNode.href.baseVal;
531
+ const dataURL = await resourceToDataURL(url, getMimeType(url), options);
532
+ await new Promise((resolve, reject) => {
533
+ clonedNode.onload = resolve;
534
+ clonedNode.onerror = options.onImageErrorHandler ? (...attributes) => {
535
+ try {
536
+ resolve(options.onImageErrorHandler(...attributes));
537
+ } catch (error) {
538
+ reject(error);
539
+ }
540
+ } : reject;
541
+ const image = clonedNode;
542
+ if (image.decode) {
543
+ image.decode = resolve;
544
+ }
545
+ if (image.loading === "lazy") {
546
+ image.loading = "eager";
547
+ }
548
+ if (isImageElement) {
549
+ clonedNode.srcset = "";
550
+ clonedNode.src = dataURL;
551
+ } else {
552
+ clonedNode.href.baseVal = dataURL;
553
+ }
554
+ });
555
+ }
556
+ async function embedChildren(clonedNode, options) {
557
+ const children = toArray(clonedNode.childNodes);
558
+ const deferreds = children.map((child) => embedImages(child, options));
559
+ await Promise.all(deferreds).then(() => clonedNode);
560
+ }
561
+ async function embedImages(clonedNode, options) {
562
+ if (isInstanceOfElement(clonedNode, Element)) {
563
+ await embedBackground(clonedNode, options);
564
+ await embedImageNode(clonedNode, options);
565
+ await embedChildren(clonedNode, options);
566
+ }
567
+ }
568
+
569
+ // node_modules/html-to-image/es/apply-style.js
570
+ function applyStyle(node, options) {
571
+ const { style } = node;
572
+ if (options.backgroundColor) {
573
+ style.backgroundColor = options.backgroundColor;
574
+ }
575
+ if (options.width) {
576
+ style.width = `${options.width}px`;
577
+ }
578
+ if (options.height) {
579
+ style.height = `${options.height}px`;
580
+ }
581
+ const manual = options.style;
582
+ if (manual != null) {
583
+ Object.keys(manual).forEach((key) => {
584
+ style[key] = manual[key];
585
+ });
586
+ }
587
+ return node;
588
+ }
589
+
590
+ // node_modules/html-to-image/es/embed-webfonts.js
591
+ var cssFetchCache = {};
592
+ async function fetchCSS(url) {
593
+ let cache2 = cssFetchCache[url];
594
+ if (cache2 != null) {
595
+ return cache2;
596
+ }
597
+ const res = await fetch(url);
598
+ const cssText = await res.text();
599
+ cache2 = { url, cssText };
600
+ cssFetchCache[url] = cache2;
601
+ return cache2;
602
+ }
603
+ async function embedFonts(data, options) {
604
+ let cssText = data.cssText;
605
+ const regexUrl = /url\(["']?([^"')]+)["']?\)/g;
606
+ const fontLocs = cssText.match(/url\([^)]+\)/g) || [];
607
+ const loadFonts = fontLocs.map(async (loc) => {
608
+ let url = loc.replace(regexUrl, "$1");
609
+ if (!url.startsWith("https://")) {
610
+ url = new URL(url, data.url).href;
611
+ }
612
+ return fetchAsDataURL(url, options.fetchRequestInit, ({ result }) => {
613
+ cssText = cssText.replace(loc, `url(${result})`);
614
+ return [loc, result];
615
+ });
616
+ });
617
+ return Promise.all(loadFonts).then(() => cssText);
618
+ }
619
+ function parseCSS(source) {
620
+ if (source == null) {
621
+ return [];
622
+ }
623
+ const result = [];
624
+ const commentsRegex = /(\/\*[\s\S]*?\*\/)/gi;
625
+ let cssText = source.replace(commentsRegex, "");
626
+ const keyframesRegex = new RegExp("((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})", "gi");
627
+ while (true) {
628
+ const matches = keyframesRegex.exec(cssText);
629
+ if (matches === null) {
630
+ break;
631
+ }
632
+ result.push(matches[0]);
633
+ }
634
+ cssText = cssText.replace(keyframesRegex, "");
635
+ const importRegex = /@import[\s\S]*?url\([^)]*\)[\s\S]*?;/gi;
636
+ const combinedCSSRegex = "((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})";
637
+ const unifiedRegex = new RegExp(combinedCSSRegex, "gi");
638
+ while (true) {
639
+ let matches = importRegex.exec(cssText);
640
+ if (matches === null) {
641
+ matches = unifiedRegex.exec(cssText);
642
+ if (matches === null) {
643
+ break;
644
+ } else {
645
+ importRegex.lastIndex = unifiedRegex.lastIndex;
646
+ }
647
+ } else {
648
+ unifiedRegex.lastIndex = importRegex.lastIndex;
649
+ }
650
+ result.push(matches[0]);
651
+ }
652
+ return result;
653
+ }
654
+ async function getCSSRules(styleSheets, options) {
655
+ const ret = [];
656
+ const deferreds = [];
657
+ styleSheets.forEach((sheet) => {
658
+ if ("cssRules" in sheet) {
659
+ try {
660
+ toArray(sheet.cssRules || []).forEach((item, index) => {
661
+ if (item.type === CSSRule.IMPORT_RULE) {
662
+ let importIndex = index + 1;
663
+ const url = item.href;
664
+ const deferred = fetchCSS(url).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
665
+ try {
666
+ sheet.insertRule(rule, rule.startsWith("@import") ? importIndex += 1 : sheet.cssRules.length);
667
+ } catch (error) {
668
+ console.error("Error inserting rule from remote css", {
669
+ rule,
670
+ error
671
+ });
672
+ }
673
+ })).catch((e) => {
674
+ console.error("Error loading remote css", e.toString());
675
+ });
676
+ deferreds.push(deferred);
677
+ }
678
+ });
679
+ } catch (e) {
680
+ const inline = styleSheets.find((a) => a.href == null) || document.styleSheets[0];
681
+ if (sheet.href != null) {
682
+ deferreds.push(fetchCSS(sheet.href).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
683
+ inline.insertRule(rule, inline.cssRules.length);
684
+ })).catch((err) => {
685
+ console.error("Error loading remote stylesheet", err);
686
+ }));
687
+ }
688
+ console.error("Error inlining remote css file", e);
689
+ }
690
+ }
691
+ });
692
+ return Promise.all(deferreds).then(() => {
693
+ styleSheets.forEach((sheet) => {
694
+ if ("cssRules" in sheet) {
695
+ try {
696
+ toArray(sheet.cssRules || []).forEach((item) => {
697
+ ret.push(item);
698
+ });
699
+ } catch (e) {
700
+ console.error(`Error while reading CSS rules from ${sheet.href}`, e);
701
+ }
702
+ }
703
+ });
704
+ return ret;
705
+ });
706
+ }
707
+ function getWebFontRules(cssRules) {
708
+ return cssRules.filter((rule) => rule.type === CSSRule.FONT_FACE_RULE).filter((rule) => shouldEmbed(rule.style.getPropertyValue("src")));
709
+ }
710
+ async function parseWebFontRules(node, options) {
711
+ if (node.ownerDocument == null) {
712
+ throw new Error("Provided element is not within a Document");
713
+ }
714
+ const styleSheets = toArray(node.ownerDocument.styleSheets);
715
+ const cssRules = await getCSSRules(styleSheets, options);
716
+ return getWebFontRules(cssRules);
717
+ }
718
+ function normalizeFontFamily(font) {
719
+ return font.trim().replace(/["']/g, "");
720
+ }
721
+ function getUsedFonts(node) {
722
+ const fonts = /* @__PURE__ */ new Set();
723
+ function traverse(node2) {
724
+ const fontFamily = node2.style.fontFamily || getComputedStyle(node2).fontFamily;
725
+ fontFamily.split(",").forEach((font) => {
726
+ fonts.add(normalizeFontFamily(font));
727
+ });
728
+ Array.from(node2.children).forEach((child) => {
729
+ if (child instanceof HTMLElement) {
730
+ traverse(child);
731
+ }
732
+ });
733
+ }
734
+ traverse(node);
735
+ return fonts;
736
+ }
737
+ async function getWebFontCSS(node, options) {
738
+ const rules = await parseWebFontRules(node, options);
739
+ const usedFonts = getUsedFonts(node);
740
+ const cssTexts = await Promise.all(rules.filter((rule) => usedFonts.has(normalizeFontFamily(rule.style.fontFamily))).map((rule) => {
741
+ const baseUrl = rule.parentStyleSheet ? rule.parentStyleSheet.href : null;
742
+ return embedResources(rule.cssText, baseUrl, options);
743
+ }));
744
+ return cssTexts.join("\n");
745
+ }
746
+ async function embedWebFonts(clonedNode, options) {
747
+ const cssText = options.fontEmbedCSS != null ? options.fontEmbedCSS : options.skipFonts ? null : await getWebFontCSS(clonedNode, options);
748
+ if (cssText) {
749
+ const styleNode = document.createElement("style");
750
+ const sytleContent = document.createTextNode(cssText);
751
+ styleNode.appendChild(sytleContent);
752
+ if (clonedNode.firstChild) {
753
+ clonedNode.insertBefore(styleNode, clonedNode.firstChild);
754
+ } else {
755
+ clonedNode.appendChild(styleNode);
756
+ }
757
+ }
758
+ }
759
+
760
+ // node_modules/html-to-image/es/index.js
761
+ async function toSvg(node, options = {}) {
762
+ const { width, height } = getImageSize(node, options);
763
+ const clonedNode = await cloneNode(node, options, true);
764
+ await embedWebFonts(clonedNode, options);
765
+ await embedImages(clonedNode, options);
766
+ applyStyle(clonedNode, options);
767
+ const datauri = await nodeToDataURL(clonedNode, width, height);
768
+ return datauri;
769
+ }
770
+ async function toCanvas(node, options = {}) {
771
+ const { width, height } = getImageSize(node, options);
772
+ const svg = await toSvg(node, options);
773
+ const img = await createImage(svg);
774
+ const canvas = document.createElement("canvas");
775
+ const context = canvas.getContext("2d");
776
+ const ratio = options.pixelRatio || getPixelRatio();
777
+ const canvasWidth = options.canvasWidth || width;
778
+ const canvasHeight = options.canvasHeight || height;
779
+ canvas.width = canvasWidth * ratio;
780
+ canvas.height = canvasHeight * ratio;
781
+ if (!options.skipAutoScale) {
782
+ checkCanvasDimensions(canvas);
783
+ }
784
+ canvas.style.width = `${canvasWidth}`;
785
+ canvas.style.height = `${canvasHeight}`;
786
+ if (options.backgroundColor) {
787
+ context.fillStyle = options.backgroundColor;
788
+ context.fillRect(0, 0, canvas.width, canvas.height);
789
+ }
790
+ context.drawImage(img, 0, 0, canvas.width, canvas.height);
791
+ return canvas;
792
+ }
793
+ async function toPng(node, options = {}) {
794
+ const canvas = await toCanvas(node, options);
795
+ return canvas.toDataURL();
796
+ }
797
+
798
+ // src/runtime/dom.ts
799
+ var overlayAttribute = "data-wand-ui";
800
+ function pickElementAtPoint(x, y) {
801
+ for (const element of document.elementsFromPoint(x, y)) {
802
+ if (!(element instanceof HTMLElement)) continue;
803
+ if (element.closest(`[${overlayAttribute}]`)) continue;
804
+ return element;
805
+ }
806
+ return null;
807
+ }
808
+ function selectorFor(element) {
809
+ if (element.id) return `#${cssEscape(element.id)}`;
810
+ const segments = [];
811
+ for (let current = element; current && current !== document.body; current = current.parentElement) {
812
+ let segment = current.tagName.toLowerCase();
813
+ const siblings = current.parentElement ? Array.from(current.parentElement.children).filter((child) => child.tagName === current.tagName) : [];
814
+ if (siblings.length > 1) {
815
+ segment += `:nth-of-type(${siblings.indexOf(current) + 1})`;
816
+ }
817
+ segments.unshift(segment);
818
+ }
819
+ return segments.join(" > ");
820
+ }
821
+ function domPathFor(element) {
822
+ const path = [];
823
+ for (let current = element; current; current = current.parentElement) {
824
+ path.unshift(selectorSegment(current));
825
+ if (current === document.documentElement) break;
826
+ }
827
+ return path;
828
+ }
829
+ function attributesFor(element) {
830
+ return Object.fromEntries(
831
+ Array.from(element.attributes).map((attribute) => [attribute.name, attribute.value])
832
+ );
833
+ }
834
+ function htmlFor(element) {
835
+ const clone = element.cloneNode(true);
836
+ clone.querySelectorAll("[data-wand-root]").forEach((node) => node.remove());
837
+ return clone.outerHTML;
838
+ }
839
+ function reactOwnerHintsFor(element) {
840
+ const fiberKey = Object.keys(element).find((key) => key.startsWith("__reactFiber$"));
841
+ const fiber = fiberKey ? element[fiberKey] : null;
842
+ const hints = [];
843
+ let cursor = asFiber(fiber);
844
+ while (cursor && hints.length < 6) {
845
+ const name = typeof cursor.type === "string" ? cursor.type : cursor.type?.displayName ?? cursor.type?.name ?? null;
846
+ const source = cursor._debugSource ? `${cursor._debugSource.fileName}:${cursor._debugSource.lineNumber}` : null;
847
+ if (name || source) {
848
+ hints.push({
849
+ name,
850
+ key: cursor.key ?? null,
851
+ source
852
+ });
853
+ }
854
+ cursor = cursor.return ?? null;
855
+ }
856
+ return hints;
857
+ }
858
+ function selectorSegment(element) {
859
+ if (element.id) return `#${cssEscape(element.id)}`;
860
+ return element.tagName.toLowerCase();
861
+ }
862
+ function cssEscape(value) {
863
+ return globalThis.CSS?.escape ? globalThis.CSS.escape(value) : value.replace(/[^a-zA-Z0-9_-]/g, "\\$&");
864
+ }
865
+ function asFiber(value) {
866
+ return value && typeof value === "object" ? value : null;
867
+ }
868
+
869
+ // src/runtime/capture.ts
870
+ function viewportCaptureOptions(metrics) {
871
+ const documentWidth = Math.max(metrics.documentWidth, metrics.viewportWidth);
872
+ const documentHeight = Math.max(metrics.documentHeight, metrics.viewportHeight);
873
+ return {
874
+ cacheBust: true,
875
+ width: metrics.viewportWidth,
876
+ height: metrics.viewportHeight,
877
+ canvasWidth: metrics.viewportWidth,
878
+ canvasHeight: metrics.viewportHeight,
879
+ style: {
880
+ width: `${documentWidth}px`,
881
+ height: `${documentHeight}px`,
882
+ transform: `translate(${-metrics.scrollX}px, ${-metrics.scrollY}px)`
883
+ }
884
+ };
885
+ }
886
+
887
+ // src/runtime/panel.ts
888
+ function setComposerSending(panel, sending) {
889
+ const sendButton = panel.querySelector('[data-action="send"]');
890
+ const queueButton = panel.querySelector('[data-action="queue"]');
891
+ if (!sendButton || !queueButton) return;
892
+ sendButton.disabled = sending;
893
+ queueButton.disabled = sending;
894
+ sendButton.classList.toggle("wand-sending", sending);
895
+ sendButton.innerHTML = sending ? `<span class="wand-spinner" aria-hidden="true"></span><span>Sending...</span>` : "Send to agent";
896
+ }
897
+ function formatJsonPreview(value, maxLength = 180) {
898
+ let json;
899
+ try {
900
+ json = JSON.stringify(value) ?? "null";
901
+ } catch {
902
+ json = "[unserializable]";
903
+ }
904
+ if (json.length <= maxLength) return json;
905
+ if (maxLength <= 3) return ".".repeat(maxLength);
906
+ return `${json.slice(0, maxLength - 3)}...`;
907
+ }
908
+
909
+ // src/runtime/styles.ts
910
+ var styles = `
911
+ .wand-launcher,
912
+ .wand-panel,
913
+ .wand-frame,
914
+ .wand-toast {
915
+ box-sizing: border-box;
916
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
917
+ }
918
+ .wand-launcher {
919
+ position: fixed;
920
+ right: 18px;
921
+ bottom: 18px;
922
+ z-index: 2147483647;
923
+ width: 42px;
924
+ height: 42px;
925
+ border: 1px solid rgba(17, 24, 39, 0.15);
926
+ border-radius: 999px;
927
+ background: rgba(17, 24, 39, 0.94);
928
+ color: white;
929
+ display: inline-flex;
930
+ align-items: center;
931
+ justify-content: center;
932
+ cursor: pointer;
933
+ box-shadow: 0 12px 28px rgba(0, 0, 0, 0.22);
934
+ }
935
+ .wand-launcher svg { width: 18px; height: 18px; }
936
+ .wand-frame {
937
+ position: fixed;
938
+ z-index: 2147483645;
939
+ pointer-events: none;
940
+ border: 2px solid #3c7ce6;
941
+ background: rgba(60, 124, 230, 0.08);
942
+ box-shadow: 0 0 0 1px rgba(60, 124, 230, 0.28);
943
+ transition: left 120ms ease-out, top 120ms ease-out, width 120ms ease-out, height 120ms ease-out;
944
+ }
945
+ .wand-panel {
946
+ position: fixed;
947
+ right: 18px;
948
+ bottom: 72px;
949
+ z-index: 2147483647;
950
+ width: min(360px, calc(100vw - 36px));
951
+ border: 1px solid rgba(17, 24, 39, 0.14);
952
+ border-radius: 8px;
953
+ background: rgba(255, 255, 255, 0.98);
954
+ color: #111827;
955
+ box-shadow: 0 18px 42px rgba(0, 0, 0, 0.18);
956
+ overflow: hidden;
957
+ }
958
+ .wand-panel-header,
959
+ .wand-panel-section {
960
+ padding: 12px;
961
+ }
962
+ .wand-panel-header {
963
+ display: flex;
964
+ align-items: center;
965
+ justify-content: space-between;
966
+ border-bottom: 1px solid rgba(17, 24, 39, 0.08);
967
+ font-size: 13px;
968
+ font-weight: 600;
969
+ }
970
+ .wand-panel textarea {
971
+ box-sizing: border-box;
972
+ width: 100%;
973
+ max-width: 100%;
974
+ min-height: 92px;
975
+ resize: vertical;
976
+ border: 1px solid rgba(17, 24, 39, 0.16);
977
+ border-radius: 6px;
978
+ padding: 9px 10px;
979
+ color: #111827;
980
+ background: white;
981
+ font: inherit;
982
+ }
983
+ .wand-actions {
984
+ display: flex;
985
+ gap: 8px;
986
+ margin-top: 10px;
987
+ }
988
+ .wand-agent-picker {
989
+ display: grid;
990
+ gap: 5px;
991
+ margin-top: 10px;
992
+ }
993
+ .wand-agent-picker select {
994
+ box-sizing: border-box;
995
+ width: 100%;
996
+ border: 1px solid rgba(17, 24, 39, 0.16);
997
+ border-radius: 6px;
998
+ padding: 8px 10px;
999
+ color: #111827;
1000
+ background: white;
1001
+ font: inherit;
1002
+ }
1003
+ .wand-agent-picker select:disabled {
1004
+ color: rgba(17, 24, 39, 0.42);
1005
+ background: #f8fafc;
1006
+ }
1007
+ .wand-actions button,
1008
+ .wand-queue-item button {
1009
+ border: 0;
1010
+ border-radius: 6px;
1011
+ padding: 8px 10px;
1012
+ font: inherit;
1013
+ cursor: pointer;
1014
+ }
1015
+ .wand-actions button:disabled {
1016
+ cursor: wait;
1017
+ opacity: 0.72;
1018
+ }
1019
+ .wand-actions button:first-child {
1020
+ background: #111827;
1021
+ color: white;
1022
+ }
1023
+ .wand-actions button:last-child,
1024
+ .wand-queue-item button {
1025
+ background: #eef2f7;
1026
+ color: #111827;
1027
+ }
1028
+ .wand-queue {
1029
+ border-top: 1px solid rgba(17, 24, 39, 0.08);
1030
+ }
1031
+ .wand-queue-item {
1032
+ display: grid;
1033
+ gap: 8px;
1034
+ padding: 10px 12px;
1035
+ border-top: 1px solid rgba(17, 24, 39, 0.06);
1036
+ font-size: 12px;
1037
+ }
1038
+ .wand-queue-item:first-child { border-top: 0; }
1039
+ .wand-queue-controls { display: flex; gap: 8px; }
1040
+ .wand-agent-run {
1041
+ background: rgba(60, 124, 230, 0.08);
1042
+ }
1043
+ .wand-run-header {
1044
+ display: flex;
1045
+ align-items: center;
1046
+ gap: 8px;
1047
+ }
1048
+ .wand-run-title {
1049
+ min-width: 0;
1050
+ flex: 1;
1051
+ overflow-wrap: anywhere;
1052
+ }
1053
+ .wand-run-header button {
1054
+ flex: none;
1055
+ }
1056
+ .wand-run-message {
1057
+ overflow: hidden;
1058
+ margin: 0;
1059
+ color: rgba(17, 24, 39, 0.76);
1060
+ font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1061
+ white-space: nowrap;
1062
+ text-overflow: ellipsis;
1063
+ }
1064
+ .wand-sending {
1065
+ display: inline-flex;
1066
+ align-items: center;
1067
+ gap: 7px;
1068
+ }
1069
+ .wand-spinner {
1070
+ width: 12px;
1071
+ height: 12px;
1072
+ border: 2px solid rgba(255, 255, 255, 0.38);
1073
+ border-top-color: currentColor;
1074
+ border-radius: 999px;
1075
+ animation: wand-spin 700ms linear infinite;
1076
+ }
1077
+ @keyframes wand-spin {
1078
+ to { transform: rotate(360deg); }
1079
+ }
1080
+ @media (prefers-reduced-motion: reduce) {
1081
+ .wand-spinner { animation: none; }
1082
+ }
1083
+ .wand-muted {
1084
+ color: rgba(17, 24, 39, 0.58);
1085
+ font-size: 12px;
1086
+ }
1087
+ .wand-toast {
1088
+ position: fixed;
1089
+ right: 18px;
1090
+ bottom: 72px;
1091
+ z-index: 2147483647;
1092
+ max-width: min(320px, calc(100vw - 36px));
1093
+ border-radius: 6px;
1094
+ background: rgba(17, 24, 39, 0.94);
1095
+ color: white;
1096
+ padding: 9px 11px;
1097
+ font-size: 12px;
1098
+ box-shadow: 0 12px 28px rgba(0, 0, 0, 0.22);
1099
+ }
1100
+ `;
1101
+
1102
+ // src/runtime/index.ts
1103
+ var defaultServerUrl = typeof process !== "undefined" && process.env.NEXT_PUBLIC_WAND_SERVER_URL ? process.env.NEXT_PUBLIC_WAND_SERVER_URL : "http://127.0.0.1:4711";
1104
+ function mountWand(options = {}) {
1105
+ if (typeof window === "undefined" || document.querySelector("[data-wand-root]")) return;
1106
+ const serverUrl2 = options.serverUrl ?? defaultServerUrl;
1107
+ const state = {
1108
+ active: false,
1109
+ selected: null,
1110
+ hovered: null,
1111
+ runs: [],
1112
+ queue: [],
1113
+ agentConfig: {
1114
+ availableAdapters: [],
1115
+ selectedAdapter: null
1116
+ },
1117
+ runsEventSource: null,
1118
+ composerFor: null,
1119
+ composerDraft: "",
1120
+ composerFocused: false,
1121
+ composerSelectionStart: null,
1122
+ composerSelectionEnd: null,
1123
+ composerSelectionDirection: null
1124
+ };
1125
+ const root = document.createElement("div");
1126
+ root.dataset.wandRoot = "true";
1127
+ root.dataset.wandUi = "true";
1128
+ const shadow = root.attachShadow({ mode: "open" });
1129
+ shadow.innerHTML = `<style>${styles}</style>`;
1130
+ document.body.append(root);
1131
+ const frame = document.createElement("div");
1132
+ frame.className = "wand-frame";
1133
+ frame.hidden = true;
1134
+ frame.dataset.wandUi = "true";
1135
+ const launcher = document.createElement("button");
1136
+ launcher.type = "button";
1137
+ launcher.className = "wand-launcher";
1138
+ launcher.dataset.wandUi = "true";
1139
+ launcher.setAttribute("aria-label", "Toggle Wand inspector");
1140
+ launcher.innerHTML = inspectIcon();
1141
+ shadow.append(frame, launcher);
1142
+ launcher.addEventListener("click", async () => {
1143
+ state.active = !state.active;
1144
+ if (!state.active) {
1145
+ state.selected = null;
1146
+ state.hovered = null;
1147
+ resetComposerState(state, null);
1148
+ frame.hidden = true;
1149
+ stopRunsStream(state);
1150
+ renderPanel(shadow, state, serverUrl2, frame);
1151
+ } else {
1152
+ await Promise.all([
1153
+ refreshQueue(state, serverUrl2),
1154
+ refreshRuns(state, serverUrl2),
1155
+ refreshAgentConfig(state, serverUrl2)
1156
+ ]);
1157
+ renderPanel(shadow, state, serverUrl2, frame);
1158
+ syncRunsStream(shadow, state, serverUrl2, frame);
1159
+ }
1160
+ launcher.innerHTML = state.active ? closeIcon() : inspectIcon();
1161
+ });
1162
+ window.addEventListener(
1163
+ "pointermove",
1164
+ (event) => {
1165
+ if (!state.active || state.selected) return;
1166
+ state.hovered = pickElementAtPoint(event.clientX, event.clientY);
1167
+ renderFrame(frame, state.hovered);
1168
+ },
1169
+ true
1170
+ );
1171
+ window.addEventListener(
1172
+ "pointerdown",
1173
+ (event) => {
1174
+ if (!state.active) return;
1175
+ if (event.button !== 0) return;
1176
+ if (event.target instanceof Element && event.target.closest("[data-wand-root]")) return;
1177
+ const target = pickElementAtPoint(event.clientX, event.clientY);
1178
+ if (!target) return;
1179
+ event.preventDefault();
1180
+ event.stopPropagation();
1181
+ if (state.selected !== target) resetComposerState(state, target);
1182
+ state.selected = target;
1183
+ renderFrame(frame, target);
1184
+ renderPanel(shadow, state, serverUrl2, frame);
1185
+ },
1186
+ true
1187
+ );
1188
+ window.addEventListener(
1189
+ "click",
1190
+ (event) => {
1191
+ if (!state.active) return;
1192
+ if (event.target instanceof Element && event.target.closest("[data-wand-root]")) return;
1193
+ if (!pickElementAtPoint(event.clientX, event.clientY)) return;
1194
+ event.preventDefault();
1195
+ event.stopPropagation();
1196
+ },
1197
+ true
1198
+ );
1199
+ window.addEventListener(
1200
+ "keydown",
1201
+ (event) => {
1202
+ if (!state.active || event.key !== "Escape") return;
1203
+ state.active = false;
1204
+ state.selected = null;
1205
+ state.hovered = null;
1206
+ resetComposerState(state, null);
1207
+ frame.hidden = true;
1208
+ stopRunsStream(state);
1209
+ launcher.innerHTML = inspectIcon();
1210
+ renderPanel(shadow, state, serverUrl2, frame);
1211
+ },
1212
+ true
1213
+ );
1214
+ window.addEventListener("resize", () => renderFrame(frame, state.selected ?? state.hovered));
1215
+ window.addEventListener("scroll", () => renderFrame(frame, state.selected ?? state.hovered), true);
1216
+ }
1217
+ function renderPanel(shadow, state, serverUrl2, frame) {
1218
+ rememberComposerState(shadow, state);
1219
+ shadow.querySelector(".wand-panel")?.remove();
1220
+ if (!state.active) return;
1221
+ const panel = document.createElement("section");
1222
+ panel.className = "wand-panel";
1223
+ panel.dataset.wandUi = "true";
1224
+ panel.innerHTML = `
1225
+ <div class="wand-panel-header">
1226
+ <span>${state.selected ? "Describe the change" : "Select an element"}</span>
1227
+ <span class="wand-muted wand-run-count">${runCountText(state)}</span>
1228
+ </div>
1229
+ ${state.selected ? `
1230
+ <div class="wand-panel-section">
1231
+ <textarea placeholder="What should change?"></textarea>
1232
+ <div class="wand-actions">
1233
+ <button type="button" data-action="send" ${state.agentConfig.selectedAdapter ? "" : "disabled"}>Send to agent</button>
1234
+ <button type="button" data-action="queue">Queue it</button>
1235
+ </div>
1236
+ <label class="wand-agent-picker">
1237
+ <span class="wand-muted">Agent</span>
1238
+ <select data-action="select-adapter" ${state.agentConfig.availableAdapters.length === 0 ? "disabled" : ""}>
1239
+ ${adapterOptionsHtml(state.agentConfig)}
1240
+ </select>
1241
+ </label>
1242
+ </div>
1243
+ ` : `<div class="wand-panel-section wand-muted">Move the cursor, then click a page element.</div>`}
1244
+ <div class="wand-queue">
1245
+ ${queueItemsHtml(state)}
1246
+ </div>
1247
+ `;
1248
+ panel.addEventListener("change", async (event) => {
1249
+ const target = event.target;
1250
+ if (target.dataset.action !== "select-adapter") return;
1251
+ const adapter = target.value;
1252
+ if (!state.agentConfig.availableAdapters.includes(adapter)) return;
1253
+ try {
1254
+ state.agentConfig = await request(serverUrl2, "/api/agent-config", {
1255
+ method: "PUT",
1256
+ headers: { "content-type": "application/json" },
1257
+ body: JSON.stringify({ adapter })
1258
+ });
1259
+ renderPanel(shadow, state, serverUrl2, frame);
1260
+ } catch (error) {
1261
+ toast(shadow, error instanceof Error ? error.message : String(error));
1262
+ await refreshAgentConfig(state, serverUrl2);
1263
+ renderPanel(shadow, state, serverUrl2, frame);
1264
+ }
1265
+ });
1266
+ panel.addEventListener("click", async (event) => {
1267
+ const target = event.target;
1268
+ const action = target.dataset.action;
1269
+ if (!action) return;
1270
+ try {
1271
+ if (action === "send" || action === "queue") {
1272
+ const textarea = panel.querySelector("textarea");
1273
+ const prompt = textarea.value.trim();
1274
+ if (!prompt || !state.selected) return;
1275
+ if (action === "send" && !state.agentConfig.selectedAdapter) return;
1276
+ if (action === "send") setComposerSending(panel, true);
1277
+ try {
1278
+ const capture = await captureSelection(state.selected);
1279
+ if (action === "send") {
1280
+ toast(shadow, "Sending to agent...");
1281
+ await post(serverUrl2, "/api/send", {
1282
+ prompt,
1283
+ capture,
1284
+ adapter: state.agentConfig.selectedAdapter
1285
+ });
1286
+ toast(shadow, "Agent started.");
1287
+ state.selected = null;
1288
+ resetComposerState(state, null);
1289
+ frame.hidden = true;
1290
+ } else {
1291
+ await post(serverUrl2, "/api/queue", { prompt, capture });
1292
+ toast(shadow, "Queued.");
1293
+ state.selected = null;
1294
+ resetComposerState(state, null);
1295
+ }
1296
+ await Promise.all([refreshQueue(state, serverUrl2), refreshRuns(state, serverUrl2)]);
1297
+ renderPanel(shadow, state, serverUrl2, frame);
1298
+ syncRunsStream(shadow, state, serverUrl2, frame);
1299
+ return;
1300
+ } finally {
1301
+ if (action === "send" && panel.isConnected) setComposerSending(panel, false);
1302
+ }
1303
+ }
1304
+ if (action === "stop-run") {
1305
+ const item2 = target.closest("[data-run-id]");
1306
+ const id2 = item2?.dataset.runId;
1307
+ if (!id2) return;
1308
+ await request(serverUrl2, `/api/runs/${encodeURIComponent(id2)}`, { method: "DELETE" });
1309
+ await refreshRuns(state, serverUrl2);
1310
+ renderPanel(shadow, state, serverUrl2, frame);
1311
+ return;
1312
+ }
1313
+ const item = target.closest("[data-queue-id]");
1314
+ const id = item?.dataset.queueId;
1315
+ if (!id) return;
1316
+ const queued = state.queue.find((entry) => entry.id === id);
1317
+ if (!queued) return;
1318
+ if (action === "send-queued") {
1319
+ toast(shadow, "Sending queued request...");
1320
+ await post(serverUrl2, "/api/send", { prompt: queued.prompt, context: queued.context });
1321
+ await request(serverUrl2, `/api/queue/${encodeURIComponent(id)}`, { method: "DELETE" });
1322
+ toast(shadow, "Queued request started.");
1323
+ } else if (action === "delete-queued") {
1324
+ await request(serverUrl2, `/api/queue/${encodeURIComponent(id)}`, { method: "DELETE" });
1325
+ }
1326
+ await Promise.all([refreshQueue(state, serverUrl2), refreshRuns(state, serverUrl2)]);
1327
+ renderPanel(shadow, state, serverUrl2, frame);
1328
+ } catch (error) {
1329
+ toast(shadow, error instanceof Error ? error.message : String(error));
1330
+ }
1331
+ });
1332
+ shadow.append(panel);
1333
+ restoreComposerState(shadow, panel, state);
1334
+ }
1335
+ async function captureSelection(element) {
1336
+ const rect = element.getBoundingClientRect();
1337
+ return {
1338
+ id: crypto.randomUUID(),
1339
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1340
+ url: window.location.href,
1341
+ route: `${window.location.pathname}${window.location.search}${window.location.hash}`,
1342
+ title: document.title,
1343
+ selector: selectorFor(element),
1344
+ domPath: domPathFor(element),
1345
+ tagName: element.tagName,
1346
+ text: (element.textContent ?? "").trim().slice(0, 500),
1347
+ attributes: attributesFor(element),
1348
+ rect: {
1349
+ x: rect.x,
1350
+ y: rect.y,
1351
+ width: rect.width,
1352
+ height: rect.height
1353
+ },
1354
+ reactOwnerHints: reactOwnerHintsFor(element),
1355
+ elementHtml: htmlFor(element),
1356
+ viewportDataUrl: await toPng(
1357
+ document.documentElement,
1358
+ viewportCaptureOptions({
1359
+ viewportWidth: window.innerWidth,
1360
+ viewportHeight: window.innerHeight,
1361
+ scrollX: window.scrollX,
1362
+ scrollY: window.scrollY,
1363
+ documentWidth: document.documentElement.scrollWidth,
1364
+ documentHeight: document.documentElement.scrollHeight
1365
+ })
1366
+ ),
1367
+ elementDataUrl: await toPng(element, { cacheBust: true })
1368
+ };
1369
+ }
1370
+ function renderFrame(frame, target) {
1371
+ if (!target) {
1372
+ frame.hidden = true;
1373
+ return;
1374
+ }
1375
+ const rect = target.getBoundingClientRect();
1376
+ frame.hidden = false;
1377
+ frame.style.left = `${rect.left}px`;
1378
+ frame.style.top = `${rect.top}px`;
1379
+ frame.style.width = `${rect.width}px`;
1380
+ frame.style.height = `${rect.height}px`;
1381
+ }
1382
+ async function refreshQueue(state, serverUrl2) {
1383
+ try {
1384
+ state.queue = await request(serverUrl2, "/api/queue");
1385
+ } catch {
1386
+ state.queue = [];
1387
+ }
1388
+ }
1389
+ async function refreshRuns(state, serverUrl2) {
1390
+ try {
1391
+ state.runs = await request(serverUrl2, "/api/runs");
1392
+ } catch {
1393
+ state.runs = [];
1394
+ }
1395
+ }
1396
+ async function refreshAgentConfig(state, serverUrl2) {
1397
+ try {
1398
+ state.agentConfig = await request(serverUrl2, "/api/agent-config");
1399
+ } catch {
1400
+ state.agentConfig = {
1401
+ availableAdapters: [],
1402
+ selectedAdapter: null
1403
+ };
1404
+ }
1405
+ }
1406
+ function syncRunsStream(shadow, state, serverUrl2, frame) {
1407
+ if (!state.active || state.runsEventSource !== null) return;
1408
+ const source = new EventSource(`${serverUrl2}/api/runs/events`);
1409
+ source.addEventListener("runs", (event) => {
1410
+ state.runs = JSON.parse(event.data);
1411
+ if (state.active) {
1412
+ renderQueue(shadow, state);
1413
+ renderRunCount(shadow, state);
1414
+ }
1415
+ });
1416
+ state.runsEventSource = source;
1417
+ }
1418
+ function stopRunsStream(state) {
1419
+ state.runsEventSource?.close();
1420
+ state.runsEventSource = null;
1421
+ }
1422
+ async function post(serverUrl2, pathname, body) {
1423
+ return request(serverUrl2, pathname, {
1424
+ method: "POST",
1425
+ headers: { "content-type": "application/json" },
1426
+ body: JSON.stringify(body)
1427
+ });
1428
+ }
1429
+ async function request(serverUrl2, pathname, init) {
1430
+ const response = await fetch(`${serverUrl2}${pathname}`, init);
1431
+ const payload = await response.json();
1432
+ if (!response.ok) throw new Error(payload.error ?? `Request failed: ${response.status}`);
1433
+ return payload;
1434
+ }
1435
+ function toast(shadow, message) {
1436
+ shadow.querySelector(".wand-toast")?.remove();
1437
+ const node = document.createElement("div");
1438
+ node.className = "wand-toast";
1439
+ node.textContent = message;
1440
+ shadow.append(node);
1441
+ window.setTimeout(() => node.remove(), 2400);
1442
+ }
1443
+ function escapeHtml(value) {
1444
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
1445
+ }
1446
+ function renderQueue(shadow, state) {
1447
+ const queue = shadow.querySelector(".wand-queue");
1448
+ if (!queue) return;
1449
+ queue.innerHTML = queueItemsHtml(state);
1450
+ }
1451
+ function renderRunCount(shadow, state) {
1452
+ const count = shadow.querySelector(".wand-run-count");
1453
+ if (!count) return;
1454
+ count.textContent = runCountText(state);
1455
+ }
1456
+ function runCountText(state) {
1457
+ return `${state.runs.length} running / ${state.queue.length} queued`;
1458
+ }
1459
+ function adapterOptionsHtml(config) {
1460
+ const placeholder = config.selectedAdapter ? "" : `<option value="">Choose agent</option>`;
1461
+ return `${placeholder}${config.availableAdapters.map(
1462
+ (adapter) => `<option value="${adapter}" ${adapter === config.selectedAdapter ? "selected" : ""}>${adapterLabel(adapter)}</option>`
1463
+ ).join("")}`;
1464
+ }
1465
+ function adapterLabel(adapter) {
1466
+ switch (adapter) {
1467
+ case "claude-code":
1468
+ return "Claude Code";
1469
+ case "opencode":
1470
+ return "OpenCode";
1471
+ case "codex":
1472
+ return "Codex";
1473
+ }
1474
+ }
1475
+ function queueItemsHtml(state) {
1476
+ return `
1477
+ ${state.runs.map(
1478
+ (run) => `
1479
+ <div class="wand-queue-item wand-agent-run" data-run-id="${run.id}">
1480
+ <div class="wand-run-header">
1481
+ <div class="wand-run-title">${escapeHtml(run.prompt)}</div>
1482
+ <button type="button" data-action="stop-run">Stop</button>
1483
+ </div>
1484
+ <pre class="wand-run-message">${escapeHtml(formatJsonPreview(run.latestMessage))}</pre>
1485
+ </div>
1486
+ `
1487
+ ).join("")}
1488
+ ${state.queue.map(
1489
+ (item) => `
1490
+ <div class="wand-queue-item" data-queue-id="${item.id}">
1491
+ <div>${escapeHtml(item.prompt)}</div>
1492
+ <div class="wand-muted">${new Date(item.createdAt).toLocaleString()}</div>
1493
+ <div class="wand-queue-controls">
1494
+ <button type="button" data-action="send-queued">Send</button>
1495
+ <button type="button" data-action="delete-queued">Delete</button>
1496
+ </div>
1497
+ </div>
1498
+ `
1499
+ ).join("")}
1500
+ `;
1501
+ }
1502
+ function rememberComposerState(shadow, state) {
1503
+ const textarea = shadow.querySelector(".wand-panel textarea");
1504
+ if (!textarea || state.composerFor !== state.selected) return;
1505
+ syncComposerState(shadow, textarea, state);
1506
+ }
1507
+ function restoreComposerState(shadow, panel, state) {
1508
+ const textarea = panel.querySelector("textarea");
1509
+ if (!textarea || !state.selected) return;
1510
+ if (state.composerFor !== state.selected) resetComposerState(state, state.selected);
1511
+ textarea.value = state.composerDraft;
1512
+ const sync = () => syncComposerState(shadow, textarea, state);
1513
+ textarea.addEventListener("input", sync);
1514
+ textarea.addEventListener("focus", sync);
1515
+ textarea.addEventListener("blur", sync);
1516
+ textarea.addEventListener("select", sync);
1517
+ textarea.addEventListener("keyup", sync);
1518
+ textarea.addEventListener("click", sync);
1519
+ if (!state.composerFocused) return;
1520
+ const selectionStart = state.composerSelectionStart;
1521
+ const selectionEnd = state.composerSelectionEnd;
1522
+ const selectionDirection = state.composerSelectionDirection;
1523
+ textarea.focus();
1524
+ if (selectionStart !== null && selectionEnd !== null) {
1525
+ textarea.setSelectionRange(
1526
+ selectionStart,
1527
+ selectionEnd,
1528
+ selectionDirection ?? "none"
1529
+ );
1530
+ }
1531
+ sync();
1532
+ }
1533
+ function syncComposerState(shadow, textarea, state) {
1534
+ state.composerDraft = textarea.value;
1535
+ state.composerFocused = shadow.activeElement === textarea;
1536
+ state.composerSelectionStart = textarea.selectionStart;
1537
+ state.composerSelectionEnd = textarea.selectionEnd;
1538
+ state.composerSelectionDirection = textarea.selectionDirection;
1539
+ }
1540
+ function resetComposerState(state, selected) {
1541
+ state.composerFor = selected;
1542
+ state.composerDraft = "";
1543
+ state.composerFocused = false;
1544
+ state.composerSelectionStart = null;
1545
+ state.composerSelectionEnd = null;
1546
+ state.composerSelectionDirection = null;
1547
+ }
1548
+ function inspectIcon() {
1549
+ return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 2v4M12 18v4M2 12h4M18 12h4"/></svg>`;
1550
+ }
1551
+ function closeIcon() {
1552
+ return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18M6 6l12 12"/></svg>`;
1553
+ }
1554
+
1555
+ // src/client.ts
1556
+ var script = document.currentScript;
1557
+ var serverUrl = script instanceof HTMLScriptElement ? script.dataset.wandServerUrl : void 0;
1558
+ mountWand(serverUrl ? { serverUrl } : {});
1559
+ })();