diff --git a/src/main/firstLaunch.ts b/src/main/firstLaunch.ts
new file mode 100644
index 0000000..5690c60
--- /dev/null
+++ b/src/main/firstLaunch.ts
@@ -0,0 +1,73 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0
+ * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
+ * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop!
+ */
+
+import { app } from "electron";
+import { BrowserWindow } from "electron/main";
+import { copyFileSync, mkdirSync, readdirSync } from "fs";
+import { join } from "path";
+import { SplashProps } from "shared/browserWinProperties";
+import { ICON_PATH, VIEW_DIR } from "shared/paths";
+
+import { autoStart } from "./autoStart";
+import { DATA_DIR } from "./constants";
+import { createWindows } from "./mainWindow";
+import { Settings, State } from "./settings";
+import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
+
+interface Data {
+ minimizeToTray: boolean;
+ discordBranch: "stable" | "canary" | "ptb";
+ autoStart: boolean;
+ importSettings: boolean;
+ richPresence: boolean;
+}
+
+export function createFirstLaunchTour() {
+ const win = new BrowserWindow({
+ ...SplashProps,
+ frame: true,
+ autoHideMenuBar: true,
+ height: 470,
+ width: 550,
+ icon: ICON_PATH
+ });
+
+ makeLinksOpenExternally(win);
+
+ win.loadFile(join(VIEW_DIR, "first-launch.html"));
+ win.webContents.addListener("console-message", (_e, _l, msg) => {
+ if (msg === "cancel") return app.exit();
+
+ if (!msg.startsWith("form:")) return;
+ const data = JSON.parse(msg.slice(5)) as Data;
+
+ State.store.firstLaunch = false;
+ Settings.store.minimizeToTray = data.minimizeToTray;
+ Settings.store.discordBranch = data.discordBranch;
+ Settings.store.arRPC = data.richPresence;
+
+ if (data.autoStart) autoStart.enable();
+
+ if (data.importSettings) {
+ const from = join(app.getPath("userData"), "..", "Vencord", "settings");
+ const to = join(DATA_DIR, "settings");
+ try {
+ const files = readdirSync(from);
+ mkdirSync(to, { recursive: true });
+
+ for (const file of files) {
+ copyFileSync(join(from, file), join(to, file));
+ }
+ } catch (e) {
+ console.error("Failed to import settings:", e);
+ }
+ }
+
+ win.close();
+
+ createWindows();
+ });
+}
diff --git a/src/main/index.ts b/src/main/index.ts
new file mode 100644
index 0000000..bc571d1
--- /dev/null
+++ b/src/main/index.ts
@@ -0,0 +1,108 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0
+ * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
+ * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop!
+ */
+
+import "./ipc";
+
+import { app, BrowserWindow, nativeTheme } from "electron";
+import { checkUpdates } from "updater/main";
+
+import { DATA_DIR } from "./constants";
+import { createFirstLaunchTour } from "./firstLaunch";
+import { createWindows, mainWin } from "./mainWindow";
+import { registerMediaPermissionsHandler } from "./mediaPermissions";
+import { registerScreenShareHandler } from "./screenShare";
+import { Settings, State } from "./settings";
+import { isDeckGameMode } from "./utils/steamOS";
+
+if (IS_DEV) {
+ require("source-map-support").install();
+}
+
+// Make the Vencord files use our DATA_DIR
+process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
+
+function init() {
+ const { disableSmoothScroll, hardwareAcceleration } = Settings.store;
+
+ const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(",");
+ const disabledFeatures = app.commandLine.getSwitchValue("disable-features").split(",");
+
+ if (hardwareAcceleration === false) {
+ app.disableHardwareAcceleration();
+ } else {
+ enabledFeatures.push("VaapiVideoDecodeLinuxGL", "VaapiVideoEncoder", "VaapiVideoDecoder");
+ }
+
+ if (disableSmoothScroll) {
+ app.commandLine.appendSwitch("disable-smooth-scrolling");
+ }
+
+ // work around chrome 66 disabling autoplay by default
+ app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
+ // WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows.
+ // HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service.
+ //
+ // WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790
+ disabledFeatures.push(
+ "WinRetrieveSuggestionsOnlyOnDemand",
+ "HardwareMediaKeyHandling",
+ "MediaSessionService",
+ "WidgetLayering"
+ );
+
+ app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].filter(Boolean).join(","));
+ app.commandLine.appendSwitch("disable-features", [...new Set(disabledFeatures)].filter(Boolean).join(","));
+
+ // In the Flatpak on SteamOS the theme is detected as light, but SteamOS only has a dark mode, so we just override it
+ if (isDeckGameMode) nativeTheme.themeSource = "dark";
+
+ app.on("second-instance", (_event, _cmdLine, _cwd, data: any) => {
+ if (data.IS_DEV) app.quit();
+ else if (mainWin) {
+ if (mainWin.isMinimized()) mainWin.restore();
+ if (!mainWin.isVisible()) mainWin.show();
+ mainWin.focus();
+ }
+ });
+
+ app.whenReady().then(async () => {
+ checkUpdates();
+ if (process.platform === "win32") app.setAppUserModelId("dev.vencord.vesktop");
+
+ registerScreenShareHandler();
+ registerMediaPermissionsHandler();
+
+ bootstrap();
+
+ app.on("activate", () => {
+ if (BrowserWindow.getAllWindows().length === 0) createWindows();
+ });
+ });
+}
+
+if (!app.requestSingleInstanceLock({ IS_DEV })) {
+ if (IS_DEV) {
+ console.log("Vesktop is already running. Quitting previous instance...");
+ init();
+ } else {
+ console.log("Vesktop is already running. Quitting...");
+ app.quit();
+ }
+} else {
+ init();
+}
+
+async function bootstrap() {
+ if (!Object.hasOwn(State.store, "firstLaunch")) {
+ createFirstLaunchTour();
+ } else {
+ createWindows();
+ }
+}
+
+app.on("window-all-closed", () => {
+ if (process.platform !== "darwin") app.quit();
+});
diff --git a/src/main/ipc.ts b/src/main/ipc.ts
new file mode 100644
index 0000000..3a76ce0
--- /dev/null
+++ b/src/main/ipc.ts
@@ -0,0 +1,155 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0
+ * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
+ * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop!
+ */
+
+if (process.platform === "linux") import("./venmic");
+
+import { execFile } from "child_process";
+import { app, BrowserWindow, clipboard, dialog, nativeImage, RelaunchOptions, session, shell } from "electron";
+import { mkdirSync, readFileSync, watch } from "fs";
+import { open, readFile } from "fs/promises";
+import { release } from "os";
+import { join } from "path";
+import { debounce } from "shared/utils/debounce";
+
+import { IpcEvents } from "../shared/IpcEvents";
+import { setBadgeCount } from "./appBadge";
+import { autoStart } from "./autoStart";
+import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
+import { mainWin } from "./mainWindow";
+import { Settings } from "./settings";
+import { handle, handleSync } from "./utils/ipcWrappers";
+import { PopoutWindows } from "./utils/popout";
+import { isDeckGameMode, showGamePage } from "./utils/steamOS";
+import { isValidVencordInstall } from "./utils/vencordLoader";
+
+handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js"));
+handleSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, () =>
+ readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8")
+);
+
+handleSync(IpcEvents.GET_RENDERER_SCRIPT, () => readFileSync(join(__dirname, "renderer.js"), "utf-8"));
+handleSync(IpcEvents.GET_RENDERER_CSS_FILE, () => join(__dirname, "renderer.css"));
+
+handleSync(IpcEvents.GET_SETTINGS, () => Settings.plain);
+handleSync(IpcEvents.GET_VERSION, () => app.getVersion());
+
+handleSync(
+ IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY,
+ () => process.platform === "win32" && Number(release().split(".").pop()) >= 22621
+);
+
+handleSync(IpcEvents.AUTOSTART_ENABLED, () => autoStart.isEnabled());
+handle(IpcEvents.ENABLE_AUTOSTART, autoStart.enable);
+handle(IpcEvents.DISABLE_AUTOSTART, autoStart.disable);
+
+handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: string) => {
+ Settings.setData(settings, path);
+});
+
+handle(IpcEvents.RELAUNCH, async () => {
+ const options: RelaunchOptions = {
+ args: process.argv.slice(1).concat(["--relaunch"])
+ };
+ if (isDeckGameMode) {
+ // We can't properly relaunch when running under gamescope, but we can at least navigate to our page in Steam.
+ await showGamePage();
+ } else if (app.isPackaged && process.env.APPIMAGE) {
+ execFile(process.env.APPIMAGE, options.args);
+ } else {
+ app.relaunch(options);
+ }
+ app.exit();
+});
+
+handle(IpcEvents.SHOW_ITEM_IN_FOLDER, (_, path) => {
+ shell.showItemInFolder(path);
+});
+
+handle(IpcEvents.FOCUS, () => {
+ mainWin.show();
+ mainWin.setSkipTaskbar(false);
+});
+
+handle(IpcEvents.CLOSE, (e, key?: string) => {
+ const popout = PopoutWindows.get(key!);
+ if (popout) return popout.close();
+
+ const win = BrowserWindow.fromWebContents(e.sender) ?? e.sender;
+ win.close();
+});
+
+handle(IpcEvents.MINIMIZE, e => {
+ mainWin.minimize();
+});
+
+handle(IpcEvents.MAXIMIZE, e => {
+ if (mainWin.isMaximized()) {
+ mainWin.unmaximize();
+ } else {
+ mainWin.maximize();
+ }
+});
+
+handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => {
+ const ses = session.defaultSession;
+
+ const available = ses.availableSpellCheckerLanguages;
+ const applicable = languages.filter(l => available.includes(l)).slice(0, 3);
+ if (applicable.length) ses.setSpellCheckerLanguages(applicable);
+});
+
+handle(IpcEvents.SPELLCHECK_REPLACE_MISSPELLING, (e, word: string) => {
+ e.sender.replaceMisspelling(word);
+});
+
+handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => {
+ e.sender.session.addWordToSpellCheckerDictionary(word);
+});
+
+handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
+ const res = await dialog.showOpenDialog(mainWin!, {
+ properties: ["openDirectory"]
+ });
+ if (!res.filePaths.length) return "cancelled";
+
+ const dir = res.filePaths[0];
+ if (!isValidVencordInstall(dir)) return "invalid";
+
+ return dir;
+});
+
+handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
+
+handle(IpcEvents.CLIPBOARD_COPY_IMAGE, async (_, buf: ArrayBuffer, src: string) => {
+ clipboard.write({
+ html: `
`,
+ image: nativeImage.createFromBuffer(Buffer.from(buf))
+ });
+});
+
+function readCss() {
+ return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
+}
+
+open(VENCORD_QUICKCSS_FILE, "a+").then(fd => {
+ fd.close();
+ watch(
+ VENCORD_QUICKCSS_FILE,
+ { persistent: false },
+ debounce(async () => {
+ mainWin?.webContents.postMessage("VencordQuickCssUpdate", await readCss());
+ }, 50)
+ );
+});
+
+mkdirSync(VENCORD_THEMES_DIR, { recursive: true });
+watch(
+ VENCORD_THEMES_DIR,
+ { persistent: false },
+ debounce(() => {
+ mainWin?.webContents.postMessage("VencordThemeUpdate", void 0);
+ })
+);
diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts
new file mode 100644
index 0000000..41dbed8
--- /dev/null
+++ b/src/main/mainWindow.ts
@@ -0,0 +1,488 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0
+ * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
+ * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop!
+ */
+
+import {
+ app,
+ BrowserWindow,
+ BrowserWindowConstructorOptions,
+ dialog,
+ Menu,
+ MenuItemConstructorOptions,
+ nativeTheme,
+ shell,
+ Tray
+} from "electron";
+import { rm } from "fs/promises";
+import { join } from "path";
+import { IpcEvents } from "shared/IpcEvents";
+import { isTruthy } from "shared/utils/guards";
+import { once } from "shared/utils/once";
+import type { SettingsStore } from "shared/utils/SettingsStore";
+
+import { ICON_PATH } from "../shared/paths";
+import { createAboutWindow } from "./about";
+import { initArRPC } from "./arrpc";
+import {
+ BrowserUserAgent,
+ DATA_DIR,
+ DEFAULT_HEIGHT,
+ DEFAULT_WIDTH,
+ MessageBoxChoice,
+ MIN_HEIGHT,
+ MIN_WIDTH,
+ VENCORD_FILES_DIR
+} from "./constants";
+import { Settings, State, VencordSettings } from "./settings";
+import { createSplashWindow } from "./splash";
+import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
+import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS";
+import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader";
+
+let isQuitting = false;
+let tray: Tray;
+
+applyDeckKeyboardFix();
+
+app.on("before-quit", () => {
+ isQuitting = true;
+});
+
+export let mainWin: BrowserWindow;
+
+function makeSettingsListenerHelpers(o: SettingsStore) {
+ const listeners = new Map<(data: any) => void, PropertyKey>();
+
+ const addListener: typeof o.addChangeListener = (path, cb) => {
+ listeners.set(cb, path);
+ o.addChangeListener(path, cb);
+ };
+ const removeAllListeners = () => {
+ for (const [listener, path] of listeners) {
+ o.removeChangeListener(path as any, listener);
+ }
+
+ listeners.clear();
+ };
+
+ return [addListener, removeAllListeners] as const;
+}
+
+const [addSettingsListener, removeSettingsListeners] = makeSettingsListenerHelpers(Settings);
+const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings);
+
+function initTray(win: BrowserWindow) {
+ const onTrayClick = () => {
+ if (Settings.store.clickTrayToShowHide && win.isVisible()) win.hide();
+ else win.show();
+ };
+ const trayMenu = Menu.buildFromTemplate([
+ {
+ label: "Open",
+ click() {
+ win.show();
+ }
+ },
+ {
+ label: "About and More",
+ click: createAboutWindow
+ },
+ {
+ label: "Fix/Repair Vencord",
+ async click() {
+ await downloadVencordFiles();
+ app.relaunch();
+ app.quit();
+ }
+ },
+ {
+ label: "Reset User Data",
+ async click() {
+ await clearData(win);
+ }
+ },
+ {
+ type: "separator"
+ },
+ {
+ label: "Open Updater",
+ click() {
+ const updaterPath = join(app.getPath('exe'), '..', 'Updater.exe');
+ shell.openPath(updaterPath);
+ }
+ },
+ {
+ label: "Restart Aerocord",
+ click() {
+ app.relaunch();
+ app.quit();
+ }
+ },
+ {
+ label: "Close Aerocord",
+ click() {
+ isQuitting = true;
+ app.quit();
+ }
+ }
+ ]);
+
+ tray = new Tray(ICON_PATH);
+ tray.setToolTip("Aerocord");
+ tray.setContextMenu(trayMenu);
+ tray.on("click", onTrayClick);
+}
+
+async function clearData(win: BrowserWindow) {
+ const { response } = await dialog.showMessageBox(win, {
+ message: "Are you sure you want to reset Aerocord?",
+ detail: "This will log you out, clear caches and reset all your settings!\n\nAerocord will automatically restart after this operation.",
+ buttons: ["Yes", "No"],
+ cancelId: MessageBoxChoice.Cancel,
+ defaultId: MessageBoxChoice.Default,
+ type: "warning"
+ });
+
+ if (response === MessageBoxChoice.Cancel) return;
+
+ win.close();
+
+ await win.webContents.session.clearStorageData();
+ await win.webContents.session.clearCache();
+ await win.webContents.session.clearCodeCaches({});
+ await rm(DATA_DIR, { force: true, recursive: true });
+
+ app.relaunch();
+ app.quit();
+}
+
+type MenuItemList = Array;
+
+function initMenuBar(win: BrowserWindow) {
+ const isWindows = process.platform === "win32";
+ const isDarwin = process.platform === "darwin";
+ const wantCtrlQ = !isWindows || VencordSettings.store.winCtrlQ;
+
+ const subMenu = [
+ {
+ label: "About and Documentation",
+ click: createAboutWindow
+ },
+ {
+ label: "Force Update Vencord",
+ async click() {
+ await downloadVencordFiles();
+ app.relaunch();
+ app.quit();
+ },
+ toolTip: "Aerocord will automatically restart after this operation"
+ },
+ {
+ label: "Reset Aerocord",
+ async click() {
+ await clearData(win);
+ },
+ toolTip: "Aerocord will automatically restart after this operation"
+ },
+ {
+ label: "Relaunch",
+ accelerator: "CmdOrCtrl+Shift+R",
+ click() {
+ app.relaunch();
+ app.quit();
+ }
+ },
+ ...(!isDarwin
+ ? []
+ : ([
+ {
+ type: "separator"
+ },
+ {
+ label: "Settings",
+ accelerator: "CmdOrCtrl+,",
+ async click() {
+ mainWin.webContents.executeJavaScript(
+ "Vencord.Webpack.Common.SettingsRouter.open('My Account')"
+ );
+ }
+ },
+ {
+ type: "separator"
+ },
+ {
+ role: "hide"
+ },
+ {
+ role: "hideOthers"
+ },
+ {
+ role: "unhide"
+ },
+ {
+ type: "separator"
+ }
+ ] satisfies MenuItemList)),
+ {
+ label: "Quit",
+ accelerator: wantCtrlQ ? "CmdOrCtrl+Q" : void 0,
+ visible: !isWindows,
+ role: "quit",
+ click() {
+ app.quit();
+ }
+ },
+ isWindows && {
+ label: "Quit",
+ accelerator: "Alt+F4",
+ role: "quit",
+ click() {
+ app.quit();
+ }
+ },
+ // See https://github.com/electron/electron/issues/14742 and https://github.com/electron/electron/issues/5256
+ {
+ label: "Zoom in (hidden, hack for Qwertz and others)",
+ accelerator: "CmdOrCtrl+=",
+ role: "zoomIn",
+ visible: false
+ }
+ ] satisfies MenuItemList;
+
+ const menu = Menu.buildFromTemplate([
+ {
+ label: "Vesktop",
+ role: "appMenu",
+ submenu: subMenu.filter(isTruthy)
+ },
+ { role: "fileMenu" },
+ { role: "editMenu" },
+ { role: "viewMenu" },
+ { role: "windowMenu" }
+ ]);
+
+ Menu.setApplicationMenu(menu);
+}
+
+function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
+ // We want the default window behaivour to apply in game mode since it expects everything to be fullscreen and maximized.
+ if (isDeckGameMode) return {};
+
+ const { x, y, width, height } = State.store.windowBounds ?? {};
+
+ const options = {
+ width: width ?? DEFAULT_WIDTH,
+ height: height ?? DEFAULT_HEIGHT
+ } as BrowserWindowConstructorOptions;
+
+ if (x != null && y != null) {
+ options.x = x;
+ options.y = y;
+ }
+
+ if (!Settings.store.disableMinSize) {
+ options.minWidth = MIN_WIDTH;
+ options.minHeight = MIN_HEIGHT;
+ }
+
+ return options;
+}
+
+function getDarwinOptions(): BrowserWindowConstructorOptions {
+ const options = {
+ titleBarStyle: "hidden",
+ trafficLightPosition: { x: 10, y: 10 }
+ } as BrowserWindowConstructorOptions;
+
+ const { splashTheming, splashBackground } = Settings.store;
+ const { macosTranslucency } = VencordSettings.store;
+
+ if (macosTranslucency) {
+ options.vibrancy = "sidebar";
+ options.backgroundColor = "#ffffff00";
+ } else {
+ if (splashTheming) {
+ options.backgroundColor = splashBackground;
+ } else {
+ options.backgroundColor = nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff";
+ }
+ }
+
+ return options;
+}
+
+function initWindowBoundsListeners(win: BrowserWindow) {
+ const saveState = () => {
+ State.store.maximized = win.isMaximized();
+ State.store.minimized = win.isMinimized();
+ };
+
+ win.on("maximize", saveState);
+ win.on("minimize", saveState);
+ win.on("unmaximize", saveState);
+
+ const saveBounds = () => {
+ State.store.windowBounds = win.getBounds();
+ };
+
+ win.on("resize", saveBounds);
+ win.on("move", saveBounds);
+}
+
+function initSettingsListeners(win: BrowserWindow) {
+ addSettingsListener("tray", enable => {
+ if (enable) initTray(win);
+ else tray?.destroy();
+ });
+ addSettingsListener("disableMinSize", disable => {
+ if (disable) {
+ // 0 no work
+ win.setMinimumSize(1, 1);
+ } else {
+ win.setMinimumSize(MIN_WIDTH, MIN_HEIGHT);
+
+ const { width, height } = win.getBounds();
+ win.setBounds({
+ width: Math.max(width, MIN_WIDTH),
+ height: Math.max(height, MIN_HEIGHT)
+ });
+ }
+ });
+
+ addVencordSettingsListener("macosTranslucency", enabled => {
+ if (enabled) {
+ win.setVibrancy("sidebar");
+ win.setBackgroundColor("#ffffff00");
+ } else {
+ win.setVibrancy(null);
+ win.setBackgroundColor("#ffffff");
+ }
+ });
+
+ addSettingsListener("enableMenu", enabled => {
+ win.setAutoHideMenuBar(enabled ?? false);
+ });
+}
+
+function initSpellCheck(win: BrowserWindow) {
+ win.webContents.on("context-menu", (_, data) => {
+ win.webContents.send(IpcEvents.SPELLCHECK_RESULT, data.misspelledWord, data.dictionarySuggestions);
+ });
+}
+
+function createMainWindow() {
+ // Clear up previous settings listeners
+ removeSettingsListeners();
+ removeVencordSettingsListeners();
+
+ const { staticTitle, transparencyOption, enableMenu, customTitleBar } = Settings.store;
+
+ const { frameless, transparent } = VencordSettings.store;
+
+ const noFrame = frameless === true || customTitleBar === true;
+
+ const win = (mainWin = new BrowserWindow({
+ show: false,
+ webPreferences: {
+ nodeIntegration: false,
+ sandbox: false,
+ contextIsolation: true,
+ devTools: true,
+ preload: join(__dirname, "preload.js"),
+ spellcheck: true
+ },
+ icon: ICON_PATH,
+ frame: !noFrame,
+ ...(transparent && {
+ transparent: true,
+ backgroundColor: "#00000000"
+ }),
+ ...(transparencyOption &&
+ transparencyOption !== "none" && {
+ backgroundColor: "#00000000",
+ backgroundMaterial: transparencyOption
+ }),
+ // Fix transparencyOption for custom discord titlebar
+ ...(customTitleBar &&
+ transparencyOption &&
+ transparencyOption !== "none" && {
+ transparent: true
+ }),
+ ...(staticTitle && { title: "Vesktop" }),
+ ...(process.platform === "darwin" && getDarwinOptions()),
+ ...getWindowBoundsOptions(),
+ autoHideMenuBar: enableMenu
+ }));
+ win.setMenuBarVisibility(false);
+
+ win.on("close", e => {
+ const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false;
+ if (isQuitting || (process.platform !== "darwin" && !useTray)) return;
+
+ e.preventDefault();
+
+ if (process.platform === "darwin") app.hide();
+ else win.hide();
+
+ return false;
+ });
+
+ if (Settings.store.staticTitle) win.on("page-title-updated", e => e.preventDefault());
+
+ initWindowBoundsListeners(win);
+ if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win);
+ initMenuBar(win);
+ makeLinksOpenExternally(win);
+ initSettingsListeners(win);
+ initSpellCheck(win);
+
+ win.webContents.setUserAgent(BrowserUserAgent);
+
+ const subdomain =
+ Settings.store.discordBranch === "canary" || Settings.store.discordBranch === "ptb"
+ ? `${Settings.store.discordBranch}.`
+ : "";
+
+ win.loadURL(`https://${subdomain}discord.com/app`);
+
+ return win;
+}
+
+const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDesktopMain.js")));
+
+export async function createWindows() {
+ const startMinimized = process.argv.includes("--start-minimized");
+ const splash = createSplashWindow(startMinimized);
+ // SteamOS letterboxes and scales it terribly, so just full screen it
+ if (isDeckGameMode) splash.setFullScreen(true);
+ await ensureVencordFiles();
+ runVencordMain();
+
+ mainWin = createMainWindow();
+
+ mainWin.webContents.on("did-finish-load", () => {
+ splash.destroy();
+
+ if (!startMinimized) {
+ mainWin!.show();
+ if (State.store.maximized && !isDeckGameMode) mainWin!.maximize();
+ }
+
+ if (isDeckGameMode) {
+ // always use entire display
+ mainWin!.setFullScreen(true);
+
+ askToApplySteamLayout(mainWin);
+ }
+
+ mainWin.once("show", () => {
+ if (State.store.maximized && !mainWin!.isMaximized() && !isDeckGameMode) {
+ mainWin!.maximize();
+ }
+ });
+ });
+
+ initArRPC();
+}
diff --git a/src/main/mediaPermissions.ts b/src/main/mediaPermissions.ts
new file mode 100644
index 0000000..f89aca5
--- /dev/null
+++ b/src/main/mediaPermissions.ts
@@ -0,0 +1,24 @@
+/*
+ * SPDX-License-Identifier: GPL-3.0
+ * Aerocord, a vesktop fork for older microsoft NT releases such as NT 6.0, 6.1, 6.2 and 6.3.
+ * Credits to vendicated and the rest of the vesktop contribuitors for making Vesktop!
+ */
+
+import { session, systemPreferences } from "electron";
+
+export function registerMediaPermissionsHandler() {
+ if (process.platform !== "darwin") return;
+
+ session.defaultSession.setPermissionRequestHandler(async (_webContents, permission, callback, details) => {
+ let granted = true;
+
+ if (details.mediaTypes?.includes("audio")) {
+ granted = await systemPreferences.askForMediaAccess("microphone");
+ }
+ if (details.mediaTypes?.includes("video")) {
+ granted &&= await systemPreferences.askForMediaAccess("camera");
+ }
+
+ callback(granted);
+ });
+}