diff --git a/apps/desktop/electron/main.cjs b/apps/desktop/electron/main.cjs index 35abc987d87..0da63e69c4c 100644 --- a/apps/desktop/electron/main.cjs +++ b/apps/desktop/electron/main.cjs @@ -3137,7 +3137,7 @@ function buildApplicationMenu() { label: 'Actual Size', accelerator: 'CommandOrControl+0', click: () => { - if (mainWindow && !mainWindow.isDestroyed()) mainWindow.webContents.setZoomLevel(0) + setAndPersistZoomLevel(mainWindow, 0) } }, { @@ -3145,8 +3145,7 @@ function buildApplicationMenu() { accelerator: 'CommandOrControl+Plus', click: () => { if (mainWindow && !mainWindow.isDestroyed()) { - const next = Math.min(mainWindow.webContents.getZoomLevel() + 0.1, 9) - mainWindow.webContents.setZoomLevel(next) + setAndPersistZoomLevel(mainWindow, mainWindow.webContents.getZoomLevel() + 0.1) } } }, @@ -3155,8 +3154,7 @@ function buildApplicationMenu() { accelerator: 'CommandOrControl+-', click: () => { if (mainWindow && !mainWindow.isDestroyed()) { - const next = Math.max(mainWindow.webContents.getZoomLevel() - 0.1, -9) - mainWindow.webContents.setZoomLevel(next) + setAndPersistZoomLevel(mainWindow, mainWindow.webContents.getZoomLevel() - 0.1) } } }, @@ -3218,6 +3216,38 @@ function installPreviewShortcut(window) { }) } +// Zoom level is persisted in the renderer's own localStorage (per-origin, +// survives reloads/restarts) rather than a main-process JSON file. The main +// process owns setZoomLevel, so we mirror each change into localStorage and +// read it back on did-finish-load to re-apply after reloads or crash recovery. +const ZOOM_STORAGE_KEY = 'hermes:desktop:zoomLevel' + +function clampZoomLevel(value) { + if (!Number.isFinite(value)) return 0 + return Math.min(Math.max(value, -9), 9) +} + +function setAndPersistZoomLevel(window, zoomLevel) { + if (!window || window.isDestroyed()) return + const next = clampZoomLevel(zoomLevel) + window.webContents.setZoomLevel(next) + window.webContents + .executeJavaScript(`try { localStorage.setItem(${JSON.stringify(ZOOM_STORAGE_KEY)}, ${JSON.stringify(String(next))}) } catch {}`) + .catch(error => rememberLog(`[zoom] persist failed: ${error?.message || error}`)) +} + +function restorePersistedZoomLevel(window) { + if (!window || window.isDestroyed()) return + window.webContents + .executeJavaScript(`(() => { try { return localStorage.getItem(${JSON.stringify(ZOOM_STORAGE_KEY)}) } catch { return null } })()`) + .then(stored => { + if (stored == null || !window || window.isDestroyed()) return + const level = clampZoomLevel(Number(stored)) + window.webContents.setZoomLevel(level) + }) + .catch(error => rememberLog(`[zoom] restore failed: ${error?.message || error}`)) +} + function installZoomShortcuts(window) { // Override Ctrl/Cmd + +/-/0 with half the default zoom step (0.1 vs 0.2). // The menu items handle this on macOS (where the menu is always present), @@ -3231,15 +3261,13 @@ function installZoomShortcuts(window) { const key = input.key if (key === '0') { event.preventDefault() - window.webContents.setZoomLevel(0) + setAndPersistZoomLevel(window, 0) } else if (key === '=' || key === '+') { event.preventDefault() - const next = Math.min(window.webContents.getZoomLevel() + ZOOM_STEP, 9) - window.webContents.setZoomLevel(next) + setAndPersistZoomLevel(window, window.webContents.getZoomLevel() + ZOOM_STEP) } else if (key === '-') { event.preventDefault() - const next = Math.max(window.webContents.getZoomLevel() - ZOOM_STEP, -9) - window.webContents.setZoomLevel(next) + setAndPersistZoomLevel(window, window.webContents.getZoomLevel() - ZOOM_STEP) } }) } @@ -4730,6 +4758,7 @@ function createWindow() { } mainWindow.webContents.once('did-finish-load', () => { + restorePersistedZoomLevel(mainWindow) broadcastBootProgress() sendWindowStateChanged() startHermes().catch(error => rememberLog(error.stack || error.message))