From f2a1c068a13da19338f6c096b129b79b6a1647e4 Mon Sep 17 00:00:00 2001 From: Vishnu Vardhan Date: Tue, 19 May 2026 15:42:33 +0530 Subject: [PATCH 1/8] [BUG-195] - Regex fix --- .../app/src/components/browser/snapshot.ts | 104 +++++++++++--- packages/backend/src/index.ts | 16 ++- packages/backend/src/runner.ts | 36 ++++- packages/service/src/constants.ts | 16 +-- packages/service/src/index.ts | 83 +++++++++-- packages/service/src/launcher.ts | 136 ++++++++++++++++++ packages/service/src/reporter.ts | 10 +- packages/service/src/session.ts | 92 ++++++++++++ packages/service/src/types.ts | 4 + packages/service/src/utils.ts | 23 +++ 10 files changed, 477 insertions(+), 43 deletions(-) diff --git a/packages/app/src/components/browser/snapshot.ts b/packages/app/src/components/browser/snapshot.ts index 20024e52..d74e0f25 100644 --- a/packages/app/src/components/browser/snapshot.ts +++ b/packages/app/src/components/browser/snapshot.ts @@ -133,10 +133,11 @@ export class DevtoolsBrowser extends Element { iframe { background-color: white; - flex: 1; + position: absolute; + top: 0; + left: 0; border: none; border-radius: 0 0 0.5rem 0.5rem; - min-height: 0; } .screenshot-overlay { @@ -169,6 +170,7 @@ export class DevtoolsBrowser extends Element { position: relative; flex: 1; min-height: 0; + overflow: hidden; display: flex; flex-direction: column; } @@ -249,7 +251,7 @@ export class DevtoolsBrowser extends Element { #setIframeSize() { const metadata = this.metadata - if (!this.section || !this.iframe || !this.header || !metadata) { + if (!this.section || !this.header || !metadata) { return } @@ -262,26 +264,72 @@ export class DevtoolsBrowser extends Element { return } - this.iframe.removeAttribute('style') - const frameSize = this.getBoundingClientRect() - const headerSize = this.header.getBoundingClientRect() - - let scale = frameSize.width / viewportWidth - if (scale > 0.85) { - /** - * Make sure we stop scaling at 85% of the viewport width to ensure - * we keep the aspect ratio. We substract 0.05 to have a bit of a - * padding - */ - scale = frameSize.height / viewportHeight - 0.05 - } + this.iframe?.removeAttribute('style') - this.section.style.width = `${viewportWidth * scale}px` - this.section.style.height = `${Math.min(frameSize.height, viewportHeight * scale)}px` - this.iframe.style.width = `${viewportWidth}px` - // this.iframe.style.height = `${(Math.min(frameSize.height, viewportHeight * scale) - headerSize.height)}px` - this.iframe.style.height = `${viewportHeight - headerSize.height / scale}px` - this.iframe.style.transform = `scale(${scale})` + // Defer the measurement to the next frame so we read post-reflow + // dimensions when the surrounding panel has just resized (e.g. Chrome + // DevTools opening/closing). Reading on the same tick as the resize + // event can return the pre-reflow size. + requestAnimationFrame(() => { + if (!this.section || !this.header) { + return + } + const frameSize = this.getBoundingClientRect() + const headerSize = this.header.getBoundingClientRect() + const hostStyle = getComputedStyle(this) + + // getBoundingClientRect returns the padding-box; the section lives + // inside the content box, so the host's padding eats into what the + // section can actually claim. Subtract it explicitly — otherwise a + // height-limited scale can push section.width past the visible edge. + const padX = + parseFloat(hostStyle.paddingLeft || '0') + + parseFloat(hostStyle.paddingRight || '0') + const padY = + parseFloat(hostStyle.paddingTop || '0') + + parseFloat(hostStyle.paddingBottom || '0') + + // Use the recorded viewport as authoritative — the iframe renders at + // that size and the section mirrors it. We tried fitting to the + // measured contentDocument scrollHeight to remove dead space below + // short pages, but the measurement races with async CSS loading: + // sites whose stylesheets arrive late report a near-zero scrollHeight + // at first paint, which made the section collapse to a strip even + // for full-height pages like github.com/login. Easier and more + // reliable to mirror what a real browser shows at the recorded size. + const effectiveViewportH = viewportHeight + + // Fit the (effective) viewport into the area beneath the mock browser's + // header, preserving aspect ratio. min(scaleW, scaleH) is the standard + // "contain" sizing — guarantees no overflow on either axis whether the + // container is wider or taller than the viewport. + const availW = Math.max(0, frameSize.width - padX) + const availH = Math.max(0, frameSize.height - padY - headerSize.height) + const scale = Math.max( + 0, + Math.min(availW / viewportWidth, availH / effectiveViewportH) + ) + + this.section.style.width = `${viewportWidth * scale}px` + this.section.style.height = `${effectiveViewportH * scale + headerSize.height}px` + + // The iframe only exists in mutation-replay view — in screenshot/video + // modes the section is filled by an or