diff --git a/apps/desktop/src/renderer/components/CaptionModelSelect.tsx b/apps/desktop/src/renderer/components/CaptionModelSelect.tsx new file mode 100644 index 0000000..a02163e --- /dev/null +++ b/apps/desktop/src/renderer/components/CaptionModelSelect.tsx @@ -0,0 +1,41 @@ +// 실시간 자막 전용 로컬 Whisper 모델 선택 — STT 탭과 일반 탭 자막 섹션이 함께 쓴다. + +import { FormControl, InputLabel, MenuItem, Select, Typography } from '@mui/material' +import { d3roPalette, d3roTypo } from '@d3ro/ui/theme' +import { useI18n } from '@d3ro/i18n' +import type { STTModel } from '@d3ro/core/types' + +/** "받아쓰기와 같게" — 설정에는 null 로 저장한다 */ +const SAME_AS_DICTATION = '__same__' + +interface CaptionModelSelectProps { + value: string | null | undefined + /** 선택지로 보일 모델 — 받아 둔 모델만 넘긴다 */ + models: readonly STTModel[] + onChange: (modelId: string | null) => void + fullWidth?: boolean +} + +export function CaptionModelSelect({ value, models, onChange, fullWidth }: CaptionModelSelectProps): React.ReactElement { + const { t } = useI18n() + return ( + + {t('settings.captionModel')} + + + {t('settings.captionModel.desc')} + + + ) +} diff --git a/apps/desktop/src/renderer/components/STTTab.tsx b/apps/desktop/src/renderer/components/STTTab.tsx index 3c2f53e..a6e2415 100644 --- a/apps/desktop/src/renderer/components/STTTab.tsx +++ b/apps/desktop/src/renderer/components/STTTab.tsx @@ -50,6 +50,7 @@ import type { STTModel, } from '@d3ro/core/types' import { CodexOAuthGuideModal } from './CodexOAuthGuideModal' +import { CaptionModelSelect } from './CaptionModelSelect' interface STTTabProps { config: Partial @@ -362,6 +363,14 @@ export function STTTab({ config, updateConfig }: STTTabProps): React.ReactElemen + {/* 실시간 자막 전용 모델 — 1초마다 다시 인식하므로 받아쓰기와 다른 모델이 나을 수 있다 */} + m.downloaded)} + onChange={(modelId) => updateConfig('captionSttModelId', modelId)} + /> + {/* 모델 다운로드 진행 바 또는 다운로드 버튼 */} {(() => { const selected = localModels.find((m) => m.id === (config.sttModelId ?? 'large-v3-turbo')) diff --git a/apps/desktop/src/renderer/components/SettingsModal.tsx b/apps/desktop/src/renderer/components/SettingsModal.tsx index 626a84c..61495c1 100644 --- a/apps/desktop/src/renderer/components/SettingsModal.tsx +++ b/apps/desktop/src/renderer/components/SettingsModal.tsx @@ -28,6 +28,7 @@ import { Led } from '@d3ro/ui/components/ds' import { LicenseTab } from './LicenseTab' import { CloudSyncSection } from './CloudSyncSection' import { STTTab } from './STTTab' +import { CaptionModelSelect } from './CaptionModelSelect' import { InputConsentPanel } from './input-insights/InputConsentPanel' import { KeyBindingField } from './keybinding/KeyBindingField' import { asTranslationKey } from './keybinding/translation-key' @@ -64,9 +65,6 @@ const ACTION_GROUP_LABEL_KEYS: Readonly> = const ACTION_GROUP_ORDER: readonly KeyBindingActionGroup[] = ['voice', 'window', 'input'] -/** 자막 모델 선택지 "받아쓰기와 같게" — 설정에는 null 로 저장한다 */ -const SAME_AS_DICTATION = '__same__' - export function SettingsModal({ open, initialTab = 0, onClose }: SettingsModalProps): React.ReactElement { const { t, locale, setLocale } = useI18n() const [activeTab, setActiveTab] = useState(initialTab) @@ -493,26 +491,11 @@ export function SettingsModal({ open, initialTab = 0, onClose }: SettingsModalPr - - {t('settings.captionModel')} - - - {t('settings.captionModel.desc')} - - + updateConfig('captionSttModelId', modelId)} + />