Improve settings default handling

This commit is contained in:
Vendicated
2026-08-24 14:40:50 +02:00
parent 09ce424b30
commit fd7ada3cd8
14 changed files with 123 additions and 78 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ function init() {
app.commandLine.removeSwitch("enable-features"); app.commandLine.removeSwitch("enable-features");
app.commandLine.removeSwitch("disable-features"); app.commandLine.removeSwitch("disable-features");
if (hardwareAcceleration === false || process.argv.includes("--disable-gpu")) { if (!hardwareAcceleration || process.argv.includes("--disable-gpu")) {
enableHardwareAcceleration = false; enableHardwareAcceleration = false;
app.disableHardwareAcceleration(); app.disableHardwareAcceleration();
} else { } else {
+7 -8
View File
@@ -344,12 +344,11 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions {
const { transparent, macosVibrancyStyle } = VencordSettings.store; const { transparent, macosVibrancyStyle } = VencordSettings.store;
const frameless = nativeTitleBar !== true; const frameless = !nativeTitleBar;
const backgroundColor = const backgroundColor = splashTheming ? splashBackground : nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff";
splashTheming !== false ? splashBackground : nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff";
const options: BrowserWindowConstructorOptions = { const options: BrowserWindowConstructorOptions = {
show: Settings.store.enableSplashScreen === false && !CommandLine.values["start-minimized"], show: !Settings.store.enableSplashScreen && !CommandLine.values["start-minimized"],
backgroundColor, backgroundColor,
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
@@ -363,8 +362,8 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions {
}, },
frame: !frameless, frame: !frameless,
autoHideMenuBar: enableMenu, autoHideMenuBar: enableMenu,
hasShadow: enableShadow !== false, hasShadow: enableShadow,
roundedCorners: enableRoundedCorners !== false, roundedCorners: enableRoundedCorners,
...getWindowBoundsOptions() ...getWindowBoundsOptions()
}; };
@@ -410,7 +409,7 @@ function createMainWindow() {
if (process.platform === "darwin" && Settings.store.nativeTitleBar) win.setWindowButtonVisibility(false); if (process.platform === "darwin" && Settings.store.nativeTitleBar) win.setWindowButtonVisibility(false);
win.on("close", e => { 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; if (isQuitting || (process.platform !== "darwin" && !useTray)) return;
e.preventDefault(); e.preventDefault();
@@ -470,7 +469,7 @@ export async function createWindows() {
const startMinimized = CommandLine.values["start-minimized"]; const startMinimized = CommandLine.values["start-minimized"];
let splash: BrowserWindow | undefined; let splash: BrowserWindow | undefined;
if (Settings.store.enableSplashScreen !== false) { if (Settings.store.enableSplashScreen) {
splash = createSplashWindow(startMinimized); splash = createSplashWindow(startMinimized);
// SteamOS letterboxes and scales it terribly, so just full screen it // SteamOS letterboxes and scales it terribly, so just full screen it
+8 -2
View File
@@ -7,15 +7,17 @@
import { type Settings as TVencordSettings } from "@vencord/types/Vencord"; import { type Settings as TVencordSettings } from "@vencord/types/Vencord";
import { mkdirSync, readFileSync, writeFileSync } from "fs"; import { mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join } from "path"; import { dirname, join } from "path";
import { DefaultVesktopSettings } from "shared/defaultSettings";
import type { Settings as TSettings, State as TState } from "shared/settings"; import type { Settings as TSettings, State as TState } from "shared/settings";
import { SettingsStore } from "shared/utils/SettingsStore"; import { SettingsStore } from "shared/utils/SettingsStore";
import { DATA_DIR, VENCORD_SETTINGS_FILE } from "./constants"; import { DATA_DIR, VENCORD_SETTINGS_FILE } from "./constants";
import { mergeDefaults } from "./utils/mergeDefaults";
const SETTINGS_FILE = join(DATA_DIR, "settings.json"); const SETTINGS_FILE = join(DATA_DIR, "settings.json");
const STATE_FILE = join(DATA_DIR, "state.json"); const STATE_FILE = join(DATA_DIR, "state.json");
function loadSettings<T extends object = any>(file: string, name: string) { function loadSettings<T extends object = any>(file: string, name: string, defaults?: T) {
let settings = {} as T; let settings = {} as T;
try { try {
const content = readFileSync(file, "utf8"); const content = readFileSync(file, "utf8");
@@ -26,6 +28,10 @@ function loadSettings<T extends object = any>(file: string, name: string) {
} }
} catch {} } catch {}
if (defaults) {
mergeDefaults(settings, defaults);
}
const store = new SettingsStore(settings); const store = new SettingsStore(settings);
store.addGlobalChangeListener(o => { store.addGlobalChangeListener(o => {
try { try {
@@ -39,6 +45,6 @@ function loadSettings<T extends object = any>(file: string, name: string) {
return store; return store;
} }
export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vesktop settings"); export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vesktop settings", DefaultVesktopSettings);
export const VencordSettings = loadSettings<TVencordSettings>(VENCORD_SETTINGS_FILE, "Vencord settings"); export const VencordSettings = loadSettings<TVencordSettings>(VENCORD_SETTINGS_FILE, "Vencord settings");
export const State = loadSettings<TState>(STATE_FILE, "Vesktop state"); export const State = loadSettings<TState>(STATE_FILE, "Vesktop state");
+1 -1
View File
@@ -26,7 +26,7 @@ export function createSplashWindow(startMinimized = false) {
const { splashBackground, splashColor, splashTheming, splashPixelated } = Settings.store; const { splashBackground, splashColor, splashTheming, splashPixelated } = Settings.store;
if (splashTheming !== false) { if (splashTheming) {
if (splashColor) { if (splashColor) {
const semiTransparentSplashColor = splashColor.replace("rgb(", "rgba(").replace(")", ", 0.2)"); const semiTransparentSplashColor = splashColor.replace("rgb(", "rgba(").replace(")", ", 0.2)");
+24
View File
@@ -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<T>(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;
}
+1 -1
View File
@@ -14,7 +14,7 @@ let GuildReadStateStore: any;
let NotificationSettingsStore: any; let NotificationSettingsStore: any;
export function setBadge() { export function setBadge() {
if (Settings.store.appBadge === false) return; if (!Settings.store.appBadge) return;
try { try {
const mentionCount = GuildReadStateStore.getTotalMentionCount(); const mentionCount = GuildReadStateStore.getTotalMentionCount();
@@ -27,7 +27,7 @@ export const AutoStartToggle: SettingsComponent = ({ settings }) => {
<VesktopSettingsSwitch <VesktopSettingsSwitch
title="Auto Start Minimized" title="Auto Start Minimized"
description={"Start Vesktop minimized when starting with system"} description={"Start Vesktop minimized when starting with system"}
value={settings.autoStartMinimized ?? false} value={settings.autoStartMinimized}
onChange={v => (settings.autoStartMinimized = v)} onChange={v => (settings.autoStartMinimized = v)}
disabled={!autoStartEnabled} disabled={!autoStartEnabled}
/> />
@@ -14,7 +14,7 @@ export const NotificationBadgeToggle: SettingsComponent = ({ settings }) => {
<VesktopSettingsSwitch <VesktopSettingsSwitch
title="Notification Badge" title="Notification Badge"
description="Show mention badge on the app icon" description="Show mention badge on the app icon"
value={settings.appBadge ?? true} value={settings.appBadge}
onChange={v => { onChange={v => {
settings.appBadge = v; settings.appBadge = v;
if (v) setBadge(); if (v) setBadge();
+16 -35
View File
@@ -10,7 +10,7 @@ import { classNameFactory } from "@vencord/types/api/Styles";
import { BaseText, Divider, ErrorBoundary } from "@vencord/types/components"; import { BaseText, Divider, ErrorBoundary } from "@vencord/types/components";
import { ComponentType } from "react"; import { ComponentType } from "react";
import { WebRTCIPHandlingPolicyPicker } from "renderer/components/settings/WebRTCIPHandlingPolicyPicker"; 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 { isMac } from "renderer/utils";
import { AutoStartToggle } from "./AutoStartToggle"; import { AutoStartToggle } from "./AutoStartToggle";
@@ -26,7 +26,6 @@ interface BooleanSetting {
key: keyof typeof Settings.store; key: keyof typeof Settings.store;
title: string; title: string;
description: string; description: string;
defaultValue: boolean;
disabled?(): boolean; disabled?(): boolean;
invisible?(): boolean; invisible?(): boolean;
} }
@@ -42,64 +41,55 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
{ {
key: "hardwareAcceleration", key: "hardwareAcceleration",
title: "Hardware Acceleration", title: "Hardware Acceleration",
description: "Enable hardware acceleration", description: "Enable hardware acceleration"
defaultValue: true
}, },
{ {
key: "hardwareVideoAcceleration", key: "hardwareVideoAcceleration",
title: "Video Hardware Acceleration", title: "Video Hardware Acceleration",
description: description:
"Enable hardware video acceleration. This can improve performance of screenshare and video playback, but may cause graphical glitches and infinitely loading streams.", "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
disabled: () => Settings.store.hardwareAcceleration === false
} }
], ],
"User Interface": [ "User Interface": [
{ {
key: "nativeTitleBar", key: "nativeTitleBar",
title: "Native Titlebar", title: "Native Titlebar",
description: "Enable the system titlebar in addition to Discord's custom one. Requires a full restart.", description: "Enable the system titlebar in addition to Discord's custom one. Requires a full restart."
defaultValue: false
}, },
{ {
key: "staticTitle", key: "staticTitle",
title: "Static Title", title: "Static Title",
description: 'Makes the window title "Vesktop" instead of changing to the current page', description: 'Makes the window title "Vesktop" instead of changing to the current page'
defaultValue: false
}, },
{ {
key: "enableMenu", key: "enableMenu",
title: "Enable Menu Bar", title: "Enable Menu Bar",
description: "Enables the application menu bar. Press ALT to toggle visibility.", description: "Enables the application menu bar. Press ALT to toggle visibility.",
defaultValue: false,
disabled: () => Settings.store.nativeTitleBar !== true disabled: () => Settings.store.nativeTitleBar !== true
}, },
{ {
key: "enableShadow", key: "enableShadow",
title: "Enable Window Shadow", title: "Enable Window Shadow",
description: "Enables the window shadow. Requires a full restart.", description: "Enables the window shadow. Requires a full restart.",
defaultValue: true,
disabled: () => Settings.store.nativeTitleBar === true disabled: () => Settings.store.nativeTitleBar === true
}, },
{ {
key: "enableRoundedCorners", key: "enableRoundedCorners",
title: "Enable Rounded Corners", title: "Enable Rounded Corners",
description: "Enables rounded corners. Requires a full restart.", description: "Enables rounded corners. Requires a full restart.",
defaultValue: true,
disabled: () => Settings.store.nativeTitleBar === true disabled: () => Settings.store.nativeTitleBar === true
}, },
{ {
key: "enableSplashScreen", key: "enableSplashScreen",
title: "Enable Splash Screen", title: "Enable Splash Screen",
description: description:
"Shows a small splash screen while Vesktop is loading. Disabling this option will show the main window earlier while it's still loading.", "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
}, },
{ {
key: "splashTheming", key: "splashTheming",
title: "Splash theming", title: "Splash theming",
description: "Adapt the splash window colors to your custom theme", description: "Adapt the splash window colors to your custom theme"
defaultValue: true
}, },
WindowsTransparencyControls, WindowsTransparencyControls,
UserAssetsButton UserAssetsButton
@@ -109,34 +99,29 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
key: "tray", key: "tray",
title: "Tray Icon", title: "Tray Icon",
description: "Add a tray icon for Vesktop", description: "Add a tray icon for Vesktop",
defaultValue: true,
invisible: () => isMac invisible: () => isMac
}, },
{ {
key: "minimizeToTray", key: "minimizeToTray",
title: "Minimize to tray", title: "Minimize to tray",
description: "Hitting X will make Vesktop minimize to the tray instead of closing", description: "Hitting X will make Vesktop minimize to the tray instead of closing",
defaultValue: true,
invisible: () => isMac, invisible: () => isMac,
disabled: () => Settings.store.tray === false disabled: () => !Settings.store.tray
}, },
{ {
key: "clickTrayToShowHide", key: "clickTrayToShowHide",
title: "Hide/Show on tray click", title: "Hide/Show on tray click",
description: "Left clicking tray icon will toggle the vesktop window visibility.", description: "Left clicking tray icon will toggle the vesktop window visibility."
defaultValue: false
}, },
{ {
key: "disableMinSize", key: "disableMinSize",
title: "Disable minimum window size", title: "Disable minimum window size",
description: "Allows you to make the window as small as your heart desires", description: "Allows you to make the window as small as your heart desires"
defaultValue: false
}, },
{ {
key: "disableSmoothScroll", key: "disableSmoothScroll",
title: "Disable smooth scrolling", title: "Disable smooth scrolling",
description: "Disables smooth scrolling", description: "Disables smooth scrolling"
defaultValue: false
} }
], ],
Notifications: [ Notifications: [
@@ -144,23 +129,20 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
{ {
key: "enableTaskbarFlashing", key: "enableTaskbarFlashing",
title: "Enable Taskbar Flashing", title: "Enable Taskbar Flashing",
description: "Flashes the app in your taskbar when you have new notifications.", description: "Flashes the app in your taskbar when you have new notifications."
defaultValue: false
} }
], ],
Miscellaneous: [ Miscellaneous: [
{ {
key: "arRPC", key: "arRPC",
title: "Rich Presence", title: "Rich Presence",
description: "Enables Rich Presence via arRPC", description: "Enables Rich Presence via arRPC"
defaultValue: false
}, },
{ {
key: "openLinksWithElectron", key: "openLinksWithElectron",
title: "Open Links in app (experimental)", title: "Open Links in app (experimental)",
description: "Opens links in a new Vesktop window instead of your web browser", description: "Opens links in a new Vesktop window instead of your web browser"
defaultValue: false
}, },
WebRTCIPHandlingPolicyPicker WebRTCIPHandlingPolicyPicker
@@ -182,16 +164,15 @@ function SettingsSections() {
{settings.map((Setting, i) => { {settings.map((Setting, i) => {
if (typeof Setting === "function") return <Setting key={`Custom-${i}`} settings={Settings} />; if (typeof Setting === "function") return <Setting key={`Custom-${i}`} settings={Settings} />;
const { defaultValue, title, description, key, disabled, invisible } = Setting; const { title, description, key, disabled, invisible } = Setting;
if (invisible?.()) return null; if (invisible?.()) return null;
return ( return (
<VesktopSettingsSwitch <VesktopSettingsSwitch
title={title} title={title}
description={description} description={description}
value={Settings[key as any] ?? defaultValue}
onChange={v => (Settings[key as any] = v)}
disabled={disabled?.()} disabled={disabled?.()}
{...getValueAndOnChange(key)}
key={key} key={key}
/> />
); );
@@ -74,7 +74,7 @@ function Asset({ asset }: { asset: UserAssetType }) {
{isSplash && ( {isSplash && (
<FormSwitch <FormSwitch
title="Nearest-Neighbor Scaling (for pixel art)" title="Nearest-Neighbor Scaling (for pixel art)"
value={settings.splashPixelated ?? false} value={settings.splashPixelated}
onChange={val => (settings.splashPixelated = val)} onChange={val => (settings.splashPixelated = val)}
className={Margins.top16} className={Margins.top16}
hideBorder hideBorder
@@ -33,7 +33,7 @@ export const WebRTCIPHandlingPolicyPicker: SettingsComponent = ({ settings }) =>
]} ]}
closeOnSelect={true} closeOnSelect={true}
select={v => (settings.webRTCIPHandlingPolicy = v)} select={v => (settings.webRTCIPHandlingPolicy = v)}
isSelected={v => v === (settings.webRTCIPHandlingPolicy ?? "default")} isSelected={v => v === settings.webRTCIPHandlingPolicy}
serialize={s => s} serialize={s => s}
/> />
</div> </div>
+5 -3
View File
@@ -25,10 +25,12 @@ export function useSettings() {
return Settings.store; return Settings.store;
} }
export function getValueAndOnChange(key: keyof typeof Settings.store) { export function getValueAndOnChange<K extends keyof typeof Settings.store, V extends (typeof Settings.store)[K]>(
key: K
) {
return { return {
value: Settings.store[key] as any, value: Settings.store[key] as V,
onChange: (value: any) => (Settings.store[key] = value) onChange: (value: V) => (Settings.store[key] = value)
}; };
} }
+33
View File
@@ -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"
};
+23 -23
View File
@@ -7,36 +7,36 @@
import type { Rectangle } from "electron"; import type { Rectangle } from "electron";
export interface Settings { export interface Settings {
discordBranch?: "stable" | "canary" | "ptb"; discordBranch: "stable" | "canary" | "ptb";
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic"; transparencyOption: "none" | "mica" | "tabbed" | "acrylic";
webRTCIPHandlingPolicy?: webRTCIPHandlingPolicy:
| "default" | "default"
| "default_public_interface_only" | "default_public_interface_only"
| "default_public_and_private_interfaces" | "default_public_and_private_interfaces"
| "disable_non_proxied_udp"; | "disable_non_proxied_udp";
tray?: boolean; tray: boolean;
minimizeToTray?: boolean; minimizeToTray: boolean;
autoStartMinimized?: boolean; autoStartMinimized: boolean;
openLinksWithElectron?: boolean; openLinksWithElectron: boolean;
staticTitle?: boolean; staticTitle: boolean;
enableMenu?: boolean; enableMenu: boolean;
enableShadow?: boolean; enableShadow: boolean;
enableRoundedCorners?: boolean; enableRoundedCorners: boolean;
disableSmoothScroll?: boolean; disableSmoothScroll: boolean;
hardwareAcceleration?: boolean; hardwareAcceleration: boolean;
hardwareVideoAcceleration?: boolean; hardwareVideoAcceleration: boolean;
arRPC?: boolean; arRPC: boolean;
appBadge?: boolean; appBadge: boolean;
enableTaskbarFlashing?: boolean; enableTaskbarFlashing: boolean;
disableMinSize?: boolean; disableMinSize: boolean;
clickTrayToShowHide?: boolean; clickTrayToShowHide: boolean;
nativeTitleBar?: boolean; nativeTitleBar: boolean;
enableSplashScreen?: boolean; enableSplashScreen: boolean;
splashTheming?: boolean; splashTheming: boolean;
splashPixelated: boolean;
splashColor?: string; splashColor?: string;
splashBackground?: string; splashBackground?: string;
splashPixelated?: boolean;
spellCheckLanguages?: string[]; spellCheckLanguages?: string[];