import { app, BrowserWindow, shell, dialog } from "electron"; import path from "path"; import { checkDocker, dockerDownloadUrl } from "./docker-check"; import { defaultWorkspacePath, detectNwnHome } from "./paths"; let mainWindow: BrowserWindow | null = null; let serverPort: number; function setEnvironment(): void { if (!process.env.WORKSPACE_PATH) { process.env.WORKSPACE_PATH = defaultWorkspacePath(); } if (!process.env.NWN_HOME_PATH) { const detected = detectNwnHome(); if (detected) process.env.NWN_HOME_PATH = detected; } if (!process.env.GIT_PROVIDER_URL) { process.env.GIT_PROVIDER_URL = "https://gitea.layonara.com"; } process.env.ELECTRON = "1"; } async function startBackend(): Promise { const { startServer } = await import( path.join(__dirname, "../packages/backend/dist/index.js") ); const port = await findFreePort(); await startServer(port); return port; } function findFreePort(): Promise { return new Promise((resolve, reject) => { const net = require("net"); const srv = net.createServer(); srv.listen(0, () => { const port = srv.address().port; srv.close(() => resolve(port)); }); srv.on("error", reject); }); } function createWindow(port: number): BrowserWindow { const win = new BrowserWindow({ width: 1280, height: 900, minWidth: 960, minHeight: 600, title: "Layonara Forge", icon: path.join(__dirname, "../assets/icon.png"), webPreferences: { preload: path.join(__dirname, "preload.js"), contextIsolation: true, nodeIntegration: false, }, }); win.loadURL(`http://localhost:${port}`); win.webContents.setWindowOpenHandler(({ url }) => { if (url.startsWith("http")) shell.openExternal(url); return { action: "deny" }; }); win.on("closed", () => { mainWindow = null; }); return win; } async function showDockerMissing(): Promise { const url = dockerDownloadUrl(); const { response } = await dialog.showMessageBox({ type: "warning", title: "Docker Required", message: "Layonara Forge requires Docker to build modules and run the NWN server.", detail: "Docker Desktop must be installed and running before using the Forge.\n\nClick \"Download Docker\" to open the download page, or \"Check Again\" after installing.", buttons: ["Download Docker", "Check Again", "Quit"], defaultId: 0, cancelId: 2, }); if (response === 0) { shell.openExternal(url); return showDockerMissing(); } if (response === 1) { const status = await checkDocker(); if (status.available) return true; return showDockerMissing(); } return false; } app.whenReady().then(async () => { setEnvironment(); const docker = await checkDocker(); if (!docker.available) { const installed = await showDockerMissing(); if (!installed) { app.quit(); return; } } try { serverPort = await startBackend(); } catch (err: any) { dialog.showErrorBox( "Forge Startup Error", `Failed to start the backend server:\n\n${err.message}`, ); app.quit(); return; } mainWindow = createWindow(serverPort); }); app.on("window-all-closed", () => { app.quit(); }); app.on("activate", () => { if (mainWindow === null && serverPort) { mainWindow = createWindow(serverPort); } });