const { app, BrowserWindow, ipcMain, desktopCapturer, shell } = require('electron'); const fs = require('fs'); const fsp = fs.promises; const path = require('path'); const { registerDatabaseIpc } = require('./database'); let mainWindow; // Suppress Autofill devtools errors by disabling related features app.commandLine.appendSwitch('disable-features', 'Autofill,AutofillAssistant,AutofillServerCommunication'); // Allow media autoplay without user gesture (bypasses Chromium autoplay policy) app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); // Accept self-signed certificates in development (for --ssl dev server) if (process.env.SSL === 'true') { app.commandLine.appendSwitch('ignore-certificate-errors'); } function createWindow() { mainWindow = new BrowserWindow({ width: 1400, height: 900, minWidth: 800, minHeight: 600, frame: false, titleBarStyle: 'hidden', backgroundColor: '#0a0a0f', webPreferences: { nodeIntegration: false, contextIsolation: true, preload: path.join(__dirname, 'preload.js'), webSecurity: true, }, }); // In development, load from Angular dev server if (process.env.NODE_ENV === 'development') { const devUrl = process.env.SSL === 'true' ? 'https://localhost:4200' : 'http://localhost:4200'; mainWindow.loadURL(devUrl); if (process.env.DEBUG_DEVTOOLS === '1') { mainWindow.webContents.openDevTools(); } } else { // In production, load the built Angular app // The dist folder is at the project root, not in electron folder mainWindow.loadFile(path.join(__dirname, '..', 'dist', 'client', 'browser', 'index.html')); } mainWindow.on('closed', () => { mainWindow = null; }); // Force all external links to open in the system default browser mainWindow.webContents.setWindowOpenHandler(({ url }) => { shell.openExternal(url); return { action: 'deny' }; }); mainWindow.webContents.on('will-navigate', (event, url) => { // Allow navigation to the app itself (dev server or file://) const currentUrl = mainWindow.webContents.getURL(); const isSameOrigin = new URL(url).origin === new URL(currentUrl).origin; if (!isSameOrigin) { event.preventDefault(); shell.openExternal(url); } }); } // Register database IPC handlers before app is ready registerDatabaseIpc(); // IPC handler for opening URLs in the system default browser ipcMain.handle('open-external', async (_event, url) => { if (typeof url === 'string' && (url.startsWith('http://') || url.startsWith('https://'))) { await shell.openExternal(url); return true; } return false; }); app.whenReady().then(createWindow); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); // IPC handlers for window controls ipcMain.on('window-minimize', () => { mainWindow?.minimize(); }); ipcMain.on('window-maximize', () => { if (mainWindow?.isMaximized()) { mainWindow.unmaximize(); } else { mainWindow?.maximize(); } }); ipcMain.on('window-close', () => { mainWindow?.close(); }); // IPC handler for desktop capturer (screen sharing) ipcMain.handle('get-sources', async () => { 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(), })); }); // IPC handler for app data path ipcMain.handle('get-app-data-path', () => { return app.getPath('userData'); }); // IPC for basic file operations used by renderer ipcMain.handle('file-exists', async (_event, filePath) => { try { await fsp.access(filePath, fs.constants.F_OK); return true; } catch { return false; } }); ipcMain.handle('read-file', async (_event, filePath) => { const data = await fsp.readFile(filePath); return data.toString('base64'); }); ipcMain.handle('write-file', async (_event, filePath, base64Data) => { const buffer = Buffer.from(base64Data, 'base64'); await fsp.writeFile(filePath, buffer); return true; }); ipcMain.handle('ensure-dir', async (_event, dirPath) => { await fsp.mkdir(dirPath, { recursive: true }); return true; });