fix(desktop): resync renderer after Windows maximize - #3472
Conversation
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks for isolating the workaround behind a small Windows-only scheduler. The lifecycle guards and coalescing look sound, and I did not find a production correctness issue in the implementation. The remaining gap is that the test proves calls against a mock, while the bug and the proposed fix both live in Electron's real Windows layout behavior; I left that boundary inline.
AI-assisted review disclosure: OpenAI Codex performed an independent exact-head review. I verified the implementation, lifecycle guards, test scope, and live PR state, and I made the final review decision.
ea8bb87 to
bd88d4b
Compare
|
Heads up on the failing It fails at That's a regression from #3001, which merged into main earlier today: The We'll approve once #3484 is resolved and the Windows job can go green on this head. AI-assisted review. |
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks — the diagnosis here is the part that matters, and it is right: maximize is where this reproduces, but the actual gap is that Electron 43's root View layout/repaint does not follow the native maximize. Naming the trigger and the cause separately is what keeps this from becoming a CSS patch.
Reviewed at exact head 83d3d12174620640ea8475cde7673534c8c3142a against base f83b469b557a30036d11572d3c14d69bc0766eaa. No P0–P3.
What we checked, since 495 lines for a window-resize fix invites the question:
- The production change is 48 lines and sits inside the existing main-window lifecycle. Re-calling
setContentView(window.contentView)is not a workaround —NativeWindowViews::SetContentViewdetaches and re-attaches the content view and then runsFlushPendingRootLayout, which is exactly the layout flush that did not happen. Reusing the samecontentViewmeans no new size state is introduced;pendingonly coalesces events. There is no second size authority here. - The remaining ~270 lines are the verifier, and they buy something real: the previous coverage asserted that a mock was called. This reads the native client area through PowerShell
GetClientRect/IsZoomedand the renderer viewport through CDP, then walks normal → maximized → normal checking DPR andhtml/body/#root/.appFramealignment against the native client. For a bug that only exists at the native/renderer boundary on Windows, that is the only evidence that means anything from a macOS checkout. - Guards cover the failure paths: window destroyed, WebContents destroyed, restore, non-Windows. We looked for a stale-
pendingpath that could fire against a destroyed window and did not find one.
Verified locally: helper tests 4/4, node --test scripts/verify-packaged-app.test.mjs scripts/verify-windows-harness.test.mjs 51/51, strict TS compile clean, git diff --check clean.
Not approving yet, and it is not about the code. The red test and package runs on this head are stale merge refs — they were created at 11:23Z against a base that predates the midTurn fix that landed on main at 11:49Z, and the failure in the log is packages/cli/src/pi-tui-runner.ts missing midTurn, which has nothing to do with this PR. A rerun will not help; pull_request runs test the merge commit computed at event time, so only a new push recomputes it. Please rebase, and we will take the result on the new head.
This review was AI-assisted. Findings were verified against the exact head listed above; any mistakes are ours to correct — please push back where we got it wrong.
|
Heads up — this has drifted into conflict with Worth knowing before you rebase: #3397 landed on 2026-08-22 and added ASF license headers across ~2685 files, so a rebase will touch more than you'd expect, and any file you add now needs a header ( I'd like to review this — just ping me once it's rebased and CI is green. |
83d3d12 to
9fcd182
Compare
|
Rebased onto current Local validation completed:
The new |
|
Reviewed at The problem statement holds — a native Windows maximize changes the HWND client area, and Electron's root view layout does not reliably follow the native transition, so the renderer keeps the pre-maximize height. That is an ordinary user path, not a constructed one. What I liked about the shape of the fix: The verification is the strongest part. Rather than asserting against mocks, the new packaged smoke drives [P3] const handleResize = (): void => { scheduleSave(); scheduleMaximizedRendererSync(); };
const handleMaximize = (): void => { scheduleSave(); scheduleMaximizedRendererSync(); };Two names for one function, bound to two events. One shared handler would read the same and remove the chance of the two drifting apart later under the impression that they are meant to differ. Purely cosmetic — no behavioural consequence, since On CI: both workflow runs on this head are One thing I could not check: the fix depends on |
|
@Astro-Han Thanks for the review. Addressed the P3 in Local validation is green:
The updated-head |
Astro-Han
left a comment
There was a problem hiding this comment.
Approving c733e773a363c34944b61c1880f3e2d1694b4907. Both required checks are terminal green and bound to that exact SHA (package 32614918972, test 32614918937). Three P3s, all inline, none blocking.
- #3472 (comment) — the deferred body has no
try/catch, so a native throw becomes a main-processuncaughtException. - #3472 (comment) — the DPR clause in
rendererViewportMatchesNativeClientmay be specific to 96-DPI runners. - Child
WebContentsViewcoverage — discussed below, since it is about what the tests reach rather than about a line.
Thanks for folding in the earlier note about the two byte-identical geometry handlers; handleWindowGeometryChange reads better than the duplicate did.
What was verified on a real machine
Windows x64, 2560×1440 at 150%, Electron 43.2.0. Three maximize/restore rounds plus six 80 ms rapid toggles on both the base and the patched build, with a full-viewport diagnostic pattern injected into the renderer so "did it paint correctly" became pixel-measurable, cross-checked three ways per step (screenshot, CDP layout probe, native client probe).
- Layout agreed with the native client area within 1 px at every step of all 14, including immediately after maximize, on both builds.
- The full-viewport border filled the entire client area in every maximized screenshot, consistent at t0 / 150 ms / 1.5 s.
- The rAF frame counter advanced continuously throughout, including after the rapid toggles — the paint pipeline never froze.
The honest part, which I would rather state than bury: the base build did not reproduce the original bug on that machine. The unpatched version painted correctly too. So that run cannot demonstrate "broken before, fixed after" — it can only demonstrate that the patch introduces no observable regression there. The "the bug exists and this fixes it" link rests on the packaged maximize/restore check this PR adds, which is green on this head.
Two caveats from that run, also stated rather than smoothed over: the window was backgrounded with three anti-throttling flags set (a fidelity concession, since a person was using the machine), and maximize/restore was driven through ShowWindowAsync(SW_MAXIMIZE/SW_RESTORE) — the native path the maximize button and Win+↑ both funnel into, but not the literal gestures.
Child WebContentsView — the gap that was open, now filled
Re-applying the same contentView had no coverage for the embedded-browser child views: the unit test mocks an empty contentView, and the verify probe only inspects the main renderer. That mattered because setContentView is the one operation in this change that could plausibly disturb a child view's attachment.
It was closed two ways. A minimal reproduction on the same Electron version (contentView + child WebContentsView, reapply on and off, three rounds plus rapid toggles) showed the child survives, stays in contentView.children, keeps its bounds, keeps rAF running, and captures correct pixels — with both groups identical. Then the patched dev app with a real session and the browser panel open showed the child's frame counter advancing continuously through every maximize phase (+18–19 frames per 300 ms), no destruction, no detachment, and the DOM-side strip at the right position and full height.
Not verified, and I am not going to claim it: the child's visual position after maximize while visible — the run was interrupted twice by the machine's actual user and stopped rather than pressed. Multi-monitor and mixed-DPI were not reachable with one display.
Reviewed at 2026-08-23 12:42 UTC. No P0–P2.
c733e77 to
dbd401e
Compare
|
@Astro-Han Both new P3s are addressed in Post-rebase local validation is green: workspace dependency build, Desktop main-process build, maximize sync and Windows harness tests (43/43), Desktop typecheck, lint, format, ASF headers, and diff checks. The new-head |
|
Hi — thanks for the Heads-up that this branch now conflicts with current
That's a single real source conflict, so it needs your judgement rather than a mechanical resolution: Once the branch is conflict-free and CI is green on the new head, I'll pick the review back up — the P3 fixes themselves aren't in question here, this is purely the branch having drifted behind AI-assisted maintenance note, not a review. It does not count as the required human review under |
dbd401e to
d95e89c
Compare
|
@Astro-Han Rebased once more onto current The verifier conflict was resolved by retaining both the current rollback-registration coverage from Exact-head local validation:
Fresh exact-head checks should be created for this push. The fresh workflows are waiting for maintainer approval: CI 32638152502 and Release Windows check 32638152482. Please approve both exact-head runs. |
jackwener
left a comment
There was a problem hiding this comment.
Acting on behalf of repository owner WAWQAQ under the standing Kabi authorization for PR review and APPROVE; exact head d95e89c5fd5d4480b998ee0d2f4745dd8feb0a05.
GO: no P0–P2; one non-blocking P3 inline. Incremental coverage included the full old-to-new range-diff, the added teardown-race error boundary, the packaged Windows geometry verifier, and separate reconciliation of reviews, inline comments, and issue comments. Focused validation: maximize sync 5/5 and Windows harness 39/39. Not independently re-run on a physical Windows host in this pass.
Fresh machine gates: OPEN, non-draft, MERGEABLE/CLEAN; exact-head test and package are completed/success. This agent-executed approval does not claim to satisfy the repository's separate required-human-review wording. No merge performed.
| // Electron can report CSS or physical viewport pixels depending on the | ||
| // packaged app's DPI-awareness mode. Proportional agreement with the native | ||
| // client is the stable contract; equating that scale to DPR is not. | ||
| return dimensionsMatch(widthScale, heightScale, 0.01); |
There was a problem hiding this comment.
[P3] This now checks only that the native/client width and height have the same scale, so any uniformly scaled stale renderer is accepted. Calling this production function with a renderer/layout of 800×600, a maximized native client of 1600×1200, and DPR 1 returns true, even though the renderer is still at the restored size. The existing guards do not catch it: the document can fill its own stale viewport and both scales are still exactly 2. The negative test uses 1920×900, whose aspect-ratio mismatch exercises a different case. Please add a uniformly scaled stale case and require the common scale to be approximately 1 (native reports CSS pixels) or approximately devicePixelRatio (native reports physical pixels). This is non-blocking because the reported #3416 path (1240×820 → 1920×1080) changes aspect ratio and remains covered.
* 修复 Windows 最大化后渲染区未同步 * 补充 Windows 最大化打包烟测 * 改用原生窗口句柄验证最大化 * 强化 Windows 最大化尺寸烟测 * 补充 Windows 最大化文件许可头 * 合并窗口尺寸同步事件处理器 * 收紧 Windows 最大化同步容错
Summary
Fixes #3416
Verification
npm --workspace @maka/desktop run typecheckapps/desktop/dist/main/__tests__/shell-env.test.jsthree times in isolation: 12/12 passed on every run.Validation limit
The current host is macOS, so the packaged Windows 11 maximize transition still needs Windows CI or a Windows machine for final visual confirmation.