diff --git a/package.json b/package.json index 583f34c..783835e 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@types/node": "25.5.2", "@codemirror/state": "6.5.4" }, - "ignoredBuiltDependencies": [ + "onlyBuiltDependencies": [ "chromedriver" ] }, diff --git a/packages/app/src/components/browser/snapshot.ts b/packages/app/src/components/browser/snapshot.ts index 20024e5..6bce777 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,46 @@ 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') + + // Defer to next frame so we read post-reflow dimensions on resize events. + 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; subtract host padding + // so a height-limited scale doesn't push section.width past the edge. + const padX = + parseFloat(hostStyle.paddingLeft || '0') + + parseFloat(hostStyle.paddingRight || '0') + const padY = + parseFloat(hostStyle.paddingTop || '0') + + parseFloat(hostStyle.paddingBottom || '0') + + const effectiveViewportH = viewportHeight + + 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` - 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})` + // Iframe absent in screenshot/video modes — section sizing above still runs. + if (this.iframe) { + this.iframe.style.width = `${viewportWidth}px` + this.iframe.style.height = `${viewportHeight}px` + this.iframe.style.transformOrigin = '0 0' + this.iframe.style.transform = `scale(${scale})` + } + }) } #handleShowCommand = (event: Event) => @@ -320,6 +342,11 @@ export class DevtoolsBrowser extends Element { this.requestUpdate() } + // View-mode flips swap the iframe with /