fix(desktop): macOS 단축키 표기 (Win/Ctrl/Alt → ⌘/⌃/⌥/⇧)

기존: HotkeyRecordModal에서 키 녹화 시 'Win+Shift+1' 같은 정적 displayLabel 저장
- KEY_DISPLAY_MAP이 Windows 전용 (91/92 → 'Win')
- SettingsModal HotkeyDisplay가 binding.displayLabel.split(' + ')로 chip 분리
  - 그런데 displayLabel은 join('+') (space 없음) — split이 작동 안 해서 단일 chip 'Win+Shift+1'
- macOS에서도 'Win'으로 표시되어 사용자 혼동

수정:
- preload/index.ts: process.platform 노출 (electronAPI.platform)
- renderer/utils/format-hotkey.ts 신규
  - formatHotkeySegments(binding) → 플랫폼별 segment 배열
    - macOS: ['⌘', '⇧', '1'] (⌃ ⌥ ⇧ ⌘ 순, Apple HIG)
    - Win/Linux: ['Ctrl', 'Win', 'Alt', 'Shift', '1']
  - formatHotkeyLabel(binding) → 단일 문자열
    - macOS: '⌘⇧1' (구분자 없음, Apple 표준)
    - Win/Linux: 'Ctrl + Shift + 1'
- 사용 위치 일괄 교체 (binding.displayLabel → format 헬퍼):
  - SettingsModal HotkeyDisplay (chip 표시)
  - SettingsModal Agent 모드 설명 ({{key}} 치환)
  - OnboardingModal 단축키 chip + 완료 화면
  - DashboardPage CrtDisplay 'PRESS X TO RECORD'
  - HotkeyRecordModal currentBinding 표시

displayLabel 필드는 ConfigService 호환을 위해 유지 (저장된 값은 그대로)
This commit is contained in:
윤찬 2026-04-11 09:04:51 +09:00
parent 07c6d13c99
commit 0d17b71866
6 changed files with 135 additions and 8 deletions

View file

@ -0,0 +1,119 @@
// 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'
type Platform = 'darwin' | 'win32' | 'linux'
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 분기.
function keyCodeToName(keyCode: number, platform: Platform): 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(' + ')
}