diff --git a/app/components/@settings/tabs/settings/SettingsTab.tsx b/app/components/@settings/tabs/settings/SettingsTab.tsx
index d2cffde9..28a6e1ca 100644
--- a/app/components/@settings/tabs/settings/SettingsTab.tsx
+++ b/app/components/@settings/tabs/settings/SettingsTab.tsx
@@ -7,6 +7,32 @@ import { themeStore, kTheme } from '~/lib/stores/theme';
import type { UserProfile } from '~/components/@settings/core/types';
import { useStore } from '@nanostores/react';
import { shortcutsStore } from '~/lib/stores/settings';
+import { isMac } from '~/utils/os';
+
+// Helper to format shortcut key display
+const formatShortcutKey = (key: string) => {
+ if (key === '`') {
+ return '`';
+ }
+
+ return key.toUpperCase();
+};
+
+// Helper to get modifier key symbols/text
+const getModifierSymbol = (modifier: string): string => {
+ switch (modifier) {
+ case 'meta':
+ return isMac ? '⌘' : 'Win';
+ case 'alt':
+ return isMac ? '⌥' : 'Alt';
+ case 'ctrl':
+ return isMac ? '⌃' : 'Ctrl';
+ case 'shift':
+ return '⇧';
+ default:
+ return modifier;
+ }
+};
export default function SettingsTab() {
const [currentTimezone, setCurrentTimezone] = useState('');
@@ -237,37 +263,42 @@ export default function SettingsTab() {
key={name}
className="flex items-center justify-between p-2 rounded-lg bg-[#FAFAFA] dark:bg-[#1A1A1A] hover:bg-purple-50 dark:hover:bg-purple-500/10 transition-colors"
>
-
- {name.replace(/([A-Z])/g, ' $1').toLowerCase()}
-
+
+
+ {name.replace(/([A-Z])/g, ' $1').toLowerCase()}
+
+ {shortcut.description && (
+ {shortcut.description}
+ )}
+
{shortcut.ctrlOrMetaKey && (
- {navigator.platform.includes('Mac') ? '⌘' : 'Ctrl'}
+ {getModifierSymbol(isMac ? 'meta' : 'ctrl')}
)}
{shortcut.ctrlKey && (
- Ctrl
+ {getModifierSymbol('ctrl')}
)}
{shortcut.metaKey && (
- ⌘
+ {getModifierSymbol('meta')}
)}
{shortcut.altKey && (
- {navigator.platform.includes('Mac') ? '⌥' : 'Alt'}
+ {getModifierSymbol('alt')}
)}
{shortcut.shiftKey && (
- ⇧
+ {getModifierSymbol('shift')}
)}
- {shortcut.key.toUpperCase()}
+ {formatShortcutKey(shortcut.key)}
diff --git a/app/lib/hooks/useShortcuts.ts b/app/lib/hooks/useShortcuts.ts
index 94f7fb05..39308fcd 100644
--- a/app/lib/hooks/useShortcuts.ts
+++ b/app/lib/hooks/useShortcuts.ts
@@ -1,6 +1,10 @@
import { useStore } from '@nanostores/react';
import { useEffect } from 'react';
import { shortcutsStore, type Shortcuts } from '~/lib/stores/settings';
+import { isMac } from '~/utils/os';
+
+// List of keys that should not trigger shortcuts when typing in input/textarea
+const INPUT_ELEMENTS = ['input', 'textarea'];
class ShortcutEventEmitter {
#emitter = new EventTarget();
@@ -25,55 +29,54 @@ export function useShortcuts(): void {
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent): void => {
- // Debug logging
- console.log('Key pressed:', {
- key: event.key,
- code: event.code, // This gives us the physical key regardless of modifiers
- ctrlKey: event.ctrlKey,
- shiftKey: event.shiftKey,
- altKey: event.altKey,
- metaKey: event.metaKey,
- });
-
- /*
- * Check for theme toggle shortcut first (Option + Command + Shift + D)
- * Use event.code to check for the physical D key regardless of the character produced
- */
+ // Don't trigger shortcuts when typing in input fields
if (
- event.code === 'KeyD' &&
- event.metaKey && // Command (Mac) or Windows key
- event.altKey && // Option (Mac) or Alt (Windows)
- event.shiftKey &&
- !event.ctrlKey
+ document.activeElement &&
+ INPUT_ELEMENTS.includes(document.activeElement.tagName.toLowerCase()) &&
+ !event.altKey && // Allow Alt combinations even in input fields
+ !event.metaKey && // Allow Cmd/Win combinations even in input fields
+ !event.ctrlKey // Allow Ctrl combinations even in input fields
) {
- event.preventDefault();
- event.stopPropagation();
- shortcuts.toggleTheme.action();
-
return;
}
- // Handle other shortcuts
- for (const name in shortcuts) {
- const shortcut = shortcuts[name as keyof Shortcuts];
+ // Debug logging in development only
+ if (process.env.NODE_ENV === 'development') {
+ console.log('Key pressed:', {
+ key: event.key,
+ code: event.code,
+ ctrlKey: event.ctrlKey,
+ shiftKey: event.shiftKey,
+ altKey: event.altKey,
+ metaKey: event.metaKey,
+ target: event.target,
+ });
+ }
- if (name === 'toggleTheme') {
- continue;
- } // Skip theme toggle as it's handled above
-
- // For other shortcuts, check both key and code
+ // Handle shortcuts
+ for (const [name, shortcut] of Object.entries(shortcuts)) {
const keyMatches =
shortcut.key.toLowerCase() === event.key.toLowerCase() || `Key${shortcut.key.toUpperCase()}` === event.code;
+ // Handle ctrlOrMetaKey based on OS
+ const ctrlOrMetaKeyMatches = shortcut.ctrlOrMetaKey
+ ? (isMac && event.metaKey) || (!isMac && event.ctrlKey)
+ : true;
+
const modifiersMatch =
+ ctrlOrMetaKeyMatches &&
(shortcut.ctrlKey === undefined || shortcut.ctrlKey === event.ctrlKey) &&
(shortcut.metaKey === undefined || shortcut.metaKey === event.metaKey) &&
(shortcut.shiftKey === undefined || shortcut.shiftKey === event.shiftKey) &&
(shortcut.altKey === undefined || shortcut.altKey === event.altKey);
if (keyMatches && modifiersMatch) {
- event.preventDefault();
- event.stopPropagation();
+ // Prevent default browser behavior if specified
+ if (shortcut.isPreventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
shortcutEventEmitter.dispatch(name as keyof Shortcuts);
shortcut.action();
break;
diff --git a/app/lib/stores/settings.ts b/app/lib/stores/settings.ts
index be8cc109..97802a0b 100644
--- a/app/lib/stores/settings.ts
+++ b/app/lib/stores/settings.ts
@@ -21,6 +21,8 @@ export interface Shortcut {
metaKey?: boolean;
ctrlOrMetaKey?: boolean;
action: () => void;
+ description?: string; // Description of what the shortcut does
+ isPreventDefault?: boolean; // Whether to prevent default browser behavior
}
export interface Shortcuts {
@@ -35,32 +37,41 @@ export const LOCAL_PROVIDERS = ['OpenAILike', 'LMStudio', 'Ollama'];
export type ProviderSetting = Record;
+// Define safer shortcuts that don't conflict with browser defaults
export const shortcutsStore = map({
toggleTerminal: {
key: '`',
ctrlOrMetaKey: true,
action: () => workbenchStore.toggleTerminal(),
+ description: 'Toggle terminal',
+ isPreventDefault: true,
},
toggleTheme: {
key: 'd',
- metaKey: true, // Command key on Mac, Windows key on Windows
- altKey: true, // Option key on Mac, Alt key on Windows
+ metaKey: true,
+ altKey: true,
shiftKey: true,
action: () => toggleTheme(),
+ description: 'Toggle theme',
+ isPreventDefault: true,
},
toggleChat: {
- key: 'k',
+ key: 'j', // Changed from 'k' to 'j' to avoid conflicts
ctrlOrMetaKey: true,
+ altKey: true, // Added alt key to make it more unique
action: () => chatStore.setKey('showChat', !chatStore.get().showChat),
+ description: 'Toggle chat',
+ isPreventDefault: true,
},
toggleSettings: {
key: 's',
ctrlOrMetaKey: true,
altKey: true,
action: () => {
- // This will be connected to the settings panel toggle
document.dispatchEvent(new CustomEvent('toggle-settings'));
},
+ description: 'Toggle settings',
+ isPreventDefault: true,
},
});
diff --git a/app/utils/os.ts b/app/utils/os.ts
new file mode 100644
index 00000000..0c8d87b9
--- /dev/null
+++ b/app/utils/os.ts
@@ -0,0 +1,4 @@
+// Helper to detect OS
+export const isMac = typeof navigator !== 'undefined' ? navigator.platform.toLowerCase().includes('mac') : false;
+export const isWindows = typeof navigator !== 'undefined' ? navigator.platform.toLowerCase().includes('win') : false;
+export const isLinux = typeof navigator !== 'undefined' ? navigator.platform.toLowerCase().includes('linux') : false;