diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e21b465d..5ab66b77f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- Properly map all hotkeys in UI based on the platform [#784](https://github.com/sourcebot-dev/sourcebot/pull/784) + ## [4.10.16] - 2026-01-22 ### Changed diff --git a/packages/web/src/app/[domain]/browse/components/bottomPanel.tsx b/packages/web/src/app/[domain]/browse/components/bottomPanel.tsx index 4a155207a..abd166d40 100644 --- a/packages/web/src/app/[domain]/browse/components/bottomPanel.tsx +++ b/packages/web/src/app/[domain]/browse/components/bottomPanel.tsx @@ -67,7 +67,7 @@ export const BottomPanel = ({ order }: BottomPanelProps) => { > Explore - + diff --git a/packages/web/src/app/[domain]/chat/components/chatSidePanel.tsx b/packages/web/src/app/[domain]/chat/components/chatSidePanel.tsx index ec2fce510..6bb65aa6a 100644 --- a/packages/web/src/app/[domain]/chat/components/chatSidePanel.tsx +++ b/packages/web/src/app/[domain]/chat/components/chatSidePanel.tsx @@ -232,7 +232,7 @@ export const ChatSidePanel = ({ - + Open side panel diff --git a/packages/web/src/app/[domain]/components/searchBar/searchSuggestionsBox.tsx b/packages/web/src/app/[domain]/components/searchBar/searchSuggestionsBox.tsx index f034168ea..2bd82812d 100644 --- a/packages/web/src/app/[domain]/components/searchBar/searchSuggestionsBox.tsx +++ b/packages/web/src/app/[domain]/components/searchBar/searchSuggestionsBox.tsx @@ -411,13 +411,13 @@ const SearchSuggestionsBox = forwardRef(({ Syntax help:

- +
{isFocused && ( - + to select diff --git a/packages/web/src/app/[domain]/components/searchModeSelector.tsx b/packages/web/src/app/[domain]/components/searchModeSelector.tsx index cf63f7341..aaa8f4412 100644 --- a/packages/web/src/app/[domain]/components/searchModeSelector.tsx +++ b/packages/web/src/app/[domain]/components/searchModeSelector.tsx @@ -95,7 +95,7 @@ export const SearchModeSelector = ({ Search
- +
@@ -135,7 +135,7 @@ export const SearchModeSelector = ({
- +
diff --git a/packages/web/src/app/[domain]/components/syntaxReferenceGuideHint.tsx b/packages/web/src/app/[domain]/components/syntaxReferenceGuideHint.tsx index fab0264a5..0581a3edd 100644 --- a/packages/web/src/app/[domain]/components/syntaxReferenceGuideHint.tsx +++ b/packages/web/src/app/[domain]/components/syntaxReferenceGuideHint.tsx @@ -11,7 +11,7 @@ export const SyntaxReferenceGuideHint = () => { className="text-sm cursor-pointer" onClick={() => onOpenChanged(!isOpen)} > - Reference guide: + Reference guide: ) } \ No newline at end of file diff --git a/packages/web/src/app/[domain]/search/components/searchResultsPage.tsx b/packages/web/src/app/[domain]/search/components/searchResultsPage.tsx index c709fadda..0409e0776 100644 --- a/packages/web/src/app/[domain]/search/components/searchResultsPage.tsx +++ b/packages/web/src/app/[domain]/search/components/searchResultsPage.tsx @@ -298,7 +298,7 @@ const PanelGroup = ({ - + Open filter panel diff --git a/packages/web/src/app/components/keyboardShortcutHint.tsx b/packages/web/src/app/components/keyboardShortcutHint.tsx index 383743024..ebb5d082e 100644 --- a/packages/web/src/app/components/keyboardShortcutHint.tsx +++ b/packages/web/src/app/components/keyboardShortcutHint.tsx @@ -4,22 +4,89 @@ import { cn, IS_MAC } from '@/lib/utils' import React, { useMemo } from 'react' interface KeyboardShortcutHintProps { - shortcut: string - label?: string - className?: string + shortcut: string + label?: string + className?: string } /** - * Converts Mac-specific keyboard shortcuts to platform-appropriate shortcuts. - * On Mac: displays the shortcut as-is (e.g., "⌘") - * On Windows/Linux: replaces "⌘" with "Ctrl" + * Maps for converting react-hotkeys syntax to platform-specific symbols. + * Accepts shortcuts like "mod+b", "alt+shift+f12", etc. */ -function getPlatformShortcut(shortcut: string): string { - if (IS_MAC) { - return shortcut; +const MAC_KEY_MAP: Record = { + mod: '⌘', + meta: '⌘', + ctrl: '⌃', + control: '⌃', + alt: '⌥', + option: '⌥', + shift: '⇧', + enter: '↵', + return: '↵', + backspace: '⌫', + delete: '⌦', + escape: '⎋', + esc: '⎋', + tab: '⇥', + space: '␣', + up: '↑', + down: '↓', + left: '←', + right: '→', +}; + +const WINDOWS_KEY_MAP: Record = { + mod: 'Ctrl', + meta: 'Win', + ctrl: 'Ctrl', + control: 'Ctrl', + alt: 'Alt', + option: 'Alt', + shift: 'Shift', + enter: 'Enter', + return: 'Enter', + backspace: 'Backspace', + delete: 'Delete', + escape: 'Esc', + esc: 'Esc', + tab: 'Tab', + space: 'Space', + up: '↑', + down: '↓', + left: '←', + right: '→', +}; + +/** + * Converts a single key from react-hotkeys syntax to platform-appropriate display. + */ +function mapKey(key: string, keyMap: Record): string { + const lowerKey = key.toLowerCase(); + if (keyMap[lowerKey]) { + return keyMap[lowerKey]; + } + // For single letters, keep uppercase + if (key.length === 1) { + return key.toUpperCase(); } - // Replace Mac Command key symbol with Ctrl for non-Mac platforms - return shortcut.replace(/⌘/g, 'Ctrl'); + // For function keys (F1-F12), keep as-is but uppercase + if (/^f\d{1,2}$/i.test(key)) { + return key.toUpperCase(); + } + // Default: return the key with first letter capitalized + return key.charAt(0).toUpperCase() + key.slice(1).toLowerCase(); +} + +/** + * Converts react-hotkeys syntax to platform-appropriate keyboard shortcut display. + * Accepts formats like: "mod+b", "alt+shift+f12", "ctrl enter" + */ +function getPlatformShortcut(shortcut: string): string { + // Split by + or space to handle both "mod+b" and "⌘ B" formats + const keys = shortcut.split(/[+\s]+/).filter(Boolean); + const keyMap = IS_MAC ? MAC_KEY_MAP : WINDOWS_KEY_MAP; + + return keys.map(key => mapKey(key, keyMap)).join(' '); } export function KeyboardShortcutHint({ shortcut, label, className }: KeyboardShortcutHintProps) { diff --git a/packages/web/src/ee/features/codeNav/components/exploreMenu/index.tsx b/packages/web/src/ee/features/codeNav/components/exploreMenu/index.tsx index 30e09b000..0fa8a5c26 100644 --- a/packages/web/src/ee/features/codeNav/components/exploreMenu/index.tsx +++ b/packages/web/src/ee/features/codeNav/components/exploreMenu/index.tsx @@ -172,7 +172,7 @@ export const ExploreMenu = ({ Search all repositories diff --git a/packages/web/src/ee/features/codeNav/components/symbolHoverPopup/index.tsx b/packages/web/src/ee/features/codeNav/components/symbolHoverPopup/index.tsx index a786f600e..2dd86d505 100644 --- a/packages/web/src/ee/features/codeNav/components/symbolHoverPopup/index.tsx +++ b/packages/web/src/ee/features/codeNav/components/symbolHoverPopup/index.tsx @@ -287,7 +287,7 @@ export const SymbolHoverPopup: React.FC = ({ side="bottom" className="flex flex-row items-center gap-2" > - + {`Go to ${symbolInfo.symbolDefinitions && symbolInfo.symbolDefinitions.length > 1 ? "definitions" : "definition"}`} @@ -306,7 +306,7 @@ export const SymbolHoverPopup: React.FC = ({ side="bottom" className="flex flex-row items-center gap-2" > - + Find references diff --git a/packages/web/src/features/fileTree/components/fileTreePanel.tsx b/packages/web/src/features/fileTree/components/fileTreePanel.tsx index e96e3dbc4..89696eba4 100644 --- a/packages/web/src/features/fileTree/components/fileTreePanel.tsx +++ b/packages/web/src/features/fileTree/components/fileTreePanel.tsx @@ -155,7 +155,7 @@ export const FileTreePanel = ({ order }: FileTreePanelProps) => { - + Close file tree @@ -175,7 +175,7 @@ export const FileTreePanel = ({ order }: FileTreePanelProps) => { - + Search files @@ -217,7 +217,7 @@ export const FileTreePanel = ({ order }: FileTreePanelProps) => { - + Open file tree