Merge branch 'main' into main
This commit is contained in:
commit
f08af9446f
34 changed files with 1835 additions and 855 deletions
18
.github/ISSUE_TEMPLATE/blank.yml
vendored
Normal file
18
.github/ISSUE_TEMPLATE/blank.yml
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
name: Blank Issue
|
||||||
|
description: Reserved for developers. Use the bug report or feature request templates instead.
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# READ THIS BEFORE OPENING AN ISSUE
|
||||||
|
|
||||||
|
This form is only meant for Vesktop developers. If you don't know what you're doing,
|
||||||
|
please use the bug report or feature request templates instead.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: content
|
||||||
|
attributes:
|
||||||
|
label: Content
|
||||||
|
validations:
|
||||||
|
required: true
|
58
.github/ISSUE_TEMPLATE/bug_report.md
vendored
58
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -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. -->
|
|
106
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
106
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
name: 🐛 Bug / Crash Report
|
||||||
|
description: Create a bug or crash report for Vesktop
|
||||||
|
labels: [bug]
|
||||||
|
title: "[Bug] <title>"
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
**Thanks 🩷 for taking the time to fill out this bug report! Before proceeding, please read the following**
|
||||||
|
|
||||||
|
Make sure a similar issue doesn't already exist by [searching the existing issues](https://github.com/Vencord/Vesktop/issues?q=is%3Aissue) for keywords!
|
||||||
|
|
||||||
|
Make sure both Vesktop and Vencord are fully up to date. You can update Vencord by right-clicking the Vesktop tray icon and pressing "Update Vencord"
|
||||||
|
|
||||||
|
Do not report any of the following issues:
|
||||||
|
- Purely graphical glitches like flickering, scaling issues, etc: Issue with your gpu. Nothing we can do, update drivers or disable hardware acceleration
|
||||||
|
- Vencord related issues: This is the Vesktop repo, not Vencord
|
||||||
|
- Screenshare not starting / black screening on Linux: Issue with your desktop environment, specifically its xdg-desktop-portal
|
||||||
|
|
||||||
|
Linux users: Please only report issues with supported packages (flatpak and any builds from the README / releases).
|
||||||
|
We do not support other packages, like the AUR or Nix packages, so please first make sure your issue is reproducible with official releases,
|
||||||
|
like [our Flatpak](https://flathub.org/apps/dev.vencord.Vesktop) or [AppImage](https://vencord.dev/download/vesktop/amd64/appimage)
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: discord
|
||||||
|
attributes:
|
||||||
|
label: Discord Account
|
||||||
|
description: Who on Discord is making this request? Not required but encouraged for easier follow-up
|
||||||
|
placeholder: username#0000
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: Operating System
|
||||||
|
description: What operating system are you using (eg Windows 10, macOS Big Sur, Ubuntu 20.04)?
|
||||||
|
placeholder: Windows 10
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: linux-de
|
||||||
|
attributes:
|
||||||
|
label: Linux Only ~ Desktop Environment
|
||||||
|
description: If you are on Linux, what Desktop environment are you using (eg GNOME, KDE, XFCE)? Are you using Wayland or Xorg?
|
||||||
|
placeholder: Gnome on Wayland
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: bug-description
|
||||||
|
attributes:
|
||||||
|
label: What happens when the bug or crash occurs?
|
||||||
|
description: Where does this bug or crash occur, when does it occur, etc.
|
||||||
|
placeholder: The bug/crash happens sometimes when I do ..., causing this to not work/the app to crash. I think it happens because of ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected-behaviour
|
||||||
|
attributes:
|
||||||
|
label: What is the expected behaviour?
|
||||||
|
description: Simply detail what the expected behaviour is.
|
||||||
|
placeholder: I expect Vencord/Discord to open the ... page instead of ..., it prevents me from doing ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: steps-to-take
|
||||||
|
attributes:
|
||||||
|
label: How do you recreate this bug or crash?
|
||||||
|
description: Give us a list of steps in order to recreate the bug or crash.
|
||||||
|
placeholder: |
|
||||||
|
1. Do ...
|
||||||
|
2. Then ...
|
||||||
|
3. Do this ..., ... and then ...
|
||||||
|
4. Observe "the bug" or "the crash"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: debug-logs
|
||||||
|
attributes:
|
||||||
|
label: Debug Logs
|
||||||
|
description: Run vesktop from the command line. Include the relevant command line output here
|
||||||
|
value: |
|
||||||
|
```
|
||||||
|
Replace this text with your crash-log. Do not remove the backticks
|
||||||
|
```
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: agreement-check
|
||||||
|
attributes:
|
||||||
|
label: Request Agreement
|
||||||
|
description: We only accept reports for bugs that happen on supported and up to date Vesktop releases
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues and found no similar issue
|
||||||
|
required: true
|
||||||
|
- label: I am using the latest Vesktop and Vencord versions
|
||||||
|
required: true
|
||||||
|
- label: This issue occurs on an official release (not just the AUR or Nix packages)
|
||||||
|
required: true
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Vencord Support Server
|
||||||
|
url: https://discord.gg/D9uwnFnqmd
|
||||||
|
about: If you need help regarding Vesktop or Vencord, please join our support server!
|
10
.github/ISSUE_TEMPLATE/custom.md
vendored
10
.github/ISSUE_TEMPLATE/custom.md
vendored
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
name: Custom issue template
|
|
||||||
about: Describe this issue template's purpose here.
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
72
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
72
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
name: 🛠️ Feature Request
|
||||||
|
description: Create a feature request for Vesktop
|
||||||
|
labels: [bug]
|
||||||
|
title: "[Bug] <title>"
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
**Thanks 🩷 for taking the time to fill out this request! Before proceeding, please read the following**
|
||||||
|
|
||||||
|
Make sure a similar request doesn't already exist by [searching the existing issues](https://github.com/Vencord/Vesktop/issues?q=is%3Aissue) for keywords!
|
||||||
|
|
||||||
|
This form is only meant for **Vesktop feature requests**.
|
||||||
|
For plugin requests or Vencord feature requests, go [here](https://github.com/Vencord/plugin-requests/issues/new?template=request.yml) instead!
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: discord
|
||||||
|
attributes:
|
||||||
|
label: Discord Account
|
||||||
|
description: Who on Discord is making this request? Not required but encouraged for easier follow-up
|
||||||
|
placeholder: username#0000
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: motivation
|
||||||
|
attributes:
|
||||||
|
label: Motivation
|
||||||
|
description: If your feature request related to a problem? Please describe
|
||||||
|
placeholder: I'm always frustrated when ..., I think it would be better if ...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: Solution
|
||||||
|
description: Describe the solution you'd like
|
||||||
|
placeholder: A clear and concise description of what you want to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: Alternatives
|
||||||
|
description: Describe alternatives you've considered
|
||||||
|
placeholder: A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context here. Screenshots or mockups could help greatly
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: agreement-check
|
||||||
|
attributes:
|
||||||
|
label: Request Agreement
|
||||||
|
description: This form is only for Vesktop feature requests. If the following don't apply, re-read the introduction text
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues and found no similar issue
|
||||||
|
required: true
|
||||||
|
- label: This is not a plugin request
|
||||||
|
required: true
|
||||||
|
- label: This is not a Vencord feature request
|
||||||
|
required: true
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -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.
|
|
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
|
@ -4,6 +4,7 @@ on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- v*
|
- v*
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
|
|
BIN
build/background.tiff
Normal file
BIN
build/background.tiff
Normal file
Binary file not shown.
21
build/entitlements.mac.plist
Normal file
21
build/entitlements.mac.plist
Normal 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>
|
|
@ -28,6 +28,38 @@
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
<releases>
|
<releases>
|
||||||
|
<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 & 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>
|
||||||
|
<p>New Features</p>
|
||||||
|
<ul>
|
||||||
|
<li>Added categories to Vesktop settings to reduce visual clutter by @justin13888</li>
|
||||||
|
<li>Added support for Vencord's transparent window options</li>
|
||||||
|
</ul>
|
||||||
|
<p>Fixes</p>
|
||||||
|
<ul>
|
||||||
|
<li>Fixed ugly error popups when starting Vesktop without working internet connection</li>
|
||||||
|
<li>Fixed popout title bars on Windows</li>
|
||||||
|
<li>Fixed spellcheck entries</li>
|
||||||
|
<li>Fixed screenshare audio using microphone on debian, by @Curve</li>
|
||||||
|
<li>Fixed a bug where autostart on Linux won't preserve command line flags</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="1.5.0" date="2024-01-16" type="stable">
|
<release version="1.5.0" date="2024-01-16" type="stable">
|
||||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.0</url>
|
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.0</url>
|
||||||
<description>
|
<description>
|
||||||
|
@ -162,11 +194,7 @@
|
||||||
<control>voice</control>
|
<control>voice</control>
|
||||||
<display_length compare="ge">760</display_length>
|
<display_length compare="ge">760</display_length>
|
||||||
<display_length compare="le">1200</display_length>
|
<display_length compare="le">1200</display_length>
|
||||||
<internet>always</internet>
|
|
||||||
</recommends>
|
</recommends>
|
||||||
<supports>
|
|
||||||
<internet>always</internet>
|
|
||||||
</supports>
|
|
||||||
<content_rating type="oars-1.1">
|
<content_rating type="oars-1.1">
|
||||||
<content_attribute id="social-chat">intense</content_attribute>
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
<content_attribute id="social-audio">intense</content_attribute>
|
<content_attribute id="social-audio">intense</content_attribute>
|
||||||
|
|
58
package.json
58
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "vesktop",
|
"name": "vesktop",
|
||||||
"version": "1.5.0",
|
"version": "1.5.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
@ -24,36 +24,36 @@
|
||||||
"updateMeta": "tsx scripts/utils/updateMeta.mts"
|
"updateMeta": "tsx scripts/utils/updateMeta.mts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"arrpc": "github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8"
|
"arrpc": "github:OpenAsar/arrpc#6960a8fd4d65d566da93dbdb8a7ca474aa0a3c9c"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@vencord/venmic": "^3.3.2"
|
"@vencord/venmic": "^3.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
||||||
"@types/node": "^20.11.2",
|
"@types/node": "^20.11.26",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.65",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||||
"@typescript-eslint/parser": "^6.19.0",
|
"@typescript-eslint/parser": "^7.2.0",
|
||||||
"@vencord/types": "^0.1.2",
|
"@vencord/types": "^0.1.2",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.4.5",
|
||||||
"electron": "^28.1.3",
|
"electron": "^29.1.1",
|
||||||
"electron-builder": "^24.9.1",
|
"electron-builder": "^24.13.3",
|
||||||
"esbuild": "^0.19.11",
|
"esbuild": "^0.20.1",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-import-resolver-alias": "^1.1.2",
|
"eslint-import-resolver-alias": "^1.1.2",
|
||||||
"eslint-plugin-license-header": "^0.6.0",
|
"eslint-plugin-license-header": "^0.6.0",
|
||||||
"eslint-plugin-path-alias": "^1.0.0",
|
"eslint-plugin-path-alias": "^1.0.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||||
"eslint-plugin-unused-imports": "^3.0.0",
|
"eslint-plugin-unused-imports": "^3.1.0",
|
||||||
"prettier": "^3.2.2",
|
"prettier": "^3.2.5",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"tsx": "^4.7.0",
|
"tsx": "^4.7.1",
|
||||||
"type-fest": "^4.9.0",
|
"type-fest": "^4.12.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.4.2",
|
||||||
"xml-formatter": "^3.6.0"
|
"xml-formatter": "^3.6.2"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.11.0",
|
"packageManager": "pnpm@8.11.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -131,6 +131,26 @@
|
||||||
"com.apple.security.device.camera": true
|
"com.apple.security.device.camera": 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": {
|
"nsis": {
|
||||||
"include": "build/installer.nsh",
|
"include": "build/installer.nsh",
|
||||||
"oneClick": false
|
"oneClick": false
|
||||||
|
|
1555
pnpm-lock.yaml
1555
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -8,4 +8,4 @@ import "./utils/dotenv";
|
||||||
|
|
||||||
import { spawnNodeModuleBin } from "./utils/spawn.mjs";
|
import { spawnNodeModuleBin } from "./utils/spawn.mjs";
|
||||||
|
|
||||||
spawnNodeModuleBin("electron", [".", ...(process.env.ELECTRON_LAUNCH_FLAGS?.split(" ") ?? [])]);
|
spawnNodeModuleBin("electron", [process.cwd(), ...(process.env.ELECTRON_LAUNCH_FLAGS?.split(" ") ?? [])]);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
interface AutoStart {
|
interface AutoStart {
|
||||||
|
@ -17,7 +17,16 @@ interface AutoStart {
|
||||||
function makeAutoStartLinux(): AutoStart {
|
function makeAutoStartLinux(): AutoStart {
|
||||||
const configDir = process.env.XDG_CONFIG_HOME || join(process.env.HOME!, ".config");
|
const configDir = process.env.XDG_CONFIG_HOME || join(process.env.HOME!, ".config");
|
||||||
const dir = join(configDir, "autostart");
|
const dir = join(configDir, "autostart");
|
||||||
const file = join(dir, "vencord.desktop");
|
const file = join(dir, "vesktop.desktop");
|
||||||
|
|
||||||
|
// IM STUPID
|
||||||
|
const legacyName = join(dir, "vencord.desktop");
|
||||||
|
if (existsSync(legacyName)) renameSync(legacyName, file);
|
||||||
|
|
||||||
|
// "Quoting must be done by enclosing the argument between double quotes and escaping the double quote character,
|
||||||
|
// backtick character ("`"), dollar sign ("$") and backslash character ("\") by preceding it with an additional backslash character"
|
||||||
|
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables
|
||||||
|
const commandLine = process.argv.map(arg => '"' + arg.replace(/["$`\\]/g, "\\$&") + '"').join(" ");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isEnabled: () => existsSync(file),
|
isEnabled: () => existsSync(file),
|
||||||
|
@ -25,12 +34,11 @@ function makeAutoStartLinux(): AutoStart {
|
||||||
const desktopFile = `
|
const desktopFile = `
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Version=1.0
|
Name=Vesktop
|
||||||
Name=Vencord
|
Comment=Vesktop autostart script
|
||||||
Comment=Vencord autostart script
|
Exec=${commandLine}
|
||||||
Exec=${process.execPath}
|
|
||||||
Terminal=false
|
|
||||||
StartupNotify=false
|
StartupNotify=false
|
||||||
|
Terminal=false
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
mkdirSync(dir, { recursive: true });
|
mkdirSync(dir, { recursive: true });
|
||||||
|
|
|
@ -48,14 +48,14 @@ export const DEFAULT_HEIGHT = 720;
|
||||||
|
|
||||||
export const DISCORD_HOSTNAMES = ["discord.com", "canary.discord.com", "ptb.discord.com"];
|
export const DISCORD_HOSTNAMES = ["discord.com", "canary.discord.com", "ptb.discord.com"];
|
||||||
|
|
||||||
const UserAgents = {
|
const BrowserUserAgents = {
|
||||||
darwin: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
darwin: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
||||||
linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.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:
|
windows:
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UserAgent = UserAgents[process.platform] || UserAgents.windows;
|
export const BrowserUserAgent = BrowserUserAgents[process.platform] || BrowserUserAgents.windows;
|
||||||
|
|
||||||
export const enum MessageBoxChoice {
|
export const enum MessageBoxChoice {
|
||||||
Default,
|
Default,
|
||||||
|
|
|
@ -27,23 +27,35 @@ process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
|
||||||
function init() {
|
function init() {
|
||||||
const { disableSmoothScroll, hardwareAcceleration } = Settings.store;
|
const { disableSmoothScroll, hardwareAcceleration } = Settings.store;
|
||||||
|
|
||||||
if (hardwareAcceleration === false) app.disableHardwareAcceleration();
|
const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(",");
|
||||||
|
const disabledFeatures = app.commandLine.getSwitchValue("disable-features").split(",");
|
||||||
|
|
||||||
|
if (hardwareAcceleration === false) {
|
||||||
|
app.disableHardwareAcceleration();
|
||||||
|
} else {
|
||||||
|
enabledFeatures.push("VaapiVideoDecodeLinuxGL", "VaapiVideoEncoder", "VaapiVideoDecoder");
|
||||||
|
}
|
||||||
|
|
||||||
if (disableSmoothScroll) {
|
if (disableSmoothScroll) {
|
||||||
app.commandLine.appendSwitch("disable-smooth-scrolling");
|
app.commandLine.appendSwitch("disable-smooth-scrolling");
|
||||||
}
|
}
|
||||||
|
|
||||||
// work around chrome 66 disabling autoplay by default
|
// work around chrome 66 disabling autoplay by default
|
||||||
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
|
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
|
||||||
|
|
||||||
// WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows.
|
// WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows.
|
||||||
// HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service.
|
// HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service.
|
||||||
//
|
//
|
||||||
// WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790
|
// WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790
|
||||||
app.commandLine.appendSwitch(
|
disabledFeatures.push(
|
||||||
"disable-features",
|
"WinRetrieveSuggestionsOnlyOnDemand",
|
||||||
"WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService,WidgetLayering"
|
"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(","));
|
||||||
|
|
||||||
// In the Flatpak on SteamOS the theme is detected as light, but SteamOS only has a dark mode, so we just override it
|
// 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";
|
if (isDeckGameMode) nativeTheme.themeSource = "dark";
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,13 @@ import { ICON_PATH } from "../shared/paths";
|
||||||
import { createAboutWindow } from "./about";
|
import { createAboutWindow } from "./about";
|
||||||
import { initArRPC } from "./arrpc";
|
import { initArRPC } from "./arrpc";
|
||||||
import {
|
import {
|
||||||
|
BrowserUserAgent,
|
||||||
DATA_DIR,
|
DATA_DIR,
|
||||||
DEFAULT_HEIGHT,
|
DEFAULT_HEIGHT,
|
||||||
DEFAULT_WIDTH,
|
DEFAULT_WIDTH,
|
||||||
MessageBoxChoice,
|
MessageBoxChoice,
|
||||||
MIN_HEIGHT,
|
MIN_HEIGHT,
|
||||||
MIN_WIDTH,
|
MIN_WIDTH,
|
||||||
UserAgent,
|
|
||||||
VENCORD_FILES_DIR
|
VENCORD_FILES_DIR
|
||||||
} from "./constants";
|
} from "./constants";
|
||||||
import { Settings, State, VencordSettings } from "./settings";
|
import { Settings, State, VencordSettings } from "./settings";
|
||||||
|
@ -73,6 +73,10 @@ const [addSettingsListener, removeSettingsListeners] = makeSettingsListenerHelpe
|
||||||
const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings);
|
const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings);
|
||||||
|
|
||||||
function initTray(win: BrowserWindow) {
|
function initTray(win: BrowserWindow) {
|
||||||
|
const onTrayClick = () => {
|
||||||
|
if (Settings.store.clickTrayToShowHide && win.isVisible()) win.hide();
|
||||||
|
else win.show();
|
||||||
|
};
|
||||||
const trayMenu = Menu.buildFromTemplate([
|
const trayMenu = Menu.buildFromTemplate([
|
||||||
{
|
{
|
||||||
label: "Open",
|
label: "Open",
|
||||||
|
@ -120,7 +124,7 @@ function initTray(win: BrowserWindow) {
|
||||||
tray = new Tray(ICON_PATH);
|
tray = new Tray(ICON_PATH);
|
||||||
tray.setToolTip("Vesktop");
|
tray.setToolTip("Vesktop");
|
||||||
tray.setContextMenu(trayMenu);
|
tray.setContextMenu(trayMenu);
|
||||||
tray.on("click", () => win.show());
|
tray.on("click", onTrayClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearData(win: BrowserWindow) {
|
async function clearData(win: BrowserWindow) {
|
||||||
|
@ -426,7 +430,7 @@ function createMainWindow() {
|
||||||
initSettingsListeners(win);
|
initSettingsListeners(win);
|
||||||
initSpellCheck(win);
|
initSpellCheck(win);
|
||||||
|
|
||||||
win.webContents.setUserAgent(UserAgent);
|
win.webContents.setUserAgent(BrowserUserAgent);
|
||||||
|
|
||||||
const subdomain =
|
const subdomain =
|
||||||
Settings.store.discordBranch === "canary" || Settings.store.discordBranch === "ptb"
|
Settings.store.discordBranch === "canary" || Settings.store.discordBranch === "ptb"
|
||||||
|
|
|
@ -5,41 +5,54 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createWriteStream } from "fs";
|
import { createWriteStream } from "fs";
|
||||||
import type { IncomingMessage } from "http";
|
import { Readable } from "stream";
|
||||||
import { get, RequestOptions } from "https";
|
import { pipeline } from "stream/promises";
|
||||||
import { finished } from "stream/promises";
|
import { setTimeout } from "timers/promises";
|
||||||
|
|
||||||
export async function downloadFile(url: string, file: string, options: RequestOptions = {}) {
|
interface FetchieOptions {
|
||||||
const res = await simpleReq(url, options);
|
retryOnNetworkError?: boolean;
|
||||||
await finished(
|
}
|
||||||
res.pipe(
|
|
||||||
createWriteStream(file, {
|
export async function downloadFile(url: string, file: string, options: RequestInit = {}, fetchieOpts?: FetchieOptions) {
|
||||||
autoClose: true
|
const res = await fetchie(url, options, fetchieOpts);
|
||||||
})
|
await pipeline(
|
||||||
)
|
// @ts-expect-error odd type error
|
||||||
|
Readable.fromWeb(res.body!),
|
||||||
|
createWriteStream(file, {
|
||||||
|
autoClose: true
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function simpleReq(url: string, options: RequestOptions = {}) {
|
const ONE_MINUTE_MS = 1000 * 60;
|
||||||
return new Promise<IncomingMessage>((resolve, reject) => {
|
|
||||||
get(url, options, res => {
|
|
||||||
const { statusCode, statusMessage, headers } = res;
|
|
||||||
if (statusCode! >= 400) return void reject(`${statusCode}: ${statusMessage} - ${url}`);
|
|
||||||
if (statusCode! >= 300) return simpleReq(headers.location!, options).then(resolve).catch(reject);
|
|
||||||
|
|
||||||
resolve(res);
|
export async function fetchie(url: string, options?: RequestInit, { retryOnNetworkError }: FetchieOptions = {}) {
|
||||||
});
|
let res: Response | undefined;
|
||||||
});
|
|
||||||
}
|
try {
|
||||||
|
res = await fetch(url, options);
|
||||||
export async function simpleGet(url: string, options: RequestOptions = {}) {
|
} catch (err) {
|
||||||
const res = await simpleReq(url, options);
|
if (retryOnNetworkError) {
|
||||||
|
console.error("Failed to fetch", url + ".", "Gonna retry with backoff.");
|
||||||
return new Promise<Buffer>((resolve, reject) => {
|
|
||||||
const chunks = [] as Buffer[];
|
for (let tries = 0, delayMs = 500; tries < 20; tries++, delayMs = Math.min(2 * delayMs, ONE_MINUTE_MS)) {
|
||||||
|
await setTimeout(delayMs);
|
||||||
res.once("error", reject);
|
try {
|
||||||
res.on("data", chunk => chunks.push(chunk));
|
res = await fetch(url, options);
|
||||||
res.once("end", () => resolve(Buffer.concat(chunks)));
|
break;
|
||||||
});
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res) throw new Error(`Failed to fetch ${url}\n${err}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.ok) return res;
|
||||||
|
|
||||||
|
let msg = `Got non-OK response for ${url}: ${res.status} ${res.statusText}`;
|
||||||
|
|
||||||
|
const reason = await res.text().catch(() => "");
|
||||||
|
if (reason) msg += `\n${reason}`;
|
||||||
|
|
||||||
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { existsSync, mkdirSync } from "fs";
|
import { existsSync, mkdirSync } from "fs";
|
||||||
import type { RequestOptions } from "https";
|
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
import { USER_AGENT, VENCORD_FILES_DIR } from "../constants";
|
import { USER_AGENT, VENCORD_FILES_DIR } from "../constants";
|
||||||
import { downloadFile, simpleGet } from "./http";
|
import { downloadFile, fetchie } from "./http";
|
||||||
|
|
||||||
const API_BASE = "https://api.github.com";
|
const API_BASE = "https://api.github.com";
|
||||||
|
|
||||||
|
@ -31,27 +30,29 @@ export interface ReleaseData {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function githubGet(endpoint: string) {
|
export async function githubGet(endpoint: string) {
|
||||||
const opts: RequestOptions = {
|
const opts: RequestInit = {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/vnd.github+json",
|
Accept: "application/vnd.github+json",
|
||||||
"User-Agent": USER_AGENT
|
"User-Agent": USER_AGENT
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (process.env.GITHUB_TOKEN) opts.headers!.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
if (process.env.GITHUB_TOKEN) (opts.headers! as any).Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
||||||
|
|
||||||
return simpleGet(API_BASE + endpoint, opts);
|
return fetchie(API_BASE + endpoint, opts, { retryOnNetworkError: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function downloadVencordFiles() {
|
export async function downloadVencordFiles() {
|
||||||
const release = await githubGet("/repos/Vendicated/Vencord/releases/latest");
|
const release = await githubGet("/repos/Vendicated/Vencord/releases/latest");
|
||||||
|
|
||||||
const { assets } = JSON.parse(release.toString("utf-8")) as ReleaseData;
|
const { assets }: ReleaseData = await release.json();
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
assets
|
assets
|
||||||
.filter(({ name }) => FILES_TO_DOWNLOAD.some(f => name.startsWith(f)))
|
.filter(({ name }) => FILES_TO_DOWNLOAD.some(f => name.startsWith(f)))
|
||||||
.map(({ name, browser_download_url }) => downloadFile(browser_download_url, join(VENCORD_FILES_DIR, name)))
|
.map(({ name, browser_download_url }) =>
|
||||||
|
downloadFile(browser_download_url, join(VENCORD_FILES_DIR, name), {}, { retryOnNetworkError: true })
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,17 +4,58 @@
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { PatchBay } from "@vencord/venmic";
|
import type { PatchBay as PatchBayType } from "@vencord/venmic";
|
||||||
import { app, ipcMain } from "electron";
|
import { app, ipcMain } from "electron";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { IpcEvents } from "shared/IpcEvents";
|
import { IpcEvents } from "shared/IpcEvents";
|
||||||
import { STATIC_DIR } from "shared/paths";
|
import { STATIC_DIR } from "shared/paths";
|
||||||
|
|
||||||
type LinkData = Parameters<PatchBay["link"]>[0];
|
type LinkData = Parameters<PatchBayType["link"]>[0];
|
||||||
|
|
||||||
|
let PatchBay: typeof PatchBayType | undefined;
|
||||||
|
let patchBayInstance: PatchBayType | undefined;
|
||||||
|
|
||||||
|
let imported = false;
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
let patchBay: import("@vencord/venmic").PatchBay | undefined;
|
|
||||||
let isGlibcxxToOld = false;
|
let hasPipewirePulse = false;
|
||||||
|
let isGlibCxxOutdated = false;
|
||||||
|
|
||||||
|
function importVenmic() {
|
||||||
|
if (imported) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
imported = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
PatchBay = (require(join(STATIC_DIR, `dist/venmic-${process.arch}.node`)) as typeof import("@vencord/venmic"))
|
||||||
|
.PatchBay;
|
||||||
|
|
||||||
|
hasPipewirePulse = PatchBay.hasPipeWire();
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error("Failed to import venmic", e);
|
||||||
|
isGlibCxxOutdated = (e?.stack || e?.message || "").toLowerCase().includes("glibc");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function obtainVenmic() {
|
||||||
|
if (!imported) {
|
||||||
|
importVenmic();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PatchBay && !initialized) {
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
patchBayInstance = new PatchBay();
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error("Failed to instantiate venmic", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return patchBayInstance;
|
||||||
|
}
|
||||||
|
|
||||||
function getRendererAudioServicePid() {
|
function getRendererAudioServicePid() {
|
||||||
return (
|
return (
|
||||||
|
@ -25,33 +66,17 @@ function getRendererAudioServicePid() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function obtainVenmic() {
|
|
||||||
if (!initialized) {
|
|
||||||
initialized = true;
|
|
||||||
try {
|
|
||||||
const { PatchBay } = require(
|
|
||||||
join(STATIC_DIR, `dist/venmic-${process.arch}.node`)
|
|
||||||
) as typeof import("@vencord/venmic");
|
|
||||||
patchBay = new PatchBay();
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error("Failed to initialise venmic. Make sure you're using pipewire", e);
|
|
||||||
isGlibcxxToOld = (e?.stack || e?.message || "").toLowerCase().includes("glibc");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return patchBay;
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
|
||||||
const audioPid = getRendererAudioServicePid();
|
const audioPid = getRendererAudioServicePid();
|
||||||
|
|
||||||
const list = obtainVenmic()
|
const list = obtainVenmic()
|
||||||
?.list()
|
?.list()
|
||||||
.filter(s => s["application.process.id"] !== audioPid)
|
.filter(s => s["application.process.id"] !== audioPid)
|
||||||
.map(s => s["application.name"]);
|
.map(s => s["application.name"]);
|
||||||
|
|
||||||
return list
|
const uniqueTargets = [...new Set(list)];
|
||||||
? { ok: true, targets: [...new Set(list)] } // Remove duplicates
|
|
||||||
: { ok: false, isGlibcxxToOld };
|
return list ? { ok: true, targets: uniqueTargets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated };
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boolean) => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boolean) => {
|
||||||
|
@ -72,11 +97,12 @@ ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boo
|
||||||
return obtainVenmic()?.link(data);
|
return obtainVenmic()?.link(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean) => {
|
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean, onlyDefaultSpeakers?: boolean) => {
|
||||||
const pid = getRendererAudioServicePid();
|
const pid = getRendererAudioServicePid();
|
||||||
|
|
||||||
const data: LinkData = {
|
const data: LinkData = {
|
||||||
exclude: [{ key: "application.process.id", value: pid }]
|
exclude: [{ key: "application.process.id", value: pid }],
|
||||||
|
only_default_speakers: onlyDefaultSpeakers
|
||||||
};
|
};
|
||||||
|
|
||||||
if (workaround) {
|
if (workaround) {
|
||||||
|
|
|
@ -62,9 +62,12 @@ export const VesktopNative = {
|
||||||
/** only available on Linux. */
|
/** only available on Linux. */
|
||||||
virtmic: {
|
virtmic: {
|
||||||
list: () =>
|
list: () =>
|
||||||
invoke<{ ok: false; isGlibcxxToOld: boolean } | { ok: true; targets: string[] }>(IpcEvents.VIRT_MIC_LIST),
|
invoke<
|
||||||
|
{ ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: string[]; hasPipewirePulse: boolean }
|
||||||
|
>(IpcEvents.VIRT_MIC_LIST),
|
||||||
start: (targets: string[], workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START, targets, workaround),
|
start: (targets: string[], workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START, targets, workaround),
|
||||||
startSystem: (workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround),
|
startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) =>
|
||||||
|
invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers),
|
||||||
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
|
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
|
||||||
},
|
},
|
||||||
arrpc: {
|
arrpc: {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import "./screenSharePicker.css";
|
import "./screenSharePicker.css";
|
||||||
|
|
||||||
import { closeModal, Margins, Modals, openModal, useAwaiter } from "@vencord/types/utils";
|
import { closeModal, Logger, Margins, Modals, ModalSize, openModal, useAwaiter } from "@vencord/types/utils";
|
||||||
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
|
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
@ -36,7 +36,9 @@ interface StreamSettings {
|
||||||
fps: StreamFps;
|
fps: StreamFps;
|
||||||
audio: boolean;
|
audio: boolean;
|
||||||
audioSource?: string;
|
audioSource?: string;
|
||||||
|
contentHint?: string;
|
||||||
workaround?: boolean;
|
workaround?: boolean;
|
||||||
|
onlyDefaultSpeakers?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamPick extends StreamSettings {
|
export interface StreamPick extends StreamSettings {
|
||||||
|
@ -49,7 +51,9 @@ interface Source {
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentSettings: StreamSettings | null = null;
|
export let currentSettings: StreamSettings | null = null;
|
||||||
|
|
||||||
|
const logger = new Logger("VesktopScreenShare");
|
||||||
|
|
||||||
addPatch({
|
addPatch({
|
||||||
patches: [
|
patches: [
|
||||||
|
@ -59,6 +63,20 @@ addPatch({
|
||||||
match: /this.localWant=/,
|
match: /this.localWant=/,
|
||||||
replace: "$self.patchStreamQuality(this);$&"
|
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) {
|
patchStreamQuality(opts: any) {
|
||||||
|
@ -73,6 +91,14 @@ addPatch({
|
||||||
bitrateMax: 8000000,
|
bitrateMax: 8000000,
|
||||||
bitrateTarget: 600000
|
bitrateTarget: 600000
|
||||||
});
|
});
|
||||||
|
if (opts?.encode) {
|
||||||
|
Object.assign(opts.encode, {
|
||||||
|
framerate,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
pixelCount: height * width
|
||||||
|
});
|
||||||
|
}
|
||||||
Object.assign(opts.capture, {
|
Object.assign(opts.capture, {
|
||||||
framerate,
|
framerate,
|
||||||
width,
|
width,
|
||||||
|
@ -167,54 +193,102 @@ function StreamSettings({
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="vcd-screen-picker-settings-grid">
|
||||||
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
<div>
|
||||||
<Card className="vcd-screen-picker-card vcd-screen-picker-preview">
|
<Forms.FormTitle>What you're streaming</Forms.FormTitle>
|
||||||
<img src={thumb} alt="" />
|
<Card className="vcd-screen-picker-card vcd-screen-picker-preview">
|
||||||
<Text variant="text-sm/normal">{source.name}</Text>
|
<img src={thumb} alt="" />
|
||||||
</Card>
|
<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">
|
<Card className="vcd-screen-picker-card">
|
||||||
<div className="vcd-screen-picker-quality">
|
<div className="vcd-screen-picker-quality">
|
||||||
<section>
|
<section>
|
||||||
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
||||||
<div className="vcd-screen-picker-radios">
|
<div className="vcd-screen-picker-radios">
|
||||||
{StreamResolutions.map(res => (
|
{StreamResolutions.map(res => (
|
||||||
<label className="vcd-screen-picker-radio" data-checked={settings.resolution === res}>
|
<label
|
||||||
<Text variant="text-sm/bold">{res}</Text>
|
className="vcd-screen-picker-radio"
|
||||||
<input
|
data-checked={settings.resolution === res}
|
||||||
type="radio"
|
>
|
||||||
name="resolution"
|
<Text variant="text-sm/bold">{res}</Text>
|
||||||
value={res}
|
<input
|
||||||
checked={settings.resolution === res}
|
type="radio"
|
||||||
onChange={() => setSettings(s => ({ ...s, resolution: res }))}
|
name="resolution"
|
||||||
/>
|
value={res}
|
||||||
</label>
|
checked={settings.resolution === res}
|
||||||
))}
|
onChange={() => setSettings(s => ({ ...s, resolution: res }))}
|
||||||
</div>
|
/>
|
||||||
</section>
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
<Forms.FormTitle>Frame Rate</Forms.FormTitle>
|
||||||
<div className="vcd-screen-picker-radios">
|
<div className="vcd-screen-picker-radios">
|
||||||
{StreamFps.map(fps => (
|
{StreamFps.map(fps => (
|
||||||
<label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}>
|
<label className="vcd-screen-picker-radio" data-checked={settings.fps === fps}>
|
||||||
<Text variant="text-sm/bold">{fps}</Text>
|
<Text variant="text-sm/bold">{fps}</Text>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
name="fps"
|
name="fps"
|
||||||
value={fps}
|
value={fps}
|
||||||
checked={settings.fps === fps}
|
checked={settings.fps === fps}
|
||||||
onChange={() => setSettings(s => ({ ...s, fps }))}
|
onChange={() => setSettings(s => ({ ...s, fps }))}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</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 && (
|
{isWindows && (
|
||||||
<Switch
|
<Switch
|
||||||
value={settings.audio}
|
value={settings.audio}
|
||||||
|
@ -230,11 +304,13 @@ function StreamSettings({
|
||||||
<AudioSourcePickerLinux
|
<AudioSourcePickerLinux
|
||||||
audioSource={settings.audioSource}
|
audioSource={settings.audioSource}
|
||||||
workaround={settings.workaround}
|
workaround={settings.workaround}
|
||||||
|
onlyDefaultSpeakers={settings.onlyDefaultSpeakers}
|
||||||
setAudioSource={source => setSettings(s => ({ ...s, audioSource: source }))}
|
setAudioSource={source => setSettings(s => ({ ...s, audioSource: source }))}
|
||||||
setWorkaround={workaround => setSettings(s => ({ ...s, workaround: workaround }))}
|
setWorkaround={value => setSettings(s => ({ ...s, workaround: value }))}
|
||||||
|
setOnlyDefaultSpeakers={value => setSettings(s => ({ ...s, onlyDefaultSpeakers: value }))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -242,63 +318,102 @@ function StreamSettings({
|
||||||
function AudioSourcePickerLinux({
|
function AudioSourcePickerLinux({
|
||||||
audioSource,
|
audioSource,
|
||||||
workaround,
|
workaround,
|
||||||
|
onlyDefaultSpeakers,
|
||||||
setAudioSource,
|
setAudioSource,
|
||||||
setWorkaround
|
setWorkaround,
|
||||||
|
setOnlyDefaultSpeakers
|
||||||
}: {
|
}: {
|
||||||
audioSource?: string;
|
audioSource?: string;
|
||||||
workaround?: boolean;
|
workaround?: boolean;
|
||||||
|
onlyDefaultSpeakers?: boolean;
|
||||||
setAudioSource(s: string): void;
|
setAudioSource(s: string): void;
|
||||||
setWorkaround(b: boolean): void;
|
setWorkaround(b: boolean): void;
|
||||||
|
setOnlyDefaultSpeakers(b: boolean): void;
|
||||||
}) {
|
}) {
|
||||||
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
|
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
|
||||||
fallbackValue: { ok: true, targets: [] }
|
fallbackValue: { ok: true, targets: [], hasPipewirePulse: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null;
|
const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null;
|
||||||
|
const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true;
|
||||||
|
|
||||||
|
const [ignorePulseWarning, setIgnorePulseWarning] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<>
|
||||||
<Forms.FormTitle>Audio</Forms.FormTitle>
|
<Forms.FormTitle>Audio Settings</Forms.FormTitle>
|
||||||
{loading && <Forms.FormTitle>Loading Audio sources...</Forms.FormTitle>}
|
<Card className="vcd-screen-picker-card">
|
||||||
{!sources.ok &&
|
{loading ? (
|
||||||
(sources.isGlibcxxToOld ? (
|
<Forms.FormTitle>Loading Audio Sources...</Forms.FormTitle>
|
||||||
|
) : (
|
||||||
|
<Forms.FormTitle>Audio Source</Forms.FormTitle>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!sources.ok && sources.isGlibCxxOutdated && (
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
Failed to retrieve Audio Sources because your C++ library is too old to run venmic. If you would
|
Failed to retrieve Audio Sources because your C++ library is too old to run
|
||||||
like to stream with Audio, see{" "}
|
<a href="https://github.com/Vencord/venmic" target="_blank">
|
||||||
|
venmic
|
||||||
|
</a>
|
||||||
|
. See{" "}
|
||||||
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
||||||
this guide
|
this guide
|
||||||
</a>
|
</a>{" "}
|
||||||
|
for possible solutions.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasPipewirePulse || ignorePulseWarning ? (
|
||||||
|
allSources && (
|
||||||
|
<Select
|
||||||
|
options={allSources.map(s => ({ label: s, value: s, default: s === "None" }))}
|
||||||
|
isSelected={s => s === audioSource}
|
||||||
|
select={setAudioSource}
|
||||||
|
serialize={String}
|
||||||
|
/>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<Forms.FormText>
|
<Text variant="text-sm/normal">
|
||||||
Failed to retrieve Audio Sources. If you would like to stream with Audio, make sure you're using
|
Could not find pipewire-pulse. This usually means that you do not run pipewire as your main
|
||||||
Pipewire, not Pulseaudio
|
audio-server. <br />
|
||||||
</Forms.FormText>
|
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>
|
||||||
|
)}
|
||||||
|
|
||||||
{allSources && (
|
<Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} />
|
||||||
<Select
|
|
||||||
options={allSources.map(s => ({ label: s, value: s, default: s === "None" }))}
|
|
||||||
isSelected={s => s === audioSource}
|
|
||||||
select={setAudioSource}
|
|
||||||
serialize={String}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<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
|
<Switch
|
||||||
onChange={setWorkaround}
|
hideBorder
|
||||||
value={workaround ?? false}
|
onChange={setOnlyDefaultSpeakers}
|
||||||
note={
|
disabled={audioSource !== "Entire System"}
|
||||||
<>
|
value={onlyDefaultSpeakers ?? true}
|
||||||
Work around an issue that causes the microphone to be shared instead of the correct audio. Only
|
note={
|
||||||
enable if you're experiencing this issue.
|
<>
|
||||||
</>
|
When sharing entire desktop audio, only share apps that play to the default speakers and
|
||||||
}
|
ignore apps that play to other speakers or devices.
|
||||||
>
|
</>
|
||||||
Microphone Workaround
|
}
|
||||||
</Switch>
|
>
|
||||||
</section>
|
Only Default Speakers
|
||||||
|
</Switch>
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,16 +434,16 @@ function ModalComponent({
|
||||||
const [settings, setSettings] = useState<StreamSettings>({
|
const [settings, setSettings] = useState<StreamSettings>({
|
||||||
resolution: "1080",
|
resolution: "1080",
|
||||||
fps: "60",
|
fps: "60",
|
||||||
|
contentHint: "motion",
|
||||||
audio: true
|
audio: true
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modals.ModalRoot {...modalProps}>
|
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||||
<Modals.ModalHeader className="vcd-screen-picker-header">
|
<Modals.ModalHeader className="vcd-screen-picker-header">
|
||||||
<Forms.FormTitle tag="h2">ScreenShare</Forms.FormTitle>
|
<Forms.FormTitle tag="h2">ScreenShare</Forms.FormTitle>
|
||||||
<Modals.ModalCloseButton onClick={close} />
|
<Modals.ModalCloseButton onClick={close} />
|
||||||
</Modals.ModalHeader>
|
</Modals.ModalHeader>
|
||||||
|
|
||||||
<Modals.ModalContent className="vcd-screen-picker-modal">
|
<Modals.ModalContent className="vcd-screen-picker-modal">
|
||||||
{!selected ? (
|
{!selected ? (
|
||||||
<ScreenPicker screens={screens} chooseScreen={setSelected} />
|
<ScreenPicker screens={screens} chooseScreen={setSelected} />
|
||||||
|
@ -341,35 +456,62 @@ function ModalComponent({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modals.ModalContent>
|
</Modals.ModalContent>
|
||||||
|
|
||||||
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
<Modals.ModalFooter className="vcd-screen-picker-footer">
|
||||||
<Button
|
<Button
|
||||||
disabled={!selected}
|
disabled={!selected}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
currentSettings = settings;
|
currentSettings = settings;
|
||||||
|
try {
|
||||||
// If there are 2 connections, the second one is the existing stream.
|
const frameRate = Number(settings.fps);
|
||||||
// In that case, we patch its quality
|
|
||||||
const conn = [...MediaEngineStore.getMediaEngine().connections][1];
|
|
||||||
if (conn && conn.videoStreamParameters.length > 0) {
|
|
||||||
const height = Number(settings.resolution);
|
const height = Number(settings.resolution);
|
||||||
const width = Math.round(height * (16 / 9));
|
const width = Math.round(height * (16 / 9));
|
||||||
Object.assign(conn.videoStreamParameters[0], {
|
|
||||||
maxFrameRate: Number(settings.fps),
|
|
||||||
maxPixelCount: width * height,
|
|
||||||
maxBitrate: 8000000,
|
|
||||||
maxResolution: {
|
|
||||||
type: "fixed",
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
submit({
|
const conn = [...MediaEngineStore.getMediaEngine().connections].find(
|
||||||
id: selected!,
|
connection => connection.streamUserId === UserStore.getCurrentUser().id
|
||||||
...settings
|
);
|
||||||
});
|
|
||||||
|
if (conn) {
|
||||||
|
conn.videoStreamParameters[0].maxFrameRate = frameRate;
|
||||||
|
conn.videoStreamParameters[0].maxResolution.height = height;
|
||||||
|
conn.videoStreamParameters[0].maxResolution.width = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
submit({
|
||||||
|
id: selected!,
|
||||||
|
...settings
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
const conn = [...MediaEngineStore.getMediaEngine().connections].find(
|
||||||
|
connection => connection.streamUserId === UserStore.getCurrentUser().id
|
||||||
|
);
|
||||||
|
if (!conn) return;
|
||||||
|
|
||||||
|
const track = conn.input.stream.getVideoTracks()[0];
|
||||||
|
|
||||||
|
const constraints = {
|
||||||
|
...track.getConstraints(),
|
||||||
|
frameRate,
|
||||||
|
width: { min: 640, ideal: width, max: width },
|
||||||
|
height: { min: 480, ideal: height, max: height },
|
||||||
|
advanced: [{ width: width, height: height }],
|
||||||
|
resizeMode: "none"
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await track.applyConstraints(constraints);
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"Applied constraints successfully. New constraints:",
|
||||||
|
track.getConstraints()
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Failed to apply constraints.", e);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error while submitting stream.", error);
|
||||||
|
}
|
||||||
|
|
||||||
close();
|
close();
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -11,6 +11,21 @@
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vcd-screen-picker-settings-grid {
|
||||||
|
gap: 1em;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vcd-screen-picker-settings-grid > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vcd-screen-picker-card {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.vcd-screen-picker-grid {
|
.vcd-screen-picker-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
@ -122,3 +137,10 @@
|
||||||
.vcd-screen-picker-audio {
|
.vcd-screen-picker-audio {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vcd-screen-picker-hint-description {
|
||||||
|
color: var(--header-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
|
@ -83,6 +83,12 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
|
||||||
invisible: () => isMac,
|
invisible: () => isMac,
|
||||||
disabled: () => Settings.store.tray === false
|
disabled: () => Settings.store.tray === false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "clickTrayToShowHide",
|
||||||
|
title: "Hide/Show on tray click",
|
||||||
|
description: "Left clicking tray icon will toggle the vesktop window visibility.",
|
||||||
|
defaultValue: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "disableMinSize",
|
key: "disableMinSize",
|
||||||
title: "Disable minimum window size",
|
title: "Disable minimum window size",
|
||||||
|
|
|
@ -3,3 +3,9 @@
|
||||||
[class^=listItem_]:has(+ [class^=listItem_] [data-list-item-id=guildsnav___app-download-button]) {
|
[class^=listItem_]:has(+ [class^=listItem_] [data-list-item-id=guildsnav___app-download-button]) {
|
||||||
display: none;
|
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;
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "./hideGarbage.css";
|
import "./fixes.css";
|
||||||
|
|
||||||
import { isWindows, localStorage } from "./utils";
|
import { isWindows, localStorage } from "./utils";
|
||||||
|
|
||||||
|
|
24
src/renderer/patches/hideSwitchDevice.tsx
Normal file
24
src/renderer/patches/hideSwitchDevice.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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 { addPatch } from "./shared";
|
||||||
|
|
||||||
|
addPatch({
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: "lastOutputSystemDevice.justChanged",
|
||||||
|
replacement: {
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
|
match: /(\i)\.default\.getState\(\).neverShowModal/,
|
||||||
|
replace: "$& || $self.shouldIgnore($1)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
shouldIgnore(state: any) {
|
||||||
|
return Object.keys(state?.default?.lastDeviceConnected ?? {})?.[0] === "vencord-screen-share";
|
||||||
|
}
|
||||||
|
});
|
25
src/renderer/patches/hideVenmicInput.tsx
Normal file
25
src/renderer/patches/hideVenmicInput.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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 { addPatch } from "./shared";
|
||||||
|
|
||||||
|
addPatch({
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: 'setSinkId"in',
|
||||||
|
replacement: {
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
|
match: /return (\i)\?navigator\.mediaDevices\.enumerateDevices/,
|
||||||
|
replace: "return $1 ? $self.filteredDevices"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
async filteredDevices() {
|
||||||
|
const original = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
return original.filter(x => x.label !== "vencord-screen-share");
|
||||||
|
}
|
||||||
|
});
|
|
@ -7,7 +7,9 @@
|
||||||
// TODO: Possibly auto generate glob if we have more patches in the future
|
// TODO: Possibly auto generate glob if we have more patches in the future
|
||||||
import "./enableNotificationsByDefault";
|
import "./enableNotificationsByDefault";
|
||||||
import "./platformClass";
|
import "./platformClass";
|
||||||
import "./screenShareAudio";
|
import "./hideSwitchDevice";
|
||||||
|
import "./hideVenmicInput";
|
||||||
|
import "./screenShareFixes";
|
||||||
import "./spellCheck";
|
import "./spellCheck";
|
||||||
import "./windowsTitleBar";
|
import "./windowsTitleBar";
|
||||||
import "./keybinds";
|
import "./keybinds";
|
||||||
|
|
|
@ -4,8 +4,12 @@
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Logger } from "@vencord/types/utils";
|
||||||
|
import { currentSettings } from "renderer/components/ScreenSharePicker";
|
||||||
import { isLinux } from "renderer/utils";
|
import { isLinux } from "renderer/utils";
|
||||||
|
|
||||||
|
const logger = new Logger("VesktopStreamFixes");
|
||||||
|
|
||||||
if (isLinux) {
|
if (isLinux) {
|
||||||
const original = navigator.mediaDevices.getDisplayMedia;
|
const original = navigator.mediaDevices.getDisplayMedia;
|
||||||
|
|
||||||
|
@ -23,6 +27,29 @@ if (isLinux) {
|
||||||
const stream = await original.call(this, opts);
|
const stream = await original.call(this, opts);
|
||||||
const id = await getVirtmic();
|
const id = await getVirtmic();
|
||||||
|
|
||||||
|
const frameRate = Number(currentSettings?.fps);
|
||||||
|
const height = Number(currentSettings?.resolution);
|
||||||
|
const width = Math.round(height * (16 / 9));
|
||||||
|
const track = stream.getVideoTracks()[0];
|
||||||
|
|
||||||
|
track.contentHint = String(currentSettings?.contentHint);
|
||||||
|
|
||||||
|
const constraints = {
|
||||||
|
...track.getConstraints(),
|
||||||
|
frameRate,
|
||||||
|
width: { min: 640, ideal: width, max: width },
|
||||||
|
height: { min: 480, ideal: height, max: height },
|
||||||
|
advanced: [{ width: width, height: height }],
|
||||||
|
resizeMode: "none"
|
||||||
|
};
|
||||||
|
|
||||||
|
track
|
||||||
|
.applyConstraints(constraints)
|
||||||
|
.then(() => {
|
||||||
|
logger.info("Applied constraints successfully. New constraints: ", track.getConstraints());
|
||||||
|
})
|
||||||
|
.catch(e => logger.error("Failed to apply constraints.", e));
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
const audio = await navigator.mediaDevices.getUserMedia({
|
const audio = await navigator.mediaDevices.getUserMedia({
|
||||||
audio: {
|
audio: {
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { addContextMenuPatch } from "@vencord/types/api/ContextMenu";
|
import { addContextMenuPatch } from "@vencord/types/api/ContextMenu";
|
||||||
import { findStoreLazy } from "@vencord/types/webpack";
|
import { findStoreLazy } from "@vencord/types/webpack";
|
||||||
import { ContextMenu, FluxDispatcher, Menu } from "@vencord/types/webpack/common";
|
import { FluxDispatcher, Menu, useStateFromStores } from "@vencord/types/webpack/common";
|
||||||
|
|
||||||
import { addPatch } from "./shared";
|
import { addPatch } from "./shared";
|
||||||
|
|
||||||
|
@ -46,7 +46,8 @@ addPatch({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
addContextMenuPatch("textarea-context", children => () => {
|
addContextMenuPatch("textarea-context", children => {
|
||||||
|
const spellCheckEnabled = useStateFromStores([SpellCheckStore], () => SpellCheckStore.isEnabled());
|
||||||
const hasCorrections = Boolean(word && corrections?.length);
|
const hasCorrections = Boolean(word && corrections?.length);
|
||||||
|
|
||||||
children.push(
|
children.push(
|
||||||
|
@ -71,11 +72,9 @@ addContextMenuPatch("textarea-context", children => () => {
|
||||||
<Menu.MenuCheckboxItem
|
<Menu.MenuCheckboxItem
|
||||||
id="vcd-spellcheck-enabled"
|
id="vcd-spellcheck-enabled"
|
||||||
label="Enable Spellcheck"
|
label="Enable Spellcheck"
|
||||||
checked={SpellCheckStore.isEnabled()}
|
checked={spellCheckEnabled}
|
||||||
action={() => {
|
action={() => {
|
||||||
FluxDispatcher.dispatch({ type: "SPELLCHECK_TOGGLE" });
|
FluxDispatcher.dispatch({ type: "SPELLCHECK_TOGGLE" });
|
||||||
// Haven't found a good way to update state, so just close for now 🤷♀️
|
|
||||||
ContextMenu.close();
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Menu.MenuGroup>
|
</Menu.MenuGroup>
|
||||||
|
|
2
src/shared/settings.d.ts
vendored
2
src/shared/settings.d.ts
vendored
|
@ -20,7 +20,7 @@ export interface Settings {
|
||||||
arRPC?: boolean;
|
arRPC?: boolean;
|
||||||
appBadge?: boolean;
|
appBadge?: boolean;
|
||||||
disableMinSize?: boolean;
|
disableMinSize?: boolean;
|
||||||
|
clickTrayToShowHide?: boolean;
|
||||||
/** @deprecated use customTitleBar */
|
/** @deprecated use customTitleBar */
|
||||||
discordWindowsTitleBar?: boolean;
|
discordWindowsTitleBar?: boolean;
|
||||||
customTitleBar?: boolean;
|
customTitleBar?: boolean;
|
||||||
|
|
|
@ -81,7 +81,7 @@ export async function checkUpdates() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const raw = await githubGet("/repos/Vencord/Vesktop/releases/latest");
|
const raw = await githubGet("/repos/Vencord/Vesktop/releases/latest");
|
||||||
const data = JSON.parse(raw.toString("utf-8")) as ReleaseData;
|
const data: ReleaseData = await raw.json();
|
||||||
|
|
||||||
const oldVersion = app.getVersion();
|
const oldVersion = app.getVersion();
|
||||||
const newVersion = data.tag_name.replace(/^v/, "");
|
const newVersion = data.tag_name.replace(/^v/, "");
|
||||||
|
|
Loading…
Reference in a new issue