import { app, BrowserWindow, Tray, Menu, nativeImage } from 'electron'; import * as path from 'path'; import { fileURLToPath } from 'url'; import * as dotenv from 'dotenv'; import { setupIpcHandlers, cleanupIpcHandlers } from './ipc-handlers.js'; import { initDatabase, closeDatabase } from './database.js'; // ES module equivalent of __dirname const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Load environment variables from .env file // In dev: __dirname = dist/main, so go up to electron-app root // In prod: __dirname = resources/app.asar/dist/main, .env should be in resources const envPath = process.env.NODE_ENV === 'development' ? path.join(__dirname, '../../.env') : path.join(process.resourcesPath, '.env'); dotenv.config({ path: envPath }); console.log('Loading .env from:', envPath); console.log('WS_URL:', process.env.WS_URL); console.log('API_URL:', process.env.API_URL); let mainWindow: BrowserWindow | null = null; let tray: Tray | null = null; const isDev = process.env.NODE_ENV === 'development'; function getIconPath(): string { if (isDev) { return process.platform === 'win32' ? path.join(__dirname, '../../resources/icons/icon.ico') : path.join(__dirname, '../../resources/icons/logo.png'); } return process.platform === 'win32' ? path.join(process.resourcesPath, 'icons', 'icon.ico') : path.join(process.resourcesPath, 'icons', 'logo.png'); } function createWindow(): void { const iconPath = getIconPath(); console.log('Window icon path:', iconPath); mainWindow = new BrowserWindow({ width: 1400, height: 900, minWidth: 1000, minHeight: 700, frame: false, backgroundColor: '#0a0e27', icon: iconPath, show: false, // Don't show until loaded webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: false, contextIsolation: true, sandbox: true, devTools: true, // Enable dev tools in production for debugging }, title: 'rmtPocketWatcher', }); // Setup IPC handlers for WebSocket communication setupIpcHandlers(mainWindow); // Load the app if (isDev) { mainWindow.loadURL('http://localhost:5173'); mainWindow.webContents.openDevTools(); } else { const rendererPath = path.join(__dirname, '../renderer/index.html'); console.log('Loading renderer from:', rendererPath); mainWindow.loadFile(rendererPath).catch(err => { console.error('Failed to load renderer:', err); }); } // Show window and open dev tools to see errors mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDescription) => { console.error('Failed to load:', errorCode, errorDescription); }); mainWindow.webContents.on('did-finish-load', () => { console.log('Window loaded successfully'); mainWindow?.show(); }); mainWindow.on('closed', () => { cleanupIpcHandlers(); mainWindow = null; }); } function createTray(): void { // In dev: __dirname = dist/main, logo is at root // In prod: __dirname = resources/app.asar/dist/main const iconPath = getIconPath(); console.log('Tray icon path:', iconPath); let icon = nativeImage.createFromPath(iconPath); if (process.platform !== 'win32') { icon = icon.resize({ width: 16, height: 16 }); } tray = new Tray(icon); const contextMenu = Menu.buildFromTemplate([ { label: 'Show rmtPocketWatcher', click: () => { if (mainWindow) { mainWindow.show(); mainWindow.focus(); } } }, { label: 'Open DevTools', click: () => { if (mainWindow) { mainWindow.webContents.openDevTools(); } } }, { label: 'Reload', click: () => { if (mainWindow) { mainWindow.reload(); } } }, { label: 'Quit', click: () => { app.quit(); } } ]); tray.setToolTip('rmtPocketWatcher'); tray.setContextMenu(contextMenu); // Double-click to show window tray.on('double-click', () => { if (mainWindow) { mainWindow.show(); mainWindow.focus(); } }); } app.whenReady().then(() => { initDatabase(); createTray(); createWindow(); }); app.on('window-all-closed', () => { // Quit the app when all windows are closed app.quit(); }); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); app.on('before-quit', () => { cleanupIpcHandlers(); closeDatabase(); });