import { app, BrowserWindow, ipcMain, screen, globalShortcut, clipboard } from 'electron' import { join } from 'path' let floatingWindow: BrowserWindow | null = null let settingsWindow: BrowserWindow | null = null let floatingWindowReady = false let tooltipOpenCache = false function createFloatingWindow(): void { const { width } = screen.getPrimaryDisplay().workAreaSize floatingWindow = new BrowserWindow({ width: 400, height: 400, x: width - 420, y: 60, frame: false, transparent: true, alwaysOnTop: true, skipTaskbar: true, resizable: false, hasShadow: false, webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false, nodeIntegration: false, contextIsolation: true } }) floatingWindow.setIgnoreMouseEvents(true, { forward: true }) floatingWindow.setAlwaysOnTop(true, 'floating') floatingWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true }) // Mark window as ready when content is loaded floatingWindow.webContents.on('did-finish-load', () => { floatingWindowReady = true }) // Handle mouse enter/leave events to control click-through ipcMain.on('set-ignore-mouse-events', (_, ignore: boolean, options?: { forward: boolean }) => { if (floatingWindow) { floatingWindow.setIgnoreMouseEvents(ignore, options) } }) // Handle open settings from renderer ipcMain.on('open-settings', () => { createSettingsWindow() }) // Handle quit app from renderer ipcMain.on('quit-app', () => { app.quit() }) // Load the floating window HTML if (process.env['ELECTRON_RENDERER_URL']) { floatingWindow.loadURL(`${process.env['ELECTRON_RENDERER_URL']}/floating.html`) } else { floatingWindow.loadFile(join(__dirname, '../renderer/floating.html')) } // Handle window drag ipcMain.on('floating-window-move', (_, { x, y }) => { if (floatingWindow) { floatingWindow.setPosition(x, y) } }) // Handle get window bounds ipcMain.handle('get-window-bounds', () => { if (floatingWindow) { const bounds = floatingWindow.getBounds() return { x: bounds.x, y: bounds.y } } return { x: 0, y: 0 } }) // Listen for tooltip state changes from renderer to update cache ipcMain.on('tooltip-state-changed', (_, isOpen: boolean) => { tooltipOpenCache = isOpen }) // Handle check if tooltip is open ipcMain.handle('is-tooltip-open', () => { if (floatingWindow) { return floatingWindow.webContents.executeJavaScript('window.__tooltipOpen || false') } return false }) } function createSettingsWindow(): void { // If settings window already exists, focus it if (settingsWindow && !settingsWindow.isDestroyed()) { settingsWindow.focus() return } settingsWindow = new BrowserWindow({ width: 900, height: 600, title: '设置', resizable: false, webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false, nodeIntegration: false, contextIsolation: true } }) // Load settings page if (process.env['ELECTRON_RENDERER_URL']) { settingsWindow.loadURL(`${process.env['ELECTRON_RENDERER_URL']}/settings.html`) } else { settingsWindow.loadFile(join(__dirname, '../renderer/settings.html')) } settingsWindow.on('closed', () => { settingsWindow = null }) } function registerGlobalShortcuts(): void { // Register Cmd+K (Mac) or Ctrl+K (Windows/Linux) const shortcut = process.platform === 'darwin' ? 'Command+K' : 'Control+K' const registered = globalShortcut.register(shortcut, () => { if (floatingWindow && !floatingWindow.isDestroyed() && floatingWindowReady) { // Get clipboard text immediately for faster response const selectedText = clipboard.readText('selection') const text = selectedText || clipboard.readText() // Use cached state for instant response if (tooltipOpenCache) { // If tooltip is open, close it tooltipOpenCache = false floatingWindow.webContents.send('close-tooltip') } else { // If tooltip is closed, open it with selected text if (text && text.trim()) { tooltipOpenCache = true floatingWindow.webContents.send('show-text-action-prompt', text) floatingWindow.focus() } } } }) if (!registered) { console.error('Global shortcut registration failed') } } // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.whenReady().then(() => { // IPC test ipcMain.on('ping', () => console.log('pong')) createFloatingWindow() registerGlobalShortcuts() app.on('activate', function () { // On macOS it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (BrowserWindow.getAllWindows().length === 0) { createFloatingWindow() } }) }) // Quit when all windows are closed, except on macOS. There, it's common // for applications and their menu bar to stay active until the user quits // explicitly with Cmd + Q. app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) // Unregister all shortcuts when app is about to quit app.on('will-quit', () => { globalShortcut.unregisterAll() }) // In this file you can include the rest of your app's specific main process // code. You can also put them in separate files and require them here.