@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.
- package/dist/features/screenshot.d.ts +29 -0
- package/dist/features/screenshot.d.ts.map +1 -1
- package/dist/index.cjs.js +161 -36
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +161 -36
- package/dist/index.esm.js.map +1 -1
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -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;;
|
|
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
|
-
//
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
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
|
|
9341
|
-
const ctx =
|
|
9342
|
-
|
|
9343
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
9358
|
-
|
|
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
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
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
|
|
9380
|
-
const ctx =
|
|
9381
|
-
|
|
9382
|
-
|
|
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
|
-
|
|
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
|
}
|