import { app, BrowserWindow, Tray, Menu, globalShortcut, ipcMain, nativeImage, Notification } from 'electron'; import path from 'path'; import Store from 'electron-store'; import { autoUpdater } from 'electron-updater'; const store = new Store(); let mainWindow: BrowserWindow | null = null; let tray: Tray | null = null; // Single instance lock const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { app.quit(); } else { app.on('second-instance', () => { if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore(); mainWindow.focus(); } }); } function createWindow() { mainWindow = new BrowserWindow({ width: 1200, height: 800, minWidth: 800, minHeight: 600, backgroundColor: '#1a1a1a', show: false, webPreferences: { nodeIntegration: false, contextIsolation: true, preload: path.join(__dirname, 'preload.js'), }, titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default', frame: process.platform !== 'win32', icon: path.join(__dirname, '../../assets/icon.png'), }); // Load app if (process.env.NODE_ENV === 'development') { mainWindow.loadURL('http://localhost:5173'); mainWindow.webContents.openDevTools(); } else { mainWindow.loadFile(path.join(__dirname, '../renderer/index.html')); } // Show when ready mainWindow.once('ready-to-show', () => { mainWindow?.show(); checkForUpdates(); }); // Minimize to tray instead of closing mainWindow.on('close', (event) => { if (!app.isQuitting && store.get('minimizeToTray', true)) { event.preventDefault(); mainWindow?.hide(); if (process.platform === 'darwin') { app.dock.hide(); } } }); mainWindow.on('closed', () => { mainWindow = null; }); } function createTray() { const iconPath = path.join(__dirname, '../../assets/tray-icon.png'); const icon = nativeImage.createFromPath(iconPath); tray = new Tray(icon.resize({ width: 16, height: 16 })); updateTrayMenu(); tray.setToolTip('AeThex Connect'); tray.on('click', () => { if (mainWindow) { if (mainWindow.isVisible()) { mainWindow.hide(); } else { mainWindow.show(); if (process.platform === 'darwin') { app.dock.show(); } } } }); tray.on('right-click', () => { tray?.popUpContextMenu(); }); } function updateTrayMenu() { if (!tray) return; const muted = store.get('muted', false) as boolean; const deafened = store.get('deafened', false) as boolean; const contextMenu = Menu.buildFromTemplate([ { label: 'Open AeThex Connect', click: () => { mainWindow?.show(); if (process.platform === 'darwin') { app.dock.show(); } }, }, { type: 'separator' }, { label: 'Mute', type: 'checkbox', checked: muted, click: (menuItem) => { store.set('muted', menuItem.checked); mainWindow?.webContents.send('toggle-mute', menuItem.checked); updateTrayMenu(); }, }, { label: 'Deafen', type: 'checkbox', checked: deafened, click: (menuItem) => { store.set('deafened', menuItem.checked); mainWindow?.webContents.send('toggle-deafen', menuItem.checked); updateTrayMenu(); }, }, { type: 'separator' }, { label: 'Settings', click: () => { mainWindow?.webContents.send('open-settings'); mainWindow?.show(); }, }, { type: 'separator' }, { label: 'Check for Updates', click: () => { checkForUpdates(); }, }, { type: 'separator' }, { label: 'Quit', click: () => { app.isQuitting = true; app.quit(); }, }, ]); tray.setContextMenu(contextMenu); } function registerGlobalShortcuts() { // Push-to-talk (Ctrl+Shift+T by default) const pttShortcut = (store.get('pttShortcut', 'CommandOrControl+Shift+T') as string); globalShortcut.register(pttShortcut, () => { mainWindow?.webContents.send('push-to-talk-pressed'); }); // Toggle mute (Ctrl+Shift+M) globalShortcut.register('CommandOrControl+Shift+M', () => { const muted = !store.get('muted', false); store.set('muted', muted); mainWindow?.webContents.send('toggle-mute', muted); updateTrayMenu(); }); // Toggle deafen (Ctrl+Shift+D) globalShortcut.register('CommandOrControl+Shift+D', () => { const deafened = !store.get('deafened', false); store.set('deafened', deafened); mainWindow?.webContents.send('toggle-deafen', deafened); updateTrayMenu(); }); } // Auto-updater function checkForUpdates() { if (process.env.NODE_ENV === 'development') return; autoUpdater.checkForUpdatesAndNotify(); } autoUpdater.on('update-available', () => { const notification = new Notification({ title: 'Update Available', body: 'A new version of AeThex Connect is being downloaded.', }); notification.show(); }); autoUpdater.on('update-downloaded', () => { const notification = new Notification({ title: 'Update Ready', body: 'Restart AeThex Connect to apply the update.', }); notification.show(); notification.on('click', () => { autoUpdater.quitAndInstall(); }); }); // IPC Handlers // Rich Presence ipcMain.handle('set-rich-presence', async (event, activity) => { console.log('Rich presence:', activity); // TODO: Integrate with Discord RPC, Windows Game Bar, etc. return { success: true }; }); // Screen sharing sources ipcMain.handle('get-sources', async () => { const { desktopCapturer } = require('electron'); const sources = await desktopCapturer.getSources({ types: ['window', 'screen'], thumbnailSize: { width: 150, height: 150 }, }); return sources.map((source) => ({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataURL(), })); }); // Notifications ipcMain.on('show-notification', (event, { title, body, icon }) => { const notification = new Notification({ title: title, body: body, icon: icon || path.join(__dirname, '../../assets/icon.png'), }); notification.show(); notification.on('click', () => { mainWindow?.show(); }); }); // Auto-launch ipcMain.handle('get-auto-launch', async () => { return app.getLoginItemSettings().openAtLogin; }); ipcMain.handle('set-auto-launch', async (event, enabled: boolean) => { app.setLoginItemSettings({ openAtLogin: enabled, openAsHidden: false, }); return { success: true }; }); // Badge count (macOS) ipcMain.handle('set-badge-count', async (event, count: number) => { if (process.platform === 'darwin') { app.dock.setBadge(count > 0 ? String(count) : ''); } return { success: true }; }); // Window controls ipcMain.on('minimize-window', () => { mainWindow?.minimize(); }); ipcMain.on('maximize-window', () => { if (mainWindow?.isMaximized()) { mainWindow?.unmaximize(); } else { mainWindow?.maximize(); } }); ipcMain.on('close-window', () => { mainWindow?.close(); }); // App initialization app.whenReady().then(() => { createWindow(); createTray(); registerGlobalShortcuts(); // macOS - recreate window when dock icon clicked app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } else { mainWindow?.show(); if (process.platform === 'darwin') { app.dock.show(); } } }); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); app.on('before-quit', () => { app.isQuitting = true; }); app.on('will-quit', () => { globalShortcut.unregisterAll(); }); // Handle deep links (aethex:// protocol) if (process.defaultApp) { if (process.argv.length >= 2) { app.setAsDefaultProtocolClient('aethex', process.execPath, [ path.resolve(process.argv[1]), ]); } } else { app.setAsDefaultProtocolClient('aethex'); } app.on('open-url', (event, url) => { event.preventDefault(); console.log('Deep link:', url); mainWindow?.webContents.send('deep-link', url); });