import { app, BrowserWindow, nativeImage, shell } from 'electron' import { dirname, join, posix, win32 } from 'node:path' import { fileURLToPath } from 'node:url' const currentDirectory = dirname(fileURLToPath(import.meta.url)) type WindowIconEnvironment = { platform: NodeJS.Platform isPackaged: boolean appPath: string resourcesPath: string } export function resolveWindowIcon( environment: WindowIconEnvironment = { platform: process.platform, isPackaged: app.isPackaged, appPath: app.getAppPath(), resourcesPath: process.resourcesPath } ): string | undefined { if (environment.platform === 'darwin') { return undefined } const fileName = environment.platform === 'win32' ? environment.isPackaged ? 'icon.ico' : 'icon-taskbar.ico' : 'icon.png' const joinPath = environment.platform === 'win32' ? win32.join : posix.join return environment.isPackaged ? joinPath(environment.resourcesPath, fileName) : joinPath(environment.appPath, 'build', fileName) } function isAllowedExternalUrl(url: string): boolean { try { return ['http:', 'https:'].includes(new URL(url).protocol) } catch { return false } } function hasSameOrigin(url: string, allowedUrl: string): boolean { try { return new URL(url).origin === new URL(allowedUrl).origin } catch { return false } } export function createMainWindow(shouldQuit: () => boolean): BrowserWindow { const iconPath = resolveWindowIcon() const icon = iconPath ? nativeImage.createFromPath(iconPath) : undefined const usableIcon = icon && !icon.isEmpty() ? icon : undefined const window = new BrowserWindow({ width: 1180, height: 760, minWidth: 680, minHeight: 560, show: false, frame: false, ...(usableIcon ? { icon: usableIcon } : {}), backgroundColor: '#f4f1ea', webPreferences: { preload: join(currentDirectory, '../preload/index.cjs'), contextIsolation: true, nodeIntegration: false, sandbox: true } }) if (usableIcon) { window.setIcon(usableIcon) } window.once('ready-to-show', () => { window.show() }) window.on('close', (event) => { if (!shouldQuit()) { event.preventDefault() window.hide() } }) window.webContents.setWindowOpenHandler(({ url }) => { if (isAllowedExternalUrl(url)) { void shell.openExternal(url) } return { action: 'deny' } }) window.webContents.on('will-navigate', (event, url) => { const developmentUrl = process.env.ELECTRON_RENDERER_URL if (!developmentUrl || !hasSameOrigin(url, developmentUrl)) { event.preventDefault() } }) return window } export function loadMainWindow(window: BrowserWindow): void { if (process.env.ELECTRON_RENDERER_URL) { void window.loadURL(process.env.ELECTRON_RENDERER_URL) } else { void window.loadFile(join(currentDirectory, '../renderer/index.html')) } } export function showWindow(window: BrowserWindow): void { if (window.isMinimized()) { window.restore() } window.show() window.focus() } export function toggleWindow(window: BrowserWindow): void { if (window.isVisible() && window.isFocused()) { window.hide() return } showWindow(window) }