// src/renderer/components/OnboardingModal.tsx // 첫 실행 시 마이크 + 핫키 설정 안내 import { useState, useEffect } from 'react' import { Dialog, DialogContent, Box, Typography, Button, Stack, Chip, } from '@mui/material' import MicIcon from '@mui/icons-material/Mic' import KeyboardIcon from '@mui/icons-material/Keyboard' import CheckCircleIcon from '@mui/icons-material/CheckCircle' import OpenInNewIcon from '@mui/icons-material/OpenInNew' import { d3roPalette, d3roFontMono, d3roShadow } from '../theme' import { Led } from './ds' import { HotkeyRecordModal } from './HotkeyRecordModal' import { useI18n } from '../i18n' import type { HotkeyBinding, AudioDevice } from '@shared/types' interface OnboardingModalProps { open: boolean onClose: () => void } export function OnboardingModal({ open, onClose }: OnboardingModalProps): React.ReactElement { const { t } = useI18n() const [step, setStep] = useState(0) // 0: 환영, 1: 마이크, 2: 핫키, 3: Ollama, 4: 완료 const [devices, setDevices] = useState([]) const [selectedDevice, setSelectedDevice] = useState('default') const [hotkeyBinding, setHotkeyBinding] = useState(null) const [hotkeyModalOpen, setHotkeyModalOpen] = useState(false) useEffect(() => { if (!open) return setStep(0) window.electronAPI.audio.getDevices().then((r) => { if (r.success) setDevices(r.data) }) window.electronAPI.hotkey.getDictationShortcut().then((r) => { if (r.success && r.data) setHotkeyBinding(r.data) }) }, [open]) const handleFinish = () => { // 온보딩 완료 플래그 저장 window.electronAPI.config.set({ key: 'onboardingCompleted' as keyof import('@shared/types').AppConfig, value: true as never }) onClose() } const handleHotkeySave = (binding: HotkeyBinding) => { setHotkeyBinding(binding) window.electronAPI.hotkey.setDictationShortcut({ binding }) window.electronAPI.hotkey.setEnabled({ enabled: true }) } return ( <> {/* Step 0: 환영 */} {step === 0 && ( D3RO-VOICE {t('onboarding.welcome.desc')} )} {/* Step 1: 마이크 */} {step === 1 && ( {t('onboarding.mic.title')} {t('onboarding.mic.desc')} {devices.map((d, idx) => ( { setSelectedDevice(d.deviceId) window.electronAPI.audio.setSelectedDevice({ deviceId: d.deviceId }) }} sx={{ p: 1.5, borderRadius: '8px', cursor: 'pointer', bgcolor: selectedDevice === d.deviceId ? d3roPalette.accent.amberDim : d3roPalette.bg.inset, border: selectedDevice === d.deviceId ? `1px solid ${d3roPalette.accent.amber}` : `1px solid ${d3roPalette.border.subtle}`, '&:hover': { bgcolor: d3roPalette.bg.cardHover }, }} > {d.label}{d.isDefault ? ` ${t('settings.deviceDefault')}` : ''} ))} )} {/* Step 2: 핫키 */} {step === 2 && ( {t('onboarding.hotkey.title')} {t('onboarding.hotkey.desc')} {hotkeyBinding ? ( {hotkeyBinding.displayLabel.split(' + ').map((key) => ( ))} ) : ( {t('onboarding.hotkey.notSet')} )} )} {/* Step 3: Ollama 설치 */} {step === 3 && ( {t('onboarding.ollama.title')} {t('onboarding.ollama.desc')} $ ollama pull qwen3:4b {t('onboarding.ollama.modelHint')} )} {/* Step 4: 완료 */} {step === 4 && ( {t('onboarding.done.title')} {hotkeyBinding ? t('onboarding.done.descWithKey', { key: hotkeyBinding.displayLabel }) : t('onboarding.done.descNoKey')} )} setHotkeyModalOpen(false)} onSave={handleHotkeySave} currentBinding={hotkeyBinding} title={t('hotkey.dictationTitle')} /> ) }