image-exporter 1.2.2 → 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 +198 -125
  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 +198 -125
  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 +81 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/package.json +27 -3
package/dist/index.d.ts CHANGED
@@ -1,7 +1,15 @@
1
1
  import { capture } from "./capture";
2
2
  import { downloadImages } from "./capture/download-images";
3
+ import { copyImageToClipboard } from "./capture/copy-to-clipboard";
4
+ declare global {
5
+ interface Window {
6
+ imageExporter: typeof capture;
7
+ imageExporterDownload: typeof downloadImages;
8
+ imageExporterCopyToClipboard: typeof copyImageToClipboard;
9
+ }
10
+ }
3
11
  /** Exports for use as an imported package */
4
- export { capture, downloadImages };
12
+ export { capture, downloadImages, copyImageToClipboard };
5
13
  /** Type exports */
6
- export type { ImageOptions, Config, ParsedImageOptions, Image, Label, Format, Scale, Quality, IncludeScaleInLabel, LoggingLevel, } from "./types";
14
+ export type { ImageOptions, Config, ParsedImageOptions, Image, Output, Label, Format, Scale, Quality, IncludeScaleInLabel, LoggingLevel, } from "./types";
7
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAQ3D,6CAA6C;AAC7C,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;AAEnC,mBAAmB;AACnB,YAAY,EACV,YAAY,EACZ,MAAM,EACN,kBAAkB,EAClB,KAAK,EACL,KAAK,EACL,MAAM,EACN,KAAK,EACL,OAAO,EACP,mBAAmB,EACnB,YAAY,GACb,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,aAAa,EAAE,OAAO,OAAO,CAAC;QAC9B,qBAAqB,EAAE,OAAO,cAAc,CAAC;QAC7C,4BAA4B,EAAE,OAAO,oBAAoB,CAAC;KAC3D;CACF;AASD,6CAA6C;AAC7C,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,CAAC;AAEzD,mBAAmB;AACnB,YAAY,EACV,YAAY,EACZ,MAAM,EACN,kBAAkB,EAClB,KAAK,EACL,MAAM,EACN,KAAK,EACL,MAAM,EACN,KAAK,EACL,OAAO,EACP,mBAAmB,EACnB,YAAY,GACb,MAAM,SAAS,CAAC"}
package/dist/index.js 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 };
@@ -12532,7 +12553,7 @@ var require_zipEntries = __commonJS((exports, module) => {
12532
12553
  if (this.centralDirRecords !== this.files.length) {
12533
12554
  if (this.centralDirRecords !== 0 && this.files.length === 0) {
12534
12555
  throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length);
12535
- } else {}
12556
+ }
12536
12557
  }
12537
12558
  },
