@uxbertlabs/reportly 1.0.23 → 1.0.24
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 +163 -26
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +163 -26
- 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
package/dist/index.esm.js
CHANGED
|
@@ -9297,51 +9297,144 @@ var parseBackgroundColor = function (context, element, backgroundColorOverride)
|
|
|
9297
9297
|
// Screenshot capture using html2canvas
|
|
9298
9298
|
class Screenshot {
|
|
9299
9299
|
constructor() {
|
|
9300
|
+
this.captureDelay = 500; // Default delay before capture
|
|
9300
9301
|
this.currentScreenshot = null;
|
|
9301
9302
|
}
|
|
9303
|
+
/**
|
|
9304
|
+
* Wait for specified milliseconds
|
|
9305
|
+
*/
|
|
9302
9306
|
wait(ms) {
|
|
9303
9307
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
9304
9308
|
}
|
|
9309
|
+
/**
|
|
9310
|
+
* Wait for animations to complete and DOM to stabilize
|
|
9311
|
+
*/
|
|
9312
|
+
async waitForStability() {
|
|
9313
|
+
// Wait for initial stabilization
|
|
9314
|
+
await this.wait(this.captureDelay);
|
|
9315
|
+
// Wait for images to load
|
|
9316
|
+
await this.waitForImages();
|
|
9317
|
+
// Wait for fonts to load
|
|
9318
|
+
await this.waitForFonts();
|
|
9319
|
+
// Additional short wait for any final rendering
|
|
9320
|
+
await this.wait(100);
|
|
9321
|
+
}
|
|
9322
|
+
/**
|
|
9323
|
+
* Wait for all images to finish loading
|
|
9324
|
+
*/
|
|
9325
|
+
async waitForImages() {
|
|
9326
|
+
const images = Array.from(document.images);
|
|
9327
|
+
const imagePromises = images.map(img => {
|
|
9328
|
+
if (img.complete) {
|
|
9329
|
+
return Promise.resolve();
|
|
9330
|
+
}
|
|
9331
|
+
return new Promise(resolve => {
|
|
9332
|
+
img.addEventListener('load', () => resolve(), {
|
|
9333
|
+
once: true
|
|
9334
|
+
});
|
|
9335
|
+
img.addEventListener('error', () => resolve(), {
|
|
9336
|
+
once: true
|
|
9337
|
+
});
|
|
9338
|
+
// Timeout after 3 seconds for slow images
|
|
9339
|
+
setTimeout(() => resolve(), 3000);
|
|
9340
|
+
});
|
|
9341
|
+
});
|
|
9342
|
+
await Promise.all(imagePromises);
|
|
9343
|
+
}
|
|
9344
|
+
/**
|
|
9345
|
+
* Wait for fonts to load
|
|
9346
|
+
*/
|
|
9347
|
+
async waitForFonts() {
|
|
9348
|
+
if ('fonts' in document) {
|
|
9349
|
+
try {
|
|
9350
|
+
await Promise.race([document.fonts.ready, this.wait(1000) // Timeout after 1 second
|
|
9351
|
+
]);
|
|
9352
|
+
} catch (e) {
|
|
9353
|
+
// Font loading failed, continue anyway
|
|
9354
|
+
console.warn('Font loading check failed:', e);
|
|
9355
|
+
}
|
|
9356
|
+
}
|
|
9357
|
+
}
|
|
9358
|
+
/**
|
|
9359
|
+
* Force a reflow to ensure styles are applied
|
|
9360
|
+
*/
|
|
9361
|
+
forceReflow() {
|
|
9362
|
+
// Reading offsetHeight forces a reflow
|
|
9363
|
+
document.body.offsetHeight;
|
|
9364
|
+
}
|
|
9305
9365
|
async capture(mode = "fullpage") {
|
|
9306
9366
|
try {
|
|
9307
|
-
//
|
|
9367
|
+
// Show loading screen immediately (before hiding UI)
|
|
9368
|
+
this.showLoadingScreen();
|
|
9369
|
+
// Wait for DOM to stabilize, animations to complete, and assets to load
|
|
9370
|
+
await this.waitForStability();
|
|
9371
|
+
// Hide loading screen and UXbert UI elements BEFORE capturing
|
|
9372
|
+
this.hideLoadingScreen();
|
|
9308
9373
|
this.hideUXbertElements();
|
|
9374
|
+
// Force a reflow to ensure all styles are computed
|
|
9375
|
+
this.forceReflow();
|
|
9309
9376
|
const originalScrollY = window.scrollY;
|
|
9310
9377
|
let canvas;
|
|
9311
9378
|
if (mode === "viewport") {
|
|
9312
9379
|
// Capture only the current viewport
|
|
9313
9380
|
canvas = await html2canvas(document.body, {
|
|
9314
|
-
// foreignObjectRendering: true,
|
|
9315
9381
|
useCORS: true,
|
|
9316
9382
|
allowTaint: false,
|
|
9317
|
-
logging:
|
|
9383
|
+
logging: false,
|
|
9318
9384
|
width: window.innerWidth,
|
|
9319
9385
|
height: window.innerHeight,
|
|
9320
9386
|
windowWidth: window.innerWidth,
|
|
9321
9387
|
windowHeight: window.innerHeight,
|
|
9322
9388
|
x: window.scrollX,
|
|
9323
9389
|
y: window.scrollY,
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9390
|
+
// Improved rendering options
|
|
9391
|
+
backgroundColor: null,
|
|
9392
|
+
imageTimeout: 5000,
|
|
9393
|
+
removeContainer: true,
|
|
9394
|
+
scale: window.devicePixelRatio || 1,
|
|
9395
|
+
onclone: clonedDoc => {
|
|
9396
|
+
// Ensure all styles are applied in the cloned document
|
|
9397
|
+
const clonedBody = clonedDoc.body;
|
|
9398
|
+
// Force all animations to their final state
|
|
9399
|
+
const animatedElements = clonedBody.querySelectorAll('*');
|
|
9400
|
+
animatedElements.forEach(el => {
|
|
9401
|
+
const element = el;
|
|
9402
|
+
// Pause all animations
|
|
9403
|
+
element.style.animationPlayState = 'paused';
|
|
9404
|
+
element.style.animationDelay = '-9999s';
|
|
9405
|
+
element.style.animationIterationCount = '1';
|
|
9406
|
+
// Disable transitions
|
|
9407
|
+
element.style.transition = 'none';
|
|
9408
|
+
});
|
|
9409
|
+
// Ensure lazy-loaded images are visible
|
|
9410
|
+
const images = clonedBody.querySelectorAll('img');
|
|
9411
|
+
images.forEach(img => {
|
|
9412
|
+
img.style.opacity = '1';
|
|
9413
|
+
img.style.visibility = 'visible';
|
|
9414
|
+
});
|
|
9329
9415
|
},
|
|
9330
9416
|
ignoreElements: element => {
|
|
9417
|
+
// Ignore loading screen by ID
|
|
9418
|
+
if (element.id === 'uxbert-screenshot-loading') {
|
|
9419
|
+
return true;
|
|
9420
|
+
}
|
|
9421
|
+
// Ignore Uxbert elements
|
|
9422
|
+
if (element.getAttribute('data-uxbert-reportly') !== null || element.className?.toString().includes('uxbert-') || element.id?.includes('uxbert-')) {
|
|
9423
|
+
return true;
|
|
9424
|
+
}
|
|
9331
9425
|
// Skip cross-origin images that might cause issues
|
|
9332
9426
|
if (element.tagName === "IMG") {
|
|
9333
9427
|
const img = element;
|
|
9334
9428
|
try {
|
|
9335
9429
|
// Test if image is accessible
|
|
9336
|
-
const
|
|
9337
|
-
const ctx =
|
|
9338
|
-
|
|
9339
|
-
|
|
9430
|
+
const testCanvas = document.createElement("canvas");
|
|
9431
|
+
const ctx = testCanvas.getContext("2d");
|
|
9432
|
+
testCanvas.width = 1;
|
|
9433
|
+
testCanvas.height = 1;
|
|
9340
9434
|
ctx?.drawImage(img, 0, 0, 1, 1);
|
|
9341
|
-
|
|
9435
|
+
testCanvas.toDataURL();
|
|
9342
9436
|
return false; // Include the image
|
|
9343
9437
|
} catch (e) {
|
|
9344
|
-
// Image is tainted, skip it
|
|
9345
9438
|
console.warn("Skipping cross-origin image:", img.src);
|
|
9346
9439
|
return true;
|
|
9347
9440
|
}
|
|
@@ -9352,6 +9445,8 @@ class Screenshot {
|
|
|
9352
9445
|
} else {
|
|
9353
9446
|
// Scroll to top to capture full page
|
|
9354
9447
|
window.scrollTo(0, 0);
|
|
9448
|
+
// Wait a bit for scroll to settle
|
|
9449
|
+
await this.wait(100);
|
|
9355
9450
|
// Get full page dimensions
|
|
9356
9451
|
const fullPageHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
|
|
9357
9452
|
const fullPageWidth = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);
|
|
@@ -9362,25 +9457,56 @@ class Screenshot {
|
|
|
9362
9457
|
logging: false,
|
|
9363
9458
|
width: fullPageWidth,
|
|
9364
9459
|
height: fullPageHeight,
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9460
|
+
windowWidth: fullPageWidth,
|
|
9461
|
+
windowHeight: fullPageHeight,
|
|
9462
|
+
// Improved rendering options
|
|
9463
|
+
backgroundColor: null,
|
|
9464
|
+
imageTimeout: 5000,
|
|
9465
|
+
removeContainer: true,
|
|
9466
|
+
scale: window.devicePixelRatio || 1,
|
|
9467
|
+
onclone: clonedDoc => {
|
|
9468
|
+
// Ensure all styles are applied in the cloned document
|
|
9469
|
+
const clonedBody = clonedDoc.body;
|
|
9470
|
+
// Force all animations to their final state
|
|
9471
|
+
const animatedElements = clonedBody.querySelectorAll('*');
|
|
9472
|
+
animatedElements.forEach(el => {
|
|
9473
|
+
const element = el;
|
|
9474
|
+
// Pause all animations at their end state
|
|
9475
|
+
element.style.animationPlayState = 'paused';
|
|
9476
|
+
element.style.animationDelay = '-9999s';
|
|
9477
|
+
element.style.animationIterationCount = '1';
|
|
9478
|
+
// Disable transitions
|
|
9479
|
+
element.style.transition = 'none';
|
|
9480
|
+
});
|
|
9481
|
+
// Ensure lazy-loaded images are visible
|
|
9482
|
+
const images = clonedBody.querySelectorAll('img');
|
|
9483
|
+
images.forEach(img => {
|
|
9484
|
+
img.style.opacity = '1';
|
|
9485
|
+
img.style.visibility = 'visible';
|
|
9486
|
+
});
|
|
9368
9487
|
},
|
|
9369
9488
|
ignoreElements: element => {
|
|
9489
|
+
// Ignore loading screen by ID
|
|
9490
|
+
if (element.id === 'uxbert-screenshot-loading') {
|
|
9491
|
+
return true;
|
|
9492
|
+
}
|
|
9493
|
+
// Ignore Uxbert elements
|
|
9494
|
+
if (element.getAttribute('data-uxbert-reportly') !== null || element.className?.toString().includes('uxbert-') || element.id?.includes('uxbert-')) {
|
|
9495
|
+
return true;
|
|
9496
|
+
}
|
|
9370
9497
|
// Skip cross-origin images that might cause issues
|
|
9371
9498
|
if (element.tagName === "IMG") {
|
|
9372
9499
|
const img = element;
|
|
9373
9500
|
try {
|
|
9374
9501
|
// Test if image is accessible
|
|
9375
|
-
const
|
|
9376
|
-
const ctx =
|
|
9377
|
-
|
|
9378
|
-
|
|
9502
|
+
const testCanvas = document.createElement("canvas");
|
|
9503
|
+
const ctx = testCanvas.getContext("2d");
|
|
9504
|
+
testCanvas.width = 1;
|
|
9505
|
+
testCanvas.height = 1;
|
|
9379
9506
|
ctx?.drawImage(img, 0, 0, 1, 1);
|
|
9380
|
-
|
|
9507
|
+
testCanvas.toDataURL();
|
|
9381
9508
|
return false; // Include the image
|
|
9382
9509
|
} catch (e) {
|
|
9383
|
-
// Image is tainted, skip it
|
|
9384
9510
|
console.warn("Skipping cross-origin image:", img.src);
|
|
9385
9511
|
return true;
|
|
9386
9512
|
}
|
|
@@ -9393,15 +9519,13 @@ class Screenshot {
|
|
|
9393
9519
|
}
|
|
9394
9520
|
// Show UXbert UI elements again
|
|
9395
9521
|
this.showUXbertElements();
|
|
9396
|
-
// Hide loading screen
|
|
9397
|
-
this.hideLoadingScreen();
|
|
9398
9522
|
// Convert to base64
|
|
9399
9523
|
this.currentScreenshot = canvas.toDataURL("image/png");
|
|
9400
9524
|
return this.currentScreenshot;
|
|
9401
9525
|
} catch (error) {
|
|
9402
9526
|
console.error("Screenshot capture failed:", error);
|
|
9403
|
-
this.showUXbertElements();
|
|
9404
9527
|
this.hideLoadingScreen();
|
|
9528
|
+
this.showUXbertElements();
|
|
9405
9529
|
throw error;
|
|
9406
9530
|
}
|
|
9407
9531
|
}
|
|
@@ -9485,6 +9609,19 @@ class Screenshot {
|
|
|
9485
9609
|
loadingOverlay.remove();
|
|
9486
9610
|
}
|
|
9487
9611
|
}
|
|
9612
|
+
/**
|
|
9613
|
+
* Set the delay before capture in milliseconds
|
|
9614
|
+
* @param ms - Delay in milliseconds (default: 500)
|
|
9615
|
+
*/
|
|
9616
|
+
setCaptureDelay(ms) {
|
|
9617
|
+
this.captureDelay = Math.max(0, Math.min(ms, 5000)); // Clamp between 0-5000ms
|
|
9618
|
+
}
|
|
9619
|
+
/**
|
|
9620
|
+
* Get the current capture delay
|
|
9621
|
+
*/
|
|
9622
|
+
getCaptureDelay() {
|
|
9623
|
+
return this.captureDelay;
|
|
9624
|
+
}
|
|
9488
9625
|
getScreenshot() {
|
|
9489
9626
|
return this.currentScreenshot;
|
|
9490
9627
|
}
|