From fd7ada3cd82c4253ca1968e5c2a52b770e98480f Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 24 Aug 2026 14:40:50 +0200 Subject: [PATCH] Improve settings default handling --- src/main/main.ts | 2 +- src/main/mainWindow.ts | 15 +++--- src/main/settings.ts | 10 +++- src/main/splash.ts | 2 +- src/main/utils/mergeDefaults.ts | 24 +++++++++ src/renderer/appBadge.ts | 2 +- .../components/settings/AutoStartToggle.tsx | 2 +- .../settings/NotificationBadgeToggle.tsx | 2 +- src/renderer/components/settings/Settings.tsx | 51 ++++++------------- .../components/settings/UserAssets.tsx | 2 +- .../settings/WebRTCIPHandlingPolicyPicker.tsx | 2 +- src/renderer/settings.ts | 8 +-- src/shared/defaultSettings.ts | 33 ++++++++++++ src/shared/settings.d.ts | 46 ++++++++--------- 14 files changed, 123 insertions(+), 78 deletions(-) create mode 100644 src/main/utils/mergeDefaults.ts create mode 100644 src/shared/defaultSettings.ts diff --git a/src/main/main.ts b/src/main/main.ts index 0c81512..7230e4d 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -40,7 +40,7 @@ function init() { app.commandLine.removeSwitch("enable-features"); app.commandLine.removeSwitch("disable-features"); - if (hardwareAcceleration === false || process.argv.includes("--disable-gpu")) { + if (!hardwareAcceleration || process.argv.includes("--disable-gpu")) { enableHardwareAcceleration = false; app.disableHardwareAcceleration(); } else { diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index 9f5ff60..08bfe2e 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -344,12 +344,11 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions { const { transparent, macosVibrancyStyle } = VencordSettings.store; - const frameless = nativeTitleBar !== true; - const backgroundColor = - splashTheming !== false ? splashBackground : nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff"; + const frameless = !nativeTitleBar; + const backgroundColor = splashTheming ? splashBackground : nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff"; const options: BrowserWindowConstructorOptions = { - show: Settings.store.enableSplashScreen === false && !CommandLine.values["start-minimized"], + show: !Settings.store.enableSplashScreen && !CommandLine.values["start-minimized"], backgroundColor, webPreferences: { nodeIntegration: false, @@ -363,8 +362,8 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions { }, frame: !frameless, autoHideMenuBar: enableMenu, - hasShadow: enableShadow !== false, - roundedCorners: enableRoundedCorners !== false, + hasShadow: enableShadow, + roundedCorners: enableRoundedCorners, ...getWindowBoundsOptions() }; @@ -410,7 +409,7 @@ function createMainWindow() { if (process.platform === "darwin" && Settings.store.nativeTitleBar) win.setWindowButtonVisibility(false); win.on("close", e => { - const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false; + const useTray = !isDeckGameMode && Settings.store.minimizeToTray && Settings.store.tray; if (isQuitting || (process.platform !== "darwin" && !useTray)) return; e.preventDefault(); @@ -470,7 +469,7 @@ export async function createWindows() { const startMinimized = CommandLine.values["start-minimized"]; let splash: BrowserWindow | undefined; - if (Settings.store.enableSplashScreen !== false) { + if (Settings.store.enableSplashScreen) { splash = createSplashWindow(startMinimized); // SteamOS letterboxes and scales it terribly, so just full screen it diff --git a/src/main/settings.ts b/src/main/settings.ts index f23cbe5..267e2bf 100644 --- a/src/main/settings.ts +++ b/src/main/settings.ts @@ -7,15 +7,17 @@ import { type Settings as TVencordSettings } from "@vencord/types/Vencord"; import { mkdirSync, readFileSync, writeFileSync } from "fs"; import { dirname, join } from "path"; +import { DefaultVesktopSettings } from "shared/defaultSettings"; 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"; +import { mergeDefaults } from "./utils/mergeDefaults"; const SETTINGS_FILE = join(DATA_DIR, "settings.json"); const STATE_FILE = join(DATA_DIR, "state.json"); -function loadSettings(file: string, name: string) { +function loadSettings(file: string, name: string, defaults?: T) { let settings = {} as T; try { const content = readFileSync(file, "utf8"); @@ -26,6 +28,10 @@ function loadSettings(file: string, name: string) { } } catch {} + if (defaults) { + mergeDefaults(settings, defaults); + } + const store = new SettingsStore(settings); store.addGlobalChangeListener(o => { try { @@ -39,6 +45,6 @@ function loadSettings(file: string, name: string) { return store; } -export const Settings = loadSettings(SETTINGS_FILE, "Vesktop settings"); +export const Settings = loadSettings(SETTINGS_FILE, "Vesktop settings", DefaultVesktopSettings); export const VencordSettings = loadSettings(VENCORD_SETTINGS_FILE, "Vencord settings"); export const State = loadSettings(STATE_FILE, "Vesktop state"); diff --git a/src/main/splash.ts b/src/main/splash.ts index cfc7746..e11ec9d 100644 --- a/src/main/splash.ts +++ b/src/main/splash.ts @@ -26,7 +26,7 @@ export function createSplashWindow(startMinimized = false) { const { splashBackground, splashColor, splashTheming, splashPixelated } = Settings.store; - if (splashTheming !== false) { + if (splashTheming) { if (splashColor) { const semiTransparentSplashColor = splashColor.replace("rgb(", "rgba(").replace(")", ", 0.2)"); diff --git a/src/main/utils/mergeDefaults.ts b/src/main/utils/mergeDefaults.ts new file mode 100644 index 0000000..494d809 --- /dev/null +++ b/src/main/utils/mergeDefaults.ts @@ -0,0 +1,24 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2026 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/** + * Recursively merges defaults into an object and returns the same object + * @param obj Object + * @param defaults Defaults + * @returns obj + */ +export function mergeDefaults(obj: T, defaults: T): T { + for (const key in defaults) { + const v = defaults[key]; + if (typeof v === "object" && !Array.isArray(v)) { + obj[key] ??= {} as any; + mergeDefaults(obj[key], v); + } else { + obj[key] ??= v; + } + } + return obj; +} diff --git a/src/renderer/appBadge.ts b/src/renderer/appBadge.ts index 97f3e36..bc9983f 100644 --- a/src/renderer/appBadge.ts +++ b/src/renderer/appBadge.ts @@ -14,7 +14,7 @@ let GuildReadStateStore: any; let NotificationSettingsStore: any; export function setBadge() { - if (Settings.store.appBadge === false) return; + if (!Settings.store.appBadge) return; try { const mentionCount = GuildReadStateStore.getTotalMentionCount(); diff --git a/src/renderer/components/settings/AutoStartToggle.tsx b/src/renderer/components/settings/AutoStartToggle.tsx index d99f2cd..a8ff5d9 100644 --- a/src/renderer/components/settings/AutoStartToggle.tsx +++ b/src/renderer/components/settings/AutoStartToggle.tsx @@ -27,7 +27,7 @@ export const AutoStartToggle: SettingsComponent = ({ settings }) => { (settings.autoStartMinimized = v)} disabled={!autoStartEnabled} /> diff --git a/src/renderer/components/settings/NotificationBadgeToggle.tsx b/src/renderer/components/settings/NotificationBadgeToggle.tsx index 6e17e3d..bac9c38 100644 --- a/src/renderer/components/settings/NotificationBadgeToggle.tsx +++ b/src/renderer/components/settings/NotificationBadgeToggle.tsx @@ -14,7 +14,7 @@ export const NotificationBadgeToggle: SettingsComponent = ({ settings }) => { { settings.appBadge = v; if (v) setBadge(); diff --git a/src/renderer/components/settings/Settings.tsx b/src/renderer/components/settings/Settings.tsx index d509c38..d04b179 100644 --- a/src/renderer/components/settings/Settings.tsx +++ b/src/renderer/components/settings/Settings.tsx @@ -10,7 +10,7 @@ import { classNameFactory } from "@vencord/types/api/Styles"; import { BaseText, Divider, ErrorBoundary } from "@vencord/types/components"; import { ComponentType } from "react"; import { WebRTCIPHandlingPolicyPicker } from "renderer/components/settings/WebRTCIPHandlingPolicyPicker"; -import { Settings, useSettings } from "renderer/settings"; +import { getValueAndOnChange, Settings, useSettings } from "renderer/settings"; import { isMac } from "renderer/utils"; import { AutoStartToggle } from "./AutoStartToggle"; @@ -26,7 +26,6 @@ interface BooleanSetting { key: keyof typeof Settings.store; title: string; description: string; - defaultValue: boolean; disabled?(): boolean; invisible?(): boolean; } @@ -42,64 +41,55 @@ const SettingsOptions: Record> { key: "hardwareAcceleration", title: "Hardware Acceleration", - description: "Enable hardware acceleration", - defaultValue: true + description: "Enable hardware acceleration" }, { key: "hardwareVideoAcceleration", title: "Video Hardware Acceleration", description: "Enable hardware video acceleration. This can improve performance of screenshare and video playback, but may cause graphical glitches and infinitely loading streams.", - defaultValue: false, - disabled: () => Settings.store.hardwareAcceleration === false + disabled: () => !Settings.store.hardwareAcceleration } ], "User Interface": [ { key: "nativeTitleBar", title: "Native Titlebar", - description: "Enable the system titlebar in addition to Discord's custom one. Requires a full restart.", - defaultValue: false + description: "Enable the system titlebar in addition to Discord's custom one. Requires a full restart." }, { key: "staticTitle", title: "Static Title", - description: 'Makes the window title "Vesktop" instead of changing to the current page', - defaultValue: false + description: 'Makes the window title "Vesktop" instead of changing to the current page' }, { key: "enableMenu", title: "Enable Menu Bar", description: "Enables the application menu bar. Press ALT to toggle visibility.", - defaultValue: false, disabled: () => Settings.store.nativeTitleBar !== true }, { key: "enableShadow", title: "Enable Window Shadow", description: "Enables the window shadow. Requires a full restart.", - defaultValue: true, disabled: () => Settings.store.nativeTitleBar === true }, { key: "enableRoundedCorners", title: "Enable Rounded Corners", description: "Enables rounded corners. Requires a full restart.", - defaultValue: true, disabled: () => Settings.store.nativeTitleBar === true }, { key: "enableSplashScreen", title: "Enable Splash Screen", description: - "Shows a small splash screen while Vesktop is loading. Disabling this option will show the main window earlier while it's still loading.", - defaultValue: true + "Shows a small splash screen while Vesktop is loading. Disabling this option will show the main window earlier while it's still loading." }, { key: "splashTheming", title: "Splash theming", - description: "Adapt the splash window colors to your custom theme", - defaultValue: true + description: "Adapt the splash window colors to your custom theme" }, WindowsTransparencyControls, UserAssetsButton @@ -109,34 +99,29 @@ const SettingsOptions: Record> key: "tray", title: "Tray Icon", description: "Add a tray icon for Vesktop", - defaultValue: true, invisible: () => isMac }, { key: "minimizeToTray", title: "Minimize to tray", description: "Hitting X will make Vesktop minimize to the tray instead of closing", - defaultValue: true, invisible: () => isMac, - disabled: () => Settings.store.tray === false + disabled: () => !Settings.store.tray }, { key: "clickTrayToShowHide", title: "Hide/Show on tray click", - description: "Left clicking tray icon will toggle the vesktop window visibility.", - defaultValue: false + description: "Left clicking tray icon will toggle the vesktop window visibility." }, { key: "disableMinSize", title: "Disable minimum window size", - description: "Allows you to make the window as small as your heart desires", - defaultValue: false + description: "Allows you to make the window as small as your heart desires" }, { key: "disableSmoothScroll", title: "Disable smooth scrolling", - description: "Disables smooth scrolling", - defaultValue: false + description: "Disables smooth scrolling" } ], Notifications: [ @@ -144,23 +129,20 @@ const SettingsOptions: Record> { key: "enableTaskbarFlashing", title: "Enable Taskbar Flashing", - description: "Flashes the app in your taskbar when you have new notifications.", - defaultValue: false + description: "Flashes the app in your taskbar when you have new notifications." } ], Miscellaneous: [ { key: "arRPC", title: "Rich Presence", - description: "Enables Rich Presence via arRPC", - defaultValue: false + description: "Enables Rich Presence via arRPC" }, { key: "openLinksWithElectron", title: "Open Links in app (experimental)", - description: "Opens links in a new Vesktop window instead of your web browser", - defaultValue: false + description: "Opens links in a new Vesktop window instead of your web browser" }, WebRTCIPHandlingPolicyPicker @@ -182,16 +164,15 @@ function SettingsSections() { {settings.map((Setting, i) => { if (typeof Setting === "function") return ; - const { defaultValue, title, description, key, disabled, invisible } = Setting; + const { title, description, key, disabled, invisible } = Setting; if (invisible?.()) return null; return ( (Settings[key as any] = v)} disabled={disabled?.()} + {...getValueAndOnChange(key)} key={key} /> ); diff --git a/src/renderer/components/settings/UserAssets.tsx b/src/renderer/components/settings/UserAssets.tsx index aac6b61..04c174b 100644 --- a/src/renderer/components/settings/UserAssets.tsx +++ b/src/renderer/components/settings/UserAssets.tsx @@ -74,7 +74,7 @@ function Asset({ asset }: { asset: UserAssetType }) { {isSplash && ( (settings.splashPixelated = val)} className={Margins.top16} hideBorder diff --git a/src/renderer/components/settings/WebRTCIPHandlingPolicyPicker.tsx b/src/renderer/components/settings/WebRTCIPHandlingPolicyPicker.tsx index 533dde0..f49621c 100644 --- a/src/renderer/components/settings/WebRTCIPHandlingPolicyPicker.tsx +++ b/src/renderer/components/settings/WebRTCIPHandlingPolicyPicker.tsx @@ -33,7 +33,7 @@ export const WebRTCIPHandlingPolicyPicker: SettingsComponent = ({ settings }) => ]} closeOnSelect={true} select={v => (settings.webRTCIPHandlingPolicy = v)} - isSelected={v => v === (settings.webRTCIPHandlingPolicy ?? "default")} + isSelected={v => v === settings.webRTCIPHandlingPolicy} serialize={s => s} /> diff --git a/src/renderer/settings.ts b/src/renderer/settings.ts index 0a5ba80..f21daa6 100644 --- a/src/renderer/settings.ts +++ b/src/renderer/settings.ts @@ -25,10 +25,12 @@ export function useSettings() { return Settings.store; } -export function getValueAndOnChange(key: keyof typeof Settings.store) { +export function getValueAndOnChange( + key: K +) { return { - value: Settings.store[key] as any, - onChange: (value: any) => (Settings.store[key] = value) + value: Settings.store[key] as V, + onChange: (value: V) => (Settings.store[key] = value) }; } diff --git a/src/shared/defaultSettings.ts b/src/shared/defaultSettings.ts new file mode 100644 index 0000000..c2f160e --- /dev/null +++ b/src/shared/defaultSettings.ts @@ -0,0 +1,33 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2026 Vendicated and Vesktop contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Settings } from "./settings"; + +export const DefaultVesktopSettings: Settings = { + discordBranch: "stable", + hardwareAcceleration: true, + hardwareVideoAcceleration: false, + nativeTitleBar: false, + staticTitle: false, + enableMenu: false, + enableShadow: true, + enableRoundedCorners: true, + enableSplashScreen: true, + splashTheming: true, + tray: true, + minimizeToTray: true, + clickTrayToShowHide: false, + disableMinSize: false, + disableSmoothScroll: false, + enableTaskbarFlashing: false, + arRPC: true, + openLinksWithElectron: false, + autoStartMinimized: false, + splashPixelated: false, + webRTCIPHandlingPolicy: "default", + appBadge: true, + transparencyOption: "none" +}; diff --git a/src/shared/settings.d.ts b/src/shared/settings.d.ts index 3533e45..3faf5cf 100644 --- a/src/shared/settings.d.ts +++ b/src/shared/settings.d.ts @@ -7,36 +7,36 @@ import type { Rectangle } from "electron"; export interface Settings { - discordBranch?: "stable" | "canary" | "ptb"; - transparencyOption?: "none" | "mica" | "tabbed" | "acrylic"; - webRTCIPHandlingPolicy?: + discordBranch: "stable" | "canary" | "ptb"; + transparencyOption: "none" | "mica" | "tabbed" | "acrylic"; + webRTCIPHandlingPolicy: | "default" | "default_public_interface_only" | "default_public_and_private_interfaces" | "disable_non_proxied_udp"; - tray?: boolean; - minimizeToTray?: boolean; - autoStartMinimized?: boolean; - openLinksWithElectron?: boolean; - staticTitle?: boolean; - enableMenu?: boolean; - enableShadow?: boolean; - enableRoundedCorners?: boolean; - disableSmoothScroll?: boolean; - hardwareAcceleration?: boolean; - hardwareVideoAcceleration?: boolean; - arRPC?: boolean; - appBadge?: boolean; - enableTaskbarFlashing?: boolean; - disableMinSize?: boolean; - clickTrayToShowHide?: boolean; - nativeTitleBar?: boolean; + tray: boolean; + minimizeToTray: boolean; + autoStartMinimized: boolean; + openLinksWithElectron: boolean; + staticTitle: boolean; + enableMenu: boolean; + enableShadow: boolean; + enableRoundedCorners: boolean; + disableSmoothScroll: boolean; + hardwareAcceleration: boolean; + hardwareVideoAcceleration: boolean; + arRPC: boolean; + appBadge: boolean; + enableTaskbarFlashing: boolean; + disableMinSize: boolean; + clickTrayToShowHide: boolean; + nativeTitleBar: boolean; - enableSplashScreen?: boolean; - splashTheming?: boolean; + enableSplashScreen: boolean; + splashTheming: boolean; + splashPixelated: boolean; splashColor?: string; splashBackground?: string; - splashPixelated?: boolean; spellCheckLanguages?: string[];