const { app, BrowserWindow, ipcMain, dialog, shell } = require('electron'); const path = require('path'); const { spawn } = require('child_process'); const fs = require('fs'); let mainWindow; function createWindow() { mainWindow = new BrowserWindow({ width: 1200, height: 820, minWidth: 1000, minHeight: 700, icon: path.join(__dirname, 'docs', 'dekupai-logo.png'), // Custom app logo frame: false, // Sleek frameless design transparent: false, // Set to false to avoid rendering bugs on some Windows setups backgroundColor: '#0a0a0f', // Dark baseline background webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, webSecurity: false // Disabled to allow canvas pixel reading on local file:// assets for Auto-Trim } }); mainWindow.loadFile(path.join(__dirname, 'index.html')); mainWindow.setMenu(null); // Disable default menu and browser shortcuts (F5, F11, Ctrl+F5) to act as a proper native application. // Open DevTools if running in dev (optional) // mainWindow.webContents.openDevTools(); } app.whenReady().then(() => { createWindow(); app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on('window-all-closed', function () { if (process.platform !== 'darwin') app.quit(); }); // Helper: Get Python Path function getPythonPaths() { const isWindows = process.platform === 'win32'; const venvPython = isWindows ? path.join(__dirname, '.venv', 'Scripts', 'python.exe') : path.join(__dirname, '.venv', 'bin', 'python'); const systemPython = 'python'; const hasVenv = fs.existsSync(venvPython); return { venvPython, systemPython, hasVenv }; } // IPC: Check Python Setup Status ipcMain.handle('check-python-setup', async () => { const { hasVenv } = getPythonPaths(); return { hasVenv }; }); // IPC: Run Setup ipcMain.handle('run-python-setup', async (event) => { const { systemPython } = getPythonPaths(); const setupScript = path.join(__dirname, 'backend', 'setup.py'); return new Promise((resolve) => { console.log(`[Main Process] Spawning setup: ${systemPython} ${setupScript}`); // Spawn host python to run setup.py const process = spawn(systemPython, [setupScript], { shell: true }); let stderrData = ''; process.stdout.on('data', (data) => { const output = data.toString(); console.log(`[Setup Output] ${output.trim()}`); const lines = output.split('\n'); for (const line of lines) { if (line.trim()) { event.sender.send('setup-progress', line.trim()); } } }); process.stderr.on('data', (data) => { const errStr = data.toString(); stderrData += errStr; console.error(`[Setup Error] ${errStr.trim()}`); event.sender.send('setup-progress', `[ERROR] ${errStr.trim()}`); }); process.on('close', (code) => { console.log(`[Main Process] Setup completed with exit code ${code}`); resolve({ code, error: stderrData }); }); }); }); // IPC: Run Background Removal Process ipcMain.handle('remove-background', async (event, { inputPath, outputPath }) => { const { venvPython, hasVenv } = getPythonPaths(); if (!hasVenv) { return { success: false, error: 'Python virtual environment not set up. Please run setup first.' }; } const processScript = path.join(__dirname, 'backend', 'process.py'); return new Promise((resolve) => { console.log(`[Main Process] Spawning background removal: ${venvPython} ${processScript}`); // Spawn venv python to run process.py const args = ['--input', inputPath, '--output', outputPath]; const process = spawn(venvPython, [processScript, ...args], { shell: false }); let stderrData = ''; process.stdout.on('data', (data) => { const output = data.toString(); console.log(`[Python Output] ${output.trim()}`); const lines = output.split('\n'); for (const line of lines) { if (line.trim()) { event.sender.send('process-progress', line.trim()); } } }); process.stderr.on('data', (data) => { const errStr = data.toString(); stderrData += errStr; console.error(`[Python Error] ${errStr.trim()}`); event.sender.send('process-progress', `[ERROR] ${errStr.trim()}`); }); process.on('close', (code) => { console.log(`[Main Process] Process completed with exit code ${code}`); if (code === 0) { resolve({ success: true, outputPath }); } else { resolve({ success: false, error: stderrData || `Process failed with exit code ${code}` }); } }); }); }); // IPC: Select Image File Dialog ipcMain.handle('select-image', async () => { const result = await dialog.showOpenDialog(mainWindow, { properties: ['openFile'], filters: [ { name: 'Images', extensions: ['jpg', 'jpeg', 'png', 'webp', 'bmp'] } ] }); if (!result.canceled && result.filePaths.length > 0) { return result.filePaths[0]; } return null; }); // IPC: Get Temp Output File Path ipcMain.handle('get-temp-path', async (event, fileName) => { const tempDir = app.getPath('temp'); // Ensure unique name to avoid cache issues const uniqueName = `birefnet_${Date.now()}_${fileName}`; return path.join(tempDir, uniqueName); }); // IPC: Export Image Dialog ipcMain.handle('export-image', async (event, { base64Data, defaultName }) => { const result = await dialog.showSaveDialog(mainWindow, { defaultPath: defaultName || 'processed_image.png', filters: [ { name: 'PNG Image', extensions: ['png'] }, { name: 'JPEG Image', extensions: ['jpg', 'jpeg'] }, { name: 'WebP Image', extensions: ['webp'] } ] }); if (!result.canceled && result.filePath) { try { // Strip base64 headers if present const base64Content = base64Data.replace(/^data:image\/\w+;base64,/, ""); fs.writeFileSync(result.filePath, base64Content, 'base64'); return { success: true, filePath: result.filePath }; } catch (err) { return { success: false, error: err.message }; } } return { success: false, canceled: true }; }); // IPC: Show in File Explorer ipcMain.handle('show-in-explorer', async (event, filePath) => { if (fs.existsSync(filePath)) { shell.showItemInFolder(filePath); return true; } return false; }); // IPC: Frameless Window Controls ipcMain.on('window-minimize', () => { if (mainWindow) mainWindow.minimize(); }); ipcMain.on('window-maximize', () => { if (mainWindow) { if (mainWindow.isMaximized()) { mainWindow.unmaximize(); } else { mainWindow.maximize(); } } }); ipcMain.on('window-close', () => { if (mainWindow) mainWindow.close(); });