Electron main process entry point.
Handles window creation, security configuration, Sentry initialization, update checking,
Windows installer events (Squirrel), and preload/environment setup for the renderer process.
/** * Set up auto-updater event listeners for logging and error handling. * @source */ autoUpdater.on("checking-for-update", () => { if (!isProduction) { console.info("[Auto-Updater] đ Checking for updates..."); } });
if (!isProduction) { console.debug("[Main] đ Auto-updater configured"); } endGroup();
/** * Restore persisted auto-updater preferences from storage. * Sets `allowPrerelease` based on the stored update channel preference. * @source */ try { constsavedChannel = (store.get("update_channel") asstring) || "stable"; autoUpdater.allowPrerelease = savedChannel === "beta"; console.debug( `[Main] đ Auto-updater initialized allowPrerelease=${autoUpdater.allowPrerelease}`, ); } catch (err) { console.debug("[Main] đ Could not read update_channel from store:", err); }
/** * Configures Content Security Policy headers for all web requests. * Complements the CSP meta tag in index.html with additional security. * In development (app not packaged), relaxes connect-src to allow WebSocket and Vite dev server. * @remarks Called during app initialization to set up security headers. * @source */ functionconfigureSecurityHeaders() { returnwithGroup(`[Main] Configure Security Headers`, () => { console.info("[Main] đ Setting up Content Security Policy headers...");
// Determine if we're in development mode constisDevMode = !app.isPackaged;
// Set CSP headers for all requests // Reference: docs/guides/SECURITY.md for CSP policy source of truth session.defaultSession.webRequest.onHeadersReceived((details, callback) => { // Build CSP directives based on environment constconnectSrcDirectives = isDevMode ? "connect-src 'self' https://graphql.anilist.co https://api.mangadex.org https://api.comick.fun https://api.github.com ws: http://localhost:*;" : "connect-src 'self' https://graphql.anilist.co https://api.mangadex.org https://api.comick.fun https://api.github.com;";
// Allow inline scripts in development (Vite HMR injects small inline scripts). // Production remains strict (no 'unsafe-inline'). constscriptSrcDirective = isDevMode ? "script-src 'self' 'unsafe-inline';" : "script-src 'self';";
if (isDevMode) { console.info( "[Main] âšī¸ CSP configured in development mode (WebSocket + Vite dev server allowed)", ); } else { console.info("[Main] â CSP headers configured for production"); } }); }
/** * Configures permission handlers to deny all external permission requests. * Prevents the app from requesting camera, microphone, geolocation, etc. * @remarks Called during app initialization before windows are created. * @source */ functionconfigurePermissionHandlers() { returnwithGroup(`[Main] Configure Permission Handlers`, () => { console.info("[Main] đ Setting up permission handlers...");
// We run this synchronously to ensure everything is properly created before quitting spawnSync(updateDotExe, [ "--createShortcut", exeName, "--shortcut-locations", "Desktop,StartMenu", ]);
// If we handled a squirrel event, quit this instance and let the installer handle it if (handleSquirrelEvent()) { app.quit(); } }
// Handle general startup if (squirrelStartup) { app.quit(); }
/** Indicates if the application is running in development mode. @source */ constinDevelopment = process.env.NODE_ENV === "development";
/** * Indicates if developer tools should be enabled. * Set via ENABLE_DEVTOOLS environment variable ("1" or "true"). * @source */ constenableDevTools = process.env.ENABLE_DEVTOOLS === "1" || process.env.ENABLE_DEVTOOLS === "true";
// Make app version available to the renderer process via environment variable process.env.VITE_APP_VERSION = app.getVersion();
/** * Closes and destroys the splash screen window. * Safely checks if the window exists and hasn't already been destroyed. * @source */ functioncloseSplashScreen() { if (splashWindow && !splashWindow.isDestroyed()) { console.info("[Main] đ¨ Closing splash screen"); splashWindow.close(); splashWindow = null; } }
/** * Creates the main application window. * Sets up the BrowserWindow, registers IPC listeners, handles content loading, * and manages the transition from splash screen to main window. * @returns Void (main window is managed by Electron). * @remarks Handles both development (Vite dev server) and production (bundled) loading modes. * @source */ functioncreateWindow() { returnwithGroup(`[Main] Create Main Window`, () => { console.info("[Main] đĒ Creating main application window..."); constpreload = path.join(__dirname, "preload.js"); constmainWindow = newBrowserWindow({ width:1200, height:800, show:false, // Don't show until ready webPreferences: { sandbox:true, devTools:inDevelopment || enableDevTools, contextIsolation:true, nodeIntegration:false, nodeIntegrationInSubFrames:false, preload:preload, }, titleBarStyle:"hidden", });
// Load content from dev server (development) or bundled file (production) if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { console.debug( `[Main] đ Loading dev server: ${MAIN_WINDOW_VITE_DEV_SERVER_URL}`, ); mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL); } else { constfilePath = path.join( __dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`, ); console.debug(`[Main] đ Loading file: ${filePath}`); console.debug(`[Main] đ __dirname: ${__dirname}`); console.debug( `[Main] đ MAIN_WINDOW_VITE_NAME: ${MAIN_WINDOW_VITE_NAME}`, ); console.debug(`[Main] đ app.isPackaged: ${app.isPackaged}`); console.debug( `[Main] đ process.resourcesPath: ${process.resourcesPath}`, ); mainWindow.loadFile(filePath).catch((err) => { console.error(`[Main] â Failed to load main window file:`, err); }); }
// Auto-open DevTools if explicitly enabled via env var if (enableDevTools) { console.info("[Main] đ§ DevTools enabled via ENABLE_DEVTOOLS env var"); mainWindow.webContents.openDevTools({ mode:"right" }); }
// Show main window and close splash when ready mainWindow.once("ready-to-show", () => { console.info("[Main] â Main window ready-to-show event fired");
// Add a small delay to ensure smooth transition setTimeout(() => { if (contentLoaded) { closeSplashScreen(); mainWindow.show(); console.info("[Main] â Main window displayed"); } else { console.error( "[Main] â Main window content not loaded, keeping splash visible", ); } }, 1500); });
console.info("[Main] â Main window created successfully"); }); }
/** * Installs development extensions (e.g., React Developer Tools). * @returns Promise that resolves when extensions are installed, or no-op if dev tools not enabled. * @remarks Only runs when inDevelopment is true or enableDevTools is set via environment. * @source */ asyncfunctioninstallExtensions() { // Gate behind dev-only or explicit opt-in if (!inDevelopment && !enableDevTools) { console.debug( "[Main] đ Dev tools not enabled, skipping extension installation", ); return; }
// Configure security before creating windows // In production (app.isPackaged), configure full CSP headers // In development, CSP is still applied but allows WebSocket and Vite dev server configureSecurityHeaders(); configurePermissionHandlers();
/** * Handle macOS-specific behavior for window-all-closed event. * On macOS, applications typically remain active even with all windows closed, * allowing the user to reopen windows via the dock or menu. * On other platforms, closing all windows quits the application. * @source */ app.on("window-all-closed", () => { console.info("[Main] đĒ All windows closed"); if (process.platform !== "darwin") { console.info("[Main] đ Quitting app (non-macOS)"); app.quit(); } });
/** * Handle macOS-specific activate event. * On macOS, the activate event fires when the dock icon is clicked or the app is activated. * If no windows are open, this recreates the main window(s). * @source */ app.on("activate", () => { console.debug("[Main] đ App activated"); if (BrowserWindow.getAllWindows().length === 0) { console.info("[Main] đĒ No windows open, creating new window"); createSplashScreen(); createWindow(); } });
/** * Export public functions for use in other modules or tests. * These functions are the main entry points for window and extension management. * @source */ export { createWindow, createSplashScreen, closeSplashScreen, installExtensions, configureSecurityHeaders, configurePermissionHandlers, };
Description
Electron main process entry point. Handles window creation, security configuration, Sentry initialization, update checking, Windows installer events (Squirrel), and preload/environment setup for the renderer process.
Source