fix(desktop): Mac sidecar 부팅 + 단축키 녹화 모달 ⌘ 표기

문제 1: STT release 안 됨 → 진짜 원인은 sidecar import 실패
- 시스템 python3 (Python 3.13)에 numpy/faster-whisper 등 sidecar deps 없음
- 매번 spawn 직후 ModuleNotFoundError로 즉사 → /health 30s 타임아웃
- 사용자에게는 '핫키 release 안 됨'으로 보이지만, 실제로는 핫키는 정상,
  STT 응답이 없어 결과 팝업이 안 뜬 것

수정:
- apps/desktop/sidecar/.venv/ 신규 (gitignored)
  - Python 3.13.13 venv
  - 최소 deps: faster-whisper 1.2.1, fastapi 0.135, uvicorn 0.44, python-multipart, numpy 2.4
  - torch/pyannote는 lazy import이므로 화자 구분 쓸 때 추가 설치
- main/utils/paths.ts: getSidecarCommand가 venv python 우선 사용
  - existsSync(.venv/bin/python3) → 그걸로 spawn
  - 없으면 시스템 python3 폴백 (기존 동작)
  - Windows는 .venv/Scripts/python.exe

문제 2: HotkeyRecordModal 칩에 'WIN' 그대로 표기
- getKeyName()이 KEY_DISPLAY_MAP (Windows 전용 매핑) 사용
  - 91/92 → 'Win' 하드코딩
- format-hotkey.ts의 keyCodeToName/getPlatform export
- HotkeyRecordModal.getKeyName이 keyCodeToName(platform) 호출
  → macOS는 ⌘/⇧/⌃/⌥
- 'Shift + Win + 1 — 저장을 눌러주세요' 안내 텍스트도 macOS는 구분자 없이 결합
This commit is contained in:
윤찬 2026-04-11 09:11:07 +09:00
parent d2408c135f
commit 29c24a1520
3 changed files with 28 additions and 9 deletions

View file

@ -54,7 +54,7 @@ export function getRecPath(): string {
/**
* STT sidecar .
* - dev: python/python3 + sidecar/main.py
* - dev: sidecar/.venv/bin/python () + sidecar/main.py, python3
* - production: sidecar/sidecar(.exe) (PyInstaller )
*/
export function getSidecarCommand(): { command: string; args: string[] } {
@ -71,8 +71,19 @@ export function getSidecarCommand(): { command: string; args: string[] } {
return { command: pythonCmd, args: [pyPath] }
}
// dev: Python 직접 실행
const sidecarPath = path.join(app.getAppPath(), 'sidecar', 'main.py')
// dev: venv 우선 → 없으면 시스템 python
const sidecarDir = path.join(app.getAppPath(), 'sidecar')
const sidecarPath = path.join(sidecarDir, 'main.py')
const venvPython =
process.platform === 'win32'
? path.join(sidecarDir, '.venv', 'Scripts', 'python.exe')
: path.join(sidecarDir, '.venv', 'bin', 'python3')
if (existsSync(venvPython)) {
return { command: venvPython, args: [sidecarPath] }
}
const pythonCmd = process.platform === 'win32' ? 'python' : 'python3'
return { command: pythonCmd, args: [sidecarPath] }
}

View file

@ -18,7 +18,7 @@ import {
import { d3roPalette } from '@d3ro/ui/theme'
import { useI18n } from '@d3ro/i18n'
import type { HotkeyBinding } from '@d3ro/core/types'
import { formatHotkeyLabel } from '../utils/format-hotkey'
import { formatHotkeyLabel, getPlatform, keyCodeToName } from '../utils/format-hotkey'
// ── 키 이름 매핑 (Windows) ──────────────────────────────
const KEY_DISPLAY_MAP: Record<number, string> = {
@ -55,7 +55,10 @@ const RESERVED_COMBOS = [
const MODIFIER_KEYCODES = new Set([16, 17, 18, 91, 92, 160, 161, 162, 163, 164, 165])
function getKeyName(keyCode: number, key: string): string {
if (KEY_DISPLAY_MAP[keyCode]) return KEY_DISPLAY_MAP[keyCode]
// 플랫폼별 라벨 (macOS는 ⌘/⇧/⌃/⌥). format-hotkey.ts의 단일 매핑을 사용.
const platform = getPlatform()
const mapped = keyCodeToName(keyCode, platform)
if (mapped && !mapped.startsWith('Key')) return mapped
if (key.length === 1) return key.toUpperCase()
return key
}
@ -291,7 +294,12 @@ export function HotkeyRecordModal({
{/* 상태 표시 */}
{isReady && !error && (
<Typography sx={{ color: d3roPalette.tag.green, fontSize: '12px', mt: 1, fontWeight: 600 }}>
{t('hotkey.ready', { keys: captured?.map(k => k.name).join(' + ') ?? '' })}
{t('hotkey.ready', {
keys:
captured
?.map((k) => k.name)
.join(getPlatform() === 'darwin' ? '' : ' + ') ?? ''
})}
</Typography>
)}

View file

@ -7,9 +7,9 @@
import type { HotkeyBinding } from '@d3ro/core/types'
type Platform = 'darwin' | 'win32' | 'linux'
export type Platform = 'darwin' | 'win32' | 'linux'
function getPlatform(): Platform {
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로 추정.
@ -18,7 +18,7 @@ function getPlatform(): Platform {
}
// keyCode → 키명. Windows VK_* 기준 + 일부 macOS 분기.
function keyCodeToName(keyCode: number, platform: Platform): string {
export function keyCodeToName(keyCode: number, platform: Platform = getPlatform()): string {
// Modifier 표기 — 플랫폼별
if (platform === 'darwin') {
if (keyCode === 16 || keyCode === 160 || keyCode === 161) return '⇧'