From e412f5faf71820da97d6691f7fab96cc4e08d9fc Mon Sep 17 00:00:00 2001 From: Alessandro <155005371+3clyp50@users.noreply.github.com> Date: Mon, 27 Apr 2026 17:35:50 +0200 Subject: [PATCH] Fix browser canvas startup viewport settle Wait for the right-canvas browser surface to finish its opening transition before using its dimensions as the Playwright viewport. Measure raw stage dimensions for stability, then apply the existing clamped viewport values so initial screencasts do not render into a stretched canvas. Add a browser regression guard for the raw viewport settle path. --- plugins/_browser/webui/browser-store.js | 36 +++++++++++++++++++------ tests/test_browser_agent_regressions.py | 13 +++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/plugins/_browser/webui/browser-store.js b/plugins/_browser/webui/browser-store.js index 1bc5bb337..a848a4313 100644 --- a/plugins/_browser/webui/browser-store.js +++ b/plugins/_browser/webui/browser-store.js @@ -13,6 +13,8 @@ const BROWSER_FIRST_INSTALL_TIMEOUT_MS = 300000; const BROWSER_CONFIG_REFRESH_MS = 15000; const VIEWPORT_SYNC_DEBOUNCE_MS = 220; const VIEWPORT_SYNC_SIZE_TOLERANCE = 4; +const CANVAS_VIEWPORT_SETTLE_MS = 260; +const SURFACE_VIEWPORT_STABLE_FRAMES = 2; const ANNOTATION_DRAG_THRESHOLD = 6; const ANNOTATION_MAX_COMMENTS = 24; const ANNOTATION_DOM_LIMIT = 1200; @@ -91,6 +93,7 @@ const model = { _mode: "", _surfaceMounted: false, _surfaceSwitching: false, + _surfaceOpenedAt: 0, _connectSequence: 0, _viewerToken: "", extensionMenuOpen: false, @@ -403,6 +406,7 @@ const model = { const targetBrowserId = requestedBrowserId || this.activeBrowserId || this.firstBrowserId(); this._mode = nextMode; this._surfaceMounted = true; + this._surfaceOpenedAt = Date.now(); this._lastViewportKey = ""; if (!modeChanged && (this.frameSrc || !targetBrowserId)) return; @@ -442,12 +446,17 @@ const model = { let stableCount = 0; for (let index = 0; index < 24; index += 1) { await nextAnimationFrame(); - const viewport = this.currentViewportSize(); + const viewport = this.surfaceViewportMeasurement(); if (!viewport) continue; - const key = `${viewport.width}x${viewport.height}`; + const key = `${viewport.rawWidth}x${viewport.rawHeight}`; if (key === lastKey) { stableCount += 1; - if (stableCount >= 2) return viewport; + const canvasSettled = this._mode !== "canvas" + || !this._surfaceOpenedAt + || Date.now() - this._surfaceOpenedAt >= CANVAS_VIEWPORT_SETTLE_MS; + if (canvasSettled && stableCount >= SURFACE_VIEWPORT_STABLE_FRAMES) { + return { width: viewport.width, height: viewport.height }; + } } else { stableCount = 0; lastKey = key; @@ -1286,15 +1295,26 @@ const model = { }, currentViewportSize() { + const measurement = this.surfaceViewportMeasurement(); + if (!measurement) return null; + return { + width: measurement.width, + height: measurement.height, + }; + }, + + surfaceViewportMeasurement() { const stage = this._stageElement; if (!stage) return null; const rect = stage.getBoundingClientRect?.(); - const width = Math.round(rect?.width || stage.clientWidth || 0); - const height = Math.round(rect?.height || stage.clientHeight || 0); - if (width < 80 || height < 80) return null; + const rawWidth = Math.round(rect?.width || stage.clientWidth || 0); + const rawHeight = Math.round(rect?.height || stage.clientHeight || 0); + if (rawWidth < 80 || rawHeight < 80) return null; return { - width: Math.max(320, width), - height: Math.max(200, height), + rawWidth, + rawHeight, + width: Math.max(320, rawWidth), + height: Math.max(200, rawHeight), }; }, diff --git a/tests/test_browser_agent_regressions.py b/tests/test_browser_agent_regressions.py index 9e4c40c8e..fea453201 100644 --- a/tests/test_browser_agent_regressions.py +++ b/tests/test_browser_agent_regressions.py @@ -347,6 +347,19 @@ def test_browser_viewer_allows_slow_extension_startup(): assert "Installing Chromium for the first Browser run" in js +def test_browser_canvas_startup_waits_for_raw_viewport_settle(): + js = (PROJECT_ROOT / "plugins" / "_browser" / "webui" / "browser-store.js").read_text( + encoding="utf-8" + ) + + assert "const CANVAS_VIEWPORT_SETTLE_MS = 260;" in js + assert "surfaceViewportMeasurement()" in js + assert "rawWidth" in js + assert "rawHeight" in js + assert "const key = `${viewport.rawWidth}x${viewport.rawHeight}`;" in js + assert "Date.now() - this._surfaceOpenedAt >= CANVAS_VIEWPORT_SETTLE_MS" in js + + def test_browser_ui_spinners_have_browser_local_animation(): main_html = (PROJECT_ROOT / "plugins" / "_browser" / "webui" / "browser-panel.html").read_text( encoding="utf-8"