Skip to content

fix(browser): 全屏模态打开时让原生浏览器视图让位,避免弹窗被浏览器遮挡 - #2007

Open
Jacky-li-li-li wants to merge 1 commit into
proma-ai:mainfrom
Jacky-li-li-li:fix/browser-cover-modal-dialog
Open

fix(browser): 全屏模态打开时让原生浏览器视图让位,避免弹窗被浏览器遮挡#2007
Jacky-li-li-li wants to merge 1 commit into
proma-ai:mainfrom
Jacky-li-li-li:fix/browser-cover-modal-dialog

Conversation

@Jacky-li-li-li

Copy link
Copy Markdown

概述

修复受管浏览器打开时,居中全屏模态被右侧浏览器原生视图压住的问题。受影响场景包括:删除项目/会话/对话的确认弹窗、点击图片的预览(含图片编辑)、以及所有基于 Radix Dialog/AlertDialog 的模态弹窗——与浏览器区域相交的部分不可见且无法点击(点击会落入网页)。

复现步骤

  1. 打开一个 Agent 会话,在右侧工作区打开受管浏览器并正常浏览网页;
  2. 任一操作:
    • 在左侧栏对任一项目或会话执行删除,弹出居中确认框;
    • 点击聊天或会话消息中的图片,打开图片预览 / 图片编辑;
    • 打开设置等其它模态弹窗;
  3. 弹窗与浏览器区域相交——相交部分(通常含「删除」按钮或图片主体)被浏览器原生视图遮挡,看不到也点不到。

根因

受管浏览器是 Electron 原生 WebContentsView,作为原生子视图天然盖在 renderer DOM 之上,CSS z-index 无法反转BrowserSlot.tsx 现有注释亦承认此约束)。当前 mainBrowserSlot 只按自身尺寸与 Tab 生命周期控制可见性、不为任何应用浮层让位(此前的“任意浮层出现即隐藏”方案曾造成白屏与闪烁而被回退),因此与浏览器区域相交的居中弹窗必然被压住。

方案

把“让位”严格限定在全屏模态,且采用无状态 DOM 观测而不是挂载计数:

  • BrowserSlot 每 200ms 检测 DOM 中是否存在已挂载的模态内容(role="dialog" / role="alertdialog")。Radix 的 Dialog/AlertDialog 内容随开关挂载/卸载,因此「存在性」就是可靠信号;这也天然覆盖绕过 ui/dialog.tsx 包装、直接使用 Radix 原语的组件(图片预览 ImageLightbox、FAQ/快捷键弹窗等);
  • 模态出现 → 立即发布 visible=false 让位,并带 preserveSessionOnHide 防止隐藏期间被后台回收;模态消失 → 携带新 revision 恢复视图;
  • 判定不依赖 +1/-1 计数,不会因 HMR / StrictMode / 页面整载产生计数漂移而把浏览器永久留在隐藏态(该问题在基于计数的早期实现中实测出现过:计数卡在 10 但无任何弹窗,浏览器被永久隐藏);轮询同时是自愈机制,任何被中断的状态翻转都会在下一个 tick 校正;
  • Popover / Dropdown / Toast 等局部浮层触发隐藏,维持现状,避免白屏与闪烁。

改动文件

  • apps/electron/src/renderer/components/browser/BrowserSlot.tsx(+34 / -3)

测试方法

  1. bun run typecheck(apps/electron)通过;
  2. bun run build:renderer 通过(仅项目既有 chunk 警告);
  3. dev 人工冒烟(macOS arm64):
    • 浏览器正常浏览时,删除项目/会话的确认弹窗完整显示、按钮可正常点击(见截图 1);
    • 点击聊天/会话中的图片,图片预览完整显示、不被浏览器遮挡(见截图 2),关闭后浏览器恢复;
    • 各模态关闭后浏览器视图即时恢复,无白屏、无残留;
    • 长时间操作与页面热更新后浏览器仍稳定显示,不再出现“永久空白”。

截图(修复前后对比)

截图 1 — 删除确认弹窗:修改前右侧被浏览器原生视图压住、「删除」按钮不可见不可点;修改后弹窗完整、关闭后浏览器恢复:

before-delete
after-delete

截图 2 — 图片预览:修改前点击图片后预览同样被浏览器 tab 遮挡(图中央为浏览器页面叠在预览之上);修改后预览完整显示:

before-lightbox

备注

Made with Proma · GitHub

受管浏览器基于 Electron 原生 WebContentsView,它盖在 renderer DOM 之上且无法被
CSS z-index 反转。此前浏览器打开时,居中全屏模态(删除项目/会话确认框、
Dialog 等)与浏览器区域相交的部分会被原生视图压住:内容不可见,点击也会落入
网页,必须让原生视图在模态打开期间让位。

BrowserSlot 采用无状态轮询观测 DOM 中是否存在已挂载的模态内容
(role=dialog / role=alertdialog,Radix Dialog 随开关挂载/卸载),模态出现即
发布 visible=false 让位(保留 session、防后台回收),模态消失即恢复。判定以
DOM 实际状态为准,不依赖挂载计数,不会因 HMR / StrictMode / 页面整载导致计数
漂移而把浏览器永久留在隐藏态;轮询同时承担自愈,任何被中断的状态翻转都会在
下一个 200ms tick 校正。Popover / Dropdown / Toast 等局部浮层仍不隐藏浏览器,
维持现状避免白屏与闪烁。

Typecheck 与 renderer 构建通过;dev 冒烟:浏览器打开时删除弹窗完整可见可点,
取消后浏览器即时恢复且不再出现空白。

Made-with: Proma
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant