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
162
src/shared/errors.ts
Normal file
162
src/shared/errors.ts
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// src/shared/errors.ts
|
||||
|
||||
export enum ErrorCode {
|
||||
// === Success ===
|
||||
Success = 0,
|
||||
|
||||
// === STT (100-199) ===
|
||||
STTEngineNotInstalled = 100,
|
||||
STTModelNotFound = 101,
|
||||
STTModelNotLoaded = 102,
|
||||
STTModelLoadFailed = 103,
|
||||
STTModelDownloadFailed = 104,
|
||||
STTModelDownloadCancelled = 105,
|
||||
STTTranscriptionFailed = 110,
|
||||
STTTranscriptionTimeout = 111,
|
||||
STTTranscriptionCancelled = 112,
|
||||
STTNoAudioData = 113,
|
||||
STTAudioTooShort = 114,
|
||||
STTLanguageNotSupported = 120,
|
||||
STTSidecarSpawnFailed = 130,
|
||||
STTSidecarCrashed = 131,
|
||||
STTSidecarCommunicationFailed = 132,
|
||||
STTGPUNotAvailable = 140,
|
||||
|
||||
// === TTS (200-299) ===
|
||||
TTSEngineNotInstalled = 200,
|
||||
TTSVoiceNotFound = 201,
|
||||
TTSVoiceNotLoaded = 202,
|
||||
TTSVoiceLoadFailed = 203,
|
||||
TTSVoiceDownloadFailed = 204,
|
||||
TTSSynthesisFailed = 210,
|
||||
TTSPlaybackFailed = 211,
|
||||
TTSPlaybackInterrupted = 212,
|
||||
TTSTextTooLong = 220,
|
||||
TTSTextEmpty = 221,
|
||||
|
||||
// === LLM / Ollama (300-399) ===
|
||||
LLMServerUnreachable = 300,
|
||||
LLMServerConnectionFailed = 301,
|
||||
LLMServerTimeout = 302,
|
||||
LLMModelNotFound = 310,
|
||||
LLMModelNotLoaded = 311,
|
||||
LLMModelLoadFailed = 312,
|
||||
LLMModelPullFailed = 313,
|
||||
LLMModelPullCancelled = 314,
|
||||
LLMProcessingFailed = 320,
|
||||
LLMProcessingTimeout = 321,
|
||||
LLMProcessingCancelled = 322,
|
||||
LLMResponseParseFailed = 323,
|
||||
LLMInvalidAction = 330,
|
||||
LLMPromptTooLong = 331,
|
||||
|
||||
// === Audio (400-499) ===
|
||||
AudioDeviceNotFound = 400,
|
||||
AudioDeviceAccessDenied = 401,
|
||||
AudioDeviceBusy = 402,
|
||||
AudioCaptureStartFailed = 410,
|
||||
AudioCaptureStopFailed = 411,
|
||||
AudioCaptureFailed = 412,
|
||||
AudioNoPermission = 420,
|
||||
AudioStreamError = 430,
|
||||
AudioBufferOverflow = 431,
|
||||
|
||||
// === Hotkey (500-599) ===
|
||||
HotkeyRegistrationFailed = 500,
|
||||
HotkeyConflict = 501,
|
||||
HotkeySystemReserved = 502,
|
||||
HotkeyHookInitFailed = 510,
|
||||
HotkeyHookCrashed = 511,
|
||||
|
||||
// === TextInsert (600-699) ===
|
||||
TextInsertFailed = 600,
|
||||
TextInsertClipboardSaveFailed = 601,
|
||||
TextInsertClipboardRestoreFailed = 602,
|
||||
TextInsertKeySimulationFailed = 603,
|
||||
TextInsertNoActiveWindow = 610,
|
||||
TextInsertTargetAppNotResponding = 611,
|
||||
|
||||
// === History / Dictionary / DB (700-799) ===
|
||||
DBOpenFailed = 700,
|
||||
DBMigrationFailed = 701,
|
||||
DBQueryFailed = 702,
|
||||
DBWriteFailed = 703,
|
||||
HistoryNotFound = 710,
|
||||
HistoryExportFailed = 711,
|
||||
DictionaryNotFound = 720,
|
||||
DictionaryDuplicate = 721,
|
||||
DictionaryImportFailed = 722,
|
||||
DictionaryExportFailed = 723,
|
||||
DictionaryImportInvalidFormat = 724,
|
||||
|
||||
// === Config (800-899) ===
|
||||
ConfigReadFailed = 800,
|
||||
ConfigWriteFailed = 801,
|
||||
ConfigInvalidValue = 802,
|
||||
ConfigKeyNotFound = 803,
|
||||
ConfigResetFailed = 804,
|
||||
ConfigMigrationFailed = 810,
|
||||
|
||||
// === System / Window (900-999) ===
|
||||
WindowCreationFailed = 900,
|
||||
WindowNotFound = 901,
|
||||
TrayCreationFailed = 910,
|
||||
NotificationFailed = 920,
|
||||
PermissionDenied = 930,
|
||||
ExternalOpenFailed = 940,
|
||||
SoundPlayFailed = 950,
|
||||
AppAlreadyRunning = 960,
|
||||
UnknownError = 999
|
||||
}
|
||||
|
||||
/**
|
||||
* D3RO-VOICE 표준 에러 객체 (Speakly NXError 패턴)
|
||||
*/
|
||||
export class D3ROError extends Error {
|
||||
readonly code: ErrorCode
|
||||
readonly details?: Record<string, unknown>
|
||||
|
||||
constructor(code: ErrorCode, message: string, details?: Record<string, unknown>) {
|
||||
super(message)
|
||||
this.name = 'D3ROError'
|
||||
this.code = code
|
||||
this.details = details
|
||||
}
|
||||
|
||||
toJSON(): D3ROErrorJSON {
|
||||
return {
|
||||
code: this.code,
|
||||
message: this.message,
|
||||
details: this.details
|
||||
}
|
||||
}
|
||||
|
||||
static fromJSON(json: D3ROErrorJSON): D3ROError {
|
||||
return new D3ROError(json.code, json.message, json.details)
|
||||
}
|
||||
}
|
||||
|
||||
export interface D3ROErrorJSON {
|
||||
code: ErrorCode
|
||||
message: string
|
||||
details?: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* IPC 핸들러에서 사용하는 표준 응답 래퍼.
|
||||
*/
|
||||
export type IPCResult<T> =
|
||||
| { success: true; data: T }
|
||||
| { success: false; error: D3ROErrorJSON }
|
||||
|
||||
export function ipcSuccess<T>(data: T): IPCResult<T> {
|
||||
return { success: true, data }
|
||||
}
|
||||
|
||||
export function ipcError<T>(
|
||||
code: ErrorCode,
|
||||
message: string,
|
||||
details?: Record<string, unknown>
|
||||
): IPCResult<T> {
|
||||
return { success: false, error: { code, message, details } }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue