Skip to content

fix(desktop): keep maximized state when saved bounds exceed the display - #58442

Open
tneemo wants to merge 1 commit into
anomalyco:devfrom
tneemo:fix/desktop-maximized-window-state
Open

tneemo wants to merge 1 commit into
anomalyco:devfrom
tneemo:fix/desktop-maximized-window-state

Conversation

@tneemo

@tneemo tneemo commented Oct 23, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #58247

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When the window is maximized, manageWindowState keeps saving the normal (unmaximized) bounds. On a display shorter than the 1280x800 default (e.g. 1388x781), those bounds can be a few pixels taller than the display (y: 34, height: 783). On the next launch resolveWindowState rejects them and returns the default-size fallback, but that fallback dropped isMaximized/isFullScreen and always put the window on the primary display at 0,0. So the window never reopened maximized, and on a multi-monitor setup it also jumped to the primary display.

The fallback now:

  • keeps isMaximized and isFullScreen from the saved state
  • picks the display the saved bounds belong to via displays.matching() (the same screen.getDisplayMatching() used when the state is recorded), falling back to the primary display only if that display is no longer connected
  • places the window at that display's global origin (display.x/display.y) instead of 0,0, so monitors to the right, to the left (negative x) or a primary display not at the origin all work
  • caps the default size to that display, so 1280x800 no longer comes back larger than a small screen

Related: #57088 changes how edge-snapped bounds are accepted. It doesn't cover this case, but both PRs touch resolveWindowState.

How did you verify your code works?

  • Added regression tests:
    • the exact state from the issue on a single 1388x781 display (maximized and fullscreen)
    • the size cap
    • overflowing bounds on a second monitor at x: 1920 and at x: -1440, which stay on that monitor with the maximized flag kept
    • falling back to a primary display that does not start at 0,0
  • The new tests fail on dev and pass with the fix. The test displays use an Electron-like matching helper (largest overlap, else primary).
  • cd packages/desktop && bun run test: 136 pass, 1 skip, 0 fail.
  • bun run check (lint + typecheck across the monorepo) passes.
  • oxlint shows no new warnings.

Screenshots / recordings

N/A, not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #56970

Copilot AI balanced review requested due to automatic review settings October 23, 2026 12:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix works. I ran the #58247 reproduction against the real window-state.ts on a single 1388×781 display. On the base, the window never reopens maximized and the oversized bounds are saved again on every launch. With this PR, it reopens maximized from the second launch on, and the saved state settles to bounds that fit the display, so the loop ends. The new tests fail on the base and pass with the change. I didn't run the Electron app itself.

Public API: unchanged. This is internal desktop main-process logic, and the window-state file format is the same.

One small point, inline: the fallback to the primary display can't trigger in practice, so it, sameBounds and its test can be dropped.

Related: #56970


const matched = displays.matching({ x: state.x, y: state.y, width: state.width, height: state.height })

const display = displays.all().find((bounds) => sameBounds(bounds, matched)) ?? displays.primary()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the real app, displays.matching is screen.getDisplayMatching(bounds).bounds (windows/index.ts, windows/early.ts). It always returns one of the currently connected displays, so the displays.all().find(sameBounds) ?? displays.primary() fallback never triggers. Using const display = displays.matching({ ... }) directly would keep the fix smaller. You could then drop sameBounds and the "matched display is no longer current" test, which only checks a matching stub that returns a display that isn't connected.

This branch has not been deployed

No deployments
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.

desktop: maximized state lost when saved bounds exceed the display

2 participants