import { join } from "node:path"; import { BrowserWindow, Menu, MenuItem, app, ipcMain, nativeImage, } from "electron"; import windowIconAsset from "../../assets/desktop/icon.png?asset"; 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.createFromDataURL(windowIconAsset); // windowIcon.setTemplateImage(true); /** * Create the main application window */ export function createMainWindow() { // create the window mainWindow = new BrowserWindow({ minWidth: 300, minHeight: 300, width: 1280, height: 720, backgroundColor: "#191919", frame: !config.customFrame, icon: windowIcon, webPreferences: { // relative to `.vite/build` preload: join(__dirname, "preload.js"), contextIsolation: true, nodeIntegration: false, spellcheck: true, }, }); // hide the options mainWindow.setMenu(null); // maximise the window if it was maximised before if (config.windowState.isMaximised) { mainWindow.maximize(); } // restore last position if it was moved previously if (config.windowState.x > 0 || config.windowState.y > 0) { mainWindow.setPosition( config.windowState.x ?? 0, config.windowState.y ?? 0, ); } // restore last size if it was resized previously if (config.windowState.width > 0 && config.windowState.height > 0) { mainWindow.setSize( config.windowState.width ?? 1280, config.windowState.height ?? 720, ); } // 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 = { x: mainWindow.getPosition()[0], y: mainWindow.getPosition()[1], width: mainWindow.getSize()[0], height: mainWindow.getSize()[1], isMaximised: mainWindow.isMaximized(), }; } mainWindow.on("maximize", generateState); mainWindow.on("unmaximize", generateState); mainWindow.on("moved", generateState); mainWindow.on("resized", generateState); // rebind zoom controls to be more sensible mainWindow.webContents.on("before-input-event", (event, input) => { if (input.control && (input.key === "=" || 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, ); } else if (input.control && input.key === "0") { // reset zoom to default. event.preventDefault(); mainWindow.webContents.setZoomLevel(0); } else if ( input.key === "F5" || ((input.control || input.meta) && input.key.toLowerCase() === "r") ) { event.preventDefault(); mainWindow.webContents.reload(); } }); // send the config mainWindow.webContents.on("did-finish-load", () => config.sync()); // 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; });