164 Commits

Author SHA1 Message Date
Vendicated
4a2d12f273 fix lockfile 2025-07-08 01:08:39 +02:00
Vendicated
3498e39cbb bump to v1.5.8 2025-07-08 01:02:17 +02:00
Vendicated
3278b16923 Remove GTK mixed symbols workaround
This bug is fixed in electron 37, so the workaround is no longer needed
2025-07-06 19:16:07 +02:00
Vendicated
fde447bc1d improve about window 2025-07-06 18:57:57 +02:00
Vendicated
4baf6de472 arrpc: fix link handling 2025-07-06 17:54:58 +02:00
Justin
8aa91b4f01 Move arrpc server into a worker thread to reduce stutters (#1053)
Co-authored-by: V <vendicated@riseup.net>
2025-07-06 17:34:32 +02:00
Vendicated
099845deb7 bump electron again surely this time they fixed every bug 2025-07-06 16:27:04 +02:00
Vendicated
3982e122a7 upgrade to electron 37 2025-06-27 23:08:50 +02:00
Cookie
8d3c9390ae update discord css variable name (#1164) 2025-06-27 03:09:33 +02:00
Vendicated
236fc806de fake markdown wtf 2025-06-26 15:09:35 +02:00
Vendicated
2dcebeca79 revise issue template once again 2025-06-26 15:08:28 +02:00
Vendicated
e6589eacfc fix lockfile 2025-06-21 04:12:21 +02:00
Vendicated
29d1c73d81 okay pnpm 2025-06-21 04:10:45 +02:00
Vendicated
26906b9776 patch electron-updater to fix rpm updater 2025-06-21 04:06:13 +02:00
Vendicated
36c67aa54a bump dependencies 2025-06-21 04:06:00 +02:00
Cookie
3cd4e94762 fix spellcheck (#1163) 2025-06-20 19:29:13 +02:00
Vendicated
6950e0b03a add comment explaining the patch 2025-06-10 17:22:30 +02:00
Vendicated
f98309c7b7 fix random logouts (Discord shitcode moment) 2025-06-10 17:16:26 +02:00
Vendicated
a2dade9140 i am very stupid 2025-06-09 01:36:45 +02:00
Vendicated
83ad4970e5 fix Video HWA wrongly being disabled if HWA setting was never changed 2025-06-09 01:36:15 +02:00
Vending Machine
f123f5fc3c Update dev.vencord.Vesktop.metainfo.xml 2025-06-08 22:07:07 +02:00
Vending Machine
0a856b26da Update meta.yml 2025-06-08 22:04:59 +02:00
Vendicated
dad306a4b5 bump to v1.5.7 2025-06-08 21:47:06 +02:00
Vendicated
a52ee1dbb6 bump dependencies 2025-06-08 21:45:17 +02:00
Vendicated
524f8ff277 ipcCommands: check if mainWin is alive 2025-06-08 21:39:56 +02:00
Vendicated
83104e3625 fix discord titlebar buttons missing 2025-06-08 21:34:31 +02:00
Vendicated
fcb61c8f42 improve handling of no hardware acceleration 2025-06-08 21:21:57 +02:00
Vendicated
48e9aea47e add hardware video acceleration switch & improve settings ui 2025-06-08 20:56:28 +02:00
Vendicated
6e7d912b95 make linux screenshare audio stereo 2025-05-20 01:45:24 +02:00
Vendicated
475012cbed bump electron patch 2025-05-16 22:30:11 +02:00
Vendicated
382dac96f7 fix return value 2025-05-16 21:01:19 +02:00
Vendicated
3295a7d344 migrate executeJavaScript calls to new command system 2025-05-16 21:00:32 +02:00
Vendicated
bb3cec0d13 Fix Invalid URL error 2025-05-16 20:48:34 +02:00
Vendicated
1a673f2318 build: automatically glob all patch files 2025-05-16 20:43:27 +02:00
Vendicated
d9a7e81f71 switch to node inbuilt source map support 2025-05-16 19:59:29 +02:00
Vendicated
73848311b1 oh cookie what are u doing 2025-05-16 19:52:16 +02:00
Vendicated
857779e77b remove legacy code 2025-05-16 19:47:10 +02:00
William
fa627b384f add StartupWMClass - fixes AppImage icon & pinning (#1158)
Co-authored-by: Vending Machine <vendicated@riseup.net>
2025-05-16 19:38:02 +02:00
Cookie
43a8781492 Link Discord connections through the browser instead of a popup (#1159)
Co-authored-by: Vending Machine <vendicated@riseup.net>
2025-05-16 19:34:48 +02:00
Vendicated
b17fef93ba make Discord's auto gain control toggle actually work 2025-05-16 03:37:32 +02:00
Vendicated
fc16fc5404 Fix not launching on GNOME
Reference: https://github.com/electron/electron/issues/46538
Also improves the chromium switch code
2025-05-14 05:26:29 +02:00
Vendicated
e760e58ed7 upgrade to electron 36 2025-05-14 05:26:29 +02:00
Cookie
3936a0a41e fix: handle loadURL() failures correctly (#1093) 2025-04-22 22:03:17 +02:00
Glitchtest
c7d830c57c Revert loopbackWithMute to loopback (#1155)
apparently loopbackWithMute does not mute your own app like initially assumed but rather mutes the system audio for the user
2025-04-17 23:23:42 +02:00
Vendicated
7bf05bd907 add back window menu (minimise, close, etc) on mac 2025-04-17 14:39:19 +02:00
Vending Machine
ae20445301 manually update metainfo 2025-04-13 14:49:49 +02:00
Vending Machine
33d1ac43e3 fix meta.yml 2025-04-13 14:47:32 +02:00
Vending Machine
da51ebb0a7 fix(ci): strip v prefix from version in vencord.dev updater 2025-04-13 14:44:27 +02:00
Vendicated
d0399cbde4 bump to v1.5.6 2025-04-13 14:31:02 +02:00
Vendicated
49784bc1aa fix window button patch 2025-04-13 14:29:13 +02:00
Vendicated
43f59cefda use Vencord's addPatch instead of manually adding patches 2025-04-13 14:20:28 +02:00
Sqaaakoi
c42c1b7bbd Fix Visual Refresh titlebar (#1104) 2025-04-04 15:44:47 +02:00
Ryan Cao
765ffc0b57 MacOs: fix DevTools shortcut (#1141) 2025-04-04 15:39:55 +02:00
khcrysalis
3c87c89b3a README: add homebrew as installation method (#1144) 2025-04-02 06:11:52 +02:00
Vendicated
391ad94b85 bump dependencies 2025-04-01 22:02:42 +02:00
rushii
8f94196646 fix: disable Browser Tab shortcuts like Ctrl+W (#1130)
Fixes #934
---------

Co-authored-by: Vending Machine <vendicated@riseup.net>
2025-03-27 01:01:50 +01:00
Vending Machine
878184cab1 improve build instructions 2025-03-27 00:20:29 +01:00
Vending Machine
156ba6ab7b ScreenShare(Windows): loopback -> loopbackWithMute 2025-03-23 22:43:34 +01:00
v
fd91a23188 Update and rename blank.yml to dev-issue.yml 2025-03-06 01:12:17 +01:00
Vendicated
9ca9e78da7 no more issues 2025-03-06 00:14:35 +01:00
Vendicated
a1d5e4dcdc bump dependencies 2025-02-18 15:28:41 +01:00
Vendicated
da5d0f8f19 update blank issue template 2025-02-18 14:18:50 +01:00
Vendicated
d7bc56660b includes is slightly better than every here 2025-02-12 13:18:20 +01:00
Vendicated
d39c54b3ee fix edge case when ~/package.json exists with type: module 2025-02-12 13:16:53 +01:00
v
670c62267e Add workflow to automatically update version in vencord.dev repo 2025-02-12 12:57:15 +01:00
v
8938fe27b2 metaInfo workflow: add to release and push instead of PR 2025-02-08 06:20:29 +01:00
Vendicated
e6c1a03c59 Revert "use Vencord's addPatch instead of manually adding patches"
This was pushed prematurely since it depends on a change to Vencord that hasn't been released yet

This reverts commit 68930a1f50.
2025-02-08 03:53:33 +01:00
Vendicated
68930a1f50 use Vencord's addPatch instead of manually adding patches 2025-02-07 23:13:28 +01:00
Vendicated
3b76c30db2 fix automatic license header inserter still using 2023 (lmao) 2025-02-07 04:16:15 +01:00
Vendicated
523f657b3b hide app download button in server list 2025-02-07 03:54:52 +01:00
github-actions[bot]
d75ab4af1c update Metainfo for v1.5.5 (#1079)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-06 05:51:52 +01:00
Vendicated
7e33780743 Fix Splash colour retrieval & show main window earlier if splash is disabled 2025-02-06 05:36:10 +01:00
Vendicated
9905592b24 add option to disable splash screen 2025-02-06 05:16:32 +01:00
Vendicated
6e1913cafc bump to v1.5.5 2025-02-06 04:56:54 +01:00
Vendicated
b620e07445 remember stream resolution & fps settings 2025-02-06 04:51:20 +01:00
Vendicated
9b0503f49d fix naming of displayid -> displayId 2025-02-06 04:27:52 +01:00
Vendicated
c0b79e6e93 make static title option apply without restart 2025-02-06 04:19:57 +01:00
Vendicated
57cae6f9f1 Settings should be wrapped in ErrorBoundary 2025-02-06 04:13:03 +01:00
Vendicated
2e72fa6589 use proper loggers 2025-02-06 04:00:14 +01:00
Vendicated
4ee57da6f3 remove legacy migration code 2025-02-06 03:47:38 +01:00
Cookie
7560727372 Support discord:// uri scheme (#813)
Co-authored-by: v <vendicated@riseup.net>
2025-02-06 03:45:20 +01:00
BlurOne!
6ba562c663 Enhance debug-logs textarea (#1078)
Update the debug-logs textarea to use the render feature, which automatically makes the content of the textarea into a code block, instead of requiring the default value with backticks.
2025-02-05 23:58:59 +01:00
Vendicated
872b60be1c bump deps so cookie quits yelling at me about arrpc 2025-02-05 20:27:32 +01:00
Cookie
8cd80f4af1 fix: First start ui being blank on windows (#1043) 2025-02-05 20:15:20 +01:00
Cookie
7b5e1ed4da arrpc: support OBS auto streamer mode (#822)
Co-authored-by: Vendicated <vendicated@riseup.net>
2025-02-05 19:12:38 +00:00
Cookie
56442ae1e9 feat(rpc): implement arrpc's link event (#1016)
Co-authored-by: Vendicated <vendicated@riseup.net>
2025-02-05 20:00:40 +01:00
Vendicated
c9be618164 add main to renderer command API 2025-02-02 03:17:45 +01:00
Cookie
eddbe27c4d update hardware accel flags for chromium 131 (#1058)
Co-authored-by: v <vendicated@riseup.net>
2025-02-02 01:51:04 +01:00
Vendicated
00fb658355 clean up and optimise Screenshare UI & CSS 2025-02-01 19:49:56 +01:00
v
67a1847cea please stop opening issues about screenshare stuff 2025-01-31 05:08:02 +01:00
Vendicated
030ffca499 settings: add buttons to open chrome://gpu & webrtc-internals 2025-01-24 01:09:25 +01:00
Vendicated
53913c07bf npm postinstall: update arrpc detectables database 2025-01-22 22:14:27 +01:00
Vendicated
f6e29231f5 update dependencies 2025-01-22 22:14:27 +01:00
RSKYS
7a19c81964 README.md: add slackware link to unofficial repos (#1056) 2025-01-20 12:05:31 +01:00
makin
5a72491ab0 Add 4k resolution to screenshare (#1024) 2025-01-15 17:40:56 +01:00
github-actions[bot]
6c4ecc0d64 Metainfo for v1.5.4 (#990)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-05 23:07:25 +01:00
Vendicated
524fc8bc0a bump to v1.5.4 2024-12-05 20:49:47 +01:00
Cookie
5b6c1c6d81 correctly set splash background (#937)
fixes #858
2024-12-03 04:02:34 +01:00
Vendicated
852410a43b Update Electron ~ fixes blue & light mode devtools 2024-12-03 03:58:54 +01:00
Vendicated
5d675efb64 upgrade to electron33 it probably goes crazy idk what they changed 2024-10-26 13:56:28 +02:00
lewisakura
03c7ad4cc0 ci: remove mentions from meta workflow [skip ci] 2024-10-22 20:22:00 +01:00
δDD
135a369848 fix typo in Settings ui (#924)
Fixed spelling mistake (Miscelleanous → Miscellaneous)
2024-10-22 13:44:49 +00:00
ryanamay
8993b0d520 fix screenshare picker window subtitle alignment (#875)
Co-authored-by: v <vendicated@riseup.net>
2024-10-14 17:40:47 +02:00
adryd
ccff1ac3ef Enable speech-dispatcher support for TTS on linux (#874) 2024-10-04 18:51:55 +00:00
Tiagoquix
062b536617 Update text in bug report (#873) 2024-09-24 23:55:04 +02:00
Vendicated
d008f90399 update arrpc to 5aadc307cb 2024-09-20 20:27:26 +02:00
Vendicated
4fdf43ea6a https://tenor.com/view/ah-eto-bleh-27178660 2024-09-20 20:23:57 +02:00
Vendicated
b94379f5bd workaround electron/electron#43367 2024-09-20 20:16:11 +02:00
Vendicated
37db07807a upgrade electron to v32 2024-09-20 20:07:17 +02:00
Aiden
4274647c81 update ESLint to v9 (#859) 2024-09-20 20:02:15 +02:00
v
24fbf35542 https://x.com/ArmCordClient/status/1834617546445672703 2024-09-13 23:16:42 +02:00
lewisakura
c8eccc7e9d ci: add signing certificate password for mac [skip ci] 2024-09-13 22:02:34 +01:00
v
a318f6b407 Update feature-request.yml 2024-08-27 12:59:57 +02:00
donCESAR12345
75354ad8e6 fix(rpm): don't generate build_id links to avoid conflicts (#826)
...with other packages
2024-08-24 00:17:27 +02:00
lewisakura
af9ed58eef ci: update winget releaser [skip ci] 2024-08-10 10:21:31 +01:00
v
e0453418bd Update bug_report.yml 2024-08-05 19:18:22 +02:00
lewisakura
22344512ad feat: notarization (#776) 2024-07-23 15:10:50 +01:00
Noah
9acc6652ff AudioShare: Even more granular selection, Allow device sharing (#750) 2024-07-12 16:14:18 +02:00
vee
61bbd7f6aa Update bug_report.yml 2024-07-11 05:31:07 +02:00
github-actions[bot]
f31f06f5c4 meta: Insert release changes for v1.5.3 (#724) 2024-07-04 20:21:38 +02:00
vee
786fe131b8 Update install instructions 2024-07-04 20:21:14 +02:00
Vendicated
35d6f19e80 bump to v1.5.3 2024-07-04 19:49:52 +02:00
Vendicated
8798bbce44 only check updates in production 2024-07-04 19:47:23 +02:00
Vendicated
485eb8eaef update user agents 2024-07-04 19:43:27 +02:00
HAHALOSAH
1e39ec7d39 macOS: Workaround for making things in draggable area clickable (#582) 2024-07-04 19:38:56 +02:00
Vendicated
5fa4ae9824 fix first launch switches 2024-07-04 19:30:03 +02:00
Vendicated
281885a87e add auto updater 2024-07-04 19:22:26 +02:00
Vendicated
021aca4089 improve tray strings 2024-07-04 18:51:18 +02:00
Vendicated
f341c5e5bb specify that it's for security so users don't get annoyed 2024-07-04 18:47:33 +02:00
Vendicated
0d203ac21d remove silly console.log 2024-07-04 18:41:03 +02:00
Vendicated
1f12d270ec fix potential sandbox escape via custom vencordDir 2024-07-04 18:40:24 +02:00
kittykel
ec3d83f7ca fix: specify framerate in constraints (#703) 2024-07-04 18:04:57 +02:00
Vendicated
05014f747a fix incorrectly packaging node_modules 2024-07-04 17:54:38 +02:00
Vendicated
9c44da0cae update workflow actions 2024-07-04 17:46:49 +02:00
Lionir
5d5b38b259 meta: Use Network category instead of AudioVideo (#721) 2024-07-04 03:34:17 +02:00
Vendicated
9e3d83a2ee build: arm64 on windows, universal on mac 2024-06-29 12:36:49 +02:00
Nico
0e49e5e8f6 fix: update switch device and notifications patch (#688) 2024-06-21 16:16:01 +02:00
vee
456d5a61cd FOR THE LOVE OF THE GODS PLEASE STOP 2024-06-21 03:43:43 +02:00
Tiagoquix
ebd4e6b28e Make feature request issue template apply appropriate tag (#671) 2024-06-19 19:09:53 +02:00
Tiagoquix
3fe2094814 Change default screen-sharing quality to 720p 30 FPS (#665) 2024-06-18 00:21:55 +02:00
Vendicated
9554902704 update arrpc 2024-06-16 18:42:48 +02:00
Noah
da7f13288f linux audioshare: add granular selection, more options, better ui (#621) 2024-06-16 18:10:03 +02:00
Vendicated
1a4d173bb4 Upgrade to electron 31 2024-06-16 17:59:46 +02:00
lewisakura
abd1e8c6da ci: update winget releaser 2024-06-04 16:51:29 +01:00
Vendicated
7fe79b629e fix(css): brand-experiment is now brand-500 2024-06-01 19:12:29 +02:00
MrGarlic1
d3e8dfa724 hide macos traffic light with custom titlebar (#636)
Co-authored-by: MrGarlic <bsamans@samans.com>
2024-06-01 17:25:42 +02:00
MrGarlic1
3772db9eea Windows Portable: store settings in portable folder (#592)
Co-authored-by: MrGarlic <bsamans@samans.com>
Co-authored-by: vee <vendicated@riseup.net>
2024-05-29 02:03:01 +02:00
Vendicated
b6c3c8024b disable background throttling; hopefully fix unloads when in background 2024-05-28 18:11:55 +02:00
Vendicated
6b948668b9 make package manager check warn instead of error on mismatched version 2024-05-23 17:40:57 +02:00
Vendicated
f232cfc3c8 Spellcheck: add language changer & swap position with paste/cut/copy 2024-05-23 17:30:40 +02:00
Vendicated
bca8872db1 bump venmic 2024-05-22 22:54:35 +02:00
Vendicated
463c42330e bump vencord types 2024-05-17 23:00:06 +02:00
Vendicated
d3b94fc4df fix react types version 2024-05-17 22:52:05 +02:00
MrGarlic1
a8d72fa665 fix: do not re-position on screen that was disconnected (#598)
Co-authored-by: MrGarlic <bsamans@samans.com>
2024-05-10 21:04:16 +02:00
Jörg Thalheim
69f14ee611 README: update link to new nixos wiki (#594)
This commit updates the the link from the former, unofficial nixos wiki
page to the new https://wiki.nixos.org
ref: NixOS/foundation#113
2024-05-09 13:37:36 +02:00
kaitlynkitty
944a699e83 remove obsolete patch (moved to vencord) (#563) 2024-05-08 15:14:55 +00:00
Nick
d11a9a04ff chore: update pnpm to latest version (#590) 2024-05-08 03:47:30 +02:00
splatterxl
56b96bded9 simplify mac links in readme (#585) 2024-05-06 20:27:32 +02:00
Oleh Polisan
ac35f81476 fix: Screenshare UI for non-linux systems (#568) 2024-05-05 04:14:08 +02:00
Vendicated
0623a71271 improve issue templates 2024-05-03 14:49:49 +02:00
samara
a66af353f7 macOS: Add customized dmg background (#565) 2024-05-02 23:41:09 +02:00
github-actions[bot]
cb55cf4b74 add Metainfo for v1.5.2 (#557)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-01 16:57:29 +02:00
Vendicated
e9da30e420 bump to v1.5.2 2024-05-01 16:45:15 +02:00
☆ sam
c52abdc1ee mac: add entitlements needed for camera/microphone access (#533) 2024-04-26 22:02:19 +02:00
115 changed files with 7574 additions and 4592 deletions

View File

@@ -5,4 +5,4 @@
# https://github.com/settings/personal-access-tokens/new
GITHUB_TOKEN=
ELECTRON_LAUNCH_FLAGS="--ozone-platform-hint=auto --enable-webrtc-pipewire-capturer --enable-features=WaylandWindowDecorations"
ELECTRON_LAUNCH_FLAGS="--enable-source-maps --ozone-platform-hint=auto"

View File

@@ -1,69 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"ignorePatterns": ["dist", "node_modules"],
"plugins": [
"@typescript-eslint",
"license-header",
"simple-import-sort",
"unused-imports",
"path-alias",
"prettier"
],
"settings": {
"import/resolver": {
"alias": {
"map": []
}
}
},
"rules": {
"license-header/header": ["error", "scripts/header.txt"],
"eqeqeq": ["error", "always", { "null": "ignore" }],
"spaced-comment": ["error", "always", { "markers": ["!"] }],
"yoda": "error",
"prefer-destructuring": [
"error",
{
"VariableDeclarator": { "array": false, "object": true },
"AssignmentExpression": { "array": false, "object": false }
}
],
"operator-assignment": ["error", "always"],
"no-useless-computed-key": "error",
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
"no-invalid-regexp": "error",
"no-constant-condition": ["error", { "checkLoops": false }],
"no-duplicate-imports": "error",
"no-extra-semi": "error",
"dot-notation": "error",
"no-useless-escape": "error",
"no-fallthrough": "error",
"for-direction": "error",
"no-async-promise-executor": "error",
"no-cond-assign": "error",
"no-dupe-else-if": "error",
"no-duplicate-case": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-prototype-builtins": "error",
"no-regex-spaces": "error",
"no-shadow-restricted-names": "error",
"no-unexpected-multiline": "error",
"no-unsafe-optional-chaining": "error",
"no-useless-backreference": "error",
"use-isnan": "error",
"prefer-const": "error",
"prefer-spread": "error",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"unused-imports/no-unused-imports": "error",
"path-alias/no-relative": "error",
"prettier/prettier": "error"
}
}

View File

@@ -1,58 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
<!--
Please do not open issues for the following things. We cannot help you with them:
- "Vesktop.app is damaged" on MacOs ~ Fake issue created by crApple. Google how to fix it https://google.it/search?q=fix+app+is+damaged
- Screenshare does not start / is black ~ This is an issue with your desktop environment, specifically its xdg-desktop-portal
- Purely graphical glitches, like flickering, scaling issues, short whitescreens, etc ~ These are most likely issues with your GPU. try to disable hardware acceleration
- Vencord related issues ~ This is the Vesktop repo, not Vencord
- Getting logged out after restart ~ If you use DevTools, make sure you have NoDevtoolsWarning enabled. Otherwise try reinstalling Vesktop
-->
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**To Reproduce**
<!--
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-->
**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**Desktop (please complete the following information):**
- OS/Distro: [e.g. Windows / Fedora Linux / MacOs]
- Desktop Environment (linux only): [e.g. gnome, kde, sway]
- Version: [e.g. 22]
**Command line output**
<!-- Run vesktop from the command line. Include the relevant command line output here: -->
```
paste inside these backticks
```
**Additional context**
<!-- Add any other context about the problem here. -->

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Vencord Support Server
url: https://discord.gg/D9uwnFnqmd
about: "Need Help? Join our support server and ask in the #vesktop-support channel!"

View File

@@ -1,10 +0,0 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---

20
.github/ISSUE_TEMPLATE/dev-issue.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Vesktop Developer Issue
description: Reserved for Vesktop Developers. Join our support server for support.
body:
- type: markdown
attributes:
value: |
# This form is reserved for Vesktop Developers. Do not open an issue.
Instead, use the [#vesktop-support channel](https://discord.com/channels/1015060230222131221/1345457031426871417) on our [Discord server](https://vencord.dev/discord) for help and reporting issues.
Your issue will be closed immediately with no comment and you will be blocked if you ignore this.
This is because 99% of issues are not actually bugs, but rather user or system issues and it adds a lot of noise to our development process.
- type: textarea
id: content
attributes:
label: Content
validations:
required: true

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -6,33 +6,37 @@ on:
- published
workflow_dispatch:
permissions:
contents: write
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 # Install pnpm using packageManager key in package.json
- name: Use Node.js 18.18.2
uses: actions/setup-node@v3
with:
node-version: 18.18.2
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: pnpm i
- name: Install dependencies
run: pnpm i
- name: Update metainfo
run: pnpm updateMeta
- name: Update metainfo
run: pnpm updateMeta
- name: Commit and merge in changes
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b ci/meta-update
git add meta/dev.vencord.Vesktop.metainfo.xml
git commit -m "Insert release changes for ${{ github.event.release.tag_name }}"
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"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Commit and merge in changes
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
gh release upload "${{ github.event.release.tag_name }}" meta/dev.vencord.Vesktop.metainfo.xml
git add meta/dev.vencord.Vesktop.metainfo.xml
git commit -m "metainfo: add entry for ${{ github.event.release.tag_name }}"
git push origin HEAD:main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -22,13 +22,13 @@ jobs:
platform: windows
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 # Install pnpm using packageManager key in package.json
- name: Use Node.js 18.18.2
uses: actions/setup-node@v3
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 18.18.2
node-version: 20
cache: "pnpm"
- name: Install dependencies
@@ -43,11 +43,17 @@ jobs:
pnpm electron-builder --${{ matrix.platform }} --publish always
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run Electron Builder
if: ${{ matrix.platform == 'mac' }}
run: |
echo "$API_KEY" > apple.p8
pnpm electron-builder --${{ matrix.platform }} --publish always
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.APPLE_SIGNING_CERT }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.APPLE_SIGNING_CERT }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_SIGNING_CERT_PASSWORD }}
API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY: apple.p8
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}

View File

@@ -11,13 +11,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 # Install pnpm using packageManager key in package.json
- name: Use Node.js 18.18.2
uses: actions/setup-node@v3
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 18.18.2
node-version: 20
cache: "pnpm"
- name: Install dependencies

View File

@@ -0,0 +1,29 @@
name: Update vencord.dev Vesktop version
on:
release:
types:
- published
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Update scripts/_latestVesktopVersion.txt file in vencord.dev repo
run: |
git config --global user.name "$USERNAME"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git clone https://$USERNAME:$API_TOKEN@github.com/$GH_REPO.git repo
cd repo
version="${{ github.event.release.tag_name }}"
echo "${version#v}" > scripts/_latestVesktopVersion.txt
git add scripts/_latestVesktopVersion.txt
git commit -m "Update Vesktop version to ${{ github.event.release.tag_name }}"
git push https://$USERNAME:$API_TOKEN@github.com/$GH_REPO.git
env:
API_TOKEN: ${{ secrets.VENCORD_DEV_GITHUB_TOKEN }}
GH_REPO: Vencord/vencord.dev
USERNAME: GitHub-Actions

View File

@@ -13,10 +13,10 @@ on:
jobs:
winget:
name: Publish winget package
runs-on: ubuntu-latest
runs-on: windows-latest
steps:
- name: Submit package to Winget Community Repo
uses: vedantmgoyal2009/winget-releaser@e68d386d5d6a1cef8cb0fb5e62b77ebcb83e7d58 # v2
uses: vedantmgoyal2009/winget-releaser@0db4f0a478166abd0fa438c631849f0b8dcfb99f
with:
identifier: Vencord.Vesktop
token: ${{ secrets.WINGET_PAT }}

1
.npmrc
View File

@@ -1 +1,2 @@
node-linker=hoisted
package-manager-strict=false

View File

@@ -21,15 +21,18 @@ Vesktop is a custom Discord desktop app
If you don't know the difference, pick the Installer.
- [Installer](https://vencord.dev/download/vesktop/amd64/windows)
- [Portable](https://vencord.dev/download/vesktop/amd64/windows-portable)
- [Installer](https://vencord.dev/download/vesktop/universal/windows)
- Portable:
- [x64 / amd64](https://vencord.dev/download/vesktop/amd64/windows-portable)
- [Arm® 64](https://vencord.dev/download/vesktop/arm64/windows-portable)
### Mac
If you don't know the difference, pick amd64
Download the latest [Vesktop.dmg](https://vencord.dev/download/vesktop/universal/dmg) or use [Homebrew](https://brew.sh/)
- [amd64 / x86_64](https://vencord.dev/download/vesktop/amd64/dmg)
- [arm64 / aarch64](https://vencord.dev/download/vesktop/arm64/dmg)
```sh
brew install vesktop
```
### Linux
@@ -42,7 +45,7 @@ If you don't know the difference, pick amd64.
- [Ubuntu/Debian (.deb)](https://vencord.dev/download/vesktop/amd64/deb)
- [Fedora/RHEL (.rpm)](https://vencord.dev/download/vesktop/amd64/rpm)
- [tarball](https://vencord.dev/download/vesktop/amd64/tar)
- arm64 / aarch64
- Arm® 64 / aarch64
- [AppImage](https://vencord.dev/download/vesktop/arm64/appimage)
- [Ubuntu/Debian (.deb)](https://vencord.dev/download/vesktop/arm64/deb)
- [Fedora/RHEL (.rpm)](https://vencord.dev/download/vesktop/arm64/rpm)
@@ -53,11 +56,17 @@ If you don't know the difference, pick amd64.
Below you can find unofficial packages created by the community. They are not officially supported by us, so before reporting issues, please first confirm the issue also happens on official builds. When in doubt, consult with their packager first. The flatpak and AppImage should work on any distro that [supports them](https://flatpak.org/setup/), so I recommend you just use those instead!
- Arch Linux: [Vesktop on the Arch user repository](https://aur.archlinux.org/packages?K=vesktop)
- NixOS: https://nixos.wiki/wiki/Discord#Vesktop
- NixOS: https://wiki.nixos.org/wiki/Discord#Vesktop
- Slackware: [Vesktop on the SlackBuilds](https://slackbuilds.org/result/?search=vesktop)
- Windows - Scoop: https://scoop.sh/#/apps?q=Vesktop
## Building from Source
You need to have the following dependencies installed:
- [Git](https://git-scm.com/downloads)
- [Node.js](https://nodejs.org/en/download)
- pnpm: `npm install --global pnpm`
Packaging will create builds in the dist/ folder
```sh
@@ -70,10 +79,12 @@ pnpm i
# Either run it without packaging
pnpm start
# Or package
# Or package (will build packages for your OS)
pnpm package
# Or only build the pacman target
# Or only build the Linux Pacman package
pnpm package --linux pacman
# Or package to a directory only
pnpm package:dir
```

BIN
build/background.tiff Normal file

Binary file not shown.

View File

@@ -0,0 +1,21 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.bluetooth</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

103
eslint.config.mjs Normal file
View File

@@ -0,0 +1,103 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
//@ts-check
import stylistic from "@stylistic/eslint-plugin";
import pathAlias from "eslint-plugin-path-alias";
import simpleHeader from "eslint-plugin-simple-header";
import importSort from "eslint-plugin-simple-import-sort";
import unusedImports from "eslint-plugin-unused-imports";
import tseslint from "typescript-eslint";
import prettier from "eslint-plugin-prettier";
export default tseslint.config(
{ ignores: ["dist"] },
{
files: ["src/**/*.{tsx,ts,mts,mjs,js,jsx}"],
plugins: {
simpleHeader,
stylistic,
importSort,
unusedImports,
pathAlias,
prettier
},
settings: {
"import/resolver": {
alias: {
map: []
}
}
},
languageOptions: {
parser: tseslint.parser,
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname
}
},
rules: {
"simpleHeader/header": [
"error",
{
files: ["scripts/header.txt"],
templates: { author: [".*", "Vendicated and Vesktop contributors"] }
}
],
// ESLint Rules
yoda: "error",
eqeqeq: ["error", "always", { null: "ignore" }],
"prefer-destructuring": [
"error",
{
VariableDeclarator: { array: false, object: true },
AssignmentExpression: { array: false, object: false }
}
],
"operator-assignment": ["error", "always"],
"no-useless-computed-key": "error",
"no-unneeded-ternary": ["error", { defaultAssignment: false }],
"no-invalid-regexp": "error",
"no-constant-condition": ["error", { checkLoops: false }],
"no-duplicate-imports": "error",
"dot-notation": "error",
"no-useless-escape": "error",
"no-fallthrough": "error",
"for-direction": "error",
"no-async-promise-executor": "error",
"no-cond-assign": "error",
"no-dupe-else-if": "error",
"no-duplicate-case": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-prototype-builtins": "error",
"no-regex-spaces": "error",
"no-shadow-restricted-names": "error",
"no-unexpected-multiline": "error",
"no-unsafe-optional-chaining": "error",
"no-useless-backreference": "error",
"use-isnan": "error",
"prefer-const": "error",
"prefer-spread": "error",
// Styling Rules
"stylistic/spaced-comment": ["error", "always", { markers: ["!"] }],
"stylistic/no-extra-semi": "error",
// Plugin Rules
"importSort/imports": "error",
"importSort/exports": "error",
"unusedImports/no-unused-imports": "error",
"pathAlias/no-relative": "error",
"prettier/prettier": "error"
}
}
);

View File

@@ -28,6 +28,107 @@
</screenshot>
</screenshots>
<releases>
<release version="1.5.7" date="2025-06-08" type="stable">
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.7</url>
<description>
<p>What's Changed</p>
<ul>
<li>Starting a screenshare on Windows will no longer mute system audio</li>
<li>Fixed showing "Invalid URL" errors in some edge cases (spotty network)</li>
<li>Now respects your Autogain preference</li>
<li>Improved the flow for linking third party accounts</li>
<li>Fixed issue that would cause the AppImage to have no icon and be unpinnable</li>
<li>Added the ability to screenshare with stereo audio</li>
<li>Added Video Hardware Acceleration switch - Used to always be enabled. Now it is opt-in (disabled by default) to fix graphical glitches that were affecting many users</li>
<li>Fixed Discord titlebar buttons</li>
<li>Fixed "Object has been destroyed" error in some edge cases</li>
</ul>
</description>
</release>
<release version="1.5.6" date="2025-04-13" type="stable">
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.6</url>
<description>
<p>What's Changed</p>
<ul>
<li>Fixes the new Discord titlebar @Sqaaakoi</li>
<li>Fixes Clipboard copy actions not working</li>
<li>Should now properly hide the "Download Apps" button again</li>
<li>No longer responds to Browser Tab shortcuts like Ctrl+W by @rushiiMachine</li>
<li>DevTools keybind should now work properly on Mac @ryanccn</li>
<li>Should no longer throw Sandbox errors on Ubuntu</li>
</ul>
</description>
</release>
<release version="1.5.5" date="2025-02-06" type="stable">
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.5</url>
<description>
<p>What's Changed</p>
<ul>
<li>Now remembers your previous Screenshare resolution &amp; FPS</li>
<li>You can now disable the splash screen in Vesktop Settings</li>
<li>Now supports deep links (opening Discord Message Links in Vesktop) by @Covkie</li>
<li>Now supports discord:// uri scheme, allowing it to open things like invites from your Browser even while closed by @Covkie</li>
<li>Added 4k resolution to screenshare by @makindotcc</li>
<li>Fixed some performance issues caused by a recent Discord update</li>
<li>Updated Electron to v34 &amp; chromium to v132, bringing new features and fixes</li>
</ul>
</description>
</release>
<release version="1.5.4" date="2024-12-05" type="stable">
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.4</url>
<description>
<p>What's Changed</p>
<ul>
<li>Upgraded electron to version 33 which brings many improvements and bug fixes</li>
<li>AudioShare: add even more granular selection, Allow device sharing by @Curve</li>
<li>Enable speech-dispatcher support for TTS on linux by @adryd325</li>
<li>fixed screenshare picker window subtitle alignment by @ryawaa</li>
<li>fixed splash corners by @Covkie</li>
</ul>
</description>
</release>
<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 &amp; 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 &amp; 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">
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.2</url>
<description>
<p>What's Changed</p>
<ul>
<li>Fixed scrollbars looking wrong (actually Discord's fault)</li>
<li>Tray: Added left click hide/show feature by @0bCdian</li>
<li>MacOS: Fixed the app not properly requesting microphone permissions by @ssalggnikool</li>
<li>Linux: Various fixed related to audio screenshare by @Curve</li>
<li>Linux: Overhauled &amp; improved screenshare with better framerate by @kaitlynkittyy</li>
<li>Users can now pass --enable/disable-features command line flags by @takase1121</li>
</ul>
</description>
</release>
<release version="1.5.1" date="2024-03-12" type="stable">
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.1</url>
<description>
@@ -168,7 +269,7 @@
<url type="vcs-browser">https://github.com/Vencord/Vesktop</url>
<categories>
<category>InstantMessaging</category>
<category>AudioVideo</category>
<category>Network</category>
</categories>
<requires>
<control>pointing</control>
@@ -194,4 +295,4 @@
<keyword>Privacy</keyword>
<keyword>Mod</keyword>
</keywords>
</component>
</component>

View File

@@ -1,11 +1,11 @@
{
"name": "vesktop",
"version": "1.5.1",
"version": "1.5.8",
"private": true,
"description": "",
"description": "Vesktop is a custom Discord desktop app",
"keywords": [],
"homepage": "https://vencord.dev/",
"license": "GPL-3.0",
"license": "GPL-3.0-or-later",
"author": "Vendicated <vendicated@riseup.net>",
"main": "dist/js/main.js",
"scripts": {
@@ -13,7 +13,7 @@
"build:dev": "pnpm build --dev",
"package": "pnpm build && electron-builder",
"package:dir": "pnpm build && electron-builder --dir",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx,.mts,.mjs",
"lint": "eslint",
"lint:fix": "pnpm lint --fix",
"start": "pnpm build && electron .",
"start:dev": "pnpm build:dev && electron .",
@@ -21,41 +21,43 @@
"test": "pnpm lint && pnpm testTypes",
"testTypes": "tsc --noEmit",
"watch": "pnpm build --watch",
"updateMeta": "tsx scripts/utils/updateMeta.mts"
"updateMeta": "tsx scripts/utils/updateMeta.mts",
"updateArrpcDB": "node ./node_modules/arrpc/update_db.js",
"postinstall": "pnpm updateArrpcDB"
},
"dependencies": {
"arrpc": "github:OpenAsar/arrpc#6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c"
"arrpc": "github:OpenAsar/arrpc#2234e9c9111f4c42ebcc3aa6a2215bfd979eef77",
"electron-updater": "^6.6.2"
},
"optionalDependencies": {
"@vencord/venmic": "^3.4.2"
"@vencord/venmic": "^6.1.0"
},
"devDependencies": {
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
"@types/node": "^20.11.26",
"@types/react": "^18.2.65",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vencord/types": "^0.1.2",
"dotenv": "^16.4.5",
"electron": "^29.1.1",
"electron-builder": "^24.13.3",
"esbuild": "^0.20.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"@stylistic/eslint-plugin": "^5.1.0",
"@types/node": "^24.0.10",
"@types/react": "18.3.1",
"@vencord/types": "^1.11.5",
"dotenv": "^16.5.0",
"electron": "^37.2.0",
"electron-builder": "^26.0.12",
"esbuild": "^0.25.5",
"eslint": "^9.30.1",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-license-header": "^0.6.0",
"eslint-plugin-path-alias": "^1.0.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-simple-import-sort": "^12.0.0",
"eslint-plugin-unused-imports": "^3.1.0",
"prettier": "^3.2.5",
"eslint-plugin-path-alias": "^2.1.0",
"eslint-plugin-prettier": "^5.5.1",
"eslint-plugin-simple-header": "^1.2.2",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-unused-imports": "^4.1.4",
"prettier": "^3.6.2",
"source-map-support": "^0.5.21",
"tsx": "^4.7.1",
"type-fest": "^4.12.0",
"typescript": "^5.4.2",
"xml-formatter": "^3.6.2"
"tsx": "^4.20.3",
"type-fest": "^4.41.0",
"typescript": "^5.8.3",
"typescript-eslint": "^8.35.1",
"xml-formatter": "^3.6.6"
},
"packageManager": "pnpm@8.11.0",
"packageManager": "pnpm@10.7.1",
"engines": {
"node": ">=18",
"pnpm": ">=8"
@@ -65,11 +67,18 @@
"productName": "Vesktop",
"files": [
"!*",
"!node_modules",
"dist/js",
"static",
"package.json",
"LICENSE"
],
"protocols": {
"name": "Discord",
"schemes": [
"discord"
]
},
"beforePack": "scripts/build/sandboxFix.js",
"linux": {
"icon": "build/icon.icns",
@@ -106,11 +115,15 @@
}
],
"desktop": {
"Name": "Vesktop",
"GenericName": "Internet Messenger",
"Type": "Application",
"Categories": "Network;InstantMessaging;Chat;",
"Keywords": "discord;vencord;electron;chat;"
"entry": {
"Name": "Vesktop",
"GenericName": "Internet Messenger",
"Type": "Application",
"Categories": "Network;InstantMessaging;Chat;",
"Keywords": "discord;vencord;electron;chat;",
"MimeType": "x-scheme-handler/discord",
"StartupWMClass": "vesktop"
}
}
},
"mac": {
@@ -118,18 +131,40 @@
{
"target": "default",
"arch": [
"x64",
"arm64"
"universal"
]
}
],
"category": "Network",
"category": "public.app-category.social-networking",
"darkModeSupport": true,
"extendInfo": {
"NSMicrophoneUsageDescription": "This app needs access to the microphone",
"NSCameraUsageDescription": "This app needs access to the camera",
"com.apple.security.device.audio-input": true,
"com.apple.security.device.camera": true
}
},
"notarize": true
},
"dmg": {
"background": "build/background.tiff",
"icon": "build/icon.icns",
"iconSize": 105,
"window": {
"width": 512,
"height": 340
},
"contents": [
{
"x": 140,
"y": 160
},
{
"x": 372,
"y": 160,
"type": "link",
"path": "/Applications"
}
]
},
"nsis": {
"include": "build/installer.nsh",
@@ -137,12 +172,40 @@
},
"win": {
"target": [
"nsis",
"zip"
{
"target": "nsis",
"arch": [
"x64",
"arm64"
]
},
{
"target": "zip",
"arch": [
"x64",
"arm64"
]
}
]
},
"publish": {
"provider": "github"
},
"rpm": {
"fpm": [
"--rpm-rpmbuild-define=_build_id_links none"
]
}
},
"pnpm": {
"patchedDependencies": {
"arrpc@3.5.0": "patches/arrpc@3.5.0.patch",
"electron-updater": "patches/electron-updater.patch"
},
"onlyBuiltDependencies": [
"@vencord/venmic",
"electron",
"esbuild"
]
}
}

27
patches/arrpc@3.5.0.patch Normal file
View File

@@ -0,0 +1,27 @@
diff --git a/src/process/index.js b/src/process/index.js
index 389b0845256a34b4536d6da99edb00d17f13a6b4..f17a0ac687e9110ebfd33cb91fd2f6250d318643 100644
--- a/src/process/index.js
+++ b/src/process/index.js
@@ -5,8 +5,20 @@ import fs from 'node:fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
-const __dirname = dirname(fileURLToPath(import.meta.url));
-const DetectableDB = JSON.parse(fs.readFileSync(join(__dirname, 'detectable.json'), 'utf8'));
+const DetectableDB = require('./detectable.json');
+DetectableDB.push(
+ {
+ aliases: ["Obs"],
+ executables: [
+ { is_launcher: false, name: "obs", os: "linux" },
+ { is_launcher: false, name: "obs.exe", os: "win32" },
+ { is_launcher: false, name: "obs.app", os: "darwin" }
+ ],
+ hook: true,
+ id: "STREAMERMODE",
+ name: "OBS"
+ }
+);
import * as Natives from './native/index.js';
const Native = Natives[process.platform];

View File

@@ -0,0 +1,16 @@
diff --git a/out/RpmUpdater.js b/out/RpmUpdater.js
index 563187bb18cb0bd154dff6620cb62b8c8f534cd6..d91594026c2bac9cc78ef3b1183df3241d7d9624 100644
--- a/out/RpmUpdater.js
+++ b/out/RpmUpdater.js
@@ -32,7 +32,10 @@ class RpmUpdater extends BaseUpdater_1.BaseUpdater {
const sudo = this.wrapSudo();
// pkexec doesn't want the command to be wrapped in " quotes
const wrapper = /pkexec/i.test(sudo) ? "" : `"`;
- const packageManager = this.spawnSyncLog("which zypper");
+ let packageManager;
+ try {
+ packageManager = this.spawnSyncLog("which zypper");
+ } catch {}
const installerPath = this.installerPath;
if (installerPath == null) {
this.dispatchError(new Error("No valid update available, can't quit and install"));

8168
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,14 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { BuildContext, BuildOptions, context } from "esbuild";
import { copyFile } from "fs/promises";
import vencordDep from "./vencordDep.mjs";
import { includeDirPlugin } from "./includeDirPlugin.mts";
const isDev = process.argv.includes("--dev");
@@ -57,6 +58,12 @@ await Promise.all([
outfile: "dist/js/main.js",
footer: { js: "//# sourceURL=VCDMain" }
}),
createContext({
...NodeCommonOpts,
entryPoints: ["src/main/arrpc/worker.ts"],
outfile: "dist/js/arRpcWorker.js",
footer: { js: "//# sourceURL=VCDArRpcWorker" }
}),
createContext({
...NodeCommonOpts,
entryPoints: ["src/preload/index.ts"],
@@ -65,9 +72,8 @@ await Promise.all([
}),
createContext({
...NodeCommonOpts,
entryPoints: ["src/updater/preload.ts"],
outfile: "dist/js/updaterPreload.js",
footer: { js: "//# sourceURL=VCDUpdaterPreload" }
entryPoints: ["src/preload/splash.ts"],
outfile: "dist/js/splashPreload.js"
}),
createContext({
...CommonOpts,
@@ -79,7 +85,7 @@ await Promise.all([
jsxFactory: "VencordCreateElement",
jsxFragment: "VencordFragment",
external: ["@vencord/types/*"],
plugins: [vencordDep],
plugins: [vencordDep, includeDirPlugin("patches", "src/renderer/patches")],
footer: { js: "//# sourceURL=VCDRenderer" }
})
]);

View File

@@ -0,0 +1,25 @@
import { Plugin } from "esbuild";
import { readdir } from "fs/promises";
const makeImportAllCode = (files: string[]) =>
files.map(f => `require("./${f.replace(/\.[cm]?[tj]sx?$/, "")}")`).join("\n");
const makeImportDirRecursiveCode = (dir: string) => readdir(dir).then(files => makeImportAllCode(files));
export function includeDirPlugin(namespace: string, path: string): Plugin {
return {
name: `include-dir-plugin:${namespace}`,
setup(build) {
const filter = new RegExp(`^__${namespace}__$`);
build.onResolve({ filter }, args => ({ path: args.path, namespace }));
build.onLoad({ filter, namespace }, async args => {
return {
contents: await makeImportDirRecursiveCode(path),
resolveDir: path
};
});
}
};
}

View File

@@ -1,9 +1,3 @@
/*
* 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
*/
export const VencordFragment = /* #__PURE__*/ Symbol.for("react.fragment");
export let VencordCreateElement = (...args) =>
(VencordCreateElement = Vencord.Webpack.Common.React.createElement)(...args);

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
// Based on https://github.com/gergof/electron-builder-sandbox-fix/blob/master/lib/index.js

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { globalExternalsWithRegExp } from "@fal-works/esbuild-plugin-global-externals";

View File

@@ -1,5 +1,5 @@
/*
* 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
* Copyright (c) {year} {author}
* SPDX-License-Identifier: GPL-3.0-or-later
*/

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./start";

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { config } from "dotenv";

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { spawn as spaaawn, SpawnOptions } from "child_process";

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { promises as fs } from "node:fs";

4
src/globals.d.ts vendored
View File

@@ -1,13 +1,13 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
declare global {
export var VesktopNative: typeof import("preload/VesktopNative").VesktopNative;
export var Vesktop: typeof import("renderer/index");
export var VCDP: any;
export var VesktopPatchGlobals: any;
export var IS_DEV: boolean;
}

View File

@@ -1,28 +1,36 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { BrowserWindow } from "electron";
import { app, BrowserWindow } from "electron";
import { join } from "path";
import { ICON_PATH, VIEW_DIR } from "shared/paths";
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
export function createAboutWindow() {
export async function createAboutWindow() {
const height = 750;
const width = height * (4 / 3);
const about = new BrowserWindow({
center: true,
autoHideMenuBar: true,
icon: ICON_PATH,
webPreferences: {
preload: join(__dirname, "updaterPreload.js")
}
height,
width
});
makeLinksOpenExternally(about);
about.loadFile(join(VIEW_DIR, "about.html"));
const data = new URLSearchParams({
APP_VERSION: app.getVersion()
});
about.loadFile(join(VIEW_DIR, "about.html"), {
search: data.toString()
});
return about;
}

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { app, NativeImage, nativeImage } from "electron";
@@ -29,10 +29,10 @@ export function setBadgeCount(count: number) {
break;
case "darwin":
if (count === 0) {
app.dock.setBadge("");
app.dock!.setBadge("");
break;
}
app.dock.setBadge(count === -1 ? "•" : count.toString());
app.dock!.setBadge(count === -1 ? "•" : count.toString());
break;
case "win32":
const [index, description] = getBadgeIndexAndDescription(count);

View File

@@ -1,38 +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 Server from "arrpc";
import { IpcEvents } from "shared/IpcEvents";
import { mainWin } from "./mainWindow";
import { Settings } from "./settings";
let server: any;
const inviteCodeRegex = /^(\w|-)+$/;
export async function initArRPC() {
if (server || !Settings.store.arRPC) return;
try {
server = await new Server();
server.on("activity", (data: any) => mainWin.webContents.send(IpcEvents.ARRPC_ACTIVITY, JSON.stringify(data)));
server.on("invite", (invite: string, callback: (valid: boolean) => void) => {
invite = String(invite);
if (!inviteCodeRegex.test(invite)) return callback(false);
mainWin.webContents
// Safety: Result of JSON.stringify should always be safe to equal
// Also, just to be super super safe, invite is regex validated above
.executeJavaScript(`Vesktop.openInviteModal(${JSON.stringify(invite)})`)
.then(callback);
});
} catch (e) {
console.error("Failed to start arRPC server", e);
}
}
Settings.addChangeListener("arRPC", initArRPC);

77
src/main/arrpc/index.ts Normal file
View File

@@ -0,0 +1,77 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { resolve } from "path";
import { IpcCommands } from "shared/IpcEvents";
import { MessageChannel, Worker } from "worker_threads";
import { sendRendererCommand } from "../ipcCommands";
import { Settings } from "../settings";
import { ArRpcEvent, ArRpcHostEvent } from "./types";
let worker: Worker;
const inviteCodeRegex = /^(\w|-)+$/;
export async function initArRPC() {
if (worker || !Settings.store.arRPC) return;
try {
const { port1: hostPort, port2: workerPort } = new MessageChannel();
worker = new Worker(resolve(__dirname, "./arRpcWorker.js"), {
workerData: {
workerPort
},
transferList: [workerPort]
});
hostPort.on("message", async ({ type, nonce, data }: ArRpcEvent) => {
switch (type) {
case "activity": {
sendRendererCommand(IpcCommands.RPC_ACTIVITY, data);
break;
}
case "invite": {
const invite = String(data);
const response: ArRpcHostEvent = {
type: "ack-invite",
nonce,
data: false
};
if (!inviteCodeRegex.test(invite)) {
return hostPort.postMessage(response);
}
response.data = await sendRendererCommand(IpcCommands.RPC_INVITE, invite).catch(() => false);
hostPort.postMessage(response);
break;
}
case "link": {
const response: ArRpcHostEvent = {
type: "ack-link",
nonce: nonce,
data: false
};
response.data = await sendRendererCommand(IpcCommands.RPC_DEEP_LINK, data).catch(() => false);
hostPort.postMessage(response);
break;
}
}
});
} catch (e) {
console.error("Failed to start arRPC server", e);
}
}
Settings.addChangeListener("arRPC", initArRPC);

38
src/main/arrpc/types.ts Normal file
View File

@@ -0,0 +1,38 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vesktop contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
export type ArRpcEvent = ArRpcActivityEvent | ArRpcInviteEvent | ArRpcLinkEvent;
export type ArRpcHostEvent = ArRpcHostAckInviteEvent | ArRpcHostAckLinkEvent;
export interface ArRpcActivityEvent {
type: "activity";
nonce: string;
data: string;
}
export interface ArRpcInviteEvent {
type: "invite";
nonce: string;
data: string;
}
export interface ArRpcLinkEvent {
type: "link";
nonce: string;
data: any;
}
export interface ArRpcHostAckInviteEvent {
type: "ack-invite";
nonce: string;
data: boolean;
}
export interface ArRpcHostAckLinkEvent {
type: "ack-link";
nonce: string;
data: boolean;
}

73
src/main/arrpc/worker.ts Normal file
View File

@@ -0,0 +1,73 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vesktop contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import Server from "arrpc";
import { randomUUID } from "crypto";
import { MessagePort, workerData } from "worker_threads";
import { ArRpcEvent, ArRpcHostEvent } from "./types";
let server: any;
type InviteCallback = (valid: boolean) => void;
type LinkCallback = InviteCallback;
const inviteCallbacks = new Map<string, InviteCallback>();
const linkCallbacks = new Map<string, LinkCallback>();
(async function () {
const { workerPort } = workerData as { workerPort: MessagePort };
server = await new Server();
server.on("activity", (data: any) => {
const event: ArRpcEvent = {
type: "activity",
data: JSON.stringify(data),
nonce: randomUUID()
};
workerPort.postMessage(event);
});
server.on("invite", (invite: string, callback: InviteCallback) => {
const nonce = randomUUID();
inviteCallbacks.set(nonce, callback);
const event: ArRpcEvent = {
type: "invite",
data: invite,
nonce
};
workerPort.postMessage(event);
});
server.on("link", async (data: any, callback: LinkCallback) => {
const nonce = randomUUID();
linkCallbacks.set(nonce, callback);
const event: ArRpcEvent = {
type: "link",
data,
nonce
};
workerPort.postMessage(event);
});
workerPort.on("message", (e: ArRpcHostEvent) => {
switch (e.type) {
case "ack-invite": {
inviteCallbacks.get(e.nonce)?.(e.data);
inviteCallbacks.delete(e.nonce);
break;
}
case "ack-link": {
linkCallbacks.get(e.nonce)?.(e.data);
linkCallbacks.delete(e.nonce);
break;
}
}
});
})();

View File

@@ -1,12 +1,13 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { app } from "electron";
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs";
import { join } from "path";
import { stripIndent } from "shared/utils/text";
interface AutoStart {
isEnabled(): boolean;
@@ -31,15 +32,15 @@ function makeAutoStartLinux(): AutoStart {
return {
isEnabled: () => existsSync(file),
enable() {
const desktopFile = `
[Desktop Entry]
Type=Application
Name=Vesktop
Comment=Vesktop autostart script
Exec=${commandLine}
StartupNotify=false
Terminal=false
`.trim();
const desktopFile = stripIndent`
[Desktop Entry]
Type=Application
Name=Vesktop
Comment=Vesktop autostart script
Exec=${commandLine}
StartupNotify=false
Terminal=false
`;
mkdirSync(dir, { recursive: true });
writeFileSync(file, desktopFile);

View File

@@ -1,32 +1,27 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { app } from "electron";
import { existsSync, readdirSync, renameSync, rmdirSync } from "fs";
import { join } from "path";
import { existsSync, mkdirSync } from "fs";
import { dirname, join } from "path";
const LEGACY_DATA_DIR = join(app.getPath("appData"), "VencordDesktop", "VencordDesktop");
export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("userData"));
// TODO: remove eventually
if (existsSync(LEGACY_DATA_DIR)) {
try {
console.warn("Detected legacy settings dir", LEGACY_DATA_DIR + ".", "migrating to", DATA_DIR);
for (const file of readdirSync(LEGACY_DATA_DIR)) {
renameSync(join(LEGACY_DATA_DIR, file), join(DATA_DIR, file));
}
rmdirSync(LEGACY_DATA_DIR);
renameSync(
join(app.getPath("appData"), "VencordDesktop", "IndexedDB"),
join(DATA_DIR, "sessionData", "IndexedDB")
);
} catch (e) {
console.error("Migration failed", e);
}
}
app.setPath("sessionData", join(DATA_DIR, "sessionData"));
const vesktopDir = dirname(process.execPath);
export const PORTABLE =
process.platform === "win32" &&
!process.execPath.toLowerCase().endsWith("electron.exe") &&
!existsSync(join(vesktopDir, "Uninstall Vesktop.exe"));
export const DATA_DIR =
process.env.VENCORD_USER_DATA_DIR || (PORTABLE ? join(vesktopDir, "Data") : join(app.getPath("userData")));
mkdirSync(DATA_DIR, { recursive: true });
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_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
@@ -36,7 +31,8 @@ export const VENCORD_THEMES_DIR = join(DATA_DIR, "themes");
// needs to be inline require because of circular dependency
// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised
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)`;
@@ -48,11 +44,11 @@ export const DEFAULT_HEIGHT = 720;
export const DISCORD_HOSTNAMES = ["discord.com", "canary.discord.com", "ptb.discord.com"];
const VersionString = `AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${process.versions.chrome.split(".")[0]}.0.0.0 Safari/537.36`;
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",
linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
windows:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) 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) ${VersionString}`,
linux: `Mozilla/5.0 (X11; Linux x86_64) ${VersionString}`,
windows: `Mozilla/5.0 (Windows NT 10.0; Win64; x64) ${VersionString}`
};
export const BrowserUserAgent = BrowserUserAgents[process.platform] || BrowserUserAgents.windows;

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { app } from "electron";
@@ -18,16 +18,17 @@ import { Settings, State } from "./settings";
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
interface Data {
minimizeToTray: boolean;
discordBranch: "stable" | "canary" | "ptb";
autoStart: boolean;
importSettings: boolean;
richPresence: boolean;
minimizeToTray?: "on";
autoStart?: "on";
importSettings?: "on";
richPresence?: "on";
}
export function createFirstLaunchTour() {
const win = new BrowserWindow({
...SplashProps,
transparent: false,
frame: true,
autoHideMenuBar: true,
height: 470,
@@ -44,10 +45,11 @@ export function createFirstLaunchTour() {
if (!msg.startsWith("form:")) return;
const data = JSON.parse(msg.slice(5)) as Data;
console.log(data);
State.store.firstLaunch = false;
Settings.store.minimizeToTray = data.minimizeToTray;
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();

View File

@@ -1,13 +1,13 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./ipc";
import { app, BrowserWindow, nativeTheme } from "electron";
import { checkUpdates } from "updater/main";
import { autoUpdater } from "electron-updater";
import { DATA_DIR } from "./constants";
import { createFirstLaunchTour } from "./firstLaunch";
@@ -17,44 +17,86 @@ import { registerScreenShareHandler } from "./screenShare";
import { Settings, State } from "./settings";
import { isDeckGameMode } from "./utils/steamOS";
if (IS_DEV) {
require("source-map-support").install();
if (!IS_DEV) {
autoUpdater.checkForUpdatesAndNotify();
}
console.log("Vesktop v" + app.getVersion());
// Make the Vencord files use our DATA_DIR
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
const isLinux = process.platform === "linux";
export let enableHardwareAcceleration = true;
function init() {
const { disableSmoothScroll, hardwareAcceleration } = Settings.store;
app.setAsDefaultProtocolClient("discord");
const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(",");
const disabledFeatures = app.commandLine.getSwitchValue("disable-features").split(",");
const { disableSmoothScroll, hardwareAcceleration, hardwareVideoAcceleration } = Settings.store;
if (hardwareAcceleration === false) {
const enabledFeatures = new Set(app.commandLine.getSwitchValue("enable-features").split(","));
const disabledFeatures = new Set(app.commandLine.getSwitchValue("disable-features").split(","));
app.commandLine.removeSwitch("enable-features");
app.commandLine.removeSwitch("disable-features");
if (hardwareAcceleration === false || process.argv.includes("--disable-gpu")) {
enableHardwareAcceleration = false;
app.disableHardwareAcceleration();
} else {
enabledFeatures.push("VaapiVideoDecodeLinuxGL", "VaapiVideoEncoder", "VaapiVideoDecoder");
if (hardwareVideoAcceleration) {
enabledFeatures.add("AcceleratedVideoEncoder");
enabledFeatures.add("AcceleratedVideoDecoder");
if (isLinux) {
enabledFeatures.add("AcceleratedVideoDecodeLinuxGL");
enabledFeatures.add("AcceleratedVideoDecodeLinuxZeroCopyGL");
}
}
}
if (disableSmoothScroll) {
app.commandLine.appendSwitch("disable-smooth-scrolling");
}
// disable renderer backgrounding to prevent the app from unloading when in the background
// https://github.com/electron/electron/issues/2822
// https://github.com/GoogleChrome/chrome-launcher/blob/5a27dd574d47a75fec0fb50f7b774ebf8a9791ba/docs/chrome-flags-for-tools.md#task-throttling
app.commandLine.appendSwitch("disable-renderer-backgrounding");
app.commandLine.appendSwitch("disable-background-timer-throttling");
app.commandLine.appendSwitch("disable-backgrounding-occluded-windows");
if (process.platform === "win32") {
disabledFeatures.add("CalculateNativeWinOcclusion");
}
// work around chrome 66 disabling autoplay by default
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
// WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows.
// HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service.
//
// WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790
disabledFeatures.push(
"WinRetrieveSuggestionsOnlyOnDemand",
"HardwareMediaKeyHandling",
"MediaSessionService",
"WidgetLayering"
);
app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].filter(Boolean).join(","));
app.commandLine.appendSwitch("disable-features", [...new Set(disabledFeatures)].filter(Boolean).join(","));
// WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows.
// HardwareMediaKeyHandling, MediaSessionService: Prevent Discord from registering as a media service.
disabledFeatures.add("WinRetrieveSuggestionsOnlyOnDemand");
disabledFeatures.add("HardwareMediaKeyHandling");
disabledFeatures.add("MediaSessionService");
if (isLinux) {
// Support TTS on Linux using https://wiki.archlinux.org/title/Speech_dispatcher
app.commandLine.appendSwitch("enable-speech-dispatcher");
}
disabledFeatures.forEach(feat => enabledFeatures.delete(feat));
const enabledFeaturesArray = enabledFeatures.values().filter(Boolean).toArray();
const disabledFeaturesArray = disabledFeatures.values().filter(Boolean).toArray();
if (enabledFeaturesArray.length) {
app.commandLine.appendSwitch("enable-features", enabledFeaturesArray.join(","));
console.log("Enabled Chromium features:", enabledFeaturesArray.join(", "));
}
if (disabledFeaturesArray.length) {
app.commandLine.appendSwitch("disable-features", disabledFeaturesArray.join(","));
console.log("Disabled Chromium features:", disabledFeaturesArray.join(", "));
}
// In the Flatpak on SteamOS the theme is detected as light, but SteamOS only has a dark mode, so we just override it
if (isDeckGameMode) nativeTheme.themeSource = "dark";
@@ -69,7 +111,6 @@ function init() {
});
app.whenReady().then(async () => {
checkUpdates();
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.vesktop");
registerScreenShareHandler();
@@ -103,6 +144,12 @@ async function bootstrap() {
}
}
// MacOS only event
export let darwinURL: string | undefined;
app.on("open-url", (_, url) => {
darwinURL = url;
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});

View File

@@ -1,15 +1,26 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
if (process.platform === "linux") import("./venmic");
import { execFile } from "child_process";
import { app, BrowserWindow, clipboard, dialog, nativeImage, RelaunchOptions, session, shell } from "electron";
import {
app,
BrowserWindow,
clipboard,
dialog,
IpcMainInvokeEvent,
nativeImage,
RelaunchOptions,
session,
shell
} from "electron";
import { mkdirSync, readFileSync, watch } from "fs";
import { open, readFile } from "fs/promises";
import { enableHardwareAcceleration } from "main";
import { release } from "os";
import { join } from "path";
import { debounce } from "shared/utils/debounce";
@@ -19,7 +30,7 @@ import { setBadgeCount } from "./appBadge";
import { autoStart } from "./autoStart";
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
import { mainWin } from "./mainWindow";
import { Settings } from "./settings";
import { Settings, State } from "./settings";
import { handle, handleSync } from "./utils/ipcWrappers";
import { PopoutWindows } from "./utils/popout";
import { isDeckGameMode, showGamePage } from "./utils/steamOS";
@@ -35,6 +46,7 @@ handleSync(IpcEvents.GET_RENDERER_CSS_FILE, () => join(__dirname, "renderer.css"
handleSync(IpcEvents.GET_SETTINGS, () => Settings.plain);
handleSync(IpcEvents.GET_VERSION, () => app.getVersion());
handleSync(IpcEvents.GET_ENABLE_HARDWARE_ACCELERATION, () => enableHardwareAcceleration);
handleSync(
IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY,
@@ -68,37 +80,34 @@ handle(IpcEvents.SHOW_ITEM_IN_FOLDER, (_, path) => {
shell.showItemInFolder(path);
});
function getWindow(e: IpcMainInvokeEvent, key?: string) {
return key ? PopoutWindows.get(key)! : (BrowserWindow.fromWebContents(e.sender) ?? mainWin);
}
handle(IpcEvents.FOCUS, () => {
mainWin.show();
mainWin.setSkipTaskbar(false);
});
handle(IpcEvents.CLOSE, (e, key?: string) => {
const popout = PopoutWindows.get(key!);
if (popout) return popout.close();
const win = BrowserWindow.fromWebContents(e.sender) ?? e.sender;
win.close();
getWindow(e, key).close();
});
handle(IpcEvents.MINIMIZE, e => {
mainWin.minimize();
handle(IpcEvents.MINIMIZE, (e, key?: string) => {
getWindow(e, key).minimize();
});
handle(IpcEvents.MAXIMIZE, e => {
if (mainWin.isMaximized()) {
mainWin.unmaximize();
handle(IpcEvents.MAXIMIZE, (e, key?: string) => {
const win = getWindow(e, key);
if (win.isMaximized()) {
win.unmaximize();
} else {
mainWin.maximize();
win.maximize();
}
});
handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => {
const ses = session.defaultSession;
const available = ses.availableSpellCheckerLanguages;
const applicable = languages.filter(l => available.includes(l)).slice(0, 3);
if (applicable.length) ses.setSpellCheckerLanguages(applicable);
handleSync(IpcEvents.SPELLCHECK_GET_AVAILABLE_LANGUAGES, e => {
e.returnValue = session.defaultSession.availableSpellCheckerLanguages;
});
handle(IpcEvents.SPELLCHECK_REPLACE_MISSPELLING, (e, word: string) => {
@@ -109,7 +118,14 @@ handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => {
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!, {
properties: ["openDirectory"]
});
@@ -118,7 +134,9 @@ handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
const dir = res.filePaths[0];
if (!isValidVencordInstall(dir)) return "invalid";
return dir;
State.store.vencordDir = dir;
return "ok";
});
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
@@ -130,6 +148,17 @@ handle(IpcEvents.CLIPBOARD_COPY_IMAGE, async (_, buf: ArrayBuffer, src: string)
});
});
function openDebugPage(page: string) {
const win = new BrowserWindow({
autoHideMenuBar: true
});
win.loadURL(page);
}
handle(IpcEvents.DEBUG_LAUNCH_GPU, () => openDebugPage("chrome://gpu"));
handle(IpcEvents.DEBUG_LAUNCH_WEBRTC_INTERNALS, () => openDebugPage("chrome://webrtc-internals"));
function readCss() {
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
}

61
src/main/ipcCommands.ts Normal file
View File

@@ -0,0 +1,61 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { randomUUID } from "crypto";
import { ipcMain } from "electron";
import { IpcEvents } from "shared/IpcEvents";
import { mainWin } from "./mainWindow";
const resolvers = new Map<string, Record<"resolve" | "reject", (data: any) => void>>();
export interface IpcMessage {
nonce: string;
message: string;
data?: any;
}
export interface IpcResponse {
nonce: string;
ok: boolean;
data?: any;
}
/**
* Sends a message to the renderer process and waits for a response.
* `data` must be serializable as it will be sent over IPC.
*
* You must add a handler for the message in the renderer process.
*/
export function sendRendererCommand<T = any>(message: string, data?: any) {
if (mainWin.isDestroyed()) {
console.warn("Main window is destroyed, cannot send IPC command:", message);
return Promise.reject(new Error("Main window is destroyed"));
}
const nonce = randomUUID();
const promise = new Promise<T>((resolve, reject) => {
resolvers.set(nonce, { resolve, reject });
});
mainWin.webContents.send(IpcEvents.IPC_COMMAND, { nonce, message, data });
return promise;
}
ipcMain.on(IpcEvents.IPC_COMMAND, (_event, { nonce, ok, data }: IpcResponse) => {
const resolver = resolvers.get(nonce);
if (!resolver) throw new Error(`Unknown message: ${nonce}`);
if (ok) {
resolver.resolve(data);
} else {
resolver.reject(data);
}
resolvers.delete(nonce);
});

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import {
@@ -12,11 +12,14 @@ import {
Menu,
MenuItemConstructorOptions,
nativeTheme,
screen,
session,
Tray
} from "electron";
import { EventEmitter } from "events";
import { rm } from "fs/promises";
import { join } from "path";
import { IpcEvents } from "shared/IpcEvents";
import { IpcCommands, IpcEvents } from "shared/IpcEvents";
import { isTruthy } from "shared/utils/guards";
import { once } from "shared/utils/once";
import type { SettingsStore } from "shared/utils/SettingsStore";
@@ -34,8 +37,10 @@ import {
MIN_WIDTH,
VENCORD_FILES_DIR
} from "./constants";
import { darwinURL } from "./index";
import { sendRendererCommand } from "./ipcCommands";
import { Settings, State, VencordSettings } from "./settings";
import { createSplashWindow } from "./splash";
import { createSplashWindow, updateSplashMessage } from "./splash";
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS";
import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader";
@@ -89,7 +94,7 @@ function initTray(win: BrowserWindow) {
click: createAboutWindow
},
{
label: "Update Vencord",
label: "Repair Vencord",
async click() {
await downloadVencordFiles();
app.relaunch();
@@ -106,14 +111,14 @@ function initTray(win: BrowserWindow) {
type: "separator"
},
{
label: "Relaunch",
label: "Restart",
click() {
app.relaunch();
app.quit();
}
},
{
label: "Quit Vesktop",
label: "Quit",
click() {
isQuitting = true;
app.quit();
@@ -196,9 +201,7 @@ function initMenuBar(win: BrowserWindow) {
label: "Settings",
accelerator: "CmdOrCtrl+,",
async click() {
mainWin.webContents.executeJavaScript(
"Vencord.Webpack.Common.SettingsRouter.open('My Account')"
);
sendRendererCommand(IpcCommands.NAVIGATE_SETTINGS);
}
},
{
@@ -243,7 +246,7 @@ function initMenuBar(win: BrowserWindow) {
}
] satisfies MenuItemList;
const menu = Menu.buildFromTemplate([
const menuItems = [
{
label: "Vesktop",
role: "appMenu",
@@ -252,8 +255,10 @@ function initMenuBar(win: BrowserWindow) {
{ role: "fileMenu" },
{ role: "editMenu" },
{ role: "viewMenu" },
{ role: "windowMenu" }
]);
isDarwin && { role: "windowMenu" }
] satisfies MenuItemList;
const menu = Menu.buildFromTemplate(menuItems.filter(isTruthy));
Menu.setApplicationMenu(menu);
}
@@ -269,7 +274,9 @@ function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
height: height ?? DEFAULT_HEIGHT
} as BrowserWindowConstructorOptions;
if (x != null && y != null) {
const storedDisplay = screen.getAllDisplays().find(display => display.id === State.store.displayId);
if (x != null && y != null && storedDisplay) {
options.x = x;
options.y = y;
}
@@ -295,7 +302,7 @@ function getDarwinOptions(): BrowserWindowConstructorOptions {
options.vibrancy = "sidebar";
options.backgroundColor = "#ffffff00";
} else {
if (splashTheming) {
if (splashTheming !== false) {
options.backgroundColor = splashBackground;
} else {
options.backgroundColor = nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff";
@@ -317,6 +324,7 @@ function initWindowBoundsListeners(win: BrowserWindow) {
const saveBounds = () => {
State.store.windowBounds = win.getBounds();
State.store.displayId = screen.getDisplayMatching(State.store.windowBounds).id;
};
win.on("resize", saveBounds);
@@ -328,6 +336,7 @@ function initSettingsListeners(win: BrowserWindow) {
if (enable) initTray(win);
else tray?.destroy();
});
addSettingsListener("disableMinSize", disable => {
if (disable) {
// 0 no work
@@ -356,12 +365,51 @@ function initSettingsListeners(win: BrowserWindow) {
addSettingsListener("enableMenu", enabled => {
win.setAutoHideMenuBar(enabled ?? false);
});
addSettingsListener("spellCheckLanguages", languages => initSpellCheckLanguages(win, languages));
}
async function initSpellCheckLanguages(win: BrowserWindow, languages?: string[]) {
languages ??= await sendRendererCommand(IpcCommands.GET_LANGUAGES);
if (!languages) return;
const ses = session.defaultSession;
const available = ses.availableSpellCheckerLanguages;
const applicable = languages.filter(l => available.includes(l)).slice(0, 5);
if (applicable.length) ses.setSpellCheckerLanguages(applicable);
}
function initSpellCheck(win: BrowserWindow) {
win.webContents.on("context-menu", (_, data) => {
win.webContents.send(IpcEvents.SPELLCHECK_RESULT, data.misspelledWord, data.dictionarySuggestions);
});
initSpellCheckLanguages(win, Settings.store.spellCheckLanguages);
}
function initDevtoolsListeners(win: BrowserWindow) {
win.webContents.on("devtools-opened", () => {
win.webContents.send(IpcEvents.DEVTOOLS_OPENED);
});
win.webContents.on("devtools-closed", () => {
win.webContents.send(IpcEvents.DEVTOOLS_CLOSED);
});
}
function initStaticTitle(win: BrowserWindow) {
const listener = (e: { preventDefault: Function }) => e.preventDefault();
if (Settings.store.staticTitle) win.on("page-title-updated", listener);
addSettingsListener("staticTitle", enabled => {
if (enabled) {
win.setTitle("Vesktop");
win.on("page-title-updated", listener);
} else {
win.off("page-title-updated", listener);
}
});
}
function createMainWindow() {
@@ -369,21 +417,27 @@ function createMainWindow() {
removeSettingsListeners();
removeVencordSettingsListeners();
const { staticTitle, transparencyOption, enableMenu, customTitleBar } = Settings.store;
const { staticTitle, transparencyOption, enableMenu, customTitleBar, splashTheming, splashBackground } =
Settings.store;
const { frameless, transparent } = VencordSettings.store;
const noFrame = frameless === true || customTitleBar === true;
const backgroundColor =
splashTheming !== false ? splashBackground : nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff";
const win = (mainWin = new BrowserWindow({
show: false,
show: Settings.store.enableSplashScreen === false,
backgroundColor,
webPreferences: {
nodeIntegration: false,
sandbox: false,
contextIsolation: true,
devTools: true,
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,
frame: !noFrame,
@@ -408,6 +462,7 @@ function createMainWindow() {
autoHideMenuBar: enableMenu
}));
win.setMenuBarVisibility(false);
if (process.platform === "darwin" && customTitleBar) win.setWindowButtonVisibility(false);
win.on("close", e => {
const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false;
@@ -421,44 +476,68 @@ function createMainWindow() {
return false;
});
if (Settings.store.staticTitle) win.on("page-title-updated", e => e.preventDefault());
initWindowBoundsListeners(win);
if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win);
initMenuBar(win);
makeLinksOpenExternally(win);
initSettingsListeners(win);
initSpellCheck(win);
initDevtoolsListeners(win);
initStaticTitle(win);
win.webContents.setUserAgent(BrowserUserAgent);
const subdomain =
Settings.store.discordBranch === "canary" || Settings.store.discordBranch === "ptb"
? `${Settings.store.discordBranch}.`
: "";
win.loadURL(`https://${subdomain}discord.com/app`);
// if the open-url event is fired (in index.ts) while starting up, darwinURL will be set. If not fall back to checking the process args (which Windows and Linux use for URI calling.)
// win.webContents.session.clearCache().then(() => {
loadUrl(darwinURL || process.argv.find(arg => arg.startsWith("discord://")));
// });
return win;
}
const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDesktopMain.js")));
const loadEvents = new EventEmitter();
export function loadUrl(uri: string | undefined) {
const branch = Settings.store.discordBranch;
const subdomain = branch === "canary" || branch === "ptb" ? `${branch}.` : "";
// we do not rely on 'did-finish-load' because it fires even if loadURL fails which triggers early detruction of the splash
mainWin
.loadURL(`https://${subdomain}discord.com/${uri ? new URL(uri).pathname.slice(1) || "app" : "app"}`)
.then(() => loadEvents.emit("app-loaded"))
.catch(error => retryUrl(error.url, error.code));
}
const retryDelay = 1000;
function retryUrl(url: string, description: string) {
console.log(`retrying in ${retryDelay}ms`);
updateSplashMessage(`Failed to load Discord: ${description}`);
setTimeout(() => loadUrl(url), retryDelay);
}
export async function createWindows() {
const startMinimized = process.argv.includes("--start-minimized");
const splash = createSplashWindow(startMinimized);
// SteamOS letterboxes and scales it terribly, so just full screen it
if (isDeckGameMode) splash.setFullScreen(true);
let splash: BrowserWindow | undefined;
if (Settings.store.enableSplashScreen !== false) {
splash = createSplashWindow(startMinimized);
// SteamOS letterboxes and scales it terribly, so just full screen it
if (isDeckGameMode) splash.setFullScreen(true);
}
await ensureVencordFiles();
runVencordMain();
mainWin = createMainWindow();
mainWin.webContents.on("did-finish-load", () => {
splash.destroy();
loadEvents.on("app-loaded", () => {
splash?.destroy();
if (!startMinimized) {
mainWin!.show();
if (splash) mainWin!.show();
if (State.store.maximized && !isDeckGameMode) mainWin!.maximize();
}
@@ -476,5 +555,15 @@ export async function createWindows() {
});
});
mainWin.webContents.on("did-navigate", (_, url: string, responseCode: number) => {
updateSplashMessage(""); // clear the splash message
// check url to ensure app doesn't loop
if (responseCode >= 300 && new URL(url).pathname !== `/app`) {
loadUrl(undefined);
console.warn(`'did-navigate': Caught bad page response: ${responseCode}, redirecting to main app`);
}
});
initArRPC();
}

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { session, systemPreferences } from "electron";
@@ -12,11 +12,13 @@ export function registerMediaPermissionsHandler() {
session.defaultSession.setPermissionRequestHandler(async (_webContents, permission, callback, details) => {
let granted = true;
if (details.mediaTypes?.includes("audio")) {
granted = await systemPreferences.askForMediaAccess("microphone");
}
if (details.mediaTypes?.includes("video")) {
granted &&= await systemPreferences.askForMediaAccess("camera");
if ("mediaTypes" in details) {
if (details.mediaTypes?.includes("audio")) {
granted &&= await systemPreferences.askForMediaAccess("microphone");
}
if (details.mediaTypes?.includes("video")) {
granted &&= await systemPreferences.askForMediaAccess("camera");
}
}
callback(granted);

View File

@@ -1,13 +1,14 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { desktopCapturer, session, Streams } from "electron";
import type { StreamPick } from "renderer/components/ScreenSharePicker";
import { IpcEvents } from "shared/IpcEvents";
import { IpcCommands, IpcEvents } from "shared/IpcEvents";
import { sendRendererCommand } from "./ipcCommands";
import { handle } from "./utils/ipcWrappers";
const isWayland =
@@ -49,11 +50,11 @@ export function registerScreenShareHandler() {
if (isWayland) {
const video = data[0];
if (video) {
const stream = await request.frame
.executeJavaScript(
`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify([video])},true)`
)
.catch(() => null);
const stream = await sendRendererCommand<StreamPick>(IpcCommands.SCREEN_SHARE_PICKER, {
screens: [video],
skipPicker: true
}).catch(() => null);
if (stream === null) return callback({});
}
@@ -61,13 +62,13 @@ export function registerScreenShareHandler() {
return;
}
const choice = await request.frame
.executeJavaScript(`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`)
.then(e => e as StreamPick)
.catch(e => {
console.error("Error during screenshare picker", e);
return null;
});
const choice = await sendRendererCommand<StreamPick>(IpcCommands.SCREEN_SHARE_PICKER, {
screens: data,
skipPicker: false
}).catch(e => {
console.error("Error during screenshare picker", e);
return null;
});
if (!choice) return callback({});

View File

@@ -1,10 +1,10 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join } from "path";
import type { Settings as TSettings, State as TState } from "shared/settings";
import { SettingsStore } from "shared/utils/SettingsStore";
@@ -35,30 +35,5 @@ function loadSettings<T extends object = any>(file: string, name: string) {
}
export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vesktop settings");
if (Object.hasOwn(Settings.plain, "discordWindowsTitleBar")) {
Settings.plain.customTitleBar = Settings.plain.discordWindowsTitleBar;
delete Settings.plain.discordWindowsTitleBar;
Settings.markAsChanged();
}
export const VencordSettings = loadSettings<any>(VENCORD_SETTINGS_FILE, "Vencord settings");
if (Object.hasOwn(Settings.plain, "firstLaunch") && !existsSync(STATE_FILE)) {
console.warn("legacy state in settings.json detected. migrating to state.json");
const state = {} as TState;
for (const prop of [
"firstLaunch",
"maximized",
"minimized",
"skippedUpdate",
"steamOSLayoutVersion",
"windowBounds"
] as const) {
state[prop] = Settings.plain[prop];
delete Settings.plain[prop];
}
Settings.markAsChanged();
writeFileSync(STATE_FILE, JSON.stringify(state, null, 4));
}
export const State = loadSettings<TState>(STATE_FILE, "Vesktop state");

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { BrowserWindow } from "electron";
@@ -11,18 +11,23 @@ import { ICON_PATH, VIEW_DIR } from "shared/paths";
import { Settings } from "./settings";
let splash: BrowserWindow | undefined;
export function createSplashWindow(startMinimized = false) {
const splash = new BrowserWindow({
splash = new BrowserWindow({
...SplashProps,
icon: ICON_PATH,
show: !startMinimized
show: !startMinimized,
webPreferences: {
preload: join(__dirname, "splashPreload.js")
}
});
splash.loadFile(join(VIEW_DIR, "splash.html"));
const { splashBackground, splashColor, splashTheming } = Settings.store;
if (splashTheming) {
if (splashTheming !== false) {
if (splashColor) {
const semiTransparentSplashColor = splashColor.replace("rgb(", "rgba(").replace(")", ", 0.2)");
@@ -37,3 +42,7 @@ export function createSplashWindow(startMinimized = false) {
return splash;
}
export function updateSplashMessage(message: string) {
if (splash && !splash.isDestroyed()) splash.webContents.send("update-splash-message", message);
}

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { createWriteStream } from "fs";

View File

@@ -1,30 +1,40 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { ipcMain, IpcMainEvent, IpcMainInvokeEvent, WebFrameMain } from "electron";
import { DISCORD_HOSTNAMES } from "main/constants";
import { IpcEvents } from "shared/IpcEvents";
export function validateSender(frame: WebFrameMain) {
const { hostname, protocol } = new URL(frame.url);
export function validateSender(frame: WebFrameMain | null, event: string) {
if (!frame) throw new Error(`ipc[${event}]: No sender frame`);
if (!frame.url) return;
try {
var { hostname, protocol } = new URL(frame.url);
} catch (e) {
throw new Error(`ipc[${event}]: Invalid URL ${frame.url}`);
}
if (protocol === "file:") return;
if (!DISCORD_HOSTNAMES.includes(hostname)) throw new Error("ipc: Disallowed host " + hostname);
if (!DISCORD_HOSTNAMES.includes(hostname)) {
throw new Error(`ipc[${event}]: Disallowed hostname ${hostname}`);
}
}
export function handleSync(event: IpcEvents, cb: (e: IpcMainEvent, ...args: any[]) => any) {
ipcMain.on(event, (e, ...args) => {
validateSender(e.senderFrame);
validateSender(e.senderFrame, event);
e.returnValue = cb(e, ...args);
});
}
export function handle(event: IpcEvents, cb: (e: IpcMainInvokeEvent, ...args: any[]) => any) {
ipcMain.handle(event, (e, ...args) => {
validateSender(e.senderFrame);
validateSender(e.senderFrame, event);
return cb(e, ...args);
});
}

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { BrowserWindow, shell } from "electron";
@@ -50,7 +50,7 @@ export function handleExternalUrl(url: string, protocol?: string): { action: "de
export function makeLinksOpenExternally(win: BrowserWindow) {
win.webContents.setWindowOpenHandler(({ url, frameName, features }) => {
try {
var { protocol, hostname, pathname } = new URL(url);
var { protocol, hostname, pathname, searchParams } = new URL(url);
} catch {
return { action: "deny" };
}
@@ -59,8 +59,10 @@ export function makeLinksOpenExternally(win: BrowserWindow) {
return createOrFocusPopup(frameName, features);
}
if (url === "about:blank" || (frameName === "authorize" && DISCORD_HOSTNAMES.includes(hostname)))
return { action: "allow" };
if (url === "about:blank") return { action: "allow" };
// Drop the static temp page Discord web loads for the connections popout
if (frameName === "authorize" && searchParams.get("loading") === "true") return { action: "deny" };
return handleExternalUrl(url, protocol);
});

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { BrowserWindow, BrowserWindowConstructorOptions } from "electron";

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { BrowserWindow, dialog } from "electron";

View File

@@ -1,10 +1,11 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { existsSync, mkdirSync } from "fs";
import { mkdirSync } from "fs";
import { access, constants as FsConstants, writeFile } from "fs/promises";
import { join } from "path";
import { USER_AGENT, VENCORD_FILES_DIR } from "../constants";
@@ -56,13 +57,20 @@ export async function downloadVencordFiles() {
);
}
export function isValidVencordInstall(dir: string) {
return FILES_TO_DOWNLOAD.every(f => existsSync(join(dir, f)));
const existsAsync = (path: string) =>
access(path, FsConstants.F_OK)
.then(() => true)
.catch(() => false);
export async function isValidVencordInstall(dir: string) {
const results = await Promise.all(["package.json", ...FILES_TO_DOWNLOAD].map(f => existsAsync(join(dir, f))));
return !results.includes(false);
}
export async function ensureVencordFiles() {
if (isValidVencordInstall(VENCORD_FILES_DIR)) return;
if (await isValidVencordInstall(VENCORD_FILES_DIR)) return;
mkdirSync(VENCORD_FILES_DIR, { recursive: true });
await downloadVencordFiles();
await Promise.all([downloadVencordFiles(), writeFile(join(VENCORD_FILES_DIR, "package.json"), "{}")]);
}

View File

@@ -1,16 +1,16 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import type { PatchBay as PatchBayType } from "@vencord/venmic";
import type { LinkData, Node, PatchBay as PatchBayType } from "@vencord/venmic";
import { app, ipcMain } from "electron";
import { join } from "path";
import { IpcEvents } from "shared/IpcEvents";
import { STATIC_DIR } from "shared/paths";
type LinkData = Parameters<PatchBayType["link"]>[0];
import { Settings } from "./settings";
let PatchBay: typeof PatchBayType | undefined;
let patchBayInstance: PatchBayType | undefined;
@@ -69,47 +69,64 @@ function getRendererAudioServicePid() {
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
const audioPid = getRendererAudioServicePid();
const list = obtainVenmic()
?.list()
.filter(s => s["application.process.id"] !== audioPid)
.map(s => s["application.name"]);
const { granularSelect } = Settings.store.audio ?? {};
const uniqueTargets = [...new Set(list)];
const targets = obtainVenmic()
?.list(granularSelect ? ["node.name"] : 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 { ignoreDevices, ignoreInputMedia, ignoreVirtual, workaround } = Settings.store.audio ?? {};
const data: LinkData = {
include: targets.map(target => ({ key: "application.name", value: target })),
exclude: [{ key: "application.process.id", value: pid }]
include,
exclude: [{ "application.process.id": pid }],
ignore_devices: ignoreDevices
};
if (ignoreInputMedia ?? true) {
data.exclude.push({ "media.class": "Stream/Input/Audio" });
}
if (ignoreVirtual) {
data.exclude.push({ "node.virtual": "true" });
}
if (workaround) {
data.workaround = [
{ key: "application.process.id", value: pid },
{ key: "media.name", value: "RecordStream" }
];
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
}
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 { workaround, ignoreDevices, ignoreInputMedia, ignoreVirtual, onlySpeakers, onlyDefaultSpeakers } =
Settings.store.audio ?? {};
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
};
if (ignoreInputMedia ?? true) {
data.exclude.push({ "media.class": "Stream/Input/Audio" });
}
if (ignoreVirtual) {
data.exclude.push({ "node.virtual": "true" });
}
if (workaround) {
data.workaround = [
{ key: "application.process.id", value: pid },
{ key: "media.name", value: "RecordStream" }
];
data.workaround = [{ "application.process.id": pid, "media.name": "RecordStream" }];
}
return obtainVenmic()?.link(data);

10
src/module.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vesktop contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
declare module "__patches__" {
const never: never;
export default never;
}

View File

@@ -1,12 +1,13 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Node } from "@vencord/venmic";
import { ipcRenderer } from "electron";
import { IpcMessage, IpcResponse } from "main/ipcCommands";
import type { Settings } from "shared/settings";
import type { LiteralUnion } from "type-fest";
import { IpcEvents } from "../shared/IpcEvents";
import { invoke, sendSync } from "./typedIpc";
@@ -19,12 +20,19 @@ ipcRenderer.on(IpcEvents.SPELLCHECK_RESULT, (_, w: string, s: string[]) => {
spellCheckCallbacks.forEach(cb => cb(w, s));
});
let onDevtoolsOpen = () => {};
let onDevtoolsClose = () => {};
ipcRenderer.on(IpcEvents.DEVTOOLS_OPENED, () => onDevtoolsOpen());
ipcRenderer.on(IpcEvents.DEVTOOLS_CLOSED, () => onDevtoolsClose());
export const VesktopNative = {
app: {
relaunch: () => invoke<void>(IpcEvents.RELAUNCH),
getVersion: () => sendSync<void>(IpcEvents.GET_VERSION),
setBadgeCount: (count: number) => invoke<void>(IpcEvents.SET_BADGE_COUNT, count),
supportsWindowsTransparency: () => sendSync<boolean>(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY)
supportsWindowsTransparency: () => sendSync<boolean>(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY),
getEnableHardwareAcceleration: () => sendSync<boolean>(IpcEvents.GET_ENABLE_HARDWARE_ACCELERATION)
},
autostart: {
isEnabled: () => sendSync<boolean>(IpcEvents.AUTOSTART_ENABLED),
@@ -33,14 +41,15 @@ export const VesktopNative = {
},
fileManager: {
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: {
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),
set: (settings: Settings, path?: string) => invoke<void>(IpcEvents.SET_SETTINGS, settings, path)
},
spellcheck: {
setLanguages: (languages: readonly string[]) => invoke<void>(IpcEvents.SPELLCHECK_SET_LANGUAGES, languages),
getAvailableLanguages: () => sendSync<string[]>(IpcEvents.SPELLCHECK_GET_AVAILABLE_LANGUAGES),
onSpellcheckResult(cb: SpellCheckerResultCallback) {
spellCheckCallbacks.add(cb);
},
@@ -53,8 +62,12 @@ export const VesktopNative = {
win: {
focus: () => invoke<void>(IpcEvents.FOCUS),
close: (key?: string) => invoke<void>(IpcEvents.CLOSE, key),
minimize: () => invoke<void>(IpcEvents.MINIMIZE),
maximize: () => invoke<void>(IpcEvents.MAXIMIZE)
minimize: (key?: string) => invoke<void>(IpcEvents.MINIMIZE, key),
maximize: (key?: string) => invoke<void>(IpcEvents.MAXIMIZE, key),
setDevtoolsCallbacks: (onOpen: () => void, onClose: () => void) => {
onDevtoolsOpen = onOpen;
onDevtoolsClose = onClose;
}
},
capturer: {
getLargeThumbnail: (id: string) => invoke<string>(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, id)
@@ -63,20 +76,24 @@ export const VesktopNative = {
virtmic: {
list: () =>
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),
start: (targets: string[], workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START, targets, workaround),
startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) =>
invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers),
start: (include: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START, include),
startSystem: (exclude: Node[]) => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, exclude),
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
},
arrpc: {
onActivity(cb: (data: string) => void) {
ipcRenderer.on(IpcEvents.ARRPC_ACTIVITY, (_, data: string) => cb(data));
}
},
clipboard: {
copyImage: (imageBuffer: Uint8Array, imageSrc: string) =>
invoke<void>(IpcEvents.CLIPBOARD_COPY_IMAGE, imageBuffer, imageSrc)
},
debug: {
launchGpu: () => invoke<void>(IpcEvents.DEBUG_LAUNCH_GPU),
launchWebrtcInternals: () => invoke<void>(IpcEvents.DEBUG_LAUNCH_WEBRTC_INTERNALS)
},
commands: {
onCommand(cb: (message: IpcMessage) => void) {
ipcRenderer.on(IpcEvents.IPC_COMMAND, (_, message) => cb(message));
},
respond: (response: IpcResponse) => ipcRenderer.send(IpcEvents.IPC_COMMAND, response)
}
};

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { contextBridge, ipcRenderer, webFrame } from "electron";
@@ -40,5 +40,3 @@ if (IS_DEV) {
});
}
// #endregion
VesktopNative.spellcheck.setLanguages(window.navigator.languages);

13
src/preload/splash.ts Normal file
View File

@@ -0,0 +1,13 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vesktop contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { contextBridge, ipcRenderer } from "electron";
contextBridge.exposeInMainWorld("VesktopSplashNative", {
onUpdateMessage(callback: (message: string) => void) {
ipcRenderer.on("update-splash-message", (_, message: string) => callback(message));
}
});

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { ipcRenderer } from "electron";

View File

@@ -1,12 +1,13 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { filters, waitFor } from "@vencord/types/webpack";
import { RelationshipStore } from "@vencord/types/webpack/common";
import { VesktopLogger } from "./logger";
import { Settings } from "./settings";
let GuildReadStateStore: any;
@@ -26,7 +27,7 @@ export function setBadge() {
VesktopNative.app.setBadgeCount(totalCount);
} catch (e) {
console.error(e);
VesktopLogger.error("Failed to update badge count", e);
}
}

68
src/renderer/arrpc.ts Normal file
View File

@@ -0,0 +1,68 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Logger } from "@vencord/types/utils";
import { findLazy, findStoreLazy, onceReady } from "@vencord/types/webpack";
import { FluxDispatcher, InviteActions } from "@vencord/types/webpack/common";
import { IpcCommands } from "shared/IpcEvents";
import { onIpcCommand } from "./ipcCommands";
import { Settings } from "./settings";
const logger = new Logger("VesktopRPC", "#5865f2");
const StreamerModeStore = findStoreLazy("StreamerModeStore");
const arRPC = Vencord.Plugins.plugins["WebRichPresence (arRPC)"] as any as {
handleEvent(e: MessageEvent): void;
};
onIpcCommand(IpcCommands.RPC_ACTIVITY, async jsonData => {
if (!Settings.store.arRPC) return;
await onceReady;
const data = JSON.parse(jsonData);
if (data.socketId === "STREAMERMODE" && StreamerModeStore.autoToggle) {
FluxDispatcher.dispatch({
type: "STREAMER_MODE_UPDATE",
key: "enabled",
value: data.activity?.application_id === "STREAMERMODE"
});
return;
}
arRPC.handleEvent(new MessageEvent("message", { data: jsonData }));
});
onIpcCommand(IpcCommands.RPC_INVITE, async code => {
const { invite } = await InviteActions.resolveInvite(code, "Desktop Modal");
if (!invite) return false;
VesktopNative.win.focus();
FluxDispatcher.dispatch({
type: "INVITE_MODAL_OPEN",
invite,
code,
context: "APP"
});
return true;
});
const { DEEP_LINK } = findLazy(m => m.DEEP_LINK?.handler);
onIpcCommand(IpcCommands.RPC_DEEP_LINK, async data => {
logger.debug("Opening deep link:", data);
try {
DEEP_LINK.handler({ args: data });
return true;
} catch (err) {
logger.error("Failed to open deep link:", err);
return false;
}
});

9
src/renderer/common.ts Normal file
View File

@@ -0,0 +1,9 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vesktop contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { findStoreLazy } from "@vencord/types/webpack";
export const MediaEngineStore = findStoreLazy("MediaEngineStore");

View File

@@ -1,13 +1,13 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./screenSharePicker.css";
import { closeModal, Logger, Margins, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils";
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
import { closeModal, Logger, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils";
import { onceReady } from "@vencord/types/webpack";
import {
Button,
Card,
@@ -19,26 +19,36 @@ import {
UserStore,
useState
} from "@vencord/types/webpack/common";
import { Node } from "@vencord/venmic";
import type { Dispatch, SetStateAction } from "react";
import { MediaEngineStore } from "renderer/common";
import { addPatch } from "renderer/patches/shared";
import { isLinux, isWindows } from "renderer/utils";
import { State, useSettings, useVesktopState } from "renderer/settings";
import { classNameFactory, isLinux, isWindows } from "renderer/utils";
const StreamResolutions = ["480", "720", "1080", "1440"] as const;
const StreamResolutions = ["480", "720", "1080", "1440", "2160"] as const;
const StreamFps = ["15", "30", "60"] as const;
const MediaEngineStore = findStoreLazy("MediaEngineStore");
const cl = classNameFactory("vcd-screen-picker-");
export type StreamResolution = (typeof StreamResolutions)[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 {
resolution: StreamResolution;
fps: StreamFps;
audio: boolean;
audioSource?: string;
contentHint?: string;
workaround?: boolean;
onlyDefaultSpeakers?: boolean;
includeSources?: AudioSources;
excludeSources?: AudioSources;
}
export interface StreamPick extends StreamSettings {
@@ -63,27 +73,14 @@ addPatch({
match: /this.localWant=/,
replace: "$self.patchStreamQuality(this);$&"
}
},
{
find: "x-google-max-bitrate",
replacement: [
{
// eslint-disable-next-line no-useless-escape
match: /"x-google-max-bitrate=".concat\(\i\)/,
replace: '"x-google-max-bitrate=".concat("80_000")'
},
{
match: /;level-asymmetry-allowed=1/,
replace: ";b=AS:800000;level-asymmetry-allowed=1"
}
]
}
],
patchStreamQuality(opts: any) {
if (!currentSettings) return;
const { screenshareQuality } = State.store;
if (!screenshareQuality) return;
const framerate = Number(currentSettings.fps);
const height = Number(currentSettings.resolution);
const framerate = Number(screenshareQuality.frameRate);
const height = Number(screenshareQuality.resolution);
const width = Math.round(height * (16 / 9));
Object.assign(opts, {
@@ -132,13 +129,17 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) {
modalProps={props}
submit={async v => {
didSubmit = true;
if (v.audioSource && v.audioSource !== "None") {
if (v.audioSource === "Entire System") {
await VesktopNative.virtmic.startSystem(v.workaround);
if (v.includeSources && v.includeSources !== "None") {
if (v.includeSources === "Entire System") {
await VesktopNative.virtmic.startSystem(
!v.excludeSources || isSpecialSource(v.excludeSources) ? [] : v.excludeSources
);
} else {
await VesktopNative.virtmic.start([v.audioSource], v.workaround);
await VesktopNative.virtmic.start(v.includeSources);
}
}
resolve(v);
}}
close={() => {
@@ -160,20 +161,188 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) {
function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScreen: (id: string) => void }) {
return (
<div className="vcd-screen-picker-grid">
<div className={cl("screen-grid")}>
{screens.map(({ id, name, url }) => (
<label key={id}>
<input type="radio" name="screen" value={id} onChange={() => chooseScreen(id)} />
<label key={id} className={cl("screen-label")}>
<input
type="radio"
className={cl("screen-radio")}
name="screen"
value={id}
onChange={() => chooseScreen(id)}
/>
<img src={url} alt="" />
<Text variant="text-sm/normal">{name}</Text>
<Text className={cl("screen-name")} variant="text-sm/normal">
{name}
</Text>
</label>
))}
</div>
);
}
function StreamSettings({
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={cl("header")}>
<Forms.FormTitle tag="h2" className={cl("header-title")}>
Venmic Settings
</Forms.FormTitle>
<Modals.ModalCloseButton onClick={close} />
</Modals.ModalHeader>
<Modals.ModalContent className={cl("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,
deviceSelect: v ? false : Settings.audio?.deviceSelect
})
}
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>
<Switch
hideBorder
onChange={value => {
Settings.audio = { ...Settings.audio, deviceSelect: value };
setAudioSources("None");
}}
value={Settings.audio?.deviceSelect ?? false}
disabled={Settings.audio?.ignoreDevices}
note={
<>
Allow to select devices such as microphones. Requires <b>Ignore Devices</b> to be turned
off.
</>
}
>
Device Selection
</Switch>
</Modals.ModalContent>
<Modals.ModalFooter className={cl("footer")}>
<Button color={Button.Colors.TRANSPARENT} onClick={close}>
Back
</Button>
</Modals.ModalFooter>
</Modals.ModalRoot>
);
}
function OptionRadio<Settings extends object, Key extends keyof Settings>(props: {
options: Array<string> | ReadonlyArray<string>;
labels?: Array<string>;
settings: Settings;
settingsKey: Key;
onChange: (option: string) => void;
}) {
const { options, settings, settingsKey, labels, onChange } = props;
return (
<div className={cl("option-radios")}>
{(options as string[]).map((option, idx) => (
<label className={cl("option-radio")} data-checked={settings[settingsKey] === option} key={option}>
<Text variant="text-sm/bold">{labels?.[idx] ?? option}</Text>
<input
className={cl("option-input")}
type="radio"
name="fps"
value={option}
checked={settings[settingsKey] === option}
onChange={() => onChange(option)}
/>
</label>
))}
</div>
);
}
function StreamSettingsUi({
source,
settings,
setSettings,
@@ -184,6 +353,9 @@ function StreamSettings({
setSettings: Dispatch<SetStateAction<StreamSettings>>;
skipPicker: boolean;
}) {
const Settings = useSettings();
const qualitySettings = State.store.screenshareQuality!;
const [thumb] = useAwaiter(
() => (skipPicker ? Promise.resolve(source.url) : VesktopNative.capturer.getLargeThumbnail(source.id)),
{
@@ -192,227 +364,318 @@ function StreamSettings({
}
);
const openSettings = () => {
const key = openModal(props => (
<AudioSettingsModal
modalProps={props}
close={() => props.onClose()}
setAudioSources={sources =>
setSettings(s => ({ ...s, includeSources: sources, excludeSources: sources }))
}
/>
));
};
return (
<div className="vcd-screen-picker-settings-grid">
<div>
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
<Card className="vcd-screen-picker-card vcd-screen-picker-preview">
<img src={thumb} alt="" />
<Text variant="text-sm/normal">{source.name}</Text>
</Card>
<div>
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
<Card className={cl("card", "preview")}>
<img src={thumb} alt="" className={cl(isLinux ? "preview-img-linux" : "preview-img")} />
<Text variant="text-sm/normal">{source.name}</Text>
</Card>
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
<Card className="vcd-screen-picker-card">
<div className="vcd-screen-picker-quality">
<section>
<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>
))}
<Card className={cl("card")}>
<div className={cl("quality")}>
<section className={cl("quality-section")}>
<Forms.FormTitle>Resolution</Forms.FormTitle>
<OptionRadio
options={StreamResolutions}
settings={qualitySettings}
settingsKey="resolution"
onChange={value => (qualitySettings.resolution = value)}
/>
</section>
<section className={cl("quality-section")}>
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
<OptionRadio
options={StreamFps}
settings={qualitySettings}
settingsKey="frameRate"
onChange={value => (qualitySettings.frameRate = value)}
/>
</section>
</div>
<div className={cl("quality")}>
<section className={cl("quality-section")}>
<Forms.FormTitle>Content Type</Forms.FormTitle>
<div>
<OptionRadio
options={["motion", "detail"]}
labels={["Prefer Smoothness", "Prefer Clarity"]}
settings={settings}
settingsKey="contentHint"
onChange={option => setSettings(s => ({ ...s, contentHint: option }))}
/>
<div className={cl("hint-description")}>
<p>
Choosing "Prefer Clarity" will result in a significantly lower framerate in exchange
for a much sharper and clearer image.
</p>
</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>
</section>
</div>
</Card>
</div>
<div>
{isWindows && (
<Switch
value={settings.audio}
onChange={checked => setSettings(s => ({ ...s, audio: checked }))}
hideBorder
className="vcd-screen-picker-audio"
>
Stream With Audio
</Switch>
)}
</div>
{isWindows && (
<Switch
value={settings.audio}
onChange={checked => setSettings(s => ({ ...s, audio: checked }))}
hideBorder
className={cl("audio")}
>
Stream With Audio
</Switch>
)}
</section>
</div>
{isLinux && (
<AudioSourcePickerLinux
audioSource={settings.audioSource}
workaround={settings.workaround}
onlyDefaultSpeakers={settings.onlyDefaultSpeakers}
setAudioSource={source => setSettings(s => ({ ...s, audioSource: source }))}
setWorkaround={value => setSettings(s => ({ ...s, workaround: value }))}
setOnlyDefaultSpeakers={value => setSettings(s => ({ ...s, onlyDefaultSpeakers: value }))}
openSettings={openSettings}
includeSources={settings.includeSources}
excludeSources={settings.excludeSources}
deviceSelect={Settings.audio?.deviceSelect}
granularSelect={Settings.audio?.granularSelect}
setIncludeSources={sources => setSettings(s => ({ ...s, includeSources: sources }))}
setExcludeSources={sources => setSettings(s => ({ ...s, excludeSources: sources }))}
/>
)}
</div>
</Card>
</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, deviceSelect?: boolean): AudioItem[] {
if (isSpecialSource(node)) {
return [{ name: node, value: node }];
}
const rtn: AudioItem[] = [];
const mediaClass = node["media.class"];
if (mediaClass?.includes("Video") || mediaClass?.includes("Midi")) {
return rtn;
}
if (!deviceSelect && node["device.id"]) {
return rtn;
}
const name = node["application.name"];
if (name) {
rtn.push({ name: name, value: { "application.name": name } });
}
if (!granularSelect) {
return rtn;
}
const rawName = node["node.name"];
if (!name) {
rtn.push({ name: rawName, value: { "node.name": rawName } });
}
const binary = node["application.process.binary"];
if (!name && binary) {
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;
if (pid) {
rtn.push({
name: `${first.name} (${pid})`,
value: { ...firstValues, "application.process.id": pid }
});
}
const mediaName = node["media.name"];
if (mediaName) {
rtn.push({
name: `${first.name} [${mediaName}]`,
value: { ...firstValues, "media.name": mediaName }
});
}
if (mediaClass) {
rtn.push({
name: `${first.name} [${mediaClass}]`,
value: { ...firstValues, "media.class": mediaClass }
});
}
return rtn;
}
function 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({
audioSource,
workaround,
onlyDefaultSpeakers,
setAudioSource,
setWorkaround,
setOnlyDefaultSpeakers
includeSources,
excludeSources,
deviceSelect,
granularSelect,
openSettings,
setIncludeSources,
setExcludeSources
}: {
audioSource?: string;
workaround?: boolean;
onlyDefaultSpeakers?: boolean;
setAudioSource(s: string): void;
setWorkaround(b: boolean): void;
setOnlyDefaultSpeakers(b: boolean): void;
includeSources?: AudioSources;
excludeSources?: AudioSources;
deviceSelect?: boolean;
granularSelect?: boolean;
openSettings: () => void;
setIncludeSources: (s: AudioSources) => void;
setExcludeSources: (s: AudioSources) => void;
}) {
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
});
const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null;
const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true;
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, deviceSelect))
.flat()
.filter(uniqueName)
: [];
return (
<>
<Forms.FormTitle>Audio Settings</Forms.FormTitle>
<Card className="vcd-screen-picker-card">
{loading ? (
<Forms.FormTitle>Loading Audio Sources...</Forms.FormTitle>
) : (
<Forms.FormTitle>Audio Source</Forms.FormTitle>
)}
{!sources.ok && sources.isGlibCxxOutdated && (
<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>
)}
{hasPipewirePulse || ignorePulseWarning ? (
allSources && (
<div className={cl({ quality: includeSources === "Entire System" })}>
<section>
<Forms.FormTitle>{loading ? "Loading Sources..." : "Audio Sources"}</Forms.FormTitle>
<Select
options={allSources.map(({ name, value }) => ({
label: name,
value: value,
default: name === "None"
}))}
isSelected={isItemSelected(includeSources)}
select={updateItems(setIncludeSources, includeSources)}
serialize={String}
popoutPosition="top"
closeOnSelect={false}
/>
</section>
{includeSources === "Entire System" && (
<section>
<Forms.FormTitle>Exclude Sources</Forms.FormTitle>
<Select
options={allSources.map(s => ({ label: s, value: s, default: s === "None" }))}
isSelected={s => s === audioSource}
select={setAudioSource}
options={allSources
.filter(x => x.name !== "Entire System")
.map(({ name, value }) => ({
label: name,
value: value,
default: name === "None"
}))}
isSelected={isItemSelected(excludeSources)}
select={updateItems(setExcludeSources, excludeSources)}
serialize={String}
popoutPosition="top"
closeOnSelect={false}
/>
)
) : (
<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>
</section>
)}
<Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} />
<Switch
onChange={setWorkaround}
value={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={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>
</div>
<Button color={Button.Colors.TRANSPARENT} onClick={openSettings} className={cl("settings-button")}>
Open Audio Settings
</Button>
</>
);
}
@@ -432,23 +695,26 @@ function ModalComponent({
}) {
const [selected, setSelected] = useState<string | undefined>(skipPicker ? screens[0].id : void 0);
const [settings, setSettings] = useState<StreamSettings>({
resolution: "1080",
fps: "60",
contentHint: "motion",
audio: true
audio: true,
includeSources: "None"
});
const qualitySettings = (useVesktopState().screenshareQuality ??= {
resolution: "720",
frameRate: "30"
});
return (
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
<Modals.ModalHeader className="vcd-screen-picker-header">
<Modals.ModalHeader className={cl("header")}>
<Forms.FormTitle tag="h2">ScreenShare</Forms.FormTitle>
<Modals.ModalCloseButton onClick={close} />
</Modals.ModalHeader>
<Modals.ModalContent className="vcd-screen-picker-modal">
<Modals.ModalContent className={cl("modal")}>
{!selected ? (
<ScreenPicker screens={screens} chooseScreen={setSelected} />
) : (
<StreamSettings
<StreamSettingsUi
source={screens.find(s => s.id === selected)!}
settings={settings}
setSettings={setSettings}
@@ -456,14 +722,14 @@ function ModalComponent({
/>
)}
</Modals.ModalContent>
<Modals.ModalFooter className="vcd-screen-picker-footer">
<Modals.ModalFooter className={cl("footer")}>
<Button
disabled={!selected}
onClick={() => {
currentSettings = settings;
try {
const frameRate = Number(settings.fps);
const height = Number(settings.resolution);
const frameRate = Number(qualitySettings.frameRate);
const height = Number(qualitySettings.resolution);
const width = Math.round(height * (16 / 9));
const conn = [...MediaEngineStore.getMediaEngine().connections].find(
@@ -491,7 +757,7 @@ function ModalComponent({
const constraints = {
...track.getConstraints(),
frameRate,
frameRate: { min: frameRate, ideal: frameRate },
width: { min: 640, ideal: width, max: width },
height: { min: 480, ideal: height, max: height },
advanced: [{ width: width, height: height }],

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
export * as ScreenShare from "./ScreenSharePicker";

View File

@@ -2,7 +2,7 @@
padding: 1em;
}
.vcd-screen-picker-header h1 {
.vcd-screen-picker-header-title {
margin: 0;
}
@@ -11,49 +11,36 @@
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 {
flex-grow: 1;
}
.vcd-screen-picker-grid {
/* Screen Grid */
.vcd-screen-picker-screen-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2em 1em;
}
.vcd-screen-picker-grid input {
.vcd-screen-picker-screen-radio {
appearance: none;
cursor: pointer;
}
.vcd-screen-picker-selected img {
border: 2px solid var(--brand-experiment);
border-radius: 3px;
}
.vcd-screen-picker-grid label {
.vcd-screen-picker-screen-label {
overflow: hidden;
padding: 4px 0px;
padding: 8px;
cursor: pointer;
display: grid;
justify-items: center;
}
.vcd-screen-picker-grid label:hover {
outline: 2px solid var(--brand-experiment);
.vcd-screen-picker-screen-label:hover {
outline: 2px solid var(--brand-500);
}
.vcd-screen-picker-grid div {
.vcd-screen-picker-screen-name {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
@@ -67,8 +54,13 @@
box-sizing: border-box;
}
.vcd-screen-picker-preview img {
width: 100%;
.vcd-screen-picker-preview-img-linux {
width: 60%;
margin-bottom: 0.5em;
}
.vcd-screen-picker-preview-img {
width: 90%;
margin-bottom: 0.5em;
}
@@ -80,59 +72,56 @@
margin-bottom: 1em;
}
.vcd-screen-picker-radio input {
display: none;
/* Option Radios */
.vcd-screen-picker-option-radios {
display: flex;
width: 100%;
border-radius: 3px;
}
.vcd-screen-picker-radio {
.vcd-screen-picker-option-radio {
flex: 1 1 auto;
text-align: center;
background-color: var(--background-secondary);
border: 1px solid var(--primary-800);
padding: 0.3em;
cursor: pointer;
}
.vcd-screen-picker-radio h2 {
margin: 0;
.vcd-screen-picker-option-radio:first-child {
border-radius: 3px 0 0 3px;
}
.vcd-screen-picker-radio[data-checked="true"] {
background-color: var(--brand-experiment);
border-color: var(--brand-experiment);
.vcd-screen-picker-option-radio:last-child {
border-radius: 0 3px 3px 0;
}
.vcd-screen-picker-radio[data-checked="true"] h2 {
color: var(--interactive-active);
.vcd-screen-picker-option-input {
display: none;
}
.vcd-screen-picker-option-radio[data-checked="true"] {
background-color: var(--brand-500);
border-color: var(--brand-500);
}
.vcd-screen-picker-quality {
display: flex;
gap: 1em;
margin-bottom: 0.5em;
}
.vcd-screen-picker-quality section {
.vcd-screen-picker-quality-section {
flex: 1 1 auto;
}
.vcd-screen-picker-radios {
display: flex;
width: 100%;
border-radius: 3px;
.vcd-screen-picker-settings-button {
margin-left: auto;
margin-top: 0.3rem;
}
.vcd-screen-picker-radios label {
flex: 1 1 auto;
text-align: center;
}
.vcd-screen-picker-radios label:first-child {
border-radius: 3px 0 0 3px;
}
.vcd-screen-picker-radios label:last-child {
border-radius: 0 3px 3px 0;
}
.vcd-screen-picker-audio {
margin-bottom: 0;
@@ -143,4 +132,4 @@
font-size: 14px;
line-height: 20px;
font-weight: 400;
}
}

View File

@@ -1,18 +1,19 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Switch, useState } from "@vencord/types/webpack/common";
import { useState } from "@vencord/types/webpack/common";
import { SettingsComponent } from "./Settings";
import { VesktopSettingsSwitch } from "./VesktopSettingsSwitch";
export const AutoStartToggle: SettingsComponent = () => {
const [autoStartEnabled, setAutoStartEnabled] = useState(VesktopNative.autostart.isEnabled());
return (
<Switch
<VesktopSettingsSwitch
value={autoStartEnabled}
onChange={async v => {
await VesktopNative.autostart[v ? "enable" : "disable"]();
@@ -21,6 +22,6 @@ export const AutoStartToggle: SettingsComponent = () => {
note="Automatically start Vesktop on computer start-up"
>
Start With System
</Switch>
</VesktopSettingsSwitch>
);
};

View File

@@ -0,0 +1,120 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import {
Margins,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalRoot,
ModalSize,
openModal,
useForceUpdater
} from "@vencord/types/utils";
import { Button, Forms, Text, Toasts } from "@vencord/types/webpack/common";
import { Settings } from "shared/settings";
import { SettingsComponent } from "./Settings";
export const DeveloperOptionsButton: SettingsComponent = ({ settings }) => {
return <Button onClick={() => openDeveloperOptionsModal(settings)}>Open Developer Settings</Button>;
};
function openDeveloperOptionsModal(settings: Settings) {
openModal(props => (
<ModalRoot {...props} size={ModalSize.MEDIUM}>
<ModalHeader>
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>
Vesktop Developer Options
</Text>
<ModalCloseButton onClick={props.onClose} />
</ModalHeader>
<ModalContent>
<div style={{ padding: "1em 0" }}>
<Forms.FormTitle tag="h5">Vencord Location</Forms.FormTitle>
<VencordLocationPicker settings={settings} />
<Forms.FormTitle tag="h5" className={Margins.top16}>
Debugging
</Forms.FormTitle>
<div className="vcd-settings-button-grid">
<Button onClick={() => VesktopNative.debug.launchGpu()}>Open chrome://gpu</Button>
<Button onClick={() => VesktopNative.debug.launchWebrtcInternals()}>
Open chrome://webrtc-internals
</Button>
</div>
</div>
</ModalContent>
</ModalRoot>
));
}
const VencordLocationPicker: SettingsComponent = ({ settings }) => {
const forceUpdate = useForceUpdater();
const vencordDir = VesktopNative.fileManager.getVencordDir();
return (
<>
<Forms.FormText>
Vencord files are loaded from{" "}
{vencordDir ? (
<a
href="about:blank"
onClick={e => {
e.preventDefault();
VesktopNative.fileManager.showItemInFolder(vencordDir!);
}}
>
{vencordDir}
</a>
) : (
"the default location"
)}
</Forms.FormText>
<div className="vcd-settings-button-grid">
<Button
size={Button.Sizes.SMALL}
onClick={async () => {
const choice = await VesktopNative.fileManager.selectVencordDir();
switch (choice) {
case "cancelled":
break;
case "ok":
Toasts.show({
message: "Vencord install changed. Fully restart Vesktop to apply.",
id: Toasts.genId(),
type: Toasts.Type.SUCCESS
});
break;
case "invalid":
Toasts.show({
message:
"You did not choose a valid Vencord install. Make sure you're selecting the dist dir!",
id: Toasts.genId(),
type: Toasts.Type.FAILURE
});
break;
}
forceUpdate();
}}
>
Change
</Button>
<Button
size={Button.Sizes.SMALL}
color={Button.Colors.RED}
onClick={async () => {
await VesktopNative.fileManager.selectVencordDir(null);
forceUpdate();
}}
>
Reset
</Button>
</div>
</>
);
};

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Select } from "@vencord/types/webpack/common";

View File

@@ -1,17 +1,17 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Switch } from "@vencord/types/webpack/common";
import { setBadge } from "renderer/appBadge";
import { SettingsComponent } from "./Settings";
import { VesktopSettingsSwitch } from "./VesktopSettingsSwitch";
export const NotificationBadgeToggle: SettingsComponent = ({ settings }) => {
return (
<Switch
<VesktopSettingsSwitch
value={settings.appBadge ?? true}
onChange={v => {
settings.appBadge = v;
@@ -21,6 +21,6 @@ export const NotificationBadgeToggle: SettingsComponent = ({ settings }) => {
note="Show mention badge on the app icon"
>
Notification Badge
</Switch>
</VesktopSettingsSwitch>
);
};

View File

@@ -1,20 +1,22 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./settings.css";
import { Forms, Switch, Text } from "@vencord/types/webpack/common";
import { ErrorBoundary } from "@vencord/types/components";
import { Forms, Text } from "@vencord/types/webpack/common";
import { ComponentType } from "react";
import { Settings, useSettings } from "renderer/settings";
import { isMac, isWindows } from "renderer/utils";
import { AutoStartToggle } from "./AutoStartToggle";
import { DeveloperOptionsButton } from "./DeveloperOptions";
import { DiscordBranchPicker } from "./DiscordBranchPicker";
import { NotificationBadgeToggle } from "./NotificationBadgeToggle";
import { VencordLocationPicker } from "./VencordLocationPicker";
import { VesktopSettingsSwitch } from "./VesktopSettingsSwitch";
import { WindowsTransparencyControls } from "./WindowsTransparencyControls";
interface BooleanSetting {
@@ -37,6 +39,14 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
title: "Hardware Acceleration",
description: "Enable hardware acceleration",
defaultValue: true
},
{
key: "hardwareVideoAcceleration",
title: "Video Hardware Acceleration",
description:
"Enable hardware video acceleration. This can improve performance of screenshare and video playback, but may cause graphical glitches and infinitely loading streams.",
defaultValue: false,
disabled: () => Settings.store.hardwareAcceleration === false
}
],
"User Interface": [
@@ -59,11 +69,18 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
defaultValue: false,
disabled: () => Settings.store.customTitleBar ?? isWindows
},
{
key: "enableSplashScreen",
title: "Enable Splash Screen",
description:
"Shows a small splash screen while Vesktop is loading. Disabling this option will show the main window earlier while it's still loading.",
defaultValue: true
},
{
key: "splashTheming",
title: "Splash theming",
description: "Adapt the splash window colors to your custom theme",
defaultValue: false
defaultValue: true
},
WindowsTransparencyControls
],
@@ -102,16 +119,8 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
defaultValue: false
}
],
"Notifications & Updates": [
NotificationBadgeToggle,
{
key: "checkUpdates",
title: "Check for updates",
description: "Automatically check for Vesktop updates",
defaultValue: true
}
],
Miscelleanous: [
Notifications: [NotificationBadgeToggle],
Miscellaneous: [
{
key: "arRPC",
title: "Rich Presence",
@@ -126,51 +135,61 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
defaultValue: false
}
],
"Vencord Location": [VencordLocationPicker]
"Developer Options": [DeveloperOptionsButton]
};
function SettingsSections() {
const Settings = useSettings();
const sections = Object.entries(SettingsOptions).map(([title, settings]) => (
<Forms.FormSection
title={title}
key={title}
className="vcd-settings-section"
titleClassName="vcd-settings-title"
>
{settings.map(Setting => {
if (typeof Setting === "function") return <Setting settings={Settings} />;
const sections = Object.entries(SettingsOptions).map(([title, settings], i, arr) => (
<div key={title} className="vcd-settings-category">
<Text variant="heading-lg/semibold" color="header-primary" className="vcd-settings-category-title">
{title}
</Text>
const { defaultValue, title, description, key, disabled, invisible } = Setting;
if (invisible?.()) return null;
<div className="vcd-settings-category-content">
{settings.map(Setting => {
if (typeof Setting === "function") return <Setting settings={Settings} />;
return (
<Switch
value={Settings[key as any] ?? defaultValue}
onChange={v => (Settings[key as any] = v)}
note={description}
disabled={disabled?.()}
key={key}
>
{title}
</Switch>
);
})}
</Forms.FormSection>
const { defaultValue, title, description, key, disabled, invisible } = Setting;
if (invisible?.()) return null;
return (
<VesktopSettingsSwitch
value={Settings[key as any] ?? defaultValue}
onChange={v => (Settings[key as any] = v)}
note={description}
disabled={disabled?.()}
key={key}
>
{title}
</VesktopSettingsSwitch>
);
})}
</div>
{i < arr.length - 1 && <Forms.FormDivider className="vcd-settings-category-divider" />}
</div>
));
return <>{sections}</>;
}
export default function SettingsUi() {
return (
<Forms.FormSection>
<Text variant="heading-lg/semibold" style={{ color: "var(--header-primary)" }} tag="h2">
Vesktop Settings
</Text>
<SettingsSections />
</Forms.FormSection>
);
}
export default ErrorBoundary.wrap(
function SettingsUI() {
return (
<Forms.FormSection>
{/* FIXME: Outdated type */}
{/* @ts-expect-error Outdated type */}
<Text variant="heading-xl/semibold" color="header-primary" className="vcd-settings-title">
Vesktop Settings
</Text>
<SettingsSections />
</Forms.FormSection>
);
},
{
message:
"Failed to render the Vesktop Settings tab. If this issue persists, try to right click the Vesktop tray icon, then click 'Repair Vencord'. And make sure your Vesktop is up to date."
}
);

View File

@@ -1,62 +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 { Button, Forms, Toasts } from "@vencord/types/webpack/common";
import { SettingsComponent } from "./Settings";
export const VencordLocationPicker: SettingsComponent = ({ settings }) => {
return (
<>
<Forms.FormText>
Vencord files are loaded from{" "}
{settings.vencordDir ? (
<a
href="about:blank"
onClick={e => {
e.preventDefault();
VesktopNative.fileManager.showItemInFolder(settings.vencordDir!);
}}
>
{settings.vencordDir}
</a>
) : (
"the default location"
)}
</Forms.FormText>
<div className="vcd-location-btns">
<Button
size={Button.Sizes.SMALL}
onClick={async () => {
const choice = await VesktopNative.fileManager.selectVencordDir();
switch (choice) {
case "cancelled":
return;
case "invalid":
Toasts.show({
message:
"You did not choose a valid Vencord install. Make sure you're selecting the dist dir!",
id: Toasts.genId(),
type: Toasts.Type.FAILURE
});
return;
}
settings.vencordDir = choice;
}}
>
Change
</Button>
<Button
size={Button.Sizes.SMALL}
color={Button.Colors.RED}
onClick={() => (settings.vencordDir = void 0)}
>
Reset
</Button>
</div>
</>
);
};

View File

@@ -0,0 +1,16 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vesktop contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Switch } from "@vencord/types/webpack/common";
import { ComponentProps } from "react";
export function VesktopSettingsSwitch(props: ComponentProps<typeof Switch>) {
return (
<Switch {...props} hideBorder className="vcd-settings-switch">
{props.children}
</Switch>
);
}

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Margins } from "@vencord/types/utils";
@@ -13,8 +13,8 @@ export const WindowsTransparencyControls: SettingsComponent = ({ settings }) =>
if (!VesktopNative.app.supportsWindowsTransparency()) return null;
return (
<>
<Forms.FormTitle className={Margins.top16 + " " + Margins.bottom8}>Transparency Options</Forms.FormTitle>
<div>
<Forms.FormTitle className={Margins.bottom8}>Transparency Options</Forms.FormTitle>
<Forms.FormText className={Margins.bottom8}>
Requires a full restart. You will need a theme that supports transparency for this to work.
</Forms.FormText>
@@ -42,8 +42,6 @@ export const WindowsTransparencyControls: SettingsComponent = ({ settings }) =>
isSelected={v => v === settings.transparencyOption}
serialize={s => s}
/>
<Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} />
</>
</div>
);
};

View File

@@ -1,14 +1,34 @@
.vcd-location-btns {
.vcd-settings-button-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5em;
margin-top: 0.5em;
}
.vcd-settings-section {
margin-top: 1.5rem;
.vcd-settings-title {
margin-bottom: 32px;
}
.vcd-settings-title {
margin-bottom: 0.5rem;
.vcd-settings-category {
display: flex;
flex-direction: column;
}
.vcd-settings-category-title {
margin-bottom: 16px;
}
.vcd-settings-category-content {
display: flex;
flex-direction: column;
gap: 24px;
}
.vcd-settings-category-divider {
margin-top: 32px;
margin-bottom: 32px;
}
.vcd-settings-switch {
margin-bottom: 0;
}

View File

@@ -1,11 +0,0 @@
/* Download Desktop button in guilds list */
[class^=listItem_]:has([data-list-item-id=guildsnav___app-download-button]),
[class^=listItem_]:has(+ [class^=listItem_] [data-list-item-id=guildsnav___app-download-button]) {
display: none;
}
/* FIXME: remove this once Discord fixes their css to not explode scrollbars on chromium >=121 */
* {
scrollbar-width: unset !important;
scrollbar-color: unset !important;
}

View File

@@ -1,12 +1,10 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./fixes.css";
import { isWindows, localStorage } from "./utils";
import { localStorage } from "./utils";
// Make clicking Notifications focus the window
const originalSetOnClick = Object.getOwnPropertyDescriptor(Notification.prototype, "onclick")!.set!;
@@ -22,14 +20,3 @@ Object.defineProperty(Notification.prototype, "onclick", {
// Hide "Download Discord Desktop now!!!!" banner
localStorage.setItem("hideNag", "true");
// FIXME: Remove eventually.
// Originally, Vencord always used a Windows user agent. This seems to cause captchas
// Now, we use a platform specific UA - HOWEVER, discord FOR SOME REASON????? caches
// device props in localStorage. This code fixes their cache to properly update the platform in SuperProps
if (!isWindows)
try {
const deviceProperties = localStorage.getItem("deviceProperties");
if (deviceProperties && JSON.parse(deviceProperties).os === "Windows")
localStorage.removeItem("deviceProperties");
} catch {}

View File

@@ -1,45 +1,29 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./fixes";
import "./appBadge";
import "./patches";
import "./themedSplash";
console.log("read if cute :3");
import "./ipcCommands";
import "./appBadge";
import "./fixes";
import "./arrpc";
import "__patches__"; // auto generated by the build script
export * as Components from "./components";
import { findByPropsLazy } from "@vencord/types/webpack";
import { FluxDispatcher } from "@vencord/types/webpack/common";
import SettingsUi from "./components/settings/Settings";
import { VesktopLogger } from "./logger";
import { Settings } from "./settings";
export { Settings };
const InviteActions = findByPropsLazy("resolveInvite");
import type SettingsPlugin from "@vencord/types/plugins/_core/settings";
export async function openInviteModal(code: string) {
const { invite } = await InviteActions.resolveInvite(code, "Desktop Modal");
if (!invite) return false;
VesktopLogger.log("read if cute :3");
VesktopLogger.log("Vesktop v" + VesktopNative.app.getVersion());
VesktopNative.win.focus();
FluxDispatcher.dispatch({
type: "INVITE_MODAL_OPEN",
invite,
code,
context: "APP"
});
return true;
}
const customSettingsSections = (
Vencord.Plugins.plugins.Settings as any as { customSections: ((ID: Record<string, unknown>) => any)[] }
).customSections;
const customSettingsSections = (Vencord.Plugins.plugins.Settings as any as typeof SettingsPlugin).customSections;
customSettingsSections.push(() => ({
section: "Vesktop",
@@ -47,13 +31,3 @@ customSettingsSections.push(() => ({
element: SettingsUi,
className: "vc-vesktop-settings"
}));
const arRPC = Vencord.Plugins.plugins["WebRichPresence (arRPC)"] as any as {
handleEvent(e: MessageEvent): void;
};
VesktopNative.arrpc.onActivity(data => {
if (!Settings.store.arRPC) return;
arRPC.handleEvent(new MessageEvent("message", { data }));
});

View File

@@ -0,0 +1,54 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { SettingsRouter } from "@vencord/types/webpack/common";
import { IpcCommands } from "shared/IpcEvents";
import { openScreenSharePicker } from "./components/ScreenSharePicker";
type IpcCommandHandler = (data: any) => any;
const handlers = new Map<string, IpcCommandHandler>();
function respond(nonce: string, ok: boolean, data: any) {
VesktopNative.commands.respond({ nonce, ok, data });
}
VesktopNative.commands.onCommand(async ({ message, nonce, data }) => {
const handler = handlers.get(message);
if (!handler) {
return respond(nonce, false, `No handler for message: ${message}`);
}
try {
const result = await handler(data);
respond(nonce, true, result);
} catch (err) {
respond(nonce, false, String(err));
}
});
export function onIpcCommand(channel: string, handler: IpcCommandHandler) {
if (handlers.has(channel)) {
throw new Error(`Handler for message ${channel} already exists`);
}
handlers.set(channel, handler);
}
export function offIpcCommand(channel: string) {
handlers.delete(channel);
}
/* Generic Handlers */
onIpcCommand(IpcCommands.NAVIGATE_SETTINGS, () => {
SettingsRouter.open("My Account");
});
onIpcCommand(IpcCommands.GET_LANGUAGES, () => navigator.languages);
onIpcCommand(IpcCommands.SCREEN_SHARE_PICKER, data => openScreenSharePicker(data.screens, data.skipPicker));

9
src/renderer/logger.ts Normal file
View File

@@ -0,0 +1,9 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Logger } from "@vencord/types/utils";
export const VesktopLogger = new Logger("Vesktop", "#d3869b");

View File

@@ -0,0 +1,41 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vesktop contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addPatch } from "./shared";
addPatch({
patches: [
// Discord Web blocks the devtools keybin on mac specifically, disable that
{
find: '"mod+alt+i"',
replacement: {
match: /"discord\.com"===location\.host/,
replace: "false"
}
},
// Discord Web uses an incredibly broken devtools detector with false positives.
// They "hide" (aka remove from storage) your token if it "detects" open devtools.
// Due to the false positives, this leads to random logouts.
// Patch their devtools detection to use proper Electron APIs instead to fix the false positives
{
find: ".setDevtoolsCallbacks(",
group: true,
replacement: [
{
// eslint-disable-next-line no-useless-escape
match: /if\(null!=(\i)\)(?=.{0,50}\1\.window\.setDevtoolsCallbacks)/,
replace: "if(true)"
},
{
// eslint-disable-next-line no-useless-escape
match: /\b\i\.window\.setDevtoolsCallbacks/g,
replace: "VesktopNative.win.setDevtoolsCallbacks"
}
]
}
]
});

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addPatch } from "./shared";
@@ -13,7 +13,7 @@ addPatch({
replacement: {
// FIXME: fix eslint rule
// eslint-disable-next-line no-useless-escape
match: /\.isPlatformEmbedded(?=\?\i\.DesktopNotificationTypes\.ALL)/g,
match: /\.isPlatformEmbedded(?=\?\i\.\i\.ALL)/g,
replace: "$&||true"
}
}

View File

@@ -0,0 +1,51 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vesktop contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Logger } from "@vencord/types/utils";
import { MediaEngineStore } from "renderer/common";
const logger = new Logger("FixAutoGain");
function fixTrackConstraints(constraint: MediaTrackConstraints) {
const target = constraint.advanced?.find(opt => Object.hasOwn(opt, "autoGainControl")) ?? constraint;
target.autoGainControl = MediaEngineStore.getAutomaticGainControl();
}
function fixStreamConstraints(constraints: MediaStreamConstraints | undefined) {
if (!constraints?.audio) return;
if (typeof constraints.audio !== "object") {
constraints.audio = {};
}
fixTrackConstraints(constraints.audio);
}
const originalGetUserMedia = navigator.mediaDevices.getUserMedia;
navigator.mediaDevices.getUserMedia = function (constraints) {
try {
fixStreamConstraints(constraints);
logger.debug("Fixed getUserMedia constraints", constraints);
} catch (e) {
logger.error("Failed to fix getUserMedia constraints", e);
}
return originalGetUserMedia.call(this, constraints);
};
const originalApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
MediaStreamTrack.prototype.applyConstraints = function (constraints) {
if (constraints) {
try {
fixTrackConstraints(constraints);
logger.debug("Fixed applyConstraints constraints", constraints);
} catch (e) {
logger.error("Failed to fix applyConstraints constraints", e);
}
}
return originalApplyConstraints.call(this, constraints);
};

View File

@@ -0,0 +1,19 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addPatch } from "./shared";
addPatch({
patches: [
{
find: '"app-download-button"',
replacement: {
match: /return(?=.{0,50}id:"app-download-button")/,
replace: "return null;return"
}
}
]
});

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addPatch } from "./shared";
@@ -12,13 +12,13 @@ addPatch({
find: "lastOutputSystemDevice.justChanged",
replacement: {
// eslint-disable-next-line no-useless-escape
match: /(\i)\.default\.getState\(\).neverShowModal/,
replace: "$& || $self.shouldIgnore($1)"
match: /(\i)\.\i\.getState\(\).neverShowModal/,
replace: "$& || $self.shouldIgnoreDevice($1)"
}
}
],
shouldIgnore(state: any) {
shouldIgnoreDevice(state: any) {
return Object.keys(state?.default?.lastDeviceConnected ?? {})?.[0] === "vencord-screen-share";
}
});

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addPatch } from "./shared";

View File

@@ -1,14 +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
*/
// TODO: Possibly auto generate glob if we have more patches in the future
import "./enableNotificationsByDefault";
import "./platformClass";
import "./hideSwitchDevice";
import "./hideVenmicInput";
import "./screenShareFixes";
import "./spellCheck";
import "./windowsTitleBar";

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Settings } from "renderer/settings";

View File

@@ -1,11 +1,12 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Logger } from "@vencord/types/utils";
import { currentSettings } from "renderer/components/ScreenSharePicker";
import { State } from "renderer/settings";
import { isLinux } from "renderer/utils";
const logger = new Logger("VesktopStreamFixes");
@@ -27,8 +28,8 @@ if (isLinux) {
const stream = await original.call(this, opts);
const id = await getVirtmic();
const frameRate = Number(currentSettings?.fps);
const height = Number(currentSettings?.resolution);
const frameRate = Number(State.store.screenshareQuality?.frameRate ?? 30);
const height = Number(State.store.screenshareQuality?.resolution ?? 720);
const width = Math.round(height * (16 / 9));
const track = stream.getVideoTracks()[0];
@@ -36,7 +37,7 @@ if (isLinux) {
const constraints = {
...track.getConstraints(),
frameRate,
frameRate: { min: frameRate, ideal: frameRate },
width: { min: 640, ideal: width, max: width },
height: { min: 480, ideal: height, max: height },
advanced: [{ width: width, height: height }],
@@ -58,10 +59,15 @@ if (isLinux) {
},
autoGainControl: false,
echoCancellation: false,
noiseSuppression: false
noiseSuppression: false,
channelCount: 2,
sampleRate: 48000,
sampleSize: 16
}
});
audio.getAudioTracks().forEach(t => stream.addTrack(t));
stream.getAudioTracks().forEach(t => stream.removeTrack(t));
stream.addTrack(audio.getAudioTracks()[0]);
}
return stream;

View File

@@ -1,12 +1,12 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Patch } from "@vencord/types/utils/types";
window.VCDP = {};
window.VesktopPatchGlobals = {};
interface PatchData {
patches: Omit<Patch, "plugin">[];
@@ -16,15 +16,9 @@ interface PatchData {
export function addPatch<P extends PatchData>(p: P) {
const { patches, ...globals } = p;
for (const patch of patches as Patch[]) {
if (!Array.isArray(patch.replacement)) patch.replacement = [patch.replacement];
for (const r of patch.replacement) {
if (typeof r.replace === "string") r.replace = r.replace.replaceAll("$self", "VCDP");
}
patch.plugin = "Vesktop";
Vencord.Plugins.patches.push(patch);
for (const patch of patches) {
Vencord.Plugins.addPatch(patch, "Vesktop", "VesktopPatchGlobals");
}
Object.assign(VCDP, globals);
Object.assign(VesktopPatchGlobals, globals);
}

View File

@@ -1,12 +1,13 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addContextMenuPatch } from "@vencord/types/api/ContextMenu";
import { findStoreLazy } from "@vencord/types/webpack";
import { FluxDispatcher, Menu, useStateFromStores } from "@vencord/types/webpack/common";
import { FluxDispatcher, Menu, useMemo, useStateFromStores } from "@vencord/types/webpack/common";
import { useSettings } from "renderer/settings";
import { addPatch } from "./shared";
@@ -50,7 +51,16 @@ addContextMenuPatch("textarea-context", children => {
const spellCheckEnabled = useStateFromStores([SpellCheckStore], () => SpellCheckStore.isEnabled());
const hasCorrections = Boolean(word && corrections?.length);
children.push(
const availableLanguages = useMemo(VesktopNative.spellcheck.getAvailableLanguages, []);
const settings = useSettings();
const spellCheckLanguages = (settings.spellCheckLanguages ??= [...new Set(navigator.languages)]);
const pasteSectionIndex = children.findIndex(c => c?.props?.children?.some?.(c => c?.props?.id === "paste"));
children.splice(
pasteSectionIndex === -1 ? children.length : pasteSectionIndex,
0,
<Menu.MenuGroup>
{hasCorrections && (
<>
@@ -69,14 +79,39 @@ addContextMenuPatch("textarea-context", children => {
/>
</>
)}
<Menu.MenuCheckboxItem
id="vcd-spellcheck-enabled"
label="Enable Spellcheck"
checked={spellCheckEnabled}
action={() => {
FluxDispatcher.dispatch({ type: "SPELLCHECK_TOGGLE" });
}}
/>
<Menu.MenuItem id="vcd-spellcheck-settings" label="Spellcheck Settings">
<Menu.MenuCheckboxItem
id="vcd-spellcheck-enabled"
label="Enable Spellcheck"
checked={spellCheckEnabled}
action={() => {
FluxDispatcher.dispatch({ type: "SPELLCHECK_TOGGLE" });
}}
/>
<Menu.MenuItem id="vcd-spellcheck-languages" label="Languages" disabled={!spellCheckEnabled}>
{availableLanguages.map(lang => {
const isEnabled = spellCheckLanguages.includes(lang);
return (
<Menu.MenuCheckboxItem
id={"vcd-spellcheck-lang-" + lang}
label={lang}
checked={isEnabled}
disabled={!isEnabled && spellCheckLanguages.length >= 5}
action={() => {
const newSpellCheckLanguages = spellCheckLanguages.filter(l => l !== lang);
if (newSpellCheckLanguages.length === spellCheckLanguages.length) {
newSpellCheckLanguages.push(lang);
}
settings.spellCheckLanguages = newSpellCheckLanguages;
}}
/>
);
})}
</Menu.MenuItem>
</Menu.MenuItem>
</Menu.MenuGroup>
);
});

View File

@@ -0,0 +1,21 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addPatch } from "./shared";
addPatch({
patches: [
{
find: ".STREAMER_MODE_ENABLE,",
replacement: {
// remove if (platformEmbedded) check from streamer mode toggle
// eslint-disable-next-line no-useless-escape
match: /if\(\i\.\i\)(?=return.{0,200}?"autoToggle")/g,
replace: ""
}
}
]
});

View File

@@ -0,0 +1,32 @@
/*
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2025 Vendicated and Vencord contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addPatch } from "./shared";
addPatch({
patches: [
{
find: ",setSystemTrayApplications",
replacement: [
{
// eslint-disable-next-line no-useless-escape
match: /\i\.window\.(close|minimize|maximize)/g,
replace: `VesktopNative.win.$1`
},
{
// TODO: Fix eslint rule
// eslint-disable-next-line no-useless-escape
match: /(focus(\(\i\)){).{0,150}?\.focus\(\i,\i\)/,
replace: "$1VesktopNative.win.focus$2"
},
{
match: /,getEnableHardwareAcceleration/,
replace: "$&:VesktopNative.app.getEnableHardwareAcceleration,_oldGetEnableHardwareAcceleration"
}
]
}
]
});

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Settings } from "renderer/settings";
@@ -19,11 +19,25 @@ if (Settings.store.customTitleBar)
// eslint-disable-next-line no-useless-escape
match: /case \i\.\i\.WINDOWS:/,
replace: 'case "WEB":'
}
]
},
// Visual Refresh
{
find: ".systemBar,",
replacement: [
{
// TODO: Fix eslint rule
// eslint-disable-next-line no-useless-escape
match: /\i===\i\.PlatformTypes\.WINDOWS/g,
replace: "true"
},
...["close", "minimize", "maximize"].map(op => ({
match: new RegExp(String.raw`\i\.\i\.${op}\b`),
replace: `VesktopNative.win.${op}`
}))
{
// TODO: Fix eslint rule
// eslint-disable-next-line no-useless-escape
match: /\i===\i\.PlatformTypes\.WEB/g,
replace: "false"
}
]
}
]

View File

@@ -1,12 +1,15 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { useEffect, useReducer } from "@vencord/types/webpack/common";
import { SettingsStore } from "shared/utils/SettingsStore";
import { VesktopLogger } from "./logger";
import { localStorage } from "./utils";
export const Settings = new SettingsStore(VesktopNative.settings.get());
Settings.addGlobalChangeListener((o, p) => VesktopNative.settings.set(o, p));
@@ -28,3 +31,38 @@ export function getValueAndOnChange(key: keyof typeof Settings.store) {
onChange: (value: any) => (Settings.store[key] = value)
};
}
interface TState {
screenshareQuality?: {
resolution: string;
frameRate: string;
};
}
const stateKey = "VesktopState";
const currentState: TState = (() => {
const stored = localStorage.getItem(stateKey);
if (!stored) return {};
try {
return JSON.parse(stored);
} catch (e) {
VesktopLogger.error("Failed to parse stored state", e);
return {};
}
})();
export const State = new SettingsStore<TState>(currentState);
State.addGlobalChangeListener((o, p) => localStorage.setItem(stateKey, JSON.stringify(o)));
export function useVesktopState() {
const [, update] = useReducer(x => x + 1, 0);
useEffect(() => {
State.addGlobalChangeListener(update);
return () => State.removeGlobalChangeListener(update);
}, []);
return State.store;
}

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Settings } from "./settings";
@@ -10,22 +10,56 @@ function isValidColor(color: CSSStyleValue | undefined): color is CSSUnparsedVal
return color instanceof CSSUnparsedValue && typeof color[0] === "string" && CSS.supports("color", color[0]);
}
// https://gist.github.com/earthbound19/e7fe15fdf8ca3ef814750a61bc75b5ce
function clamp(value: number, min: number, max: number) {
return Math.max(Math.min(value, max), min);
}
const linearToGamma = (c: number) => (c >= 0.0031308 ? 1.055 * Math.pow(c, 1 / 2.4) - 0.055 : 12.92 * c);
function oklabToSRGB({ L, a, b }: { L: number; a: number; b: number }) {
let l = L + a * +0.3963377774 + b * +0.2158037573;
let m = L + a * -0.1055613458 + b * -0.0638541728;
let s = L + a * -0.0894841775 + b * -1.291485548;
l **= 3;
m **= 3;
s **= 3;
let R = l * +4.0767416621 + m * -3.3077115913 + s * +0.2309699292;
let G = l * -1.2684380046 + m * +2.6097574011 + s * -0.3413193965;
let B = l * -0.0041960863 + m * -0.7034186147 + s * +1.707614701;
R = 255 * linearToGamma(R);
G = 255 * linearToGamma(G);
B = 255 * linearToGamma(B);
R = Math.round(clamp(R, 0, 255));
G = Math.round(clamp(G, 0, 255));
B = Math.round(clamp(B, 0, 255));
return `rgb(${R}, ${G}, ${B})`;
}
function resolveColor(color: string) {
const span = document.createElement("span");
span.style.color = color;
span.style.display = "none";
document.body.append(span);
const rgbColor = getComputedStyle(span).color;
let rgbColor = getComputedStyle(span).color;
span.remove();
if (rgbColor.startsWith("oklab(")) {
// scam
const [_, L, a, b] = rgbColor.match(/oklab\((.+?)[, ]+(.+?)[, ]+(.+?)\)/) ?? [];
if (L && a && b) {
rgbColor = oklabToSRGB({ L: parseFloat(L), a: parseFloat(a), b: parseFloat(b) });
}
}
return rgbColor;
}
const updateSplashColors = () => {
const bodyStyles = document.body.computedStyleMap();
const color = bodyStyles.get("--text-normal");
const color = bodyStyles.get("--text-default");
const backgroundColor = bodyStyles.get("--background-primary");
if (isValidColor(color)) {

View File

@@ -1,9 +1,10 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
// Discord deletes this from the window so we need to capture it in a variable
export const { localStorage } = window;
export const isFirstRun = (() => {
@@ -18,3 +19,26 @@ const { platform } = navigator;
export const isWindows = platform.startsWith("Win");
export const isMac = platform.startsWith("Mac");
export const isLinux = platform.startsWith("Linux");
type ClassNameFactoryArg = string | string[] | Record<string, unknown> | false | null | undefined | 0 | "";
/**
* @param prefix The prefix to add to each class, defaults to `""`
* @returns A classname generator function
* @example
* const cl = classNameFactory("plugin-");
*
* cl("base", ["item", "editable"], { selected: null, disabled: true })
* // => "plugin-base plugin-item plugin-editable plugin-disabled"
*/
export const classNameFactory =
(prefix: string = "") =>
(...args: ClassNameFactoryArg[]) => {
const classNames = new Set<string>();
for (const arg of args) {
if (arg && typeof arg === "string") classNames.add(arg);
else if (Array.isArray(arg)) arg.forEach(name => classNames.add(name));
else if (arg && typeof arg === "object")
Object.entries(arg).forEach(([name, value]) => value && classNames.add(name));
}
return Array.from(classNames, name => prefix + name).join(" ");
};

View File

@@ -1,7 +1,7 @@
/*
* 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
* SPDX-License-Identifier: GPL-3.0-or-later
*/
export const enum IpcEvents {
@@ -12,6 +12,7 @@ export const enum IpcEvents {
GET_VERSION = "VCD_GET_VERSION",
SUPPORTS_WINDOWS_TRANSPARENCY = "VCD_SUPPORTS_WINDOWS_TRANSPARENCY",
GET_ENABLE_HARDWARE_ACCELERATION = "VCD_GET_ENABLE_HARDWARE_ACCELERATION",
RELAUNCH = "VCD_RELAUNCH",
CLOSE = "VCD_CLOSE",
@@ -23,13 +24,14 @@ export const enum IpcEvents {
GET_SETTINGS = "VCD_GET_SETTINGS",
SET_SETTINGS = "VCD_SET_SETTINGS",
GET_VENCORD_DIR = "VCD_GET_VENCORD_DIR",
SELECT_VENCORD_DIR = "VCD_SELECT_VENCORD_DIR",
UPDATER_GET_DATA = "VCD_UPDATER_GET_DATA",
UPDATER_DOWNLOAD = "VCD_UPDATER_DOWNLOAD",
UPDATE_IGNORE = "VCD_UPDATE_IGNORE",
SPELLCHECK_SET_LANGUAGES = "VCD_SPELLCHECK_SET_LANGUAGES",
SPELLCHECK_GET_AVAILABLE_LANGUAGES = "VCD_SPELLCHECK_GET_AVAILABLE_LANGUAGES",
SPELLCHECK_RESULT = "VCD_SPELLCHECK_RESULT",
SPELLCHECK_REPLACE_MISSPELLING = "VCD_SPELLCHECK_REPLACE_MISSPELLING",
SPELLCHECK_ADD_TO_DICTIONARY = "VCD_SPELLCHECK_ADD_TO_DICTIONARY",
@@ -47,7 +49,25 @@ export const enum IpcEvents {
VIRT_MIC_START_SYSTEM = "VCD_VIRT_MIC_START_ALL",
VIRT_MIC_STOP = "VCD_VIRT_MIC_STOP",
ARRPC_ACTIVITY = "VCD_ARRPC_ACTIVITY",
CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE",
CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE"
DEBUG_LAUNCH_GPU = "VCD_DEBUG_LAUNCH_GPU",
DEBUG_LAUNCH_WEBRTC_INTERNALS = "VCD_DEBUG_LAUNCH_WEBRTC",
IPC_COMMAND = "VCD_IPC_COMMAND",
DEVTOOLS_OPENED = "VCD_DEVTOOLS_OPENED",
DEVTOOLS_CLOSED = "VCD_DEVTOOLS_CLOSED"
}
export const enum IpcCommands {
RPC_ACTIVITY = "rpc:activity",
RPC_INVITE = "rpc:invite",
RPC_DEEP_LINK = "rpc:link",
NAVIGATE_SETTINGS = "navigate:settings",
GET_LANGUAGES = "navigator.languages",
SCREEN_SHARE_PICKER = "screenshare:picker"
}

Some files were not shown because too many files have changed in this diff Show More