import { getPlatform } from '../utils/platform.js' /** * Shortcuts that are typically intercepted by the OS, terminal, or shell * and will likely never reach the application. */ export type ReservedShortcut = { key: string reason: string severity: 'error' | 'warning' } /** * Shortcuts that cannot be rebound - they are hardcoded in Claude Code. */ export const NON_REBINDABLE: ReservedShortcut[] = [ { key: 'ctrl+c', reason: 'Cannot be rebound - used for interrupt/exit (hardcoded)', severity: 'error', }, { key: 'ctrl+d', reason: 'Cannot be rebound - used for exit (hardcoded)', severity: 'error', }, { key: 'ctrl+m', reason: 'Cannot be rebound - identical to Enter in terminals (both send CR)', severity: 'error', }, ] /** * Terminal control shortcuts that are intercepted by the terminal/OS. * These will likely never reach the application. * * Note: ctrl+s (XOFF) and ctrl+q (XON) are NOT included here because: * - Most modern terminals disable flow control by default * - We use ctrl+s for the stash feature */ export const TERMINAL_RESERVED: ReservedShortcut[] = [ { key: 'ctrl+z', reason: 'Unix process suspend (SIGTSTP)', severity: 'warning', }, { key: 'ctrl+\\', reason: 'Terminal quit signal (SIGQUIT)', severity: 'error', }, ] /** * macOS-specific shortcuts that the OS intercepts. */ export const MACOS_RESERVED: ReservedShortcut[] = [ { key: 'cmd+c', reason: 'macOS system copy', severity: 'error' }, { key: 'cmd+v', reason: 'macOS system paste', severity: 'error' }, { key: 'cmd+x', reason: 'macOS system cut', severity: 'error' }, { key: 'cmd+q', reason: 'macOS quit application', severity: 'error' }, { key: 'cmd+w', reason: 'macOS close window/tab', severity: 'error' }, { key: 'cmd+tab', reason: 'macOS app switcher', severity: 'error' }, { key: 'cmd+space', reason: 'macOS Spotlight', severity: 'error' }, ] /** * Get all reserved shortcuts for the current platform. * Includes non-rebindable shortcuts and terminal-reserved shortcuts. */ export function getReservedShortcuts(): ReservedShortcut[] { const platform = getPlatform() // Non-rebindable shortcuts first (highest priority) const reserved = [...NON_REBINDABLE, ...TERMINAL_RESERVED] if (platform === 'macos') { reserved.push(...MACOS_RESERVED) } return reserved } /** * Normalize a key string for comparison (lowercase, sorted modifiers). * Chords (space-separated steps like "ctrl+x ctrl+b") are normalized * per-step — splitting on '+' first would mangle "x ctrl" into a mainKey * overwritten by the next step, collapsing the chord into its last key. */ export function normalizeKeyForComparison(key: string): string { return key.trim().split(/\s+/).map(normalizeStep).join(' ') } function normalizeStep(step: string): string { const parts = step.split('+') const modifiers: string[] = [] let mainKey = '' for (const part of parts) { const lower = part.trim().toLowerCase() if ( [ 'ctrl', 'control', 'alt', 'opt', 'option', 'meta', 'cmd', 'command', 'shift', ].includes(lower) ) { // Normalize modifier names if (lower === 'control') modifiers.push('ctrl') else if (lower === 'option' || lower === 'opt') modifiers.push('alt') else if (lower === 'command' || lower === 'cmd') modifiers.push('cmd') else modifiers.push(lower) } else { mainKey = lower } } modifiers.sort() return [...modifiers, mainKey].join('+') }