refactor(main): IPC 채널 SSOT 일원화 + AppConfig 타입 강화 (WS2)

- ipc-channels.ts SSOT에 9개 그룹/키 추가: INSTRUCTION, SYSTEM_AUDIO,
  POPUP_RESULT/HISTORY/COMMAND/CAPTION, VOICE_PARTIAL, CLIPBOARD, APP,
  VOICE_CONVERSATION.FINISH_LISTENING
- WindowManager/handlers/bootstrap 하드코딩 채널 -> IPC_CHANNELS 교체.
  불일치 4건(result:* vs window:tip*)은 preload 재검증 후 SSOT로 통일.
  notifyRenderer channel: string -> IPCChannel 타입 좁힘.
- AppConfig에 7개 누락 키 추가(customInstructions, llmChains,
  voiceCommandRules, voiceCommandsEnabled, activeInstructionId,
  activeChainId, captionAudioSource) -> as never 16건 제거.
- ChainService/CustomInstructionService/WindowManager dynamic require -> static import.
- services/index.ts 데드 레지스트리 제거 (import 0건).
SKIP: ipcSuccess/ipcError 헬퍼 통일, catch 패턴(별도)
정책: docs/REFACTOR_POLICY.md DP2, DP9
This commit is contained in:
Yun Chan 2026-07-22 01:54:18 +09:00
parent b820c789bb
commit aef44289a5
15 changed files with 161 additions and 126 deletions

View file

@ -33,6 +33,8 @@ import {
} from './windows/WindowManager'
import { createTray } from './windows/TrayManager'
import { registerAllIpcHandlers } from './ipc'
import { IPC_CHANNELS } from '@d3ro/core/ipc-channels'
import type { IPCChannel } from '@d3ro/core/ipc-channels'
const logger = getLogger('bootstrap')
@ -171,7 +173,7 @@ async function initPopupWindows(): Promise<void> {
unregisterPopupNavKeys()
} else {
const instructions = getCustomInstructionService().getAll()
const activeId = configGet('activeInstructionId' as keyof import('@d3ro/core/types').AppConfig) as unknown as string
const activeId = configGet('activeInstructionId') as string | null
showCommandPopup(instructions as unknown as Array<Record<string, unknown>>, activeId || null)
registerPopupNavKeys('command')
}
@ -250,7 +252,7 @@ async function initVoiceMode(): Promise<void> {
}
// 렌더러 UI 갱신 알림 (Dashboard 통계 + History 목록)
notifyRenderer('app:dataChanged', { type: 'session-completed' })
notifyRenderer(IPC_CHANNELS.APP.DATA_CHANGED, { type: 'session-completed' })
})
voiceMode.on('session-cancelled', ({ reason }) => {
@ -268,7 +270,7 @@ async function initVoiceMode(): Promise<void> {
}
/** 메인 윈도우 렌더러에 UI 갱신 이벤트 전송 */
function notifyRenderer(channel: string, data?: Record<string, unknown>): void {
function notifyRenderer(channel: IPCChannel, data?: Record<string, unknown>): void {
const win = getMainWindow()
if (win && !win.isDestroyed()) {
win.webContents.send(channel, data ?? {})
@ -330,13 +332,13 @@ async function initMeetingMode(): Promise<void> {
function setupHistoryPopupIPC(): void {
// 히스토리 팝업 열기 (핫키에서 호출)
ipcMainRef.on('history:showPopup', () => {
ipcMainRef.on(IPC_CHANNELS.POPUP_HISTORY.SHOW_POPUP, () => {
const entries = getHistoryService().list({ page: 0, pageSize: 10 }).entries
showHistoryPopup(entries as unknown as Array<Record<string, unknown>>)
})
// 아이템 선택 → 텍스트 삽입
ipcMainRef.on('history:itemSelected', (_event: Electron.IpcMainEvent, data: { text: string }) => {
ipcMainRef.on(IPC_CHANNELS.POPUP_HISTORY.ITEM_SELECTED, (_event: Electron.IpcMainEvent, data: { text: string }) => {
hideHistoryPopup()
unregisterPopupNavKeys()
setTimeout(async () => {
@ -349,7 +351,7 @@ function setupHistoryPopupIPC(): void {
})
// 팝업 닫기
ipcMainRef.on('history:popupDismissed', () => {
ipcMainRef.on(IPC_CHANNELS.POPUP_HISTORY.POPUP_DISMISSED, () => {
hideHistoryPopup()
unregisterPopupNavKeys()
})
@ -359,28 +361,28 @@ function setupHistoryPopupIPC(): void {
function setupCommandPopupIPC(): void {
// 명령어 선택 → 활성 명령어로 설정
ipcMainRef.on('command:selected', (_event: Electron.IpcMainEvent, data: { id: string; name: string }) => {
ipcMainRef.on(IPC_CHANNELS.POPUP_COMMAND.SELECTED, (_event: Electron.IpcMainEvent, data: { id: string; name: string }) => {
hideCommandPopup()
unregisterPopupNavKeys()
if (data.id) {
// 명령어 선택 → 활성 명령어로 설정
configSet('activeInstructionId' as keyof import('@d3ro/core/types').AppConfig, data.id as never)
configSet('activeInstructionId', data.id)
configSet('defaultLLMAction', 'custom')
logger.info(`Active command set: ${data.name} (${data.id})`)
} else {
// 선택 해제 → 명령어 없음 (원본 삽입)
configSet('activeInstructionId' as keyof import('@d3ro/core/types').AppConfig, '' as never)
configSet('activeInstructionId', '')
configSet('defaultLLMAction', 'none')
logger.info('Active command cleared (none)')
}
// CMD 페이지 UI 갱신 알림
notifyRenderer('app:dataChanged', { type: 'command-changed', activeId: data.id })
notifyRenderer(IPC_CHANNELS.APP.DATA_CHANGED, { type: 'command-changed', activeId: data.id })
})
// 팝업 닫기
ipcMainRef.on('command:dismissed', () => {
ipcMainRef.on(IPC_CHANNELS.POPUP_COMMAND.DISMISSED, () => {
hideCommandPopup()
unregisterPopupNavKeys()
})