From 37560fa1330cae35b68a3b7338c4aaeea1beb3b7 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Fri, 10 Jul 2026 08:03:03 +0800 Subject: [PATCH] Prevent Electron modifier wheel zoom (#1016) --- src/main.tsx | 3 ++ src/ui/lib/electron-wheel-zoom.test.ts | 39 ++++++++++++++++++++++++++ src/ui/lib/electron-wheel-zoom.ts | 20 +++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 src/ui/lib/electron-wheel-zoom.test.ts create mode 100644 src/ui/lib/electron-wheel-zoom.ts diff --git a/src/main.tsx b/src/main.tsx index 00ed6260..1e4c8894 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -10,6 +10,7 @@ import { Toaster } from "@/components/sonner"; import { Auth, getStoredAuth, clearStoredAuth } from "@/auth/Auth"; import { getUserInfo, getCurrentToken, appReadyPromise } from "@/main-axios"; import { applyAccentColor, applyFontSize } from "@/lib/theme"; +import { installElectronWheelZoomGuard } from "@/lib/electron-wheel-zoom"; import type { FontSizeId } from "@/types/ui-types"; import { useServiceWorker } from "@/hooks/use-service-worker"; import { useTranslation } from "react-i18next"; @@ -340,6 +341,8 @@ function RootApp() { return ; } +installElectronWheelZoomGuard(); + prepareClientCacheVersion().finally(() => { createRoot(document.getElementById("root")!).render( diff --git a/src/ui/lib/electron-wheel-zoom.test.ts b/src/ui/lib/electron-wheel-zoom.test.ts new file mode 100644 index 00000000..2be6a5fa --- /dev/null +++ b/src/ui/lib/electron-wheel-zoom.test.ts @@ -0,0 +1,39 @@ +import { afterEach, describe, expect, it } from "vitest"; +import { installElectronWheelZoomGuard } from "./electron-wheel-zoom"; + +const win = window as unknown as Record; +let cleanup: (() => void) | undefined; + +afterEach(() => { + cleanup?.(); + cleanup = undefined; + delete win.IS_ELECTRON; + delete win.electronAPI; +}); + +function dispatchWheel(init?: WheelEventInit): WheelEvent { + const event = new WheelEvent("wheel", { + bubbles: true, + cancelable: true, + ...init, + }); + window.dispatchEvent(event); + return event; +} + +describe("installElectronWheelZoomGuard", () => { + it("does not block ordinary browser wheel events", () => { + cleanup = installElectronWheelZoomGuard(); + + expect(dispatchWheel({ ctrlKey: true }).defaultPrevented).toBe(false); + }); + + it("blocks modifier wheel zoom in Electron", () => { + win.electronAPI = { isElectron: true }; + cleanup = installElectronWheelZoomGuard(); + + expect(dispatchWheel({ metaKey: true }).defaultPrevented).toBe(true); + expect(dispatchWheel({ ctrlKey: true }).defaultPrevented).toBe(true); + expect(dispatchWheel().defaultPrevented).toBe(false); + }); +}); diff --git a/src/ui/lib/electron-wheel-zoom.ts b/src/ui/lib/electron-wheel-zoom.ts new file mode 100644 index 00000000..43374e2e --- /dev/null +++ b/src/ui/lib/electron-wheel-zoom.ts @@ -0,0 +1,20 @@ +import { isElectron } from "./electron"; + +export function installElectronWheelZoomGuard(): () => void { + if (!isElectron()) return () => {}; + + const preventModifierWheelZoom = (event: WheelEvent) => { + if (!event.metaKey && !event.ctrlKey) return; + event.preventDefault(); + }; + + window.addEventListener("wheel", preventModifierWheelZoom, { + capture: true, + passive: false, + }); + + return () => + window.removeEventListener("wheel", preventModifierWheelZoom, { + capture: true, + }); +}