Phase 3.5 구현: 커서 위치 히스토리 팝업 (D3RO 고유 기능)

- HistoryPopup: Vanilla JS, 다크 카드(#242427), 앰버 악센트(#f25b29)
- Ctrl+Shift+V → 커서 위치에 최근 10건 히스토리 팝업
- Arrow↑↓ 선택, Enter 붙여넣기, 1-9 직접 선택, ESC 닫기
- focusable: false → 활성 앱 포커스 유지
- 2-phase 리사이즈, 등장/퇴장 애니메이션
- WindowManager: HistoryPopup 관리 + 프리로딩
- Bootstrap: globalShortcut 등록 + IPC 연동
This commit is contained in:
Yun Chan 2026-04-05 02:37:35 +09:00
parent 291e2a29d0
commit f131b581a9
7 changed files with 459 additions and 5 deletions

View file

@ -1,6 +1,6 @@
// src/main/bootstrap.ts — 초기화 시퀀스
import { app, dialog } from 'electron'
import { app, dialog, globalShortcut, ipcMain as ipcMainRef } from 'electron'
import { initLoggerService, getLogger } from './services/LoggerService'
import { initConfigService } from './services/ConfigService'
import { getHotkeyService } from './services/HotkeyService'
@ -15,7 +15,11 @@ import {
hideRecordingTip,
updateRecordingTipState,
sendAudioLevelToTip,
showResultPopup
showResultPopup,
showHistoryPopup,
hideHistoryPopup,
sendKeyToHistoryPopup,
isHistoryPopupVisible
} from './windows/WindowManager'
import { createTray } from './windows/TrayManager'
import { registerAllIpcHandlers } from './ipc'
@ -92,6 +96,17 @@ async function initHotkey(): Promise<void> {
async function initPopupWindows(): Promise<void> {
preloadPopupWindows()
setupHistoryPopupIPC()
// Ctrl+Shift+V → 히스토리 팝업 토글
globalShortcut.register('Ctrl+Shift+V', () => {
if (isHistoryPopupVisible()) {
hideHistoryPopup()
} else {
const entries = getHistoryService().list({ page: 0, pageSize: 10 }).entries
showHistoryPopup(entries as unknown as Array<Record<string, unknown>>)
}
})
}
async function initVoiceMode(): Promise<void> {
@ -149,3 +164,33 @@ async function initLLMPolling(): Promise<void> {
const llm = getLocalLLMService()
llm.startPolling()
}
// ── HistoryPopup IPC 연동 ────────────────────────────
function setupHistoryPopupIPC(): void {
const { ipcMain } = require('electron')
const { getTextInsertService } = require('./services/TextInsertService')
// 히스토리 팝업 열기 (핫키에서 호출)
ipcMain.on('history:showPopup', () => {
const entries = getHistoryService().list({ page: 0, pageSize: 10 }).entries
showHistoryPopup(entries as unknown as Array<Record<string, unknown>>)
})
// 아이템 선택 → 텍스트 삽입
ipcMain.on('history:itemSelected', (_event: Electron.IpcMainEvent, data: { text: string }) => {
hideHistoryPopup()
setTimeout(async () => {
try {
await getTextInsertService().insertText(data.text)
} catch (error) {
logger.warn(`History popup insert failed: ${error instanceof Error ? error.message : String(error)}`)
}
}, 200)
})
// 팝업 닫기
ipcMain.on('history:popupDismissed', () => {
hideHistoryPopup()
})
}