Add settings: theme, zoom controls, keyboard shortcuts (step 8)
Theme (light/dark/system) persisted via Zustand's persist middleware to localStorage, applied by toggling .dark on <html> — the CSS custom variant was already wired up from step 1 but nothing had driven it until now, so the app was silently stuck in light mode regardless of OS appearance. "System" resolves via matchMedia and stays live via a change listener. Zoom in/out/reset go through a small main-process IPC (webContents.setZoomLevel) since the renderer can't reach Electron's webFrame directly with contextIsolation on. Keyboard shortcuts (Cmd/Ctrl+N, +comma, +=/-/0) are a single window-level listener mounted once at the app root. Verified end-to-end via CDP: confirmed system theme correctly resolved dark to match the real OS appearance at test time, manually switching to Light actually removed the .dark class and persisted to localStorage, zoom in/reset both confirmed via the IPC's returned zoom level (cumulative across calls), and both keyboard shortcuts (dispatched as real KeyboardEvents) correctly opened their modals. Also added a matchMedia polyfill to the Vitest setup file — jsdom doesn't implement it, which broke App.test.tsx once useAppliedTheme started running on every render.
This commit is contained in:
@@ -8,6 +8,7 @@ import { registerDatabaseIpc } from './ipc/database'
|
||||
import { registerAddonsIpc } from './ipc/addons'
|
||||
import { registerLogsIpc } from './ipc/logs'
|
||||
import { registerCreateIpc } from './ipc/create'
|
||||
import { registerWindowIpc } from './ipc/window'
|
||||
import { killAllRunningCommands } from './commandRunner'
|
||||
|
||||
function createWindow(): void {
|
||||
@@ -63,6 +64,7 @@ app.whenReady().then(() => {
|
||||
registerAddonsIpc()
|
||||
registerLogsIpc()
|
||||
registerCreateIpc()
|
||||
registerWindowIpc()
|
||||
|
||||
createWindow()
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ipcMain } from 'electron'
|
||||
|
||||
const MIN_ZOOM = -3
|
||||
const MAX_ZOOM = 5
|
||||
|
||||
export function registerWindowIpc(): void {
|
||||
ipcMain.handle('window:zoomIn', (event) => {
|
||||
const level = Math.min(MAX_ZOOM, event.sender.getZoomLevel() + 0.5)
|
||||
event.sender.setZoomLevel(level)
|
||||
return level
|
||||
})
|
||||
|
||||
ipcMain.handle('window:zoomOut', (event) => {
|
||||
const level = Math.max(MIN_ZOOM, event.sender.getZoomLevel() - 0.5)
|
||||
event.sender.setZoomLevel(level)
|
||||
return level
|
||||
})
|
||||
|
||||
ipcMain.handle('window:zoomReset', (event) => {
|
||||
event.sender.setZoomLevel(0)
|
||||
return 0
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user