diff --git a/src/main/index.ts b/src/main/index.ts index 2160e4c..a7ea7dc 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -50,17 +50,25 @@ function createWindow(): void { } }) - if (saved.isMaximized) win.maximize() + // Full screen and maximize are mutually exclusive; full screen wins so the + // restored geometry matches how the window was left. + if (saved.isFullScreen) win.setFullScreen(true) + else if (saved.isMaximized) win.maximize() win.on('ready-to-show', () => win.show()) // Remember the window geometry across launches. getNormalBounds() returns the - // restored (un-maximized) rect, so re-maximizing next launch still restores to - // a sane size. Debounced because resize/move fire in bursts while dragging. + // restored (un-maximized, un-fullscreen) rect, so re-maximizing / re-entering + // full screen next launch still restores to a sane size. Debounced because + // resize/move fire in bursts while dragging. let persistTimer: ReturnType | null = null const persistBounds = (): void => { if (win.isDestroyed()) return - windowStateStore.save({ bounds: win.getNormalBounds(), isMaximized: win.isMaximized() }) + windowStateStore.save({ + bounds: win.getNormalBounds(), + isMaximized: win.isMaximized(), + isFullScreen: win.isFullScreen() + }) } const schedulePersist = (): void => { if (persistTimer) clearTimeout(persistTimer) @@ -70,6 +78,8 @@ function createWindow(): void { win.on('move', schedulePersist) win.on('maximize', schedulePersist) win.on('unmaximize', schedulePersist) + win.on('enter-full-screen', schedulePersist) + win.on('leave-full-screen', schedulePersist) win.on('close', () => { if (persistTimer) clearTimeout(persistTimer) persistBounds() // flush synchronously before the window goes away diff --git a/src/main/store/windowStateStore.ts b/src/main/store/windowStateStore.ts index e14ce8c..2af2d8e 100644 --- a/src/main/store/windowStateStore.ts +++ b/src/main/store/windowStateStore.ts @@ -5,10 +5,12 @@ import type { WindowBounds } from './windowStateCore' interface WindowStateFile { version: 1 - /** Last *normal* (un-maximized) bounds; null until the user resizes once. */ + /** Last *normal* (un-maximized, un-fullscreen) bounds; null until first resize. */ bounds: WindowBounds | null /** Re-maximize on next launch if the window was maximized at close. */ isMaximized: boolean + /** Re-enter native full screen on next launch if it was full screen at close. */ + isFullScreen: boolean } /** @@ -19,7 +21,7 @@ interface WindowStateFile { */ class WindowStateStore { private filePath = '' - private data: WindowStateFile = { version: 1, bounds: null, isMaximized: false } + private data: WindowStateFile = { version: 1, bounds: null, isMaximized: false, isFullScreen: false } init(): void { this.filePath = join(app.getPath('userData'), 'window-state.json') @@ -29,7 +31,8 @@ class WindowStateStore { this.data = { version: 1, bounds: parsed.bounds ?? null, - isMaximized: parsed.isMaximized ?? false + isMaximized: parsed.isMaximized ?? false, + isFullScreen: parsed.isFullScreen ?? false } } catch { // Corrupt file → fall back to defaults (window state is non-critical). @@ -41,8 +44,13 @@ class WindowStateStore { return this.data } - save(state: { bounds: WindowBounds; isMaximized: boolean }): void { - this.data = { version: 1, bounds: state.bounds, isMaximized: state.isMaximized } + save(state: { bounds: WindowBounds; isMaximized: boolean; isFullScreen: boolean }): void { + this.data = { + version: 1, + bounds: state.bounds, + isMaximized: state.isMaximized, + isFullScreen: state.isFullScreen + } try { writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), 'utf8') } catch {