arcy.js 0.0.2 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +94 -3
  2. package/dist/arcy.chat.3968644db103.js +1271 -0
  3. package/dist/arcy.flow.fef131718e42.js +3 -0
  4. package/dist/arcy.legacy.b4005932a6cc.js +568 -0
  5. package/dist/arcy.loader.js +2 -0
  6. package/dist/arcy.modern.a51b9a32dd28.js +568 -0
  7. package/dist/arcy.picker.73f47221b73c.js +59 -0
  8. package/dist/chat-BK57WIAA.js +3867 -0
  9. package/dist/chunk-6HBSGVRN.js +219 -0
  10. package/dist/chunk-7VVKR3AO.js +122 -0
  11. package/dist/chunk-CQ2DESAJ.js +263 -0
  12. package/dist/chunk-DKYMIAYF.js +7 -0
  13. package/dist/chunk-FR6SJSDU.js +220 -0
  14. package/dist/chunk-LGWYJSYX.js +271 -0
  15. package/dist/chunk-NIHUDSWK.js +155 -0
  16. package/dist/chunk-NY3NXM2V.js +213 -0
  17. package/dist/design-mode.cjs +135 -0
  18. package/dist/design-mode.d.ts +197 -0
  19. package/dist/design-mode.js +2 -0
  20. package/dist/flow-M7W3H3C5.js +1422 -0
  21. package/dist/fonts/dm-sans-latin-ext.woff2 +0 -0
  22. package/dist/fonts/dm-sans-latin.woff2 +0 -0
  23. package/dist/fonts/ibm-plex-sans-latin-ext.woff2 +0 -0
  24. package/dist/fonts/ibm-plex-sans-latin.woff2 +0 -0
  25. package/dist/fonts/inter-latin-ext.woff2 +0 -0
  26. package/dist/fonts/inter-latin.woff2 +0 -0
  27. package/dist/fonts/lato-latin-ext.woff2 +0 -0
  28. package/dist/fonts/lato-latin.woff2 +0 -0
  29. package/dist/fonts/manrope-latin-ext.woff2 +0 -0
  30. package/dist/fonts/manrope-latin.woff2 +0 -0
  31. package/dist/fonts/montserrat-latin-ext.woff2 +0 -0
  32. package/dist/fonts/montserrat-latin.woff2 +0 -0
  33. package/dist/fonts/nunito-latin-ext.woff2 +0 -0
  34. package/dist/fonts/nunito-latin.woff2 +0 -0
  35. package/dist/fonts/open-sans-latin-ext.woff2 +0 -0
  36. package/dist/fonts/open-sans-latin.woff2 +0 -0
  37. package/dist/fonts/playfair-display-latin-ext.woff2 +0 -0
  38. package/dist/fonts/playfair-display-latin.woff2 +0 -0
  39. package/dist/fonts/poppins-latin-ext.woff2 +0 -0
  40. package/dist/fonts/poppins-latin.woff2 +0 -0
  41. package/dist/fonts/roboto-latin-ext.woff2 +0 -0
  42. package/dist/fonts/roboto-latin.woff2 +0 -0
  43. package/dist/fonts/source-sans-3-latin-ext.woff2 +0 -0
  44. package/dist/fonts/source-sans-3-latin.woff2 +0 -0
  45. package/dist/fonts/work-sans-latin-ext.woff2 +0 -0
  46. package/dist/fonts/work-sans-latin.woff2 +0 -0
  47. package/dist/index.cjs +13848 -0
  48. package/dist/index.d.ts +169 -0
  49. package/dist/index.js +4743 -0
  50. package/dist/picker-JPESE6JJ.js +2134 -0
  51. package/dist/snippet.cjs +46 -0
  52. package/dist/snippet.d.ts +39 -0
  53. package/dist/snippet.js +43 -0
  54. package/package.json +69 -5
  55. package/index.js +0 -5
