Phase 1+2 구현: Electron 뼈대 + STT/핫키/오케스트레이터
Phase 1: - 프로젝트 초기화 (TypeScript strict, electron-vite, ESLint, Prettier) - shared 타입 (ipc-channels 113채널, types, errors, constants) - 메인 프로세스 뼈대 (bootstrap, lifecycle, 단일 인스턴스) - LoggerService, ConfigService (electron-store ESM dynamic import) - React 19 + MUI 7 Dashboard, 시스템 트레이 Phase 2: - AudioCaptureService (node-record-lpcm16, PCM16 16kHz mono) - HotkeyService (uiohook-napi, 더블프레스, holdMode/toggleMode) - LocalSTTService (faster-whisper Python sidecar, 이중 조건 플러시) - VoiceModeService 오케스트레이터 (이중 상태머신, Action Queue) - Python sidecar (FastAPI: health/load/transcribe/shutdown) - IPC 핸들러 (voice, stt, hotkey) + Preload API 확장
This commit is contained in:
parent
e24bb8378c
commit
1d152d01a1
46 changed files with 10828 additions and 4 deletions
172
src/shared/ipc-channels.ts
Normal file
172
src/shared/ipc-channels.ts
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// src/shared/ipc-channels.ts
|
||||
// IPC 채널명 중앙 정의 — 모든 채널명은 이 파일에서만 정의한다.
|
||||
|
||||
export const IPC_CHANNELS = {
|
||||
VOICE: {
|
||||
START_RECORDING: 'voice:startRecording',
|
||||
STOP_RECORDING: 'voice:stopRecording',
|
||||
CANCEL_RECORDING: 'voice:cancelRecording',
|
||||
GET_STATE: 'voice:getState',
|
||||
SET_MODE: 'voice:setMode',
|
||||
GET_MODE: 'voice:getMode',
|
||||
// Main → Renderer events
|
||||
STATE_CHANGED: 'voice:stateChanged',
|
||||
TRANSCRIPTION_DELTA: 'voice:transcriptionDelta',
|
||||
TRANSCRIPTION_COMPLETE: 'voice:transcriptionComplete',
|
||||
ERROR: 'voice:error',
|
||||
AUDIO_LEVEL: 'voice:audioLevel'
|
||||
},
|
||||
|
||||
AUDIO: {
|
||||
GET_DEVICES: 'audio:getDevices',
|
||||
GET_SELECTED_DEVICE: 'audio:getSelectedDevice',
|
||||
SET_SELECTED_DEVICE: 'audio:setSelectedDevice',
|
||||
TEST_DEVICE: 'audio:testDevice',
|
||||
// Main → Renderer events
|
||||
DEVICE_CHANGED: 'audio:deviceChanged'
|
||||
},
|
||||
|
||||
STT: {
|
||||
GET_STATUS: 'stt:getStatus',
|
||||
GET_MODELS: 'stt:getModels',
|
||||
GET_ACTIVE_MODEL: 'stt:getActiveModel',
|
||||
SET_MODEL: 'stt:setModel',
|
||||
DOWNLOAD_MODEL: 'stt:downloadModel',
|
||||
CANCEL_DOWNLOAD: 'stt:cancelDownload',
|
||||
GET_LANGUAGE: 'stt:getLanguage',
|
||||
SET_LANGUAGE: 'stt:setLanguage',
|
||||
// Main → Renderer events
|
||||
STATUS_CHANGED: 'stt:statusChanged',
|
||||
DOWNLOAD_PROGRESS: 'stt:downloadProgress'
|
||||
},
|
||||
|
||||
TTS: {
|
||||
SPEAK: 'tts:speak',
|
||||
STOP: 'tts:stop',
|
||||
GET_VOICES: 'tts:getVoices',
|
||||
GET_ACTIVE_VOICE: 'tts:getActiveVoice',
|
||||
SET_VOICE: 'tts:setVoice',
|
||||
GET_STATUS: 'tts:getStatus',
|
||||
DOWNLOAD_VOICE: 'tts:downloadVoice',
|
||||
// Main → Renderer events
|
||||
STATUS_CHANGED: 'tts:statusChanged',
|
||||
SPEAKING_STATE_CHANGED: 'tts:speakingStateChanged'
|
||||
},
|
||||
|
||||
LLM: {
|
||||
GET_STATUS: 'llm:getStatus',
|
||||
GET_MODELS: 'llm:getModels',
|
||||
GET_ACTIVE_MODEL: 'llm:getActiveModel',
|
||||
SET_MODEL: 'llm:setModel',
|
||||
PROCESS: 'llm:process',
|
||||
CANCEL_PROCESS: 'llm:cancelProcess',
|
||||
GET_SERVER_URL: 'llm:getServerUrl',
|
||||
SET_SERVER_URL: 'llm:setServerUrl',
|
||||
PULL_MODEL: 'llm:pullModel',
|
||||
// Main → Renderer events
|
||||
STATUS_CHANGED: 'llm:statusChanged',
|
||||
PROCESS_PROGRESS: 'llm:processProgress',
|
||||
PULL_PROGRESS: 'llm:pullProgress'
|
||||
},
|
||||
|
||||
HOTKEY: {
|
||||
GET_DICTATION_SHORTCUT: 'hotkey:getDictationShortcut',
|
||||
SET_DICTATION_SHORTCUT: 'hotkey:setDictationShortcut',
|
||||
GET_HANDS_FREE_SHORTCUT: 'hotkey:getHandsFreeShortcut',
|
||||
SET_HANDS_FREE_SHORTCUT: 'hotkey:setHandsFreeShortcut',
|
||||
GET_COMMAND_SHORTCUT: 'hotkey:getCommandShortcut',
|
||||
SET_COMMAND_SHORTCUT: 'hotkey:setCommandShortcut',
|
||||
IS_ENABLED: 'hotkey:isEnabled',
|
||||
SET_ENABLED: 'hotkey:setEnabled',
|
||||
START_RECORDING: 'hotkey:startRecording',
|
||||
STOP_RECORDING: 'hotkey:stopRecording',
|
||||
// Main → Renderer events
|
||||
TRIGGERED: 'hotkey:triggered',
|
||||
RECORDING_RESULT: 'hotkey:recordingResult'
|
||||
},
|
||||
|
||||
CONFIG: {
|
||||
GET: 'config:get',
|
||||
SET: 'config:set',
|
||||
GET_ALL: 'config:getAll',
|
||||
RESET: 'config:reset',
|
||||
GET_THEME: 'config:getTheme',
|
||||
SET_THEME: 'config:setTheme',
|
||||
GET_LANGUAGE: 'config:getLanguage',
|
||||
SET_LANGUAGE: 'config:setLanguage',
|
||||
GET_AUTO_LAUNCH: 'config:getAutoLaunch',
|
||||
SET_AUTO_LAUNCH: 'config:setAutoLaunch',
|
||||
GET_CLOSE_TO_TRAY: 'config:getCloseToTray',
|
||||
SET_CLOSE_TO_TRAY: 'config:setCloseToTray',
|
||||
// Main → Renderer events
|
||||
CHANGED: 'config:changed'
|
||||
},
|
||||
|
||||
HISTORY: {
|
||||
GET_ALL: 'history:getAll',
|
||||
GET_BY_ID: 'history:getById',
|
||||
DELETE: 'history:delete',
|
||||
DELETE_ALL: 'history:deleteAll',
|
||||
SEARCH: 'history:search',
|
||||
EXPORT: 'history:export',
|
||||
// Main → Renderer events
|
||||
ADDED: 'history:added'
|
||||
},
|
||||
|
||||
DICTIONARY: {
|
||||
GET_ALL: 'dictionary:getAll',
|
||||
ADD: 'dictionary:add',
|
||||
UPDATE: 'dictionary:update',
|
||||
DELETE: 'dictionary:delete',
|
||||
IMPORT: 'dictionary:import',
|
||||
EXPORT: 'dictionary:export',
|
||||
SEARCH: 'dictionary:search'
|
||||
},
|
||||
|
||||
WINDOW: {
|
||||
MINIMIZE: 'window:minimize',
|
||||
MAXIMIZE: 'window:maximize',
|
||||
CLOSE: 'window:close',
|
||||
IS_MAXIMIZED: 'window:isMaximized',
|
||||
SHOW_RECORDING_TIP: 'window:showRecordingTip',
|
||||
HIDE_RECORDING_TIP: 'window:hideRecordingTip',
|
||||
SHOW_RESULT_POPUP: 'window:showResultPopup',
|
||||
HIDE_RESULT_POPUP: 'window:hideResultPopup',
|
||||
TIP_MEASURED: 'window:tipMeasured',
|
||||
// Main → Renderer events
|
||||
TIP_STATE_CHANGED: 'window:tipStateChanged',
|
||||
TIP_PREPARE: 'window:tipPrepare',
|
||||
TIP_SHOW: 'window:tipShow'
|
||||
},
|
||||
|
||||
SYSTEM: {
|
||||
GET_PLATFORM: 'system:getPlatform',
|
||||
GET_VERSION: 'system:getVersion',
|
||||
CHECK_MIC_PERMISSION: 'system:checkMicPermission',
|
||||
REQUEST_MIC_PERMISSION: 'system:requestMicPermission',
|
||||
SHOW_NOTIFICATION: 'system:showNotification',
|
||||
OPEN_EXTERNAL: 'system:openExternal',
|
||||
GET_ACTIVE_APP: 'system:getActiveApp',
|
||||
INSERT_TEXT: 'system:insertText',
|
||||
PLAY_SOUND: 'system:playSound',
|
||||
SET_SOUND_ENABLED: 'system:setSoundEnabled',
|
||||
IS_SOUND_ENABLED: 'system:isSoundEnabled'
|
||||
},
|
||||
|
||||
STATS: {
|
||||
GET_SUMMARY: 'stats:getSummary',
|
||||
GET_DAILY: 'stats:getDaily',
|
||||
GET_WEEKLY: 'stats:getWeekly',
|
||||
// Main → Renderer events
|
||||
UPDATED: 'stats:updated'
|
||||
}
|
||||
} as const
|
||||
|
||||
// 타입 유틸리티: 채널명 유니온 추출
|
||||
type NestedValues<T> = T extends Record<string, infer V>
|
||||
? V extends string
|
||||
? V
|
||||
: NestedValues<V>
|
||||
: never
|
||||
|
||||
export type IPCChannel = NestedValues<typeof IPC_CHANNELS>
|
||||
Loading…
Add table
Add a link
Reference in a new issue