Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/adapt-the-player-pane-to-the-window.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@wdio/devtools-app": patch
---

Adapt the player pane to the window it is actually in. The pane's height came from a pixel number resolved once, at construction, from whatever window happened to be open then, and nothing recomputed it: measured at 124px in a 1280x720 window and still 124px at 2560x1440, so a trace rendered into a 13px-wide box on a 2560px screen. Not mobile-specific — wrong for every trace, just least visible on a desktop one.

Three separate things froze it, and all three had to go. `MIN_WORKBENCH_HEIGHT` was `Math.min(300, window.innerHeight * 0.3)` evaluated at module import, so it took the window open at page load and — being the pane's own `minPosition` — pinned the pane there for the life of the page; loaded in a 413px-tall window it is exactly the 124px measured. `DragController.initialPosition` took a number rather than the getter its bounds already accepted, so a window-derived default could never follow the window. And each controller registered its resize handling by assigning `window.onresize`, which is a single slot: with five controllers on the page only the last one constructed ever adjusted, and it clobbered anything else on that slot.

A height the user dragged still wins. It is stored, and a resize only re-clamps it — so it survives a window that still has room for it and is pulled back inside one that no longer does, rather than leaving the drag handle off-screen.

The player component also re-fitted only on `resize` and `window-drag`, which meant it depended on whoever changed the layout remembering to announce it — and the dock divider, the sidebar collapsing and browser zoom announce nothing. It now watches its own box with a `ResizeObserver`, which covers all of them.
9 changes: 9 additions & 0 deletions packages/app/src/components/browser/snapshot-styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ export const snapshotStyles = css`
background: ${unsafeCSS(BROWSER_BACKDROP_GRADIENT)};
}

/* A device frame owns its column, which already provides the backdrop and the
gap, so the host's own 1.25rem is 40px of height and width spent on
nothing. It is spent OUTSIDE the box deviceFrameSize measures, so the frame
came out 40px short on each axis — and because a portrait frame's width
follows its height, the lost height cost width a second time. */
:host([device-frame]) {
padding: 0.25rem !important;
}

section {
box-sizing: border-box;
width: calc(100% - 0px); /* host padding already applied */
Expand Down
23 changes: 20 additions & 3 deletions packages/app/src/components/browser/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,21 @@ export class DevtoolsBrowser extends Element {
@query('section')
section?: HTMLElement

/**
* Watches the player's OWN box rather than the window. Re-fitting used to
* hang off `resize` and `window-drag`, so it depended on whoever changed the
* layout announcing it — and the dock divider, the sidebar collapsing and
* browser zoom announce nothing. Every one of those moves this box.
*/
#boxObserver?: ResizeObserver

/** The window events the player handles while connected, as one table so its
* registration and its teardown cannot drift. Every handler is a per-instance
* arrow field, so the reference removeEventListener gets is the one that was
* added — a bound method would produce a new function per call and never
* detach. */
* detach. Sizing is not among them: that is the ResizeObserver's job. */
#windowListeners(): ReadonlyArray<readonly [string, EventListener]> {
return [
['resize', this.#handleResize],
['window-drag', this.#handleResize],
['app-mutation-highlight', this.#highlightMutation],
['app-mutation-select', this.#handleMutationSelect],
['a11y-highlight', this.#highlightBySelector],
Expand All @@ -157,6 +163,11 @@ export class DevtoolsBrowser extends Element {
for (const [type, handler] of this.#windowListeners()) {
window.addEventListener(type, handler)
}
// Safe against the observer loop: this watches the host, and the sizing it
// triggers writes to a descendant. The host is laid out by its parent
// (width/height 100%), so nothing it writes can feed back into this box.
this.#boxObserver = new ResizeObserver(() => this.#handleResize())
this.#boxObserver.observe(this)
await this.updateComplete
}

Expand All @@ -169,6 +180,8 @@ export class DevtoolsBrowser extends Element {
for (const [type, handler] of this.#windowListeners()) {
window.removeEventListener(type, handler)
}
this.#boxObserver?.disconnect()
this.#boxObserver = undefined
}

#captureShape?: { screenshot: string; size: ImageSize | null }
Expand Down Expand Up @@ -470,6 +483,10 @@ export class DevtoolsBrowser extends Element {

// View-mode flips swap the iframe with <img>/<video> and don't fire resize.
updated() {
// Drives the host-padding rule above. An attribute rather than a style:
// the padding is `!important` in the component's own sheet, so only
// another rule in that sheet can win.
this.toggleAttribute('device-frame', Boolean(this.metadata?.device))
this.#setIframeSize()
}

Expand Down
Loading