@uxbertlabs/reportly 1.0.23 → 1.0.25

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.
@@ -1,12 +1,41 @@
1
1
  declare class Screenshot {
2
2
  private currentScreenshot;
3
+ private captureDelay;
3
4
  constructor();
5
+ /**
6
+ * Wait for specified milliseconds
7
+ */
4
8
  private wait;
9
+ /**
10
+ * Wait for animations to complete and DOM to stabilize
11
+ */
12
+ private waitForStability;
13
+ /**
14
+ * Wait for all images to finish loading
15
+ */
16
+ private waitForImages;
17
+ /**
18
+ * Wait for fonts to load
19
+ */
20
+ private waitForFonts;
21
+ /**
22
+ * Force a reflow to ensure styles are applied
23
+ */
24
+ private forceReflow;
5
25
  capture(mode?: "viewport" | "fullpage"): Promise<string>;
6
26
  private hideUXbertElements;
7
27
  private showUXbertElements;
8
28
  private showLoadingScreen;
9
29
  private hideLoadingScreen;
30
+ /**
31
+ * Set the delay before capture in milliseconds
32
+ * @param ms - Delay in milliseconds (default: 500)
33
+ */
34
+ setCaptureDelay(ms: number): void;
35
+ /**
36
+ * Get the current capture delay
37
+ */
38
+ getCaptureDelay(): number;
10
39
  getScreenshot(): string | null;
11
40
  clear(): void;
12
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/features/screenshot.ts"],"names":[],"mappings":"AAGA,cAAM,UAAU;IACd,OAAO,CAAC,iBAAiB,CAAgB;;IAKzC,OAAO,CAAC,IAAI;IAGN,OAAO,CAAC,IAAI,GAAE,UAAU,GAAG,UAAuB,GAAG,OAAO,CAAC,MAAM,CAAC;IA8H1E,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,iBAAiB;IA6DzB,OAAO,CAAC,iBAAiB;IAOzB,aAAa,IAAI,MAAM,GAAG,IAAI;IAI9B,KAAK,IAAI,IAAI;CAGd;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/features/screenshot.ts"],"names":[],"mappings":"AAGA,cAAM,UAAU;IACd,OAAO,CAAC,iBAAiB,CAAgB;IACzC,OAAO,CAAC,YAAY,CAAe;;IAMnC;;OAEG;IACH,OAAO,CAAC,IAAI;IAIZ;;OAEG;YACW,gBAAgB;IAc9B;;OAEG;YACW,aAAa;IAgB3B;;OAEG;YACW,YAAY;IAc1B;;OAEG;IACH,OAAO,CAAC,WAAW;IAKb,OAAO,CAAC,IAAI,GAAE,UAAU,GAAG,UAAuB,GAAG,OAAO,CAAC,MAAM,CAAC;IAqL1E,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,iBAAiB;IA6DzB,OAAO,CAAC,iBAAiB;IAOzB;;;OAGG;IACH,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIjC;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB,aAAa,IAAI,MAAM,GAAG,IAAI;IAI9B,KAAK,IAAI,IAAI;CAGd;AAED,eAAe,UAAU,CAAC"}
package/dist/index.cjs.js CHANGED
@@ -9301,51 +9301,143 @@ var parseBackgroundColor = function (context, element, backgroundColorOverride)
9301
9301
  // Screenshot capture using html2canvas
9302
9302
  class Screenshot {
9303
9303
  constructor() {
9304
+ this.captureDelay = 500; // Default delay before capture
9304
9305
  this.currentScreenshot = null;
9305
9306
  }
9307
+ /**
9308
+ * Wait for specified milliseconds
9309
+ */
9306
9310
  wait(ms) {
9307
9311
  return new Promise(resolve => setTimeout(resolve, ms));
9308
9312
  }
9313
+ /**
9314
+ * Wait for animations to complete and DOM to stabilize
9315
+ */
9316
+ async waitForStability() {
9317
+ // Wait for initial stabilization
9318
+ await this.wait(this.captureDelay);
9319
+ // Wait for images to load
9320
+ await this.waitForImages();
9321
+ // Wait for fonts to load
9322
+ await this.waitForFonts();
9323
+ // Additional short wait for any final rendering
9324
+ await this.wait(100);
9325
+ }
9326
+ /**
9327
+ * Wait for all images to finish loading
9328
+ */
9329
+ async waitForImages() {
9330
+ const images = Array.from(document.images);
9331
+ const imagePromises = images.map(img => {
9332
+ if (img.complete) {
9333
+ return Promise.resolve();
9334
+ }
9335
+ return new Promise(resolve => {
9336
+ img.addEventListener("load", () => resolve(), {
9337
+ once: true
9338
+ });
9339
+ img.addEventListener("error", () => resolve(), {
9340
+ once: true
9341
+ });
9342
+ // Timeout after 3 seconds for slow images
9343
+ setTimeout(() => resolve(), 3000);
9344
+ });
9345
+ });
9346
+ await Promise.all(imagePromises);
9347
+ }
9348
+ /**
9349
+ * Wait for fonts to load
9350
+ */
9351
+ async waitForFonts() {
9352
+ if ("fonts" in document) {
9353
+ try {
9354
+ await Promise.race([document.fonts.ready, this.wait(1000) // Timeout after 1 second
9355
+ ]);
9356
+ } catch (e) {
9357
+ // Font loading failed, continue anyway
9358
+ console.warn("Font loading check failed:", e);
9359
+ }
9360
+ }
9361
+ }
9362
+ /**
9363
+ * Force a reflow to ensure styles are applied
9364
+ */
9365
+ forceReflow() {
9366
+ // Reading offsetHeight forces a reflow
9367
+ document.body.offsetHeight;
9368
+ }
9309
9369
  async capture(mode = "fullpage") {
9310
9370
  try {
9311
- // Hide UXbert UI elements before capturing
9371
+ // Show loading screen immediately (before hiding UI)
9372
+ this.showLoadingScreen();
9373
+ // Wait for DOM to stabilize, animations to complete, and assets to load
9374
+ await this.waitForStability();
9375
+ // Hide loading screen and UXbert UI elements BEFORE capturing
9376
+ this.hideLoadingScreen();
9312
9377
  this.hideUXbertElements();
9313
- const originalScrollY = window.scrollY;
9378
+ // Force a reflow to ensure all styles are computed
9379
+ this.forceReflow();
9314
9380
  let canvas;
9315
9381
  if (mode === "viewport") {
9316
9382
  // Capture only the current viewport
9317
9383
  canvas = await html2canvas(document.body, {
9318
- // foreignObjectRendering: true,
9319
9384
  useCORS: true,
9320
9385
  allowTaint: false,
9321
- logging: true,
9386
+ logging: false,
9322
9387
  width: window.innerWidth,
9323
9388
  height: window.innerHeight,
9324
9389
  windowWidth: window.innerWidth,
9325
9390
  windowHeight: window.innerHeight,
9326
9391
  x: window.scrollX,
9327
9392
  y: window.scrollY,
9328
- onclone: async () => {
9329
- // Show loading screen after clone (won't appear in screenshot)
9330
- this.showLoadingScreen();
9331
- // Wait 1 second after cloning to let animations complete
9332
- await this.wait(1000);
9393
+ // Improved rendering options
9394
+ backgroundColor: null,
9395
+ imageTimeout: 5000,
9396
+ removeContainer: true,
9397
+ scale: window.devicePixelRatio || 1,
9398
+ onclone: clonedDoc => {
9399
+ // Ensure all styles are applied in the cloned document
9400
+ const clonedBody = clonedDoc.body;
9401
+ // Force all animations to their final state
9402
+ const animatedElements = clonedBody.querySelectorAll("*");
9403
+ animatedElements.forEach(el => {
9404
+ const element = el;
9405
+ // Pause all animations
9406
+ element.style.animationPlayState = "paused";
9407
+ element.style.animationDelay = "-9999s";
9408
+ element.style.animationIterationCount = "1";
9409
+ // Disable transitions
9410
+ element.style.transition = "none";
9411
+ });
9412
+ // Ensure lazy-loaded images are visible
9413
+ const images = clonedBody.querySelectorAll("img");
9414
+ images.forEach(img => {
9415
+ img.style.opacity = "1";
9416
+ img.style.visibility = "visible";
9417
+ });
9333
9418
  },
9334
9419
  ignoreElements: element => {
9420
+ // Ignore loading screen by ID
9421
+ if (element.id === "uxbert-screenshot-loading") {
9422
+ return true;
9423
+ }
9424
+ // Ignore Uxbert elements
9425
+ if (element.getAttribute("data-uxbert-reportly") !== null || element.className?.toString().includes("uxbert-") || element.id?.includes("uxbert-")) {
9426
+ return true;
9427
+ }
9335
9428
  // Skip cross-origin images that might cause issues
9336
9429
  if (element.tagName === "IMG") {
9337
9430
  const img = element;
9338
9431
  try {
9339
9432
  // Test if image is accessible
9340
- const canvas = document.createElement("canvas");
9341
- const ctx = canvas.getContext("2d");
9342
- canvas.width = 1;
9343
- canvas.height = 1;
9433
+ const testCanvas = document.createElement("canvas");
9434
+ const ctx = testCanvas.getContext("2d");
9435
+ testCanvas.width = 1;
9436
+ testCanvas.height = 1;
9344
9437
  ctx?.drawImage(img, 0, 0, 1, 1);
9345
- canvas.toDataURL(); // This will throw if tainted
9438
+ testCanvas.toDataURL();
9346
9439
  return false; // Include the image
9347
9440
  } catch (e) {
9348
- // Image is tainted, skip it
9349
9441
  console.warn("Skipping cross-origin image:", img.src);
9350
9442
  return true;
9351
9443
  }
@@ -9354,37 +9446,61 @@ class Screenshot {
9354
9446
  }
9355
9447
  });
9356
9448
  } else {
9357
- // Scroll to top to capture full page
9358
- window.scrollTo(0, 0);
9359
- // Get full page dimensions
9360
- const fullPageHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
9361
- const fullPageWidth = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);
9449
+ // Wait a bit for scroll to settle
9450
+ await this.wait(1000);
9362
9451
  // Capture the full page
9363
9452
  canvas = await html2canvas(document.body, {
9364
9453
  useCORS: true,
9365
9454
  allowTaint: false,
9366
9455
  logging: false,
9367
- width: fullPageWidth,
9368
- height: fullPageHeight,
9369
- onclone: async () => {
9370
- // Show loading screen after clone (won't appear in screenshot)
9371
- this.showLoadingScreen();
9456
+ // Improved rendering options
9457
+ backgroundColor: null,
9458
+ imageTimeout: 5000,
9459
+ removeContainer: true,
9460
+ scale: window.devicePixelRatio || 1,
9461
+ onclone: clonedDoc => {
9462
+ // Ensure all styles are applied in the cloned document
9463
+ const clonedBody = clonedDoc.body;
9464
+ // Force all animations to their final state
9465
+ const animatedElements = clonedBody.querySelectorAll("*");
9466
+ animatedElements.forEach(el => {
9467
+ const element = el;
9468
+ // Pause all animations at their end state
9469
+ element.style.animationPlayState = "paused";
9470
+ element.style.animationDelay = "-9999s";
9471
+ element.style.animationIterationCount = "1";
9472
+ // Disable transitions
9473
+ element.style.transition = "none";
9474
+ });
9475
+ // Ensure lazy-loaded images are visible
9476
+ const images = clonedBody.querySelectorAll("img");
9477
+ images.forEach(img => {
9478
+ img.style.opacity = "1";
9479
+ img.style.visibility = "visible";
9480
+ });
9372
9481
  },
9373
9482
  ignoreElements: element => {
9483
+ // Ignore loading screen by ID
9484
+ if (element.id === "uxbert-screenshot-loading") {
9485
+ return true;
9486
+ }
9487
+ // Ignore Uxbert elements
9488
+ if (element.getAttribute("data-uxbert-reportly") !== null || element.className?.toString().includes("uxbert-") || element.id?.includes("uxbert-")) {
9489
+ return true;
9490
+ }
9374
9491
  // Skip cross-origin images that might cause issues
9375
9492
  if (element.tagName === "IMG") {
9376
9493
  const img = element;
9377
9494
  try {
9378
9495
  // Test if image is accessible
9379
- const canvas = document.createElement("canvas");
9380
- const ctx = canvas.getContext("2d");
9381
- canvas.width = 1;
9382
- canvas.height = 1;
9496
+ const testCanvas = document.createElement("canvas");
9497
+ const ctx = testCanvas.getContext("2d");
9498
+ testCanvas.width = 1;
9499
+ testCanvas.height = 1;
9383
9500
  ctx?.drawImage(img, 0, 0, 1, 1);
9384
- canvas.toDataURL(); // This will throw if tainted
9501
+ testCanvas.toDataURL();
9385
9502
  return false; // Include the image
9386
9503
  } catch (e) {
9387
- // Image is tainted, skip it
9388
9504
  console.warn("Skipping cross-origin image:", img.src);
9389
9505
  return true;
9390
9506
  }
@@ -9392,20 +9508,16 @@ class Screenshot {
9392
9508
  return false;
9393
9509
  }
9394
9510
  });
9395
- // Restore scroll position
9396
- window.scrollTo(0, originalScrollY);
9397
9511
  }
9398
9512
  // Show UXbert UI elements again
9399
9513
  this.showUXbertElements();
9400
- // Hide loading screen
9401
- this.hideLoadingScreen();
9402
9514
  // Convert to base64
9403
9515
  this.currentScreenshot = canvas.toDataURL("image/png");
9404
9516
  return this.currentScreenshot;
9405
9517
  } catch (error) {
9406
9518
  console.error("Screenshot capture failed:", error);
9407
- this.showUXbertElements();
9408
9519
  this.hideLoadingScreen();
9520
+ this.showUXbertElements();
9409
9521
  throw error;
9410
9522
  }
9411
9523
  }
@@ -9489,6 +9601,19 @@ class Screenshot {
9489
9601
  loadingOverlay.remove();
9490
9602
  }
9491
9603
  }
9604
+ /**
9605
+ * Set the delay before capture in milliseconds
9606
+ * @param ms - Delay in milliseconds (default: 500)
9607
+ */
9608
+ setCaptureDelay(ms) {
9609
+ this.captureDelay = Math.max(0, Math.min(ms, 5000)); // Clamp between 0-5000ms
9610
+ }
9611
+ /**
9612
+ * Get the current capture delay
9613
+ */
9614
+ getCaptureDelay() {
9615
+ return this.captureDelay;
9616
+ }
9492
9617
  getScreenshot() {
9493
9618
  return this.currentScreenshot;
9494
9619
  }