From 3d812eb9bf54c39e80098bb6ce55f8498a3d3c8c Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 29 Oct 2026 00:08:48 -0700 Subject: [PATCH] =?UTF-8?q?fix(keybindings):=20show=20=E2=87=A7=20for=20Sh?= =?UTF-8?q?ift=20in=20Windows/Linux=20shortcut=20labels=20(#35754)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(keybindings): show the ⇧ glyph for Shift on Windows and Linux Spelled-out Shift made non-mac shortcut labels long (Ctrl+Shift+Backspace). * test(menu): expect the ⇧ glyph in non-mac app menu hints; align double-tap hint --- src/main/menu/register-app-menu.test.ts | 6 +++--- .../src/components/link-actions/LinkActionPopover.tsx | 2 +- .../src/components/link-routing-preference-dialog.tsx | 2 +- .../native-chat/use-native-chat-context-menu.test.tsx | 2 +- .../src/components/plugins/list/PluginCardArt.tsx | 2 +- .../src/components/settings/ShortcutCommandBlock.tsx | 2 +- .../terminal-pane/TerminalContextMenu.test.tsx | 10 +++++----- src/renderer/src/hooks/shortcut-label-cache.test.tsx | 2 +- src/shared/keybindings-default-bindings.test.ts | 2 +- src/shared/keybindings-double-tap.test.ts | 4 ++-- src/shared/keybindings-parsing.test.ts | 4 ++-- src/shared/keybindings/formatting.ts | 4 ++-- 12 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/menu/register-app-menu.test.ts b/src/main/menu/register-app-menu.test.ts index 3332412515a..b755841724a 100644 --- a/src/main/menu/register-app-menu.test.ts +++ b/src/main/menu/register-app-menu.test.ts @@ -128,7 +128,7 @@ describe('registerAppMenu', () => { expect(buildFromTemplateMock).toHaveBeenCalledTimes(1) const viewSubmenu = getSubmenu(getTemplate(), 'View') - const expectedForceReloadLabel = `Force Reload\t${isMac ? '⌘⇧R' : 'Ctrl+Shift+R'}` + const expectedForceReloadLabel = `Force Reload\t${isMac ? '⌘⇧R' : 'Ctrl+⇧+R'}` expect(viewSubmenu).toEqual( expect.arrayContaining([expect.objectContaining({ label: 'Reload' })]) @@ -254,7 +254,7 @@ describe('registerAppMenu', () => { registerAppMenu(buildMenuOptions()) const viewSubmenu = getSubmenu(getTemplate(), 'View') - const expectedLabel = `Open Worktree Palette\t${isMac ? '⌘J' : 'Ctrl+Shift+J'}` + const expectedLabel = `Open Worktree Palette\t${isMac ? '⌘J' : 'Ctrl+⇧+J'}` const paletteItem = viewSubmenu.find((item) => item.label === expectedLabel) expect(paletteItem).toBeDefined() @@ -461,7 +461,7 @@ describe('registerAppMenu', () => { expect(template.find((item) => item.label === 'Orca')).toBeUndefined() const fileLabels = getSubmenu(template, 'File').map((item) => item.label) - expect(fileLabels).not.toContain(`Export as PDF...\t${isMac ? '⌘⇧E' : 'Ctrl+Shift+E'}`) + expect(fileLabels).not.toContain(`Export as PDF...\t${isMac ? '⌘⇧E' : 'Ctrl+⇧+E'}`) expect(fileLabels[0]).toBe('Settings') expect(fileLabels).toEqual(expect.arrayContaining(['Settings', 'Exit'])) diff --git a/src/renderer/src/components/link-actions/LinkActionPopover.tsx b/src/renderer/src/components/link-actions/LinkActionPopover.tsx index 8b787c56e4b..c78044502ac 100644 --- a/src/renderer/src/components/link-actions/LinkActionPopover.tsx +++ b/src/renderer/src/components/link-actions/LinkActionPopover.tsx @@ -30,7 +30,7 @@ function ActionRow({ const isMac = navigator.userAgent.includes('Mac') const keys = shortcut === 'alternate' - ? [isMac ? '⇧' : 'Shift', isMac ? '⌘' : 'Ctrl', 'Click'] + ? ['⇧', isMac ? '⌘' : 'Ctrl', 'Click'] : shortcut === 'primary' ? [isMac ? '⌘' : 'Ctrl', 'Click'] : null diff --git a/src/renderer/src/components/link-routing-preference-dialog.tsx b/src/renderer/src/components/link-routing-preference-dialog.tsx index 27a969c412c..edb93379b87 100644 --- a/src/renderer/src/components/link-routing-preference-dialog.tsx +++ b/src/renderer/src/components/link-routing-preference-dialog.tsx @@ -62,7 +62,7 @@ export function LinkRoutingPreferenceDialogProvider({ const displayHost = displayHostForUrl(displayedRequest?.options.url) const openLinksInAppDefault = displayedRequest?.options.openLinksInAppDefault === true const isMac = navigator.userAgent.includes('Mac') - const systemBrowserShortcutKeys = isMac ? ['⇧', '⌘'] : ['Shift', 'Ctrl'] + const systemBrowserShortcutKeys = isMac ? ['⇧', '⌘'] : ['⇧', 'Ctrl'] useEffect(() => { setContextualToursBlockingSurfaceVisible(activeRequest !== null) diff --git a/src/renderer/src/components/native-chat/use-native-chat-context-menu.test.tsx b/src/renderer/src/components/native-chat/use-native-chat-context-menu.test.tsx index 4e8f2354f6f..cfaff0ac528 100644 --- a/src/renderer/src/components/native-chat/use-native-chat-context-menu.test.tsx +++ b/src/renderer/src/components/native-chat/use-native-chat-context-menu.test.tsx @@ -202,7 +202,7 @@ it('updates nominal toggle labels, including an unassigned binding', () => { vi.stubGlobal('navigator', { userAgent: 'Windows NT 10.0' }) useAppStore.setState({ keybindings: {} }) const view = render( {}} />) - expect(view.container.textContent).toContain('Ctrl+Shift+J') + expect(view.container.textContent).toContain('Ctrl+⇧+J') act(() => useAppStore.setState({ keybindings: { 'terminal.toggleNativeChat': ['Mod+Alt+Y', 'Mod+Alt+K'] } diff --git a/src/renderer/src/components/plugins/list/PluginCardArt.tsx b/src/renderer/src/components/plugins/list/PluginCardArt.tsx index 3e56212d599..61bda8ea79a 100644 --- a/src/renderer/src/components/plugins/list/PluginCardArt.tsx +++ b/src/renderer/src/components/plugins/list/PluginCardArt.tsx @@ -136,7 +136,7 @@ function KeycapArt({ shortcut }: { shortcut: string | null }): React.JSX.Element ? formatKeybinding(shortcut, platform) : isMac() ? ['⌘', '⌥', '⇧'] - : ['Ctrl', 'Alt', 'Shift'] + : ['Ctrl', 'Alt', '⇧'] return (
{keys.map((key, index) => ( diff --git a/src/renderer/src/components/settings/ShortcutCommandBlock.tsx b/src/renderer/src/components/settings/ShortcutCommandBlock.tsx index c4ab9670ef4..5dfff97e6df 100644 --- a/src/renderer/src/components/settings/ShortcutCommandBlock.tsx +++ b/src/renderer/src/components/settings/ShortcutCommandBlock.tsx @@ -72,7 +72,7 @@ export function ShortcutCommandBlock({ const isDigitIndex = isDigitIndexActionId(item.id) const canEnable = isDisabled && previousBindings.length > 0 - const doubleTapHint = platform === 'darwin' ? '⇧⇧' : 'Shift Shift' + const doubleTapHint = platform === 'darwin' ? '⇧⇧' : '⇧ ⇧' const recordingMessage = translate( 'auto.components.settings.ShortcutCommandBlock.eb72c52c28', 'Press a shortcut, or double-tap a modifier (e.g. {{value0}}). Esc cancels.', diff --git a/src/renderer/src/components/terminal-pane/TerminalContextMenu.test.tsx b/src/renderer/src/components/terminal-pane/TerminalContextMenu.test.tsx index 4ff23e94c1e..2b78e2ece31 100644 --- a/src/renderer/src/components/terminal-pane/TerminalContextMenu.test.tsx +++ b/src/renderer/src/components/terminal-pane/TerminalContextMenu.test.tsx @@ -208,7 +208,7 @@ describe('TerminalContextMenu', () => { it('uses nominal primary toggle labels, including default and unbound', () => { for (const [bindings, label] of [ - [undefined, 'Ctrl+Shift+J'], + [undefined, 'Ctrl+⇧+J'], [['Mod+Alt+Y', 'Mod+Alt+K'], 'Ctrl+Alt+Y'], [[], null] ] as const) { @@ -236,11 +236,11 @@ describe('TerminalContextMenu', () => { renderMenu({ keybindings }) - expect(shortcuts.list).toContain('Ctrl+Shift+C') - expect(shortcuts.list).toContain('Ctrl+Shift+A') + expect(shortcuts.list).toContain('Ctrl+⇧+C') + expect(shortcuts.list).toContain('Ctrl+⇧+A') expect(shortcuts.list).toContain('Ctrl+V') - expect(shortcuts.list).toContain('Ctrl+Shift+D') - expect(shortcuts.list).toContain('Alt+Shift+D') + expect(shortcuts.list).toContain('Ctrl+⇧+D') + expect(shortcuts.list).toContain('Alt+⇧+D') expect(shortcuts.list.some((shortcut) => shortcut.includes(','))).toBe(false) }) diff --git a/src/renderer/src/hooks/shortcut-label-cache.test.tsx b/src/renderer/src/hooks/shortcut-label-cache.test.tsx index 89936b361dc..79749337f88 100644 --- a/src/renderer/src/hooks/shortcut-label-cache.test.tsx +++ b/src/renderer/src/hooks/shortcut-label-cache.test.tsx @@ -111,7 +111,7 @@ describe('shortcut label memoization', () => { const overrides = overridesFor('Mod+Shift+K') expect(formatShortcutLabel('tab.close', overrides)).toBe('⌘⇧K') platformRef.current = 'win32' - expect(formatShortcutLabel('tab.close', overrides)).toBe('Ctrl+Shift+K') + expect(formatShortcutLabel('tab.close', overrides)).toBe('Ctrl+⇧+K') platformRef.current = 'darwin' expect(formatShortcutLabel('tab.close', overrides)).toBe('⌘⇧K') }) diff --git a/src/shared/keybindings-default-bindings.test.ts b/src/shared/keybindings-default-bindings.test.ts index 4a825bd1b04..e54848399fe 100644 --- a/src/shared/keybindings-default-bindings.test.ts +++ b/src/shared/keybindings-default-bindings.test.ts @@ -53,7 +53,7 @@ describe('keybindings', () => { 'Mod+Shift+A' ]) expect(formatKeybindingList(['Mod+Shift+A'], 'darwin')).toBe('⌘⇧A') - expect(formatKeybindingList(['Mod+Shift+A'], 'linux')).toBe('Ctrl+Shift+A') + expect(formatKeybindingList(['Mod+Shift+A'], 'linux')).toBe('Ctrl+⇧+A') const macChord = { key: 'a', diff --git a/src/shared/keybindings-double-tap.test.ts b/src/shared/keybindings-double-tap.test.ts index a7bbcc53df5..af5a1e90c2b 100644 --- a/src/shared/keybindings-double-tap.test.ts +++ b/src/shared/keybindings-double-tap.test.ts @@ -111,7 +111,7 @@ describe('keybindings', () => { it('formats double-tap bindings as the modifier glyph twice', () => { expect(formatKeybinding('DoubleTap+Shift', 'darwin')).toEqual(['⇧', '⇧']) - expect(formatKeybinding('DoubleTap+Shift', 'linux')).toEqual(['Shift', 'Shift']) + expect(formatKeybinding('DoubleTap+Shift', 'linux')).toEqual(['⇧', '⇧']) expect(formatKeybinding('DoubleTap+Mod', 'darwin')).toEqual(['⌘', '⌘']) expect(formatKeybinding('DoubleTap+Mod', 'win32')).toEqual(['Ctrl', 'Ctrl']) expect(formatKeybinding('DoubleTap+Cmd', 'win32')).toEqual(['Cmd', 'Cmd']) @@ -119,7 +119,7 @@ describe('keybindings', () => { // Ctrl's glyph ⌃ diverges from Mod's ⌘ on Mac, so cover it explicitly. expect(formatKeybinding('DoubleTap+Ctrl', 'darwin')).toEqual(['⌃', '⌃']) expect(formatKeybindingList(['DoubleTap+Shift'], 'darwin')).toBe('⇧ ⇧') - expect(formatKeybindingList(['DoubleTap+Shift'], 'linux')).toBe('Shift Shift') + expect(formatKeybindingList(['DoubleTap+Shift'], 'linux')).toBe('⇧ ⇧') }) it('reports conflicts across two double-tap bindings', () => { diff --git a/src/shared/keybindings-parsing.test.ts b/src/shared/keybindings-parsing.test.ts index 0477e0b996f..f8be1ee704a 100644 --- a/src/shared/keybindings-parsing.test.ts +++ b/src/shared/keybindings-parsing.test.ts @@ -169,9 +169,9 @@ describe('keybindings', () => { it('formats keybindings with platform labels', () => { expect(formatKeybindingList(['Mod+Shift+J'], 'darwin')).toBe('⌘⇧J') - expect(formatKeybindingList(['Mod+Shift+J'], 'linux')).toBe('Ctrl+Shift+J') + expect(formatKeybindingList(['Mod+Shift+J'], 'linux')).toBe('Ctrl+⇧+J') expect(formatKeybindingList(['Mod+Shift+Backspace'], 'darwin')).toBe('⌘⇧⌫') - expect(formatKeybindingList(['Mod+Shift+Backspace'], 'linux')).toBe('Ctrl+Shift+Backspace') + expect(formatKeybindingList(['Mod+Shift+Backspace'], 'linux')).toBe('Ctrl+⇧+Backspace') expect(formatKeybindingList([], 'win32')).toBe('Unassigned') }) diff --git a/src/shared/keybindings/formatting.ts b/src/shared/keybindings/formatting.ts index 387b07363a0..2a40eaf4c39 100644 --- a/src/shared/keybindings/formatting.ts +++ b/src/shared/keybindings/formatting.ts @@ -32,7 +32,7 @@ function formatModifierGlyph(modifier: ModifierToken, isMac: boolean): string { case 'Alt': return isMac ? '⌥' : 'Alt' case 'Shift': - return isMac ? '⇧' : 'Shift' + return '⇧' } } @@ -60,7 +60,7 @@ export function formatKeybinding(binding: string, platform: NodeJS.Platform): st parts.push(isMac ? '⌥' : 'Alt') } if (parsed.shift) { - parts.push(isMac ? '⇧' : 'Shift') + parts.push('⇧') } parts.push(formatKeyToken(parsed.key, isMac)) return parts