image-exporter 1.2.3 → 1.3.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.
Files changed (45) hide show
  1. package/README.md +32 -3
  2. package/dist/capture/capture-element.d.ts +2 -1
  3. package/dist/capture/capture-element.d.ts.map +1 -1
  4. package/dist/capture/capture.dom.test.d.ts +2 -0
  5. package/dist/capture/capture.dom.test.d.ts.map +1 -0
  6. package/dist/capture/copy-to-clipboard.d.ts +13 -0
  7. package/dist/capture/copy-to-clipboard.d.ts.map +1 -0
  8. package/dist/capture/copy-to-clipboard.dom.test.d.ts +2 -0
  9. package/dist/capture/copy-to-clipboard.dom.test.d.ts.map +1 -0
  10. package/dist/capture/cors-cleanup.dom.test.d.ts +2 -0
  11. package/dist/capture/cors-cleanup.dom.test.d.ts.map +1 -0
  12. package/dist/capture/data-url-to-blob.d.ts +9 -0
  13. package/dist/capture/data-url-to-blob.d.ts.map +1 -0
  14. package/dist/capture/data-url-to-blob.node.test.d.ts +2 -0
  15. package/dist/capture/data-url-to-blob.node.test.d.ts.map +1 -0
  16. package/dist/capture/dedup-consistency.node.test.d.ts +2 -0
  17. package/dist/capture/dedup-consistency.node.test.d.ts.map +1 -0
  18. package/dist/capture/determine-total-elements.d.ts +7 -3
  19. package/dist/capture/determine-total-elements.d.ts.map +1 -1
  20. package/dist/capture/determine-total-elements.dom.test.d.ts +2 -0
  21. package/dist/capture/determine-total-elements.dom.test.d.ts.map +1 -0
  22. package/dist/capture/download-images.d.ts +8 -0
  23. package/dist/capture/download-images.d.ts.map +1 -1
  24. package/dist/capture/get-image-options.dom.test.d.ts +2 -0
  25. package/dist/capture/get-image-options.dom.test.d.ts.map +1 -0
  26. package/dist/capture/handle-filenames.d.ts +5 -6
  27. package/dist/capture/handle-filenames.d.ts.map +1 -1
  28. package/dist/capture/index.d.ts.map +1 -1
  29. package/dist/capture/make-unique.d.ts +14 -0
  30. package/dist/capture/make-unique.d.ts.map +1 -0
  31. package/dist/capture/make-unique.node.test.d.ts +2 -0
  32. package/dist/capture/make-unique.node.test.d.ts.map +1 -0
  33. package/dist/index.browser.js +16 -16
  34. package/dist/index.browser.js.map +20 -17
  35. package/dist/index.cjs +195 -122
  36. package/dist/index.cjs.map +20 -17
  37. package/dist/index.d.ts +10 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +195 -122
  40. package/dist/index.js.map +20 -17
  41. package/dist/logger.d.ts +5 -5
  42. package/dist/logger.d.ts.map +1 -1
  43. package/dist/types.d.ts +33 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/package.json +27 -3
package/dist/index.cjs CHANGED
@@ -4,39 +4,60 @@ var __defProp = Object.defineProperty;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
7
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
8
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
22
  for (let key of __getOwnPropNames(mod))
11
23
  if (!__hasOwnProp.call(to, key))
12
24
  __defProp(to, key, {
13
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
14
26
  enumerable: true
15
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
16
30
  return to;
17
31
  };
18
- var __moduleCache = /* @__PURE__ */ new WeakMap;
19
32
  var __toCommonJS = (from) => {
20
- var entry = __moduleCache.get(from), desc;
33
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
21
34
  if (entry)
22
35
  return entry;
23
36
  entry = __defProp({}, "__esModule", { value: true });
24
- if (from && typeof from === "object" || typeof from === "function")
25
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
26
- get: () => from[key],
27
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
28
- }));
37
+ if (from && typeof from === "object" || typeof from === "function") {
38
+ for (var key of __getOwnPropNames(from))
39
+ if (!__hasOwnProp.call(entry, key))
40
+ __defProp(entry, key, {
41
+ get: __accessProp.bind(from, key),
42
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
43
+ });
44
+ }
29
45
  __moduleCache.set(from, entry);