12538
12559
  readEndOfCentral: function() {
@@ -12705,38 +12726,38 @@ var require_lib = __commonJS((exports, module) => {
12705
12726
  module.exports = JSZip;
12706
12727
  });
12707
12728
 
12729
+ // src/capture/make-unique.ts
12730
+ function makeUnique(fileName, seen) {
12731
+ if (!seen.has(fileName)) {
12732
+ seen.add(fileName);
12733
+ return fileName;
12734
+ }
12735
+ const lastDot = fileName.lastIndexOf(".");
12736
+ const base = lastDot !== -1 ? fileName.slice(0, lastDot) : fileName;
12737
+ const extension = lastDot !== -1 ? fileName.slice(lastDot) : "";
12738
+ const match = base.match(/-(\d+)$/);
12739
+ let stem = base;
12740
+ let counter = 2;
12741
+ if (match) {
12742
+ stem = base.slice(0, -match[0].length);
12743
+ counter = parseInt(match[1], 10) + 1;
12744
+ }
12745
+ let candidate = `${stem}-${counter}${extension}`;
12746
+ while (seen.has(candidate)) {
12747
+ counter++;
12748
+ candidate = `${stem}-${counter}${extension}`;
12749
+ }
12750
+ seen.add(candidate);
12751
+ return candidate;
12752
+ }
12753
+
12708
12754
  // src/capture/handle-filenames.ts
12709
- function handleFileNames(imageOptions, filenames) {
12755
+ function handleFileNames(imageOptions, seen) {
12710
12756
  let proposedFilename = imageOptions.label;
12711
12757
  if (imageOptions.includeScaleInLabel)
12712
12758
  proposedFilename += `_@${imageOptions.scale}x`;
12713
- const extension = `.${imageOptions.format}`;
12714
- proposedFilename += extension;
12715
- if (!filenames.includes(proposedFilename)) {
12716
- filenames.push(proposedFilename);
12717
- return proposedFilename;
12718
- }
12719
- const numberPattern = /-(\d+)$/;
12720
- const match = proposedFilename.match(numberPattern);
12721
- if (match) {
12722
- const baseFilename = proposedFilename.replace(numberPattern, "");
12723
- let counter = parseInt(match[1], 10);
12724
- while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
12725
- counter++;
12726
- }
12727
- const newFilename = `${baseFilename}-${counter}${extension}`;
12728
- filenames.push(newFilename);
12729
- return newFilename;
12730
- } else {
12731
- const baseFilename = proposedFilename.replace(extension, "");
12732
- let counter = 2;
12733
- while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
12734
- counter++;
12735
- }
12736
- const newFilename = `${baseFilename}-${counter}${extension}`;
12737
- filenames.push(newFilename);
12738
- return newFilename;
12739
- }
12759
+ proposedFilename += `.${imageOptions.format}`;
12760
+ return makeUnique(proposedFilename, seen);
12740
12761
  }
12741
12762
 
12742
12763
  // node_modules/modern-screenshot/dist/index.mjs
@@ -14238,18 +14259,20 @@ async function domToWebp(node, options) {
14238
14259
  }
14239
14260
 
14240
14261
  // src/capture/capture-element.ts
