import { join, resolve } from "node:path"; import { BrowserWindow, Menu, MenuItem, app, ipcMain, nativeImage, } from "electron"; import { setBadgeCount } from "./badges"; import { config } from "./config"; import { updateTrayMenu } from "./tray"; // global reference to main window export let mainWindow: BrowserWindow; // currently in-use build export const BUILD_URL = new URL( app.commandLine.hasSwitch("force-server") ? app.commandLine.getSwitchValue("force-server") : (MAIN_WINDOW_VITE_DEV_SERVER_URL ?? "https://beta.revolt.chat"), ); // internal window state let shouldQuit = false; // load the window icon const windowIcon = nativeImage.createFromPath( resolve(process.resourcesPath, "icon.png"), ); console.info(resolve(process.resourcesPath, "icon.png")); // windowIcon.setTemplateImage(true); /** * Create the main application window */ export function createMainWindow() { // create the window mainWindow = new BrowserWindow({ minWidth: 300, minHeight: 300, width: 800, height: 600, backgroundColor: "#191919", frame: !config.customFrame, icon: windowIcon, webPreferences: { // relative to `.vite/build` preload: join(__dirname, "preload.js"), contextIsolation: true, nodeIntegration: false, spellcheck: true, }, }); // maximise the window if it was maximised before if (config.windowState.isMaximised) { mainWindow.maximize(); } // load the entrypoint mainWindow.loadURL(BUILD_URL.toString()); // minimise window to tray mainWindow.on("close", (event) => { if (!shouldQuit && config.minimiseToTray) { event.preventDefault(); mainWindow.hide(); } }); // update tray menu when window is shown/hidden mainWindow.on("show", updateTrayMenu); mainWindow.on("hide", updateTrayMenu); // keep track of window state function generateState() { config.windowState = { isMaximised: mainWindow.isMaximized(), }; } mainWindow.on("maximize", generateState); mainWindow.on("unmaximize", generateState); // rebind zoom controls to be more sensible mainWindow.webContents.on("before-input-event", (event, input) => { if (input.control && input.key === "=") { // zoom in (+) event.preventDefault(); mainWindow.webContents.setZoomLevel( mainWindow.webContents.getZoomLevel() + 1, ); } else if (input.control && input.key === "-") { // zoom out (-) event.preventDefault(); mainWindow.webContents.setZoomLevel( mainWindow.webContents.getZoomLevel() - 1, ); } }); // configure spellchecker context menu mainWindow.webContents.on("context-menu", (_, params) => { const menu = new Menu(); // add all suggestions for (const suggestion of params.dictionarySuggestions) { menu.append( new MenuItem({ label: suggestion, click: () => mainWindow.webContents.replaceMisspelling(suggestion), }), ); } // allow users to add the misspelled word to the dictionary if (params.misspelledWord) { menu.append( new MenuItem({ label: "Add to dictionary", click: () => mainWindow.webContents.session.addWordToSpellCheckerDictionary( params.misspelledWord, ), }), ); } // add an option to toggle spellchecker menu.append( new MenuItem({ label: "Toggle spellcheck", click() { config.spellchecker = !config.spellchecker; }, }), ); // show menu if we've generated enough entries if (menu.items.length > 0) { menu.popup(); } }); // push world events to the window ipcMain.on("minimise", () => mainWindow.minimize()); ipcMain.on("maximise", () => mainWindow.isMaximized() ? mainWindow.unmaximize() : mainWindow.maximize(), ); ipcMain.on("close", () => mainWindow.close()); // mainWindow.webContents.openDevTools(); let i = 0; setInterval(() => setBadgeCount((++i % 30) + 1), 1000); } /** * Quit the entire app */ export function quitApp() { shouldQuit = true; mainWindow.close(); } // Ensure global app quit works properly app.on("before-quit", () => { shouldQuit = true; });