d3ro-voice/apps/desktop/src/main/ipc/voice-handlers.ts
Yun Chan 5b6e7aae6b refactor(main): ipcSuccess/ipcError 헬퍼 통일 + catch(error) 패턴 (WS-PATTERN)
- cloud-sync-handlers 수동 ok/fail -> ipcSuccess/ipcError 헬퍼 (6 핸들러)
- catch {} -> catch(error) 에러 메시지 보강 32건 (11 ipc handler 파일)
KEEP: template-handlers(주석 명시 에러 무시), license-handlers(이미 헬퍼 사용),
       audio-handlers stop().catch(() => {})(의도적 무시)
정책: docs/REFACTOR_POLICY.md DP2
2026-07-22 02:38:23 +09:00

76 lines
3 KiB
TypeScript

// src/main/ipc/voice-handlers.ts
import { ipcMain } from 'electron'
import { IPC_CHANNELS } from '@d3ro/core/ipc-channels'
import { ipcSuccess, ipcError, ErrorCode } from '@d3ro/core/errors'
import { getVoiceModeService } from '../services/VoiceModeService'
import { getMainWindow } from '../windows/WindowManager'
import type { StartRecordingParams, StopRecordingParams, CancelRecordingParams, SetVoiceModeParams, VoiceErrorEvent } from '@d3ro/core/types'
function safeSendToRenderer(channel: string, data: unknown): void {
const win = getMainWindow()
if (win && !win.isDestroyed()) {
win.webContents.send(channel, data)
}
}
export function registerVoiceHandlers(): void {
// 음성 세션 에러/경고를 메인 윈도우로 브로드캐스트 —
// 모델 미설치/엔진 실패 등의 경고가 recording tip(3초) 외에도
// 메인 UI에서 명확히 보이도록 (설계서 02 voice:error 이벤트)
getVoiceModeService().on('error', (payload) => {
const event: VoiceErrorEvent = {
sessionId: payload.session?.id ?? null,
errorCode: payload.error.code,
message: payload.error.message,
severity: payload.severity ?? 'error',
}
safeSendToRenderer(IPC_CHANNELS.VOICE.ERROR, event)
})
ipcMain.handle(IPC_CHANNELS.VOICE.START_RECORDING, async (_event, params: StartRecordingParams) => {
try {
const voice = getVoiceModeService()
await voice.startSession('dictation')
const session = voice.currentSession
return ipcSuccess({ sessionId: session?.id ?? params.sessionId ?? '' })
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return ipcError(ErrorCode.AudioCaptureStartFailed, `Failed to start recording: ${message}`)
}
})
ipcMain.handle(IPC_CHANNELS.VOICE.STOP_RECORDING, async (_event, params: StopRecordingParams) => {
try {
const voice = getVoiceModeService()
await voice.stopSession()
return ipcSuccess({
sessionId: params.sessionId,
text: voice.currentSession?.transcription ?? '',
durationMs: voice.currentSession ? Date.now() - voice.currentSession.startedAt : 0
})
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return ipcError(ErrorCode.STTTranscriptionFailed, `Failed to stop recording: ${message}`)
}
})
ipcMain.handle(IPC_CHANNELS.VOICE.CANCEL_RECORDING, async (_event, _params: CancelRecordingParams) => {
getVoiceModeService().cancelSession()
return ipcSuccess(undefined)
})
ipcMain.handle(IPC_CHANNELS.VOICE.GET_STATE, async () => {
return ipcSuccess(getVoiceModeService().getState())
})
ipcMain.handle(IPC_CHANNELS.VOICE.SET_MODE, async (_event, params: SetVoiceModeParams) => {
// Phase 2: 모드만 설정에 저장 (실제 모드 전환은 핫키에서 처리)
return ipcSuccess(undefined)
})
ipcMain.handle(IPC_CHANNELS.VOICE.GET_MODE, async () => {
const voice = getVoiceModeService()
return ipcSuccess(voice.currentSession?.mode ?? 'dictation')
})
}