@uxbertlabs/reportly 1.0.34 → 1.0.36
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/annotation.d.ts.map +1 -1
- package/dist/features/screenshot.d.ts +1 -1
- package/dist/features/screenshot.d.ts.map +1 -1
- package/dist/index.cjs.js +139 -41
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +139 -41
- package/dist/index.esm.js.map +1 -1
- package/dist/index.min.js +4 -4
- package/dist/index.min.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -9324,36 +9324,60 @@ class Screenshot {
|
|
|
9324
9324
|
* Wait for animations to complete and DOM to stabilize
|
|
9325
9325
|
*/
|
|
9326
9326
|
async waitForStability() {
|
|
9327
|
+
console.log("⏳ Waiting for page stability...");
|
|
9327
9328
|
// Wait for initial stabilization
|
|
9328
9329
|
await this.wait(this.captureDelay);
|
|
9329
|
-
// Wait for images to
|
|
9330
|
+
// Wait for currently loading images (NO scrolling to avoid misalignment)
|
|
9331
|
+
// The onclone callback will handle image src copying
|
|
9330
9332
|
await this.waitForImages();
|
|
9331
9333
|
// Wait for fonts to load
|
|
9332
9334
|
await this.waitForFonts();
|
|
9333
|
-
//
|
|
9335
|
+
// Brief wait for final rendering
|
|
9334
9336
|
await this.wait(100);
|
|
9337
|
+
console.log("✅ Page stabilized and ready for capture");
|
|
9335
9338
|
}
|
|
9336
9339
|
/**
|
|
9337
|
-
* Wait for
|
|
9340
|
+
* Wait for visible images to finish loading (no page scrolling to avoid annotation misalignment)
|
|
9338
9341
|
*/
|
|
9339
9342
|
async waitForImages() {
|
|
9340
9343
|
const images = Array.from(document.images);
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9344
|
+
// Filter to only images that are currently loading
|
|
9345
|
+
const loadingImages = images.filter(img => !img.complete || img.naturalHeight === 0);
|
|
9346
|
+
if (loadingImages.length === 0) {
|
|
9347
|
+
console.log("✅ All visible images already loaded");
|
|
9348
|
+
return;
|
|
9349
|
+
}
|
|
9350
|
+
console.log(`⏳ Waiting for ${loadingImages.length} loading images...`);
|
|
9351
|
+
const imagePromises = loadingImages.map(img => {
|
|
9345
9352
|
return new Promise(resolve => {
|
|
9346
|
-
img.
|
|
9353
|
+
if (img.complete && img.naturalHeight !== 0) {
|
|
9354
|
+
resolve();
|
|
9355
|
+
return;
|
|
9356
|
+
}
|
|
9357
|
+
const onLoad = () => {
|
|
9358
|
+
console.log(`✅ Image loaded`);
|
|
9359
|
+
resolve();
|
|
9360
|
+
};
|
|
9361
|
+
const onError = () => {
|
|
9362
|
+
console.warn(`❌ Image failed to load`);
|
|
9363
|
+
resolve(); // Still resolve to not block
|
|
9364
|
+
};
|
|
9365
|
+
const onTimeout = () => {
|
|
9366
|
+
console.warn(`⏰ Image timed out after 2s`);
|
|
9367
|
+
resolve();
|
|
9368
|
+
};
|
|
9369
|
+
img.addEventListener("load", onLoad, {
|
|
9347
9370
|
once: true
|
|
9348
9371
|
});
|
|
9349
|
-
img.addEventListener("error",
|
|
9372
|
+
img.addEventListener("error", onError, {
|
|
9350
9373
|
once: true
|
|
9351
9374
|
});
|
|
9352
|
-
//
|
|
9353
|
-
setTimeout(
|
|
9375
|
+
// Shorter timeout - 2 seconds max
|
|
9376
|
+
setTimeout(onTimeout, 2000);
|
|
9354
9377
|
});
|
|
9355
9378
|
});
|
|
9356
9379
|
await Promise.all(imagePromises);
|
|
9380
|
+
console.log(`✅ All ${loadingImages.length} images processed`);
|
|
9357
9381
|
}
|
|
9358
9382
|
/**
|
|
9359
9383
|
* Wait for fonts to load
|
|
@@ -9414,7 +9438,8 @@ class Screenshot {
|
|
|
9414
9438
|
y: window.scrollY,
|
|
9415
9439
|
// Improved rendering options
|
|
9416
9440
|
backgroundColor: null,
|
|
9417
|
-
imageTimeout:
|
|
9441
|
+
imageTimeout: 15000,
|
|
9442
|
+
// Increased timeout for slow-loading images
|
|
9418
9443
|
removeContainer: true,
|
|
9419
9444
|
scale: window.devicePixelRatio || 1,
|
|
9420
9445
|
onclone: clonedDoc => {
|
|
@@ -9431,11 +9456,25 @@ class Screenshot {
|
|
|
9431
9456
|
// Disable transitions
|
|
9432
9457
|
element.style.transition = "none";
|
|
9433
9458
|
});
|
|
9434
|
-
//
|
|
9435
|
-
const
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9459
|
+
// Process images in the cloned document
|
|
9460
|
+
const originalImages = document.body.querySelectorAll("img");
|
|
9461
|
+
const clonedImages = clonedBody.querySelectorAll("img");
|
|
9462
|
+
clonedImages.forEach((clonedImg, index) => {
|
|
9463
|
+
const originalImg = originalImages[index];
|
|
9464
|
+
if (originalImg) {
|
|
9465
|
+
// Copy the current src from original to ensure it loads
|
|
9466
|
+
clonedImg.src = originalImg.currentSrc || originalImg.src;
|
|
9467
|
+
// Force visibility
|
|
9468
|
+
clonedImg.style.opacity = "1";
|
|
9469
|
+
clonedImg.style.visibility = "visible";
|
|
9470
|
+
clonedImg.style.display = originalImg.style.display || "inline";
|
|
9471
|
+
if (originalImg.width) clonedImg.width = originalImg.width;
|
|
9472
|
+
if (originalImg.height) clonedImg.height = originalImg.height;
|
|
9473
|
+
// Remove lazy loading attributes that might interfere
|
|
9474
|
+
clonedImg.removeAttribute("loading");
|
|
9475
|
+
clonedImg.removeAttribute("data-src");
|
|
9476
|
+
clonedImg.removeAttribute("data-srcset");
|
|
9477
|
+
}
|
|
9439
9478
|
});
|
|
9440
9479
|
},
|
|
9441
9480
|
ignoreElements: element => {
|
|
@@ -9468,8 +9507,6 @@ class Screenshot {
|
|
|
9468
9507
|
}
|
|
9469
9508
|
});
|
|
9470
9509
|
} else {
|
|
9471
|
-
// Wait a bit for scroll to settle
|
|
9472
|
-
await this.wait(1000);
|
|
9473
9510
|
// Capture the full page
|
|
9474
9511
|
canvas = await html2canvas(document.body, {
|
|
9475
9512
|
useCORS: true,
|
|
@@ -9477,7 +9514,8 @@ class Screenshot {
|
|
|
9477
9514
|
logging: false,
|
|
9478
9515
|
// Improved rendering options
|
|
9479
9516
|
backgroundColor: null,
|
|
9480
|
-
imageTimeout:
|
|
9517
|
+
imageTimeout: 15000,
|
|
9518
|
+
// Increased timeout for slow-loading images
|
|
9481
9519
|
removeContainer: true,
|
|
9482
9520
|
scale: window.devicePixelRatio || 1,
|
|
9483
9521
|
onclone: clonedDoc => {
|
|
@@ -9494,11 +9532,25 @@ class Screenshot {
|
|
|
9494
9532
|
// Disable transitions
|
|
9495
9533
|
element.style.transition = "none";
|
|
9496
9534
|
});
|
|
9497
|
-
//
|
|
9498
|
-
const
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9535
|
+
// Process images in the cloned document
|
|
9536
|
+
const originalImages = document.body.querySelectorAll("img");
|
|
9537
|
+
const clonedImages = clonedBody.querySelectorAll("img");
|
|
9538
|
+
clonedImages.forEach((clonedImg, index) => {
|
|
9539
|
+
const originalImg = originalImages[index];
|
|
9540
|
+
if (originalImg) {
|
|
9541
|
+
// Copy the current src from original to ensure it loads
|
|
9542
|
+
clonedImg.src = originalImg.currentSrc || originalImg.src;
|
|
9543
|
+
// Force visibility
|
|
9544
|
+
clonedImg.style.opacity = "1";
|
|
9545
|
+
clonedImg.style.visibility = "visible";
|
|
9546
|
+
clonedImg.style.display = originalImg.style.display || "inline";
|
|
9547
|
+
if (originalImg.width) clonedImg.width = originalImg.width;
|
|
9548
|
+
if (originalImg.height) clonedImg.height = originalImg.height;
|
|
9549
|
+
// Remove lazy loading attributes that might interfere
|
|
9550
|
+
clonedImg.removeAttribute("loading");
|
|
9551
|
+
clonedImg.removeAttribute("data-src");
|
|
9552
|
+
clonedImg.removeAttribute("data-srcset");
|
|
9553
|
+
}
|
|
9502
9554
|
});
|
|
9503
9555
|
},
|
|
9504
9556
|
ignoreElements: element => {
|
|
@@ -11630,28 +11682,74 @@ class AnnotationManager {
|
|
|
11630
11682
|
if (tempCtx && this.canvas) {
|
|
11631
11683
|
// Draw the base screenshot
|
|
11632
11684
|
tempCtx.drawImage(img, 0, 0);
|
|
11633
|
-
//
|
|
11634
|
-
const
|
|
11635
|
-
const
|
|
11636
|
-
|
|
11637
|
-
|
|
11638
|
-
|
|
11639
|
-
|
|
11640
|
-
|
|
11641
|
-
|
|
11685
|
+
// Determine the actual capture mode used
|
|
11686
|
+
const currentCanvasMode = this.getCurrentMode();
|
|
11687
|
+
const captureMode = mode || currentCanvasMode;
|
|
11688
|
+
console.log('📸 Exporting annotated screenshot:', {
|
|
11689
|
+
currentCanvasMode,
|
|
11690
|
+
captureMode,
|
|
11691
|
+
scrollPosition: {
|
|
11692
|
+
x: window.scrollX,
|
|
11693
|
+
y: window.scrollY
|
|
11694
|
+
},
|
|
11695
|
+
initialScrollPosition: this.initialScrollPosition,
|
|
11696
|
+
canvasSize: {
|
|
11697
|
+
width: this.canvas.width,
|
|
11698
|
+
height: this.canvas.height
|
|
11699
|
+
},
|
|
11700
|
+
screenshotSize: {
|
|
11701
|
+
width: img.width,
|
|
11702
|
+
height: img.height
|
|
11703
|
+
}
|
|
11704
|
+
});
|
|
11705
|
+
// We need to determine if annotations need to be offset
|
|
11706
|
+
// Annotations are stored in absolute page coordinates (with scroll offset added)
|
|
11707
|
+
// Screenshot might be:
|
|
11708
|
+
// - fullpage: captures entire page (no offset needed)
|
|
11709
|
+
// - viewport: captures current viewport (annotations need to be adjusted)
|
|
11710
|
+
if (captureMode === 'viewport') {
|
|
11711
|
+
// Screenshot is only the viewport, so we need to subtract scroll offset from annotations
|
|
11712
|
+
// to align them correctly with the viewport screenshot
|
|
11713
|
+
const scrollOffsetX = this.initialScrollPosition?.x ?? window.scrollX;
|
|
11714
|
+
const scrollOffsetY = this.initialScrollPosition?.y ?? window.scrollY;
|
|
11715
|
+
// Create a temporary canvas with viewport dimensions
|
|
11642
11716
|
const adjustedCanvas = document.createElement('canvas');
|
|
11643
|
-
adjustedCanvas.width =
|
|
11644
|
-
adjustedCanvas.height =
|
|
11717
|
+
adjustedCanvas.width = window.innerWidth;
|
|
11718
|
+
adjustedCanvas.height = window.innerHeight;
|
|
11645
11719
|
const adjustedCtx = adjustedCanvas.getContext('2d');
|
|
11646
11720
|
if (adjustedCtx) {
|
|
11647
|
-
//
|
|
11648
|
-
adjustedCtx.
|
|
11721
|
+
// Translate context to subtract scroll offset
|
|
11722
|
+
adjustedCtx.translate(-scrollOffsetX, -scrollOffsetY);
|
|
11723
|
+
// Draw the annotations canvas (which is in absolute page coordinates)
|
|
11724
|
+
adjustedCtx.drawImage(this.canvas, 0, 0);
|
|
11725
|
+
// Reset translation
|
|
11726
|
+
adjustedCtx.setTransform(1, 0, 0, 1, 0, 0);
|
|
11649
11727
|
// Draw the adjusted annotations on top of screenshot
|
|
11650
|
-
|
|
11728
|
+
// Scale if screenshot dimensions don't match viewport (due to device pixel ratio)
|
|
11729
|
+
const scaleX = img.width / window.innerWidth;
|
|
11730
|
+
const scaleY = img.height / window.innerHeight;
|
|
11731
|
+
if (scaleX !== 1 || scaleY !== 1) {
|
|
11732
|
+
tempCtx.save();
|
|
11733
|
+
tempCtx.scale(scaleX, scaleY);
|
|
11734
|
+
tempCtx.drawImage(adjustedCanvas, 0, 0);
|
|
11735
|
+
tempCtx.restore();
|
|
11736
|
+
} else {
|
|
11737
|
+
tempCtx.drawImage(adjustedCanvas, 0, 0);
|
|
11738
|
+
}
|
|
11651
11739
|
}
|
|
11652
11740
|
} else {
|
|
11653
|
-
//
|
|
11654
|
-
|
|
11741
|
+
// Fullpage mode - annotations and screenshot are both in absolute page coordinates
|
|
11742
|
+
// Just need to handle potential scaling due to device pixel ratio
|
|
11743
|
+
const scaleX = img.width / this.canvas.width;
|
|
11744
|
+
const scaleY = img.height / this.canvas.height;
|
|
11745
|
+
if (scaleX !== 1 || scaleY !== 1) {
|
|
11746
|
+
tempCtx.save();
|
|
11747
|
+
tempCtx.scale(scaleX, scaleY);
|
|
11748
|
+
tempCtx.drawImage(this.canvas, 0, 0);
|
|
11749
|
+
tempCtx.restore();
|
|
11750
|
+
} else {
|
|
11751
|
+
tempCtx.drawImage(this.canvas, 0, 0);
|
|
11752
|
+
}
|
|
11655
11753
|
}
|
|
11656
11754
|
resolve(tempCanvas.toDataURL('image/png'));
|
|
11657
11755
|
}
|