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
/