Capstone project. I'm ngl it's vibe-coded and it's only here so I can mess around with it
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #7 from chriskalos/fix/heatmap-screenshot

Fix screenshot mechanism

authored by

Chris and committed by
GitHub
5cced208 a4ac1a35

+92 -17
+92 -17
js/iframeBridge.js
··· 23 23 }; 24 24 } 25 25 26 + function withTimeout(promise, ms) { 27 + return Promise.race([ 28 + promise, 29 + new Promise((resolve) => window.setTimeout(resolve, ms)) 30 + ]); 31 + } 32 + 33 + async function waitForDocumentFonts(doc) { 34 + if (!doc?.fonts?.ready) { 35 + return; 36 + } 37 + 38 + try { 39 + await withTimeout(doc.fonts.ready, 1200); 40 + } catch (error) { 41 + console.warn('[IframeBridge] Font readiness check failed:', error); 42 + } 43 + } 44 + 45 + async function waitForImages(doc) { 46 + const images = Array.from(doc.images || []); 47 + const restoreFns = []; 48 + 49 + images.forEach((image) => { 50 + if (image.loading === 'lazy') { 51 + const previousLoading = image.loading; 52 + image.loading = 'eager'; 53 + restoreFns.push(() => { 54 + image.loading = previousLoading; 55 + }); 56 + } 57 + }); 58 + 59 + try { 60 + await withTimeout(Promise.all(images.map((image) => { 61 + if (image.complete && image.naturalWidth > 0) { 62 + return Promise.resolve(); 63 + } 64 + if (image.decode) { 65 + return image.decode().catch(() => undefined); 66 + } 67 + return new Promise((resolve) => { 68 + image.addEventListener('load', resolve, { once: true }); 69 + image.addEventListener('error', resolve, { once: true }); 70 + }); 71 + })), 1500); 72 + } finally { 73 + restoreFns.forEach((restore) => restore()); 74 + } 75 + } 76 + 26 77 export class IframeBridge { 27 78 constructor() { 28 79 this.iframe = null; ··· 205 256 const doc = this.iframeDocument; 206 257 const docEl = doc.documentElement; 207 258 const body = doc.body; 208 - const width = Math.max(docEl.scrollWidth, body?.scrollWidth || 0, docEl.clientWidth); 209 - const height = Math.max(docEl.scrollHeight, body?.scrollHeight || 0, docEl.clientHeight); 210 - const previousOverflow = docEl.style.overflow; 211 - const previousBodyOverflow = body ? body.style.overflow : ''; 259 + const win = this.iframeWindow; 260 + const viewportWidth = Math.round(win.innerWidth || docEl.clientWidth || this.iframe.clientWidth || 0); 261 + const viewportHeight = Math.round(win.innerHeight || docEl.clientHeight || this.iframe.clientHeight || 0); 262 + const width = Math.ceil(Math.max( 263 + docEl.scrollWidth, 264 + body?.scrollWidth || 0, 265 + docEl.offsetWidth, 266 + body?.offsetWidth || 0, 267 + viewportWidth 268 + )); 269 + const height = Math.ceil(Math.max( 270 + docEl.scrollHeight, 271 + body?.scrollHeight || 0, 272 + docEl.offsetHeight, 273 + body?.offsetHeight || 0, 274 + viewportHeight 275 + )); 276 + const currentScrollX = Math.round(win.scrollX || docEl.scrollLeft || body?.scrollLeft || 0); 277 + const currentScrollY = Math.round(win.scrollY || docEl.scrollTop || body?.scrollTop || 0); 212 278 213 279 try { 214 - docEl.style.overflow = 'visible'; 215 - if (body) { 216 - body.style.overflow = 'visible'; 217 - } 280 + await waitForDocumentFonts(doc); 281 + await waitForImages(doc); 218 282 219 - const canvas = await html2canvas(doc.body || docEl, { 220 - backgroundColor: '#ffffff', 283 + // Keep CSS viewport units based on the actual iframe viewport while rendering the full document. 284 + const canvas = await html2canvas(docEl, { 285 + backgroundColor: this.iframeWindow.getComputedStyle(body || docEl).backgroundColor || '#ffffff', 221 286 useCORS: true, 287 + allowTaint: false, 222 288 logging: false, 223 289 scale: 1, 224 290 width, 225 291 height, 226 - windowWidth: width, 227 - windowHeight: height, 292 + windowWidth: viewportWidth || width, 293 + windowHeight: viewportHeight || height, 228 294 x: 0, 229 295 y: 0, 230 296 scrollX: 0, 231 - scrollY: 0 297 + scrollY: 0, 298 + onclone: (clonedDoc) => { 299 + const clonedDocEl = clonedDoc.documentElement; 300 + const clonedBody = clonedDoc.body; 301 + clonedDocEl.style.width = `${width}px`; 302 + clonedDocEl.style.minWidth = `${width}px`; 303 + clonedDocEl.style.minHeight = `${height}px`; 304 + if (clonedBody) { 305 + clonedBody.style.width = `${width}px`; 306 + clonedBody.style.minWidth = `${width}px`; 307 + clonedBody.style.minHeight = `${height}px`; 308 + } 309 + } 232 310 }); 233 311 234 312 return { ··· 238 316 capturedAt: Date.now() 239 317 }; 240 318 } finally { 241 - docEl.style.overflow = previousOverflow; 242 - if (body) { 243 - body.style.overflow = previousBodyOverflow; 244 - } 319 + win.scrollTo(currentScrollX, currentScrollY); 245 320 } 246 321 } 247 322