30
46
  return entry;
31
47
  };
48
+ var __moduleCache;
32
49
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
50
+ var __returnValue = (v) => v;
51
+ function __exportSetter(name, newValue) {
52
+ this[name] = __returnValue.bind(null, newValue);
53
+ }
33
54
  var __export = (target, all) => {
34
55
  for (var name in all)
35
56
  __defProp(target, name, {
36
57
  get: all[name],
37
58
  enumerable: true,
38
59
  configurable: true,
39
- set: (newValue) => all[name] = () => newValue
60
+ set: __exportSetter.bind(all, name)
40
61
  });
41
62
  };
42
63
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -53,7 +74,7 @@ __export(exports_buffer, {
53
74
  default: () => buffer_default,
54
75
  constants: () => constants,
55
76
  btoa: () => btoa,
56
- atob: () => atob,
77
+ atob: () => atob2,
57
78
  INSPECT_MAX_BYTES: () => INSPECT_MAX_BYTES,
58
79
  File: () => File,
59
80
  Buffer: () => Buffer2,
@@ -726,7 +747,7 @@ function notimpl(name) {
726
747
  throw Error(name + " is not implemented for node:buffer browser polyfill");
727
748
  };
728
749
  }
729
- var lookup, revLookup, code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", i, len, customInspectSymbol, INSPECT_MAX_BYTES = 50, kMaxLength = 2147483647, kStringMaxLength = 536870888, btoa, atob, File, Blob2, constants, ERR_BUFFER_OUT_OF_BOUNDS, ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE, MAX_ARGUMENTS_LENGTH = 4096, INVALID_BASE64_RE, hexSliceLookupTable, resolveObjectURL, isUtf8, isAscii = (str) => {
750
+ var lookup, revLookup, code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", i, len, customInspectSymbol, INSPECT_MAX_BYTES = 50, kMaxLength = 2147483647, kStringMaxLength = 536870888, btoa, atob2, File, Blob2, constants, ERR_BUFFER_OUT_OF_BOUNDS, ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE, MAX_ARGUMENTS_LENGTH = 4096, INVALID_BASE64_RE, hexSliceLookupTable, resolveObjectURL, isUtf8, isAscii = (str) => {
730
751
  for (let char of str)
731
752
  if (char.charCodeAt(0) > 127)
732
753
  return false;
@@ -741,7 +762,7 @@ var init_buffer = __esm(() => {
741
762
  revLookup[95] = 63;
742
763
  customInspectSymbol = typeof Symbol === "function" && typeof Symbol.for === "function" ? Symbol.for("nodejs.util.inspect.custom") : null;
743
764
  btoa = globalThis.btoa;
744
- atob = globalThis.atob;
765
+ atob2 = globalThis.atob;
745
766
  File = globalThis.File;
746
767
  Blob2 = globalThis.Blob;
747
768
  constants = { MAX_LENGTH: kMaxLength, MAX_STRING_LENGTH: kStringMaxLength };
@@ -12537,7 +12558,7 @@ var require_zipEntries = __commonJS((exports2, module2) => {
12537
12558
  if (this.centralDirRecords !== this.files.length) {
12538
12559
  if (this.centralDirRecords !== 0 && this.files.length === 0) {
12539
12560
  throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length);
12540
- } else {}
12561
+ }
12541
12562
  }
12542
12563
  },
12543
12564
  readEndOfCentral: function() {
@@ -12714,42 +12735,43 @@ var require_lib = __commonJS((exports2, module2) => {
12714
12735
  var exports_src = {};
12715
12736
  __export(exports_src, {
12716
12737
  downloadImages: () => downloadImages,
12738
+ copyImageToClipboard: () => copyImageToClipboard,
12717
12739
  capture: () => capture
12718
12740
  });
12719
12741
  module.exports = __toCommonJS(exports_src);
12720
12742
 
12743
+ // src/capture/make-unique.ts
12744
+ function makeUnique(fileName, seen) {
12745
+ if (!seen.has(fileName)) {
12746
+ seen.add(fileName);
12747
+ return fileName;
12748
+ }
12749
+ const lastDot = fileName.lastIndexOf(".");
12750
+ const base = lastDot !== -1 ? fileName.slice(0, lastDot) : fileName;
12751
+ const extension = lastDot !== -1 ? fileName.slice(lastDot) : "";
12752
+ const match = base.match(/-(\d+)$/);
12753
+ let stem = base;
12754
+ let counter = 2;
12755
+ if (match) {
12756
+ stem = base.slice(0, -match[0].length);
12757
+ counter = parseInt(match[1], 10) + 1;
12758
+ }
12759
+ let candidate = `${stem}-${counter}${extension}`;
12760
+ while (seen.has(candidate)) {
12761
+ counter++;
12762
+ candidate = `${stem}-${counter}${extension}`;
12763
+ }
12764
+ seen.add(candidate);
12765
+ return candidate;
12766
+ }
12767
+
12721
12768
  // src/capture/handle-filenames.ts
12722
- function handleFileNames(imageOptions, filenames) {
12769
+ function handleFileNames(imageOptions, seen) {
12723
12770
  let proposedFilename = imageOptions.label;
12724
12771
  if (imageOptions.includeScaleInLabel)
12725
12772
  proposedFilename += `_@${imageOptions.scale}x`;
12726
- const extension = `.${imageOptions.format}`;
12727
- proposedFilename += extension;
12728
- if (!filenames.includes(proposedFilename)) {
12729
- filenames.push(proposedFilename);
12730
- return proposedFilename;
12731
- }
12732
- const numberPattern = /-(\d+)$/;
12733
- const match = proposedFilename.match(numberPattern);
12734
- if (match) {
12735
- const baseFilename = proposedFilename.replace(numberPattern, "");
12736
- let counter = parseInt(match[1], 10);
12737
- while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
12738
- counter++;
12739
- }
12740
- const newFilename = `${baseFilename}-${counter}${extension}`;
12741
- filenames.push(newFilename);
12742
- return newFilename;
12743
- } else {
12744
- const baseFilename = proposedFilename.replace(extension, "");
12745
- let counter = 2;
12746
- while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
12747
- counter++;
12748
- }
12749
- const newFilename = `${baseFilename}-${counter}${extension}`;
12750
- filenames.push(newFilename);
12751
- return newFilename;
12752
- }
12773
+ proposedFilename += `.${imageOptions.format}`;
12774
+ return makeUnique(proposedFilename, seen);
12753
12775
  }
12754
12776
 
12755
12777
  // node_modules/modern-screenshot/dist/index.mjs
@@ -14251,18 +14273,20 @@ async function domToWebp(node, options) {
14251
14273
  }
14252
14274
 
14253
14275
  // src/capture/capture-element.ts
14254
- async function captureElement(element, imageOptions, filenames) {
14276
+ async function captureElement(element, imageOptions, seen, screenshotOptions) {
14277
+ let cleanUpBackground = false;
14255
14278
  try {
14256
14279
  let dataURL = "";
14257
- let htmlToImageOptions = {
14280
+ const userFilter = screenshotOptions?.filter;
14281
+ const htmlToImageOptions = {
14282
+ ...screenshotOptions,
14258
14283
  quality: imageOptions.quality,
14259
14284
  scale: imageOptions.scale,
14260
- filter
14285
+ filter: (node) => filter(node) && (userFilter ? userFilter(node) : true)
14261
14286
  };
14262
14287
  const styles = getComputedStyle(element);
14263
14288
  const backgroundColor = styles.backgroundColor;
14264
14289
  const backgroundImage = styles.backgroundImage;
14265
- let cleanUpBackground = false;
14266
14290
  if (backgroundColor === "rgba(0, 0, 0, 0)" && backgroundImage === "none" && imageOptions.format === "jpg") {
14267
14291
  element.style.backgroundColor = "#FFFFFF";
14268
14292
  cleanUpBackground = true;
@@ -14281,17 +14305,17 @@ async function captureElement(element, imageOptions, filenames) {
14281
14305
  dataURL = await domToWebp(element, htmlToImageOptions);
14282
14306
  break;
14283
14307
  }
14284
- if (cleanUpBackground) {
14285
- element.style.backgroundColor = "";
14286
- element.style.backgroundImage = "";
14287
- }
14288
14308
  return {
14289
14309
  dataURL,
14290
- fileName: handleFileNames(imageOptions, filenames)
14310
+ fileName: handleFileNames(imageOptions, seen)
14291
14311
  };
14292
14312
  } catch (error) {
14293
- console.error("ImageExporter: Error in captureImage", error);
14294
- return { dataURL: "", fileName: "" };
14313
+ throw new Error(`ImageExporter: failed to capture element as ${imageOptions.format}`, { cause: error });
14314
+ } finally {
14315
+ if (cleanUpBackground) {
14316
+ element.style.backgroundColor = "";
14317
+ element.style.backgroundImage = "";
14318
+ }
14295
14319
  }
14296
14320
  }
14297
14321
  var filter = (node) => {
@@ -14328,7 +14352,7 @@ async function downloadImages(images, userConfig = defaultConfig) {
14328
14352
  const uniqueImages = ensureUniqueFileNames(images);
14329
14353
  if (uniqueImages.length === 1) {
14330
14354
  const image = uniqueImages[0];
14331
- triggerDownload(image.dataURL, image.fileName);
14355
+ triggerDownload(image.blob ?? image.dataURL, image.fileName);
14332
14356
  } else if (uniqueImages.length > 1) {
14333
14357
  const imagesBlob = await zipUpImages(uniqueImages);
14334
14358
  if (imagesBlob)
@@ -14352,8 +14376,12 @@ async function zipUpImages(images) {
14352
14376
  const zip = new import_jszip.default;
14353
14377
  try {
14354
14378
  images.forEach((image) => {
14355
- const content = image.dataURL.split(",")[1];
14356
- zip.file(image.fileName, content, { base64: true });
14379
+ if (image.blob) {
14380
+ zip.file(image.fileName, image.blob);
14381
+ } else {
14382
+ const content = image.dataURL.split(",")[1];
14383
+ zip.file(image.fileName, content, { base64: true });
14384
+ }
14357
14385
  });
14358
14386
  } catch (error) {
14359
14387
  console.error("Image Exporter - Error adding images to ZIP:", error);
@@ -14378,27 +14406,15 @@ function parseLabel(config) {
14378
14406
  }
14379
14407
  }
14380
14408
  function ensureUniqueFileNames(images) {
14381
- const fileNameMap = new Map;
14409
+ const seen = new Set;
14382
14410
  return images.map((image) => {
14383
- const { fileName } = image;
14384
- const lastDotIndex = fileName.lastIndexOf(".");
14385
- const baseName = lastDotIndex !== -1 ? fileName.substring(0, lastDotIndex) : fileName;
14386
- const extension = lastDotIndex !== -1 ? fileName.substring(lastDotIndex) : "";
14387
- if (!fileNameMap.has(fileName)) {
14388
- fileNameMap.set(fileName, 1);
14389
- return image;
14390
- }
14391
- const count = fileNameMap.get(fileName) + 1;
14392
- fileNameMap.set(fileName, count);
14393
- const newFileName = `${baseName}-${count}${extension}`;
14394
- return {
14395
- ...image,
14396
- fileName: newFileName
14397
- };
14411
+ const fileName = makeUnique(image.fileName, seen);
14412
+ return fileName === image.fileName ? image : { ...image, fileName };
14398
14413
  });
14399
14414
  }
14400
14415
 
14401
14416
  // src/logger.ts
14417
+ var hasWindow = typeof window !== "undefined";
14402
14418
  var log = {
14403
14419
  info: (...messages) => logAction(messages, "info"),
14404
14420
  error: (...messages) => logAction(messages, "error"),
@@ -14434,16 +14450,18 @@ async function logAction(messages, type = "info") {
14434
14450
  console.groupEnd();
14435
14451
  break;
14436
14452
  }
14437
- if (windowLogging)
14453
+ if (windowLogging && hasWindow)
14438
14454
  window.imageExporterLogs.push({ message: combinedMessage, type });
14439
14455
  }
14440
14456
  async function logProgress(progress, total) {
14441
- if (windowLogging) {
14457
+ if (windowLogging && hasWindow) {
14442
14458
  window.imageExporterProgress.push([progress, total]);
14443
14459
  }
14444
14460
  }
14445
- window.imageExporterLogs = [];
14446
- window.imageExporterProgress = [];
14461
+ if (hasWindow) {
14462
+ window.imageExporterLogs = [];
14463
+ window.imageExporterProgress = [];
14464
+ }
14447
14465
 
14448
14466
  // src/cors-proxy/is-valid-url.ts
14449
14467
  function isValidUrl(string) {
@@ -14456,7 +14474,7 @@ function isValidUrl(string) {
14456
14474
  return false;
14457
14475
  }
14458
14476
  return true;
14459
- } catch (_) {
14477
+ } catch {
14460
14478
  return false;
14461
14479
  }
14462
14480
  }
@@ -14465,7 +14483,7 @@ function isValidUrl(string) {
14465
14483
  async function proxyCSS(config) {
14466
14484
  const stylesheetElements = document.querySelectorAll('link[rel="stylesheet"]');
14467
14485
  log.verbose("stylesheet elements to proxy", stylesheetElements.length);
14468
- for (let stylesheetElement of stylesheetElements) {
14486
+ for (const stylesheetElement of stylesheetElements) {
14469
14487
  const stylesheetURL = stylesheetElement.getAttribute("href");
14470
14488
  if (!stylesheetURL)
14471
14489
  continue;
@@ -14479,7 +14497,7 @@ async function proxyCSS(config) {
14479
14497
  const proxiedURL = config.corsProxyBaseUrl + encodeURIComponent(stylesheetURL);
14480
14498
  try {
14481
14499
  const response = await fetch(proxiedURL);
14482
- let cssContent = await response.text();
14500
+ const cssContent = await response.text();
14483
14501
  const styleElement = document.createElement("style");
14484
14502
  styleElement.textContent = cssContent;
14485
14503
  styleElement.setAttribute("original-link-element", encodeURIComponent(stylesheetElement.outerHTML));
@@ -14535,7 +14553,7 @@ async function cleanUpCorsProxy() {
14535
14553
  }
14536
14554
  async function restoreCSS() {
14537
14555
  const styleElements = document.querySelectorAll("style[original-link-element]");
14538
- for (let styleElement of styleElements) {
14556
+ for (const styleElement of styleElements) {
14539
14557
  const originalLinkElementHTML = decodeURIComponent(styleElement.getAttribute("original-link-element"));
14540
14558
  const tempContainer = document.createElement("div");
14541
14559
  tempContainer.innerHTML = originalLinkElementHTML;
@@ -14546,7 +14564,7 @@ async function restoreCSS() {
14546
14564
  }
14547
14565
  async function restoreImages() {
14548
14566
  const imageElements = document.querySelectorAll("img[original-src]");
14549
- for (let imageElement of imageElements) {
14567
+ for (const imageElement of imageElements) {
14550
14568
  const originalSrc = imageElement.getAttribute("original-src");
14551
14569
  imageElement.src = originalSrc;
14552
14570
  imageElement.removeAttribute("original-src");
@@ -14560,6 +14578,23 @@ var corsProxy = {
14560
14578
  cleanUp: cleanUpCorsProxy
14561
14579
  };
14562
14580
 
14581
+ // src/capture/data-url-to-blob.ts
14582
+ function dataUrlToBlob(dataURL) {
14583
+ const commaIndex = dataURL.indexOf(",");
14584
+ const meta = dataURL.slice(0, commaIndex);
14585
+ const data = dataURL.slice(commaIndex + 1);
14586
+ const mimeMatch = meta.match(/^data:([^;]+)/);
14587
+ const mime = mimeMatch ? mimeMatch[1] : "";
14588
+ if (meta.includes(";base64")) {
14589
+ const binary = atob(data);
14590
+ const bytes = new Uint8Array(binary.length);
14591
+ for (let i2 = 0;i2 < binary.length; i2++)
14592
+ bytes[i2] = binary.charCodeAt(i2);
14593
+ return new Blob([bytes], { type: mime });
14594
+ }
14595
+ return new Blob([decodeURIComponent(data)], { type: mime });
14596
+ }
14597
+
14563
14598
  // src/capture/get-image-options.ts
14564
14599
  async function getImageOptions(element, config) {
14565
14600
  return {
@@ -14601,7 +14636,7 @@ async function getImageOptions(element, config) {
14601
14636
  throw new Error(`ImageExporter: provided format is not valid.
14602
14637
  Provided: ${format}
14603
14638
  Element: ${element}
14604
- Accepted values: jpg, png, svg,
14639
+ Accepted values: jpg, png, svg, webp
14605
14640
  Defaulting to: ${config.format}`);
14606
14641
  }
14607
14642
  } catch (error) {
@@ -14692,32 +14727,35 @@ function isVisible(element) {
14692
14727
  }
14693
14728
 
14694
14729
  // src/capture/determine-total-elements.ts
14695
- async function determineTotalElements(elements) {
14730
+ async function determineTotalElements(elements, config) {
14696
14731
  try {
14697
14732
  let totalElements = 0;
14698
- for (const element of elements) {
14699
- const scaleAsString = element.dataset.scale;
14700
- if (!scaleAsString)
14701
- continue;
14702
- if (scaleAsString.includes(",")) {
14703
- const scales = scaleAsString.trim().split(",").map((scale) => parseFloat(scale));
14704
- if (scales.some((scale) => isNaN(scale)))
14705
- continue;
14706
- totalElements += scales.length;
14707
- } else {
14708
- const scaleAsNumber = parseFloat(scaleAsString.trim());
14709
- if (isNaN(scaleAsNumber)) {
14710
- continue;
14711
- } else {
14712
- totalElements++;
14713
- }
14714
- }
14733
+ for (const element of Array.from(elements)) {
14734
+ totalElements += countScalesForElement(element, config);
14715
14735
  }
14716
14736
  return totalElements;
14717
- } catch (error) {
14737
+ } catch {
14718
14738
  return 1;
14719
14739
  }
14720
14740
  }
14741
+ function countScalesForElement(element, config) {
14742
+ const scaleAsString = element.dataset.scale;
14743
+ if (!scaleAsString)
14744
+ return countConfigScale(config);
14745
+ if (scaleAsString.includes(",")) {
14746
+ const scales = scaleAsString.trim().split(",").map((scale) => parseFloat(scale));
14747
+ if (scales.some((scale) => isNaN(scale)))
14748
+ return countConfigScale(config);
14749
+ return scales.length;
14750
+ }
14751
+ const scaleAsNumber = parseFloat(scaleAsString.trim());
14752
+ if (isNaN(scaleAsNumber))
14753
+ return countConfigScale(config);
14754
+ return 1;
14755
+ }
14756
+ function countConfigScale(config) {
14757
+ return Array.isArray(config.scale) ? config.scale.length : 1;
14758
+ }
14721
14759
 
14722
14760
  // src/capture/index.ts
14723
14761
  var windowLogging = true;
@@ -14733,50 +14771,85 @@ async function capture(elements, userConfig = defaultConfig) {
14733
14771
  elements = [elements];
14734
14772
  const originalLength = elements.length;
14735
14773
  elements = removeHiddenElements(elements);
14736
- const totalElements = await determineTotalElements(elements);
14774
+ const totalElements = await determineTotalElements(elements, config);
14737
14775
  if (originalLength !== elements.length)
14738
14776
  log.verbose("Skipping capture of hidden elements: ", originalLength - elements.length);
14739
14777
  log.verbose("Element to capture", elements.length);
14740
14778
  if (userConfig.corsProxyBaseUrl)
14741
14779
  await corsProxy.run(config, elements);
14742
- let images = [];
14743
- let filenames = [];
14780
+ const images = [];
14781
+ const seen = new Set;
14744
14782
  let imageNumber = 1;
14745
- for (const element of elements) {
14746
- const imageOptions = await getImageOptions(element, config);
14747
- log.verbose("Image options", imageOptions);
14748
- if (imageOptions.scale instanceof Array) {
14749
- log.verbose("Multi-scale capture");
14750
- imageOptions.includeScaleInLabel = true;
14751
- for (const scale of imageOptions.scale) {
14752
- log.progress(imageNumber++, totalElements);
14753
- const image = await captureElement(element, { ...imageOptions, scale }, filenames);
14754
- images.push(image);
14755
- }
14756
- } else if (typeof imageOptions.scale === "number") {
14757
- log.progress(imageNumber++, totalElements);
14758
- log.verbose("Single-scale capture");
14759
- const image = await captureElement(element, imageOptions, filenames);
14760
- images.push(image);
14783
+ const tryCapture = async (element, options) => {
14784
+ try {
14785
+ const image = await captureElement(element, options, seen, config.screenshotOptions);
14786
+ images.push(applyOutput(image, config.output));
14787
+ } catch (error) {
14788
+ log.error(error);
14789
+ } finally {
14790
+ const completed = imageNumber++;
14791
+ log.progress(completed, totalElements);
14792
+ config.onProgress?.(completed, totalElements);
14793
+ }
14794
+ };
14795
+ captureLoop:
14796
+ for (const element of elements) {
14797
+ if (config.signal?.aborted)
14798
+ break;
14799
+ const imageOptions = await getImageOptions(element, config);
14800
+ log.verbose("Image options", imageOptions);
14801
+ if (imageOptions.scale instanceof Array) {
14802
+ log.verbose("Multi-scale capture");
14803
+ imageOptions.includeScaleInLabel = true;
14804
+ for (const scale of imageOptions.scale) {
14805
+ if (config.signal?.aborted)
14806
+ break captureLoop;
14807
+ await tryCapture(element, {
14808
+ ...imageOptions,
14809
+ scale
14810
+ });
14811
+ }
14812
+ } else if (typeof imageOptions.scale === "number") {
14813
+ log.verbose("Single-scale capture");
14814
+ await tryCapture(element, imageOptions);
14815
+ }
14761
14816
  }
14817
+ if (config.signal?.aborted) {
14818
+ log.verbose("Capture aborted; returning", images.length, "image(s)");
14819
+ return images;
14762
14820
  }
14763
14821
  if (config.downloadImages)
14764
14822
  await downloadImages(images, config);
14765
- if (userConfig.corsProxyBaseUrl)
14766
- await corsProxy.cleanUp();
14767
14823
  return images;
14768
14824
  } catch (error) {
14769
14825
  log.error(error);
14770
14826
  return null;
14771
14827
  } finally {
14828
+ if (userConfig.corsProxyBaseUrl)
14829
+ await corsProxy.cleanUp();
14772
14830
  log.group.close();
14773
14831
  }
14774
14832
  }
14833
+ function applyOutput(image, output) {
14834
+ if (!output || output === "dataurl")
14835
+ return image;
14836
+ const blob = dataUrlToBlob(image.dataURL);
14837
+ if (output === "blob")
14838
+ return { ...image, dataURL: "", blob };
14839
+ return { ...image, blob };
14840
+ }
14841
+
14842
+ // src/capture/copy-to-clipboard.ts
14843
+ async function copyImageToClipboard(image) {
14844
+ const blob = image.blob ?? dataUrlToBlob(image.dataURL);
14845
+ await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);
14846
+ }
14775
14847
 
14776
14848
  // src/index.ts
14777
14849
  if (typeof window !== "undefined") {
14778
14850
  window.imageExporter = capture;
14779
14851
  window.imageExporterDownload = downloadImages;
14852
+ window.imageExporterCopyToClipboard = copyImageToClipboard;
14780
14853
  }
14781
14854
 
14782
- //# debugId=C876DD0C58D9712A64756E2164756E21
14855
+ //# debugId=E8C5F97C6B31AA5264756E2164756E21