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