@@ -60,6 +60,9 @@ const CONFIRM_CHANNEL = "desktop:confirm";
6060const SET_THEME_CHANNEL = "desktop:set-theme" ;
6161const SET_SIDEBAR_OPACITY_CHANNEL = "desktop:set-sidebar-opacity" ;
6262const SET_WINDOW_BUTTON_VISIBILITY_CHANNEL = "desktop:set-window-button-visibility" ;
63+ const SET_ZOOM_FACTOR_CHANNEL = "desktop:set-zoom-factor" ;
64+ const ZOOM_MIN = 0.75 ;
65+ const ZOOM_MAX = 1.75 ;
6366const CONTEXT_MENU_CHANNEL = "desktop:context-menu" ;
6467const OPEN_EXTERNAL_CHANNEL = "desktop:open-external" ;
6568const MENU_ACTION_CHANNEL = "desktop:menu-action" ;
@@ -648,10 +651,30 @@ function configureApplicationMenu(): void {
648651 { role : "forceReload" } ,
649652 { role : "toggleDevTools" } ,
650653 { type : "separator" } ,
651- { role : "resetZoom" } ,
652- { role : "zoomIn" , accelerator : "CmdOrCtrl+=" } ,
653- { role : "zoomIn" , accelerator : "CmdOrCtrl+Plus" , visible : false } ,
654- { role : "zoomOut" } ,
654+ // Dispatch to the renderer instead of using Electron's role-based zoom.
655+ // The renderer owns the persisted zoom factor; native roles would
656+ // bypass our storage and the two would drift.
657+ {
658+ label : "Actual Size" ,
659+ accelerator : "CmdOrCtrl+0" ,
660+ click : ( ) => dispatchMenuAction ( "view-zoom-reset" ) ,
661+ } ,
662+ {
663+ label : "Zoom In" ,
664+ accelerator : "CmdOrCtrl+=" ,
665+ click : ( ) => dispatchMenuAction ( "view-zoom-in" ) ,
666+ } ,
667+ {
668+ label : "Zoom In" ,
669+ accelerator : "CmdOrCtrl+Plus" ,
670+ visible : false ,
671+ click : ( ) => dispatchMenuAction ( "view-zoom-in" ) ,
672+ } ,
673+ {
674+ label : "Zoom Out" ,
675+ accelerator : "CmdOrCtrl+-" ,
676+ click : ( ) => dispatchMenuAction ( "view-zoom-out" ) ,
677+ } ,
655678 { type : "separator" } ,
656679 { role : "togglefullscreen" } ,
657680 ] ,
@@ -1195,6 +1218,19 @@ function registerIpcHandlers(): void {
11951218 // applies the value through a CSS custom-property.
11961219 } ) ;
11971220
1221+ ipcMain . removeHandler ( SET_ZOOM_FACTOR_CHANNEL ) ;
1222+ ipcMain . handle ( SET_ZOOM_FACTOR_CHANNEL , async ( event , rawFactor : unknown ) => {
1223+ // Scale the requesting webContents. We clamp to the same [0.75, 1.75]
1224+ // range the renderer enforces so a malicious or buggy call can't drive
1225+ // the UI into an unreadable state.
1226+ const contents = event . sender ;
1227+ if ( ! contents || typeof contents . setZoomFactor !== "function" ) return ;
1228+ const numeric = typeof rawFactor === "number" ? rawFactor : Number ( rawFactor ) ;
1229+ const factor = Number . isFinite ( numeric ) ? numeric : 1 ;
1230+ const clamped = Math . max ( ZOOM_MIN , Math . min ( ZOOM_MAX , factor ) ) ;
1231+ contents . setZoomFactor ( clamped ) ;
1232+ } ) ;
1233+
11981234 ipcMain . removeHandler ( SET_WINDOW_BUTTON_VISIBILITY_CHANNEL ) ;
11991235 ipcMain . handle ( SET_WINDOW_BUTTON_VISIBILITY_CHANNEL , async ( event , rawVisible : unknown ) => {
12001236 if ( process . platform !== "darwin" || typeof rawVisible !== "boolean" ) {
0 commit comments