feat(window): persist main window size/position across launches (#97)
Addon CI / addon (arm64, macos-14, mac) (push) Waiting to run
Addon CI / addon (x64, ubuntu-22.04, linux) (push) Waiting to run
Addon CI / addon (x64, windows-latest, win) (push) Waiting to run
Ship CI / CI (push) Waiting to run

* feat(window): persist main window size/position across launches

The main window always opened at a fixed 1400x900, forcing a manual
resize every session. Save the window geometry (normal bounds +
maximized flag) to the existing desktop prefs store on close, and
restore it in createWindow.

Saved bounds are validated by a pure sanitizer against the current
display layout before use, so stale state degrades safely instead of
producing an off-screen or absurd window:
- garbage/partial config -> 1400x900 centered defaults
- size clamped between the 800x600 window minimums and the largest
  display's workArea
- position kept only when the window overlaps a display by at least
  100x50 px (unplugged monitor / resolution change -> re-center);
  negative multi-monitor coordinates remain valid
- maximized sessions save getNormalBounds() and re-maximize on
  restore; fullscreen deliberately restores windowed

No new dependency; reuses get/setDesktopConfig (atomic write,
fail-soft) in soundfont-manager.ts. The store file is already in the
reset-app-settings delete-set, so a config reset also resets bounds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: gionnibgud <gionnibgud@gmail.com>

* fix(window): don't crash shutdown if bounds persistence write fails

The close listener called setDesktopConfig synchronously with no error
handling; a disk-full or permissions failure during the write would throw
unhandled inside the close handler, risking a shutdown crash. Wrap the
write in try/catch and log a warning instead. Flagged by CodeRabbit on PR #97.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: gionnibgud <gionnibgud@gmail.com>

---------

Signed-off-by: gionnibgud <gionnibgud@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
gionnibgud
2026-07-11 15:41:47 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent f66c27b459
commit 6349ed4c5f
4 changed files with 174 additions and 5 deletions
+59
View File
@@ -0,0 +1,59 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const { loadTs } = require('./_load-ts');
const { sanitizeWindowBounds, DEFAULT_WIDTH, DEFAULT_HEIGHT, MIN_WIDTH, MIN_HEIGHT } =
loadTs('src/main/window-bounds.ts');
// A common single-display workArea (1920×1080 minus a 40px taskbar).
const PRIMARY = { x: 0, y: 0, width: 1920, height: 1040 };
const DEFAULTS = { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, maximized: false };
test('valid bounds on a matching display round-trip', () => {
const saved = { x: 100, y: 50, width: 1200, height: 800, maximized: true };
assert.deepEqual(sanitizeWindowBounds(saved, [PRIMARY]), saved);
});
test('garbage input falls back to defaults', () => {
for (const bad of [undefined, null, 'wat', 42, {}, { x: 1, y: 2 }, { x: 'a', y: 0, width: 1200, height: 800 }, { x: NaN, y: 0, width: 1200, height: 800 }, { x: 0, y: 0, width: Infinity, height: 800 }]) {
assert.deepEqual(sanitizeWindowBounds(bad, [PRIMARY]), DEFAULTS);
}
});
test('empty display list falls back to defaults', () => {
assert.deepEqual(sanitizeWindowBounds({ x: 0, y: 0, width: 1200, height: 800 }, []), DEFAULTS);
});
test('oversize bounds clamp to the largest display workArea', () => {
const out = sanitizeWindowBounds({ x: 0, y: 0, width: 5000, height: 4000 }, [PRIMARY]);
assert.deepEqual(out, { x: 0, y: 0, width: 1920, height: 1040, maximized: false });
});
test('undersize bounds clamp up to the window minimums', () => {
const out = sanitizeWindowBounds({ x: 10, y: 10, width: 300, height: 200 }, [PRIMARY]);
assert.deepEqual(out, { x: 10, y: 10, width: MIN_WIDTH, height: MIN_HEIGHT, maximized: false });
});
test('position on a now-unplugged monitor is dropped, size kept', () => {
// Saved on a second display to the right that no longer exists.
const out = sanitizeWindowBounds({ x: 2000, y: 100, width: 1200, height: 800 }, [PRIMARY]);
assert.deepEqual(out, { width: 1200, height: 800, maximized: false });
});
test('negative coordinates on a left-of-primary monitor are kept', () => {
const leftMonitor = { x: -1920, y: 0, width: 1920, height: 1040 };
const saved = { x: -1800, y: 100, width: 1200, height: 800 };
const out = sanitizeWindowBounds(saved, [leftMonitor, PRIMARY]);
assert.deepEqual(out, { ...saved, maximized: false });
});
test('sliver overlap below the grab threshold drops the position', () => {
// Only 50px of the window's left edge on screen — not enough to grab.
const out = sanitizeWindowBounds({ x: 1870, y: 100, width: 1200, height: 800 }, [PRIMARY]);
assert.deepEqual(out, { width: 1200, height: 800, maximized: false });
});
test('fractional coordinates are rounded to integers', () => {
const out = sanitizeWindowBounds({ x: 10.6, y: 20.4, width: 1200.5, height: 800.2 }, [PRIMARY]);
assert.deepEqual(out, { x: 11, y: 20, width: 1201, height: 800, maximized: false });
});