From b912a31e0099a50838fc2b10ffbc936bf8f2917e Mon Sep 17 00:00:00 2001 From: Jubayer Hoque Date: Sat, 7 Nov 2026 03:48:20 +0100 Subject: [PATCH] feat: add Cmd-B sidebar toggle shortcut (#1775) Co-authored-by: Dr. Lukas Metzger Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- packages/plugin-sdk/src/manifest.ts | 1 + src/ui/locales/en.json | 3 +- .../shell/controller/use-shell-keybindings.ts | 36 +++++++++++++++---- src/ui/shell/keybinding-registry.ts | 20 +++++++++++ .../tests/shell/keybinding-registry.test.ts | 8 +++++ 5 files changed, 61 insertions(+), 7 deletions(-) diff --git a/packages/plugin-sdk/src/manifest.ts b/packages/plugin-sdk/src/manifest.ts index 281fdb6c5..81653bbd3 100644 --- a/packages/plugin-sdk/src/manifest.ts +++ b/packages/plugin-sdk/src/manifest.ts @@ -631,6 +631,7 @@ export const CORE_KEYBINDING_ACTIONS: readonly string[] = [ "previousTab", "openCommandPalette", "reconnectSession", + "toggleSidebar", ]; /** diff --git a/src/ui/locales/en.json b/src/ui/locales/en.json index f2dc5625d..9b9a872e1 100644 --- a/src/ui/locales/en.json +++ b/src/ui/locales/en.json @@ -1054,7 +1054,8 @@ "actionNextTab": "Switch to next tab", "actionPreviousTab": "Switch to previous tab", "actionOpenCommandPalette": "Open quick search", - "actionReconnectSession": "Reconnect current session" + "actionReconnectSession": "Reconnect current session", + "actionToggleSidebar": "Toggle sidebar" }, "userProfile": { "preset_simple": "Simple", diff --git a/src/ui/shell/controller/use-shell-keybindings.ts b/src/ui/shell/controller/use-shell-keybindings.ts index 08bba29f6..407343378 100644 --- a/src/ui/shell/controller/use-shell-keybindings.ts +++ b/src/ui/shell/controller/use-shell-keybindings.ts @@ -8,7 +8,10 @@ import { } from "@/lib/app-keyboard-shortcuts"; import { getUserPreferences } from "@/main-axios"; import { parseCustomKeybindings } from "@/api/open-tabs-api"; -import { findMatchingKeybinding } from "@/lib/keybinding-match"; +import { + eventMatchesCombo, + findMatchingKeybinding, +} from "@/lib/keybinding-match"; import type { CustomKeybinding, KeybindingActionType, @@ -16,6 +19,7 @@ import type { import { GLOBAL_KEYBINDING_EVENT, getKeybindingAction, + listKeybindingDefaults, runKeybindingAction, } from "@/shell/keybinding-registry"; import { globalShortcutHandler } from "@/lib/global-shortcut-handler"; @@ -53,7 +57,7 @@ export function useShellKeybindings({ if (cancelled) return; globalKeybindingsRef.current = parseCustomKeybindings( prefs.customKeybindings, - ).filter((binding) => binding.enabled); + ); }) .catch(() => {}); }; @@ -78,6 +82,10 @@ export function useShellKeybindings({ (termRef?.current as TabHandle | null)?.reconnect?.(); return; } + if (type === "toggleSidebar") { + regions.left.setOpen((open) => !open); + return; + } const currentTabs = tabsRef.current.filter( (tab) => !tab.parentSplitTabId, ); @@ -97,16 +105,32 @@ export function useShellKeybindings({ ) return; // Only actions that run anywhere; a terminal's own are its to handle. + const customBindings = globalKeybindingsRef.current; const binding = findMatchingKeybinding( event, - globalKeybindingsRef.current.filter( - (entry) => getKeybindingAction(entry.action.type)?.scope === "global", + customBindings.filter( + (entry) => + entry.enabled && + getKeybindingAction(entry.action.type)?.scope === "global", ), ); - if (!binding) return; + if (binding) { + event.preventDefault(); + event.stopPropagation(); + runKeybindingAction(binding.action); + return; + } + + const defaultBinding = listKeybindingDefaults().find( + (entry) => + !customBindings.some( + (custom) => custom.overridesDefaultId === entry.id, + ) && eventMatchesCombo(event, entry.combo), + ); + if (!defaultBinding || !getKeybindingAction(defaultBinding.id)) return; event.preventDefault(); event.stopPropagation(); - runKeybindingAction(binding.action); + runKeybindingAction({ type: defaultBinding.id }); }; const handleAction = (event: Event) => runAction( diff --git a/src/ui/shell/keybinding-registry.ts b/src/ui/shell/keybinding-registry.ts index 764143ab1..7147df1ca 100644 --- a/src/ui/shell/keybinding-registry.ts +++ b/src/ui/shell/keybinding-registry.ts @@ -84,6 +84,11 @@ const SHELL_ACTIONS: KeybindingActionDef[] = [ "newUi.sidebar.keybindings.actionReconnectSession", 1003, ), + shellAction( + "toggleSidebar", + "newUi.sidebar.keybindings.actionToggleSidebar", + 1004, + ), ]; export const SHELL_KEYBINDING_ACTION_IDS = SHELL_ACTIONS.map( @@ -97,6 +102,21 @@ function install(): void { for (const action of SHELL_ACTIONS) { if (!actions.get(action.id)) actions.register(action); } + if (!defaults.get("toggleSidebar")) { + defaults.register({ + id: "toggleSidebar", + combo: { + key: "KeyB", + isCode: true, + ctrl: false, + alt: false, + shift: false, + meta: true, + }, + descriptionKey: "newUi.sidebar.keybindings.actionToggleSidebar", + order: 1004, + }); + } } install(); diff --git a/src/ui/tests/shell/keybinding-registry.test.ts b/src/ui/tests/shell/keybinding-registry.test.ts index 98829e755..e018c722e 100644 --- a/src/ui/tests/shell/keybinding-registry.test.ts +++ b/src/ui/tests/shell/keybinding-registry.test.ts @@ -4,6 +4,7 @@ import { SHELL_KEYBINDING_ACTION_IDS, getKeybindingAction, listKeybindingActions, + listKeybindingDefaults, registerKeybindingAction, resetKeybindingRegistry, runKeybindingAction, @@ -17,6 +18,13 @@ describe("keybinding registry", () => { SHELL_KEYBINDING_ACTION_IDS, ); expect(getKeybindingAction("nextTab")?.scope).toBe("global"); + expect(getKeybindingAction("toggleSidebar")?.scope).toBe("global"); + expect(listKeybindingDefaults()).toEqual([ + expect.objectContaining({ + id: "toggleSidebar", + combo: expect.objectContaining({ key: "KeyB", meta: true }), + }), + ]); }); it("runs a shell action by telling the shell", () => {