diff --git a/surfsense_desktop/src/main.ts b/surfsense_desktop/src/main.ts index aff64db22..10f442c08 100644 --- a/surfsense_desktop/src/main.ts +++ b/surfsense_desktop/src/main.ts @@ -5,7 +5,9 @@ import { createMainWindow } from './modules/window'; import { setupDeepLinks, handlePendingDeepLink } from './modules/deep-links'; import { setupAutoUpdater } from './modules/auto-updater'; import { setupMenu } from './modules/menu'; +import { setupTray } from './modules/tray'; import { registerIpcHandlers } from './ipc/handlers'; +import { registerClipboardHandlers } from './modules/clipboard'; registerGlobalErrorHandlers(); @@ -14,6 +16,7 @@ if (!setupDeepLinks()) { } registerIpcHandlers(); +registerClipboardHandlers(); // App lifecycle app.whenReady().then(async () => { @@ -26,6 +29,7 @@ app.whenReady().then(async () => { return; } createMainWindow(); + setupTray(); setupAutoUpdater(); handlePendingDeepLink(); diff --git a/surfsense_desktop/src/modules/clipboard.ts b/surfsense_desktop/src/modules/clipboard.ts new file mode 100644 index 000000000..4f9d7b802 --- /dev/null +++ b/surfsense_desktop/src/modules/clipboard.ts @@ -0,0 +1,14 @@ +import { ipcMain } from 'electron'; +import { IPC_CHANNELS } from '../ipc/channels'; + +let lastClipboardContent = ''; + +export function setClipboardContent(text: string): void { + lastClipboardContent = text; +} + +export function registerClipboardHandlers(): void { + ipcMain.handle(IPC_CHANNELS.GET_CLIPBOARD_CONTENT, () => { + return lastClipboardContent; + }); +} diff --git a/surfsense_desktop/src/modules/tray.ts b/surfsense_desktop/src/modules/tray.ts new file mode 100644 index 000000000..3527cf691 --- /dev/null +++ b/surfsense_desktop/src/modules/tray.ts @@ -0,0 +1,73 @@ +import { app, BrowserWindow, clipboard, Menu, Tray } from 'electron'; +import path from 'path'; +import { getServerPort } from './server'; +import { setClipboardContent } from './clipboard'; + +let tray: Tray | null = null; +let clipWindow: BrowserWindow | null = null; + +function getIconPath(): string { + if (app.isPackaged) { + return path.join(process.resourcesPath, 'icon.png'); + } + return path.join(__dirname, '..', 'assets', 'icon.png'); +} + +function createClipWindow(): BrowserWindow { + if (clipWindow && !clipWindow.isDestroyed()) { + clipWindow.focus(); + return clipWindow; + } + + clipWindow = new BrowserWindow({ + width: 420, + height: 620, + resizable: true, + minimizable: false, + maximizable: false, + fullscreenable: false, + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + contextIsolation: true, + nodeIntegration: false, + sandbox: true, + }, + show: false, + titleBarStyle: 'hiddenInset', + }); + + clipWindow.loadURL(`http://localhost:${getServerPort()}/dashboard`); + + clipWindow.once('ready-to-show', () => { + clipWindow?.show(); + }); + + clipWindow.on('closed', () => { + clipWindow = null; + }); + + return clipWindow; +} + +export function setupTray(): void { + tray = new Tray(getIconPath()); + tray.setToolTip('SurfSense'); + + const contextMenu = Menu.buildFromTemplate([ + { + label: 'Ask about clipboard', + click: () => { + const text = clipboard.readText(); + setClipboardContent(text); + createClipWindow(); + }, + }, + { type: 'separator' }, + { + label: 'Quit', + click: () => app.quit(), + }, + ]); + + tray.setContextMenu(contextMenu); +}