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)}
+ />