From ad6bb70c2013903ae8ea6bc7c9eb641f846b3da6 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Thu, 24 Sep 2026 22:08:05 +0900 Subject: [PATCH] fix(settings): show the live caption model under the dictation model The caption model selector now sits directly under the dictation model in the STT tab's Local Whisper card, where people look for it, and stays in the General tab's caption section as well; both use one shared component. The first selector is renamed to 'Dictation model' so the two are told apart. --- .../components/CaptionModelSelect.tsx | 41 +++++++++++++++++++ .../src/renderer/components/STTTab.tsx | 9 ++++ .../src/renderer/components/SettingsModal.tsx | 29 +++---------- packages/i18n/src/locales/de.json | 2 +- packages/i18n/src/locales/en.json | 2 +- packages/i18n/src/locales/es.json | 2 +- packages/i18n/src/locales/fr.json | 2 +- packages/i18n/src/locales/ja.json | 2 +- packages/i18n/src/locales/ko.json | 2 +- packages/i18n/src/locales/pt.json | 2 +- packages/i18n/src/locales/ru.json | 2 +- packages/i18n/src/locales/th.json | 2 +- packages/i18n/src/locales/vi.json | 2 +- packages/i18n/src/locales/zh-TW.json | 2 +- packages/i18n/src/locales/zh.json | 2 +- 15 files changed, 68 insertions(+), 35 deletions(-) create mode 100644 apps/desktop/src/renderer/components/CaptionModelSelect.tsx 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)} + />