diff --git a/README.md b/README.md index c63c163..0e55685 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,14 @@ Vesktop is a cross platform desktop app aiming to give you a snappier Discord experience with [Vencord](https://github.com/Vendicated/Vencord) pre-installed **Not yet supported**: -- Global Keybinds + +- Global Keybinds Bug reports, feature requests & contributions are highly appreciated!! ![](https://github.com/Vencord/Vesktop/assets/45497981/8608a899-96a9-4027-9725-2cb02ba189fd) ![grafik](https://github.com/Vencord/Vesktop/assets/45497981/8701e5de-52c4-4346-a990-719cb971642e) - ## Installing ### Windows @@ -39,7 +39,7 @@ Download Vesktop-VERSION.rpm from [releases](https://github.com/Vencord/Vesktop/ #### Other -Either download Vesktop-VERSION.AppImage and just run it directly or grab Vesktop-VERSION.tar.gz, extract it somewhere and run `vencorddesktop`. +Either download Vesktop-VERSION.AppImage and just run it directly or grab Vesktop-VERSION.tar.gz, extract it somewhere and run `vesktop`. If other packages are created, feel free to open an issue and we'll link them here. diff --git a/build/installer.nsh b/build/installer.nsh index 2877dba..2bce914 100644 --- a/build/installer.nsh +++ b/build/installer.nsh @@ -1,8 +1,8 @@ !macro preInit SetRegView 64 - WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop" - WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop" + WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop" + WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop" SetRegView 32 - WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop" - WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop" + WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop" + WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop" !macroend diff --git a/package.json b/package.json index 399ebf2..274c375 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "VencordDesktop", + "name": "vesktop", "version": "0.4.4", "private": true, "description": "", @@ -27,7 +27,7 @@ "arrpc": "github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8" }, "optionalDependencies": { - "@vencord/venmic": "^2.1.3" + "@vencord/venmic": "^3.2.0" }, "devDependencies": { "@fal-works/esbuild-plugin-global-externals": "^2.1.2", @@ -61,7 +61,7 @@ "pnpm": ">=8" }, "build": { - "appId": "dev.vencord.desktop", + "appId": "dev.vencord.vesktop", "productName": "Vesktop", "files": [ "!*", @@ -110,8 +110,7 @@ "GenericName": "Internet Messenger", "Type": "Application", "Categories": "Network;InstantMessaging;Chat;", - "Keywords": "discord;vencord;electron;chat;", - "StartupWMClass": "VencordDesktop" + "Keywords": "discord;vencord;electron;chat;" } }, "mac": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7a335b..97ada2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,8 +11,8 @@ dependencies: optionalDependencies: '@vencord/venmic': - specifier: ^2.1.3 - version: 2.1.4 + specifier: ^3.2.0 + version: 3.2.0 devDependencies: '@fal-works/esbuild-plugin-global-externals': @@ -793,8 +793,8 @@ packages: type-fest: 3.13.1 dev: true - /@vencord/venmic@2.1.4: - resolution: {integrity: sha512-Or//CHB9QtMcUXoDagkq0IF/7R8r7IABCaLIPtG8HCKSuOOFgBJ54PItnPXCliNKXWpwsUxj5z1WlZ4/VdwZHg==} + /@vencord/venmic@3.2.0: + resolution: {integrity: sha512-z+Lgmr6IgjkPEIfFZ3ZwVV4aP/OFjnB6k6Ll5YpBPuDZOyqCbKTNLADApbZa/WxMfm7YpZ22g46X4FfizuprEQ==} engines: {node: '>=14.15'} os: [linux] requiresBuild: true diff --git a/src/main/constants.ts b/src/main/constants.ts index 068521b..fb873ee 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -5,9 +5,25 @@ */ import { app } from "electron"; +import { existsSync, readdirSync, renameSync, rmdirSync } from "fs"; import { join } from "path"; -export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("userData"), "VencordDesktop"); +const LEGACY_DATA_DIR = join(app.getPath("appData"), "VencordDesktop", "VencordDesktop"); +export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("userData")); +// TODO: remove eventually +if (existsSync(LEGACY_DATA_DIR)) { + try { + console.warn("Detected legacy settings dir", LEGACY_DATA_DIR + ".", "migrating to", DATA_DIR); + for (const file of readdirSync(LEGACY_DATA_DIR)) { + renameSync(join(LEGACY_DATA_DIR, file), join(DATA_DIR, file)); + } + rmdirSync(LEGACY_DATA_DIR); + } catch (e) { + console.error("Migration failed", e); + } +} +app.setPath("sessionData", join(DATA_DIR, "sessionData")); + export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings"); export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css"); export const VENCORD_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json"); diff --git a/src/main/firstLaunch.ts b/src/main/firstLaunch.ts index 0ce585d..d1bbceb 100644 --- a/src/main/firstLaunch.ts +++ b/src/main/firstLaunch.ts @@ -14,7 +14,7 @@ import { ICON_PATH, VIEW_DIR } from "shared/paths"; import { autoStart } from "./autoStart"; import { DATA_DIR } from "./constants"; import { createWindows } from "./mainWindow"; -import { Settings } from "./settings"; +import { Settings, State } from "./settings"; import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; interface Data { @@ -44,9 +44,9 @@ export function createFirstLaunchTour() { 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.firstLaunch = false; Settings.store.arRPC = data.richPresence; if (data.autoStart) autoStart.enable(); diff --git a/src/main/index.ts b/src/main/index.ts index ef1892f..9d6a52c 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -6,7 +6,7 @@ import "./ipc"; -import { app, BrowserWindow } from "electron"; +import { app, BrowserWindow, nativeTheme } from "electron"; import { checkUpdates } from "updater/main"; import { DATA_DIR } from "./constants"; @@ -14,7 +14,8 @@ import { createFirstLaunchTour } from "./firstLaunch"; import { createWindows, mainWin } from "./mainWindow"; import { registerMediaPermissionsHandler } from "./mediaPermissions"; import { registerScreenShareHandler } from "./screenShare"; -import { Settings } from "./settings"; +import { Settings, State } from "./settings"; +import { isDeckGameMode } from "./utils/steamOS"; if (IS_DEV) { require("source-map-support").install(); @@ -43,6 +44,9 @@ function init() { "WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService,WidgetLayering" ); + // 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) { @@ -54,7 +58,7 @@ function init() { app.whenReady().then(async () => { checkUpdates(); - if (process.platform === "win32") app.setAppUserModelId("dev.vencord.desktop"); + if (process.platform === "win32") app.setAppUserModelId("dev.vencord.vesktop"); registerScreenShareHandler(); registerMediaPermissionsHandler(); @@ -80,7 +84,7 @@ if (!app.requestSingleInstanceLock({ IS_DEV })) { } async function bootstrap() { - if (!Object.hasOwn(Settings.store, "firstLaunch")) { + if (!Object.hasOwn(State.store, "firstLaunch")) { createFirstLaunchTour(); } else { createWindows(); diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index c34e351..3e1b8b8 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -34,7 +34,7 @@ import { UserAgent, VENCORD_FILES_DIR } from "./constants"; -import { Settings, VencordSettings } from "./settings"; +import { Settings, State, VencordSettings } from "./settings"; import { createSplashWindow } from "./splash"; import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS"; @@ -210,7 +210,6 @@ function initMenuBar(win: BrowserWindow) { type: "separator" }, { - label: "Hide Vesktop", // Should probably remove the label, but it says "Hide VencordDesktop" instead of "Hide Vesktop" role: "hide" }, { @@ -268,7 +267,7 @@ 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 } = Settings.store.windowBounds ?? {}; + const { x, y, width, height } = State.store.windowBounds ?? {}; const options = { width: width ?? DEFAULT_WIDTH, @@ -313,8 +312,8 @@ function getDarwinOptions(): BrowserWindowConstructorOptions { function initWindowBoundsListeners(win: BrowserWindow) { const saveState = () => { - Settings.store.maximized = win.isMaximized(); - Settings.store.minimized = win.isMinimized(); + State.store.maximized = win.isMaximized(); + State.store.minimized = win.isMinimized(); }; win.on("maximize", saveState); @@ -322,7 +321,7 @@ function initWindowBoundsListeners(win: BrowserWindow) { win.on("unmaximize", saveState); const saveBounds = () => { - Settings.store.windowBounds = win.getBounds(); + State.store.windowBounds = win.getBounds(); }; win.on("resize", saveBounds); @@ -396,7 +395,12 @@ function createMainWindow() { ...(transparencyOption && transparencyOption !== "none" && { backgroundColor: "#00000000", - backgroundMaterial: transparencyOption, + backgroundMaterial: transparencyOption + }), + // Fix transparencyOption for custom discord titlebar + ...(discordWindowsTitleBar && + transparencyOption && + transparencyOption !== "none" && { transparent: true }), ...(staticTitle && { title: "Vesktop" }), @@ -454,7 +458,9 @@ export async function createWindows() { mainWin.webContents.on("did-finish-load", () => { splash.destroy(); - if (!startMinimized || isDeckGameMode) mainWin!.show(); + if (State.store.maximized && !isDeckGameMode) { + mainWin!.maximize(); + } if (isDeckGameMode) { // always use entire display @@ -464,7 +470,7 @@ export async function createWindows() { } mainWin.once("show", () => { - if (Settings.store.maximized && !mainWin!.isMaximized() && !isDeckGameMode) { + if (State.store.maximized && !mainWin!.isMaximized() && !isDeckGameMode) { mainWin!.maximize(); } }); diff --git a/src/main/settings.ts b/src/main/settings.ts index 6fad97f..c4df364 100644 --- a/src/main/settings.ts +++ b/src/main/settings.ts @@ -4,14 +4,15 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ -import { mkdirSync, readFileSync, writeFileSync } from "fs"; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { dirname, join } from "path"; -import type { Settings as TSettings } from "shared/settings"; +import type { Settings as TSettings, State as TState } from "shared/settings"; import { SettingsStore } from "shared/utils/SettingsStore"; import { DATA_DIR, VENCORD_SETTINGS_FILE } from "./constants"; const SETTINGS_FILE = join(DATA_DIR, "settings.json"); +const STATE_FILE = join(DATA_DIR, "state.json"); function loadSettings(file: string, name: string) { let settings = {} as T; @@ -20,7 +21,7 @@ function loadSettings(file: string, name: string) { try { settings = JSON.parse(content); } catch (err) { - console.error(`Failed to parse ${name} settings.json:`, err); + console.error(`Failed to parse ${name}.json:`, err); } } catch {} @@ -33,5 +34,25 @@ function loadSettings(file: string, name: string) { return store; } -export const Settings = loadSettings(SETTINGS_FILE, "Vesktop"); -export const VencordSettings = loadSettings(VENCORD_SETTINGS_FILE, "Vencord"); +export const Settings = loadSettings(SETTINGS_FILE, "Vesktop settings"); +export const VencordSettings = loadSettings(VENCORD_SETTINGS_FILE, "Vencord settings"); + +if (Object.hasOwn(Settings.store, "firstLaunch") && !existsSync(STATE_FILE)) { + console.warn("legacy state in settings.json detected. migrating to state.json"); + const state = {} as TState; + for (const prop of [ + "firstLaunch", + "maximized", + "minimized", + "skippedUpdate", + "steamOSLayoutVersion", + "windowBounds" + ]) { + state[prop] = Settings.plain[prop]; + delete Settings.plain[prop]; + } + Settings.markAsChanged(); + writeFileSync(STATE_FILE, JSON.stringify(state, null, 4)); +} + +export const State = loadSettings(STATE_FILE, "Vesktop state"); diff --git a/src/main/utils/steamOS.ts b/src/main/utils/steamOS.ts index 8145e5a..e61166c 100644 --- a/src/main/utils/steamOS.ts +++ b/src/main/utils/steamOS.ts @@ -9,7 +9,7 @@ import { writeFile } from "fs/promises"; import { join } from "path"; import { MessageBoxChoice } from "../constants"; -import { Settings } from "../settings"; +import { State } from "../settings"; // Bump this to re-show the prompt const layoutVersion = 2; @@ -70,8 +70,8 @@ async function showLayout(appId: string) { export async function askToApplySteamLayout(win: BrowserWindow) { const appId = getAppId(); if (!appId) return; - if (Settings.store.steamOSLayoutVersion === layoutVersion) return; - const update = Boolean(Settings.store.steamOSLayoutVersion); + if (State.store.steamOSLayoutVersion === layoutVersion) return; + const update = Boolean(State.store.steamOSLayoutVersion); // Touch screen breaks in some menus when native touch mode is enabled on latest SteamOS beta, remove most of the update specific text once that's fixed. const { response } = await dialog.showMessageBox(win, { @@ -87,8 +87,8 @@ ${update ? "Click" : "Tap"} no to keep your current layout.`, type: "question" }); - if (Settings.store.steamOSLayoutVersion !== layoutVersion) { - Settings.store.steamOSLayoutVersion = layoutVersion; + if (State.store.steamOSLayoutVersion !== layoutVersion) { + State.store.steamOSLayoutVersion = layoutVersion; } if (response === MessageBoxChoice.Cancel) return; diff --git a/src/main/virtmic.ts b/src/main/virtmic.ts index 36920e9..ddbde63 100644 --- a/src/main/virtmic.ts +++ b/src/main/virtmic.ts @@ -55,22 +55,17 @@ ipcMain.handle( IpcEvents.VIRT_MIC_START, (_, targets: string[]) => obtainVenmic()?.link({ - props: targets.map(target => ({ key: "application.name", value: target })), - mode: "include" + include: targets.map(target => ({ key: "application.name", value: target })), + exclude: [{ key: "application.process.id", value: getRendererAudioServicePid() }] }) ); ipcMain.handle( IpcEvents.VIRT_MIC_START_SYSTEM, () => + // @ts-expect-error venmic types are wrong. include is actually optional but typed as required in vemic obtainVenmic()?.link({ - props: [ - { - key: "application.process.id", - value: getRendererAudioServicePid() - } - ], - mode: "exclude" + exclude: [{ key: "application.process.id", value: getRendererAudioServicePid() }] }) ); diff --git a/src/shared/settings.d.ts b/src/shared/settings.d.ts index 6e5ccfc..8c63373 100644 --- a/src/shared/settings.d.ts +++ b/src/shared/settings.d.ts @@ -20,19 +20,22 @@ export interface Settings { arRPC?: boolean; appBadge?: boolean; discordWindowsTitleBar?: boolean; - - maximized?: boolean; - minimized?: boolean; - windowBounds?: Rectangle; disableMinSize?: boolean; checkUpdates?: boolean; - skippedUpdate?: string; - firstLaunch?: boolean; splashTheming?: boolean; splashColor?: string; splashBackground?: string; +} + +export interface State { + maximized?: boolean; + minimized?: boolean; + windowBounds?: Rectangle; + + skippedUpdate?: string; + firstLaunch?: boolean; steamOSLayoutVersion?: number; } diff --git a/src/shared/utils/SettingsStore.ts b/src/shared/utils/SettingsStore.ts index 89c57dd..22dd145 100644 --- a/src/shared/utils/SettingsStore.ts +++ b/src/shared/utils/SettingsStore.ts @@ -144,4 +144,11 @@ export class SettingsStore { listeners.delete(cb); if (!listeners.size) this.pathListeners.delete(path as string); } + + /** + * Call all global change listeners + */ + public markAsChanged() { + this.globalListeners.forEach(cb => cb(this.plain, "")); + } } diff --git a/src/updater/main.ts b/src/updater/main.ts index b84081c..059afb9 100644 --- a/src/updater/main.ts +++ b/src/updater/main.ts @@ -5,7 +5,7 @@ */ import { app, BrowserWindow, shell } from "electron"; -import { Settings } from "main/settings"; +import { Settings, State } from "main/settings"; import { handle } from "main/utils/ipcWrappers"; import { makeLinksOpenExternally } from "main/utils/makeLinksOpenExternally"; import { githubGet, ReleaseData } from "main/utils/vencordLoader"; @@ -52,7 +52,7 @@ handle(IpcEvents.UPDATER_DOWNLOAD, () => { }); handle(IpcEvents.UPDATE_IGNORE, () => { - Settings.store.skippedUpdate = updateData.latestVersion; + State.store.skippedUpdate = updateData.latestVersion; }); function isOutdated(oldVersion: string, newVersion: string) { @@ -91,7 +91,7 @@ export async function checkUpdates() { release: data }; - if (Settings.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) { + if (State.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) { openNewUpdateWindow(); } } catch (e) {