From 6e9e8b1f29e7cfd021d7560d68a214d48f75d0f3 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Sun, 5 Apr 2026 12:38:54 +0900 Subject: [PATCH] =?UTF-8?q?=ED=9E=88=EC=8A=A4=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=ED=8C=9D=EC=97=85=20=EC=88=98=EC=A0=95:=20ESC=20=EB=8B=AB?= =?UTF-8?q?=EA=B8=B0=20+=20Arrow=20=ED=82=A4=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Arrow↑↓/Enter/Escape/1-9 키를 팝업 열릴 때 globalShortcut 등록, 닫힐 때 해제 - showHistoryPopup: 2-phase 제거 → 즉시 show (렌더러 비활성 문제 방지) - hideHistoryPopup/itemSelected/popupDismissed에서 nav 키 해제 --- src/main/bootstrap.ts | 40 +++++++++++++++++++++++++++++++ src/main/windows/WindowManager.ts | 40 +++++++++++++++---------------- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/main/bootstrap.ts b/src/main/bootstrap.ts index f1a7d17..c49a729 100644 --- a/src/main/bootstrap.ts +++ b/src/main/bootstrap.ts @@ -117,11 +117,49 @@ async function initPopupWindows(): Promise { globalShortcut.register('Ctrl+Shift+V', () => { if (isHistoryPopupVisible()) { hideHistoryPopup() + unregisterPopupNavKeys() } else { const entries = getHistoryService().list({ page: 0, pageSize: 10 }).entries showHistoryPopup(entries as unknown as Array>) + registerPopupNavKeys() } }) + +} + +// 히스토리 팝업 키 네비게이션 등록/해제 +const POPUP_NAV_KEYS: Array<{ accel: string; key: string }> = [ + { accel: 'Up', key: 'ArrowUp' }, + { accel: 'Down', key: 'ArrowDown' }, + { accel: 'Return', key: 'Enter' }, + { accel: 'Escape', key: 'Escape' }, + { accel: '1', key: '1' }, { accel: '2', key: '2' }, { accel: '3', key: '3' }, + { accel: '4', key: '4' }, { accel: '5', key: '5' }, { accel: '6', key: '6' }, + { accel: '7', key: '7' }, { accel: '8', key: '8' }, { accel: '9', key: '9' }, +] + +function registerPopupNavKeys(): void { + for (const { accel, key } of POPUP_NAV_KEYS) { + try { + globalShortcut.register(accel, () => { + if (isHistoryPopupVisible()) { + sendKeyToHistoryPopup(key) + } + }) + } catch { + // 일부 키는 globalShortcut으로 등록 불가할 수 있음 + } + } +} + +function unregisterPopupNavKeys(): void { + for (const { accel } of POPUP_NAV_KEYS) { + try { + globalShortcut.unregister(accel) + } catch { + // 이미 해제된 경우 무시 + } + } } async function initVoiceMode(): Promise { @@ -188,6 +226,7 @@ function setupHistoryPopupIPC(): void { // 아이템 선택 → 텍스트 삽입 ipcMainRef.on('history:itemSelected', (_event: Electron.IpcMainEvent, data: { text: string }) => { hideHistoryPopup() + unregisterPopupNavKeys() setTimeout(async () => { try { await getTextInsertService().insertText(data.text) @@ -200,5 +239,6 @@ function setupHistoryPopupIPC(): void { // 팝업 닫기 ipcMainRef.on('history:popupDismissed', () => { hideHistoryPopup() + unregisterPopupNavKeys() }) } diff --git a/src/main/windows/WindowManager.ts b/src/main/windows/WindowManager.ts index f7fc811..1f88d99 100644 --- a/src/main/windows/WindowManager.ts +++ b/src/main/windows/WindowManager.ts @@ -308,32 +308,30 @@ export function getHistoryPopupWindow(): BrowserWindow { export function showHistoryPopup(entries: Array>): void { const win = getHistoryPopupWindow() - win.webContents.send('history:showItems', { entries }) + // 커서 위치에 즉시 배치+표시 (2-phase 제거 — 숨겨진 윈도우 렌더러 비활성 문제 방지) + const cursorPos = screen.getCursorScreenPoint() + const display = screen.getDisplayNearestPoint(cursorPos) - const handler = (_event: Electron.IpcMainEvent, data: { width: number; height: number }) => { - ipcMain.removeListener('history:popupMeasured', handler) + const popupWidth = 380 + const popupHeight = Math.min(60 + entries.length * 44, 500) - const cursorPos = screen.getCursorScreenPoint() - const display = screen.getDisplayNearestPoint(cursorPos) + let x = cursorPos.x - Math.round(popupWidth / 2) + let y = cursorPos.y - popupHeight - 20 - let x = cursorPos.x - Math.round(data.width / 2) - let y = cursorPos.y - data.height - 20 - - x = Math.max(display.workArea.x, Math.min(x, display.workArea.x + display.workArea.width - data.width)) - if (y < display.workArea.y) { - y = cursorPos.y + 20 - } - - win.setBounds({ x, y, width: data.width, height: data.height }) - - if (!win.isVisible()) { - win.showInactive() - } - - win.webContents.send('history:show', {}) + x = Math.max(display.workArea.x, Math.min(x, display.workArea.x + display.workArea.width - popupWidth)) + if (y < display.workArea.y) { + y = cursorPos.y + 20 } - ipcMain.on('history:popupMeasured', handler) + win.setBounds({ x, y, width: popupWidth, height: popupHeight }) + + win.webContents.send('history:showItems', { entries }) + + if (!win.isVisible()) { + win.showInactive() + } + + win.webContents.send('history:show', {}) } export function hideHistoryPopup(): void {