feat(caption): let live captions use their own speech model

The speech engine now keeps an auxiliary model next to the dictation model
and transcribes with whichever the request names, reloading it once if the
engine restarted. Settings > STT gains a live-caption model so captions can
run on large-v3-turbo while dictation keeps its own model. The runtime
minimum rises to 1.7.0 because older engines would silently ignore the
model choice.

Suggestion paging moves to Up/Down: the page follows the selection and the
last item waits while more candidates are being generated. The Left/Right
page shortcuts are removed; they did nothing until a page had filled and
clash with Intel's display-rotation hotkeys.
This commit is contained in:
Yun Chan 2026-09-24 21:46:54 +09:00
parent 39b8e7448e
commit 4b0f685941
29 changed files with 222 additions and 234 deletions

View file

@ -51,6 +51,9 @@ import type {
} from '@d3ro/core/types'
import { CodexOAuthGuideModal } from './CodexOAuthGuideModal'
/** 자막 모델 선택지 "받아쓰기와 같게" — 설정에는 null 로 저장한다 */
const SAME_AS_DICTATION = '__same__'
interface STTTabProps {
config: Partial<AppConfig>
updateConfig: (key: keyof AppConfig, value: AppConfig[keyof AppConfig]) => void
@ -362,6 +365,30 @@ export function STTTab({ config, updateConfig }: STTTabProps): React.ReactElemen
</Select>
</FormControl>
{/* 실시간 자막 전용 모델 — 1초마다 다시 인식하므로 받아쓰기와 다른 모델이 나을 수 있다 */}
<FormControl size="small" fullWidth>
<InputLabel>{t('settings.captionModel')}</InputLabel>
<Select
label={t('settings.captionModel')}
value={config.captionSttModelId ?? SAME_AS_DICTATION}
onChange={(e) =>
updateConfig('captionSttModelId', e.target.value === SAME_AS_DICTATION ? null : e.target.value)
}
>
<MenuItem value={SAME_AS_DICTATION}>{t('settings.captionModel.same')}</MenuItem>
{localModels
.filter((m) => m.downloaded)
.map((m) => (
<MenuItem key={m.id} value={m.id}>
{m.name}
</MenuItem>
))}
</Select>
<Typography sx={{ mt: 0.5, fontSize: d3roTypo.meta.size, color: d3roPalette.text.secondary }}>
{t('settings.captionModel.desc')}
</Typography>
</FormControl>
{/* 모델 다운로드 진행 바 또는 다운로드 버튼 */}
{(() => {
const selected = localModels.find((m) => m.id === (config.sttModelId ?? 'large-v3-turbo'))