[Fix] 显示器变化后自动恢复屏幕外窗口 - #300
Merged
qiyinxi merged 1 commit intoJul 15, 2026
Merged
Conversation
Reviewer's Guide此 PR 通过围绕稳定的 64px 标题栏拖拽区域校验和调整已保存的窗口边界、按显示器/DPI 重新计算最小尺寸,并在保留负坐标和托盘/隐藏行为的前提下正确持久化/恢复窗口状态,从而增强了 Electron 窗口在显示配置变化时的定位稳健性;同时在 Vue 标题栏中标记了一个稳定的拖拽区域,并保持更新提示仍可点击。 显示器变化时窗口恢复的时序图sequenceDiagram
participant Screen
participant MainProcess
participant BrowserWindow as win
participant ensureWindowIsVisible
participant recomputeMinSize
participant Config as ConfigStorage
Screen->>MainProcess: display-metrics-changed
activate MainProcess
MainProcess->>MainProcess: handleDisplayConfigurationChanged()
MainProcess->>ensureWindowIsVisible: ensureWindowIsVisible()
activate ensureWindowIsVisible
ensureWindowIsVisible->>win: isDestroyed()
ensureWindowIsVisible->>win: isMinimized()
ensureWindowIsVisible->>win: isVisible()
ensureWindowIsVisible->>win: isMaximized()
ensureWindowIsVisible->>win: getNormalBounds()/getBounds()
ensureWindowIsVisible->>MainProcess: findDisplayWithUsableTitleBar(bounds)
alt [title bar not visible on any display]
ensureWindowIsVisible->>MainProcess: centerBoundsInWorkArea(bounds, primary.workArea)
ensureWindowIsVisible->>win: unmaximize()
ensureWindowIsVisible->>win: setBounds(safeBounds)
ensureWindowIsVisible->>ConfigStorage: loadConfig()
ensureWindowIsVisible->>ConfigStorage: saveConfig(updatedConfig)
end
deactivate ensureWindowIsVisible
MainProcess->>recomputeMinSize: recomputeMinSize()
activate recomputeMinSize
recomputeMinSize->>win: isMinimized()
recomputeMinSize->>win: getNormalBounds()/getBounds()
recomputeMinSize->>MainProcess: screen.getDisplayMatching(bounds)
recomputeMinSize->>win: getMinimumSize()
recomputeMinSize->>win: setMinimumSize(w, h)
recomputeMinSize->>win: setBounds(newBounds)
deactivate recomputeMinSize
deactivate MainProcess
文件级变更
与关联 issue 的对照评估
可能关联的 issue
Tips and commandsInteracting with Sourcery
Customizing Your Experience访问你的 dashboard 以:
Getting Help
Original review guide in EnglishReviewer's GuideThis PR hardens Electron window positioning against display configuration changes by validating and adjusting saved window bounds around a stable 64px title-bar drag area, recomputing min sizes per-display/DPI, and correctly persisting/restoring window state while preserving negative coordinates and tray/hidden behavior; it also marks a stable drag region in the Vue title bar while keeping the update-hint clickable. Sequence diagram for window recovery on display changesequenceDiagram
participant Screen
participant MainProcess
participant BrowserWindow as win
participant ensureWindowIsVisible
participant recomputeMinSize
participant Config as ConfigStorage
Screen->>MainProcess: display-metrics-changed
activate MainProcess
MainProcess->>MainProcess: handleDisplayConfigurationChanged()
MainProcess->>ensureWindowIsVisible: ensureWindowIsVisible()
activate ensureWindowIsVisible
ensureWindowIsVisible->>win: isDestroyed()
ensureWindowIsVisible->>win: isMinimized()
ensureWindowIsVisible->>win: isVisible()
ensureWindowIsVisible->>win: isMaximized()
ensureWindowIsVisible->>win: getNormalBounds()/getBounds()
ensureWindowIsVisible->>MainProcess: findDisplayWithUsableTitleBar(bounds)
alt [title bar not visible on any display]
ensureWindowIsVisible->>MainProcess: centerBoundsInWorkArea(bounds, primary.workArea)
ensureWindowIsVisible->>win: unmaximize()
ensureWindowIsVisible->>win: setBounds(safeBounds)
ensureWindowIsVisible->>ConfigStorage: loadConfig()
ensureWindowIsVisible->>ConfigStorage: saveConfig(updatedConfig)
end
deactivate ensureWindowIsVisible
MainProcess->>recomputeMinSize: recomputeMinSize()
activate recomputeMinSize
recomputeMinSize->>win: isMinimized()
recomputeMinSize->>win: getNormalBounds()/getBounds()
recomputeMinSize->>MainProcess: screen.getDisplayMatching(bounds)
recomputeMinSize->>win: getMinimumSize()
recomputeMinSize->>win: setMinimumSize(w, h)
recomputeMinSize->>win: setBounds(newBounds)
deactivate recomputeMinSize
deactivate MainProcess
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
qiyinxi
force-pushed
the
agent/restore-offscreen-window
branch
from
July 15, 2026 13:58
9e35bb2 to
6dd3c05
Compare
qiyinxi
marked this pull request as ready for review
July 15, 2026 14:18
There was a problem hiding this comment.
Hey - 我在这里给出一些高层次的反馈:
- 新的窗口边界逻辑在不同地方混用了
workArea和workAreaSize;建议在所有涉及宽度/高度的逻辑中统一使用workArea,以避免在不同平台上出现微妙的不一致。 - 在
ensureWindowIsVisible和配置保存处理函数中,loadConfig()在短时间内被多次调用;可以考虑在createWindow()内缓存当前配置,或者将其作为参数传入辅助函数,以减少重复的 I/O 并简化状态推理。 restoreToMaximized状态由多个事件处理函数更新(maximize、unmaximize、restore、show、配置保存),并在不同分支中使用;可以添加一个小的辅助函数来封装最大化/恢复的决策,从而让窗口状态转换更易理解,也更不容易出错。
给 AI 智能体的提示
Please address the comments from this code review:
## Overall Comments
- The new window bounds logic mixes `workArea` and `workAreaSize` in different places; consider standardizing on `workArea` for width/height everywhere to avoid subtle inconsistencies across platforms.
- In `ensureWindowIsVisible` and the config save handlers, `loadConfig()` is called multiple times in quick succession; caching the current config within `createWindow()` or passing it into helpers would reduce repeated I/O and simplify state reasoning.
- The `restoreToMaximized` state is updated from several event handlers (`maximize`, `unmaximize`, `restore`, `show`, config save) and used in different branches; adding a small helper to encapsulate the maximize/restore decision could make the window state transitions easier to follow and less error-prone.帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English
Hey - I've left some high level feedback:
- The new window bounds logic mixes
workAreaandworkAreaSizein different places; consider standardizing onworkAreafor width/height everywhere to avoid subtle inconsistencies across platforms. - In
ensureWindowIsVisibleand the config save handlers,loadConfig()is called multiple times in quick succession; caching the current config withincreateWindow()or passing it into helpers would reduce repeated I/O and simplify state reasoning. - The
restoreToMaximizedstate is updated from several event handlers (maximize,unmaximize,restore,show, config save) and used in different branches; adding a small helper to encapsulate the maximize/restore decision could make the window state transitions easier to follow and less error-prone.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new window bounds logic mixes `workArea` and `workAreaSize` in different places; consider standardizing on `workArea` for width/height everywhere to avoid subtle inconsistencies across platforms.
- In `ensureWindowIsVisible` and the config save handlers, `loadConfig()` is called multiple times in quick succession; caching the current config within `createWindow()` or passing it into helpers would reduce repeated I/O and simplify state reasoning.
- The `restoreToMaximized` state is updated from several event handlers (`maximize`, `unmaximize`, `restore`, `show`, config save) and used in different branches; adding a small helper to encapsulate the maximize/restore decision could make the window state transitions easier to follow and less error-prone.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
修改内容
x = 0、y = 0误判为缺失配置。根因
原实现通过
screen.getDisplayNearestPoint()选择缩放和工作区,但创建BrowserWindow时仍原样使用保存的cfgX/cfgY。后续逻辑只调整宽高,不校验可拖拽区域是否可见,也没有处理display-removed,因此断开副屏后窗口可以完全位于所有现存屏幕之外。验证
yarn exec tsc -p tsconfig.electron.json --noEmit:通过。yarn exec eslint electron/main.ts src/components/TitleBar.vue:通过。git diff --check upstream/dev_v2...HEAD:通过。Closes #298
Summary by Sourcery
确保在显示器变更时主窗口保持可见且尺寸正确,并持久化调整后的边界和状态。
Bug Fixes:
Enhancements:
Original summary in English
Summary by Sourcery
Ensure the main window remains visible and correctly sized when displays change, and persist adjusted bounds and state.
Bug Fixes:
Enhancements: