128 lines
3.5 KiB
TypeScript
128 lines
3.5 KiB
TypeScript
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('+')
|
|
}
|