Merge branch 'Vencord:main' into zoom-parity
This commit is contained in:
commit
436837d020
36 changed files with 828 additions and 537 deletions
5
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
5
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -13,10 +13,11 @@ body:
|
||||||
|
|
||||||
Make sure both Vesktop and Vencord are fully up to date. You can update Vencord by right-clicking the Vesktop tray icon and pressing "Update Vencord"
|
Make sure both Vesktop and Vencord are fully up to date. You can update Vencord by right-clicking the Vesktop tray icon and pressing "Update Vencord"
|
||||||
|
|
||||||
Do not report any of the following issues:
|
**DO NOT REPORT** any of the following issues:
|
||||||
- Purely graphical glitches like flickering, scaling issues, etc: Issue with your gpu. Nothing we can do, update drivers or disable hardware acceleration
|
- Purely graphical glitches like flickering, scaling issues, etc: Issue with your gpu. Nothing we can do, update drivers or disable hardware acceleration
|
||||||
- Vencord related issues: This is the Vesktop repo, not Vencord
|
- Vencord related issues: This is the Vesktop repo, not Vencord
|
||||||
- Screenshare not starting / black screening on Linux: Issue with your desktop environment, specifically its xdg-desktop-portal
|
- **SCREENSHARE NOT STARTING** / black screening on Linux: Issue with your desktop environment, specifically its xdg-desktop-portal.
|
||||||
|
If you're on flatpak, try using native version. If that also doesn't work, you have to fix your systen. Inspect errors and google around.
|
||||||
|
|
||||||
Linux users: Please only report issues with supported packages (flatpak and any builds from the README / releases).
|
Linux users: Please only report issues with supported packages (flatpak and any builds from the README / releases).
|
||||||
We do not support other packages, like the AUR or Nix packages, so please first make sure your issue is reproducible with official releases,
|
We do not support other packages, like the AUR or Nix packages, so please first make sure your issue is reproducible with official releases,
|
||||||
|
|
6
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
6
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
|
@ -1,7 +1,7 @@
|
||||||
name: 🛠️ Feature Request
|
name: 🛠️ Feature Request
|
||||||
description: Create a feature request for Vesktop
|
description: Request a feature for Vesktop
|
||||||
labels: [bug]
|
labels: [enhancement]
|
||||||
title: "[Bug] <title>"
|
title: "[Feature Request] <title>"
|
||||||
|
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
|
|
42
.github/workflows/meta.yml
vendored
42
.github/workflows/meta.yml
vendored
|
@ -11,28 +11,28 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
|
- uses: pnpm/action-setup@v4 # Install pnpm using packageManager key in package.json
|
||||||
|
|
||||||
- name: Use Node.js 18.18.2
|
- name: Use Node.js 20
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18.18.2
|
node-version: 20
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm i
|
run: pnpm i
|
||||||
|
|
||||||
- name: Update metainfo
|
- name: Update metainfo
|
||||||
run: pnpm updateMeta
|
run: pnpm updateMeta
|
||||||
|
|
||||||
- name: Commit and merge in changes
|
- name: Commit and merge in changes
|
||||||
run: |
|
run: |
|
||||||
git config user.name "github-actions[bot]"
|
git config user.name "github-actions[bot]"
|
||||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
git checkout -b ci/meta-update
|
git checkout -b ci/meta-update
|
||||||
git add meta/dev.vencord.Vesktop.metainfo.xml
|
git add meta/dev.vencord.Vesktop.metainfo.xml
|
||||||
git commit -m "Insert release changes for ${{ github.event.release.tag_name }}"
|
git commit -m "Insert release changes for ${{ github.event.release.tag_name }}"
|
||||||
git push origin ci/meta-update
|
git push origin ci/meta-update
|
||||||
gh pr create -B main -H ci/meta-update -t "Metainfo for ${{ github.event.release.tag_name }}" -b "This PR updates the metainfo for release ${{ github.event.release.tag_name }}. @lewisakura @Vendicated"
|
gh pr create -B main -H ci/meta-update -t "Metainfo for ${{ github.event.release.tag_name }}" -b "This PR updates the metainfo for release ${{ github.event.release.tag_name }}. @lewisakura @Vendicated"
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
|
@ -22,13 +22,13 @@ jobs:
|
||||||
platform: windows
|
platform: windows
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
|
- uses: pnpm/action-setup@v4 # Install pnpm using packageManager key in package.json
|
||||||
|
|
||||||
- name: Use Node.js 18.18.2
|
- name: Use Node.js 20
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18.18.2
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
@ -43,7 +43,7 @@ jobs:
|
||||||
pnpm electron-builder --${{ matrix.platform }} --publish always
|
pnpm electron-builder --${{ matrix.platform }} --publish always
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Run Electron Builder
|
- name: Run Electron Builder
|
||||||
if: ${{ matrix.platform == 'mac' }}
|
if: ${{ matrix.platform == 'mac' }}
|
||||||
run: |
|
run: |
|
||||||
|
|
10
.github/workflows/test.yml
vendored
10
.github/workflows/test.yml
vendored
|
@ -11,13 +11,13 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
|
- uses: pnpm/action-setup@v4 # Install pnpm using packageManager key in package.json
|
||||||
|
|
||||||
- name: Use Node.js 18.18.2
|
- name: Use Node.js 20
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18.18.2
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|
4
.github/workflows/winget-submission.yml
vendored
4
.github/workflows/winget-submission.yml
vendored
|
@ -13,10 +13,10 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
winget:
|
winget:
|
||||||
name: Publish winget package
|
name: Publish winget package
|
||||||
runs-on: ubuntu-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Submit package to Winget Community Repo
|
- name: Submit package to Winget Community Repo
|
||||||
uses: vedantmgoyal2009/winget-releaser@e68d386d5d6a1cef8cb0fb5e62b77ebcb83e7d58 # v2
|
uses: vedantmgoyal2009/winget-releaser@4614300d5812e5df91cb02ef0edbece623d5dea8
|
||||||
with:
|
with:
|
||||||
identifier: Vencord.Vesktop
|
identifier: Vencord.Vesktop
|
||||||
token: ${{ secrets.WINGET_PAT }}
|
token: ${{ secrets.WINGET_PAT }}
|
||||||
|
|
11
README.md
11
README.md
|
@ -21,15 +21,14 @@ Vesktop is a custom Discord desktop app
|
||||||
|
|
||||||
If you don't know the difference, pick the Installer.
|
If you don't know the difference, pick the Installer.
|
||||||
|
|
||||||
- [Installer](https://vencord.dev/download/vesktop/amd64/windows)
|
- [Installer](https://vencord.dev/download/vesktop/universal/windows)
|
||||||
- [Portable](https://vencord.dev/download/vesktop/amd64/windows-portable)
|
- Portable:
|
||||||
|
- [x64 / amd64](https://vencord.dev/download/vesktop/amd64/windows-portable)
|
||||||
|
- [arm64](https://vencord.dev/download/vesktop/arm64/windows-portable)
|
||||||
|
|
||||||
### Mac
|
### Mac
|
||||||
|
|
||||||
If you don't know the difference, pick the Intel build.
|
[Vesktop.dmg](https://vencord.dev/download/vesktop/universal/dmg)
|
||||||
|
|
||||||
- [Intel build (amd64)](https://vencord.dev/download/vesktop/amd64/dmg)
|
|
||||||
- [Apple Silicon (arm64)](https://vencord.dev/download/vesktop/arm64/dmg)
|
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,34 @@
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="1.5.3" date="2024-07-04" type="stable">
|
||||||
|
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.3</url>
|
||||||
|
<description>
|
||||||
|
<p>Features</p>
|
||||||
|
<ul>
|
||||||
|
<li>added arm64 Windows support</li>
|
||||||
|
<li>windows & macOS builds are now universal</li>
|
||||||
|
<li>added option to configure spellcheck languages</li>
|
||||||
|
<li>will auto-update from now on</li>
|
||||||
|
<li>updated electron to 31 & Chromium to 126</li>
|
||||||
|
<li>macOS: Added customized dmg background by @khcrysalis</li>
|
||||||
|
<li>Windows Portable: store settings in portable folder by @MrGarlic1</li>
|
||||||
|
<li>linux audioshare: added granular selection, more options, better ui by @Curve</li>
|
||||||
|
<li>changed default screen-sharing quality to 720p 30 FPS by @Tiagoquix</li>
|
||||||
|
</ul>
|
||||||
|
<p>Fixes</p>
|
||||||
|
<ul>
|
||||||
|
<li>macOS: Added workaround for making things in draggable area clickable by @HAHALOSAH</li>
|
||||||
|
<li>fixed Screenshare UI for non-linux systems by @PolisanTheEasyNick</li>
|
||||||
|
<li>fixed opening on screen that was disconnected by @MrGarlic1</li>
|
||||||
|
<li>mac: hide native window controls with custom titlebar enabled by @MrGarlic1</li>
|
||||||
|
<li>fixed some broken patches by @D3SOX</li>
|
||||||
|
<li>fixed framerate in constraints by @kittykel</li>
|
||||||
|
<li>fixed some first launch switches not applying</li>
|
||||||
|
<li>fixed potential sandbox escape via custom vencord location</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="1.5.2" date="2024-05-01" type="stable">
|
<release version="1.5.2" date="2024-05-01" type="stable">
|
||||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.2</url>
|
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.2</url>
|
||||||
<description>
|
<description>
|
||||||
|
@ -182,7 +210,7 @@
|
||||||
<url type="vcs-browser">https://github.com/Vencord/Vesktop</url>
|
<url type="vcs-browser">https://github.com/Vencord/Vesktop</url>
|
||||||
<categories>
|
<categories>
|
||||||
<category>InstantMessaging</category>
|
<category>InstantMessaging</category>
|
||||||
<category>AudioVideo</category>
|
<category>Network</category>
|
||||||
</categories>
|
</categories>
|
||||||
<requires>
|
<requires>
|
||||||
<control>pointing</control>
|
<control>pointing</control>
|
||||||
|
|
34
package.json
34
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "vesktop",
|
"name": "vesktop",
|
||||||
"version": "1.5.2",
|
"version": "1.5.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
@ -24,10 +24,11 @@
|
||||||
"updateMeta": "tsx scripts/utils/updateMeta.mts"
|
"updateMeta": "tsx scripts/utils/updateMeta.mts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"arrpc": "github:OpenAsar/arrpc#6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c"
|
"arrpc": "github:OpenAsar/arrpc#c62ec6a04c8d870530aa6944257fe745f6c59a24",
|
||||||
|
"electron-updater": "^6.2.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@vencord/venmic": "^3.5.0"
|
"@vencord/venmic": "^6.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
||||||
|
@ -37,7 +38,7 @@
|
||||||
"@typescript-eslint/parser": "^7.2.0",
|
"@typescript-eslint/parser": "^7.2.0",
|
||||||
"@vencord/types": "^1.8.4",
|
"@vencord/types": "^1.8.4",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"electron": "^29.1.1",
|
"electron": "^31.1.0",
|
||||||
"electron-builder": "^24.13.3",
|
"electron-builder": "^24.13.3",
|
||||||
"esbuild": "^0.20.1",
|
"esbuild": "^0.20.1",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
|
@ -65,6 +66,7 @@
|
||||||
"productName": "Vesktop",
|
"productName": "Vesktop",
|
||||||
"files": [
|
"files": [
|
||||||
"!*",
|
"!*",
|
||||||
|
"!node_modules",
|
||||||
"dist/js",
|
"dist/js",
|
||||||
"static",
|
"static",
|
||||||
"package.json",
|
"package.json",
|
||||||
|
@ -118,8 +120,7 @@
|
||||||
{
|
{
|
||||||
"target": "default",
|
"target": "default",
|
||||||
"arch": [
|
"arch": [
|
||||||
"x64",
|
"universal"
|
||||||
"arm64"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -158,12 +159,29 @@
|
||||||
},
|
},
|
||||||
"win": {
|
"win": {
|
||||||
"target": [
|
"target": [
|
||||||
"nsis",
|
{
|
||||||
"zip"
|
"target": "nsis",
|
||||||
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "zip",
|
||||||
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"publish": {
|
"publish": {
|
||||||
"provider": "github"
|
"provider": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"patchedDependencies": {
|
||||||
|
"arrpc@3.4.0": "patches/arrpc@3.4.0.patch"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
patches/arrpc@3.4.0.patch
Normal file
14
patches/arrpc@3.4.0.patch
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
diff --git a/src/process/index.js b/src/process/index.js
|
||||||
|
index 97ea6514b54dd9c5df588c78f0397d31ab5f882a..c2bdbd6aaa5611bc6ff1d993beeb380b1f5ec575 100644
|
||||||
|
--- a/src/process/index.js
|
||||||
|
+++ b/src/process/index.js
|
||||||
|
@@ -5,8 +5,7 @@ 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');
|
||||||
|
|
||||||
|
import * as Natives from './native/index.js';
|
||||||
|
const Native = Natives[process.platform];
|
|
@ -4,17 +4,25 @@ settings:
|
||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
patchedDependencies:
|
||||||
|
arrpc@3.4.0:
|
||||||
|
hash: biyukfa6dww2wxujy4eyvkhrti
|
||||||
|
path: patches/arrpc@3.4.0.patch
|
||||||
|
|
||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
arrpc:
|
arrpc:
|
||||||
specifier: github:OpenAsar/arrpc#6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c
|
specifier: github:OpenAsar/arrpc#c62ec6a04c8d870530aa6944257fe745f6c59a24
|
||||||
version: https://codeload.github.com/OpenAsar/arrpc/tar.gz/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c
|
version: https://codeload.github.com/OpenAsar/arrpc/tar.gz/c62ec6a04c8d870530aa6944257fe745f6c59a24(patch_hash=biyukfa6dww2wxujy4eyvkhrti)
|
||||||
|
electron-updater:
|
||||||
|
specifier: ^6.2.1
|
||||||
|
version: 6.2.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@vencord/venmic':
|
'@vencord/venmic':
|
||||||
specifier: ^3.5.0
|
specifier: ^6.1.0
|
||||||
version: 3.5.0
|
version: 6.1.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@fal-works/esbuild-plugin-global-externals':
|
'@fal-works/esbuild-plugin-global-externals':
|
||||||
specifier: ^2.1.2
|
specifier: ^2.1.2
|
||||||
|
@ -38,8 +46,8 @@ importers:
|
||||||
specifier: ^16.4.5
|
specifier: ^16.4.5
|
||||||
version: 16.4.5
|
version: 16.4.5
|
||||||
electron:
|
electron:
|
||||||
specifier: ^29.1.1
|
specifier: ^31.1.0
|
||||||
version: 29.1.1
|
version: 31.1.0
|
||||||
electron-builder:
|
electron-builder:
|
||||||
specifier: ^24.13.3
|
specifier: ^24.13.3
|
||||||
version: 24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
version: 24.13.3(electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3))
|
||||||
|
@ -424,6 +432,7 @@ packages:
|
||||||
'@humanwhocodes/config-array@0.11.14':
|
'@humanwhocodes/config-array@0.11.14':
|
||||||
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
|
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
|
deprecated: Use @eslint/config-array instead
|
||||||
|
|
||||||
'@humanwhocodes/module-importer@1.0.1':
|
'@humanwhocodes/module-importer@1.0.1':
|
||||||
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
|
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
|
||||||
|
@ -431,6 +440,7 @@ packages:
|
||||||
|
|
||||||
'@humanwhocodes/object-schema@2.0.2':
|
'@humanwhocodes/object-schema@2.0.2':
|
||||||
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
|
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
|
||||||
|
deprecated: Use @eslint/object-schema instead
|
||||||
|
|
||||||
'@isaacs/cliui@8.0.2':
|
'@isaacs/cliui@8.0.2':
|
||||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||||
|
@ -603,8 +613,8 @@ packages:
|
||||||
'@vencord/types@1.8.4':
|
'@vencord/types@1.8.4':
|
||||||
resolution: {integrity: sha512-ogLqIOHVO+5zxKUVxAfGIAUZoEfIomrlg6f0cZ/2yd5vBAn1fA9Gi/NASoKfHZuJt8ZcYw329bgn0ah/VufqMg==}
|
resolution: {integrity: sha512-ogLqIOHVO+5zxKUVxAfGIAUZoEfIomrlg6f0cZ/2yd5vBAn1fA9Gi/NASoKfHZuJt8ZcYw329bgn0ah/VufqMg==}
|
||||||
|
|
||||||
'@vencord/venmic@3.5.0':
|
'@vencord/venmic@6.1.0':
|
||||||
resolution: {integrity: sha512-kPvrPcIeMkuqQriuiQAJ9rEBeqGR2nmFBuUtbZRGyiNRF9RDAfWSJYqhHVm6F7wbcqrSZio6FazZuBo0LvjJRw==}
|
resolution: {integrity: sha512-YiCtzml/W8tYbGhu3jm5jfbbEnl2slKKARNK0jO+8qV979k9eFnfIRTxvhMN/SWq1h8ZNJdXVwvXpffQwq0RuA==}
|
||||||
engines: {node: '>=14.15'}
|
engines: {node: '>=14.15'}
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
|
||||||
|
@ -727,9 +737,9 @@ packages:
|
||||||
resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
|
resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
arrpc@https://codeload.github.com/OpenAsar/arrpc/tar.gz/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c:
|
arrpc@https://codeload.github.com/OpenAsar/arrpc/tar.gz/c62ec6a04c8d870530aa6944257fe745f6c59a24:
|
||||||
resolution: {tarball: https://codeload.github.com/OpenAsar/arrpc/tar.gz/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c}
|
resolution: {tarball: https://codeload.github.com/OpenAsar/arrpc/tar.gz/c62ec6a04c8d870530aa6944257fe745f6c59a24}
|
||||||
version: 3.3.1
|
version: 3.4.0
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
assert-plus@1.0.0:
|
assert-plus@1.0.0:
|
||||||
|
@ -1080,8 +1090,11 @@ packages:
|
||||||
electron-publish@24.13.1:
|
electron-publish@24.13.1:
|
||||||
resolution: {integrity: sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==}
|
resolution: {integrity: sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==}
|
||||||
|
|
||||||
electron@29.1.1:
|
electron-updater@6.2.1:
|
||||||
resolution: {integrity: sha512-cXN15NgCi7MkzGo5/23ZQbii+0UfhmUiDjACunmzcUofYCjF42XhFbL7JZnwgI0qtBCCeJU8qZNZt9lU91gUFw==}
|
resolution: {integrity: sha512-83eKIPW14qwZqUUM6wdsIRwVKZyjmHxQ4/8G+1C6iS5PdDt7b1umYQyj1/qPpH510GmHEQe4q0kCPe3qmb3a0Q==}
|
||||||
|
|
||||||
|
electron@31.1.0:
|
||||||
|
resolution: {integrity: sha512-TBOwqLxSxnx6+pH6GMri7R3JPH2AkuGJHfWZS0p1HsmN+Qr1T9b0IRJnnehSd/3NZAmAre4ft9Ljec7zjyKFJA==}
|
||||||
engines: {node: '>= 12.20.55'}
|
engines: {node: '>= 12.20.55'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
@ -1441,6 +1454,7 @@ packages:
|
||||||
|
|
||||||
glob@7.2.3:
|
glob@7.2.3:
|
||||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||||
|
deprecated: Glob versions prior to v9 are no longer supported
|
||||||
|
|
||||||
global-agent@3.0.0:
|
global-agent@3.0.0:
|
||||||
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
|
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
|
||||||
|
@ -1561,6 +1575,7 @@ packages:
|
||||||
|
|
||||||
inflight@1.0.6:
|
inflight@1.0.6:
|
||||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||||
|
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||||
|
|
||||||
inherits@2.0.4:
|
inherits@2.0.4:
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||||
|
@ -1789,9 +1804,15 @@ packages:
|
||||||
lodash.difference@4.5.0:
|
lodash.difference@4.5.0:
|
||||||
resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==}
|
resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==}
|
||||||
|
|
||||||
|
lodash.escaperegexp@4.1.2:
|
||||||
|
resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==}
|
||||||
|
|
||||||
lodash.flatten@4.4.0:
|
lodash.flatten@4.4.0:
|
||||||
resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
|
resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
|
||||||
|
|
||||||
|
lodash.isequal@4.5.0:
|
||||||
|
resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
|
||||||
|
|
||||||
lodash.isplainobject@4.0.6:
|
lodash.isplainobject@4.0.6:
|
||||||
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||||
|
|
||||||
|
@ -2155,6 +2176,7 @@ packages:
|
||||||
|
|
||||||
rimraf@3.0.2:
|
rimraf@3.0.2:
|
||||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||||
|
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
roarr@2.15.4:
|
roarr@2.15.4:
|
||||||
|
@ -2371,6 +2393,9 @@ packages:
|
||||||
text-table@0.2.0:
|
text-table@0.2.0:
|
||||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||||
|
|
||||||
|
tiny-typed-emitter@2.1.0:
|
||||||
|
resolution: {integrity: sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==}
|
||||||
|
|
||||||
tmp-promise@3.0.3:
|
tmp-promise@3.0.3:
|
||||||
resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==}
|
resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==}
|
||||||
|
|
||||||
|
@ -3031,7 +3056,7 @@ snapshots:
|
||||||
standalone-electron-types: 1.0.0
|
standalone-electron-types: 1.0.0
|
||||||
type-fest: 3.13.1
|
type-fest: 3.13.1
|
||||||
|
|
||||||
'@vencord/venmic@3.5.0':
|
'@vencord/venmic@6.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
cmake-js: 7.3.0
|
cmake-js: 7.3.0
|
||||||
node-addon-api: 8.0.0
|
node-addon-api: 8.0.0
|
||||||
|
@ -3220,7 +3245,7 @@ snapshots:
|
||||||
is-array-buffer: 3.0.4
|
is-array-buffer: 3.0.4
|
||||||
is-shared-array-buffer: 1.0.3
|
is-shared-array-buffer: 1.0.3
|
||||||
|
|
||||||
arrpc@https://codeload.github.com/OpenAsar/arrpc/tar.gz/6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c:
|
arrpc@https://codeload.github.com/OpenAsar/arrpc/tar.gz/c62ec6a04c8d870530aa6944257fe745f6c59a24(patch_hash=biyukfa6dww2wxujy4eyvkhrti):
|
||||||
dependencies:
|
dependencies:
|
||||||
ws: 8.13.0
|
ws: 8.13.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@ -3651,7 +3676,20 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
electron@29.1.1:
|
electron-updater@6.2.1:
|
||||||
|
dependencies:
|
||||||
|
builder-util-runtime: 9.2.4
|
||||||
|
fs-extra: 10.1.0
|
||||||
|
js-yaml: 4.1.0
|
||||||
|
lazy-val: 1.0.5
|
||||||
|
lodash.escaperegexp: 4.1.2
|
||||||
|
lodash.isequal: 4.5.0
|
||||||
|
semver: 7.6.0
|
||||||
|
tiny-typed-emitter: 2.1.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
electron@31.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@electron/get': 2.0.3
|
'@electron/get': 2.0.3
|
||||||
'@types/node': 20.11.26
|
'@types/node': 20.11.26
|
||||||
|
@ -4512,8 +4550,12 @@ snapshots:
|
||||||
|
|
||||||
lodash.difference@4.5.0: {}
|
lodash.difference@4.5.0: {}
|
||||||
|
|
||||||
|
lodash.escaperegexp@4.1.2: {}
|
||||||
|
|
||||||
lodash.flatten@4.4.0: {}
|
lodash.flatten@4.4.0: {}
|
||||||
|
|
||||||
|
lodash.isequal@4.5.0: {}
|
||||||
|
|
||||||
lodash.isplainobject@4.0.6: {}
|
lodash.isplainobject@4.0.6: {}
|
||||||
|
|
||||||
lodash.merge@4.6.2: {}
|
lodash.merge@4.6.2: {}
|
||||||
|
@ -5125,6 +5167,8 @@ snapshots:
|
||||||
|
|
||||||
text-table@0.2.0: {}
|
text-table@0.2.0: {}
|
||||||
|
|
||||||
|
tiny-typed-emitter@2.1.0: {}
|
||||||
|
|
||||||
tmp-promise@3.0.3:
|
tmp-promise@3.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
tmp: 0.2.3
|
tmp: 0.2.3
|
||||||
|
|
|
@ -63,12 +63,6 @@ await Promise.all([
|
||||||
outfile: "dist/js/preload.js",
|
outfile: "dist/js/preload.js",
|
||||||
footer: { js: "//# sourceURL=VCDPreload" }
|
footer: { js: "//# sourceURL=VCDPreload" }
|
||||||
}),
|
}),
|
||||||
createContext({
|
|
||||||
...NodeCommonOpts,
|
|
||||||
entryPoints: ["src/updater/preload.ts"],
|
|
||||||
outfile: "dist/js/updaterPreload.js",
|
|
||||||
footer: { js: "//# sourceURL=VCDUpdaterPreload" }
|
|
||||||
}),
|
|
||||||
createContext({
|
createContext({
|
||||||
...CommonOpts,
|
...CommonOpts,
|
||||||
globalName: "Vesktop",
|
globalName: "Vesktop",
|
||||||
|
|
|
@ -5,11 +5,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { existsSync, readdirSync, renameSync, rmdirSync } from "fs";
|
import { existsSync, mkdirSync, readdirSync, renameSync, rmdirSync } from "fs";
|
||||||
import { join } from "path";
|
import { dirname, join } from "path";
|
||||||
|
|
||||||
|
const vesktopDir = dirname(process.execPath);
|
||||||
|
|
||||||
|
export const PORTABLE =
|
||||||
|
process.platform === "win32" &&
|
||||||
|
!process.execPath.toLowerCase().endsWith("electron.exe") &&
|
||||||
|
!existsSync(join(vesktopDir, "Uninstall Vesktop.exe"));
|
||||||
|
|
||||||
const LEGACY_DATA_DIR = join(app.getPath("appData"), "VencordDesktop", "VencordDesktop");
|
const LEGACY_DATA_DIR = join(app.getPath("appData"), "VencordDesktop", "VencordDesktop");
|
||||||
export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("userData"));
|
export const DATA_DIR =
|
||||||
|
process.env.VENCORD_USER_DATA_DIR || (PORTABLE ? join(vesktopDir, "Data") : join(app.getPath("userData")));
|
||||||
|
|
||||||
|
mkdirSync(DATA_DIR, { recursive: true });
|
||||||
|
|
||||||
// TODO: remove eventually
|
// TODO: remove eventually
|
||||||
if (existsSync(LEGACY_DATA_DIR)) {
|
if (existsSync(LEGACY_DATA_DIR)) {
|
||||||
try {
|
try {
|
||||||
|
@ -26,7 +37,8 @@ if (existsSync(LEGACY_DATA_DIR)) {
|
||||||
console.error("Migration failed", e);
|
console.error("Migration failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.setPath("sessionData", join(DATA_DIR, "sessionData"));
|
const SESSION_DATA_DIR = join(DATA_DIR, "sessionData");
|
||||||
|
app.setPath("sessionData", SESSION_DATA_DIR);
|
||||||
|
|
||||||
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
||||||
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
|
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
|
||||||
|
@ -36,7 +48,8 @@ export const VENCORD_THEMES_DIR = join(DATA_DIR, "themes");
|
||||||
// needs to be inline require because of circular dependency
|
// needs to be inline require because of circular dependency
|
||||||
// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised
|
// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised
|
||||||
export const VENCORD_FILES_DIR =
|
export const VENCORD_FILES_DIR =
|
||||||
(require("./settings") as typeof import("./settings")).Settings.store.vencordDir || join(DATA_DIR, "vencordDist");
|
(require("./settings") as typeof import("./settings")).State.store.vencordDir ||
|
||||||
|
join(SESSION_DATA_DIR, "vencordFiles");
|
||||||
|
|
||||||
export const USER_AGENT = `Vesktop/${app.getVersion()} (https://github.com/Vencord/Vesktop)`;
|
export const USER_AGENT = `Vesktop/${app.getVersion()} (https://github.com/Vencord/Vesktop)`;
|
||||||
|
|
||||||
|
@ -49,10 +62,10 @@ export const DEFAULT_HEIGHT = 720;
|
||||||
export const DISCORD_HOSTNAMES = ["discord.com", "canary.discord.com", "ptb.discord.com"];
|
export const DISCORD_HOSTNAMES = ["discord.com", "canary.discord.com", "ptb.discord.com"];
|
||||||
|
|
||||||
const BrowserUserAgents = {
|
const BrowserUserAgents = {
|
||||||
darwin: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
darwin: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
|
||||||
linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
|
||||||
windows:
|
windows:
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BrowserUserAgent = BrowserUserAgents[process.platform] || BrowserUserAgents.windows;
|
export const BrowserUserAgent = BrowserUserAgents[process.platform] || BrowserUserAgents.windows;
|
||||||
|
|
|
@ -18,11 +18,11 @@ import { Settings, State } from "./settings";
|
||||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||||
|
|
||||||
interface Data {
|
interface Data {
|
||||||
minimizeToTray: boolean;
|
|
||||||
discordBranch: "stable" | "canary" | "ptb";
|
discordBranch: "stable" | "canary" | "ptb";
|
||||||
autoStart: boolean;
|
minimizeToTray?: "on";
|
||||||
importSettings: boolean;
|
autoStart?: "on";
|
||||||
richPresence: boolean;
|
importSettings?: "on";
|
||||||
|
richPresence?: "on";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createFirstLaunchTour() {
|
export function createFirstLaunchTour() {
|
||||||
|
@ -44,10 +44,11 @@ export function createFirstLaunchTour() {
|
||||||
if (!msg.startsWith("form:")) return;
|
if (!msg.startsWith("form:")) return;
|
||||||
const data = JSON.parse(msg.slice(5)) as Data;
|
const data = JSON.parse(msg.slice(5)) as Data;
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
State.store.firstLaunch = false;
|
State.store.firstLaunch = false;
|
||||||
Settings.store.minimizeToTray = data.minimizeToTray;
|
|
||||||
Settings.store.discordBranch = data.discordBranch;
|
Settings.store.discordBranch = data.discordBranch;
|
||||||
Settings.store.arRPC = data.richPresence;
|
Settings.store.minimizeToTray = !!data.minimizeToTray;
|
||||||
|
Settings.store.arRPC = !!data.richPresence;
|
||||||
|
|
||||||
if (data.autoStart) autoStart.enable();
|
if (data.autoStart) autoStart.enable();
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import "./ipc";
|
import "./ipc";
|
||||||
|
|
||||||
import { app, BrowserWindow, nativeTheme } from "electron";
|
import { app, BrowserWindow, nativeTheme } from "electron";
|
||||||
import { checkUpdates } from "updater/main";
|
import { autoUpdater } from "electron-updater";
|
||||||
|
|
||||||
import { DATA_DIR } from "./constants";
|
import { DATA_DIR } from "./constants";
|
||||||
import { createFirstLaunchTour } from "./firstLaunch";
|
import { createFirstLaunchTour } from "./firstLaunch";
|
||||||
|
@ -19,6 +19,8 @@ import { isDeckGameMode } from "./utils/steamOS";
|
||||||
|
|
||||||
if (IS_DEV) {
|
if (IS_DEV) {
|
||||||
require("source-map-support").install();
|
require("source-map-support").install();
|
||||||
|
} else {
|
||||||
|
autoUpdater.checkForUpdatesAndNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the Vencord files use our DATA_DIR
|
// Make the Vencord files use our DATA_DIR
|
||||||
|
@ -42,7 +44,13 @@ function init() {
|
||||||
|
|
||||||
// disable renderer backgrounding to prevent the app from unloading when in the background
|
// disable renderer backgrounding to prevent the app from unloading when in the background
|
||||||
// https://github.com/electron/electron/issues/2822
|
// 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-renderer-backgrounding");
|
||||||
|
app.commandLine.appendSwitch("disable-background-timer-throttling");
|
||||||
|
app.commandLine.appendSwitch("disable-backgrounding-occluded-windows");
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
disabledFeatures.push("CalculateNativeWinOcclusion");
|
||||||
|
}
|
||||||
|
|
||||||
// work around chrome 66 disabling autoplay by default
|
// work around chrome 66 disabling autoplay by default
|
||||||
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
|
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
|
||||||
|
@ -50,12 +58,7 @@ function init() {
|
||||||
// HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service.
|
// HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service.
|
||||||
//
|
//
|
||||||
// WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790
|
// WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790
|
||||||
disabledFeatures.push(
|
disabledFeatures.push("WinRetrieveSuggestionsOnlyOnDemand", "HardwareMediaKeyHandling", "MediaSessionService");
|
||||||
"WinRetrieveSuggestionsOnlyOnDemand",
|
|
||||||
"HardwareMediaKeyHandling",
|
|
||||||
"MediaSessionService",
|
|
||||||
"WidgetLayering"
|
|
||||||
);
|
|
||||||
|
|
||||||
app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].filter(Boolean).join(","));
|
app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].filter(Boolean).join(","));
|
||||||
app.commandLine.appendSwitch("disable-features", [...new Set(disabledFeatures)].filter(Boolean).join(","));
|
app.commandLine.appendSwitch("disable-features", [...new Set(disabledFeatures)].filter(Boolean).join(","));
|
||||||
|
@ -73,7 +76,6 @@ function init() {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
checkUpdates();
|
|
||||||
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.vesktop");
|
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.vesktop");
|
||||||
|
|
||||||
registerScreenShareHandler();
|
registerScreenShareHandler();
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { setBadgeCount } from "./appBadge";
|
||||||
import { autoStart } from "./autoStart";
|
import { autoStart } from "./autoStart";
|
||||||
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
|
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
|
||||||
import { mainWin } from "./mainWindow";
|
import { mainWin } from "./mainWindow";
|
||||||
import { Settings } from "./settings";
|
import { Settings, State } from "./settings";
|
||||||
import { handle, handleSync } from "./utils/ipcWrappers";
|
import { handle, handleSync } from "./utils/ipcWrappers";
|
||||||
import { PopoutWindows } from "./utils/popout";
|
import { PopoutWindows } from "./utils/popout";
|
||||||
import { isDeckGameMode, showGamePage } from "./utils/steamOS";
|
import { isDeckGameMode, showGamePage } from "./utils/steamOS";
|
||||||
|
@ -109,7 +109,14 @@ handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => {
|
||||||
e.sender.session.addWordToSpellCheckerDictionary(word);
|
e.sender.session.addWordToSpellCheckerDictionary(word);
|
||||||
});
|
});
|
||||||
|
|
||||||
handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
handleSync(IpcEvents.GET_VENCORD_DIR, e => (e.returnValue = State.store.vencordDir));
|
||||||
|
|
||||||
|
handle(IpcEvents.SELECT_VENCORD_DIR, async (_e, value?: null) => {
|
||||||
|
if (value === null) {
|
||||||
|
delete State.store.vencordDir;
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
|
||||||
const res = await dialog.showOpenDialog(mainWin!, {
|
const res = await dialog.showOpenDialog(mainWin!, {
|
||||||
properties: ["openDirectory"]
|
properties: ["openDirectory"]
|
||||||
});
|
});
|
||||||
|
@ -118,7 +125,9 @@ handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
||||||
const dir = res.filePaths[0];
|
const dir = res.filePaths[0];
|
||||||
if (!isValidVencordInstall(dir)) return "invalid";
|
if (!isValidVencordInstall(dir)) return "invalid";
|
||||||
|
|
||||||
return dir;
|
State.store.vencordDir = dir;
|
||||||
|
|
||||||
|
return "ok";
|
||||||
});
|
});
|
||||||
|
|
||||||
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||||
|
|
|
@ -92,7 +92,7 @@ function initTray(win: BrowserWindow) {
|
||||||
click: createAboutWindow
|
click: createAboutWindow
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Update Vencord",
|
label: "Repair Vencord",
|
||||||
async click() {
|
async click() {
|
||||||
await downloadVencordFiles();
|
await downloadVencordFiles();
|
||||||
app.relaunch();
|
app.relaunch();
|
||||||
|
@ -109,14 +109,14 @@ function initTray(win: BrowserWindow) {
|
||||||
type: "separator"
|
type: "separator"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Relaunch",
|
label: "Restart",
|
||||||
click() {
|
click() {
|
||||||
app.relaunch();
|
app.relaunch();
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Quit Vesktop",
|
label: "Quit",
|
||||||
click() {
|
click() {
|
||||||
isQuitting = true;
|
isQuitting = true;
|
||||||
app.quit();
|
app.quit();
|
||||||
|
@ -404,7 +404,9 @@ function createMainWindow() {
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
devTools: true,
|
devTools: true,
|
||||||
preload: join(__dirname, "preload.js"),
|
preload: join(__dirname, "preload.js"),
|
||||||
spellcheck: true
|
spellcheck: true,
|
||||||
|
// disable renderer backgrounding to prevent the app from unloading when in the background
|
||||||
|
backgroundThrottling: false
|
||||||
},
|
},
|
||||||
icon: ICON_PATH,
|
icon: ICON_PATH,
|
||||||
frame: !noFrame,
|
frame: !noFrame,
|
||||||
|
@ -429,6 +431,7 @@ function createMainWindow() {
|
||||||
autoHideMenuBar: enableMenu
|
autoHideMenuBar: enableMenu
|
||||||
}));
|
}));
|
||||||
win.setMenuBarVisibility(false);
|
win.setMenuBarVisibility(false);
|
||||||
|
if (process.platform === "darwin" && customTitleBar) win.setWindowButtonVisibility(false);
|
||||||
|
|
||||||
win.on("close", e => {
|
win.on("close", e => {
|
||||||
const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false;
|
const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false;
|
||||||
|
|
|
@ -12,11 +12,13 @@ export function registerMediaPermissionsHandler() {
|
||||||
session.defaultSession.setPermissionRequestHandler(async (_webContents, permission, callback, details) => {
|
session.defaultSession.setPermissionRequestHandler(async (_webContents, permission, callback, details) => {
|
||||||
let granted = true;
|
let granted = true;
|
||||||
|
|
||||||
if (details.mediaTypes?.includes("audio")) {
|
if ("mediaTypes" in details) {
|
||||||
granted = await systemPreferences.askForMediaAccess("microphone");
|
if (details.mediaTypes?.includes("audio")) {
|
||||||
}
|
granted &&= await systemPreferences.askForMediaAccess("microphone");
|
||||||
if (details.mediaTypes?.includes("video")) {
|
}
|
||||||
granted &&= await systemPreferences.askForMediaAccess("camera");
|
if (details.mediaTypes?.includes("video")) {
|
||||||
|
granted &&= await systemPreferences.askForMediaAccess("camera");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(granted);
|
callback(granted);
|
||||||
|
|
|
@ -41,14 +41,7 @@ export const VencordSettings = loadSettings<any>(VENCORD_SETTINGS_FILE, "Vencord
|
||||||
if (Object.hasOwn(Settings.plain, "firstLaunch") && !existsSync(STATE_FILE)) {
|
if (Object.hasOwn(Settings.plain, "firstLaunch") && !existsSync(STATE_FILE)) {
|
||||||
console.warn("legacy state in settings.json detected. migrating to state.json");
|
console.warn("legacy state in settings.json detected. migrating to state.json");
|
||||||
const state = {} as TState;
|
const state = {} as TState;
|
||||||
for (const prop of [
|
for (const prop of ["firstLaunch", "maximized", "minimized", "steamOSLayoutVersion", "windowBounds"] as const) {
|
||||||
"firstLaunch",
|
|
||||||
"maximized",
|
|
||||||
"minimized",
|
|
||||||
"skippedUpdate",
|
|
||||||
"steamOSLayoutVersion",
|
|
||||||
"windowBounds"
|
|
||||||
] as const) {
|
|
||||||
state[prop] = Settings.plain[prop];
|
state[prop] = Settings.plain[prop];
|
||||||
delete Settings.plain[prop];
|
delete Settings.plain[prop];
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { existsSync, mkdirSync } from "fs";
|
import { mkdirSync } from "fs";
|
||||||
|
import { access, constants as FsConstants } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
import { USER_AGENT, VENCORD_FILES_DIR } from "../constants";
|
import { USER_AGENT, VENCORD_FILES_DIR } from "../constants";
|
||||||
|
@ -56,12 +57,18 @@ export async function downloadVencordFiles() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidVencordInstall(dir: string) {
|
const existsAsync = (path: string) =>
|
||||||
return FILES_TO_DOWNLOAD.every(f => existsSync(join(dir, f)));
|
access(path, FsConstants.F_OK)
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false);
|
||||||
|
|
||||||
|
export async function isValidVencordInstall(dir: string) {
|
||||||
|
return Promise.all(FILES_TO_DOWNLOAD.map(f => existsAsync(join(dir, f)))).then(arr => !arr.includes(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function ensureVencordFiles() {
|
export async function ensureVencordFiles() {
|
||||||
if (isValidVencordInstall(VENCORD_FILES_DIR)) return;
|
if (await isValidVencordInstall(VENCORD_FILES_DIR)) return;
|
||||||
|
|
||||||
mkdirSync(VENCORD_FILES_DIR, { recursive: true });
|
mkdirSync(VENCORD_FILES_DIR, { recursive: true });
|
||||||
|
|
||||||
await downloadVencordFiles();
|
await downloadVencordFiles();
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { PatchBay as PatchBayType } from "@vencord/venmic";
|
import type { LinkData, Node, PatchBay as PatchBayType } from "@vencord/venmic";
|
||||||
import { app, ipcMain } from "electron";
|
import { app, ipcMain } from "electron";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { IpcEvents } from "shared/IpcEvents";
|
import { IpcEvents } from "shared/IpcEvents";
|
||||||
import { STATIC_DIR } from "shared/paths";
|
import { STATIC_DIR } from "shared/paths";
|
||||||
|
|
||||||
type LinkData = Parameters<PatchBayType["link"]>[0];
|
import { Settings } from "./settings";
|
||||||
|
|
||||||
let PatchBay: typeof PatchBayType | undefined;
|
let PatchBay: typeof PatchBayType | undefined;
|
||||||
let patchBayInstance: PatchBayType | undefined;
|
let patchBayInstance: PatchBayType | undefined;
|
||||||
|
@ -69,47 +69,64 @@ function getRendererAudioServicePid() {
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
|
||||||
const audioPid = getRendererAudioServicePid();
|
const audioPid = getRendererAudioServicePid();
|
||||||
|
|
||||||
const list = obtainVenmic()
|
const { granularSelect } = Settings.store.audio ?? {};
|
||||||
?.list()
|
|
||||||
.filter(s => s["application.process.id"] !== audioPid)
|
|
||||||
.map(s => s["application.name"]);
|
|
||||||
|
|
||||||
const uniqueTargets = [...new Set(list)];
|
const targets = obtainVenmic()
|
||||||
|
?.list(granularSelect ? ["application.process.id"] : undefined)
|
||||||
|
.filter(s => s["application.process.id"] !== audioPid);
|
||||||
|
|
||||||
return list ? { ok: true, targets: uniqueTargets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
|
return targets ? { ok: true, targets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boolean) => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, include: Node[]) => {
|
||||||
const pid = getRendererAudioServicePid();
|
const pid = getRendererAudioServicePid();
|
||||||
|
const { ignoreDevices, ignoreInputMedia, ignoreVirtual, workaround } = Settings.store.audio ?? {};
|
||||||
|
|
||||||
const data: LinkData = {
|
const data: LinkData = {
|
||||||
include: targets.map(target => ({ key: "application.name", value: target })),
|
include,
|
||||||
exclude: [{ key: "application.process.id", value: pid }]
|
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) {
|
if (workaround) {
|
||||||
data.workaround = [
|
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
|
||||||
{ key: "application.process.id", value: pid },
|
|
||||||
{ key: "media.name", value: "RecordStream" }
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return obtainVenmic()?.link(data);
|
return obtainVenmic()?.link(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean, onlyDefaultSpeakers?: boolean) => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, exclude: Node[]) => {
|
||||||
const pid = getRendererAudioServicePid();
|
const pid = getRendererAudioServicePid();
|
||||||
|
|
||||||
|
const { workaround, ignoreDevices, ignoreInputMedia, ignoreVirtual, onlySpeakers, onlyDefaultSpeakers } =
|
||||||
|
Settings.store.audio ?? {};
|
||||||
|
|
||||||
const data: LinkData = {
|
const data: LinkData = {
|
||||||
exclude: [{ key: "application.process.id", value: pid }],
|
include: [],
|
||||||
|
exclude: [{ "application.process.id": pid }, ...exclude],
|
||||||
|
only_speakers: onlySpeakers,
|
||||||
|
ignore_devices: ignoreDevices,
|
||||||
only_default_speakers: onlyDefaultSpeakers
|
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) {
|
if (workaround) {
|
||||||
data.workaround = [
|
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
|
||||||
{ key: "application.process.id", value: pid },
|
|
||||||
{ key: "media.name", value: "RecordStream" }
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return obtainVenmic()?.link(data);
|
return obtainVenmic()?.link(data);
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Node } from "@vencord/venmic";
|
||||||
import { ipcRenderer } from "electron";
|
import { ipcRenderer } from "electron";
|
||||||
import type { Settings } from "shared/settings";
|
import type { Settings } from "shared/settings";
|
||||||
import type { LiteralUnion } from "type-fest";
|
|
||||||
|
|
||||||
import { IpcEvents } from "../shared/IpcEvents";
|
import { IpcEvents } from "../shared/IpcEvents";
|
||||||
import { invoke, sendSync } from "./typedIpc";
|
import { invoke, sendSync } from "./typedIpc";
|
||||||
|
@ -33,7 +33,8 @@ export const VesktopNative = {
|
||||||
},
|
},
|
||||||
fileManager: {
|
fileManager: {
|
||||||
showItemInFolder: (path: string) => invoke<void>(IpcEvents.SHOW_ITEM_IN_FOLDER, path),
|
showItemInFolder: (path: string) => invoke<void>(IpcEvents.SHOW_ITEM_IN_FOLDER, path),
|
||||||
selectVencordDir: () => invoke<LiteralUnion<"cancelled" | "invalid", string>>(IpcEvents.SELECT_VENCORD_DIR)
|
getVencordDir: () => sendSync<string | undefined>(IpcEvents.GET_VENCORD_DIR),
|
||||||
|
selectVencordDir: (value?: null) => invoke<"cancelled" | "invalid" | "ok">(IpcEvents.SELECT_VENCORD_DIR, value)
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),
|
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),
|
||||||
|
@ -64,11 +65,10 @@ export const VesktopNative = {
|
||||||
virtmic: {
|
virtmic: {
|
||||||
list: () =>
|
list: () =>
|
||||||
invoke<
|
invoke<
|
||||||
{ ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: string[]; hasPipewirePulse: boolean }
|
{ ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: Node[]; hasPipewirePulse: boolean }
|
||||||
>(IpcEvents.VIRT_MIC_LIST),
|
>(IpcEvents.VIRT_MIC_LIST),
|
||||||
start: (targets: string[], workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START, targets, workaround),
|
start: (include: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START, include),
|
||||||
startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) =>
|
startSystem: (exclude: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, exclude),
|
||||||
invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers),
|
|
||||||
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
|
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
|
||||||
},
|
},
|
||||||
arrpc: {
|
arrpc: {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import "./screenSharePicker.css";
|
import "./screenSharePicker.css";
|
||||||
|
|
||||||
import { closeModal, Logger, Margins, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils";
|
import { closeModal, Logger, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils";
|
||||||
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
|
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
@ -19,8 +19,10 @@ import {
|
||||||
UserStore,
|
UserStore,
|
||||||
useState
|
useState
|
||||||
} from "@vencord/types/webpack/common";
|
} from "@vencord/types/webpack/common";
|
||||||
|
import { Node } from "@vencord/venmic";
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
import type { Dispatch, SetStateAction } from "react";
|
||||||
import { addPatch } from "renderer/patches/shared";
|
import { addPatch } from "renderer/patches/shared";
|
||||||
|
import { useSettings } from "renderer/settings";
|
||||||
import { isLinux, isWindows } from "renderer/utils";
|
import { isLinux, isWindows } from "renderer/utils";
|
||||||
|
|
||||||
const StreamResolutions = ["480", "720", "1080", "1440"] as const;
|
const StreamResolutions = ["480", "720", "1080", "1440"] as const;
|
||||||
|
@ -31,14 +33,23 @@ const MediaEngineStore = findStoreLazy("MediaEngineStore");
|
||||||
export type StreamResolution = (typeof StreamResolutions)[number];
|
export type StreamResolution = (typeof StreamResolutions)[number];
|
||||||
export type StreamFps = (typeof StreamFps)[number];
|
export type StreamFps = (typeof StreamFps)[number];
|
||||||
|
|
||||||
|
type SpecialSource = "None" | "Entire System";
|
||||||
|
|
||||||
|
type AudioSource = SpecialSource | Node;
|
||||||
|
type AudioSources = SpecialSource | Node[];
|
||||||
|
|
||||||
|
interface AudioItem {
|
||||||
|
name: string;
|
||||||
|
value: AudioSource;
|
||||||
|
}
|
||||||
|
|
||||||
interface StreamSettings {
|
interface StreamSettings {
|
||||||
resolution: StreamResolution;
|
resolution: StreamResolution;
|
||||||
fps: StreamFps;
|
fps: StreamFps;
|
||||||
audio: boolean;
|
audio: boolean;
|
||||||
audioSource?: string;
|
|
||||||
contentHint?: string;
|
contentHint?: string;
|
||||||
workaround?: boolean;
|
includeSources?: AudioSources;
|
||||||
onlyDefaultSpeakers?: boolean;
|
excludeSources?: AudioSources;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamPick extends StreamSettings {
|
export interface StreamPick extends StreamSettings {
|
||||||
|
@ -118,13 +129,17 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) {
|
||||||
modalProps={props}
|
modalProps={props}
|
||||||
submit={async v => {
|
submit={async v => {
|
||||||
didSubmit = true;
|
didSubmit = true;
|
||||||
if (v.audioSource && v.audioSource !== "None") {
|
|
||||||
if (v.audioSource === "Entire System") {
|
if (v.includeSources && v.includeSources !== "None") {
|
||||||
await VesktopNative.virtmic.startSystem(v.workaround);
|
if (v.includeSources === "Entire System") {
|
||||||
|
await VesktopNative.virtmic.startSystem(
|
||||||
|
!v.excludeSources || isSpecialSource(v.excludeSources) ? [] : v.excludeSources
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await VesktopNative.virtmic.start([v.audioSource], v.workaround);
|
await VesktopNative.virtmic.start(v.includeSources);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(v);
|
resolve(v);
|
||||||
}}
|
}}
|
||||||
close={() => {
|
close={() => {
|
||||||
|
@ -159,6 +174,113 @@ function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScre
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AudioSettingsModal({
|
||||||
|
modalProps,
|
||||||
|
close,
|
||||||
|
setAudioSources
|
||||||
|
}: {
|
||||||
|
modalProps: any;
|
||||||
|
close: () => void;
|
||||||
|
setAudioSources: (s: AudioSources) => void;
|
||||||
|
}) {
|
||||||
|
const Settings = useSettings();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||||
|
<Modals.ModalHeader className="vcd-screen-picker-header">
|
||||||
|
<Forms.FormTitle tag="h2">Venmic Settings</Forms.FormTitle>
|
||||||
|
<Modals.ModalCloseButton onClick={close} />
|
||||||
|
</Modals.ModalHeader>
|
||||||
|
<Modals.ModalContent className="vcd-screen-picker-modal">
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={v => (Settings.audio = { ...Settings.audio, workaround: v })}
|
||||||
|
value={Settings.audio?.workaround ?? false}
|
||||||
|
note={
|
||||||
|
<>
|
||||||
|
Work around an issue that causes the microphone to be shared instead of the correct audio.
|
||||||
|
Only enable if you're experiencing this issue.
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Microphone Workaround
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={v => (Settings.audio = { ...Settings.audio, onlySpeakers: v })}
|
||||||
|
value={Settings.audio?.onlySpeakers ?? true}
|
||||||
|
note={
|
||||||
|
<>
|
||||||
|
When sharing entire desktop audio, only share apps that play to a speaker. You may want to
|
||||||
|
disable this when using "mix bussing".
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Only Speakers
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={v => (Settings.audio = { ...Settings.audio, onlyDefaultSpeakers: v })}
|
||||||
|
value={Settings.audio?.onlyDefaultSpeakers ?? true}
|
||||||
|
note={
|
||||||
|
<>
|
||||||
|
When sharing entire desktop audio, only share apps that play to the <b>default</b> speakers.
|
||||||
|
You may want to disable this when using "mix bussing".
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Only Default Speakers
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={v => (Settings.audio = { ...Settings.audio, ignoreInputMedia: v })}
|
||||||
|
value={Settings.audio?.ignoreInputMedia ?? true}
|
||||||
|
note={<>Exclude nodes that are intended to capture audio.</>}
|
||||||
|
>
|
||||||
|
Ignore Inputs
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={v => (Settings.audio = { ...Settings.audio, ignoreVirtual: v })}
|
||||||
|
value={Settings.audio?.ignoreVirtual ?? false}
|
||||||
|
note={
|
||||||
|
<>
|
||||||
|
Exclude virtual nodes, such as nodes belonging to loopbacks. This might be useful when using
|
||||||
|
"mix bussing".
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Ignore Virtual
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={v => (Settings.audio = { ...Settings.audio, ignoreDevices: v })}
|
||||||
|
value={Settings.audio?.ignoreDevices ?? true}
|
||||||
|
note={<>Exclude device nodes, such as nodes belonging to microphones or speakers.</>}
|
||||||
|
>
|
||||||
|
Ignore Devices
|
||||||
|
</Switch>
|
||||||
|
<Switch
|
||||||
|
hideBorder
|
||||||
|
onChange={value => {
|
||||||
|
Settings.audio = { ...Settings.audio, granularSelect: value };
|
||||||
|
setAudioSources("None");
|
||||||
|
}}
|
||||||
|
value={Settings.audio?.granularSelect ?? false}
|
||||||
|
note={<>Allow to select applications more granularly.</>}
|
||||||
|
>
|
||||||
|
Granular Selection
|
||||||
|
</Switch>
|
||||||
|
</Modals.ModalContent>
|
||||||
|
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
||||||
|
<Button color={Button.Colors.TRANSPARENT} onClick={close}>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
</Modals.ModalFooter>
|
||||||
|
</Modals.ModalRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function StreamSettings({
|
function StreamSettings({
|
||||||
source,
|
source,
|
||||||
settings,
|
settings,
|
||||||
|
@ -170,6 +292,8 @@ function StreamSettings({
|
||||||
setSettings: Dispatch<SetStateAction<StreamSettings>>;
|
setSettings: Dispatch<SetStateAction<StreamSettings>>;
|
||||||
skipPicker: boolean;
|
skipPicker: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
const Settings = useSettings();
|
||||||
|
|
||||||
const [thumb] = useAwaiter(
|
const [thumb] = useAwaiter(
|
||||||
() => (skipPicker ? Promise.resolve(source.url) : VesktopNative.capturer.getLargeThumbnail(source.id)),
|
() => (skipPicker ? Promise.resolve(source.url) : VesktopNative.capturer.getLargeThumbnail(source.id)),
|
||||||
{
|
{
|
||||||
|
@ -178,230 +302,346 @@ function StreamSettings({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const openSettings = () => {
|
||||||
|
const key = openModal(props => (
|
||||||
|
<AudioSettingsModal
|
||||||
|
modalProps={props}
|
||||||
|
close={() => props.onClose()}
|
||||||
|
setAudioSources={sources =>
|
||||||
|
setSettings(s => ({ ...s, includeSources: sources, excludeSources: sources }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={isLinux ? "vcd-screen-picker-settings-grid" : ""}>
|
<div>
|
||||||
<div>
|
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
||||||
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
<Card className="vcd-screen-picker-card vcd-screen-picker-preview">
|
||||||
<Card className="vcd-screen-picker-card vcd-screen-picker-preview">
|
<img
|
||||||
<img
|
src={thumb}
|
||||||
src={thumb}
|
alt=""
|
||||||
alt=""
|
className={isLinux ? "vcd-screen-picker-preview-img-linux" : "vcd-screen-picker-preview-img"}
|
||||||
className={isLinux ? "vcd-screen-picker-preview-img-linux" : "vcd-screen-picker-preview-img"}
|
/>
|
||||||
/>
|
<Text variant="text-sm/normal">{source.name}</Text>
|
||||||
<Text variant="text-sm/normal">{source.name}</Text>
|
</Card>
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
|
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
|
||||||
|
|
||||||
<Card className="vcd-screen-picker-card">
|
<Card className="vcd-screen-picker-card">
|
||||||
<div className="vcd-screen-picker-quality">
|
<div className="vcd-screen-picker-quality">
|
||||||
<section>
|
<section>
|
||||||
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
||||||
|
<div className="vcd-screen-picker-radios">
|
||||||
|
{StreamResolutions.map(res => (
|
||||||
|
<label className="vcd-screen-picker-radio" data-checked={settings.resolution === res}>
|
||||||
|
<Text variant="text-sm/bold">{res}</Text>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="resolution"
|
||||||
|
value={res}
|
||||||
|
checked={settings.resolution === res}
|
||||||
|
onChange={() => setSettings(s => ({ ...s, resolution: res }))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
||||||
|
<div className="vcd-screen-picker-radios">
|
||||||
|
{StreamFps.map(fps => (
|
||||||
|
<label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}>
|
||||||
|
<Text variant="text-sm/bold">{fps}</Text>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="fps"
|
||||||
|
value={fps}
|
||||||
|
checked={settings.fps === fps}
|
||||||
|
onChange={() => setSettings(s => ({ ...s, fps }))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div className="vcd-screen-picker-quality">
|
||||||
|
<section>
|
||||||
|
<Forms.FormTitle>Content Type</Forms.FormTitle>
|
||||||
|
<div>
|
||||||
<div className="vcd-screen-picker-radios">
|
<div className="vcd-screen-picker-radios">
|
||||||
{StreamResolutions.map(res => (
|
<label
|
||||||
<label
|
className="vcd-screen-picker-radio"
|
||||||
className="vcd-screen-picker-radio"
|
data-checked={settings.contentHint === "motion"}
|
||||||
data-checked={settings.resolution === res}
|
|
||||||
>
|
|
||||||
<Text variant="text-sm/bold">{res}</Text>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="resolution"
|
|
||||||
value={res}
|
|
||||||
checked={settings.resolution === res}
|
|
||||||
onChange={() => setSettings(s => ({ ...s, resolution: res }))}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
|
||||||
<div className="vcd-screen-picker-radios">
|
|
||||||
{StreamFps.map(fps => (
|
|
||||||
<label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}>
|
|
||||||
<Text variant="text-sm/bold">{fps}</Text>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="fps"
|
|
||||||
value={fps}
|
|
||||||
checked={settings.fps === fps}
|
|
||||||
onChange={() => setSettings(s => ({ ...s, fps }))}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div className="vcd-screen-picker-quality">
|
|
||||||
<section>
|
|
||||||
<Forms.FormTitle>Content Type</Forms.FormTitle>
|
|
||||||
<div>
|
|
||||||
<div className="vcd-screen-picker-radios">
|
|
||||||
<label
|
|
||||||
className="vcd-screen-picker-radio"
|
|
||||||
data-checked={settings.contentHint === "motion"}
|
|
||||||
>
|
|
||||||
<Text variant="text-sm/bold">Prefer Smoothness</Text>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="contenthint"
|
|
||||||
value="motion"
|
|
||||||
checked={settings.contentHint === "motion"}
|
|
||||||
onChange={() => setSettings(s => ({ ...s, contentHint: "motion" }))}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label
|
|
||||||
className="vcd-screen-picker-radio"
|
|
||||||
data-checked={settings.contentHint === "detail"}
|
|
||||||
>
|
|
||||||
<Text variant="text-sm/bold">Prefer Clarity</Text>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="contenthint"
|
|
||||||
value="detail"
|
|
||||||
checked={settings.contentHint === "detail"}
|
|
||||||
onChange={() => setSettings(s => ({ ...s, contentHint: "detail" }))}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="vcd-screen-picker-hint-description">
|
|
||||||
<p>
|
|
||||||
Choosing "Prefer Clarity" will result in a significantly lower framerate in
|
|
||||||
exchange for a much sharper and clearer image.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isWindows && (
|
|
||||||
<Switch
|
|
||||||
value={settings.audio}
|
|
||||||
onChange={checked => setSettings(s => ({ ...s, audio: checked }))}
|
|
||||||
hideBorder
|
|
||||||
className="vcd-screen-picker-audio"
|
|
||||||
>
|
>
|
||||||
Stream With Audio
|
<Text variant="text-sm/bold">Prefer Smoothness</Text>
|
||||||
</Switch>
|
<input
|
||||||
)}
|
type="radio"
|
||||||
</section>
|
name="contenthint"
|
||||||
</div>
|
value="motion"
|
||||||
</Card>
|
checked={settings.contentHint === "motion"}
|
||||||
</div>
|
onChange={() => setSettings(s => ({ ...s, contentHint: "motion" }))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
className="vcd-screen-picker-radio"
|
||||||
|
data-checked={settings.contentHint === "detail"}
|
||||||
|
>
|
||||||
|
<Text variant="text-sm/bold">Prefer Clarity</Text>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="contenthint"
|
||||||
|
value="detail"
|
||||||
|
checked={settings.contentHint === "detail"}
|
||||||
|
onChange={() => setSettings(s => ({ ...s, contentHint: "detail" }))}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="vcd-screen-picker-hint-description">
|
||||||
|
<p>
|
||||||
|
Choosing "Prefer Clarity" will result in a significantly lower framerate in exchange
|
||||||
|
for a much sharper and clearer image.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{isWindows && (
|
||||||
|
<Switch
|
||||||
|
value={settings.audio}
|
||||||
|
onChange={checked => setSettings(s => ({ ...s, audio: checked }))}
|
||||||
|
hideBorder
|
||||||
|
className="vcd-screen-picker-audio"
|
||||||
|
>
|
||||||
|
Stream With Audio
|
||||||
|
</Switch>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
{isLinux && (
|
{isLinux && (
|
||||||
<AudioSourcePickerLinux
|
<AudioSourcePickerLinux
|
||||||
audioSource={settings.audioSource}
|
openSettings={openSettings}
|
||||||
workaround={settings.workaround}
|
includeSources={settings.includeSources}
|
||||||
onlyDefaultSpeakers={settings.onlyDefaultSpeakers}
|
excludeSources={settings.excludeSources}
|
||||||
setAudioSource={source => setSettings(s => ({ ...s, audioSource: source }))}
|
granularSelect={Settings.audio?.granularSelect}
|
||||||
setWorkaround={value => setSettings(s => ({ ...s, workaround: value }))}
|
setIncludeSources={sources => setSettings(s => ({ ...s, includeSources: sources }))}
|
||||||
setOnlyDefaultSpeakers={value => setSettings(s => ({ ...s, onlyDefaultSpeakers: value }))}
|
setExcludeSources={sources => setSettings(s => ({ ...s, excludeSources: sources }))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isSpecialSource(value?: AudioSource | AudioSources): value is SpecialSource {
|
||||||
|
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): AudioItem[] {
|
||||||
|
if (isSpecialSource(node)) {
|
||||||
|
return [{ name: node, value: node }];
|
||||||
|
}
|
||||||
|
|
||||||
|
const rtn: AudioItem[] = [];
|
||||||
|
|
||||||
|
const name = node["application.name"];
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
rtn.push({ name: name, value: { "application.name": name } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!granularSelect) {
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
const binary = node["application.process.binary"];
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
rtn.push({ name: binary, value: { "application.process.binary": binary } });
|
||||||
|
}
|
||||||
|
|
||||||
|
const pid = node["application.process.id"];
|
||||||
|
|
||||||
|
const first = rtn[0];
|
||||||
|
const firstValues = first.value as Node;
|
||||||
|
|
||||||
|
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 }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const mediaClass = node["media.class"];
|
||||||
|
|
||||||
|
if (!mediaClass) {
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtn.push({
|
||||||
|
name: `${first.name} [${mediaClass}]`,
|
||||||
|
value: { ...firstValues, "media.class": mediaClass }
|
||||||
|
});
|
||||||
|
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isItemSelected(sources?: AudioSources) {
|
||||||
|
return (value: AudioSource) => {
|
||||||
|
if (!sources) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSpecialSource(sources) || isSpecialSource(value)) {
|
||||||
|
return sources === value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sources.some(source => hasMatchingProps(source, value));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateItems(setSources: (s: AudioSources) => void, sources?: AudioSources) {
|
||||||
|
return (value: AudioSource) => {
|
||||||
|
if (isSpecialSource(value)) {
|
||||||
|
setSources(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSpecialSource(sources)) {
|
||||||
|
setSources([value]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isItemSelected(sources)(value)) {
|
||||||
|
setSources(sources?.filter(x => !hasMatchingProps(x, value)) ?? "None");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSources([...(sources || []), value]);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function AudioSourcePickerLinux({
|
function AudioSourcePickerLinux({
|
||||||
audioSource,
|
includeSources,
|
||||||
workaround,
|
excludeSources,
|
||||||
onlyDefaultSpeakers,
|
granularSelect,
|
||||||
setAudioSource,
|
openSettings,
|
||||||
setWorkaround,
|
setIncludeSources,
|
||||||
setOnlyDefaultSpeakers
|
setExcludeSources
|
||||||
}: {
|
}: {
|
||||||
audioSource?: string;
|
includeSources?: AudioSources;
|
||||||
workaround?: boolean;
|
excludeSources?: AudioSources;
|
||||||
onlyDefaultSpeakers?: boolean;
|
granularSelect?: boolean;
|
||||||
setAudioSource(s: string): void;
|
openSettings: () => void;
|
||||||
setWorkaround(b: boolean): void;
|
setIncludeSources: (s: AudioSources) => void;
|
||||||
setOnlyDefaultSpeakers(b: boolean): void;
|
setExcludeSources: (s: AudioSources) => void;
|
||||||
}) {
|
}) {
|
||||||
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
|
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
|
||||||
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
|
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null;
|
|
||||||
const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true;
|
const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true;
|
||||||
|
|
||||||
const [ignorePulseWarning, setIgnorePulseWarning] = useState(false);
|
const [ignorePulseWarning, setIgnorePulseWarning] = useState(false);
|
||||||
|
|
||||||
|
if (!sources.ok && sources.isGlibCxxOutdated) {
|
||||||
|
return (
|
||||||
|
<Forms.FormText>
|
||||||
|
Failed to retrieve Audio Sources because your C++ library is too old to run
|
||||||
|
<a href="https://github.com/Vencord/venmic" target="_blank">
|
||||||
|
venmic
|
||||||
|
</a>
|
||||||
|
. See{" "}
|
||||||
|
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
||||||
|
this guide
|
||||||
|
</a>{" "}
|
||||||
|
for possible solutions.
|
||||||
|
</Forms.FormText>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPipewirePulse && !ignorePulseWarning) {
|
||||||
|
return (
|
||||||
|
<Text variant="text-sm/normal">
|
||||||
|
Could not find pipewire-pulse. See{" "}
|
||||||
|
<a href="https://gist.github.com/the-spyke/2de98b22ff4f978ebf0650c90e82027e#install" target="_blank">
|
||||||
|
this guide
|
||||||
|
</a>{" "}
|
||||||
|
on how to switch to pipewire. <br />
|
||||||
|
You can still continue, however, please{" "}
|
||||||
|
<b>beware that you can only share audio of apps that are running under pipewire</b>.{" "}
|
||||||
|
<a onClick={() => setIgnorePulseWarning(true)}>I know what I'm doing!</a>
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const specialSources: SpecialSource[] = ["None", "Entire System"] as const;
|
||||||
|
|
||||||
|
const uniqueName = (value: AudioItem, index: number, list: AudioItem[]) =>
|
||||||
|
list.findIndex(x => x.name === value.name) === index;
|
||||||
|
|
||||||
|
const allSources = sources.ok
|
||||||
|
? [...specialSources, ...sources.targets]
|
||||||
|
.map(target => mapToAudioItem(target, granularSelect))
|
||||||
|
.flat()
|
||||||
|
.filter(uniqueName)
|
||||||
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Forms.FormTitle>Audio Settings</Forms.FormTitle>
|
<div className={includeSources === "Entire System" ? "vcd-screen-picker-quality" : undefined}>
|
||||||
<Card className="vcd-screen-picker-card">
|
<section>
|
||||||
{loading ? (
|
<Forms.FormTitle>{loading ? "Loading Sources..." : "Audio Sources"}</Forms.FormTitle>
|
||||||
<Forms.FormTitle>Loading Audio Sources...</Forms.FormTitle>
|
<Select
|
||||||
) : (
|
options={allSources.map(({ name, value }) => ({
|
||||||
<Forms.FormTitle>Audio Source</Forms.FormTitle>
|
label: name,
|
||||||
)}
|
value: value,
|
||||||
|
default: name === "None"
|
||||||
{!sources.ok && sources.isGlibCxxOutdated && (
|
}))}
|
||||||
<Forms.FormText>
|
isSelected={isItemSelected(includeSources)}
|
||||||
Failed to retrieve Audio Sources because your C++ library is too old to run
|
select={updateItems(setIncludeSources, includeSources)}
|
||||||
<a href="https://github.com/Vencord/venmic" target="_blank">
|
serialize={String}
|
||||||
venmic
|
popoutPosition="top"
|
||||||
</a>
|
closeOnSelect={false}
|
||||||
. See{" "}
|
/>
|
||||||
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
</section>
|
||||||
this guide
|
{includeSources === "Entire System" && (
|
||||||
</a>{" "}
|
<section>
|
||||||
for possible solutions.
|
<Forms.FormTitle>Exclude Sources</Forms.FormTitle>
|
||||||
</Forms.FormText>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{hasPipewirePulse || ignorePulseWarning ? (
|
|
||||||
allSources && (
|
|
||||||
<Select
|
<Select
|
||||||
options={allSources.map(s => ({ label: s, value: s, default: s === "None" }))}
|
options={allSources
|
||||||
isSelected={s => s === audioSource}
|
.filter(x => x.name !== "Entire System")
|
||||||
select={setAudioSource}
|
.map(({ name, value }) => ({
|
||||||
|
label: name,
|
||||||
|
value: value,
|
||||||
|
default: name === "None"
|
||||||
|
}))}
|
||||||
|
isSelected={isItemSelected(excludeSources)}
|
||||||
|
select={updateItems(setExcludeSources, excludeSources)}
|
||||||
serialize={String}
|
serialize={String}
|
||||||
|
popoutPosition="top"
|
||||||
|
closeOnSelect={false}
|
||||||
/>
|
/>
|
||||||
)
|
</section>
|
||||||
) : (
|
|
||||||
<Text variant="text-sm/normal">
|
|
||||||
Could not find pipewire-pulse. This usually means that you do not run pipewire as your main
|
|
||||||
audio-server. <br />
|
|
||||||
You can still continue, however, please beware that you can only share audio of apps that are
|
|
||||||
running under pipewire.
|
|
||||||
<br />
|
|
||||||
<a onClick={() => setIgnorePulseWarning(true)}>I know what I'm doing</a>
|
|
||||||
</Text>
|
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
<Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} />
|
<Button
|
||||||
|
color={Button.Colors.TRANSPARENT}
|
||||||
<Switch
|
onClick={openSettings}
|
||||||
onChange={setWorkaround}
|
className="vcd-screen-picker-settings-button"
|
||||||
value={workaround ?? false}
|
>
|
||||||
note={
|
Open Audio Settings
|
||||||
<>
|
</Button>
|
||||||
Work around an issue that causes the microphone to be shared instead of the correct audio.
|
|
||||||
Only enable if you're experiencing this issue.
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Microphone Workaround
|
|
||||||
</Switch>
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
hideBorder
|
|
||||||
onChange={setOnlyDefaultSpeakers}
|
|
||||||
disabled={audioSource !== "Entire System"}
|
|
||||||
value={onlyDefaultSpeakers ?? true}
|
|
||||||
note={
|
|
||||||
<>
|
|
||||||
When sharing entire desktop audio, only share apps that play to the default speakers and
|
|
||||||
ignore apps that play to other speakers or devices.
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Only Default Speakers
|
|
||||||
</Switch>
|
|
||||||
</Card>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -421,10 +661,11 @@ function ModalComponent({
|
||||||
}) {
|
}) {
|
||||||
const [selected, setSelected] = useState<string | undefined>(skipPicker ? screens[0].id : void 0);
|
const [selected, setSelected] = useState<string | undefined>(skipPicker ? screens[0].id : void 0);
|
||||||
const [settings, setSettings] = useState<StreamSettings>({
|
const [settings, setSettings] = useState<StreamSettings>({
|
||||||
resolution: "1080",
|
resolution: "720",
|
||||||
fps: "60",
|
fps: "30",
|
||||||
contentHint: "motion",
|
contentHint: "motion",
|
||||||
audio: true
|
audio: true,
|
||||||
|
includeSources: "None"
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -480,7 +721,7 @@ function ModalComponent({
|
||||||
|
|
||||||
const constraints = {
|
const constraints = {
|
||||||
...track.getConstraints(),
|
...track.getConstraints(),
|
||||||
frameRate,
|
frameRate: { min: frameRate, ideal: frameRate },
|
||||||
width: { min: 640, ideal: width, max: width },
|
width: { min: 640, ideal: width, max: width },
|
||||||
height: { min: 480, ideal: height, max: height },
|
height: { min: 480, ideal: height, max: height },
|
||||||
advanced: [{ width: width, height: height }],
|
advanced: [{ width: width, height: height }],
|
||||||
|
|
|
@ -11,17 +11,6 @@
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-settings-grid {
|
|
||||||
gap: 1em;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vcd-screen-picker-settings-grid>div {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vcd-screen-picker-card {
|
.vcd-screen-picker-card {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +27,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-selected img {
|
.vcd-screen-picker-selected img {
|
||||||
border: 2px solid var(--brand-experiment);
|
border: 2px solid var(--brand-500);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +38,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-grid label:hover {
|
.vcd-screen-picker-grid label:hover {
|
||||||
outline: 2px solid var(--brand-experiment);
|
outline: 2px solid var(--brand-500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,7 +57,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-preview-img-linux {
|
.vcd-screen-picker-preview-img-linux {
|
||||||
width: 100%;
|
width: 60%;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,8 +90,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-radio[data-checked="true"] {
|
.vcd-screen-picker-radio[data-checked="true"] {
|
||||||
background-color: var(--brand-experiment);
|
background-color: var(--brand-500);
|
||||||
border-color: var(--brand-experiment);
|
border-color: var(--brand-500);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-radio[data-checked="true"] h2 {
|
.vcd-screen-picker-radio[data-checked="true"] h2 {
|
||||||
|
@ -120,6 +109,11 @@
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vcd-screen-picker-settings-button {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-radios {
|
.vcd-screen-picker-radios {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -148,4 +142,4 @@
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,15 +104,7 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
|
||||||
defaultValue: false
|
defaultValue: false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Notifications & Updates": [
|
Notifications: [NotificationBadgeToggle],
|
||||||
NotificationBadgeToggle,
|
|
||||||
{
|
|
||||||
key: "checkUpdates",
|
|
||||||
title: "Check for updates",
|
|
||||||
description: "Automatically check for Vesktop updates",
|
|
||||||
defaultValue: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
Miscelleanous: [
|
Miscelleanous: [
|
||||||
{
|
{
|
||||||
key: "arRPC",
|
key: "arRPC",
|
||||||
|
|
|
@ -4,24 +4,28 @@
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { useForceUpdater } from "@vencord/types/utils";
|
||||||
import { Button, Forms, Toasts } from "@vencord/types/webpack/common";
|
import { Button, Forms, Toasts } from "@vencord/types/webpack/common";
|
||||||
|
|
||||||
import { SettingsComponent } from "./Settings";
|
import { SettingsComponent } from "./Settings";
|
||||||
|
|
||||||
export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
||||||
|
const forceUpdate = useForceUpdater();
|
||||||
|
const vencordDir = VesktopNative.fileManager.getVencordDir();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
Vencord files are loaded from{" "}
|
Vencord files are loaded from{" "}
|
||||||
{settings.vencordDir ? (
|
{vencordDir ? (
|
||||||
<a
|
<a
|
||||||
href="about:blank"
|
href="about:blank"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
VesktopNative.fileManager.showItemInFolder(settings.vencordDir!);
|
VesktopNative.fileManager.showItemInFolder(vencordDir!);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{settings.vencordDir}
|
{vencordDir}
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
"the default location"
|
"the default location"
|
||||||
|
@ -34,7 +38,14 @@ export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
||||||
const choice = await VesktopNative.fileManager.selectVencordDir();
|
const choice = await VesktopNative.fileManager.selectVencordDir();
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case "cancelled":
|
case "cancelled":
|
||||||
return;
|
break;
|
||||||
|
case "ok":
|
||||||
|
Toasts.show({
|
||||||
|
message: "Vencord install changed. Fully restart Vesktop to apply.",
|
||||||
|
id: Toasts.genId(),
|
||||||
|
type: Toasts.Type.SUCCESS
|
||||||
|
});
|
||||||
|
break;
|
||||||
case "invalid":
|
case "invalid":
|
||||||
Toasts.show({
|
Toasts.show({
|
||||||
message:
|
message:
|
||||||
|
@ -42,9 +53,9 @@ export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
||||||
id: Toasts.genId(),
|
id: Toasts.genId(),
|
||||||
type: Toasts.Type.FAILURE
|
type: Toasts.Type.FAILURE
|
||||||
});
|
});
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
settings.vencordDir = choice;
|
forceUpdate();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Change
|
Change
|
||||||
|
@ -52,7 +63,10 @@ export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
|
||||||
<Button
|
<Button
|
||||||
size={Button.Sizes.SMALL}
|
size={Button.Sizes.SMALL}
|
||||||
color={Button.Colors.RED}
|
color={Button.Colors.RED}
|
||||||
onClick={() => (settings.vencordDir = void 0)}
|
onClick={async () => {
|
||||||
|
await VesktopNative.fileManager.selectVencordDir(null);
|
||||||
|
forceUpdate();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -8,4 +8,9 @@
|
||||||
* {
|
* {
|
||||||
scrollbar-width: unset !important;
|
scrollbar-width: unset !important;
|
||||||
scrollbar-color: unset !important;
|
scrollbar-color: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Workaround for making things in the draggable area clickable again on macOS */
|
||||||
|
.platform-osx [class*=topic_], .platform-osx [class*=notice_] button {
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ console.log("read if cute :3");
|
||||||
|
|
||||||
export * as Components from "./components";
|
export * as Components from "./components";
|
||||||
import { findByPropsLazy, onceReady } from "@vencord/types/webpack";
|
import { findByPropsLazy, onceReady } from "@vencord/types/webpack";
|
||||||
import { FluxDispatcher } from "@vencord/types/webpack/common";
|
import { Alerts, FluxDispatcher } from "@vencord/types/webpack/common";
|
||||||
|
|
||||||
import SettingsUi from "./components/settings/Settings";
|
import SettingsUi from "./components/settings/Settings";
|
||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
|
@ -59,3 +59,19 @@ VesktopNative.arrpc.onActivity(async data => {
|
||||||
|
|
||||||
arRPC.handleEvent(new MessageEvent("message", { data }));
|
arRPC.handleEvent(new MessageEvent("message", { data }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: remove soon
|
||||||
|
const vencordDir = "vencordDir" as keyof typeof Settings.store;
|
||||||
|
if (Settings.store[vencordDir]) {
|
||||||
|
onceReady.then(() =>
|
||||||
|
setTimeout(
|
||||||
|
() =>
|
||||||
|
Alerts.show({
|
||||||
|
title: "Custom Vencord Location",
|
||||||
|
body: "Due to security hardening changes in Vesktop, your custom Vencord location had to be reset. Please configure it again in the settings.",
|
||||||
|
onConfirm: () => delete Settings.store[vencordDir]
|
||||||
|
}),
|
||||||
|
5000
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ addPatch({
|
||||||
replacement: {
|
replacement: {
|
||||||
// FIXME: fix eslint rule
|
// FIXME: fix eslint rule
|
||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
match: /\.isPlatformEmbedded(?=\?\i\.DesktopNotificationTypes\.ALL)/g,
|
match: /\.isPlatformEmbedded(?=\?\i\.\i\.ALL)/g,
|
||||||
replace: "$&||true"
|
replace: "$&||true"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ addPatch({
|
||||||
find: "lastOutputSystemDevice.justChanged",
|
find: "lastOutputSystemDevice.justChanged",
|
||||||
replacement: {
|
replacement: {
|
||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
match: /(\i)\.default\.getState\(\).neverShowModal/,
|
match: /(\i)\.\i\.getState\(\).neverShowModal/,
|
||||||
replace: "$& || $self.shouldIgnore($1)"
|
replace: "$& || $self.shouldIgnore($1)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ if (isLinux) {
|
||||||
|
|
||||||
const constraints = {
|
const constraints = {
|
||||||
...track.getConstraints(),
|
...track.getConstraints(),
|
||||||
frameRate,
|
frameRate: { min: frameRate, ideal: frameRate },
|
||||||
width: { min: 640, ideal: width, max: width },
|
width: { min: 640, ideal: width, max: width },
|
||||||
height: { min: 480, ideal: height, max: height },
|
height: { min: 480, ideal: height, max: height },
|
||||||
advanced: [{ width: width, height: height }],
|
advanced: [{ width: width, height: height }],
|
||||||
|
|
|
@ -24,6 +24,7 @@ export const enum IpcEvents {
|
||||||
GET_SETTINGS = "VCD_GET_SETTINGS",
|
GET_SETTINGS = "VCD_GET_SETTINGS",
|
||||||
SET_SETTINGS = "VCD_SET_SETTINGS",
|
SET_SETTINGS = "VCD_SET_SETTINGS",
|
||||||
|
|
||||||
|
GET_VENCORD_DIR = "VCD_GET_VENCORD_DIR",
|
||||||
SELECT_VENCORD_DIR = "VCD_SELECT_VENCORD_DIR",
|
SELECT_VENCORD_DIR = "VCD_SELECT_VENCORD_DIR",
|
||||||
|
|
||||||
UPDATER_GET_DATA = "VCD_UPDATER_GET_DATA",
|
UPDATER_GET_DATA = "VCD_UPDATER_GET_DATA",
|
||||||
|
|
18
src/shared/settings.d.ts
vendored
18
src/shared/settings.d.ts
vendored
|
@ -8,7 +8,6 @@ import type { Rectangle } from "electron";
|
||||||
|
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
discordBranch?: "stable" | "canary" | "ptb";
|
discordBranch?: "stable" | "canary" | "ptb";
|
||||||
vencordDir?: string;
|
|
||||||
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
|
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
|
||||||
tray?: boolean;
|
tray?: boolean;
|
||||||
minimizeToTray?: boolean;
|
minimizeToTray?: boolean;
|
||||||
|
@ -24,13 +23,23 @@ export interface Settings {
|
||||||
clickTrayToShowHide?: boolean;
|
clickTrayToShowHide?: boolean;
|
||||||
customTitleBar?: boolean;
|
customTitleBar?: boolean;
|
||||||
|
|
||||||
checkUpdates?: boolean;
|
|
||||||
|
|
||||||
splashTheming?: boolean;
|
splashTheming?: boolean;
|
||||||
splashColor?: string;
|
splashColor?: string;
|
||||||
splashBackground?: string;
|
splashBackground?: string;
|
||||||
|
|
||||||
spellCheckLanguages?: string[];
|
spellCheckLanguages?: string[];
|
||||||
|
|
||||||
|
audio?: {
|
||||||
|
workaround?: boolean;
|
||||||
|
granularSelect?: boolean;
|
||||||
|
|
||||||
|
ignoreVirtual?: boolean;
|
||||||
|
ignoreDevices?: boolean;
|
||||||
|
ignoreInputMedia?: boolean;
|
||||||
|
|
||||||
|
onlySpeakers?: boolean;
|
||||||
|
onlyDefaultSpeakers?: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
|
@ -39,8 +48,9 @@ export interface State {
|
||||||
windowBounds?: Rectangle;
|
windowBounds?: Rectangle;
|
||||||
displayid: int;
|
displayid: int;
|
||||||
|
|
||||||
skippedUpdate?: string;
|
|
||||||
firstLaunch?: boolean;
|
firstLaunch?: boolean;
|
||||||
|
|
||||||
steamOSLayoutVersion?: number;
|
steamOSLayoutVersion?: number;
|
||||||
|
|
||||||
|
vencordDir?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,19 @@ export class SettingsStore<T extends object> {
|
||||||
self.pathListeners.get(setPath)?.forEach(cb => cb(value));
|
self.pathListeners.get(setPath)?.forEach(cb => cb(value));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
},
|
||||||
|
deleteProperty(target, key: string) {
|
||||||
|
if (!(key in target)) return true;
|
||||||
|
|
||||||
|
const res = Reflect.deleteProperty(target, key);
|
||||||
|
if (!res) return false;
|
||||||
|
|
||||||
|
const setPath = `${path}${path && "."}${key}`;
|
||||||
|
|
||||||
|
self.globalListeners.forEach(cb => cb(root, setPath));
|
||||||
|
self.pathListeners.get(setPath)?.forEach(cb => cb(undefined));
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-License-Identifier: GPL-3.0
|
|
||||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { app, BrowserWindow, shell } from "electron";
|
|
||||||
import { Settings, State } from "main/settings";
|
|
||||||
import { handle } from "main/utils/ipcWrappers";
|
|
||||||
import { makeLinksOpenExternally } from "main/utils/makeLinksOpenExternally";
|
|
||||||
import { githubGet, ReleaseData } from "main/utils/vencordLoader";
|
|
||||||
import { join } from "path";
|
|
||||||
import { IpcEvents } from "shared/IpcEvents";
|
|
||||||
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
|
||||||
|
|
||||||
export interface UpdateData {
|
|
||||||
currentVersion: string;
|
|
||||||
latestVersion: string;
|
|
||||||
release: ReleaseData;
|
|
||||||
}
|
|
||||||
|
|
||||||
let updateData: UpdateData;
|
|
||||||
|
|
||||||
handle(IpcEvents.UPDATER_GET_DATA, () => updateData);
|
|
||||||
handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
|
||||||
const portable = !!process.env.PORTABLE_EXECUTABLE_FILE;
|
|
||||||
|
|
||||||
const { assets } = updateData.release;
|
|
||||||
const url = (() => {
|
|
||||||
switch (process.platform) {
|
|
||||||
case "win32":
|
|
||||||
return assets.find(a => {
|
|
||||||
if (!a.name.endsWith(".exe")) return false;
|
|
||||||
|
|
||||||
const isSetup = a.name.includes("Setup");
|
|
||||||
return portable ? !isSetup : isSetup;
|
|
||||||
})!.browser_download_url;
|
|
||||||
case "darwin":
|
|
||||||
return assets.find(a =>
|
|
||||||
process.arch === "arm64"
|
|
||||||
? a.name.endsWith("-arm64-mac.zip")
|
|
||||||
: a.name.endsWith("-mac.zip") && !a.name.includes("arm64")
|
|
||||||
)!.browser_download_url;
|
|
||||||
case "linux":
|
|
||||||
return updateData.release.html_url;
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
shell.openExternal(url);
|
|
||||||
});
|
|
||||||
|
|
||||||
handle(IpcEvents.UPDATE_IGNORE, () => {
|
|
||||||
State.store.skippedUpdate = updateData.latestVersion;
|
|
||||||
});
|
|
||||||
|
|
||||||
function isOutdated(oldVersion: string, newVersion: string) {
|
|
||||||
const oldParts = oldVersion.split(".");
|
|
||||||
const newParts = newVersion.split(".");
|
|
||||||
|
|
||||||
if (oldParts.length !== newParts.length)
|
|
||||||
throw new Error(`Incompatible version strings (old: ${oldVersion}, new: ${newVersion})`);
|
|
||||||
|
|
||||||
for (let i = 0; i < oldParts.length; i++) {
|
|
||||||
const oldPart = Number(oldParts[i]);
|
|
||||||
const newPart = Number(newParts[i]);
|
|
||||||
|
|
||||||
if (isNaN(oldPart) || isNaN(newPart))
|
|
||||||
throw new Error(`Invalid version string (old: ${oldVersion}, new: ${newVersion})`);
|
|
||||||
|
|
||||||
if (oldPart < newPart) return true;
|
|
||||||
if (oldPart > newPart) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function checkUpdates() {
|
|
||||||
if (Settings.store.checkUpdates === false) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const raw = await githubGet("/repos/Vencord/Vesktop/releases/latest");
|
|
||||||
const data: ReleaseData = await raw.json();
|
|
||||||
|
|
||||||
const oldVersion = app.getVersion();
|
|
||||||
const newVersion = data.tag_name.replace(/^v/, "");
|
|
||||||
updateData = {
|
|
||||||
currentVersion: oldVersion,
|
|
||||||
latestVersion: newVersion,
|
|
||||||
release: data
|
|
||||||
};
|
|
||||||
|
|
||||||
if (State.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) {
|
|
||||||
openNewUpdateWindow();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("AppUpdater: Failed to check for updates\n", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openNewUpdateWindow() {
|
|
||||||
const win = new BrowserWindow({
|
|
||||||
width: 500,
|
|
||||||
autoHideMenuBar: true,
|
|
||||||
alwaysOnTop: true,
|
|
||||||
webPreferences: {
|
|
||||||
preload: join(__dirname, "updaterPreload.js"),
|
|
||||||
nodeIntegration: false,
|
|
||||||
contextIsolation: true,
|
|
||||||
sandbox: true
|
|
||||||
},
|
|
||||||
icon: ICON_PATH
|
|
||||||
});
|
|
||||||
|
|
||||||
makeLinksOpenExternally(win);
|
|
||||||
|
|
||||||
win.loadFile(join(VIEW_DIR, "updater.html"));
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-License-Identifier: GPL-3.0
|
|
||||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { contextBridge } from "electron";
|
|
||||||
import { invoke } from "preload/typedIpc";
|
|
||||||
import { IpcEvents } from "shared/IpcEvents";
|
|
||||||
|
|
||||||
import type { UpdateData } from "./main";
|
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("Updater", {
|
|
||||||
getData: () => invoke<UpdateData>(IpcEvents.UPDATER_GET_DATA),
|
|
||||||
download: () => {
|
|
||||||
invoke<void>(IpcEvents.UPDATER_DOWNLOAD);
|
|
||||||
invoke<void>(IpcEvents.CLOSE);
|
|
||||||
},
|
|
||||||
ignore: () => invoke<void>(IpcEvents.UPDATE_IGNORE),
|
|
||||||
close: () => invoke<void>(IpcEvents.CLOSE)
|
|
||||||
});
|
|
Loading…
Reference in a new issue