Merge branch 'main' into global-shortcuts

# Conflicts:
#	src/renderer/components/settings/Settings.tsx
This commit is contained in:
agent
2026-09-11 11:38:04 +02:00
42 changed files with 1113 additions and 968 deletions
+93
View File
@@ -0,0 +1,93 @@
name: Bug/Crash Report
description: Create a bug or crash report for Vesktop.
labels: [bug]
title: "[Bug] <title>"
body:
- type: markdown
attributes:
value: |
GitHub Issues are for development, not support!
Vesktop and Vencord are two separate things. This issue tracker is only for Vesktop. If your Vesktop crashes, it is very likely an issue with Vencord, not Vesktop.
If you are experiencing any of the following issues, go to our [Discord server](https://discord.gg/D9uwnFnqmd) for support:
- Vesktop doesn't work or crashes
- A Vencord plugin doesn't work anymore
- You are having issues installing Vesktop
- You want to ask a question about Vesktop, Vencord or one of its plugins
- type: input
id: vesktop-version
attributes:
label: Vesktop Version
placeholder: e.g. 1.6.7
validations:
required: true
- type: input
id: os
attributes:
label: Operating System
placeholder: e.g. Windows 11, macOS Tahoe, Fedora Linux 44 (GNOME), Arch Linux (Hyprland)
validations:
required: true
- type: input
id: install-method
attributes:
label: Install Method
placeholder: e.g. Setup exe, dmg, Flatpak, rpm, vesktop-git AUR
validations:
required: true
- type: textarea
id: bug-description
attributes:
label: What happens when the bug or crash occurs?
description: Where does this bug or crash occur, when does it occur, etc.
placeholder: The bug/crash happens sometimes when I do ..., causing this to not work/the app to crash. I think it happens because of ...
validations:
required: true
- type: textarea
id: expected-behaviour
attributes:
label: What is the expected behaviour?
description: Simply detail what the expected behaviour is.
placeholder: I expect Vesktop/Discord to open the ... page instead of ..., it prevents me from doing ...
validations:
required: true
- type: textarea
id: steps-to-take
attributes:
label: How do you recreate this bug or crash?
description: Give us a list of steps in order to recreate the bug or crash.
placeholder: |
1. Do ...
2. Then ...
3. Do this ..., ... and then ...
4. Observe "the bug" or "the crash"
validations:
required: true
- type: textarea
id: crash-log
attributes:
label: Errors
render: text
description: Open the Developer Console with `Ctrl+Shift+i` / `Cmd+Option+i`. Then look for any red errors (Ignore network errors like Failed to load resource) and paste them. Run Vesktop from the command line to get console output and also paste that here
validations:
required: false
- type: checkboxes
id: agreement-check
attributes:
label: Request Agreement
description: We only accept reports for bugs that happen on official builds on Discord Stable. Canary and PTB are Development branches and may be unstable
options:
- label: I've searched existing issues and this hasn't been reported before
- label: I installed Vesktop via an official method (download from vesktop.dev or from source), not via something like Nix packages
- label: This is not a support request
- label: I am willing to submit a fix for this myself
+8 -2
View File
@@ -1,5 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Vencord Support Server
- name: 💬 Discord Community
url: https://discord.gg/D9uwnFnqmd
about: "Need Help? Join our support server and ask in the #vesktop-support channel!"
about: For plugin requests, support questions, and general help - join our Discord server
- name: 🔌 Plugin Requests
url: https://discord.gg/D9uwnFnqmd
about: Want a new plugin? Request it in our Discord community, not here
- name: ❓ Support & Questions
url: https://discord.gg/D9uwnFnqmd
about: Need help? Ask in our Discord server for faster responses
-20
View File
@@ -1,20 +0,0 @@
name: Vesktop Developer Issue
description: Reserved for Vesktop Developers. Join our support server for support.
body:
- type: markdown
attributes:
value: |
# This form is reserved for Vesktop Developers. Do not open an issue.
Instead, use the [#vesktop-support channel](https://discord.com/channels/1015060230222131221/1345457031426871417) on our [Discord server](https://vencord.dev/discord) for help and reporting issues.
Your issue will be closed immediately with no comment and you will be blocked if you ignore this.
This is because 99% of issues are not actually bugs, but rather user or system issues and it adds a lot of noise to our development process.
- type: textarea
id: content
attributes:
label: Content
validations:
required: true
@@ -0,0 +1,45 @@
name: Feature Request
description: Have an idea for a new feature? Request it here! No plugin requests, go to our Discord server for that
labels: [enhancement]
title: "[Feature] <title>"
body:
- type: markdown
attributes:
value: |
This form is not for any of the following:
- Plugin requests — Go to our [Discord server](https://discord.gg/D9uwnFnqmd) for that
- Feature requests for plugins — Go to our [Discord server](https://discord.gg/D9uwnFnqmd) for that
- type: textarea
id: feature-description
attributes:
label: A clear and concise description of the feature you'd like to see
validations:
required: true
- type: textarea
id: implementation-notes
attributes:
label: Implementation Notes
description: If you have ideas about how this could be implemented technically, share them here
validations:
required: false
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Screenshots, mockups, examples from other apps, etc.
validations:
required: false
- type: checkboxes
id: agreement-check
attributes:
label: Request Agreement
options:
- label: I've searched existing issues and this hasn't been suggested before
- label: This is a **core modification feature**, not a plugin request (those go in Discord)
- label: This feature would benefit most users, not just a specific use case
- label: I am willing to work on this myself
+12
View File
@@ -0,0 +1,12 @@
<!--
We do not accept PRs that were created by AI. You will be permanently blocked with no further warning if you submit AI generated PRs.
Also do not use AI in communication, it makes me ill
-->
## Describe your Changes
## Screenshots (if applicable)
## Checklist before submitting
<!-- Hint: [x] this is how to check boxes -->
- [ ] This pull request was written by me, and not an AI agent
+20 -12
View File
@@ -1,9 +1,6 @@
name: Update metainfo on release
name: Upload release metainfo
on:
release:
types:
- published
workflow_dispatch:
permissions:
@@ -15,10 +12,22 @@ jobs:
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Get latest tag
id: latest_tag
run: |
tag=$(git tag --sort=-creatordate | head -n1)
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Checkout latest tag
run: git checkout "${{ steps.latest_tag.outputs.tag }}"
- uses: pnpm/action-setup@v6 # Install pnpm using packageManager key in package.json
- name: Use Node.js 24
uses: actions/setup-node@v6
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
@@ -27,12 +36,11 @@ jobs:
- name: Update metainfo
run: pnpm generateMeta
- name: Commit and merge in changes
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
gh release upload "${{ github.event.release.tag_name }}" dist/dev.vencord.Vesktop.metainfo.xml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload metainfo to release
run: |
gh release upload "${{ steps.latest_tag.outputs.tag }}" dist/dev.vencord.Vesktop.metainfo.xml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
- uses: pnpm/action-setup@v6 # Install pnpm using packageManager key in package.json
- name: Use Node.js 24
uses: actions/setup-node@v6
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
cache: "pnpm"
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
- uses: pnpm/action-setup@v6 # Install pnpm using packageManager key in package.json
- name: Use Node.js 24
uses: actions/setup-node@v6
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
cache: "pnpm"
+14 -16
View File
@@ -1,6 +1,6 @@
{
"name": "vesktop",
"version": "1.6.5",
"version": "1.6.7",
"private": true,
"description": "Vesktop is a custom Discord desktop app",
"keywords": [],
@@ -22,42 +22,40 @@
"test": "pnpm lint && pnpm testTypes",
"testTypes": "tsc --noEmit",
"watch": "pnpm build --watch",
"generateMeta": "tsx scripts/utils/generateMeta.mts",
"updateArrpcDB": "node ./node_modules/arrpc/update_db.js",
"postinstall": "pnpm updateArrpcDB"
"generateMeta": "tsx scripts/utils/generateMeta.mts"
},
"dependencies": {
"arrpc": "github:OpenAsar/arrpc#2234e9c9111f4c42ebcc3aa6a2215bfd979eef77",
"arrpc": "github:Vencord/arrpc#82ece7442564d725d8202a8bb9bed5903ee85026",
"electron-updater": "6.6.2"
},
"optionalDependencies": {
"@vencord/venmic": "^6.1.0"
"@vencord/venmic": "^7.2.3"
},
"devDependencies": {
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
"@stylistic/eslint-plugin": "^5.10.0",
"@types/node": "^26.0.1",
"@types/node": "^26.2.0",
"@types/react": "19.2.9",
"@vencord/types": "^1.14.1",
"@vencord/types": "^1.15.1",
"dotenv": "^17.3.1",
"electron": "^43.0.0",
"electron": "43.2.0",
"electron-builder": "^26.15.3",
"esbuild": "^0.28.1",
"eslint": "^10.6.0",
"esbuild": "^0.28.2",
"eslint": "^10.8.1",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-path-alias": "^2.1.0",
"eslint-plugin-prettier": "^5.5.6",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-simple-header": "^1.2.2",
"eslint-plugin-simple-import-sort": "^13.0.0",
"eslint-plugin-simple-import-sort": "^14.0.0",
"eslint-plugin-unused-imports": "^4.4.1",
"libvesktop": "link:packages/libvesktop",
"prettier": "^3.9.4",
"prettier": "^3.9.6",
"source-map-support": "^0.5.21",
"tsx": "^4.22.4",
"type-fest": "^5.7.0",
"tsx": "^4.23.12",
"type-fest": "^5.8.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.62.1",
"typescript-eslint": "^8.67.0",
"xml-formatter": "^3.6.7"
},
"packageManager": "pnpm@11.9.0",
-27
View File
@@ -1,27 +0,0 @@
diff --git a/src/process/index.js b/src/process/index.js
index 389b0845256a34b4536d6da99edb00d17f13a6b4..f17a0ac687e9110ebfd33cb91fd2f6250d318643 100644
--- a/src/process/index.js
+++ b/src/process/index.js
@@ -5,8 +5,20 @@ import fs from 'node:fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
-const __dirname = dirname(fileURLToPath(import.meta.url));
-const DetectableDB = JSON.parse(fs.readFileSync(join(__dirname, 'detectable.json'), 'utf8'));
+const DetectableDB = require('./detectable.json');
+DetectableDB.push(
+ {
+ aliases: ["Obs"],
+ executables: [
+ { is_launcher: false, name: "obs", os: "linux" },
+ { is_launcher: false, name: "obs.exe", os: "win32" },
+ { is_launcher: false, name: "obs.app", os: "darwin" }
+ ],
+ hook: true,
+ id: "STREAMERMODE",
+ name: "OBS"
+ }
+);
import * as Natives from './native/index.js';
const Native = Natives[process.platform];
+482 -465
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,5 +1,4 @@
patchedDependencies:
arrpc@3.5.0: patches/arrpc@3.5.0.patch
electron-updater: patches/electron-updater.patch
nodeLinker: hoisted
allowBuilds:
@@ -14,3 +13,4 @@ packages:
- packages/*
minimumReleaseAgeExclude:
- electron
- '@vencord/*'
+2 -1
View File
@@ -46,7 +46,8 @@ function generateDescription(description: string, descriptionNode: Element) {
const releases = await fetch("https://api.github.com/repos/Vencord/Vesktop/releases", {
headers: {
Accept: "application/vnd.github+json",
"X-Github-Api-Version": "2022-11-28"
"X-Github-Api-Version": "2022-11-28",
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`
}
}).then(res => res.json());
+4 -1
View File
@@ -4,7 +4,8 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { resolve } from "path";
import { SESSION_DATA_DIR } from "main/constants";
import { join, resolve } from "path";
import { IpcCommands } from "shared/IpcEvents";
import { MessageChannel, Worker } from "worker_threads";
@@ -19,6 +20,8 @@ const inviteCodeRegex = /^(\w|-)+$/;
export async function initArRPC() {
if (worker || !Settings.store.arRPC) return;
process.env.ARRPC_DETECTABLE_CACHE_PATH = join(SESSION_DATA_DIR, "arrpc-detectable-db.json");
try {
const { port1: hostPort, port2: workerPort } = new MessageChannel();
+1 -1
View File
@@ -149,7 +149,7 @@ handle(IpcEvents.SELECT_VENCORD_DIR, async (_e, value?: null) => {
if (!res.filePaths.length) return "cancelled";
const dir = res.filePaths[0];
if (!isValidVencordInstall(dir)) return "invalid";
if (!(await isValidVencordInstall(dir))) return "invalid";
State.store.vencordDir = dir;
+4 -19
View File
@@ -46,7 +46,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 {
@@ -65,29 +65,14 @@ function init() {
app.commandLine.appendSwitch("disable-smooth-scrolling");
}
// disable renderer backgrounding to prevent the app from unloading when in the background
// https://github.com/electron/electron/issues/2822
// https://github.com/GoogleChrome/chrome-launcher/blob/5a27dd574d47a75fec0fb50f7b774ebf8a9791ba/docs/chrome-flags-for-tools.md#task-throttling
app.commandLine.appendSwitch("disable-renderer-backgrounding");
app.commandLine.appendSwitch("disable-background-timer-throttling");
app.commandLine.appendSwitch("disable-backgrounding-occluded-windows");
if (process.platform === "win32") {
disabledFeatures.add("CalculateNativeWinOcclusion");
}
// 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.
disabledFeatures.add("WinRetrieveSuggestionsOnlyOnDemand");
// Prevent Discord from registering as a media service.
disabledFeatures.add("HardwareMediaKeyHandling");
disabledFeatures.add("MediaSessionService");
if (isLinux) {
// Support TTS on Linux using https://wiki.archlinux.org/title/Speech_dispatcher
app.commandLine.appendSwitch("enable-speech-dispatcher");
// This is needed to fix washed out colours - https://github.com/electron/electron/issues/49566
// Supposed to be fixed already according to comments there, but it's just not lol, I can repro on Electron 43.0.0
// when moving the window from my main monitor (HDR - not sure if this is relevant lol) to second monitor (SDR) and back
@@ -99,8 +84,8 @@ function init() {
disabledFeatures.forEach(feat => enabledFeatures.delete(feat));
const enabledFeaturesArray = enabledFeatures.values().filter(Boolean).toArray();
const disabledFeaturesArray = disabledFeatures.values().filter(Boolean).toArray();
const enabledFeaturesArray = [...enabledFeatures].filter(Boolean);
const disabledFeaturesArray = [...disabledFeatures].filter(Boolean);
if (enabledFeaturesArray.length) {
app.commandLine.appendSwitch("enable-features", enabledFeaturesArray.join(","));
+40 -12
View File
@@ -156,6 +156,25 @@ function initMenuBar(win: BrowserWindow) {
accelerator: "CmdOrCtrl+=",
role: "zoomIn",
visible: false
},
// numpad zooms
{
label: "Zoom in (hidden)",
accelerator: "CmdOrCtrl+numadd",
role: "zoomIn",
visible: false
},
{
label: "Zoom out (hidden)",
accelerator: "CmdOrCtrl+numsub",
role: "zoomOut",
visible: false
},
{
label: "Reset Zoom (hidden)",
accelerator: "CmdOrCtrl+num0",
role: "resetZoom",
visible: false
}
] satisfies MenuItemList;
@@ -312,17 +331,24 @@ function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
}
function buildBrowserWindowOptions(): BrowserWindowConstructorOptions {
const { staticTitle, transparencyOption, enableMenu, customTitleBar, splashTheming, splashBackground } =
Settings.store;
const {
staticTitle,
transparencyOption,
enableMenu,
enableShadow,
enableRoundedCorners,
nativeTitleBar,
splashTheming,
splashBackground
} = Settings.store;
const { frameless, transparent, macosVibrancyStyle } = VencordSettings.store;
const { transparent, macosVibrancyStyle } = VencordSettings.store;
const noFrame = frameless === true || customTitleBar === 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,
@@ -334,8 +360,10 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions {
// disable renderer backgrounding to prevent the app from unloading when in the background
backgroundThrottling: false
},
frame: !noFrame,
frame: !frameless,
autoHideMenuBar: enableMenu,
hasShadow: enableShadow,
roundedCorners: enableRoundedCorners,
...getWindowBoundsOptions()
};
@@ -348,7 +376,7 @@ function buildBrowserWindowOptions(): BrowserWindowConstructorOptions {
options.backgroundColor = "#00000000";
options.backgroundMaterial = transparencyOption;
if (customTitleBar) {
if (frameless) {
options.transparent = true;
}
}
@@ -378,10 +406,10 @@ function createMainWindow() {
const win = (mainWin = new BrowserWindow(buildBrowserWindowOptions()));
win.setMenuBarVisibility(false);
if (process.platform === "darwin" && Settings.store.customTitleBar) win.setWindowButtonVisibility(false);
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();
@@ -441,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
+7 -1
View File
@@ -5,6 +5,7 @@
*/
import { desktopCapturer, session, Streams } from "electron";
import { release } from "os";
import type { StreamPick } from "renderer/components/ScreenSharePicker";
import { IpcCommands, IpcEvents } from "shared/IpcEvents";
@@ -14,6 +15,8 @@ import { handle } from "./utils/ipcWrappers";
const isWayland =
process.platform === "linux" && (process.env.XDG_SESSION_TYPE === "wayland" || !!process.env.WAYLAND_DISPLAY);
const supportsLoopbackWithoutChrome = process.platform === "win32" && Number(release().split(".").pop()) >= 19045;
export function registerScreenShareHandler() {
handle(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, async (_, id: string) => {
const sources = await desktopCapturer.getSources({
@@ -78,7 +81,10 @@ export function registerScreenShareHandler() {
const streams: Streams = {
video: source
};
if (choice.audio && process.platform === "win32") streams.audio = "loopback";
if (choice.audio && process.platform === "win32") {
// @ts-expect-error loopbackWithoutChrome is real but not documented
streams.audio = supportsLoopbackWithoutChrome ? "loopbackWithoutChrome" : "loopback";
}
callback(streams);
});
+8 -2
View File
@@ -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<T extends object = any>(file: string, name: string) {
function loadSettings<T extends object = any>(file: string, name: string, defaults?: T) {
let settings = {} as T;
try {
const content = readFileSync(file, "utf8");
@@ -26,6 +28,10 @@ function loadSettings<T extends object = any>(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<T extends object = any>(file: string, name: string) {
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 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;
if (splashTheming !== false) {
if (splashTheming) {
if (splashColor) {
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) && v !== null) {
obj[key] ??= {} as any;
mergeDefaults(obj[key], v);
} else {
obj[key] ??= v;
}
}
return obj;
}
+1 -1
View File
@@ -37,7 +37,7 @@ const DEFAULT_POPOUT_OPTIONS: BrowserWindowConstructorOptions = {
backgroundColor: "#2f3136",
minWidth: MIN_POPOUT_WIDTH,
minHeight: MIN_POPOUT_HEIGHT,
frame: Settings.store.customTitleBar !== true,
frame: Settings.store.nativeTitleBar,
titleBarStyle: process.platform === "darwin" ? "hidden" : undefined,
trafficLightPosition:
process.platform === "darwin"
+1 -1
View File
@@ -64,7 +64,7 @@ const existsAsync = (path: string) =>
.catch(() => false);
export async function isValidVencordInstall(dir: string) {
const results = await Promise.all(["package.json", ...FILES_TO_DOWNLOAD].map(f => existsAsync(join(dir, f))));
const results = await Promise.all(FILES_TO_DOWNLOAD.map(f => existsAsync(join(dir, f))));
return !results.includes(false);
}
+38 -48
View File
@@ -66,70 +66,60 @@ function getRendererAudioServicePid() {
);
}
function buildLinkData({ include, exclude }: { include?: Node[]; exclude?: Node[] }) {
const pid = getRendererAudioServicePid();
const { mute, ignoreDevices, ignoreInputMedia, ignoreVirtual, workaround, onlySpeakers, onlyDefaultSpeakers } =
Settings.store.audio ?? {};
const data: LinkData = {
mute: mute ?? true,
include: include ?? [],
exclude: exclude ?? [],
only_speakers: onlySpeakers,
ignore_devices: ignoreDevices,
only_default_speakers: onlyDefaultSpeakers
};
data.exclude.push({ "application.process.id": pid });
if (ignoreInputMedia ?? true) {
data.exclude.push({ "media.class": "Stream/Input/Audio" });
}
if (ignoreVirtual) {
data.exclude.push({ "node.virtual": "true" });
}
if (workaround) {
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
}
return data;
}
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
const audioPid = getRendererAudioServicePid();
const { granularSelect } = Settings.store.audio ?? {};
const targets = obtainVenmic()
?.list(granularSelect ? ["node.name"] : undefined)
?.list(granularSelect ? [] : ["application.name", "node.name"])
.filter(s => s["application.process.id"] !== audioPid);
return targets ? { ok: true, targets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
});
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, include: Node[]) => {
const pid = getRendererAudioServicePid();
const { ignoreDevices, ignoreInputMedia, ignoreVirtual, workaround } = Settings.store.audio ?? {};
const data: LinkData = {
include,
exclude: [{ "application.process.id": pid }],
ignore_devices: ignoreDevices
};
if (ignoreInputMedia ?? true) {
data.exclude.push({ "media.class": "Stream/Input/Audio" });
}
if (ignoreVirtual) {
data.exclude.push({ "node.virtual": "true" });
}
if (workaround) {
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
}
return obtainVenmic()?.link(data);
return obtainVenmic()?.link(buildLinkData({ include }));
});
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, exclude: Node[]) => {
const pid = getRendererAudioServicePid();
return obtainVenmic()?.link(buildLinkData({ exclude }));
});
const { workaround, ignoreDevices, ignoreInputMedia, ignoreVirtual, onlySpeakers, onlyDefaultSpeakers } =
Settings.store.audio ?? {};
const data: LinkData = {
include: [],
exclude: [{ "application.process.id": pid }, ...exclude],
only_speakers: onlySpeakers,
ignore_devices: ignoreDevices,
only_default_speakers: onlyDefaultSpeakers
};
if (ignoreInputMedia ?? true) {
data.exclude.push({ "media.class": "Stream/Input/Audio" });
}
if (ignoreVirtual) {
data.exclude.push({ "node.virtual": "true" });
}
if (workaround) {
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
}
return obtainVenmic()?.link(data);
ipcMain.handle(IpcEvents.VIRT_MIC_UNMUTE, () => {
return obtainVenmic()?.unmute();
});
ipcMain.handle(IpcEvents.VIRT_MIC_STOP, () => obtainVenmic()?.unlink());
+13
View File
@@ -23,3 +23,16 @@ app.whenReady().then(() => {
}
});
});
protocol.registerSchemesAsPrivileged([
{
scheme: "vesktop",
privileges: {
standard: true,
secure: true,
supportFetchAPI: true,
corsEnabled: true,
stream: true
}
}
]);
+1
View File
@@ -92,6 +92,7 @@ export const VesktopNative = {
>(IpcEvents.VIRT_MIC_LIST),
start: (include: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START, include),
startSystem: (exclude: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, exclude),
unmute: () => invoke<void>(IpcEvents.VIRT_MIC_UNMUTE),
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
},
clipboard: {
+1 -1
View File
@@ -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();
+103 -119
View File
@@ -8,7 +8,6 @@ import "./screenSharePicker.css";
import { classNameFactory } from "@vencord/types/api/Styles";
import {
BaseText,
Button,
Card,
CogWheel,
@@ -20,18 +19,18 @@ import {
RestartIcon,
Span
} from "@vencord/types/components";
import { Logger, useAwaiter, useForceUpdater } from "@vencord/types/utils";
import { onceReady } from "@vencord/types/webpack";
import {
closeModal,
Logger,
ModalCloseButton,
Modals,
ModalSize,
FluxDispatcher,
MediaEngineStore,
Modal,
openModal,
useAwaiter,
useForceUpdater
} from "@vencord/types/utils";
import { onceReady } from "@vencord/types/webpack";
import { FluxDispatcher, MediaEngineStore, Select, UserStore, useState } from "@vencord/types/webpack/common";
Select,
UserStore,
useState
} from "@vencord/types/webpack/common";
import { Node } from "@vencord/venmic";
import type { Dispatch, SetStateAction } from "react";
import { addPatch } from "renderer/patches/shared";
@@ -122,15 +121,25 @@ addPatch({
if (isLinux) {
onceReady.then(() => {
FluxDispatcher.subscribe("STREAM_CLOSE", ({ streamKey }: { streamKey: string }) => {
const owner = streamKey.split(":").at(-1);
const ownsStream = (streamKey: string) => {
return streamKey.split(":").at(-1) === UserStore.getCurrentUser().id;
};
if (owner !== UserStore.getCurrentUser().id) {
FluxDispatcher.subscribe("STREAM_CLOSE", ({ streamKey }: { streamKey: string }) => {
if (!ownsStream(streamKey)) {
return;
}
VesktopNative.virtmic.stop();
});
FluxDispatcher.subscribe("STREAM_UPDATE", ({ streamKey }: { streamKey: string }) => {
if (!ownsStream(streamKey)) {
return;
}
VesktopNative.virtmic.unmute();
});
});
}
@@ -210,15 +219,20 @@ function AudioSettingsModal({
const Settings = useSettings();
return (
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
<Modals.ModalHeader className={cl("header")}>
<BaseText size="lg" weight="semibold" tag="h3" style={{ flexGrow: 1 }}>
Audio Settings
</BaseText>
<ModalCloseButton onClick={close} />
</Modals.ModalHeader>
<Modals.ModalContent className={cl("modal", "venmic-settings")}>
<Modal
{...modalProps}
size="lg"
title="Audio Settings"
actions={[{ text: "Back", variant: "secondary", onClick: close }]}
>
<div className={cl("venmic-settings")}>
<FormSwitch
title="Initial Mute"
description="Fix an initial audio spike caused by chromium."
hideBorder
onChange={v => (Settings.audio = { ...Settings.audio, mute: v })}
value={Settings.audio?.mute ?? true}
/>
<FormSwitch
title="Microphone Workaround"
description="Work around an issue that causes the microphone to be shared instead of the correct audio. Only enable if you're experiencing this issue."
@@ -302,13 +316,8 @@ function AudioSettingsModal({
value={Settings.audio?.deviceSelect ?? false}
disabled={Settings.audio?.ignoreDevices}
/>
</Modals.ModalContent>
<Modals.ModalFooter className={cl("footer")}>
<Button variant="secondary" onClick={close}>
Back
</Button>
</Modals.ModalFooter>
</Modals.ModalRoot>
</div>
</Modal>
);
}
@@ -455,78 +464,61 @@ function isSpecialSource(value?: AudioSource | AudioSources): value is SpecialSo
return typeof value === "string";
}
function hasMatchingProps(value: Node, other: Node) {
return Object.keys(value).every(key => value[key] === other[key]);
}
function mapToAudioItem(node: AudioSource, granularSelect?: boolean, deviceSelect?: boolean): AudioItem[] {
if (isSpecialSource(node)) {
return [{ name: node, value: node }];
}
const rtn: AudioItem[] = [];
const mediaClass = node["media.class"];
if (mediaClass?.includes("Video") || mediaClass?.includes("Midi")) {
return rtn;
return [];
}
if (!deviceSelect && node["device.id"]) {
return rtn;
return [];
}
const name = node["application.name"];
const preferred = ["application.name", "node.description", "node.name", "application.process.binary"] as const;
const prop = preferred.find(prop => node[prop]);
if (name) {
rtn.push({ name: name, value: { "application.name": name } });
if (!prop) {
return [];
}
const name = node[prop]!;
const items: AudioItem[] = [{ name: name, value: { [prop]: name } }];
if (!granularSelect) {
return rtn;
return items;
}
const rawName = node["node.name"];
let granularName = name;
const granularProps: Node = { [prop]: name };
if (!name) {
rtn.push({ name: rawName, value: { "node.name": rawName } });
const append = (name: string, brackets: string) => {
const value = node[name];
if (!value) {
return;
}
const binary = node["application.process.binary"];
granularName += ` ${brackets[0]}${value}${brackets[1]}`;
granularProps[name] = value;
};
if (!name && binary) {
rtn.push({ name: binary, value: { "application.process.binary": binary } });
append("application.process.id", "<>");
append("application.process.binary", "()");
append("media.name", "[]");
items.push({ name: granularName, value: granularProps });
return items;
}
const pid = node["application.process.id"];
const first = rtn[0];
const firstValues = first.value as Node;
if (pid) {
rtn.push({
name: `${first.name} (${pid})`,
value: { ...firstValues, "application.process.id": pid }
});
}
const mediaName = node["media.name"];
if (mediaName) {
rtn.push({
name: `${first.name} [${mediaName}]`,
value: { ...firstValues, "media.name": mediaName }
});
}
if (mediaClass) {
rtn.push({
name: `${first.name} [${mediaClass}]`,
value: { ...firstValues, "media.class": mediaClass }
});
}
return rtn;
function hasMatchingProps(value: Node, other: Node) {
const keys = Object.keys(value);
return keys.length === Object.keys(other).length && keys.every(key => value[key] === other[key]);
}
function isItemSelected(sources?: AudioSources) {
@@ -655,9 +647,10 @@ function AudioSourcePickerLinux({
}))}
isSelected={isItemSelected(includeSources)}
select={updateItems(setIncludeSources, includeSources)}
serialize={String}
serialize={JSON.stringify}
popoutPosition="top"
closeOnSelect={false}
renderOptionLabel={o => o.label}
/>
</SimpleErrorBoundary>
</section>
@@ -675,9 +668,10 @@ function AudioSourcePickerLinux({
}))}
isSelected={isItemSelected(excludeSources)}
select={updateItems(setExcludeSources, excludeSources)}
serialize={String}
serialize={JSON.stringify}
popoutPosition="top"
closeOnSelect={false}
renderOptionLabel={o => o.label}
/>
</SimpleErrorBoundary>
</section>
@@ -721,30 +715,7 @@ function ModalComponent({
frameRate: "30"
});
return (
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
<Modals.ModalHeader className={cl("header")}>
<BaseText size="lg" weight="semibold" tag="h3" style={{ flexGrow: 1 }}>
Screen Share Picker
</BaseText>
<ModalCloseButton onClick={close} />
</Modals.ModalHeader>
<Modals.ModalContent className={cl("modal")}>
{!selected ? (
<ScreenPicker screens={screens} chooseScreen={setSelected} />
) : (
<StreamSettingsUi
source={screens.find(s => s.id === selected)!}
settings={settings}
setSettings={setSettings}
skipPicker={skipPicker}
/>
)}
</Modals.ModalContent>
<Modals.ModalFooter className={cl("footer")}>
<Button
disabled={!selected}
onClick={() => {
function handleGoLive() {
currentSettings = settings;
try {
const frameRate = Number(qualitySettings.frameRate);
@@ -788,10 +759,7 @@ function ModalComponent({
try {
await track.applyConstraints(constraints);
logger.info(
"Applied constraints successfully. New constraints:",
track.getConstraints()
);
logger.info("Applied constraints successfully. New constraints:", track.getConstraints());
} catch (e) {
logger.error("Failed to apply constraints.", e);
}
@@ -801,21 +769,37 @@ function ModalComponent({
}
close();
}}
>
Go Live
</Button>
}
{selected && !skipPicker ? (
<Button variant="secondary" onClick={() => setSelected(void 0)}>
Back
</Button>
const showGoBack = selected && !skipPicker;
return (
<Modal
{...modalProps}
size="lg"
title="Screen Share Picker"
actions={[
{
text: showGoBack ? "Back" : "Cancel",
variant: "secondary",
onClick: () => (showGoBack ? setSelected(void 0) : close())
},
{
text: "Go Live",
disabled: !selected,
onClick: handleGoLive
}
]}
>
{!selected ? (
<ScreenPicker screens={screens} chooseScreen={setSelected} />
) : (
<Button variant="secondary" onClick={close}>
Cancel
</Button>
<StreamSettingsUi
source={screens.find(s => s.id === selected)!}
settings={settings}
setSettings={setSettings}
skipPicker={skipPicker}
/>
)}
</Modals.ModalFooter>
</Modals.ModalRoot>
</Modal>
);
}
@@ -1,7 +1,3 @@
.vcd-screen-picker-modal {
padding: 1em;
}
.vcd-screen-picker-header-close-button {
margin-left: auto;
}
@@ -27,7 +27,7 @@ export const AutoStartToggle: SettingsComponent = ({ settings }) => {
<VesktopSettingsSwitch
title="Auto Start Minimized"
description={"Start Vesktop minimized when starting with system"}
value={settings.autoStartMinimized ?? false}
value={settings.autoStartMinimized}
onChange={v => (settings.autoStartMinimized = v)}
disabled={!autoStartEnabled}
/>
@@ -4,18 +4,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { BaseText, Button, Heading, Paragraph, TextButton } from "@vencord/types/components";
import {
Margins,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalRoot,
ModalSize,
openModal,
useForceUpdater
} from "@vencord/types/utils";
import { Toasts } from "@vencord/types/webpack/common";
import { Button, Heading, Paragraph, TextButton } from "@vencord/types/components";
import { Margins, useForceUpdater } from "@vencord/types/utils";
import { Modal, openModal, Toasts } from "@vencord/types/webpack/common";
import { Settings } from "shared/settings";
import { cl, SettingsComponent } from "./Settings";
@@ -26,20 +17,11 @@ export const DeveloperOptionsButton: SettingsComponent = ({ settings }) => {
function openDeveloperOptionsModal(settings: Settings) {
openModal(props => (
<ModalRoot {...props} size={ModalSize.MEDIUM}>
<ModalHeader>
<BaseText size="lg" weight="semibold" tag="h3" style={{ flexGrow: 1 }}>
Vesktop Developer Options
</BaseText>
<ModalCloseButton onClick={props.onClose} />
</ModalHeader>
<ModalContent>
<div style={{ padding: "1em 0" }}>
<Heading tag="h5">Vencord Location</Heading>
<Modal {...props} size="lg" title="Vesktop Developer Options">
<Heading tag="h4">Vencord Location</Heading>
<VencordLocationPicker settings={settings} />
<Heading tag="h5" className={Margins.top16}>
<Heading tag="h4" className={Margins.top16}>
Debugging
</Heading>
<div className={cl("button-grid")}>
@@ -48,9 +30,7 @@ function openDeveloperOptionsModal(settings: Settings) {
Open chrome://webrtc-internals
</Button>
</div>
</div>
</ModalContent>
</ModalRoot>
</Modal>
));
}
@@ -14,7 +14,7 @@ export const NotificationBadgeToggle: SettingsComponent = ({ settings }) => {
<VesktopSettingsSwitch
title="Notification Badge"
description="Show mention badge on the app icon"
value={settings.appBadge ?? true}
value={settings.appBadge}
onChange={v => {
settings.appBadge = v;
if (v) setBadge();
+32 -37
View File
@@ -11,8 +11,8 @@ import { BaseText, Divider, ErrorBoundary } from "@vencord/types/components";
import { ComponentType } from "react";
import { WebRTCIPHandlingPolicyPicker } from "renderer/components/settings/WebRTCIPHandlingPolicyPicker";
import { KeybindsButton } from "renderer/globalShortcuts/ShortcutSettings";
import { Settings, useSettings } from "renderer/settings";
import { isMac, isWindows } from "renderer/utils";
import { getValueAndOnChange, Settings, useSettings } from "renderer/settings";
import { isMac } from "renderer/utils";
import { AutoStartToggle } from "./AutoStartToggle";
import { DeveloperOptionsButton } from "./DeveloperOptions";
@@ -27,7 +27,6 @@ interface BooleanSetting {
key: keyof typeof Settings.store;
title: string;
description: string;
defaultValue: boolean;
disabled?(): boolean;
invisible?(): boolean;
}
@@ -43,50 +42,55 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
{
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: "customTitleBar",
title: "Discord Titlebar",
description: "Use Discord's custom title bar instead of the native system one. Requires a full restart.",
defaultValue: isWindows
key: "nativeTitleBar",
title: "Native Titlebar",
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.customTitleBar ?? isWindows
disabled: () => !Settings.store.nativeTitleBar
},
{
key: "enableShadow",
title: "Enable Window Shadow",
description: "Enables the window shadow. Requires a full restart.",
disabled: () => Settings.store.nativeTitleBar
},
{
key: "enableRoundedCorners",
title: "Enable Rounded Corners",
description: "Enables rounded corners. Requires a full restart.",
disabled: () => Settings.store.nativeTitleBar
},
{
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
@@ -96,34 +100,29 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
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"
},
KeybindsButton
],
@@ -132,23 +131,20 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
{
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
@@ -170,16 +166,15 @@ function SettingsSections() {
{settings.map((Setting, i) => {
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;
return (
<VesktopSettingsSwitch
title={title}
description={description}
value={Settings[key as any] ?? defaultValue}
onChange={v => (Settings[key as any] = v)}
disabled={disabled?.()}
{...getValueAndOnChange(key)}
key={key}
/>
);
@@ -7,18 +7,8 @@
import "./UserAssets.css";
import { BaseText, Button, FormSwitch } from "@vencord/types/components";
import {
Margins,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalRoot,
ModalSize,
openModal,
wordsFromCamel,
wordsToTitle
} from "@vencord/types/utils";
import { showToast, useState } from "@vencord/types/webpack/common";
import { Margins, wordsFromCamel, wordsToTitle } from "@vencord/types/utils";
import { Modal, openModal, showToast, useState } from "@vencord/types/webpack/common";
import { UserAssetType } from "main/userAssets";
import { useSettings } from "renderer/settings";
@@ -32,22 +22,13 @@ export const UserAssetsButton: SettingsComponent = () => {
function openAssetsModal() {
openModal(props => (
<ModalRoot {...props} size={ModalSize.MEDIUM}>
<ModalHeader>
<BaseText size="lg" weight="semibold" tag="h3" style={{ flexGrow: 1 }}>
User Assets
</BaseText>
<ModalCloseButton onClick={props.onClose} />
</ModalHeader>
<ModalContent>
<Modal {...props} size="lg" title="User Assets">
<div className="vcd-user-assets">
{CUSTOMIZABLE_ASSETS.map(asset => (
<Asset key={asset} asset={asset} />
))}
</div>
</ModalContent>
</ModalRoot>
</Modal>
));
}
@@ -93,7 +74,7 @@ function Asset({ asset }: { asset: UserAssetType }) {
{isSplash && (
<FormSwitch
title="Nearest-Neighbor Scaling (for pixel art)"
value={settings.splashPixelated ?? false}
value={settings.splashPixelated}
onChange={val => (settings.splashPixelated = val)}
className={Margins.top16}
hideBorder
@@ -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}
/>
</div>
@@ -7,17 +7,8 @@
import "./ShortcutSettings.css";
import { BaseText, Button, Card, DeleteIcon, Switch } from "@vencord/types/components";
import {
classNameFactory,
identity,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalRoot,
ModalSize,
openModal
} from "@vencord/types/utils";
import { React, Select, useState } from "@vencord/types/webpack/common";
import { classNameFactory, identity, openModal } from "@vencord/types/utils";
import { Modal, React, Select, useState } from "@vencord/types/webpack/common";
import { SettingsComponent } from "renderer/components/settings/Settings";
import { onIpcCommand } from "renderer/ipcCommands";
import { reactiveValue } from "renderer/reactiveState";
@@ -181,18 +172,9 @@ export function KeyBindSettings() {
export function openKeybindsModal() {
openModal(props => (
<ModalRoot {...props} size={ModalSize.LARGE}>
<ModalHeader>
<BaseText size="lg" weight="semibold" tag="h3" style={{ flexGrow: 1 }}>
Keybinds
</BaseText>
<ModalCloseButton onClick={props.onClose} />
</ModalHeader>
<ModalContent>
<Modal {...props} size="lg" title="Keybinds">
<KeyBindSettings />
</ModalContent>
</ModalRoot>
</Modal>
));
}
+2 -3
View File
@@ -21,8 +21,7 @@ addPatch({
],
getPlatformClass() {
if (Settings.store.customTitleBar) return "platform-win";
if (isMac) return "platform-osx";
return "platform-web";
if (Settings.store.nativeTitleBar) return "platform-web";
return isMac ? "platform-osx" : "platform-win";
}
});
+1 -1
View File
@@ -8,7 +8,7 @@ import { Settings } from "renderer/settings";
import { addPatch } from "./shared";
if (Settings.store.customTitleBar)
if (!Settings.store.nativeTitleBar)
addPatch({
patches: [
{
+5 -3
View File
@@ -25,10 +25,12 @@ export function useSettings() {
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 {
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)
};
}
+1
View File
@@ -49,6 +49,7 @@ export const enum IpcEvents {
VIRT_MIC_LIST = "VCD_VIRT_MIC_LIST",
VIRT_MIC_START = "VCD_VIRT_MIC_START",
VIRT_MIC_START_SYSTEM = "VCD_VIRT_MIC_START_ALL",
VIRT_MIC_UNMUTE = "VCD_VIRT_MIC_UNMUTE",
VIRT_MIC_STOP = "VCD_VIRT_MIC_STOP",
CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE",
+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"
};
+24 -21
View File
@@ -8,34 +8,36 @@ import type { Rectangle } from "electron";
import { KeyBind } from "renderer/globalShortcuts/ShortcutSettings";
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;
disableSmoothScroll?: boolean;
hardwareAcceleration?: boolean;
hardwareVideoAcceleration?: boolean;
arRPC?: boolean;
appBadge?: boolean;
enableTaskbarFlashing?: boolean;
disableMinSize?: boolean;
clickTrayToShowHide?: boolean;
customTitleBar?: 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[];
@@ -49,6 +51,7 @@ export interface Settings {
ignoreDevices?: boolean;
ignoreInputMedia?: boolean;
mute?: boolean;
onlySpeakers?: boolean;
onlyDefaultSpeakers?: boolean;
};