diff --git a/apps/desktop/README.i18n.yaml b/apps/desktop/README.i18n.yaml index bda88b0fc6..3a1e4f1628 100644 --- a/apps/desktop/README.i18n.yaml +++ b/apps/desktop/README.i18n.yaml @@ -57,8 +57,8 @@ en: 9b90a2651a6d2b52 zh: 86d4d9c4358aad80 /deepseek-harness-desktop/updates: - en: 87db37529b51da62 - zh: 4fc0d752e437e7d6 + en: ea11d5f8891972b6 + zh: 8aca22fbfb771a53 /deepseek-harness-desktop/updates/local-desktop-settings: en: 2e57457c44c85b5f zh: 5b8510a3dc4c6c9d diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 45c94d62c7..e1a295c457 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -271,7 +271,7 @@ On Windows, the downloaded-update confirmation explains that the application clo On macOS, installation preparation waits for the product window to leave fullscreen before displaying the restart confirmation. Cancelling leaves the window in normal mode; destroying it during the native transition cancels installation before task teardown. If macOS does not report completion within five seconds, preparation fails without stopping the Host; the localized error asks the user to exit fullscreen and retry. Installation failure recovery uses the normal window. -Native update overlays wait for a ready document and a visible parent, and reappear when that parent is shown again. Closing an overlay releases its input interception and parent listeners. The [local window qualification](tests/README.md#verification-overlay) exercises these transitions without starting a workspace. +Native update overlays wait for a ready document and a visible parent, and reappear when that parent is shown again. Already visible overlays are not shown again on parent visibility notifications; overlays loading or reappearing behind another application do not activate the application. Closing an overlay releases its input interception and parent listeners. The [local window qualification](tests/README.md#verification-overlay) exercises these transitions without starting a workspace. Packaged applications check fixed Nightly asynchronously at startup. Ordinary polling uses a ten-minute base interval with independently sampled ±20% jitter. Each check failure doubles the base delay up to one hour; success resets it. The randomized delay is bounded by that cap and starts after all joined callers settle. Foreground and system-resume checks respect the same monotonic deadline; the localized Check for Updates menu item, including in the Windows caption's Application menu, runs immediately and joins an in-flight check. A newly received mandatory policy also requests an immediate feed check. Automatic checks never open dialogs or download packages. Manual checks display checking, failure, or no-update feedback with the installed version. Ordinary update dialogs fade in and out in place; consecutive prompts replace the card content and reset its scroll position while retaining the dark translucent backdrop without blurring the parent page. On Windows, dismissal hides the modal before destroying it so the parent is enabled before native window activation changes. diff --git a/apps/desktop/README.zh.md b/apps/desktop/README.zh.md index 0504c7004c..78269d3e6d 100644 --- a/apps/desktop/README.zh.md +++ b/apps/desktop/README.zh.md @@ -271,7 +271,7 @@ Windows 下载完成后的更新确认说明应用会在安装期间关闭、完 macOS 在安装准备阶段等待产品主窗口退出全屏,再显示重启确认。取消确认后窗口保持普通模式;原生切换期间销毁窗口会在任务停止前取消安装。如果 macOS 未在五秒内报告切换完成,安装准备会报错且不停止 Host;本地化错误提示要求退出全屏后重试。安装失败后的恢复使用普通窗口。 -原生更新浮层在文档就绪且父窗口可见时显示,并在父窗口再次显示时恢复。关闭浮层会释放输入拦截和父窗口监听。[本地窗口验证](tests/README.zh.md#verification-overlay)无需启动工作区即可检查这些切换。 +原生更新浮层在文档就绪且父窗口可见时显示,并在父窗口再次显示时恢复。父窗口的可见性通知不会重复显示已可见的浮层;浮层在其他应用后方完成加载或恢复显示时,不会激活应用。关闭浮层会释放输入拦截和父窗口监听。[本地窗口验证](tests/README.zh.md#verification-overlay)无需启动工作区即可检查这些切换。 打包应用在启动后异步检查固定 Nightly。常规轮询以十分钟为基础间隔,每次独立采样 ±20% 的随机抖动。每次检查失败将基础延迟翻倍,上限为一小时;成功后重置。随机延迟不超过该上限,并从全部复用调用结算后开始计时。本地化的“检查更新…”菜单项(Windows 可从顶栏的“应用”菜单进入)立即执行,并复用正在进行的检查。回到前台和系统恢复时遵守相同的单调时钟截止时间。新收到的强更策略也会立即请求检查更新清单。自动检查从不弹窗或下载安装包。手动检查显示正在检查、失败或包含已安装版本号的无更新反馈。常规更新弹窗原位渐入渐出;连续弹窗替换卡片内容并重置其滚动位置,保留黑色半透明蒙层,不模糊父页面。Windows 在销毁模态窗口前先隐藏它,确保原生窗口激活切换前父窗口已恢复启用。 diff --git a/apps/desktop/src/update-overlay.ts b/apps/desktop/src/update-overlay.ts index 7706982641..e43f335088 100644 --- a/apps/desktop/src/update-overlay.ts +++ b/apps/desktop/src/update-overlay.ts @@ -20,7 +20,8 @@ export class DesktopUpdateOverlays { * @param preload - Isolated shell-only preload. * @param title - Localized window title. * @param nativeModal - Use a native modal; false keeps overlays out of macOS sheets. - * @returns A transparent child that follows its parent's bounds and visibility after loading and releases its listeners on close. + * @returns A transparent child that follows its parent's bounds and visibility without background activation; + * closing it releases its listeners. */ create(parent: BrowserWindow, preload: string, title: string, nativeModal = true): BrowserWindow { const window = new BrowserWindow({ @@ -51,7 +52,10 @@ export class DesktopUpdateOverlays { window.once('closed', () => { parent.off('move', follow); parent.off('resize', follow) }) let ready = false const show = (): void => { - if (ready && !window.isDestroyed() && !parent.isDestroyed() && parent.isVisible()) window.show() + if (!ready || window.isDestroyed() || parent.isDestroyed() || !parent.isVisible() || window.isVisible()) return + // macOS also emits show when an existing window becomes unoccluded. + if (parent.isFocused()) window.show() + else window.showInactive() } parent.on('show', show) window.once('closed', () => { parent.off('show', show) }) diff --git a/apps/desktop/tests/expected/update-overlay-visibility.json b/apps/desktop/tests/expected/update-overlay-visibility.json index 2e697c7afb..e8769f3924 100644 --- a/apps/desktop/tests/expected/update-overlay-visibility.json +++ b/apps/desktop/tests/expected/update-overlay-visibility.json @@ -11,5 +11,6 @@ "restored": { "parent": true, "overlay": true, "filter": "none" }, "released": { "filter": "none", "showListenersRestored": true, "keyboardListeners": 0 } }, - { "parentDestroyed": true, "overlayDestroyed": true } + { "parentDestroyed": true, "overlayDestroyed": true }, + { "backgroundOverlay": { "otherFocused": true, "overlayVisible": true } } ] diff --git a/apps/desktop/tests/fixtures/update-overlay-visibility.mjs b/apps/desktop/tests/fixtures/update-overlay-visibility.mjs index 8a92ce1a70..94edff853f 100644 --- a/apps/desktop/tests/fixtures/update-overlay-visibility.mjs +++ b/apps/desktop/tests/fixtures/update-overlay-visibility.mjs @@ -72,11 +72,39 @@ async function main() { parent.destroy() } + async function qualifyBackgroundVisibility() { + phase = 'background overlay load' + const parent = new BrowserWindow({ show: true, width: 800, height: 600 }) + windows.push(parent) + await parent.loadURL('data:text/html,