1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

fix(desktop): persist zoom state across vault locks (#17217)

* fix(desktop): persist zoom state across vault locks

Replace role-based zoom menu items with custom click handlers to fix
zoom persistence issue where keyboard shortcuts (Ctrl+/-/0, Cmd+/-/0)
weren't saving zoom changes after vault lock.

Changes:
- Add custom click handlers for zoomIn/zoomOut/resetZoom menu items
- Add WindowMain.saveZoomFactor() method for immediate persistence
- Pass WindowMain dependency to ViewMenu constructor
- Update zoom-changed event comment to clarify coverage
- Maintain existing mouse wheel zoom persistence via zoom-changed event

Fixes: PM-791
Fixes: https://github.com/bitwarden/clients/issues/4675

* chore: update to macos-15 runners

* review: downgrade macos build runner to 14

* review: align step with min zoom level

* cleanup from merge
This commit is contained in:
Addison Beck
2025-11-14 11:54:08 -05:00
committed by GitHub
parent 099a4a0f03
commit 3b97093338
3 changed files with 35 additions and 6 deletions

View File

@@ -6,6 +6,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { isDev } from "../../utils";
import { WindowMain } from "../window.main";
import { IMenubarMenu } from "./menubar";
@@ -42,11 +43,18 @@ export class ViewMenu implements IMenubarMenu {
private readonly _i18nService: I18nService;
private readonly _messagingService: MessagingService;
private readonly _isLocked: boolean;
private readonly _windowMain: WindowMain;
constructor(i18nService: I18nService, messagingService: MessagingService, isLocked: boolean) {
constructor(
i18nService: I18nService,
messagingService: MessagingService,
isLocked: boolean,
windowMain: WindowMain,
) {
this._i18nService = i18nService;
this._messagingService = messagingService;
this._isLocked = isLocked;
this._windowMain = windowMain;
}
private get searchVault(): MenuItemConstructorOptions {
@@ -86,7 +94,12 @@ export class ViewMenu implements IMenubarMenu {
return {
id: "zoomIn",
label: this.localize("zoomIn"),
role: "zoomIn",
click: async () => {
const currentZoom = this._windowMain.win.webContents.zoomFactor;
const newZoom = currentZoom + 0.1;
this._windowMain.win.webContents.zoomFactor = newZoom;
await this._windowMain.saveZoomFactor(newZoom);
},
accelerator: "CmdOrCtrl+=",
};
}
@@ -95,7 +108,12 @@ export class ViewMenu implements IMenubarMenu {
return {
id: "zoomOut",
label: this.localize("zoomOut"),
role: "zoomOut",
click: async () => {
const currentZoom = this._windowMain.win.webContents.zoomFactor;
const newZoom = Math.max(0.2, currentZoom - 0.1);
this._windowMain.win.webContents.zoomFactor = newZoom;
await this._windowMain.saveZoomFactor(newZoom);
},
accelerator: "CmdOrCtrl+-",
};
}
@@ -104,7 +122,11 @@ export class ViewMenu implements IMenubarMenu {
return {
id: "resetZoom",
label: this.localize("resetZoom"),
role: "resetZoom",
click: async () => {
const newZoom = 1.0;
this._windowMain.win.webContents.zoomFactor = newZoom;
await this._windowMain.saveZoomFactor(newZoom);
},
accelerator: "CmdOrCtrl+0",
};
}

View File

@@ -86,7 +86,7 @@ export class Menubar {
updateRequest?.restrictedCipherTypes,
),
new EditMenu(i18nService, messagingService, isLocked),
new ViewMenu(i18nService, messagingService, isLocked),
new ViewMenu(i18nService, messagingService, isLocked, windowMain),
new AccountMenu(
i18nService,
messagingService,

View File

@@ -303,7 +303,9 @@ export class WindowMain {
this.win.webContents.zoomFactor = this.windowStates[mainWindowSizeKey].zoomFactor ?? 1.0;
});
// Persist zoom changes immediately when user zooms in/out or resets zoom
// Persist zoom changes from mouse wheel and programmatic zoom operations
// NOTE: This event does NOT fire for keyboard shortcuts (Ctrl+/-/0, Cmd+/-/0)
// which are handled by custom menu click handlers in ViewMenu
// We can't depend on higher level web events (like close) to do this
// because locking the vault resets window state.
this.win.webContents.on("zoom-changed", async () => {
@@ -432,6 +434,11 @@ export class WindowMain {
await this.desktopSettingsService.setAlwaysOnTop(this.enableAlwaysOnTop);
}
async saveZoomFactor(zoomFactor: number) {
this.windowStates[mainWindowSizeKey].zoomFactor = zoomFactor;
await this.desktopSettingsService.setWindow(this.windowStates[mainWindowSizeKey]);
}
private windowStateChangeHandler(configKey: string, win: BrowserWindow) {
global.clearTimeout(this.windowStateChangeTimer);
this.windowStateChangeTimer = global.setTimeout(async () => {