@@ -0,0 +1,2134 @@
1
+ import { PICKER_CONTRACT, PICKER_GLOBAL } from './chunk-DKYMIAYF.js';
2
+ import { createShell, injectStyles, warn, HOST_ATTRIBUTE } from './chunk-LGWYJSYX.js';
3
+ import { matchTarget } from './chunk-NIHUDSWK.js';
4
+ import { resolveInteractiveTarget, createTargetFingerprint, captureEventFingerprint, closestAcrossShadow, isStableId, containsPii, SELECTOR_CANDIDATES_MAX, collectElementsDeep, querySelectorAllDeep } from './chunk-NY3NXM2V.js';
5
+
6
+ /* arcy.js — https://arcyai.com */
7
+
8
+ // src/fingerprint/selector.ts
9
+ var TIER_TEST_ATTR = 1;
10
+ var TIER_SEMANTIC_ATTR = 2;
11
+ var TIER_SEMANTIC_CLASS = 5;
12
+ var TEST_ATTRS = ["data-testid", "data-test", "data-cy"];
13
+ var SEMANTIC_ATTRS = ["name", "for", "aria-label"];
14
+ var UTILITY_BARE = /* @__PURE__ */ new Set([
15
+ "flex",
16
+ "grid",
17
+ "block",
18
+ "hidden",
19
+ "inline",
20
+ "contents",
21
+ "table",
22
+ "isolate",
23
+ "static",
24
+ "fixed",
25
+ "absolute",
26
+ "relative",
27
+ "sticky",
28
+ "visible",
29
+ "invisible",
30
+ "truncate",
31
+ "italic",
32
+ "uppercase",
33
+ "lowercase",
34
+ "capitalize",
35
+ "antialiased",
36
+ "container",
37
+ "border",
38
+ "rounded",
39
+ "shadow",
40
+ "ring",
41
+ "outline",
42
+ "transition",
43
+ "underline",
44
+ "grow",
45
+ "shrink",
46
+ "sr-only"
47
+ ]);
48
+ var UTILITY_PREFIX = /^-?(?:p|px|py|pt|pr|pb|pl|m|mx|my|mt|mr|mb|ml|gap|space|w|h|min-w|min-h|max-w|max-h|inset|top|right|bottom|left|z|text|font|leading|tracking|bg|border|rounded|shadow|opacity|flex|grid|col|row|order|basis|justify|items|content|self|place|overflow|object|aspect|size|translate|scale|rotate|duration|delay|ease|transition|ring|outline|fill|stroke|divide|decoration|cursor|select|list|align|whitespace|break|animate|blur|backdrop)-(.+)$/;
49
+ var UTILITY_SUFFIX_WORDS = /* @__PURE__ */ new Set([
50
+ "xs",
51
+ "sm",
52
+ "md",
53
+ "lg",
54
+ "xl",
55
+ "full",
56
+ "auto",
57
+ "none",
58
+ "center",
59
+ "start",
60
+ "end",
61
+ "between",
62
+ "around",
63
+ "evenly",
64
+ "baseline",
65
+ "stretch",
66
+ "wrap",
67
+ "nowrap",
68
+ "reverse",
69
+ "col",
70
+ "row",
71
+ "bold",
72
+ "semibold",
73
+ "medium",
74
+ "normal",
75
+ "light",
76
+ "thin",
77
+ "black",
78
+ "white",
79
+ "transparent",
80
+ "current",
81
+ "scroll",
82
+ "clip",
83
+ "ellipsis",
84
+ "pointer",
85
+ "left",
86
+ "right",
87
+ "top",
88
+ "bottom",
89
+ "middle",
90
+ "justify",
91
+ "screen",
92
+ "fit",
93
+ "min",
94
+ "max",
95
+ "visible",
96
+ "hidden",
97
+ "dashed",
98
+ "dotted",
99
+ "solid",
100
+ "double",
101
+ "in",
102
+ "out",
103
+ // shadcn/Tailwind theme tokens (`bg-background`, `border-border`)
104
+ "border",
105
+ "background",
106
+ "foreground",
107
+ "muted",
108
+ "primary",
109
+ "secondary",
110
+ "accent",
111
+ "destructive",
112
+ "card",
113
+ "popover",
114
+ "input"
115
+ ]);
116
+ function isUtilityShape(raw) {
117
+ const cls = raw.startsWith("!") ? raw.slice(1) : raw;
118
+ if (cls.includes(":") || cls.includes("[") || cls.includes("]") || cls.includes("/"))
119
+ return true;
120
+ if (UTILITY_BARE.has(cls)) return true;
121
+ const match = UTILITY_PREFIX.exec(cls);
122
+ if (match === null) return false;
123
+ const suffix = match[1];
124
+ if (/\d/.test(suffix)) return true;
125
+ return suffix.split("-").every((part) => UTILITY_SUFFIX_WORDS.has(part));
126
+ }
127
+ var GENERATED_CLASS_PREFIXES = ["css-", "sc-", "jss-"];
128
+ function looksGenerated(cls) {
129
+ for (const prefix of GENERATED_CLASS_PREFIXES) {
130
+ if (cls.startsWith(prefix)) return true;
131
+ }
132
+ const segment = cls.split(/[_-]/).pop() ?? "";
133
+ return segment.length >= 5 && /\d/.test(segment) && /[a-z]/.test(segment) && /[A-Z]/.test(segment);
134
+ }
135
+ function countClasses(doc) {
136
+ const all = collectElementsDeep(doc);
137
+ const counts = /* @__PURE__ */ new Map();
138
+ for (const el of all) {
139
+ const classList = el.classList;
140
+ for (let j = 0; j < classList.length; j += 1) {
141
+ const cls = classList[j];
142
+ counts.set(cls, (counts.get(cls) ?? 0) + 1);
143
+ }
144
+ }
145
+ return { counts, total: all.length };
146
+ }
147
+ var FREQUENCY_MIN = 10;
148
+ var FREQUENCY_RATIO = 0.02;
149
+ function frequencyThreshold(totalElements) {
150
+ return Math.max(FREQUENCY_MIN, Math.ceil(totalElements * FREQUENCY_RATIO));
151
+ }
152
+ var SELECTOR_VALUE_MAX = 80;
153
+ function attrValue(el, attr) {
154
+ const raw = el.getAttribute(attr);
155
+ if (raw === null) return null;
156
+ if (raw.trim() === "" || raw.length > SELECTOR_VALUE_MAX) return null;
157
+ if (/[\n\r\t\f]/.test(raw)) return null;
158
+ if (containsPii(raw)) return null;
159
+ return raw;
160
+ }
161
+ function attrSelector(attr, value) {
162
+ return `[${attr}="${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"]`;
163
+ }
164
+ function cssEscapeIdent(value) {
165
+ if (typeof CSS !== "undefined" && typeof CSS.escape === "function")
166
+ return CSS.escape(value);
167
+ let escaped = value.replace(/[^a-zA-Z0-9_\u00A0-\uFFFF-]/g, (ch) => "\\" + ch);
168
+ if (/^\d/.test(escaped)) escaped = `\\3${escaped[0]} ${escaped.slice(1)}`;
169
+ else if (/^-\d/.test(escaped))
170
+ escaped = `-\\3${escaped[1]} ${escaped.slice(2)}`;
171
+ else if (escaped === "-") escaped = "\\-";
172
+ return escaped;
173
+ }
174
+ function matchCountFor(doc, selector, el) {
175
+ let matches;
176
+ try {
177
+ matches = querySelectorAllDeep(doc, selector);
178
+ } catch {
179
+ return null;
180
+ }
181
+ return matches.includes(el) ? matches.length : null;
182
+ }
183
+ var XHTML_NS = "http://www.w3.org/1999/xhtml";
184
+ function rankSelectors(el) {
185
+ try {
186
+ return rank(el);
187
+ } catch {
188
+ return { candidates: [], autoSelector: null };
189
+ }
190
+ }
191
+ function rank(el) {
192
+ const doc = el.ownerDocument;
193
+ const tag = el.namespaceURI === null || el.namespaceURI === XHTML_NS ? el.tagName.toLowerCase() : el.tagName;
194
+ const raw = [];
195
+ for (const attr of TEST_ATTRS) {
196
+ const value = attrValue(el, attr);
197
+ if (value !== null)
198
+ raw.push({ selector: attrSelector(attr, value), tier: TIER_TEST_ATTR });
199
+ }
200
+ const id = el.getAttribute("id");
201
+ if (id !== null && isStableId(id) && !containsPii(id)) {
202
+ raw.push({ selector: `#${cssEscapeIdent(id)}`, tier: TIER_SEMANTIC_ATTR });
203
+ }
204
+ for (const attr of SEMANTIC_ATTRS) {
205
+ const value = attrValue(el, attr);
206
+ if (value !== null)
207
+ raw.push({
208
+ selector: attrSelector(attr, value),
209
+ tier: TIER_SEMANTIC_ATTR
210
+ });
211
+ }
212
+ const role = attrValue(el, "role");
213
+ if (role !== null) {
214
+ raw.push({
215
+ selector: `${tag}${attrSelector("role", role)}`,
216
+ tier: TIER_SEMANTIC_ATTR
217
+ });
218
+ }
219
+ if (el.classList.length > 0) {
220
+ const { counts, total } = countClasses(doc);
221
+ const threshold = frequencyThreshold(total);
222
+ const classList = el.classList;
223
+ const seenClasses = /* @__PURE__ */ new Set();
224
+ for (let i = 0; i < classList.length; i += 1) {
225
+ const cls = classList[i];
226
+ if (seenClasses.has(cls)) continue;
227
+ seenClasses.add(cls);
228
+ if (isUtilityShape(cls) || looksGenerated(cls) || containsPii(cls))
229
+ continue;
230
+ if ((counts.get(cls) ?? 0) > threshold) continue;
231
+ raw.push({
232
+ selector: `${tag}.${cssEscapeIdent(cls)}`,
233
+ tier: TIER_SEMANTIC_CLASS
234
+ });
235
+ }
236
+ }
237
+ const candidates = [];
238
+ for (const entry of raw) {
239
+ const matchCount = matchCountFor(doc, entry.selector, el);
240
+ if (matchCount === null) continue;
241
+ candidates.push({ selector: entry.selector, tier: entry.tier, matchCount });
242
+ }
243
+ candidates.sort((a, b) => a.tier - b.tier || a.matchCount - b.matchCount);
244
+ const capped = candidates.slice(0, SELECTOR_CANDIDATES_MAX);
245
+ const top = capped[0];
246
+ const autoSelector = top !== void 0 && top.tier <= TIER_SEMANTIC_ATTR && top.matchCount === 1 ? top.selector : null;
247
+ return { candidates: capped.map((c) => c.selector), autoSelector };
248
+ }
249
+ var PREFIX = "[modern-screenshot]";
250
+ var IN_BROWSER = typeof window !== "undefined";
251
+ var SUPPORT_WEB_WORKER = IN_BROWSER && "Worker" in window;
252
+ var USER_AGENT = IN_BROWSER ? window.navigator?.userAgent : "";
253
+ var IN_CHROME = USER_AGENT.includes("Chrome");
254
+ var IN_SAFARI = USER_AGENT.includes("AppleWebKit") && !IN_CHROME;
255
+ var IN_FIREFOX = USER_AGENT.includes("Firefox");
256
+ var isContext = (value) => value && "__CONTEXT__" in value;
257
+ var isCssFontFaceRule = (rule) => rule.constructor.name === "CSSFontFaceRule";
258
+ var isCSSImportRule = (rule) => rule.constructor.name === "CSSImportRule";
259
+ var isLayerBlockRule = (rule) => rule.constructor.name === "CSSLayerBlockRule";
260
+ var isElementNode = (node) => node.nodeType === 1;
261
+ var isSVGElementNode = (node) => typeof node.className === "object";
262
+ var isSVGImageElementNode = (node) => node.tagName === "image";
263
+ var isSVGUseElementNode = (node) => node.tagName === "use";
264
+ var isHTMLElementNode = (node) => isElementNode(node) && typeof node.style !== "undefined" && !isSVGElementNode(node);
265
+ var isCommentNode = (node) => node.nodeType === 8;
266
+ var isTextNode = (node) => node.nodeType === 3;
267
+ var isImageElement = (node) => node.tagName === "IMG";
268
+ var isVideoElement = (node) => node.tagName === "VIDEO";
269
+ var isCanvasElement = (node) => node.tagName === "CANVAS";
270
+ var isTextareaElement = (node) => node.tagName === "TEXTAREA";
271
+ var isInputElement = (node) => node.tagName === "INPUT";
272
+ var isStyleElement = (node) => node.tagName === "STYLE";
273
+ var isScriptElement = (node) => node.tagName === "SCRIPT";
274
+ var isSelectElement = (node) => node.tagName === "SELECT";
275
+ var isSlotElement = (node) => node.tagName === "SLOT";
276
+ var isIFrameElement = (node) => node.tagName === "IFRAME";
277
+ var consoleWarn = (...args) => console.warn(PREFIX, ...args);
278
+ function supportWebp(ownerDocument) {
279
+ const canvas = ownerDocument?.createElement?.("canvas");
280
+ if (canvas) {
281
+ canvas.height = canvas.width = 1;
282
+ }
283
+ return Boolean(canvas) && "toDataURL" in canvas && Boolean(canvas.toDataURL("image/webp").includes("image/webp"));
284
+ }
285
+ var isDataUrl = (url) => url.startsWith("data:");
286
+ function resolveUrl(url, baseUrl) {
287
+ if (url.match(/^[a-z]+:\/\//i))
288
+ return url;
289
+ if (IN_BROWSER && url.match(/^\/\//))
290
+ return window.location.protocol + url;
291
+ if (url.match(/^[a-z]+:/i))
292
+ return url;
293
+ if (!IN_BROWSER)
294
+ return url;
295
+ const doc = getDocument().implementation.createHTMLDocument();
296
+ const base = doc.createElement("base");
297
+ const a = doc.createElement("a");
298
+ doc.head.appendChild(base);
299
+ doc.body.appendChild(a);
300
+ if (baseUrl)
301
+ base.href = baseUrl;
302
+ a.href = url;
303
+ return a.href;
304
+ }
305
+ function getDocument(target) {
306
+ return (target && isElementNode(target) ? target?.ownerDocument : target) ?? window.document;
307
+ }
308
+ var XMLNS = "http://www.w3.org/2000/svg";
309
+ function createSvg(width, height, ownerDocument) {
310
+ const svg = getDocument(ownerDocument).createElementNS(XMLNS, "svg");
311
+ svg.setAttributeNS(null, "width", width.toString());
312
+ svg.setAttributeNS(null, "height", height.toString());
313
+ svg.setAttributeNS(null, "viewBox", `0 0 ${width} ${height}`);
314
+ return svg;
315
+ }
316
+ function svgToDataUrl(svg, removeControlCharacter) {
317
+ let xhtml = new XMLSerializer().serializeToString(svg);
318
+ if (removeControlCharacter) {
319
+ xhtml = xhtml.replace(/[\u0000-\u0008\v\f\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]/gu, "");
320
+ }
321
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xhtml)}`;
322
+ }
323
+ function readBlob(blob, type) {
324
+ return new Promise((resolve, reject) => {
325
+ const reader = new FileReader();
326
+ reader.onload = () => resolve(reader.result);
327
+ reader.onerror = () => reject(reader.error);
328
+ reader.onabort = () => reject(new Error(`Failed read blob to ${type}`));
329
+ {
330
+ reader.readAsDataURL(blob);
331
+ }
332
+ });
333
+ }
334
+ var blobToDataUrl = (blob) => readBlob(blob, "dataUrl");
335
+ function createImage(url, ownerDocument) {
336
+ const img = getDocument(ownerDocument).createElement("img");
337
+ img.decoding = "sync";
338
+ img.loading = "eager";
339
+ img.src = url;
340
+ return img;
341
+ }
342
+ function loadMedia(media, options) {
343
+ return new Promise((resolve) => {
344
+ const { timeout, ownerDocument, onError: userOnError, onWarn } = options ?? {};
345
+ const node = typeof media === "string" ? createImage(media, getDocument(ownerDocument)) : media;
346
+ let timer = null;
347
+ let removeEventListeners = null;
348
+ function onResolve() {
349
+ resolve(node);
350
+ timer && clearTimeout(timer);
351
+ removeEventListeners?.();
352
+ }
353
+ if (timeout) {
354
+ timer = setTimeout(onResolve, timeout);
355
+ }
356
+ if (isVideoElement(node)) {
357
+ const currentSrc = node.currentSrc || node.src;
358
+ if (!currentSrc) {
359
+ if (node.poster) {
360
+ return loadMedia(node.poster, options).then(resolve);
361
+ }
362
+ return onResolve();
363
+ }
364
+ if (node.readyState >= 2) {
365
+ return onResolve();
366
+ }
367
+ const onLoadeddata = onResolve;
368
+ const onError = (error) => {
369
+ onWarn?.(
370
+ "Failed video load",
371
+ currentSrc,
372
+ error
373
+ );
374
+ userOnError?.(error);
375
+ onResolve();
376
+ };
377
+ removeEventListeners = () => {
378
+ node.removeEventListener("loadeddata", onLoadeddata);
379
+ node.removeEventListener("error", onError);
380
+ };
381
+ node.addEventListener("loadeddata", onLoadeddata, { once: true });
382
+ node.addEventListener("error", onError, { once: true });
383
+ } else {
384
+ const currentSrc = isSVGImageElementNode(node) ? node.href.baseVal : node.currentSrc || node.src;
385
+ if (!currentSrc) {
386
+ return onResolve();
387
+ }
388
+ const onLoad = async () => {
389
+ if (isImageElement(node) && "decode" in node) {
390
+ try {
391
+ await node.decode();
392
+ } catch (error) {
393
+ onWarn?.(
394
+ "Failed to decode image, trying to render anyway",
395
+ node.dataset.originalSrc || currentSrc,
396
+ error
397
+ );
398
+ }
399
+ }
400
+ onResolve();
401
+ };
402
+ const onError = (error) => {
403
+ onWarn?.(
404
+ "Failed image load",
405
+ node.dataset.originalSrc || currentSrc,
406
+ error
407
+ );
408
+ onResolve();
409
+ };
410
+ if (isImageElement(node) && node.complete) {
411
+ return onLoad();
412
+ }
413
+ removeEventListeners = () => {
414
+ node.removeEventListener("load", onLoad);
415
+ node.removeEventListener("error", onError);
416
+ };
417
+ node.addEventListener("load", onLoad, { once: true });
418
+ node.addEventListener("error", onError, { once: true });
419
+ }
420
+ });
421
+ }
422
+ async function waitUntilLoad(node, options) {
423
+ if (isHTMLElementNode(node)) {
424
+ if (isImageElement(node) || isVideoElement(node)) {
425
+ await loadMedia(node, options);
426
+ } else {
427
+ await Promise.all(
428
+ ["img", "video"].flatMap((selectors) => {
429
+ return Array.from(node.querySelectorAll(selectors)).map((el) => loadMedia(el, options));
430
+ })
431
+ );
432
+ }
433
+ }
434
+ }
435
+ var uuid = /* @__PURE__ */ (function uuid2() {
436
+ let counter = 0;
437
+ const random = () => `0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4);
438
+ return () => {
439
+ counter += 1;
440
+ return `u${random()}${counter}`;
441
+ };
442
+ })();
443
+ function splitFontFamily(fontFamily) {
444
+ return fontFamily?.split(",").map((val) => val.trim().replace(/"|'/g, "").toLowerCase()).filter(Boolean);
445
+ }
446
+ var uid = 0;
447
+ function createLogger(debug) {
448
+ const prefix = `${PREFIX}[#${uid}]`;
449
+ uid++;
450
+ return {
451
+ // eslint-disable-next-line no-console
452
+ time: (label) => debug && console.time(`${prefix} ${label}`),
453
+ // eslint-disable-next-line no-console
454
+ timeEnd: (label) => debug && console.timeEnd(`${prefix} ${label}`),
455
+ warn: (...args) => debug && consoleWarn(...args)
456
+ };
457
+ }
458
+ function getDefaultRequestInit(bypassingCache) {
459
+ return {
460
+ cache: bypassingCache ? "no-cache" : "force-cache"
461
+ };
462
+ }
463
+ async function orCreateContext(node, options) {
464
+ return isContext(node) ? node : createContext(node, { ...options, autoDestruct: true });
465
+ }
466
+ async function createContext(node, options) {
467
+ const { scale = 1, workerUrl, workerNumber = 1 } = options || {};
468
+ const debug = Boolean(options?.debug);
469
+ const features = options?.features ?? true;
470
+ const ownerDocument = node.ownerDocument ?? (IN_BROWSER ? window.document : void 0);
471
+ const ownerWindow = node.ownerDocument?.defaultView ?? (IN_BROWSER ? window : void 0);
472
+ const requests = /* @__PURE__ */ new Map();
473
+ const context = {
474
+ // Options
475
+ width: 0,
476
+ height: 0,
477
+ quality: 1,
478
+ type: "image/png",
479
+ scale,
480
+ backgroundColor: null,
481
+ style: null,
482
+ filter: null,
483
+ maximumCanvasSize: 0,
484
+ timeout: 3e4,
485
+ progress: null,
486
+ debug,
487
+ fetch: {
488
+ requestInit: getDefaultRequestInit(options?.fetch?.bypassingCache),
489
+ placeholderImage: "data:image/png;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
490
+ bypassingCache: false,
491
+ ...options?.fetch
492
+ },
493
+ fetchFn: null,
494
+ font: {},
495
+ drawImageInterval: 100,
496
+ workerUrl: null,
497
+ workerNumber,
498
+ onCloneEachNode: null,
499
+ onCloneNode: null,
500
+ onEmbedNode: null,
501
+ onCreateForeignObjectSvg: null,
502
+ includeStyleProperties: null,
503
+ autoDestruct: false,
504
+ ...options,
505
+ // InternalContext
506
+ __CONTEXT__: true,
507
+ log: createLogger(debug),
508
+ node,
509
+ ownerDocument,
510
+ ownerWindow,
511
+ dpi: scale === 1 ? null : 96 * scale,
512
+ svgStyleElement: createStyleElement(ownerDocument),
513
+ svgDefsElement: ownerDocument?.createElementNS(XMLNS, "defs"),
514
+ svgStyles: /* @__PURE__ */ new Map(),
515
+ defaultComputedStyles: /* @__PURE__ */ new Map(),
516
+ workers: [
517
+ ...Array.from({
518
+ length: SUPPORT_WEB_WORKER && workerUrl && workerNumber ? workerNumber : 0
519
+ })
520
+ ].map(() => {
521
+ try {
522
+ const worker = new Worker(workerUrl);
523
+ worker.onmessage = async (event) => {
524
+ const { url, result } = event.data;
525
+ if (result) {
526
+ requests.get(url)?.resolve?.(result);
527
+ } else {
528
+ requests.get(url)?.reject?.(new Error(`Error receiving message from worker: ${url}`));
529
+ }
530
+ };
531
+ worker.onmessageerror = (event) => {
532
+ const { url } = event.data;
533
+ requests.get(url)?.reject?.(new Error(`Error receiving message from worker: ${url}`));
534
+ };
535
+ return worker;
536
+ } catch (error) {
537
+ context.log.warn("Failed to new Worker", error);
538
+ return null;
539
+ }
540
+ }).filter(Boolean),
541
+ fontFamilies: /* @__PURE__ */ new Map(),
542
+ fontCssTexts: /* @__PURE__ */ new Map(),
543
+ acceptOfImage: `${[
544
+ supportWebp(ownerDocument) && "image/webp",
545
+ "image/svg+xml",
546
+ "image/*",
547
+ "*/*"
548
+ ].filter(Boolean).join(",")};q=0.8`,
549
+ requests,
550
+ drawImageCount: 0,
551
+ tasks: [],
552
+ features,
553
+ isEnable: (key) => {
554
+ if (key === "restoreScrollPosition") {
555
+ return typeof features === "boolean" ? false : features[key] ?? false;
556
+ }
557
+ if (typeof features === "boolean") {
558
+ return features;
559
+ }
560
+ return features[key] ?? true;
561
+ },
562
+ shadowRoots: []
563
+ };
564
+ context.log.time("wait until load");
565
+ await waitUntilLoad(node, { timeout: context.timeout, onWarn: context.log.warn });
566
+ context.log.timeEnd("wait until load");
567
+ const { width, height } = resolveBoundingBox(node, context);
568
+ context.width = width;
569
+ context.height = height;
570
+ return context;
571
+ }
572
+ function createStyleElement(ownerDocument) {
573
+ if (!ownerDocument)
574
+ return void 0;
575
+ const style = ownerDocument.createElement("style");
576
+ const cssText = style.ownerDocument.createTextNode(`
577
+ .______background-clip--text {
578
+ background-clip: text;
579
+ -webkit-background-clip: text;
580
+ }
581
+ `);
582
+ style.appendChild(cssText);
583
+ return style;
584
+ }
585
+ function resolveBoundingBox(node, context) {
586
+ let { width, height } = context;
587
+ if (isElementNode(node) && (!width || !height)) {
588
+ const box = node.getBoundingClientRect();
589
+ width = width || box.width || Number(node.getAttribute("width")) || 0;
590
+ height = height || box.height || Number(node.getAttribute("height")) || 0;
591
+ }
592
+ return { width, height };
593
+ }
594
+ async function imageToCanvas(image, context) {
595
+ const {
596
+ log,
597
+ timeout,
598
+ drawImageCount,
599
+ drawImageInterval
600
+ } = context;
601
+ log.time("image to canvas");
602
+ const loaded = await loadMedia(image, { timeout, onWarn: context.log.warn });
603
+ const { canvas, context2d } = createCanvas(image.ownerDocument, context);
604
+ const drawImage = () => {
605
+ try {
606
+ context2d?.drawImage(loaded, 0, 0, canvas.width, canvas.height);
607
+ } catch (error) {
608
+ context.log.warn("Failed to drawImage", error);
609
+ }
610
+ };
611
+ drawImage();
612
+ if (context.isEnable("fixSvgXmlDecode")) {
613
+ for (let i = 0; i < drawImageCount; i++) {
614
+ await new Promise((resolve) => {
615
+ setTimeout(() => {
616
+ context2d?.clearRect(0, 0, canvas.width, canvas.height);
617
+ drawImage();
618
+ resolve();
619
+ }, i + drawImageInterval);
620
+ });
621
+ }
622
+ }
623
+ context.drawImageCount = 0;
624
+ log.timeEnd("image to canvas");
625
+ return canvas;
626
+ }
627
+ function createCanvas(ownerDocument, context) {
628
+ const { width, height, scale, backgroundColor, maximumCanvasSize: max } = context;
629
+ const canvas = ownerDocument.createElement("canvas");
630
+ canvas.width = Math.floor(width * scale);
631
+ canvas.height = Math.floor(height * scale);
632
+ canvas.style.width = `${width}px`;
633
+ canvas.style.height = `${height}px`;
634
+ if (max) {
635
+ if (canvas.width > max || canvas.height > max) {
636
+ if (canvas.width > max && canvas.height > max) {
637
+ if (canvas.width > canvas.height) {
638
+ canvas.height *= max / canvas.width;
639
+ canvas.width = max;
640
+ } else {
641
+ canvas.width *= max / canvas.height;
642
+ canvas.height = max;
643
+ }
644
+ } else if (canvas.width > max) {
645
+ canvas.height *= max / canvas.width;
646
+ canvas.width = max;
647
+ } else {
648
+ canvas.width *= max / canvas.height;
649
+ canvas.height = max;
650
+ }
651
+ }
652
+ }
653
+ const context2d = canvas.getContext("2d");
654
+ if (context2d && backgroundColor) {
655
+ context2d.fillStyle = backgroundColor;
656
+ context2d.fillRect(0, 0, canvas.width, canvas.height);
657
+ }
658
+ return { canvas, context2d };
659
+ }
660
+ function cloneCanvas(canvas, context) {
661
+ if (canvas.ownerDocument) {
662
+ try {
663
+ const dataURL = canvas.toDataURL();
664
+ if (dataURL !== "data:,") {
665
+ return createImage(dataURL, canvas.ownerDocument);
666
+ }
667
+ } catch (error) {
668
+ context.log.warn("Failed to clone canvas", error);
669
+ }
670
+ }
671
+ const cloned = canvas.cloneNode(false);
672
+ const ctx = canvas.getContext("2d");
673
+ const clonedCtx = cloned.getContext("2d");
674
+ try {
675
+ if (ctx && clonedCtx) {
676
+ clonedCtx.putImageData(
677
+ ctx.getImageData(0, 0, canvas.width, canvas.height),
678
+ 0,
679
+ 0
680
+ );
681
+ }
682
+ return cloned;
683
+ } catch (error) {
684
+ context.log.warn("Failed to clone canvas", error);
685
+ }
686
+ return cloned;
687
+ }
688
+ function cloneIframe(iframe, context) {
689
+ try {
690
+ if (iframe?.contentDocument?.documentElement) {
691
+ return cloneNode(iframe.contentDocument.documentElement, context);
692
+ }
693
+ } catch (error) {
694
+ context.log.warn("Failed to clone iframe", error);
695
+ }
696
+ return iframe.cloneNode(false);
697
+ }
698
+ function cloneImage(image) {
699
+ const cloned = image.cloneNode(false);
700
+ if (image.currentSrc && image.currentSrc !== image.src) {
701
+ cloned.src = image.currentSrc;
702
+ cloned.srcset = "";
703
+ }
704
+ if (cloned.loading === "lazy") {
705
+ cloned.loading = "eager";
706
+ }
707
+ return cloned;
708
+ }
709
+ async function cloneVideo(video, context) {
710
+ if (video.ownerDocument && !video.currentSrc && video.poster) {
711
+ return createImage(video.poster, video.ownerDocument);
712
+ }
713
+ const cloned = video.cloneNode(false);
714
+ cloned.crossOrigin = "anonymous";
715
+ if (video.currentSrc && video.currentSrc !== video.src) {
716
+ cloned.src = video.currentSrc;
717
+ }
718
+ const ownerDocument = cloned.ownerDocument;
719
+ if (ownerDocument) {
720
+ let canPlay = true;
721
+ await loadMedia(cloned, { onError: () => canPlay = false, onWarn: context.log.warn });
722
+ if (!canPlay) {
723
+ if (video.poster) {
724
+ return createImage(video.poster, video.ownerDocument);
725
+ }
726
+ return cloned;
727
+ }
728
+ cloned.currentTime = video.currentTime;
729
+ await new Promise((resolve) => {
730
+ cloned.addEventListener("seeked", resolve, { once: true });
731
+ });
732
+ const canvas = ownerDocument.createElement("canvas");
733
+ canvas.width = video.offsetWidth;
734
+ canvas.height = video.offsetHeight;
735
+ try {
736
+ const ctx = canvas.getContext("2d");
737
+ if (ctx)
738
+ ctx.drawImage(cloned, 0, 0, canvas.width, canvas.height);
739
+ } catch (error) {
740
+ context.log.warn("Failed to clone video", error);
741
+ if (video.poster) {
742
+ return createImage(video.poster, video.ownerDocument);
743
+ }
744
+ return cloned;
745
+ }
746
+ return cloneCanvas(canvas, context);
747
+ }
748
+ return cloned;
749
+ }
750
+ function cloneElement(node, context) {
751
+ if (isCanvasElement(node)) {
752
+ return cloneCanvas(node, context);
753
+ }
754
+ if (isIFrameElement(node)) {
755
+ return cloneIframe(node, context);
756
+ }
757
+ if (isImageElement(node)) {
758
+ return cloneImage(node);
759
+ }
760
+ if (isVideoElement(node)) {
761
+ return cloneVideo(node, context);
762
+ }
763
+ return node.cloneNode(false);
764
+ }
765
+ function getSandBox(context) {
766
+ let sandbox = context.sandbox;
767
+ if (!sandbox) {
768
+ const { ownerDocument } = context;
769
+ try {
770
+ if (ownerDocument) {
771
+ sandbox = ownerDocument.createElement("iframe");
772
+ sandbox.id = `__SANDBOX__${uuid()}`;
773
+ sandbox.width = "0";
774
+ sandbox.height = "0";
775
+ sandbox.style.visibility = "hidden";
776
+ sandbox.style.position = "fixed";
777
+ ownerDocument.body.appendChild(sandbox);
778
+ sandbox.srcdoc = '<!DOCTYPE html><meta charset="UTF-8"><title></title><body>';
779
+ context.sandbox = sandbox;
780
+ }
781
+ } catch (error) {
782
+ context.log.warn("Failed to getSandBox", error);
783
+ }
784
+ }
785
+ return sandbox;
786
+ }
787
+ var ignoredStyles = [
788
+ "width",
789
+ "height",
790
+ "-webkit-text-fill-color"
791
+ ];
792
+ var includedAttributes = [
793
+ "stroke",
794
+ "fill"
795
+ ];
796
+ function getDefaultStyle(node, pseudoElement, context) {
797
+ const { defaultComputedStyles } = context;
798
+ const nodeName = node.nodeName.toLowerCase();
799
+ const isSvgNode = isSVGElementNode(node) && nodeName !== "svg";
800
+ const attributes = isSvgNode ? includedAttributes.map((name) => [name, node.getAttribute(name)]).filter(([, value]) => value !== null) : [];
801
+ const key = [
802
+ isSvgNode && "svg",
803
+ nodeName,
804
+ attributes.map((name, value) => `${name}=${value}`).join(","),
805
+ pseudoElement
806
+ ].filter(Boolean).join(":");
807
+ if (defaultComputedStyles.has(key))
808
+ return defaultComputedStyles.get(key);
809
+ const sandbox = getSandBox(context);
810
+ const sandboxWindow = sandbox?.contentWindow;
811
+ if (!sandboxWindow)
812
+ return /* @__PURE__ */ new Map();
813
+ const sandboxDocument = sandboxWindow?.document;
814
+ let root;
815
+ let el;
816
+ if (isSvgNode) {
817
+ root = sandboxDocument.createElementNS(XMLNS, "svg");
818
+ el = root.ownerDocument.createElementNS(root.namespaceURI, nodeName);
819
+ attributes.forEach(([name, value]) => {
820
+ el.setAttributeNS(null, name, value);
821
+ });
822
+ root.appendChild(el);
823
+ } else {
824
+ root = el = sandboxDocument.createElement(nodeName);
825
+ }
826
+ el.textContent = " ";
827
+ sandboxDocument.body.appendChild(root);
828
+ const computedStyle = sandboxWindow.getComputedStyle(el, pseudoElement);
829
+ const styles = /* @__PURE__ */ new Map();
830
+ for (let len = computedStyle.length, i = 0; i < len; i++) {
831
+ const name = computedStyle.item(i);
832
+ if (ignoredStyles.includes(name))
833
+ continue;
834
+ styles.set(name, computedStyle.getPropertyValue(name));
835
+ }
836
+ sandboxDocument.body.removeChild(root);
837
+ defaultComputedStyles.set(key, styles);
838
+ return styles;
839
+ }
840
+ function getDiffStyle(style, defaultStyle, includeStyleProperties) {
841
+ const diffStyle = /* @__PURE__ */ new Map();
842
+ const prefixs = [];
843
+ const prefixTree = /* @__PURE__ */ new Map();
844
+ if (includeStyleProperties) {
845
+ for (const name of includeStyleProperties) {
846
+ applyTo(name);
847
+ }
848
+ } else {
849
+ for (let len = style.length, i = 0; i < len; i++) {
850
+ const name = style.item(i);
851
+ applyTo(name);
852
+ }
853
+ }
854
+ for (let len = prefixs.length, i = 0; i < len; i++) {
855
+ prefixTree.get(prefixs[i])?.forEach((value, name) => diffStyle.set(name, value));
856
+ }
857
+ function applyTo(name) {
858
+ const value = style.getPropertyValue(name);
859
+ const priority = style.getPropertyPriority(name);
860
+ const subIndex = name.lastIndexOf("-");
861
+ const prefix = subIndex > -1 ? name.substring(0, subIndex) : void 0;
862
+ if (prefix) {
863
+ let map = prefixTree.get(prefix);
864
+ if (!map) {
865
+ map = /* @__PURE__ */ new Map();
866
+ prefixTree.set(prefix, map);
867
+ }
868
+ map.set(name, [value, priority]);
869
+ }
870
+ if (defaultStyle.get(name) === value && !priority)
871
+ return;
872
+ if (prefix) {
873
+ prefixs.push(prefix);
874
+ } else {
875
+ diffStyle.set(name, [value, priority]);
876
+ }
877
+ }
878
+ return diffStyle;
879
+ }
880
+ function copyCssStyles(node, cloned, isRoot, context) {
881
+ const { ownerWindow, includeStyleProperties, currentParentNodeStyle } = context;
882
+ const clonedStyle = cloned.style;
883
+ const computedStyle = ownerWindow.getComputedStyle(node);
884
+ const defaultStyle = getDefaultStyle(node, null, context);
885
+ currentParentNodeStyle?.forEach((_, key) => {
886
+ defaultStyle.delete(key);
887
+ });
888
+ const style = getDiffStyle(computedStyle, defaultStyle, includeStyleProperties);
889
+ style.delete("transition-property");
890
+ style.delete("all");
891
+ style.delete("d");
892
+ style.delete("content");
893
+ if (isRoot) {
894
+ style.delete("position");
895
+ style.delete("margin-top");
896
+ style.delete("margin-right");
897
+ style.delete("margin-bottom");
898
+ style.delete("margin-left");
899
+ style.delete("margin-block-start");
900
+ style.delete("margin-block-end");
901
+ style.delete("margin-inline-start");
902
+ style.delete("margin-inline-end");
903
+ style.set("box-sizing", ["border-box", ""]);
904
+ }
905
+ if (style.get("background-clip")?.[0] === "text") {
906
+ cloned.classList.add("______background-clip--text");
907
+ }
908
+ if (IN_CHROME) {
909
+ if (!style.has("font-kerning"))
910
+ style.set("font-kerning", ["normal", ""]);
911
+ if ((style.get("overflow-x")?.[0] === "hidden" || style.get("overflow-y")?.[0] === "hidden") && style.get("text-overflow")?.[0] === "ellipsis" && node.scrollWidth === node.clientWidth) {
912
+ style.set("text-overflow", ["clip", ""]);
913
+ }
914
+ }
915
+ for (let len = clonedStyle.length, i = 0; i < len; i++) {
916
+ clonedStyle.removeProperty(clonedStyle.item(i));
917
+ }
918
+ style.forEach(([value, priority], name) => {
919
+ clonedStyle.setProperty(name, value, priority);
920
+ });
921
+ return style;
922
+ }
923
+ function copyInputValue(node, cloned) {
924
+ if (isTextareaElement(node) || isInputElement(node) || isSelectElement(node)) {
925
+ cloned.setAttribute("value", node.value);
926
+ }
927
+ }
928
+ var pseudoClasses = [
929
+ "::before",
930
+ "::after"
931
+ // '::placeholder', TODO
932
+ ];
933
+ var scrollbarPseudoClasses = [
934
+ "::-webkit-scrollbar",
935
+ "::-webkit-scrollbar-button",
936
+ // '::-webkit-scrollbar:horizontal', TODO
937
+ "::-webkit-scrollbar-thumb",
938
+ "::-webkit-scrollbar-track",
939
+ "::-webkit-scrollbar-track-piece",
940
+ // '::-webkit-scrollbar:vertical', TODO
941
+ "::-webkit-scrollbar-corner",
942
+ "::-webkit-resizer"
943
+ ];
944
+ function copyPseudoClass(node, cloned, copyScrollbar, context, addWordToFontFamilies) {
945
+ const { ownerWindow, svgStyleElement, svgStyles, currentNodeStyle } = context;
946
+ if (!svgStyleElement || !ownerWindow)
947
+ return;
948
+ function copyBy(pseudoClass) {
949
+ const computedStyle = ownerWindow.getComputedStyle(node, pseudoClass);
950
+ let content = computedStyle.getPropertyValue("content");
951
+ if (!content || content === "none")
952
+ return;
953
+ addWordToFontFamilies?.(content);
954
+ content = content.replace(/(')|(")|(counter\(.+\))/g, "");
955
+ const klasses = [uuid()];
956
+ const defaultStyle = getDefaultStyle(node, pseudoClass, context);
957
+ currentNodeStyle?.forEach((_, key) => {
958
+ defaultStyle.delete(key);
959
+ });
960
+ const style = getDiffStyle(computedStyle, defaultStyle, context.includeStyleProperties);
961
+ style.delete("content");
962
+ style.delete("-webkit-locale");
963
+ if (style.get("background-clip")?.[0] === "text") {
964
+ cloned.classList.add("______background-clip--text");
965
+ }
966
+ const cloneStyle = [
967
+ `content: '${content}';`
968
+ ];
969
+ style.forEach(([value, priority], name) => {
970
+ cloneStyle.push(`${name}: ${value}${priority ? " !important" : ""};`);
971
+ });
972
+ if (cloneStyle.length === 1)
973
+ return;
974
+ try {
975
+ cloned.className = [cloned.className, ...klasses].join(" ");
976
+ } catch (err) {
977
+ context.log.warn("Failed to copyPseudoClass", err);
978
+ return;
979
+ }
980
+ const cssText = cloneStyle.join("\n ");
981
+ let allClasses = svgStyles.get(cssText);
982
+ if (!allClasses) {
983
+ allClasses = [];
984
+ svgStyles.set(cssText, allClasses);
985
+ }
986
+ allClasses.push(`.${klasses[0]}${pseudoClass}`);
987
+ }
988
+ pseudoClasses.forEach(copyBy);
989
+ if (copyScrollbar)
990
+ scrollbarPseudoClasses.forEach(copyBy);
991
+ }
992
+ var excludeParentNodes = /* @__PURE__ */ new Set([
993
+ "symbol"
994
+ // test/fixtures/svg.symbol.html
995
+ ]);
996
+ async function appendChildNode(node, cloned, child, context, addWordToFontFamilies) {
997
+ if (isElementNode(child) && (isStyleElement(child) || isScriptElement(child)))
998
+ return;
999
+ if (context.filter && !context.filter(child))
1000
+ return;
1001
+ if (excludeParentNodes.has(cloned.nodeName) || excludeParentNodes.has(child.nodeName)) {
1002
+ context.currentParentNodeStyle = void 0;
1003
+ } else {
1004
+ context.currentParentNodeStyle = context.currentNodeStyle;
1005
+ }
1006
+ const childCloned = await cloneNode(child, context, false, addWordToFontFamilies);
1007
+ if (context.isEnable("restoreScrollPosition")) {
1008
+ restoreScrollPosition(node, childCloned);
1009
+ }
1010
+ cloned.appendChild(childCloned);
1011
+ }
1012
+ async function cloneChildNodes(node, cloned, context, addWordToFontFamilies) {
1013
+ let firstChild = node.firstChild;
1014
+ if (isElementNode(node)) {
1015
+ if (node.shadowRoot) {
1016
+ firstChild = node.shadowRoot?.firstChild;
1017
+ context.shadowRoots.push(node.shadowRoot);
1018
+ }
1019
+ }
1020
+ for (let child = firstChild; child; child = child.nextSibling) {
1021
+ if (isCommentNode(child))
1022
+ continue;
1023
+ if (isElementNode(child) && isSlotElement(child) && typeof child.assignedNodes === "function") {
1024
+ const nodes = child.assignedNodes();
1025
+ for (let i = 0; i < nodes.length; i++) {
1026
+ await appendChildNode(node, cloned, nodes[i], context, addWordToFontFamilies);
1027
+ }
1028
+ } else {
1029
+ await appendChildNode(node, cloned, child, context, addWordToFontFamilies);
1030
+ }
1031
+ }
1032
+ }
1033
+ function restoreScrollPosition(node, chlidCloned) {
1034
+ if (!isHTMLElementNode(node) || !isHTMLElementNode(chlidCloned))
1035
+ return;
1036
+ const { scrollTop, scrollLeft } = node;
1037
+ if (!scrollTop && !scrollLeft) {
1038
+ return;
1039
+ }
1040
+ const { transform } = chlidCloned.style;
1041
+ const matrix = new DOMMatrix(transform);
1042
+ const { a, b, c, d } = matrix;
1043
+ matrix.a = 1;
1044
+ matrix.b = 0;
1045
+ matrix.c = 0;
1046
+ matrix.d = 1;
1047
+ matrix.translateSelf(-scrollLeft, -scrollTop);
1048
+ matrix.a = a;
1049
+ matrix.b = b;
1050
+ matrix.c = c;
1051
+ matrix.d = d;
1052
+ chlidCloned.style.transform = matrix.toString();
1053
+ }
1054
+ function applyCssStyleWithOptions(cloned, context) {
1055
+ const { backgroundColor, width, height, style: styles } = context;
1056
+ const clonedStyle = cloned.style;
1057
+ if (backgroundColor)
1058
+ clonedStyle.setProperty("background-color", backgroundColor, "important");
1059
+ if (width)
1060
+ clonedStyle.setProperty("width", `${width}px`, "important");
1061
+ if (height)
1062
+ clonedStyle.setProperty("height", `${height}px`, "important");
1063
+ if (styles) {
1064
+ for (const name in styles) clonedStyle[name] = styles[name];
1065
+ }
1066
+ }
1067
+ var NORMAL_ATTRIBUTE_RE = /^[\w-:]+$/;
1068
+ async function cloneNode(node, context, isRoot = false, addWordToFontFamilies) {
1069
+ const { ownerDocument, ownerWindow, fontFamilies, onCloneEachNode } = context;
1070
+ if (ownerDocument && isTextNode(node)) {
1071
+ if (addWordToFontFamilies && /\S/.test(node.data)) {
1072
+ addWordToFontFamilies(node.data);
1073
+ }
1074
+ return ownerDocument.createTextNode(node.data);
1075
+ }
1076
+ if (ownerDocument && ownerWindow && isElementNode(node) && (isHTMLElementNode(node) || isSVGElementNode(node))) {
1077
+ const cloned2 = await cloneElement(node, context);
1078
+ if (context.isEnable("removeAbnormalAttributes")) {
1079
+ const names = cloned2.getAttributeNames();
1080
+ for (let len = names.length, i = 0; i < len; i++) {
1081
+ const name = names[i];
1082
+ if (!NORMAL_ATTRIBUTE_RE.test(name)) {
1083
+ cloned2.removeAttribute(name);
1084
+ }
1085
+ }
1086
+ }
1087
+ const style = context.currentNodeStyle = copyCssStyles(node, cloned2, isRoot, context);
1088
+ if (isRoot)
1089
+ applyCssStyleWithOptions(cloned2, context);
1090
+ let copyScrollbar = false;
1091
+ if (context.isEnable("copyScrollbar")) {
1092
+ const overflow = [
1093
+ style.get("overflow-x")?.[0],
1094
+ style.get("overflow-y")?.[0]
1095
+ ];
1096
+ copyScrollbar = overflow.includes("scroll") || (overflow.includes("auto") || overflow.includes("overlay")) && (node.scrollHeight > node.clientHeight || node.scrollWidth > node.clientWidth);
1097
+ }
1098
+ const textTransform = style.get("text-transform")?.[0];
1099
+ const families = splitFontFamily(style.get("font-family")?.[0]);
1100
+ const addWordToFontFamilies2 = families ? (word) => {
1101
+ if (textTransform === "uppercase") {
1102
+ word = word.toUpperCase();
1103
+ } else if (textTransform === "lowercase") {
1104
+ word = word.toLowerCase();
1105
+ } else if (textTransform === "capitalize") {
1106
+ word = word[0].toUpperCase() + word.substring(1);
1107
+ }
1108
+ families.forEach((family) => {
1109
+ let fontFamily = fontFamilies.get(family);
1110
+ if (!fontFamily) {
1111
+ fontFamilies.set(family, fontFamily = /* @__PURE__ */ new Set());
1112
+ }
1113
+ word.split("").forEach((text) => fontFamily.add(text));
1114
+ });
1115
+ } : void 0;
1116
+ copyPseudoClass(
1117
+ node,
1118
+ cloned2,
1119
+ copyScrollbar,
1120
+ context,
1121
+ addWordToFontFamilies2
1122
+ );
1123
+ copyInputValue(node, cloned2);
1124
+ if (!isVideoElement(node)) {
1125
+ await cloneChildNodes(
1126
+ node,
1127
+ cloned2,
1128
+ context,
1129
+ addWordToFontFamilies2
1130
+ );
1131
+ }
1132
+ await onCloneEachNode?.(cloned2);
1133
+ return cloned2;
1134
+ }
1135
+ const cloned = node.cloneNode(false);
1136
+ await cloneChildNodes(node, cloned, context);
1137
+ await onCloneEachNode?.(cloned);
1138
+ return cloned;
1139
+ }
1140
+ function destroyContext(context) {
1141
+ context.ownerDocument = void 0;
1142
+ context.ownerWindow = void 0;
1143
+ context.svgStyleElement = void 0;
1144
+ context.svgDefsElement = void 0;
1145
+ context.svgStyles.clear();
1146
+ context.defaultComputedStyles.clear();
1147
+ if (context.sandbox) {
1148
+ try {
1149
+ context.sandbox.remove();
1150
+ } catch (err) {
1151
+ context.log.warn("Failed to destroyContext", err);
1152
+ }
1153
+ context.sandbox = void 0;
1154
+ }
1155
+ context.workers = [];
1156
+ context.fontFamilies.clear();
1157
+ context.fontCssTexts.clear();
1158
+ context.requests.clear();
1159
+ context.tasks = [];
1160
+ context.shadowRoots = [];
1161
+ }
1162
+ function baseFetch(options) {
1163
+ const { url, timeout, responseType, ...requestInit } = options;
1164
+ const controller = new AbortController();
1165
+ const timer = timeout ? setTimeout(() => controller.abort(), timeout) : void 0;
1166
+ return fetch(url, { signal: controller.signal, ...requestInit }).then((response) => {
1167
+ if (!response.ok) {
1168
+ throw new Error("Failed fetch, not 2xx response", { cause: response });
1169
+ }
1170
+ switch (responseType) {
1171
+ case "arrayBuffer":
1172
+ return response.arrayBuffer();
1173
+ case "dataUrl":
1174
+ return response.blob().then(blobToDataUrl);
1175
+ case "text":
1176
+ default:
1177
+ return response.text();
1178
+ }
1179
+ }).finally(() => clearTimeout(timer));
1180
+ }
1181
+ function contextFetch(context, options) {
1182
+ const { url: rawUrl, requestType = "text", responseType = "text", imageDom } = options;
1183
+ let url = rawUrl;
1184
+ const {
1185
+ timeout,
1186
+ acceptOfImage,
1187
+ requests,
1188
+ fetchFn,
1189
+ fetch: {
1190
+ requestInit,
1191
+ bypassingCache,
1192
+ placeholderImage
1193
+ },
1194
+ font,
1195
+ workers,
1196
+ fontFamilies
1197
+ } = context;
1198
+ if (requestType === "image" && (IN_SAFARI || IN_FIREFOX)) {
1199
+ context.drawImageCount++;
1200
+ }
1201
+ let request = requests.get(rawUrl);
1202
+ if (!request) {
1203
+ if (bypassingCache) {
1204
+ if (bypassingCache instanceof RegExp && bypassingCache.test(url)) {
1205
+ url += (/\?/.test(url) ? "&" : "?") + (/* @__PURE__ */ new Date()).getTime();
1206
+ }
1207
+ }
1208
+ const canFontMinify = requestType.startsWith("font") && font && font.minify;
1209
+ const fontTexts = /* @__PURE__ */ new Set();
1210
+ if (canFontMinify) {
1211
+ const families = requestType.split(";")[1].split(",");
1212
+ families.forEach((family) => {
1213
+ if (!fontFamilies.has(family))
1214
+ return;
1215
+ fontFamilies.get(family).forEach((text) => fontTexts.add(text));
1216
+ });
1217
+ }
1218
+ const needFontMinify = canFontMinify && fontTexts.size;
1219
+ const baseFetchOptions = {
1220
+ url,
1221
+ timeout,
1222
+ responseType: needFontMinify ? "arrayBuffer" : responseType,
1223
+ headers: requestType === "image" ? { accept: acceptOfImage } : void 0,
1224
+ ...requestInit
1225
+ };
1226
+ request = {
1227
+ type: requestType,
1228
+ resolve: void 0,
1229
+ reject: void 0,
1230
+ response: null
1231
+ };
1232
+ request.response = (async () => {
1233
+ if (fetchFn && requestType === "image") {
1234
+ const result = await fetchFn(rawUrl);
1235
+ if (result)
1236
+ return result;
1237
+ }
1238
+ if (!IN_SAFARI && rawUrl.startsWith("http") && workers.length) {
1239
+ return new Promise((resolve, reject) => {
1240
+ const worker = workers[requests.size & workers.length - 1];
1241
+ worker.postMessage({ rawUrl, ...baseFetchOptions });
1242
+ request.resolve = resolve;
1243
+ request.reject = reject;
1244
+ });
1245
+ }
1246
+ return baseFetch(baseFetchOptions);
1247
+ })().catch((error) => {
1248
+ requests.delete(rawUrl);
1249
+ if (requestType === "image" && placeholderImage) {
1250
+ context.log.warn("Failed to fetch image base64, trying to use placeholder image", url);
1251
+ return typeof placeholderImage === "string" ? placeholderImage : placeholderImage(imageDom);
1252
+ }
1253
+ throw error;
1254
+ });
1255
+ requests.set(rawUrl, request);
1256
+ }
1257
+ return request.response;
1258
+ }
1259
+ async function replaceCssUrlToDataUrl(cssText, baseUrl, context, isImage) {
1260
+ if (!hasCssUrl(cssText))
1261
+ return cssText;
1262
+ for (const [rawUrl, url] of parseCssUrls(cssText, baseUrl)) {
1263
+ try {
1264
+ const dataUrl = await contextFetch(
1265
+ context,
1266
+ {
1267
+ url,
1268
+ requestType: isImage ? "image" : "text",
1269
+ responseType: "dataUrl"
1270
+ }
1271
+ );
1272
+ cssText = cssText.replace(toRE(rawUrl), `$1${dataUrl}$3`);
1273
+ } catch (error) {
1274
+ context.log.warn("Failed to fetch css data url", rawUrl, error);
1275
+ }
1276
+ }
1277
+ return cssText;
1278
+ }
1279
+ function hasCssUrl(cssText) {
1280
+ return /url\((['"]?)([^'"]+?)\1\)/.test(cssText);
1281
+ }
1282
+ var URL_RE = /url\((['"]?)([^'"]+?)\1\)/g;
1283
+ function parseCssUrls(cssText, baseUrl) {
1284
+ const result = [];
1285
+ cssText.replace(URL_RE, (raw, quotation, url) => {
1286
+ result.push([url, resolveUrl(url, baseUrl)]);
1287
+ return raw;
1288
+ });
1289
+ return result.filter(([url]) => !isDataUrl(url));
1290
+ }
1291
+ function toRE(url) {
1292
+ const escaped = url.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
1293
+ return new RegExp(`(url\\(['"]?)(${escaped})(['"]?\\))`, "g");
1294
+ }
1295
+ var properties = [
1296
+ "background-image",
1297
+ "border-image-source",
1298
+ "-webkit-border-image",
1299
+ "-webkit-mask-image",
1300
+ "list-style-image"
1301
+ ];
1302
+ function embedCssStyleImage(style, context) {
1303
+ return properties.map((property) => {
1304
+ const value = style.getPropertyValue(property);
1305
+ if (!value || value === "none") {
1306
+ return null;
1307
+ }
1308
+ if (IN_SAFARI || IN_FIREFOX) {
1309
+ context.drawImageCount++;
1310
+ }
1311
+ return replaceCssUrlToDataUrl(value, null, context, true).then((newValue) => {
1312
+ if (!newValue || value === newValue)
1313
+ return;
1314
+ style.setProperty(
1315
+ property,
1316
+ newValue,
1317
+ style.getPropertyPriority(property)
1318
+ );
1319
+ });
1320
+ }).filter(Boolean);
1321
+ }
1322
+ function embedImageElement(cloned, context) {
1323
+ if (isImageElement(cloned)) {
1324
+ const originalSrc = cloned.currentSrc || cloned.src;
1325
+ if (!isDataUrl(originalSrc)) {
1326
+ return [
1327
+ contextFetch(context, {
1328
+ url: originalSrc,
1329
+ imageDom: cloned,
1330
+ requestType: "image",
1331
+ responseType: "dataUrl"
1332
+ }).then((url) => {
1333
+ if (!url)
1334
+ return;
1335
+ cloned.srcset = "";
1336
+ cloned.dataset.originalSrc = originalSrc;
1337
+ cloned.src = url || "";
1338
+ })
1339
+ ];
1340
+ }
1341
+ if (IN_SAFARI || IN_FIREFOX) {
1342
+ context.drawImageCount++;
1343
+ }
1344
+ } else if (isSVGElementNode(cloned) && !isDataUrl(cloned.href.baseVal)) {
1345
+ const originalSrc = cloned.href.baseVal;
1346
+ return [
1347
+ contextFetch(context, {
1348
+ url: originalSrc,
1349
+ imageDom: cloned,
1350
+ requestType: "image",
1351
+ responseType: "dataUrl"
1352
+ }).then((url) => {
1353
+ if (!url)
1354
+ return;
1355
+ cloned.dataset.originalSrc = originalSrc;
1356
+ cloned.href.baseVal = url || "";
1357
+ })
1358
+ ];
1359
+ }
1360
+ return [];
1361
+ }
1362
+ function embedSvgUse(cloned, context) {
1363
+ const { ownerDocument, svgDefsElement } = context;
1364
+ const href = cloned.getAttribute("href") ?? cloned.getAttribute("xlink:href");
1365
+ if (!href)
1366
+ return [];
1367
+ const [svgUrl, id] = href.split("#");
1368
+ if (id) {
1369
+ const query = `#${id}`;
1370
+ const definition = context.shadowRoots.reduce(
1371
+ (res, root) => {
1372
+ return res ?? root.querySelector(`svg ${query}`);
1373
+ },
1374
+ ownerDocument?.querySelector(`svg ${query}`)
1375
+ );
1376
+ if (svgUrl) {
1377
+ cloned.setAttribute("href", query);
1378
+ }
1379
+ if (svgDefsElement?.querySelector(query))
1380
+ return [];
1381
+ if (definition) {
1382
+ svgDefsElement?.appendChild(definition.cloneNode(true));
1383
+ return [];
1384
+ } else if (svgUrl) {
1385
+ return [
1386
+ contextFetch(context, {
1387
+ url: svgUrl,
1388
+ responseType: "text"
1389
+ }).then((svgData) => {
1390
+ svgDefsElement?.insertAdjacentHTML("beforeend", svgData);
1391
+ })
1392
+ ];
1393
+ }
1394
+ }
1395
+ return [];
1396
+ }
1397
+ function embedNode(cloned, context) {
1398
+ const { tasks } = context;
1399
+ if (isElementNode(cloned)) {
1400
+ if (isImageElement(cloned) || isSVGImageElementNode(cloned)) {
1401
+ tasks.push(...embedImageElement(cloned, context));
1402
+ }
1403
+ if (isSVGUseElementNode(cloned)) {
1404
+ tasks.push(...embedSvgUse(cloned, context));
1405
+ }
1406
+ }
1407
+ if (isHTMLElementNode(cloned)) {
1408
+ tasks.push(...embedCssStyleImage(cloned.style, context));
1409
+ }
1410
+ cloned.childNodes.forEach((child) => {
1411
+ embedNode(child, context);
1412
+ });
1413
+ }
1414
+ async function embedWebFont(clone, context) {
1415
+ const {
1416
+ ownerDocument,
1417
+ svgStyleElement,
1418
+ fontFamilies,
1419
+ fontCssTexts,
1420
+ tasks,
1421
+ font
1422
+ } = context;
1423
+ if (!ownerDocument || !svgStyleElement || !fontFamilies.size) {
1424
+ return;
1425
+ }
1426
+ if (font && font.cssText) {
1427
+ const cssText = filterPreferredFormat(font.cssText, context);
1428
+ svgStyleElement.appendChild(ownerDocument.createTextNode(`${cssText}
1429
+ `));
1430
+ } else {
1431
+ const styleSheets = Array.from(ownerDocument.styleSheets).filter((styleSheet) => {
1432
+ try {
1433
+ return "cssRules" in styleSheet && Boolean(styleSheet.cssRules.length);
1434
+ } catch (error) {
1435
+ context.log.warn(`Error while reading CSS rules from ${styleSheet.href}`, error);
1436
+ return false;
1437
+ }
1438
+ });
1439
+ const tempDoc = ownerDocument.implementation.createHTMLDocument("");
1440
+ const tempStyleEl = tempDoc.createElement("style");
1441
+ tempDoc.head.appendChild(tempStyleEl);
1442
+ const tempStyleSheet = tempStyleEl.sheet;
1443
+ await Promise.all(
1444
+ styleSheets.flatMap((styleSheet) => {
1445
+ return Array.from(styleSheet.cssRules).map(async (cssRule) => {
1446
+ if (isCSSImportRule(cssRule)) {
1447
+ const baseUrl = cssRule.href;
1448
+ let cssText = "";
1449
+ try {
1450
+ cssText = await contextFetch(context, {
1451
+ url: baseUrl,
1452
+ requestType: "text",
1453
+ responseType: "text"
1454
+ });
1455
+ } catch (error) {
1456
+ context.log.warn(`Error fetch remote css import from ${baseUrl}`, error);
1457
+ }
1458
+ const replacedCssText = cssText.replace(
1459
+ URL_RE,
1460
+ (raw, quotation, url) => raw.replace(url, resolveUrl(url, baseUrl))
1461
+ );
1462
+ for (const rule of parseCss(replacedCssText)) {
1463
+ try {
1464
+ tempStyleSheet.insertRule(rule, tempStyleSheet.cssRules.length);
1465
+ } catch (error) {
1466
+ context.log.warn("Error inserting rule from remote css import", { rule, error });
1467
+ }
1468
+ }
1469
+ }
1470
+ });
1471
+ })
1472
+ );
1473
+ if (tempStyleSheet.cssRules.length)
1474
+ styleSheets.push(tempStyleSheet);
1475
+ const cssRules = [];
1476
+ styleSheets.forEach((sheet) => {
1477
+ unwrapCssLayers(sheet.cssRules, cssRules);
1478
+ });
1479
+ cssRules.filter((cssRule) => isCssFontFaceRule(cssRule) && hasCssUrl(cssRule.style.getPropertyValue("src")) && splitFontFamily(cssRule.style.getPropertyValue("font-family"))?.some((val) => fontFamilies.has(val))).forEach((value) => {
1480
+ const rule = value;
1481
+ const cssText = fontCssTexts.get(rule.cssText);
1482
+ if (cssText) {
1483
+ svgStyleElement.appendChild(ownerDocument.createTextNode(`${cssText}
1484
+ `));
1485
+ } else {
1486
+ tasks.push(
1487
+ replaceCssUrlToDataUrl(
1488
+ rule.cssText,
1489
+ rule.parentStyleSheet ? rule.parentStyleSheet.href : null,
1490
+ context
1491
+ ).then((cssText2) => {
1492
+ cssText2 = filterPreferredFormat(cssText2, context);
1493
+ fontCssTexts.set(rule.cssText, cssText2);
1494
+ svgStyleElement.appendChild(ownerDocument.createTextNode(`${cssText2}
1495
+ `));
1496
+ })
1497
+ );
1498
+ }
1499
+ });
1500
+ }
1501
+ }
1502
+ var COMMENTS_RE = /(\/\*[\s\S]*?\*\/)/g;
1503
+ var KEYFRAMES_RE = /((@.*?keyframes [\s\S]*?){([\s\S]*?}\s*?)})/gi;
1504
+ function parseCss(source) {
1505
+ if (source == null)
1506
+ return [];
1507
+ const result = [];
1508
+ let cssText = source.replace(COMMENTS_RE, "");
1509
+ while (true) {
1510
+ const matches = KEYFRAMES_RE.exec(cssText);
1511
+ if (!matches)
1512
+ break;
1513
+ result.push(matches[0]);
1514
+ }
1515
+ cssText = cssText.replace(KEYFRAMES_RE, "");
1516
+ const IMPORT_RE = /@import[\s\S]*?url\([^)]*\)[\s\S]*?;/gi;
1517
+ const UNIFIED_RE = new RegExp(
1518
+ // eslint-disable-next-line
1519
+ "((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})",
1520
+ "gi"
1521
+ );
1522
+ while (true) {
1523
+ let matches = IMPORT_RE.exec(cssText);
1524
+ if (!matches) {
1525
+ matches = UNIFIED_RE.exec(cssText);
1526
+ if (!matches) {
1527
+ break;
1528
+ } else {
1529
+ IMPORT_RE.lastIndex = UNIFIED_RE.lastIndex;
1530
+ }
1531
+ } else {
1532
+ UNIFIED_RE.lastIndex = IMPORT_RE.lastIndex;
1533
+ }
1534
+ result.push(matches[0]);
1535
+ }
1536
+ return result;
1537
+ }
1538
+ var URL_WITH_FORMAT_RE = /url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g;
1539
+ var FONT_SRC_RE = /src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;
1540
+ function filterPreferredFormat(str, context) {
1541
+ const { font } = context;
1542
+ const preferredFormat = font ? font?.preferredFormat : void 0;
1543
+ return preferredFormat ? str.replace(FONT_SRC_RE, (match) => {
1544
+ while (true) {
1545
+ const [src, , format] = URL_WITH_FORMAT_RE.exec(match) || [];
1546
+ if (!format)
1547
+ return "";
1548
+ if (format === preferredFormat)
1549
+ return `src: ${src};`;
1550
+ }
1551
+ }) : str;
1552
+ }
1553
+ function unwrapCssLayers(rules, out = []) {
1554
+ for (const rule of Array.from(rules)) {
1555
+ if (isLayerBlockRule(rule)) {
1556
+ out.push(...unwrapCssLayers(rule.cssRules));
1557
+ } else if ("cssRules" in rule) {
1558
+ unwrapCssLayers(rule.cssRules, out);
1559
+ } else {
1560
+ out.push(rule);
1561
+ }
1562
+ }
1563
+ return out;
1564
+ }
1565
+ var SVG_EXTERNAL_RESOURCE_REGEX = /\bx?link:?href\s*=\s*["'](?!data:)[^"']+["']/i;
1566
+ function svgHasExternalResources(svg) {
1567
+ return SVG_EXTERNAL_RESOURCE_REGEX.test(svg.innerHTML);
1568
+ }
1569
+ async function domToForeignObjectSvg(node, options) {
1570
+ const context = await orCreateContext(node, options);
1571
+ if (isElementNode(context.node) && isSVGElementNode(context.node) && !svgHasExternalResources(context.node))
1572
+ return context.node;
1573
+ const {
1574
+ ownerDocument,
1575
+ log,
1576
+ tasks,
1577
+ svgStyleElement,
1578
+ svgDefsElement,
1579
+ svgStyles,
1580
+ font,
1581
+ progress,
1582
+ autoDestruct,
1583
+ onCloneNode,
1584
+ onEmbedNode,
1585
+ onCreateForeignObjectSvg
1586
+ } = context;
1587
+ log.time("clone node");
1588
+ const clone = await cloneNode(context.node, context, true);
1589
+ if (svgStyleElement && ownerDocument) {
1590
+ let allCssText = "";
1591
+ svgStyles.forEach((klasses, cssText) => {
1592
+ allCssText += `${klasses.join(",\n")} {
1593
+ ${cssText}
1594
+ }
1595
+ `;
1596
+ });
1597
+ svgStyleElement.appendChild(ownerDocument.createTextNode(allCssText));
1598
+ }
1599
+ log.timeEnd("clone node");
1600
+ await onCloneNode?.(clone);
1601
+ if (font !== false && isElementNode(clone)) {
1602
+ log.time("embed web font");
1603
+ await embedWebFont(clone, context);
1604
+ log.timeEnd("embed web font");
1605
+ }
1606
+ log.time("embed node");
1607
+ embedNode(clone, context);
1608
+ const count = tasks.length;
1609
+ let current = 0;
1610
+ const runTask = async () => {
1611
+ while (true) {
1612
+ const task = tasks.pop();
1613
+ if (!task)
1614
+ break;
1615
+ try {
1616
+ await task;
1617
+ } catch (error) {
1618
+ context.log.warn("Failed to run task", error);
1619
+ }
1620
+ progress?.(++current, count);
1621
+ }
1622
+ };
1623
+ progress?.(current, count);
1624
+ await Promise.all([...Array.from({ length: 4 })].map(runTask));
1625
+ log.timeEnd("embed node");
1626
+ await onEmbedNode?.(clone);
1627
+ const svg = createForeignObjectSvg(clone, context);
1628
+ svgDefsElement && svg.insertBefore(svgDefsElement, svg.children[0]);
1629
+ svgStyleElement && svg.insertBefore(svgStyleElement, svg.children[0]);
1630
+ autoDestruct && destroyContext(context);
1631
+ await onCreateForeignObjectSvg?.(svg);
1632
+ return svg;
1633
+ }
1634
+ function createForeignObjectSvg(clone, context) {
1635
+ const { width, height } = context;
1636
+ const svg = createSvg(width, height, clone.ownerDocument);
1637
+ const foreignObject = svg.ownerDocument.createElementNS(svg.namespaceURI, "foreignObject");
1638
+ foreignObject.setAttributeNS(null, "x", "0%");
1639
+ foreignObject.setAttributeNS(null, "y", "0%");
1640
+ foreignObject.setAttributeNS(null, "width", "100%");
1641
+ foreignObject.setAttributeNS(null, "height", "100%");
1642
+ foreignObject.append(clone);
1643
+ svg.appendChild(foreignObject);
1644
+ return svg;
1645
+ }
1646
+ async function domToCanvas(node, options) {
1647
+ const context = await orCreateContext(node, options);
1648
+ const svg = await domToForeignObjectSvg(context);
1649
+ const dataUrl = svgToDataUrl(svg, context.isEnable("removeControlCharacter"));
1650
+ if (!context.autoDestruct) {
1651
+ context.svgStyleElement = createStyleElement(context.ownerDocument);
1652
+ context.svgDefsElement = context.ownerDocument?.createElementNS(XMLNS, "defs");
1653
+ context.svgStyles.clear();
1654
+ }
1655
+ const image = createImage(dataUrl, svg.ownerDocument);
1656
+ return await imageToCanvas(image, context);
1657
+ }
1658
+
1659
+ // src/picker/screenshot.ts
1660
+ var CROP_MARGIN_RATIO = 0.25;
1661
+ var CROP_MARGIN_MAX_PX = 160;
1662
+ var CROP_MARGIN_MIN_PX = 28;
1663
+ var CROP_MAX_WIDTH = 1600;
1664
+ var CROP_MAX_HEIGHT = 1200;
1665
+ var OUTPUT_MAX_DPR = 2;
1666
+ var OUTPUT_MAX_PIXELS = 26e5;
1667
+ var DATA_URL_SOFT_MAX = 36e5;
1668
+ var RENDER_TIMEOUT_MS = 8e3;
1669
+ var PII_PATTERNS = [
1670
+ /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
1671
+ /\b\d{3}-\d{2}-\d{4}\b/g,
1672
+ /\b\d(?:[ -]?\d){12,18}\b/g
1673
+ ];
1674
+ var MASK = "[masked]";
1675
+ var OUTLINE_COLOR = "#f04e23";
1676
+ function maskPiiText(text) {
1677
+ let masked = text;
1678
+ for (const pattern of PII_PATTERNS) {
1679
+ pattern.lastIndex = 0;
1680
+ masked = masked.replace(pattern, MASK);
1681
+ }
1682
+ return masked;
1683
+ }
1684
+ function cropMargin(size) {
1685
+ return Math.min(
1686
+ Math.max(size * CROP_MARGIN_RATIO, CROP_MARGIN_MIN_PX),
1687
+ CROP_MARGIN_MAX_PX
1688
+ );
1689
+ }
1690
+ function computeCropRect(target, viewportWidth, viewportHeight) {
1691
+ const marginX = cropMargin(target.width);
1692
+ const marginY = cropMargin(target.height);
1693
+ let left = Math.max(0, target.left - marginX);
1694
+ let top = Math.max(0, target.top - marginY);
1695
+ const right = Math.min(viewportWidth, target.left + target.width + marginX);
1696
+ const bottom = Math.min(viewportHeight, target.top + target.height + marginY);
1697
+ const width = Math.min(right - left, CROP_MAX_WIDTH);
1698
+ const height = Math.min(bottom - top, CROP_MAX_HEIGHT);
1699
+ if (right - left > width) left += (right - left - width) / 2;
1700
+ if (bottom - top > height) top += (bottom - top - height) / 2;
1701
+ return { left, top, width: Math.max(0, width), height: Math.max(0, height) };
1702
+ }
1703
+ function isTextualField(el) {
1704
+ if (el instanceof HTMLTextAreaElement) return true;
1705
+ if (!(el instanceof HTMLInputElement)) return false;
1706
+ const type = el.type;
1707
+ return type !== "checkbox" && type !== "radio" && type !== "hidden" && type !== "file";
1708
+ }
1709
+ function isCapturable(node) {
1710
+ return !(node instanceof Element && node.hasAttribute(HOST_ATTRIBUTE));
1711
+ }
1712
+ function maskClone(root) {
1713
+ const doc = root.ownerDocument ?? root;
1714
+ const view = doc.defaultView;
1715
+ const walker = doc.createTreeWalker(
1716
+ root,
1717
+ (view?.NodeFilter ?? NodeFilter).SHOW_ELEMENT | (view?.NodeFilter ?? NodeFilter).SHOW_TEXT
1718
+ );
1719
+ const hosts = [];
1720
+ let node = walker.currentNode;
1721
+ while (node) {
1722
+ if (node.nodeType === 3) {
1723
+ const text = node.nodeValue;
1724
+ if (text && text.trim()) {
1725
+ const masked = maskPiiText(text);
1726
+ if (masked !== text) node.nodeValue = masked;
1727
+ }
1728
+ } else if (node instanceof Element || node.tagName) {
1729
+ const el = node;
1730
+ if (el.hasAttribute(HOST_ATTRIBUTE)) {
1731
+ hosts.push(el);
1732
+ } else if (isTextualField(el)) {
1733
+ const isPassword = el instanceof HTMLInputElement && el.type === "password";
1734
+ const raw = el.getAttribute("value") ?? el.value ?? "";
1735
+ const masked = isPassword && raw ? MASK : maskPiiText(raw);
1736
+ if (el instanceof HTMLTextAreaElement) {
1737
+ el.textContent = masked;
1738
+ el.value = masked;
1739
+ } else {
1740
+ el.setAttribute("value", masked);
1741
+ el.value = masked;
1742
+ }
1743
+ const placeholder = el.getAttribute("placeholder");
1744
+ if (placeholder) el.setAttribute("placeholder", maskPiiText(placeholder));
1745
+ }
1746
+ }
1747
+ node = walker.nextNode();
1748
+ }
1749
+ for (const host of hosts) host.parentNode?.removeChild(host);
1750
+ }
1751
+ function drawOutline(ctx, targetRect, cropRect, scale) {
1752
+ ctx.save();
1753
+ ctx.strokeStyle = OUTLINE_COLOR;
1754
+ ctx.lineWidth = 2 * scale;
1755
+ const inset = scale;
1756
+ ctx.strokeRect(
1757
+ (targetRect.left - cropRect.left) * scale + inset,
1758
+ (targetRect.top - cropRect.top) * scale + inset,
1759
+ targetRect.width * scale - inset * 2,
1760
+ targetRect.height * scale - inset * 2
1761
+ );
1762
+ ctx.restore();
1763
+ }
1764
+ function outputScale(view, cropRect) {
1765
+ const dpr = Math.min(view.devicePixelRatio || 1, OUTPUT_MAX_DPR);
1766
+ const area = cropRect.width * cropRect.height;
1767
+ if (area <= 0) return 1;
1768
+ return Math.max(0.5, Math.min(dpr, Math.sqrt(OUTPUT_MAX_PIXELS / area)));
1769
+ }
1770
+ function halve(canvas, doc) {
1771
+ const small = doc.createElement("canvas");
1772
+ small.width = Math.max(1, Math.round(canvas.width / 2));
1773
+ small.height = Math.max(1, Math.round(canvas.height / 2));
1774
+ const ctx = small.getContext("2d");
1775
+ if (!ctx) return null;
1776
+ ctx.drawImage(canvas, 0, 0, small.width, small.height);
1777
+ return small;
1778
+ }
1779
+ async function captureElementScreenshot(el) {
1780
+ try {
1781
+ const doc = el.ownerDocument;
1782
+ const view = doc.defaultView;
1783
+ if (!view || !doc.documentElement || !el.isConnected) return null;
1784
+ const targetRectRaw = el.getBoundingClientRect();
1785
+ const targetRect = {
1786
+ left: targetRectRaw.left,
1787
+ top: targetRectRaw.top,
1788
+ width: targetRectRaw.width,
1789
+ height: targetRectRaw.height
1790
+ };
1791
+ if (targetRect.width <= 0 || targetRect.height <= 0) return null;
1792
+ const cropRect = computeCropRect(targetRect, view.innerWidth, view.innerHeight);
1793
+ if (cropRect.width <= 0 || cropRect.height <= 0) return null;
1794
+ const scale = outputScale(view, cropRect);
1795
+ const shiftX = -(cropRect.left + (view.scrollX || 0));
1796
+ const shiftY = -(cropRect.top + (view.scrollY || 0));
1797
+ const canvas = await domToCanvas(doc.documentElement, {
1798
+ width: cropRect.width,
1799
+ height: cropRect.height,
1800
+ scale,
1801
+ backgroundColor: "#ffffff",
1802
+ timeout: RENDER_TIMEOUT_MS,
1803
+ filter: isCapturable,
1804
+ onCloneNode: (cloned) => {
1805
+ maskClone(cloned);
1806
+ },
1807
+ font: { preferredFormat: "woff2" },
1808
+ style: {
1809
+ transform: `translate(${shiftX}px, ${shiftY}px)`,
1810
+ transformOrigin: "top left",
1811
+ width: `${view.innerWidth}px`,
1812
+ minHeight: `${view.innerHeight}px`,
1813
+ margin: "0",
1814
+ overflow: "visible"
1815
+ }
1816
+ });
1817
+ const ctx = canvas.getContext("2d");
1818
+ if (!ctx) return null;
1819
+ drawOutline(ctx, targetRect, cropRect, scale);
1820
+ let dataUrl = canvas.toDataURL("image/png");
1821
+ if (dataUrl.length > DATA_URL_SOFT_MAX) {
1822
+ const smaller = halve(canvas, doc);
1823
+ if (!smaller) return null;
1824
+ dataUrl = smaller.toDataURL("image/png");
1825
+ }
1826
+ return dataUrl;
1827
+ } catch {
1828
+ return null;
1829
+ }
1830
+ }
1831
+
1832
+ // src/picker/overlay.ts
1833
+ var PICKER_HOST_ID = "arcy-picker";
1834
+ var BANNER_CLASS = "arcy-picker-banner";
1835
+ var HIGHLIGHT_CLASS = "arcy-picker-highlight";
1836
+ var PICKER_CSS = `
1837
+ .${BANNER_CLASS} {
1838
+ display: flex;
1839
+ align-items: center;
1840
+ gap: 10px;
1841
+ padding: 10px 14px;
1842
+ border-radius: 999px;
1843
+ background: #101828;
1844
+ color: #ffffff;
1845
+ font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1846
+ box-shadow: 0 8px 24px rgba(16, 24, 40, 0.28);
1847
+ white-space: nowrap;
1848
+ }
1849
+ .${BANNER_CLASS}__title {
1850
+ font-weight: 600;
1851
+ margin-right: 2px;
1852
+ }
1853
+ .${BANNER_CLASS}__status {
1854
+ max-width: 260px;
1855
+ overflow: hidden;
1856
+ text-overflow: ellipsis;
1857
+ color: #d0d5dd;
1858
+ }
1859
+ .${BANNER_CLASS} button {
1860
+ appearance: none;
1861
+ border: 1px solid rgba(255, 255, 255, 0.25);
1862
+ border-radius: 999px;
1863
+ background: transparent;
1864
+ color: #ffffff;
1865
+ font: inherit;
1866
+ padding: 4px 12px;
1867
+ cursor: pointer;
1868
+ }
1869
+ .${BANNER_CLASS} button:hover {
1870
+ background: rgba(255, 255, 255, 0.1);
1871
+ }
1872
+ .${BANNER_CLASS} button.is-active {
1873
+ background: #f04e23;
1874
+ border-color: #f04e23;
1875
+ }
1876
+ .${HIGHLIGHT_CLASS} {
1877
+ border-radius: 3px;
1878
+ }
1879
+ `;
1880
+ function applySurvivalStyle(el, pointerEvents, offsets = []) {
1881
+ el.style.setProperty("position", "absolute", "important");
1882
+ el.style.setProperty("pointer-events", pointerEvents, "important");
1883
+ for (const [property, value] of offsets) {
1884
+ el.style.setProperty(property, value, "important");
1885
+ }
1886
+ }
1887
+ var HIGHLIGHT_STYLE = [
1888
+ ["box-sizing", "border-box"],
1889
+ ["border", "2px solid #f04e23"],
1890
+ ["background", "rgba(240, 78, 35, 0.08)"]
1891
+ ];
1892
+ var BANNER_OFFSET = [
1893
+ ["left", "50%"],
1894
+ ["bottom", "24px"]
1895
+ ];
1896
+ var BANNER_STYLE = [
1897
+ ["transform", "translateX(-50%)"]
1898
+ ];
1899
+ function statusLabel(target) {
1900
+ const { text, tag } = target.core;
1901
+ return text ? `Sent "${text}" to ARCY` : `Sent <${tag}> to ARCY`;
1902
+ }
1903
+ function createPickerOverlay(context) {
1904
+ let shell = createShell({ id: PICKER_HOST_ID });
1905
+ if (!shell) return null;
1906
+ const doc = shell.host.ownerDocument;
1907
+ let mode = "pick";
1908
+ let destroyed = false;
1909
+ let hovered = null;
1910
+ let screenshotToken = 0;
1911
+ injectStyles(shell, PICKER_CSS);
1912
+ const highlight = doc.createElement("div");
1913
+ highlight.className = HIGHLIGHT_CLASS;
1914
+ applySurvivalStyle(highlight, "none");
1915
+ for (const [property, value] of HIGHLIGHT_STYLE) {
1916
+ highlight.style.setProperty(property, value);
1917
+ }
1918
+ hideHighlight();
1919
+ const banner = doc.createElement("div");
1920
+ banner.className = BANNER_CLASS;
1921
+ applySurvivalStyle(banner, "auto", BANNER_OFFSET);
1922
+ for (const [property, value] of BANNER_STYLE) {
1923
+ banner.style.setProperty(property, value);
1924
+ }
1925
+ const title = doc.createElement("span");
1926
+ title.className = `${BANNER_CLASS}__title`;
1927
+ title.textContent = "ARCY element picker";
1928
+ const pickButton = doc.createElement("button");
1929
+ pickButton.type = "button";
1930
+ pickButton.textContent = "Pick";
1931
+ const browseButton = doc.createElement("button");
1932
+ browseButton.type = "button";
1933
+ browseButton.textContent = "Browse";
1934
+ const status = doc.createElement("span");
1935
+ status.className = `${BANNER_CLASS}__status`;
1936
+ status.textContent = "Click the element you want this step to target.";
1937
+ const exitButton = doc.createElement("button");
1938
+ exitButton.type = "button";
1939
+ exitButton.textContent = "Exit";
1940
+ banner.append(title, pickButton, browseButton, status, exitButton);
1941
+ shell.root.append(highlight, banner);
1942
+ function setMode(next) {
1943
+ mode = next;
1944
+ pickButton.classList.toggle("is-active", next === "pick");
1945
+ browseButton.classList.toggle("is-active", next === "browse");
1946
+ status.textContent = next === "pick" ? "Click the element you want this step to target." : "Browsing. Clicks work normally; switch back to pick.";
1947
+ if (next === "browse") hideHighlight();
1948
+ }
1949
+ function hideHighlight() {
1950
+ highlight.style.setProperty("display", "none", "important");
1951
+ }
1952
+ function moveHighlight(el) {
1953
+ try {
1954
+ const rect = el.getBoundingClientRect();
1955
+ if (rect.width === 0 && rect.height === 0) {
1956
+ hideHighlight();
1957
+ return;
1958
+ }
1959
+ highlight.style.setProperty("left", `${rect.left}px`, "important");
1960
+ highlight.style.setProperty("top", `${rect.top}px`, "important");
1961
+ highlight.style.setProperty("width", `${rect.width}px`, "important");
1962
+ highlight.style.setProperty("height", `${rect.height}px`, "important");
1963
+ highlight.style.setProperty("display", "block", "important");
1964
+ } catch {
1965
+ hideHighlight();
1966
+ }
1967
+ }
1968
+ function isExcluded(target) {
1969
+ return target instanceof Element && closestAcrossShadow(target, `[${HOST_ATTRIBUTE}]`) !== null;
1970
+ }
1971
+ function composedTarget(event) {
1972
+ if (typeof event.composedPath === "function") {
1973
+ const path = event.composedPath();
1974
+ if (path.length > 0) return path[0];
1975
+ }
1976
+ return event.target;
1977
+ }
1978
+ function hostGone() {
1979
+ if (shell !== null && shell.host.isConnected) return false;
1980
+ destroy();
1981
+ return true;
1982
+ }
1983
+ const onPointerMove = (event) => {
1984
+ try {
1985
+ if (hostGone() || mode !== "pick") return;
1986
+ const target = composedTarget(event);
1987
+ if (isExcluded(target) || !(target instanceof Element)) {
1988
+ hovered = null;
1989
+ hideHighlight();
1990
+ return;
1991
+ }
1992
+ hovered = resolveInteractiveTarget(target);
1993
+ moveHighlight(hovered);
1994
+ } catch {
1995
+ }
1996
+ };
1997
+ const onViewportShift = () => {
1998
+ try {
1999
+ if (hostGone() || mode !== "pick") return;
2000
+ if (hovered && hovered.isConnected) moveHighlight(hovered);
2001
+ else hideHighlight();
2002
+ } catch {
2003
+ }
2004
+ };
2005
+ const onPress = (event) => {
2006
+ try {
2007
+ if (hostGone() || mode !== "pick") return;
2008
+ if (isExcluded(composedTarget(event))) return;
2009
+ event.preventDefault();
2010
+ event.stopPropagation();
2011
+ event.stopImmediatePropagation();
2012
+ } catch {
2013
+ }
2014
+ };
2015
+ const onClick = (event) => {
2016
+ try {
2017
+ if (hostGone() || mode !== "pick") return;
2018
+ const target = composedTarget(event);
2019
+ if (isExcluded(target)) return;
2020
+ event.preventDefault();
2021
+ event.stopPropagation();
2022
+ event.stopImmediatePropagation();
2023
+ if (!(target instanceof Element)) return;
2024
+ const el = resolveInteractiveTarget(target);
2025
+ const fingerprint = createTargetFingerprint(captureEventFingerprint(el));
2026
+ const ranked = rankSelectors(el);
2027
+ fingerprint.selector = ranked.autoSelector;
2028
+ fingerprint.selectorCandidates = ranked.candidates;
2029
+ status.textContent = statusLabel(fingerprint);
2030
+ hovered = el;
2031
+ moveHighlight(el);
2032
+ let matchCount = 0;
2033
+ try {
2034
+ matchCount = matchTarget(fingerprint, doc).matchCount;
2035
+ } catch {
2036
+ }
2037
+ context.onPick(fingerprint, matchCount);
2038
+ if (context.screenshotsEnabled === false) return;
2039
+ const token = ++screenshotToken;
2040
+ void captureElementScreenshot(el).then((screenshot) => {
2041
+ if (!screenshot || destroyed || token !== screenshotToken) return;
2042
+ try {
2043
+ context.onScreenshot?.(screenshot);
2044
+ } catch {
2045
+ }
2046
+ });
2047
+ } catch (error) {
2048
+ warn(`The element picker could not read that element. ${String(error)}`);
2049
+ }
2050
+ };
2051
+ function isEditable(target) {
2052
+ if (!(target instanceof Element)) return false;
2053
+ const tag = target.tagName;
2054
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
2055
+ return target.isContentEditable === true;
2056
+ }
2057
+ const onKeyDown = (event) => {
2058
+ try {
2059
+ if (hostGone()) return;
2060
+ const key = event;
2061
+ if (!key.altKey || key.code !== "KeyS" && key.key.toLowerCase() !== "s")
2062
+ return;
2063
+ if (isEditable(composedTarget(key))) return;
2064
+ key.preventDefault();
2065
+ setMode(mode === "pick" ? "browse" : "pick");
2066
+ } catch {
2067
+ }
2068
+ };
2069
+ function destroy() {
2070
+ if (destroyed) return;
2071
+ destroyed = true;
2072
+ try {
2073
+ doc.removeEventListener("pointerdown", onPress, true);
2074
+ doc.removeEventListener("mousedown", onPress, true);
2075
+ doc.removeEventListener("mouseup", onPress, true);
2076
+ doc.removeEventListener("auxclick", onPress, true);
2077
+ doc.removeEventListener("click", onClick, true);
2078
+ doc.removeEventListener("mousemove", onPointerMove, true);
2079
+ doc.removeEventListener("keydown", onKeyDown, true);
2080
+ doc.defaultView?.removeEventListener("scroll", onViewportShift, true);
2081
+ doc.defaultView?.removeEventListener("resize", onViewportShift);
2082
+ } catch {
2083
+ }
2084
+ shell?.destroy();
2085
+ shell = null;
2086
+ }
2087
+ const keepFocusWhereItIs = (event) => {
2088
+ try {
2089
+ event.preventDefault();
2090
+ } catch {
2091
+ }
2092
+ };
2093
+ banner.addEventListener("pointerdown", keepFocusWhereItIs);
2094
+ banner.addEventListener("mousedown", keepFocusWhereItIs);
2095
+ pickButton.addEventListener("click", () => setMode("pick"));
2096
+ browseButton.addEventListener("click", () => setMode("browse"));
2097
+ exitButton.addEventListener("click", () => {
2098
+ destroy();
2099
+ try {
2100
+ context.onExit();
2101
+ } catch {
2102
+ }
2103
+ });
2104
+ doc.addEventListener("pointerdown", onPress, true);
2105
+ doc.addEventListener("mousedown", onPress, true);
2106
+ doc.addEventListener("mouseup", onPress, true);
2107
+ doc.addEventListener("auxclick", onPress, true);
2108
+ doc.addEventListener("click", onClick, true);
2109
+ doc.addEventListener("mousemove", onPointerMove, true);
2110
+ doc.addEventListener("keydown", onKeyDown, true);
2111
+ doc.defaultView?.addEventListener("scroll", onViewportShift, true);
2112
+ doc.defaultView?.addEventListener("resize", onViewportShift);
2113
+ setMode("pick");
2114
+ return { destroy };
2115
+ }
2116
+
2117
+ // src/picker/index.ts
2118
+ var registration = {
2119
+ contract: PICKER_CONTRACT,
2120
+ mount: (context) => {
2121
+ try {
2122
+ return createPickerOverlay(context);
2123
+ } catch {
2124
+ return null;
2125
+ }
2126
+ }
2127
+ };
2128
+ if (typeof window !== "undefined") {
2129
+ try {
2130
+ ;
2131
+ window[PICKER_GLOBAL] = registration;
2132
+ } catch {
2133
+ }
2134
+ }