Merge branch 'main' into global-shortcuts
# Conflicts: # src/renderer/components/settings/Settings.tsx
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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 }}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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];
|
||||
Generated
+482
-465
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -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/*'
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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
@@ -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)");
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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());
|
||||
|
||||
@@ -23,3 +23,16 @@ app.whenReady().then(() => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
scheme: "vesktop",
|
||||
privileges: {
|
||||
standard: true,
|
||||
secure: true,
|
||||
supportFetchAPI: true,
|
||||
corsEnabled: true,
|
||||
stream: true
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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", "[]");
|
||||
|
||||
const pid = node["application.process.id"];
|
||||
items.push({ name: granularName, value: granularProps });
|
||||
|
||||
const first = rtn[0];
|
||||
const firstValues = first.value as Node;
|
||||
return items;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -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>
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Settings } from "renderer/settings";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
if (Settings.store.customTitleBar)
|
||||
if (!Settings.store.nativeTitleBar)
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
};
|
||||
Vendored
+24
-21
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user