fix(replay): derive device space from screen extent, not first node

the first positive-bounds element is often a short status-bar node
(320x24 on android); using it gave a 320/24 aspect ratio that squashed
the screenshot overlay into a grey horizontal band. use the max extent
across elements (like the runner's screenBounds) instead.
This commit is contained in:
pj committed 2026-07-13 14:14:32 +05:30
1 parent e6b9f6db3c
commit 5dd4230251
2 files changed
+27 -8

No files matched your search

@@ -44,4 +44,18 @@ describe("deviceSpaceOf", () => {
};
expect(deviceSpaceOf(hierarchy)).toEqual({ width: 402, height: 874 });
});
it("uses the screen extent, not a short status-bar node listed first", () => {
// Regression: a 320x24 status bar precedes the 320x640 screen. Picking the
// first positive-bounds element gave a 320/24 aspect ratio, squashing the
// screenshot overlay into a grey horizontal band.
const hierarchy: Hierarchy = {
elements: [
{ bounds: { left: 0, top: 0, right: 320, bottom: 24 } },
{ bounds: { left: 0, top: 0, right: 320, bottom: 640 } },
{ bounds: { left: 20, top: 271, right: 286, bottom: 319 } },
],
};
expect(deviceSpaceOf(hierarchy)).toEqual({ width: 320, height: 640 });
});
});
+13 -8
View File
@@ -3,18 +3,23 @@ import type { Hierarchy } from "../types";
// Tap points and resolved bounds in the trace share the hierarchy root's
// coordinate space (iOS points, Android pixels, web CSS px). Screenshots may
// be scaled (iOS 3x, web DPR>1), so the overlay viewBox must come from the
// root bounds, not the image's natural pixel size. Elements are in pre-order;
// the first one with positive extent is the root window (iOS prepends a
// synthetic zero-bounds node, so plain elements[0] is not enough).
// device bounds, not the image's natural pixel size.
//
// Use the maximum extent across all elements (mirroring the runner's
// screenBounds), NOT the first positive-bounds element: the first element is
// often a status-bar/decor node a few px tall (e.g. 320x24 on Android), which
// would give a wildly wrong aspect ratio and squash the overlay into a band.
export function deviceSpaceOf(
hierarchy?: Hierarchy,
): { width: number; height: number } | undefined {
let width = 0;
let height = 0;
for (const element of hierarchy?.elements ?? []) {
const width = element.bounds.right;
const height = element.bounds.bottom;
if (width > 0 && height > 0) {
return { width, height };
}
if (element.bounds.right > width) width = element.bounds.right;
if (element.bounds.bottom > height) height = element.bounds.bottom;
}
if (width > 0 && height > 0) {
return { width, height };
}
return undefined;
}