import { app, BrowserWindow, ipcMain } from 'electron'; import path from 'path'; import url from 'url'; import { fileURLToPath } from 'url'; // Keep an in-memory API key for the running session only. Renderer should still store key in localStorage. let inMemoryKey = null; function createWindow() { // Resolve dirname equivalent in ESM const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const win = new BrowserWindow({ width: 1200, height: 800, webPreferences: { // Preload is colocated with the electron main files after the refactor preload: path.join(__dirname, 'electron-preload.js'), contextIsolation: true, nodeIntegration: false, } }); const startUrl = process.env.ELECTRON_START_URL || url.pathToFileURL(path.join(process.cwd(), 'dist', 'index.html')).toString(); win.loadURL(startUrl).catch(err => { console.error('[electron-main] Failed to load URL:', err); }); if (process.env.ELECTRON_START_URL) { try { const ses = win.webContents.session; ses.webRequest.onHeadersReceived((details, callback) => { const headers = details.responseHeaders || {}; // Allow jsDelivr CDN for scripts used by third-party libs (dev only) // Dev CSP: allow inline scripts, unsafe-eval and jsDelivr CDN for third-party libs (HMR and wasm loaders need inline scripts/eval) headers['Content-Security-Policy'] = [ "default-src 'self' 'unsafe-eval' 'unsafe-inline' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; img-src 'self' data:; connect-src *; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com" ]; callback({ responseHeaders: headers }); }); } catch (e) { console.warn('[electron-main] Failed to inject dev CSP:', e); } win.webContents.once('did-frame-finish-load', () => { try { win.webContents.openDevTools({ mode: 'right' }); } catch (e) { console.warn('[electron-main] Could not open DevTools:', e); } }); } win.webContents.on('did-finish-load', () => { console.log('[electron-main] Renderer finished load; title=', win.getTitle()); }); } 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(); }); ipcMain.handle('generate-avatar', async (event, prompt) => { try { console.log('[electron-main] generate-avatar handler invoked'); console.log('[electron-main] prompt length:', (prompt || '').length); // Prefer an in-memory key, then environment variables const apiKey = inMemoryKey || process.env.GEMINI_API_KEY || process.env.API_KEY; console.log('[electron-main] apiKey present?', !!apiKey, '(will prefer GEMINI_API_KEY if set)'); if (apiKey) { try { console.log('[electron-main] Calling GenAI helper...'); const imageData = await generateAvatarWithGenAI(prompt || '', apiKey); console.log('[electron-main] GenAI helper returned image, length:', imageData?.length || 0); return { image: imageData }; } catch (e) { console.error('[electron-main] GenAI generation failed:', e?.message || e); } } else { console.log('[electron-main] No API key present — skipping GenAI call and returning placeholder'); } } catch (outerErr) { console.error('[electron-main] Unexpected error in generate-avatar handler:', outerErr); } // Fallback placeholder image const placeholder = `data:image/svg+xml;utf8,Placeholder: ${encodeURIComponent((prompt||'').substring(0,80))}`; console.log('[electron-main] Returning placeholder image (length):', placeholder.length); return { image: placeholder }; }); // Expose simple key management for the renderer via ipc (session-only) // No renderer-side key persistence — frontend uses localStorage exclusively now.