// src/renderer/App.tsx — 루트 컴포넌트 // 테마 시스템: auto(시스템) / dark / light. 기본은 auto → 다크. // i18n: I18nProvider가 전체 트리를 감쌈. ConfigService에서 언어 로드. import { useState, useEffect, useMemo, useRef } from 'react' import { ThemeProvider, CssBaseline, useMediaQuery } from '@mui/material' import { getTheme } from './theme' import { I18nProvider } from './i18n' import { AppLayout } from './components/AppLayout' import { UpgradePromptModal } from './components/UpgradePromptModal' import { startSystemAudioCapture, stopSystemAudioCapture } from './utils/systemAudioCapture' import type { ThemeMode, ConfigChangedEvent } from '@shared/types' export function App(): React.ReactElement { const [themeMode, setThemeMode] = useState('auto') const prefersDark = useMediaQuery('(prefers-color-scheme: dark)') const systemAudioCleanupRef = useRef<(() => void) | null>(null) // 설정에서 테마 로드 + 변경 감지 useEffect(() => { window.electronAPI.config.getTheme().then((result) => { if (result.success) { setThemeMode(result.data) } }) const unsub = window.electronAPI.config.onChanged((e: ConfigChangedEvent) => { if (e.key === 'theme') { setThemeMode(e.value as ThemeMode) } }) return unsub }, []) // 시스템 오디오 캡처: 메인 프로세스의 시작/중지 요청에 응답 useEffect(() => { const unsubStart = window.electronAPI.caption.onStartSystemAudio(() => { startSystemAudioCapture((pcm16Buffer) => { window.electronAPI.caption.sendSystemAudioData(pcm16Buffer) }).catch(() => { // 시스템 오디오 캡처 실패 시 무시 — 마이크 자막만 사용 }) }) const unsubStop = window.electronAPI.caption.onStopSystemAudio(() => { stopSystemAudioCapture() }) systemAudioCleanupRef.current = () => { stopSystemAudioCapture() } return () => { unsubStart() unsubStop() systemAudioCleanupRef.current?.() } }, []) const theme = useMemo(() => getTheme(themeMode, prefersDark), [themeMode, prefersDark]) return ( ) }