fix(window-placement): persist placement intent across session restore; center peeks
Previously, session restore wrote the resolved x/y of every window to the
snapshot and re-opened windows with those absolute coordinates. The Placement
object on params (mode + edge / parentId) was silently dropped by sanitizeParams
because it isn't a primitive — so the window-open handler at restore time saw
explicit x/y and derived `placement: 'manual'`, pinning windows to coordinates
from a previous display layout. Across display switches and resizes, centered
windows drifted and edge-anchored windows landed in the wrong half.
Three changes:
- `features/peeks/background.js`: peek windows pass `center: true` so the
derivation produces `mode: 'centered'` instead of falling through to
parent-centered or cursor-display-fallback. Peeks now center on the cursor
display regardless of size, as intended.
- `backend/electron/session.ts`: new `mirrorPlacementIntent()` helper
translates the in-memory Placement object back into the legacy flat flags
(`center: true` for centered/parent-centered, `screenEdge: '<edge>'` for
edge mode) before sanitizeParams runs. Those primitives survive the
snapshot, and at restore the window-open handler re-derives the correct
Placement on the current display. 'manual' and 'cursor-display-fallback'
modes intentionally produce no flag — saved x/y (or its absence) drives
those.
- New `buildRestoreBoundsOptions()` extracts the restore-side bounds-options
logic: when descriptor.params declares an intent (`center === true` or
`screenEdge` set), drop x/y so computeInitialBounds runs against the
current display layout. Otherwise behave as before — pass saved x/y when
the saved center is on-screen, drop it when it isn't.
Existing snapshots round-trip cleanly: old descriptors have neither flag set,
so restore falls back to the prior behavior. New saves capture the intent and
self-heal on next restore.
Tests: 16 new unit tests for the two helpers (every Placement mode,
sanitizeParams composition, on/off-screen branches, bad-input handling) +
a Playwright spec asserting a peek opened with the production peeks params
lands centered on the cursor work area within 2px tolerance.