[Bug]: Embedded browser view renders a duplicated page and drifts over the chat pane at fractional DPI

Offen
#3,700 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Maintainer antworten meist innerhalb von 1 Tag

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Anfängerfreundlichkeit
40/100
Issue-Typ
Bug
Klarheit
Klar beschrieben
Aktivitätsstatus
Aktiv
Tech-Stack
rust, typescript

Rechercherichtung

Start in src/web-ui/src/app/scenes/browser/useEmbeddedBrowserWebview.ts, where syncWebviewBounds and the browser_webview_create and browser_webview_set_bounds calls pass CSS-pixel bounds straight to the native host, then check the presentation.isCurrent() guard and the BrowserPreview frozen-frame overlay. Reproduce by setting devicePixelRatio to 1.21 and confirming the child view rect differs from the host rect. Done when the native view matches the host rect at fractional scale and geometry keeps syncing after a lost resize event.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

Summary

At a fractional Windows display scale (121%, devicePixelRatio = 1.21), the built-in browser panel renders the page twice: a live interactive view plus a second, magnified and blurred copy to its right. The live view itself is also mispositioned — it is smaller than its host and shifted toward the window origin, so it visually covers the chat pane next to the panel.

Area

Desktop app

Reproduction or evidence

Steps:

  1. Set the Windows display scale to a fractional value (here: 121%).
  2. Open the built-in browser panel — either let the agent open any URL, or use the Browser tab.
  3. Observe that the page is drawn twice, and that the interactive copy does not sit inside the panel.

Measurements (Win32 GetWindowRect on the child view vs. the rectangle the frontend computes for the panel host):

host rect (CSS px)   1325.6,  89.8, 397.5 x 986.2
devicePixelRatio     1.21
child view before     1044,   ...,  398  x ...     <- CSS numbers applied 1:1 as device pixels
child view after      1604,   109,  481  x 1193    <- expected: rect x 1.21

So the child view was rendered at 1 / 1.21 of its host and pulled toward the window origin by the same factor. At 100% scale the bug is invisible, which is why it only shows up on scaled displays.

Root cause 1 — units. getBoundingClientRect() reports CSS pixels, while browser_webview_create / browser_webview_set_bounds hand those values straight to the native host, which applies them 1:1 without multiplying by the window scale factor. At fractional DPI the child view therefore shrinks and offsets.

src/web-ui/src/app/scenes/browser/useEmbeddedBrowserWebview.ts:

await api.invoke('browser_webview_set_bounds', {
  request: { label, x: bounds.left, y: bounds.top, width: bounds.width, height: bounds.height },
});

Root cause 2 — geometry sync can be blocked forever. syncWebviewBounds() returns early when presentationRef.current.isCurrent() is false. A panel that loses the presentation race still renders the visible native view, but it never syncs its geometry again, so the view stays at its creation-time size:

if (!isTauri || !target || !viewportRef.current || !mountedRef.current
  || (targetId && !presentationRef.current?.isCurrent())) return;

The presentation.run() wrapper used for the actual call has the same owner check, so it silently skips too.

Root cause 3 — the frozen frame shows through. BrowserPreview is a decorative JPEG of the native view pinned below it (captured every 5s, object-fit: fill, pointer-events: none). It is only supposed to be visible while a DOM overlay covers the native view. When the geometry above drifts, the stale frame is stretched across the gap and produces exactly the "second, blurred browser" appearance. Additionally the ResizeObserver that drives syncWebviewBounds only reports layout changes it actually witnessed — a panel that mounts at its final size never receives one.

Proposed fix (I have a patch and updated unit tests ready):

  1. Convert CSS → device pixels once, at the create / set_bounds boundary:
    const dpr = window.devicePixelRatio || 1;
    x: bounds.left * dpr, y: bounds.top * dpr,
    width: bounds.width * dpr, height: bounds.height * dpr,
    
  2. Do not gate geometry sync on presentation ownership, and add a periodic reconcile (e.g. 400 ms) so a lost resize event cannot leave the view at its creation size.
  3. Render the frozen frame only on the active panel, and hide inactive panel content (visibility: hidden).

Happy to open a PR with the patch if that is useful.

Environment, if relevant
OpenBitFun version: 1.0.8 (frontend revision bundled-b5489663e559ac91)
OS: Windows 11 家庭中文版, build 22631
Display: 2560x1440 @ 121% scale
Mode: Desktop app, built-in browser panel (not an external browser)
Vorherrschende Sprache
Rust
Sterne
2.3k
Forks
236
Ø Merge
13 Std. 3 Min.
Gemergte PRs (30 T.)
351

Entwicklungsumgebung

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus GCWing/OpenBitFun

Alle Issues in GCWing/OpenBitFun

Ähnliche Issues

Weitere Issues zu Rust

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.