AeThex-Connect/packages/desktop/src/main/index.ts
2026-01-10 08:00:59 +00:00

342 lines
8 KiB
TypeScript

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);
});