fix(window): 修复 macOS 全屏应用下呼出窗口一闪而过并落到桌面的问题#577
Conversation
- macOS 下 forceActivateWindow 改为先 app.focus({steal:true}) 激活到当前 Space 再 show,避免 dock 隐藏应用的面板落到桌面 Space
- 新增 suppressBlurHideTransiently,呼出瞬间短暂抑制 blur,防止抢焦点时被瞬时 blur 触发 hideWindow 造成一闪而过(多次调用取最长时长)
- openPluginInstaller 的 macOS 分支同步相同的激活顺序
- 补充 windowManagerMacActivation 单元测试
There was a problem hiding this comment.
Code Review
This pull request refactors the window activation sequence on macOS to prevent the window from flashing or disappearing. It introduces a transient blur suppression mechanism (suppressBlurHideTransiently) to handle overlapping suppressions and adds a test suite to verify macOS activation behavior. The reviewer identified a potential race condition where suppressBlurHideTransiently directly modifies the shared this.suppressBlurHide flag, which can conflict with other long-running suppressions. They suggested decoupling transient suppression from the main flag and checking the timer state directly in isBlurHideSuppressed() to avoid conflicts.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| private suppressBlurHideTransiently(durationMs: number): void { | ||
| this.suppressBlurHide = true | ||
| const until = Date.now() + durationMs | ||
| if (this.transientBlurSuppressTimer && this.transientBlurSuppressUntil >= until) { | ||
| return | ||
| } | ||
| if (this.transientBlurSuppressTimer) { | ||
| clearTimeout(this.transientBlurSuppressTimer) | ||
| } | ||
| this.transientBlurSuppressUntil = until | ||
| this.transientBlurSuppressTimer = setTimeout(() => { | ||
| this.suppressBlurHide = false | ||
| this.transientBlurSuppressTimer = null | ||
| this.transientBlurSuppressUntil = 0 | ||
| }, durationMs) | ||
| } |
There was a problem hiding this comment.
当前实现中,suppressBlurHideTransiently 直接修改了 this.suppressBlurHide 共享变量。这会与 openPluginInstaller 中手动设置的 this.suppressBlurHide = true 产生冲突和竞态(例如,瞬时抑制定时器触发时会错误地将 this.suppressBlurHide 置为 false,从而提前释放了 openPluginInstaller 的长时抑制)。
为了彻底解决这个问题,建议将瞬时抑制与长时抑制解耦:
- 在
suppressBlurHideTransiently中不再修改this.suppressBlurHide。 - 修改
isBlurHideSuppressed()方法(在上方第 162 行),将!!this.transientBlurSuppressTimer纳入判断:private isBlurHideSuppressed(): boolean { return this.suppressBlurHide || this.modalDialogBlurHideSuppressed || !!this.transientBlurSuppressTimer }
这样不仅能避免竞态,还能让 openPluginInstaller 直接复用 forceActivateWindow(),消除重复代码。
private suppressBlurHideTransiently(durationMs: number): void {
const until = Date.now() + durationMs
if (this.transientBlurSuppressTimer && this.transientBlurSuppressUntil >= until) {
return
}
if (this.transientBlurSuppressTimer) {
clearTimeout(this.transientBlurSuppressTimer)
}
this.transientBlurSuppressUntil = until
this.transientBlurSuppressTimer = setTimeout(() => {
this.transientBlurSuppressTimer = null
this.transientBlurSuppressUntil = 0
}, durationMs)
}
问题
macOS 下(尤其前台应用处于原生全屏时)通过全局快捷键呼出主窗口,偶发「一闪而过」或窗口掉到桌面 Space,需要手动切到桌面才能看到。
原因
ZTools 在 macOS 下
app.dock.hide()使其成为 accessory 应用,面板(type: 'panel')依靠visibleOnFullScreen: true才能覆盖全屏应用。两个叠加问题:forceActivateWindow在 macOS 下先show()再app.focus({ steal: true }),dock 隐藏的应用会把窗口显示到「家 Space」(桌面),而非当前 Space。hideWindow()。从全屏应用抢焦点瞬间系统会补发一次瞬时 blur,直接触发隐藏,造成「一闪而过」。双击唤醒路径已有suppressBlurHide规避,但全局快捷键的showWindow()路径没有。改动
forceActivateWindow调整为先app.focus({ steal: true })激活到当前 Space 再show(),避免面板落到桌面 Space。suppressBlurHideTransiently(durationMs):呼出/激活瞬间短暂抑制 blur(默认 200ms),防止抢焦点时被瞬时 blur 触发hideWindow;多次调用取最长时长,避免叠加(如双击唤醒 350ms + 普通呼出 200ms)时被提前释放。原双击唤醒的内联抑制逻辑复用此 helper。openPluginInstaller的 macOS 分支同步相同的激活顺序(该路径有自己的长时 blur 抑制,未直接复用forceActivateWindow以免瞬时抑制与外层 500ms 手动释放冲突)。tests/main/windowManagerMacActivation.test.ts单元测试。验证
pnpm typecheck:node通过tests/main/windowManagerMacActivation.test.ts单测通过