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:
parent
291e2a29d0
commit
f131b581a9
7 changed files with 459 additions and 5 deletions
|
|
@ -16,6 +16,7 @@ const logger = getLogger('WindowManager')
|
|||
let mainWindow: BrowserWindow | null = null
|
||||
let recordingTipWindow: BrowserWindow | null = null
|
||||
let resultPopupWindow: BrowserWindow | null = null
|
||||
let historyPopupWindow: BrowserWindow | null = null
|
||||
|
||||
// ── 메인 윈도우 ───────────────────────────────────────
|
||||
|
||||
|
|
@ -262,11 +263,106 @@ export function hideResultPopup(): void {
|
|||
}
|
||||
}
|
||||
|
||||
// ── HistoryPopup 팝업 ─────────────────────────────────
|
||||
|
||||
function createHistoryPopupWindow(): BrowserWindow {
|
||||
const win = new BrowserWindow({
|
||||
width: 360,
|
||||
height: 400,
|
||||
show: false,
|
||||
frame: false,
|
||||
transparent: true,
|
||||
resizable: false,
|
||||
alwaysOnTop: true,
|
||||
skipTaskbar: true,
|
||||
focusable: false,
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/popup.js'),
|
||||
sandbox: false,
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false
|
||||
}
|
||||
})
|
||||
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
win.loadURL(`${process.env['ELECTRON_RENDERER_URL']}/popups/history-popup/index.html`)
|
||||
} else {
|
||||
win.loadFile(join(__dirname, '../renderer/popups/history-popup/index.html'))
|
||||
}
|
||||
|
||||
win.on('closed', () => {
|
||||
historyPopupWindow = null
|
||||
})
|
||||
|
||||
return win
|
||||
}
|
||||
|
||||
export function getHistoryPopupWindow(): BrowserWindow {
|
||||
if (!historyPopupWindow || historyPopupWindow.isDestroyed()) {
|
||||
historyPopupWindow = createHistoryPopupWindow()
|
||||
logger.info('HistoryPopup window created')
|
||||
}
|
||||
return historyPopupWindow
|
||||
}
|
||||
|
||||
export function showHistoryPopup(entries: Array<Record<string, unknown>>): void {
|
||||
const win = getHistoryPopupWindow()
|
||||
|
||||
win.webContents.send('history:showItems', { entries })
|
||||
|
||||
const handler = (_event: Electron.IpcMainEvent, data: { width: number; height: number }) => {
|
||||
ipcMain.removeListener('history:popupMeasured', handler)
|
||||
|
||||
const cursorPos = screen.getCursorScreenPoint()
|
||||
const display = screen.getDisplayNearestPoint(cursorPos)
|
||||
|
||||
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', {})
|
||||
}
|
||||
|
||||
ipcMain.on('history:popupMeasured', handler)
|
||||
}
|
||||
|
||||
export function hideHistoryPopup(): void {
|
||||
if (historyPopupWindow && !historyPopupWindow.isDestroyed()) {
|
||||
historyPopupWindow.webContents.send('history:hide', {})
|
||||
setTimeout(() => {
|
||||
if (historyPopupWindow && !historyPopupWindow.isDestroyed()) {
|
||||
historyPopupWindow.hide()
|
||||
}
|
||||
}, 150)
|
||||
}
|
||||
}
|
||||
|
||||
export function sendKeyToHistoryPopup(key: string): void {
|
||||
if (historyPopupWindow && !historyPopupWindow.isDestroyed() && historyPopupWindow.isVisible()) {
|
||||
historyPopupWindow.webContents.send('history:keyEvent', { key })
|
||||
}
|
||||
}
|
||||
|
||||
export function isHistoryPopupVisible(): boolean {
|
||||
return !!(historyPopupWindow && !historyPopupWindow.isDestroyed() && historyPopupWindow.isVisible())
|
||||
}
|
||||
|
||||
// ── 프리로딩 ──────────────────────────────────────────
|
||||
|
||||
export function preloadPopupWindows(): void {
|
||||
getRecordingTipWindow()
|
||||
getResultPopupWindow()
|
||||
getHistoryPopupWindow()
|
||||
logger.info('Popup windows preloaded')
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue