// apps/desktop/src/renderer/utils/format-hotkey.ts // HotkeyBinding → 플랫폼별 표시 라벨 (segments + 결합 문자열). // macOS: ⌘/⌥/⌃/⇧ + key, Windows/Linux: Ctrl/Alt/Win/Shift + key. // // 기존 binding.displayLabel은 키 녹화 시 만들어진 정적 문자열이라 // 플랫폼이 다르면 잘못 보임 — 이 헬퍼가 단일 진입점. import type { HotkeyBinding } from '@d3ro/core/types' export type Platform = 'darwin' | 'win32' | 'linux' export function getPlatform(): Platform { const p = (window as { electronAPI?: { platform?: string } }).electronAPI?.platform if (p === 'darwin' || p === 'win32' || p === 'linux') return p // 폴백: SSR/테스트 환경에서는 navigator.platform로 추정. if (typeof navigator !== 'undefined' && /Mac/i.test(navigator.platform)) return 'darwin' return 'win32' } // keyCode → 키명. Windows VK_* 기준 + 일부 macOS 분기. export function keyCodeToName(keyCode: number, platform: Platform = getPlatform()): string { // Modifier 표기 — 플랫폼별 if (platform === 'darwin') { if (keyCode === 16 || keyCode === 160 || keyCode === 161) return '⇧' if (keyCode === 17 || keyCode === 162 || keyCode === 163) return '⌃' if (keyCode === 18 || keyCode === 164 || keyCode === 165) return '⌥' if (keyCode === 91 || keyCode === 92) return '⌘' } else { if (keyCode === 16 || keyCode === 160 || keyCode === 161) return 'Shift' if (keyCode === 17 || keyCode === 162 || keyCode === 163) return 'Ctrl' if (keyCode === 18 || keyCode === 164 || keyCode === 165) return 'Alt' if (keyCode === 91 || keyCode === 92) return 'Win' } // 공통 키 switch (keyCode) { case 8: return 'Backspace' case 9: return 'Tab' case 13: return platform === 'darwin' ? '↩' : 'Enter' case 19: return 'Pause' case 20: return 'CapsLock' case 27: return 'Esc' case 32: return 'Space' case 33: return 'PgUp' case 34: return 'PgDn' case 35: return 'End' case 36: return 'Home' case 37: return '←' case 38: return '↑' case 39: return '→' case 40: return '↓' case 45: return 'Insert' case 46: return platform === 'darwin' ? '⌫' : 'Delete' case 186: return ';' case 187: return '=' case 188: return ',' case 189: return '-' case 190: return '.' case 191: return '/' case 192: return '`' case 219: return '[' case 220: return '\\' case 221: return ']' case 222: return "'" default: break } // F1~F24 if (keyCode >= 112 && keyCode <= 135) return `F${keyCode - 111}` // Numpad if (keyCode >= 96 && keyCode <= 105) return `Num${keyCode - 96}` // 알파벳/숫자 if ((keyCode >= 48 && keyCode <= 57) || (keyCode >= 65 && keyCode <= 90)) { return String.fromCharCode(keyCode) } return `Key${keyCode}` } /** * binding을 플랫폼별 segment 배열로 변환. * 예) Mac: ['⌘', '⇧', '1'], Win: ['Win', 'Shift', '1'] * * 순서: * - macOS: ⌃ ⌥ ⇧ ⌘ + key (Apple 표준) * - Win/Linux: Ctrl Win Alt Shift + key */ export function formatHotkeySegments(binding: HotkeyBinding | null): string[] { if (!binding) return [] const platform = getPlatform() const segments: string[] = [] if (platform === 'darwin') { if (binding.ctrl) segments.push('⌃') if (binding.alt) segments.push('⌥') if (binding.shift) segments.push('⇧') if (binding.meta) segments.push('⌘') } else { if (binding.ctrl) segments.push('Ctrl') if (binding.meta) segments.push('Win') if (binding.alt) segments.push('Alt') if (binding.shift) segments.push('Shift') } segments.push(keyCodeToName(binding.keyCode, platform)) return segments } /** * 사람이 읽기 위한 단일 문자열 형태. * macOS: '⌘⇧1' (구분자 없음 — Apple 표준) * Win/Linux: 'Ctrl + Shift + 1' */ export function formatHotkeyLabel(binding: HotkeyBinding | null): string { const segments = formatHotkeySegments(binding) if (segments.length === 0) return '' const platform = getPlatform() return platform === 'darwin' ? segments.join('') : segments.join(' + ') }