Remember native window size across opens (fixes #203)#207
Merged
Conversation
Native windows (Plugins, Posts, Users, …) were always reopening at the
plugin-author defaults, forcing the user to resize them every time. We
already skip native windows from the server-side session snapshot
(their render callback is a closure that can't be rehydrated), but
"size I picked last time" doesn't need server state.
Per baseId `{ width, height }` lives in localStorage under
`desktop-mode-native-window-geometry`, written on
`WINDOW_RESIZE_END` for native windows in normal floating state and
read back in `openFromEntry` / `openNewFromEntry`. Snapped, maximized,
and fullscreen sizes are skipped so a tile layout doesn't poison the
stored "preferred" size. Stored sizes are clamped to the registered
`min_width` / `min_height` on open so raising the minimum in a plugin
update never reopens at the older smaller size. Store is capped at
64 entries with LRU eviction.
Contributor
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
Builds on the size persistence in the previous commit. The store now
records `{ x, y, width, height, state? }` per baseId in localStorage,
covering classic iframe-backed windows alongside native ones.
Save side: WINDOW_DRAG_END writes size + position (the size write
seeds the entry when there isn't one yet, so open-drag-close paths
get persisted even without a manual resize). WINDOW_RESIZE_END also
captures the post-resize x/y because a top-left corner drag moves
the window. WINDOW_MAXIMIZED / WINDOW_UNMAXIMIZED record the
maximized intent without disturbing the floating size or position.
Read side: `WindowManager.createWindow` consults the same store
whenever the caller doesn't pin `width` / `height` / `initialState`
/ `x` / `y`, falling back through saved -> cascade-from-rect default.
Position is clamped to the current desktop area so a window
remembered at x=2800 on a 3440px display doesn't open off-screen on
a smaller monitor. `manager.openNew` now computes its own cascade
slot and passes explicit x/y so duplicates always cascade off the
primary's remembered spot. Native windows used to hardcode `x: 0,
y: 0` in `openFromEntry`, which short-circuited the replay; that
hardcode is gone.
The unified maximize replay also lets the native path drop its
`.then(win.maximize())` chain; `initialState: 'maximized'` from
`createWindow` handles both window kinds the same way.
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.
Summary
Native windows (Plugins, Posts, Users, Comments, Settings, …) reopened at the plugin-author defaults on every open, forcing the user to resize them every time. The Plugins window in particular ships at 1180×760 which is too narrow for the installed-plugins table on many setups.
This adds per-
baseId{ width, height }persistence in localStorage so each native window reopens at the size the user last resized it to.Dashboard.WordPress.Develop.WordPress.2026-05-14.10-51-55.mp4
How it works
src/window-manager/native-window-geometry.ts— read/write a single localStorage map (desktop-mode-native-window-geometry), bounded at 64 entries with LRU eviction, with sanity checks on dimensions.src/native-windows.ts:openFromEntry/openNewFromEntrynow resolve{ width, height }from the saved map when present, falling back to the registered defaults. The stored size is clamped up to the registeredmin_width/min_heightso raising the minimum in a plugin update never reopens at a no-longer-allowed smaller size.WINDOW_RESIZE_ENDlistener writes the new size back, but only when the resized window is native AND instate === 'normal'. That keeps snap-zone halves, maximized geometry, and fullscreen geometry from poisoning the stored "preferred" size. Programmatic tiles inarrange.ts/snap-zones.tsgo through their own hooks and never fire resize-end, so they don't write either.Position is intentionally NOT persisted. Cascade-on-open keeps stacked native windows visible across viewport size changes (different monitors, different zoom levels); restoring a remembered position would happily place a window off-screen.
Why not server-side session restore
Native windows are already skipped from the session snapshot because their
rendercallback is a JS closure we can't serialize and rehydrate server-side. Persisting just{ width, height }client-side is orthogonal to that constraint and needs no PHP wiring.Tests
tests/vitest/native-window-geometry.test.ts(12 cases): round-trip, rounding, invalid input rejection, ceiling, corrupt-JSON tolerance, overwrite, trim, LRU move-to-end.tests/vitest/native-windows-sync.test.ts(6 new cases under "remembered window size"): default fallback, stored size honored, clamp-up to current minimum, persist-on-resize-end happy path, native-only filter, normal-state-only filter.Test plan
Fixes #203