- 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
76 lines
3 KiB
TypeScript
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')
|
|
})
|
|
}
|