14241
- async function captureElement(element, imageOptions, filenames) {
14262
+ async function captureElement(element, imageOptions, seen, screenshotOptions) {
14263
+ let cleanUpBackground = false;
14242
14264
  try {
14243
14265
  let dataURL = "";
14244
- let htmlToImageOptions = {
14266
+ const userFilter = screenshotOptions?.filter;
14267
+ const htmlToImageOptions = {
14268
+ ...screenshotOptions,
14245
14269
  quality: imageOptions.quality,
14246
14270
  scale: imageOptions.scale,
14247
- filter
14271
+ filter: (node) => filter(node) && (userFilter ? userFilter(node) : true)
14248
14272
  };
14249
14273
  const styles = getComputedStyle(element);
14250
14274
  const backgroundColor = styles.backgroundColor;
14251
14275
  const backgroundImage = styles.backgroundImage;
14252
- let cleanUpBackground = false;
14253
14276
  if (backgroundColor === "rgba(0, 0, 0, 0)" && backgroundImage === "none" && imageOptions.format === "jpg") {
14254
14277
  element.style.backgroundColor = "#FFFFFF";
14255
14278
  cleanUpBackground = true;
@@ -14268,17 +14291,17 @@ async function captureElement(element, imageOptions, filenames) {
14268
14291
  dataURL = await domToWebp(element, htmlToImageOptions);
14269
14292
  break;
14270
14293
  }
14271
- if (cleanUpBackground) {
14272
- element.style.backgroundColor = "";
14273
- element.style.backgroundImage = "";
14274
- }
14275
14294
  return {
14276
14295
  dataURL,
14277
- fileName: handleFileNames(imageOptions, filenames)
14296
+ fileName: handleFileNames(imageOptions, seen)
14278
14297
  };
14279
14298
  } catch (error) {
14280
- console.error("ImageExporter: Error in captureImage", error);
14281
- return { dataURL: "", fileName: "" };
14299
+ throw new Error(`ImageExporter: failed to capture element as ${imageOptions.format}`, { cause: error });
14300
+ } finally {
14301
+ if (cleanUpBackground) {
14302
+ element.style.backgroundColor = "";
14303
+ element.style.backgroundImage = "";
14304
+ }
14282
14305
  }
14283
14306
  }
14284
14307
  var filter = (node) => {
@@ -14315,7 +14338,7 @@ async function downloadImages(images, userConfig = defaultConfig) {
14315
14338
  const uniqueImages = ensureUniqueFileNames(images);
14316
14339
  if (uniqueImages.length === 1) {
14317
14340
  const image = uniqueImages[0];
14318
- triggerDownload(image.dataURL, image.fileName);
14341
+ triggerDownload(image.blob ?? image.dataURL, image.fileName);
14319
14342
  } else if (uniqueImages.length > 1) {
14320
14343
  const imagesBlob = await zipUpImages(uniqueImages);
14321
14344
  if (imagesBlob)
@@ -14339,8 +14362,12 @@ async function zipUpImages(images) {
14339
14362
  const zip = new import_jszip.default;
14340
14363
  try {
14341
14364
  images.forEach((image) => {
14342
- const content = image.dataURL.split(",")[1];
14343
- zip.file(image.fileName, content, { base64: true });
14365
+ if (image.blob) {
14366
+ zip.file(image.fileName, image.blob);
14367
+ } else {
14368
+ const content = image.dataURL.split(",")[1];
14369
+ zip.file(image.fileName, content, { base64: true });
14370
+ }
14344
14371
  });
14345
14372
  } catch (error) {
14346
14373
  console.error("Image Exporter - Error adding images to ZIP:", error);
@@ -14365,27 +14392,15 @@ function parseLabel(config) {
14365
14392
  }
14366
14393
  }
14367
14394
  function ensureUniqueFileNames(images) {
14368
- const fileNameMap = new Map;
14395
+ const seen = new Set;
14369
14396
  return images.map((image) => {
14370
- const { fileName } = image;
14371
- const lastDotIndex = fileName.lastIndexOf(".");
14372
- const baseName = lastDotIndex !== -1 ? fileName.substring(0, lastDotIndex) : fileName;
14373
- const extension = lastDotIndex !== -1 ? fileName.substring(lastDotIndex) : "";
14374
- if (!fileNameMap.has(fileName)) {
14375
- fileNameMap.set(fileName, 1);
14376
- return image;
14377
- }
14378
- const count = fileNameMap.get(fileName) + 1;
14379
- fileNameMap.set(fileName, count);
14380
- const newFileName = `${baseName}-${count}${extension}`;
14381
- return {
14382
- ...image,
14383
- fileName: newFileName
14384
- };
14397
+ const fileName = makeUnique(image.fileName, seen);
14398
+ return fileName === image.fileName ? image : { ...image, fileName };
14385
14399
  });
14386
14400
  }
14387
14401
 
14388
14402
  // src/logger.ts
14403
+ var hasWindow = typeof window !== "undefined";
14389
14404
  var log = {
14390
14405
  info: (...messages) => logAction(messages, "info"),
14391
14406
  error: (...messages) => logAction(messages, "error"),
@@ -14421,16 +14436,18 @@ async function logAction(messages, type = "info") {
14421
14436
  console.groupEnd();
14422
14437
  break;
14423
14438
  }
14424
- if (windowLogging)
14439
+ if (windowLogging && hasWindow)
14425
14440
  window.imageExporterLogs.push({ message: combinedMessage, type });
14426
14441
  }
14427
14442
  async function logProgress(progress, total) {
14428
- if (windowLogging) {
14443
+ if (windowLogging && hasWindow) {
14429
14444
  window.imageExporterProgress.push([progress, total]);
14430
14445
  }
14431
14446
  }
14432
- window.imageExporterLogs = [];
14433
- window.imageExporterProgress = [];
14447
+ if (hasWindow) {
14448
+ window.imageExporterLogs = [];
14449
+ window.imageExporterProgress = [];
14450
+ }
14434
14451
 
14435
14452
  // src/cors-proxy/is-valid-url.ts
14436
14453
  function isValidUrl(string) {
@@ -14443,7 +14460,7 @@ function isValidUrl(string) {
14443
14460
  return false;
14444
14461
  }
14445
14462
  return true;
14446
- } catch (_) {
14463
+ } catch {
14447
14464
  return false;
14448
14465
  }
14449
14466
  }
@@ -14452,7 +14469,7 @@ function isValidUrl(string) {
14452
14469
  async function proxyCSS(config) {
14453
14470
  const stylesheetElements = document.querySelectorAll('link[rel="stylesheet"]');
14454
14471
  log.verbose("stylesheet elements to proxy", stylesheetElements.length);
14455
- for (let stylesheetElement of stylesheetElements) {
14472
+ for (const stylesheetElement of stylesheetElements) {
14456
14473
  const stylesheetURL = stylesheetElement.getAttribute("href");
14457
14474
  if (!stylesheetURL)
14458
14475
  continue;
@@ -14466,7 +14483,7 @@ async function proxyCSS(config) {
14466
14483
  const proxiedURL = config.corsProxyBaseUrl + encodeURIComponent(stylesheetURL);
14467
14484
  try {
14468
14485
  const response = await fetch(proxiedURL);
14469
- let cssContent = await response.text();
14486
+ const cssContent = await response.text();
14470
14487
  const styleElement = document.createElement("style");
14471
14488
  styleElement.textContent = cssContent;
14472
14489
  styleElement.setAttribute("original-link-element", encodeURIComponent(stylesheetElement.outerHTML));
@@ -14522,7 +14539,7 @@ async function cleanUpCorsProxy() {
14522
14539
  }
14523
14540
  async function restoreCSS() {
14524
14541
  const styleElements = document.querySelectorAll("style[original-link-element]");
14525
- for (let styleElement of styleElements) {
14542
+ for (const styleElement of styleElements) {
14526
14543
  const originalLinkElementHTML = decodeURIComponent(styleElement.getAttribute("original-link-element"));
14527
14544
  const tempContainer = document.createElement("div");
14528
14545
  tempContainer.innerHTML = originalLinkElementHTML;
@@ -14533,7 +14550,7 @@ async function restoreCSS() {
14533
14550
  }
14534
14551
  async function restoreImages() {
14535
14552
  const imageElements = document.querySelectorAll("img[original-src]");
14536
- for (let imageElement of imageElements) {
14553
+ for (const imageElement of imageElements) {
14537
14554
  const originalSrc = imageElement.getAttribute("original-src");
14538
14555
  imageElement.src = originalSrc;
14539
14556
  imageElement.removeAttribute("original-src");
@@ -14547,6 +14564,23 @@ var corsProxy = {
14547
14564
  cleanUp: cleanUpCorsProxy
14548
14565
  };
14549
14566
 
14567
+ // src/capture/data-url-to-blob.ts
14568
+ function dataUrlToBlob(dataURL) {
14569
+ const commaIndex = dataURL.indexOf(",");
14570
+ const meta = dataURL.slice(0, commaIndex);
14571
+ const data = dataURL.slice(commaIndex + 1);
14572
+ const mimeMatch = meta.match(/^data:([^;]+)/);
14573
+ const mime = mimeMatch ? mimeMatch[1] : "";
14574
+ if (meta.includes(";base64")) {
14575
+ const binary = atob(data);
14576
+ const bytes = new Uint8Array(binary.length);
14577
+ for (let i2 = 0;i2 < binary.length; i2++)
14578
+ bytes[i2] = binary.charCodeAt(i2);
14579
+ return new Blob([bytes], { type: mime });
14580
+ }
14581
+ return new Blob([decodeURIComponent(data)], { type: mime });
14582
+ }
14583
+
14550
14584
  // src/capture/get-image-options.ts
14551
14585
  async function getImageOptions(element, config) {
14552
14586
  return {
@@ -14588,7 +14622,7 @@ async function getImageOptions(element, config) {
14588
14622
  throw new Error(`ImageExporter: provided format is not valid.
14589
14623
  Provided: ${format}
14590
14624
  Element: ${element}
14591
- Accepted values: jpg, png, svg,
14625
+ Accepted values: jpg, png, svg, webp
14592
14626
  Defaulting to: ${config.format}`);
14593
14627
  }
14594
14628
  } catch (error) {
@@ -14679,32 +14713,35 @@ function isVisible(element) {
14679
14713
  }
14680
14714
 
14681
14715
  // src/capture/determine-total-elements.ts
14682
- async function determineTotalElements(elements) {
14716
+ async function determineTotalElements(elements, config) {
14683
14717
  try {
14684
14718
  let totalElements = 0;
14685
- for (const element of elements) {
14686
- const scaleAsString = element.dataset.scale;
14687
- if (!scaleAsString)
14688
- continue;
14689
- if (scaleAsString.includes(",")) {
14690
- const scales = scaleAsString.trim().split(",").map((scale) => parseFloat(scale));
14691
- if (scales.some((scale) => isNaN(scale)))
14692
- continue;
14693
- totalElements += scales.length;
14694
- } else {
14695
- const scaleAsNumber = parseFloat(scaleAsString.trim());
14696
- if (isNaN(scaleAsNumber)) {
14697
- continue;
14698
- } else {
14699
- totalElements++;
14700
- }
14701
- }
14719
+ for (const element of Array.from(elements)) {
14720
+ totalElements += countScalesForElement(element, config);
14702
14721
  }
14703
14722
  return totalElements;
14704
- } catch (error) {
14723
+ } catch {
14705
14724
  return 1;
14706
14725
  }
14707
14726
  }
14727
+ function countScalesForElement(element, config) {
14728
+ const scaleAsString = element.dataset.scale;
14729
+ if (!scaleAsString)
14730
+ return countConfigScale(config);
14731
+ if (scaleAsString.includes(",")) {
14732
+ const scales = scaleAsString.trim().split(",").map((scale) => parseFloat(scale));
14733
+ if (scales.some((scale) => isNaN(scale)))
14734
+ return countConfigScale(config);
14735
+ return scales.length;
14736
+ }
14737
+ const scaleAsNumber = parseFloat(scaleAsString.trim());
14738
+ if (isNaN(scaleAsNumber))
14739
+ return countConfigScale(config);
14740
+ return 1;
14741
+ }
14742
+ function countConfigScale(config) {
14743
+ return Array.isArray(config.scale) ? config.scale.length : 1;
14744
+ }
14708
14745
 
14709
14746
  // src/capture/index.ts
14710
14747
  var windowLogging = true;
@@ -14720,54 +14757,90 @@ async function capture(elements, userConfig = defaultConfig) {
14720
14757
  elements = [elements];
14721
14758
  const originalLength = elements.length;
14722
14759
  elements = removeHiddenElements(elements);
14723
- const totalElements = await determineTotalElements(elements);
14760
+ const totalElements = await determineTotalElements(elements, config);
14724
14761
  if (originalLength !== elements.length)
14725
14762
  log.verbose("Skipping capture of hidden elements: ", originalLength - elements.length);
14726
14763
  log.verbose("Element to capture", elements.length);
14727
14764
  if (userConfig.corsProxyBaseUrl)
14728
14765
  await corsProxy.run(config, elements);
14729
- let images = [];
14730
- let filenames = [];
14766
+ const images = [];
14767
+ const seen = new Set;
14731
14768
  let imageNumber = 1;
14732
- for (const element of elements) {
14733
- const imageOptions = await getImageOptions(element, config);
14734
- log.verbose("Image options", imageOptions);
14735
- if (imageOptions.scale instanceof Array) {
14736
- log.verbose("Multi-scale capture");
14737
- imageOptions.includeScaleInLabel = true;
14738
- for (const scale of imageOptions.scale) {
14739
- log.progress(imageNumber++, totalElements);
14740
- const image = await captureElement(element, { ...imageOptions, scale }, filenames);
14741
- images.push(image);
14742
- }
14743
- } else if (typeof imageOptions.scale === "number") {
14744
- log.progress(imageNumber++, totalElements);
14745
- log.verbose("Single-scale capture");
14746
- const image = await captureElement(element, imageOptions, filenames);
14747
- images.push(image);
14748
- }
14749
- }
14750
- if (userConfig.downloadImages)
14769
+ const tryCapture = async (element, options) => {
14770
+ try {
14771
+ const image = await captureElement(element, options, seen, config.screenshotOptions);
14772
+ images.push(applyOutput(image, config.output));
14773
+ } catch (error) {
14774
+ log.error(error);
14775
+ } finally {
14776
+ const completed = imageNumber++;
14777
+ log.progress(completed, totalElements);
14778
+ config.onProgress?.(completed, totalElements);
14779
+ }
14780
+ };
14781
+ captureLoop:
14782
+ for (const element of elements) {
14783
+ if (config.signal?.aborted)
14784
+ break;
14785
+ const imageOptions = await getImageOptions(element, config);
14786
+ log.verbose("Image options", imageOptions);
14787
+ if (imageOptions.scale instanceof Array) {
14788
+ log.verbose("Multi-scale capture");
14789
+ imageOptions.includeScaleInLabel = true;
14790
+ for (const scale of imageOptions.scale) {
14791
+ if (config.signal?.aborted)
14792
+ break captureLoop;
14793
+ await tryCapture(element, {
14794
+ ...imageOptions,
14795
+ scale
14796
+ });
14797
+ }
14798
+ } else if (typeof imageOptions.scale === "number") {
14799
+ log.verbose("Single-scale capture");
14800
+ await tryCapture(element, imageOptions);
14801
+ }
14802
+ }
14803
+ if (config.signal?.aborted) {
14804
+ log.verbose("Capture aborted; returning", images.length, "image(s)");
14805
+ return images;
14806
+ }
14807
+ if (config.downloadImages)
14751
14808
  await downloadImages(images, config);
14752
- if (userConfig.corsProxyBaseUrl)
14753
- await corsProxy.cleanUp();
14754
14809
  return images;
14755
14810
  } catch (error) {
14756
14811
  log.error(error);
14757
14812
  return null;
14758
14813
  } finally {
14814
+ if (userConfig.corsProxyBaseUrl)
14815
+ await corsProxy.cleanUp();
14759
14816
  log.group.close();
14760
14817
  }
14761
14818
  }
14819
+ function applyOutput(image, output) {
14820
+ if (!output || output === "dataurl")
14821
+ return image;
14822
+ const blob = dataUrlToBlob(image.dataURL);
14823
+ if (output === "blob")
14824
+ return { ...image, dataURL: "", blob };
14825
+ return { ...image, blob };
14826
+ }
14827
+
14828
+ // src/capture/copy-to-clipboard.ts
14829
+ async function copyImageToClipboard(image) {
14830
+ const blob = image.blob ?? dataUrlToBlob(image.dataURL);
14831
+ await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);
14832
+ }
14762
14833
 
14763
14834
  // src/index.ts
14764
14835
  if (typeof window !== "undefined") {
14765
14836
  window.imageExporter = capture;
14766
14837
  window.imageExporterDownload = downloadImages;
14838
+ window.imageExporterCopyToClipboard = copyImageToClipboard;
14767
14839
  }
14768
14840
  export {
14769
14841
  downloadImages,
14842
+ copyImageToClipboard,
14770
14843
  capture
14771
14844
  };
14772
14845
 
14773
- //# debugId=156298500FD95FA864756E2164756E21
14846
+ //# debugId=5968FC6C897FE80D64756E2164756E21