import { join } from 'node:path' import { app, BrowserWindow, dialog, shell } from 'electron' import { createSidecarRuntime } from './sidecar' const DEV_SERVER_URL = 'http://localhost:3000' const SAFE_EXTERNAL_PROTOCOLS = new Set(['https:', 'http:', 'mailto:']) let mainWindow: BrowserWindow | null = null let windowCreationPromise: Promise | null = null let isQuitting = false const showErrorAndQuit = (title: string, detail: string) => { if (isQuitting) { return } dialog.showErrorBox(title, detail) app.quit() } const sidecar = createSidecarRuntime({ devServerUrl: DEV_SERVER_URL, isPackaged: app.isPackaged, resourcesPath: process.resourcesPath, isQuitting: () => isQuitting, onUnexpectedStop: (detail) => { showErrorAndQuit('Service Stopped', detail) }, }) const toErrorMessage = (error: unknown): string => (error instanceof Error ? error.message : String(error)) const canOpenExternally = (url: string): boolean => { try { const parsed = new URL(url) return SAFE_EXTERNAL_PROTOCOLS.has(parsed.protocol) } catch { return false } } const loadSplash = async (windowRef: BrowserWindow) => { if (process.env.ELECTRON_RENDERER_URL) { await windowRef.loadURL(process.env.ELECTRON_RENDERER_URL) return } await windowRef.loadFile(join(__dirname, '../renderer/index.html')) } const createWindow = async () => { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.focus() return } const windowRef = new BrowserWindow({ width: 1200, height: 800, show: false, webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: true, }, }) mainWindow = windowRef windowRef.webContents.setWindowOpenHandler(({ url }) => { if (!canOpenExternally(url)) { if (!app.isPackaged) { console.warn(`Blocked external URL: ${url}`) } return { action: 'deny' } } void shell.openExternal(url) return { action: 'deny' } }) windowRef.on('closed', () => { if (mainWindow === windowRef) { mainWindow = null } }) try { await loadSplash(windowRef) } catch (error) { if (mainWindow === windowRef) { mainWindow = null } if (!windowRef.isDestroyed()) { windowRef.destroy() } throw error } if (!windowRef.isDestroyed()) { windowRef.show() } const targetUrl = await sidecar.resolveUrl() if (isQuitting || windowRef.isDestroyed()) { return } try { await windowRef.loadURL(targetUrl) } catch (error) { if (mainWindow === windowRef) { mainWindow = null } if (!windowRef.isDestroyed()) { windowRef.destroy() } throw error } } const ensureWindow = async () => { if (windowCreationPromise) { return windowCreationPromise } windowCreationPromise = createWindow().finally(() => { windowCreationPromise = null }) return windowCreationPromise } const beginQuit = () => { isQuitting = true sidecar.stop() } const handleWindowCreationError = (error: unknown, context: string) => { console.error(`${context}:`, error) showErrorAndQuit( "App Couldn't Start", app.isPackaged ? 'A required component failed to start. Please reinstall the app.' : `${context}: ${toErrorMessage(error)}`, ) } app .whenReady() .then(() => ensureWindow()) .catch((error) => { handleWindowCreationError(error, 'Failed to create window') }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) app.on('activate', () => { if (isQuitting || BrowserWindow.getAllWindows().length > 0) { return } ensureWindow().catch((error) => { handleWindowCreationError(error, 'Failed to re-create window') }) }) app.on('before-quit', beginQuit)