const { app, BrowserWindow, dialog } = require('electron'); const path = require('path'); const { spawn } = require('child_process'); let mainWindow; let serverProcess; let serverPort = 0; function findServerBinary() { const candidates = [ path.join(__dirname, '..', 'akai-fetch'), path.join(__dirname, '..', 'fetch'), path.join(process.resourcesPath, 'akai-fetch'), path.join(process.resourcesPath, 'fetch'), ]; for (const p of candidates) { try { require('fs').accessSync(p, require('fs').constants.X_OK); return p; } catch (_) {} } return null; } async function startServer() { const bin = findServerBinary(); if (!bin) { dialog.showErrorBox('Missing Binary', 'Could not find fetch/akai-fetch binary. Place it next to the app or in resources.'); app.quit(); return; } const usePort = process.env.AKAI_PORT ? parseInt(process.env.AKAI_PORT) : 0; return new Promise((resolve, reject) => { serverProcess = spawn(bin, ['serve', '-p', String(usePort), '--no-browser'], { stdio: ['pipe', 'pipe', 'pipe'], }); let output = ''; serverProcess.stdout.on('data', (data) => { output += data.toString(); const m = output.match(/Local:\s+http:\/\/localhost:(\d+)/); if (m) { serverPort = parseInt(m[1]); resolve(); } }); serverProcess.stderr.on('data', (data) => { console.error('[server]', data.toString().trim()); }); serverProcess.on('error', (err) => { console.error('Failed to start server:', err); reject(err); }); serverProcess.on('close', (code) => { if (serverPort === 0) { reject(new Error(`Server exited with code ${code}`)); } }); }); } async function createWindow() { try { await startServer(); } catch (err) { dialog.showErrorBox('Server Error', String(err)); app.quit(); return; } mainWindow = new BrowserWindow({ width: 1200, height: 800, minWidth: 800, minHeight: 600, title: 'AKAI Utils', backgroundColor: '#1a1a2e', webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, }, }); mainWindow.loadURL(`http://localhost:${serverPort}`); mainWindow.setMenuBarVisibility(false); mainWindow.on('closed', () => { mainWindow = null; }); } app.whenReady().then(createWindow); app.on('window-all-closed', () => { if (serverProcess) { serverProcess.kill(); serverProcess = null; } if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { if (mainWindow === null) { createWindow(); } }); app.on('before-quit', () => { if (serverProcess) { serverProcess.kill(); serverProcess = null; } });