…ixes omnigent-ai#3980)
The embedded Browser tab's native WebContentsView paints above ALL renderer
DOM — no z-index or portal can put a modal over it. The existing fix (PR
omnigent-ai#3491) introduced the right mechanism (setOverlaySuppressed in the view
registry + useSuppressBrowserView hook) but wired it only to the Share dialog.
This commit takes the approach recommended in the issue: drive suppression
from the shared DialogOverlay component so every Radix dialog gets it for
free, rather than adding one useSuppressBrowserView(xOpen) per dialog.
Changes:
- Add useSuppressBrowserView hook (ref-counted, no-op outside Electron)
- Wire it to DialogOverlay, covering all ~20 dialog surfaces at once
- Add setOverlaySuppressed to browserViewRegistry (hide/restore in place)
- Add IPC handler + preload bridge for the new method
- Add full test coverage for hook and registry
Closes omnigent-ai#3980
Problem
The embedded Browser tab's native WebContentsView paints above ALL renderer DOM — no z-index or portal can put a modal over it. Opening a fork dialog, command palette, agent info, keyboard shortcuts, or any other overlay while the Browser tab is active renders the dialog underneath the browser page. You can hear yourself clicking a dialog you cannot see.
Background
PR #3491 introduced the right mechanism (
setOverlaySuppressedin the view registry +useSuppressBrowserViewhook) but wired it only to the Share dialog. The fork dialog, command palette, agent info, keyboard shortcuts, and ~20 other overlay surfaces all remained unwired.Approach
Rather than adding one
useSuppressBrowserView(xOpen)per dialog (which is fragile and easy to miss on new dialogs), this PR drives suppression from the sharedDialogOverlaycomponent. Every Radix dialog uses this component, so all dialogs get the fix for free.Non-dialog overlays (lightbox, toasts) that don't use the Radix Dialog component remain unwired — they can be handled case by case in follow-up PRs.
Changes
web/src/hooks/useSuppressBrowserView.ts(new) — Ref-counted hook; hides the native view while any overlay is open, restores it only when the last overlay closes. No-op outside Electron.web/src/components/ui/dialog.tsx— WireuseSuppressBrowserView(true)toDialogOverlay. Since it mounts only when the dialog is open, mount → suppress, unmount → restore.web/electron/src/browserViewRegistry.js— AddsetOverlaySuppressedmethod. Hides the active view in place (not detached, so the page keeps running) viaview.setVisible(). Flag outlives view swaps so a suppressed state persists across tab switches.web/electron/src/browserIpc.js— Registeromnigent:browser-set-overlay-suppressedIPC handler.web/electron/src/preload.js— ExposebrowserSetOverlaySuppressedon the context bridge.web/electron/test/browserViewRegistry.test.js— 6 new tests covering hide/restore, suppressed-state inheritance, swaps, and introspection.web/src/hooks/useSuppressBrowserView.test.tsx(new) — 5 new tests covering open/close, unmount, no-op, ref-counting, and fallback shells.Closes #3980