fix(desktop): surface configuration and provider failures instead of hiding them
Several desktop paths quietly substituted defaults or partial results: a config write could fall back to a throwaway in-memory store, speech provider errors were absorbed into empty transcriptions, and meeting exports built file names from raw titles. Writes now fail explicitly when the store is unavailable, provider and model failures reach the UI as errors, and export names pass through one sanitizer. Settings, license, ad, and support surfaces use the shared theme tokens, unused hotkey helpers are gone, and the package gains strict node/renderer typecheck configs plus red-team e2e scenarios for these flows.
This commit is contained in:
parent
c8d802d78f
commit
6ba25f53b7
43 changed files with 1721 additions and 204 deletions
|
|
@ -21,7 +21,7 @@ import { getLocalLLMService } from './LocalLLMService'
|
|||
import { D3ROError, ErrorCode } from '@d3ro/core/errors'
|
||||
import { TIMING } from '@d3ro/core/constants'
|
||||
import { RecognitionState, AudioState } from '@d3ro/core/types'
|
||||
import type { VoiceMode, VoiceState } from '@d3ro/core/types'
|
||||
import type { VoiceMode, VoiceState, LLMAction } from '@d3ro/core/types'
|
||||
import {
|
||||
showRecordingTip,
|
||||
hideRecordingTip,
|
||||
|
|
@ -122,9 +122,6 @@ class VoiceModeService extends EventEmitter {
|
|||
private _errorHideTimer: NodeJS.Timeout | null = null
|
||||
/** 녹음 종료 후 STT 준비 대기 타이머 — 전사 시작 시 반드시 해제 */
|
||||
private _sttWaitTimer: NodeJS.Timeout | null = null
|
||||
/** 실시간 부분 전사 루프 */
|
||||
private _interimTimer: NodeJS.Timeout | null = null
|
||||
private _interimBusy = false
|
||||
|
||||
// Action Queue (이벤트 직렬화)
|
||||
private _actionQueue: VoiceAction[] = []
|
||||
|
|
@ -314,9 +311,6 @@ class VoiceModeService extends EventEmitter {
|
|||
// (_initSTT는 내부에서 에러를 _handleError로 처리하므로 fire-and-forget 안전)
|
||||
void this._initSTT()
|
||||
await this._startAudio()
|
||||
|
||||
// 실시간 부분 전사 루프 시작 (STT 준비 + 스트리밍 중일 때만 동작)
|
||||
this._startInterimLoop()
|
||||
}
|
||||
|
||||
async stopSession(): Promise<void> {
|
||||
|
|
@ -482,7 +476,6 @@ class VoiceModeService extends EventEmitter {
|
|||
}
|
||||
|
||||
private async _stopAudio(): Promise<void> {
|
||||
this._stopInterimLoop()
|
||||
this._setAudioState(AudioState.STOPPED)
|
||||
|
||||
const audio = getAudioCaptureService()
|
||||
|
|
@ -503,30 +496,6 @@ class VoiceModeService extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
// ── 실시간 부분 전사 (interim) ──────────────────────────
|
||||
// 녹음 중 1.5초 간격으로 현재 버퍼(최근 12초 윈도우)를 전사해
|
||||
// RecordingTip에 "말하는 대로 적히는" 미리보기를 제공한다.
|
||||
// 최종 전사는 release 후 전체 버퍼로 다시 수행 (기존 경로 그대로).
|
||||
|
||||
private _startInterimLoop(): void {
|
||||
this._stopInterimLoop()
|
||||
this._interimTimer = setInterval(() => {
|
||||
void this._runInterimTranscribe()
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
private _stopInterimLoop(): void {
|
||||
if (this._interimTimer) {
|
||||
clearInterval(this._interimTimer)
|
||||
this._interimTimer = null
|
||||
}
|
||||
this._interimBusy = false
|
||||
}
|
||||
|
||||
private async _runInterimTranscribe(): Promise<void> {
|
||||
// Cloud STT does not support interim streaming yet
|
||||
}
|
||||
|
||||
// ── 이중 조건 플러시 ───────────────────────────────────
|
||||
|
||||
private _tryFlushAll(): void {
|
||||
|
|
@ -713,6 +682,10 @@ class VoiceModeService extends EventEmitter {
|
|||
|
||||
try {
|
||||
const action = configGet('defaultLLMAction')
|
||||
if (action === 'none') {
|
||||
this._completeSession(transcribedText)
|
||||
return
|
||||
}
|
||||
|
||||
// Phase 10.2: 스크린 컨텍스트를 LLM 프롬프트에 주입
|
||||
let contextPrefix = ''
|
||||
|
|
@ -890,7 +863,6 @@ class VoiceModeService extends EventEmitter {
|
|||
}
|
||||
|
||||
private _resetToIdle(): void {
|
||||
this._stopInterimLoop()
|
||||
this._clearSttWaitTimer()
|
||||
this._session = null
|
||||
this._audioBuffer = []
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue