diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs index a88d10afb..f963a8ef9 100644 --- a/src-tauri/src/menu.rs +++ b/src-tauri/src/menu.rs @@ -67,8 +67,9 @@ pub(crate) fn build_menu( .build(handle)?; let check_updates_item = MenuItemBuilder::with_id("check_for_updates", "Check for Updates...").build(handle)?; - let settings_item = - MenuItemBuilder::with_id("file_open_settings", "Settings...").build(handle)?; + let settings_item = MenuItemBuilder::with_id("file_open_settings", "Settings...") + .accelerator("CmdOrCtrl+,") + .build(handle)?; let app_menu = Submenu::with_items( handle, app_name.clone(), diff --git a/src/features/settings/components/SettingsView.test.tsx b/src/features/settings/components/SettingsView.test.tsx index 00585cf14..7f0e32769 100644 --- a/src/features/settings/components/SettingsView.test.tsx +++ b/src/features/settings/components/SettingsView.test.tsx @@ -91,6 +91,10 @@ const renderDisplaySection = ( options.onUpdateAppSettings ?? vi.fn().mockResolvedValue(undefined); const onToggleTransparency = options.onToggleTransparency ?? vi.fn(); const props: ComponentProps = { + reduceTransparency: options.reduceTransparency ?? false, + onToggleTransparency, + appSettings: { ...baseSettings, ...options.appSettings }, + onUpdateAppSettings, workspaceGroups: [], groupedWorkspaces: [], ungroupedLabel: "Ungrouped", @@ -102,10 +106,6 @@ const renderDisplaySection = ( onMoveWorkspaceGroup: vi.fn().mockResolvedValue(null), onDeleteWorkspaceGroup: vi.fn().mockResolvedValue(null), onAssignWorkspaceGroup: vi.fn().mockResolvedValue(null), - reduceTransparency: options.reduceTransparency ?? false, - onToggleTransparency, - appSettings: { ...baseSettings, ...options.appSettings }, - onUpdateAppSettings, onRunDoctor: vi.fn().mockResolvedValue(createDoctorResult()), onUpdateWorkspaceCodexBin: vi.fn().mockResolvedValue(undefined), scaleShortcutTitle: "Scale shortcut", @@ -263,3 +263,79 @@ describe("SettingsView Display", () => { }); }); }); + +describe("SettingsView Shortcuts", () => { + it("closes on Cmd+W", () => { + const onClose = vi.fn(); + render( + , + ); + + window.dispatchEvent( + new KeyboardEvent("keydown", { key: "w", metaKey: true, bubbles: true }), + ); + + expect(onClose).toHaveBeenCalled(); + }); + + it("closes on Escape", () => { + const onClose = vi.fn(); + render( + , + ); + + window.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true })); + + expect(onClose).toHaveBeenCalled(); + }); +}); diff --git a/src/features/settings/components/SettingsView.tsx b/src/features/settings/components/SettingsView.tsx index 1235d230a..93265ee68 100644 --- a/src/features/settings/components/SettingsView.tsx +++ b/src/features/settings/components/SettingsView.tsx @@ -257,6 +257,33 @@ export function SettingsView({ [groupedWorkspaces], ); + useEffect(() => { + const handleEscape = (event: KeyboardEvent) => { + if (event.defaultPrevented || event.key !== "Escape") { + return; + } + event.preventDefault(); + onClose(); + }; + + const handleCloseShortcut = (event: KeyboardEvent) => { + if (event.defaultPrevented) { + return; + } + if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "w") { + event.preventDefault(); + onClose(); + } + }; + + window.addEventListener("keydown", handleEscape); + window.addEventListener("keydown", handleCloseShortcut); + return () => { + window.removeEventListener("keydown", handleEscape); + window.removeEventListener("keydown", handleCloseShortcut); + }; + }, [onClose]); + useEffect(() => { setCodexPathDraft(appSettings.codexBin ?? ""); }, [appSettings.codexBin]);