diff --git a/apps/app/electron/main.js b/apps/app/electron/main.js index 7ad6df20..a39cc7d4 100644 --- a/apps/app/electron/main.js +++ b/apps/app/electron/main.js @@ -26,17 +26,34 @@ let staticServer = null; const SERVER_PORT = 3008; const STATIC_PORT = 3007; -// Get icon path - works in both dev and production +// Get icon path - works in both dev and production, cross-platform function getIconPath() { - return app.isPackaged - ? path.join(process.resourcesPath, "app", "public", "logo.png") - : path.join(__dirname, "../public/logo.png"); + // Different icon formats for different platforms + let iconFile; + if (process.platform === "win32") { + iconFile = "icon.ico"; + } else if (process.platform === "darwin") { + iconFile = "logo_larger.png"; + } else { + // Linux + iconFile = "logo_larger.png"; + } + + const iconPath = path.join(__dirname, "../public", iconFile); + + // Verify the icon exists + if (!fs.existsSync(iconPath)) { + console.warn(`[Electron] Icon not found at: ${iconPath}`); + return null; + } + + return iconPath; } /** * Start static file server for production builds */ -function startStaticServer() { +async function startStaticServer() { const staticPath = path.join(__dirname, "../out"); staticServer = http.createServer((request, response) => { @@ -89,8 +106,15 @@ function startStaticServer() { }); }); - staticServer.listen(STATIC_PORT, () => { - console.log(`[Electron] Static server running at http://localhost:${STATIC_PORT}`); + return new Promise((resolve, reject) => { + staticServer.listen(STATIC_PORT, (err) => { + if (err) { + reject(err); + } else { + console.log(`[Electron] Static server running at http://localhost:${STATIC_PORT}`); + resolve(); + } + }); }); } @@ -233,12 +257,12 @@ async function waitForServer(maxAttempts = 30) { * Create the main window */ function createWindow() { - mainWindow = new BrowserWindow({ + const iconPath = getIconPath(); + const windowOptions = { width: 1400, height: 900, minWidth: 1024, minHeight: 700, - icon: getIconPath(), webPreferences: { preload: path.join(__dirname, "preload.js"), contextIsolation: true, @@ -246,7 +270,14 @@ function createWindow() { }, titleBarStyle: "hiddenInset", backgroundColor: "#0a0a0a", - }); + }; + + // Only set icon if it exists + if (iconPath) { + windowOptions.icon = iconPath; + } + + mainWindow = new BrowserWindow(windowOptions); // Load Next.js dev server in development or static server in production const isDev = !app.isPackaged; @@ -270,13 +301,20 @@ function createWindow() { app.whenReady().then(async () => { // Set app icon (dock icon on macOS) if (process.platform === "darwin" && app.dock) { - app.dock.setIcon(getIconPath()); + const iconPath = getIconPath(); + if (iconPath) { + try { + app.dock.setIcon(iconPath); + } catch (error) { + console.warn("[Electron] Failed to set dock icon:", error.message); + } + } } try { // Start static file server in production if (app.isPackaged) { - startStaticServer(); + await startStaticServer(); } // Start backend server diff --git a/apps/app/package.json b/apps/app/package.json index ae50a1c7..40510b79 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -95,6 +95,7 @@ "appId": "com.automaker.app", "productName": "Automaker", "artifactName": "${productName}-${version}-${arch}.${ext}", + "afterPack": "./scripts/rebuild-server-natives.js", "directories": { "output": "dist" }, @@ -113,6 +114,10 @@ "from": "server-bundle/node_modules", "to": "server/node_modules" }, + { + "from": "server-bundle/package.json", + "to": "server/package.json" + }, { "from": "../../.env", "to": ".env", diff --git a/apps/app/scripts/prepare-server.js b/apps/app/scripts/prepare-server.js index 3487db71..83c0f055 100644 --- a/apps/app/scripts/prepare-server.js +++ b/apps/app/scripts/prepare-server.js @@ -64,4 +64,18 @@ execSync('npm install --omit=dev', { } }); +// Step 6: Rebuild native modules for current architecture +// This is critical for modules like node-pty that have native bindings +console.log('šŸ”Ø Rebuilding native modules for current architecture...'); +try { + execSync('npm rebuild', { + cwd: BUNDLE_DIR, + stdio: 'inherit' + }); + console.log('āœ… Native modules rebuilt successfully'); +} catch (error) { + console.warn('āš ļø Warning: Failed to rebuild native modules. Terminal functionality may not work.'); + console.warn(' Error:', error.message); +} + console.log('\nāœ… Server prepared for bundling at:', BUNDLE_DIR); diff --git a/apps/app/scripts/rebuild-server-natives.js b/apps/app/scripts/rebuild-server-natives.js new file mode 100644 index 00000000..c2eef844 --- /dev/null +++ b/apps/app/scripts/rebuild-server-natives.js @@ -0,0 +1,66 @@ +#!/usr/bin/env node + +/** + * Electron-builder afterPack hook + * Rebuilds native modules in the server bundle for the target architecture + */ + +const { exec } = require('child_process'); +const { promisify } = require('util'); +const path = require('path'); + +const execAsync = promisify(exec); + +exports.default = async function(context) { + const { appOutDir, electronPlatformName, arch, packager } = context; + const electronVersion = packager.config.electronVersion; + + // Convert arch to string if it's a number (electron-builder sometimes passes indices) + const archNames = ['ia32', 'x64', 'armv7l', 'arm64', 'universal']; + const archStr = typeof arch === 'number' ? archNames[arch] : arch; + + console.log(`\nšŸ”Ø Rebuilding server native modules for ${electronPlatformName}-${archStr}...`); + + // Path to server node_modules in the packaged app + let serverNodeModulesPath; + if (electronPlatformName === 'darwin') { + serverNodeModulesPath = path.join( + appOutDir, + `${packager.appInfo.productName}.app`, + 'Contents', + 'Resources', + 'server', + 'node_modules' + ); + } else if (electronPlatformName === 'win32') { + serverNodeModulesPath = path.join( + appOutDir, + 'resources', + 'server', + 'node_modules' + ); + } else { + serverNodeModulesPath = path.join( + appOutDir, + 'resources', + 'server', + 'node_modules' + ); + } + + try { + // Rebuild native modules for the target architecture + const rebuildCmd = `npx --yes @electron/rebuild --version=${electronVersion} --arch=${archStr} --force --module-dir="${serverNodeModulesPath}/.."`; + + console.log(` Command: ${rebuildCmd}`); + + const { stdout, stderr } = await execAsync(rebuildCmd); + if (stdout) console.log(stdout); + if (stderr) console.error(stderr); + + console.log(`āœ… Server native modules rebuilt successfully for ${archStr}\n`); + } catch (error) { + console.error(`āŒ Failed to rebuild server native modules:`, error.message); + // Don't fail the build, just warn + } +};