diff --git a/src/main/bootstrap.ts b/src/main/bootstrap.ts index 82abc10..46b9257 100644 --- a/src/main/bootstrap.ts +++ b/src/main/bootstrap.ts @@ -15,6 +15,7 @@ import { getAutoLaunchService } from './services/AutoLaunchService' import { initDatabase } from './db' import { createMainWindow, + getMainWindow, preloadPopupWindows, showHistoryPopup, hideHistoryPopup, @@ -213,6 +214,9 @@ async function initVoiceMode(): Promise { } catch (error) { logger.warn(`Failed to save history: ${error instanceof Error ? error.message : String(error)}`) } + + // 렌더러 UI 갱신 알림 (Dashboard 통계 + History 목록) + notifyRenderer('app:dataChanged', { type: 'session-completed' }) }) voiceMode.on('session-cancelled', ({ reason }) => { @@ -229,6 +233,14 @@ async function initVoiceMode(): Promise { }) } +/** 메인 윈도우 렌더러에 UI 갱신 이벤트 전송 */ +function notifyRenderer(channel: string, data?: Record): void { + const win = getMainWindow() + if (win && !win.isDestroyed()) { + win.webContents.send(channel, data ?? {}) + } +} + async function initLLMPolling(): Promise { const llm = getLocalLLMService() llm.startPolling() @@ -275,6 +287,9 @@ function setupCommandPopupIPC(): void { configSet('activeInstructionId' as keyof import('@shared/types').AppConfig, data.id as never) configSet('defaultLLMAction', 'custom') logger.info(`Active command set: ${data.name} (${data.id})`) + + // CMD 페이지 UI 갱신 알림 + notifyRenderer('app:dataChanged', { type: 'command-changed', activeId: data.id }) }) // 팝업 닫기 diff --git a/src/preload/index.ts b/src/preload/index.ts index 8d52edb..f3ca195 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -243,6 +243,12 @@ const electronAPI = { invoke('instruction:update', params), delete: (params: { id: string }) => invoke('instruction:delete', params), reorder: (params: { ids: string[] }) => invoke('instruction:reorder', params), + }, + + // ── App 이벤트 (실시간 UI 갱신) ──────────────────────── + app: { + onDataChanged: (cb: (data: { type: string; activeId?: string }) => void): Unsubscribe => + on('app:dataChanged', cb), } } as const diff --git a/src/renderer/pages/CommandsPage.tsx b/src/renderer/pages/CommandsPage.tsx index ce26b35..bddb87a 100644 --- a/src/renderer/pages/CommandsPage.tsx +++ b/src/renderer/pages/CommandsPage.tsx @@ -46,7 +46,16 @@ export function CommandsPage(): React.ReactElement { setLoading(false) }, []) - useEffect(() => { loadData() }, [loadData]) + useEffect(() => { + loadData() + const unsub = window.electronAPI.app.onDataChanged((data) => { + if (data.type === 'command-changed' && data.activeId) { + setActiveId(data.activeId) + } + loadData() + }) + return unsub + }, [loadData]) const handleActivate = (id: string) => { const newId = activeId === id ? null : id // 토글: 같은 거 누르면 해제 diff --git a/src/renderer/pages/DashboardPage.tsx b/src/renderer/pages/DashboardPage.tsx index e17d0a8..e949eda 100644 --- a/src/renderer/pages/DashboardPage.tsx +++ b/src/renderer/pages/DashboardPage.tsx @@ -117,7 +117,9 @@ export function DashboardPage(): React.ReactElement { useEffect(() => { loadData() const interval = setInterval(loadData, 30000) - return () => clearInterval(interval) + // 실시간 갱신: 세션 완료/명령어 변경 시 즉시 리로드 + const unsub = window.electronAPI.app.onDataChanged(() => { loadData() }) + return () => { clearInterval(interval); unsub() } }, [loadData]) // 날짜별 그룹핑 diff --git a/src/renderer/pages/HistoryPage.tsx b/src/renderer/pages/HistoryPage.tsx index 95c2f1e..6fce813 100644 --- a/src/renderer/pages/HistoryPage.tsx +++ b/src/renderer/pages/HistoryPage.tsx @@ -54,7 +54,11 @@ export function HistoryPage(): React.ReactElement { setLoading(false) }, [search]) - useEffect(() => { loadData() }, [loadData]) + useEffect(() => { + loadData() + const unsub = window.electronAPI.app.onDataChanged(() => { loadData() }) + return unsub + }, [loadData]) // 날짜별 그룹핑 const groupedEntries = useMemo(() => {