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:
parent
b820c789bb
commit
aef44289a5
15 changed files with 161 additions and 126 deletions
|
|
@ -1,10 +1,11 @@
|
|||
// src/main/windows/WindowManager.ts
|
||||
// 설계서 01 WindowManagerService: 메인 윈도우 + 팝업 프리로딩 + 2-phase 리사이즈
|
||||
|
||||
import { BrowserWindow, shell, screen, ipcMain, Menu } from 'electron'
|
||||
import { BrowserWindow, shell, screen, ipcMain, Menu, clipboard } from 'electron'
|
||||
import { join } from 'path'
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import { WINDOW_SIZE } from '@d3ro/core/constants'
|
||||
import { IPC_CHANNELS } from '@d3ro/core/ipc-channels'
|
||||
import { getLogger } from '../services/LoggerService'
|
||||
import { getIsQuitting } from '../lifecycle'
|
||||
import { configGet } from '../services/ConfigService'
|
||||
|
|
@ -180,7 +181,7 @@ export function showRecordingTip(
|
|||
win.setBounds({ x, y, width: TIP_WIDTH, height: TIP_HEIGHT })
|
||||
|
||||
// 상태 전송 후 즉시 show (2-phase 제거 — 숨겨진 윈도우의 렌더러 비활성 문제 방지)
|
||||
win.webContents.send('window:tipStateChanged', { state, ...params })
|
||||
win.webContents.send(IPC_CHANNELS.WINDOW.TIP_STATE_CHANGED, { state, ...params })
|
||||
if (!win.isVisible()) {
|
||||
win.showInactive()
|
||||
}
|
||||
|
|
@ -197,20 +198,20 @@ export function updateRecordingTipState(
|
|||
params?: { text?: string; errorMessage?: string }
|
||||
): void {
|
||||
if (recordingTipWindow && !recordingTipWindow.isDestroyed()) {
|
||||
recordingTipWindow.webContents.send('window:tipStateChanged', { state, ...params })
|
||||
recordingTipWindow.webContents.send(IPC_CHANNELS.WINDOW.TIP_STATE_CHANGED, { state, ...params })
|
||||
}
|
||||
}
|
||||
|
||||
export function sendAudioLevelToTip(level: number): void {
|
||||
if (recordingTipWindow && !recordingTipWindow.isDestroyed()) {
|
||||
recordingTipWindow.webContents.send('voice:audioLevel', { level })
|
||||
recordingTipWindow.webContents.send(IPC_CHANNELS.VOICE.AUDIO_LEVEL, { level })
|
||||
}
|
||||
}
|
||||
|
||||
/** 실시간 부분 전사 텍스트를 RecordingTip에 전달 */
|
||||
export function sendPartialTranscriptToTip(text: string): void {
|
||||
if (recordingTipWindow && !recordingTipWindow.isDestroyed()) {
|
||||
recordingTipWindow.webContents.send('voice:partialTranscript', { text })
|
||||
recordingTipWindow.webContents.send(IPC_CHANNELS.VOICE_PARTIAL.PARTIAL_TRANSCRIPT, { text })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -267,10 +268,10 @@ export function showResultPopup(text: string, autoHideMs = 5000): void {
|
|||
const win = getResultPopupWindow()
|
||||
|
||||
// Phase 1: prepare
|
||||
win.webContents.send('result:prepare', { text })
|
||||
win.webContents.send(IPC_CHANNELS.POPUP_RESULT.PREPARE, { text })
|
||||
|
||||
const handler = (_event: Electron.IpcMainEvent, data: { width: number; height: number }) => {
|
||||
ipcMain.removeListener('result:measured', handler)
|
||||
ipcMain.removeListener(IPC_CHANNELS.POPUP_RESULT.MEASURED, handler)
|
||||
|
||||
const cursorPos = screen.getCursorScreenPoint()
|
||||
const display = screen.getDisplayNearestPoint(cursorPos)
|
||||
|
|
@ -290,10 +291,10 @@ export function showResultPopup(text: string, autoHideMs = 5000): void {
|
|||
}
|
||||
|
||||
// Phase 2: show
|
||||
win.webContents.send('result:show', { autoHideMs })
|
||||
win.webContents.send(IPC_CHANNELS.POPUP_RESULT.SHOW, { autoHideMs })
|
||||
}
|
||||
|
||||
ipcMain.on('result:measured', handler)
|
||||
ipcMain.on(IPC_CHANNELS.POPUP_RESULT.MEASURED, handler)
|
||||
}
|
||||
|
||||
export function hideResultPopup(): void {
|
||||
|
|
@ -368,18 +369,18 @@ export function showHistoryPopup(entries: Array<Record<string, unknown>>): void
|
|||
|
||||
win.setBounds({ x, y, width: popupWidth, height: popupHeight })
|
||||
|
||||
win.webContents.send('history:showItems', { entries })
|
||||
win.webContents.send(IPC_CHANNELS.POPUP_HISTORY.SHOW_ITEMS, { entries })
|
||||
|
||||
if (!win.isVisible()) {
|
||||
win.showInactive()
|
||||
}
|
||||
|
||||
win.webContents.send('history:show', {})
|
||||
win.webContents.send(IPC_CHANNELS.POPUP_HISTORY.SHOW, {})
|
||||
}
|
||||
|
||||
export function hideHistoryPopup(): void {
|
||||
if (historyPopupWindow && !historyPopupWindow.isDestroyed()) {
|
||||
historyPopupWindow.webContents.send('history:hide', {})
|
||||
historyPopupWindow.webContents.send(IPC_CHANNELS.POPUP_HISTORY.HIDE, {})
|
||||
setTimeout(() => {
|
||||
if (historyPopupWindow && !historyPopupWindow.isDestroyed()) {
|
||||
historyPopupWindow.hide()
|
||||
|
|
@ -390,7 +391,7 @@ export function hideHistoryPopup(): void {
|
|||
|
||||
export function sendKeyToHistoryPopup(key: string): void {
|
||||
if (historyPopupWindow && !historyPopupWindow.isDestroyed() && historyPopupWindow.isVisible()) {
|
||||
historyPopupWindow.webContents.send('history:keyEvent', { key })
|
||||
historyPopupWindow.webContents.send(IPC_CHANNELS.POPUP_HISTORY.KEY_EVENT, { key })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -455,15 +456,15 @@ export function showCommandPopup(commands: Array<Record<string, unknown>>, activ
|
|||
if (y < display.workArea.y) { y = cursorPos.y + 20 }
|
||||
|
||||
win.setBounds({ x, y, width: popupWidth, height: popupHeight })
|
||||
win.webContents.send('command:showItems', { commands, activeId })
|
||||
win.webContents.send(IPC_CHANNELS.POPUP_COMMAND.SHOW_ITEMS, { commands, activeId })
|
||||
|
||||
if (!win.isVisible()) { win.showInactive() }
|
||||
win.webContents.send('command:show', {})
|
||||
win.webContents.send(IPC_CHANNELS.POPUP_COMMAND.SHOW, {})
|
||||
}
|
||||
|
||||
export function hideCommandPopup(): void {
|
||||
if (commandPopupWindow && !commandPopupWindow.isDestroyed()) {
|
||||
commandPopupWindow.webContents.send('command:hide', {})
|
||||
commandPopupWindow.webContents.send(IPC_CHANNELS.POPUP_COMMAND.HIDE, {})
|
||||
setTimeout(() => {
|
||||
if (commandPopupWindow && !commandPopupWindow.isDestroyed()) {
|
||||
commandPopupWindow.hide()
|
||||
|
|
@ -474,7 +475,7 @@ export function hideCommandPopup(): void {
|
|||
|
||||
export function sendKeyToCommandPopup(key: string): void {
|
||||
if (commandPopupWindow && !commandPopupWindow.isDestroyed() && commandPopupWindow.isVisible()) {
|
||||
commandPopupWindow.webContents.send('command:keyEvent', { key })
|
||||
commandPopupWindow.webContents.send(IPC_CHANNELS.POPUP_COMMAND.KEY_EVENT, { key })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -547,7 +548,7 @@ export function showCaptionOverlay(): void {
|
|||
|
||||
export function hideCaptionOverlay(): void {
|
||||
if (captionOverlayWindow && !captionOverlayWindow.isDestroyed()) {
|
||||
captionOverlayWindow.webContents.send('caption:hide', {})
|
||||
captionOverlayWindow.webContents.send(IPC_CHANNELS.POPUP_CAPTION.HIDE, {})
|
||||
captionOverlayWindow.hide()
|
||||
}
|
||||
}
|
||||
|
|
@ -591,7 +592,6 @@ export function reapplyThemeToAllPopups(): void {
|
|||
|
||||
// ── clipboard:copy IPC (ResultPopup에서 사용) ─────────
|
||||
|
||||
ipcMain.on('clipboard:copy', (_event, text: string) => {
|
||||
const { clipboard } = require('electron')
|
||||
ipcMain.on(IPC_CHANNELS.CLIPBOARD.COPY, (_event, text: string) => {
|
||||
clipboard.writeText(text)
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue