Compare commits
175 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b09f035ea9 | ||
|
|
44627b4cd4 | ||
|
|
97267ef89a | ||
|
|
132adcb733 | ||
|
|
738fa588a6 | ||
|
|
e63cff7a52 | ||
|
|
777a0e83fe | ||
|
|
612d35c96f | ||
|
|
0acd3e160a | ||
|
|
6993b2a7d4 | ||
|
|
2bd8ca96df | ||
|
|
4d82a6f41d | ||
|
|
cb33f1834b | ||
|
|
fb40f4b42d | ||
|
|
808eb56327 | ||
|
|
b636b65e55 | ||
|
|
7d30dcdb47 | ||
|
|
4f1615ecb3 | ||
|
|
463cd6dc46 | ||
|
|
8c007476c3 | ||
|
|
b20c77734c | ||
|
|
79cd2791f6 | ||
|
|
73de0fa535 | ||
|
|
83e74b98d5 | ||
|
|
2e4c834a90 | ||
|
|
2aa0b0fa20 | ||
|
|
3ac0ed3d78 | ||
|
|
eddc1de784 | ||
|
|
6483b3a3d9 | ||
|
|
38f0330eb2 | ||
|
|
15a49a31e1 | ||
|
|
33eb1af2be | ||
|
|
563ba1eebe | ||
|
|
62cf02e7b1 | ||
|
|
0f0bddbef9 | ||
|
|
0881143d57 | ||
|
|
effd950b2d | ||
|
|
4074e8d6ac | ||
|
|
779c8fa516 | ||
|
|
b059516707 | ||
|
|
dfa9d248d3 | ||
|
|
18925ad583 | ||
|
|
0b01732293 | ||
|
|
a89cd9d2ba | ||
|
|
2f35128acf | ||
|
|
7565eb39a4 | ||
|
|
d71d0c1cc4 | ||
|
|
dfc6970756 | ||
|
|
1429815fd1 | ||
|
|
aa397d003c | ||
|
|
40b952d8bf | ||
|
|
4974848a56 | ||
|
|
623fa5d709 | ||
|
|
fc33050496 | ||
|
|
e02acda6fa | ||
|
|
f11a72d4b5 | ||
|
|
49cd558fa1 | ||
|
|
378a1f7464 | ||
|
|
35d2dd6505 | ||
|
|
017c2c847f | ||
|
|
de2b4b7dd8 | ||
|
|
5dc5741771 | ||
|
|
971c490c41 | ||
|
|
0f804f9f6e | ||
|
|
fdf454722f | ||
|
|
208158f4d1 | ||
|
|
032b94e81d | ||
|
|
4988163744 | ||
|
|
ee7e71b9fb | ||
|
|
7efa54687a | ||
|
|
2917ff25e5 | ||
|
|
e3839c35b7 | ||
|
|
c39678d733 | ||
|
|
06dea79c74 | ||
|
|
96b0652a06 | ||
|
|
49e0411be6 | ||
|
|
94819e6f16 | ||
|
|
a232af06ed | ||
|
|
b24535483e | ||
|
|
9521c287b6 | ||
|
|
55b9edec39 | ||
|
|
1e9c70eed9 | ||
|
|
3262e083fa | ||
|
|
ec1c719553 | ||
|
|
cc62903b9c | ||
|
|
b17370cc7b | ||
|
|
886d02f7c3 | ||
|
|
0cad71f6ae | ||
|
|
b5eac15b42 | ||
|
|
19c3112d52 | ||
|
|
1c308d0e2c | ||
|
|
7f6db5eeda | ||
|
|
10b38e5b41 | ||
|
|
28282d1d76 | ||
|
|
e1512b72f4 | ||
|
|
46a2314173 | ||
|
|
e6dc026708 | ||
|
|
cac307d1fc | ||
|
|
4b8f374856 | ||
|
|
e6cc11fc0e | ||
|
|
43ca479fc8 | ||
|
|
7b0f64a9fc | ||
|
|
573a953a2f | ||
|
|
841cdcf672 | ||
|
|
0d93e08e99 | ||
|
|
c445c6194f | ||
|
|
e29d293855 | ||
|
|
e13a4eacb1 | ||
|
|
c070761f9d | ||
|
|
ae86c28247 | ||
|
|
9c95956c96 | ||
|
|
45f56c63a0 | ||
|
|
89af4316d3 | ||
|
|
a9bfb857ae | ||
|
|
b9e411ac90 | ||
|
|
670de01938 | ||
|
|
ef064eba3d | ||
|
|
d5f63da939 | ||
|
|
2aadc61af9 | ||
|
|
061fec44af | ||
|
|
b876f450c3 | ||
|
|
5f5febda9d | ||
|
|
94ba59afb5 | ||
|
|
566737017c | ||
|
|
196ee4e42c | ||
|
|
9bb02f8581 | ||
|
|
c76d7195a5 | ||
|
|
50a103710d | ||
|
|
e6e66e775c | ||
|
|
a5ec031a2f | ||
|
|
4dceadbbd2 | ||
|
|
6ee920ff2c | ||
|
|
28ad4a6f73 | ||
|
|
c5ac3e64a6 | ||
|
|
6dc26aea6a | ||
|
|
9003b94f85 | ||
|
|
f7b7931847 | ||
|
|
b87bcaefe9 | ||
|
|
3108de7c79 | ||
|
|
57006e7e52 | ||
|
|
8f1ea1f440 | ||
|
|
26b6fb13d4 | ||
|
|
e1971f55a0 | ||
|
|
ba2618878e | ||
|
|
87595deae7 | ||
|
|
6ab7030fdf | ||
|
|
5ba84e8a0d | ||
|
|
61f9559984 | ||
|
|
5fa9264bdb | ||
|
|
c9f0920f71 | ||
|
|
f502d04b5f | ||
|
|
08090e3764 | ||
|
|
a95f7f8fbd | ||
|
|
9d62cc9437 | ||
|
|
2b4f7a07d2 | ||
|
|
be9642eff1 | ||
|
|
d884b7d3d6 | ||
|
|
6d35be79b8 | ||
|
|
70ca06eb16 | ||
|
|
210ddbae06 | ||
|
|
3ee57831b9 | ||
|
|
72f83c3ac4 | ||
|
|
13d87dc85e | ||
|
|
0415cb77f7 | ||
|
|
b4da701080 | ||
|
|
ed2361ff22 | ||
|
|
c587e93c56 | ||
|
|
6ba0896a6e | ||
|
|
843b57e03e | ||
|
|
49fb4c68b6 | ||
|
|
477ecbb4ba | ||
|
|
4abce9d084 | ||
|
|
7f54858b27 | ||
|
|
be176fab71 | ||
|
|
e60f04bb79 |
@@ -4,3 +4,5 @@
|
||||
# all permissions at the defaults (public repos read only, 0 permissions):
|
||||
# https://github.com/settings/personal-access-tokens/new
|
||||
GITHUB_TOKEN=
|
||||
|
||||
ELECTRON_LAUNCH_FLAGS="--ozone-platform-hint=auto --enable-webrtc-pipewire-capturer --enable-features=WaylandWindowDecorations"
|
||||
@@ -22,7 +22,13 @@
|
||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||
"spaced-comment": ["error", "always", { "markers": ["!"] }],
|
||||
"yoda": "error",
|
||||
"prefer-destructuring": ["error", { "object": true, "array": false }],
|
||||
"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 }],
|
||||
|
||||
58
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
58
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
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. -->
|
||||
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
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.
|
||||
38
.github/workflows/meta.yml
vendored
Normal file
38
.github/workflows/meta.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Update metainfo on release
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: pnpm/action-setup@v2 # 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: Install dependencies
|
||||
run: pnpm i
|
||||
|
||||
- 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 }}
|
||||
40
.github/workflows/release.yml
vendored
40
.github/workflows/release.yml
vendored
@@ -12,17 +12,41 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
include:
|
||||
- os: macos-latest
|
||||
platform: mac
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
- os: windows-latest
|
||||
platform: windows
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v3
|
||||
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- name: Use Node.js 18.18.2
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 18.18.2
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Run Electron Builder
|
||||
uses: samuelmeuli/action-electron-builder@e4b12cd06ddf023422f1ac4e39632bd76f6e6928
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE: true
|
||||
if: ${{ matrix.platform != 'mac' }}
|
||||
run: |
|
||||
pnpm electron-builder --${{ matrix.platform }} --publish always
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run Electron Builder
|
||||
if: ${{ matrix.platform == 'mac' }}
|
||||
run: |
|
||||
pnpm electron-builder --${{ matrix.platform }} --publish always
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.APPLE_SIGNING_CERT }}
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -14,10 +14,10 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
|
||||
|
||||
- name: Use Node.js 18
|
||||
- name: Use Node.js 18.18.2
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 18.18.2
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
|
||||
25
.github/workflows/winget-submission.yml
vendored
Normal file
25
.github/workflows/winget-submission.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Submit to Winget Community Repo
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
type: string
|
||||
description: The release tag to submit
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
winget:
|
||||
name: Publish winget package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Submit package to Winget Community Repo
|
||||
uses: vedantmgoyal2009/winget-releaser@e68d386d5d6a1cef8cb0fb5e62b77ebcb83e7d58 # v2
|
||||
with:
|
||||
identifier: Vencord.Vesktop
|
||||
token: ${{ secrets.WINGET_PAT }}
|
||||
installers-regex: '\.exe$'
|
||||
release-tag: ${{ inputs.tag || github.event.release.tag_name }}
|
||||
fork-user: shiggybot
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
dist
|
||||
node_modules
|
||||
.env
|
||||
.DS_Store
|
||||
.idea/
|
||||
.pnpm-store/
|
||||
49
.vscode/settings.json
vendored
49
.vscode/settings.json
vendored
@@ -1,24 +1,25 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"cSpell.words": ["Vesktop"]
|
||||
}
|
||||
|
||||
71
README.md
71
README.md
@@ -1,53 +1,68 @@
|
||||
# Vencord Desktop
|
||||
# Vesktop
|
||||
|
||||
Vencord Desktop is a cross platform desktop app aiming to give you a snappier Discord experience with Vencord pre-installed
|
||||
Vesktop is a custom Discord desktop app
|
||||
|
||||
Vencord Desktop is currently in beta
|
||||
**Main features**:
|
||||
- Vencord preinstalled
|
||||
- Much more lightweight and faster than the official Discord app
|
||||
- Linux Screenshare with sound & wayland
|
||||
- Much better privacy, since Discord has no access to your system
|
||||
|
||||
**Not yet supported**:
|
||||
- Global Keybinds
|
||||
- see the [Roadmap](https://github.com/Vencord/Vesktop/issues/324)
|
||||
|
||||
Bug reports, feature requests & contributions are highly appreciated!!
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Installing
|
||||
|
||||
### Windows
|
||||
|
||||
Download and run Vencord-Desktop-Setup-VERSION.exe from [releases](https://github.com/Vencord/Desktop/releases/latest)
|
||||
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)
|
||||
|
||||
### Mac
|
||||
|
||||
Download and run Vencord-Desktop-VERSION.dmg from [releases](https://github.com/Vencord/Desktop/releases/latest)
|
||||
If you don't know the difference, pick amd64
|
||||
|
||||
- [amd64 / x86_64](https://vencord.dev/download/vesktop/amd64/dmg)
|
||||
- [arm64 / aarch64](https://vencord.dev/download/vesktop/arm64/dmg)
|
||||
|
||||
### Linux
|
||||
|
||||
#### Arch based
|
||||
[](https://flathub.org/apps/dev.vencord.Vesktop)
|
||||
|
||||
Install [vencord-desktop-git](https://aur.archlinux.org/packages/vencord-desktop-git) from the AUR using your favourite AUR helper, for example [yay](https://github.com/Jguer/yay)
|
||||
If you don't know the difference, pick amd64.
|
||||
|
||||
#### Ubuntu/Debian based
|
||||
- amd64 / x86_64
|
||||
- [AppImage](https://vencord.dev/download/vesktop/amd64/appimage)
|
||||
- [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
|
||||
- [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)
|
||||
- [tarball](https://vencord.dev/download/vesktop/arm64/tar)
|
||||
|
||||
Download Vencord-Desktop-VERSION.deb from [releases](https://github.com/Vencord/Desktop/releases/latest)
|
||||
#### Community packages
|
||||
|
||||
#### Fedora/RHEL based
|
||||
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!
|
||||
|
||||
Download Vencord-Desktop-VERSION.rpm from [releases](https://github.com/Vencord/Desktop/releases/latest)
|
||||
- Arch Linux: [Vesktop on the Arch user repository](https://aur.archlinux.org/packages?K=vesktop)
|
||||
- NixOS: https://nixos.wiki/wiki/Discord#Vesktop
|
||||
- Windows - Scoop: https://scoop.sh/#/apps?q=Vesktop
|
||||
|
||||
#### Other
|
||||
## Building from Source
|
||||
|
||||
Either download Vencord-Desktop-VERSION.AppImage and just run it directly or grab Vencord-Desktop-VERSION.tar.gz, extract it somewhere and run `vencorddesktop`.
|
||||
|
||||
A flatpak is planned, if you want packages for other repos, feel free to create them and they can be linked as unofficial here
|
||||
|
||||
## Building
|
||||
|
||||
Packaging will create builds in the dist/ folder. You can then install them like mentioned above or distribute them
|
||||
Packaging will create builds in the dist/ folder
|
||||
|
||||
```sh
|
||||
git clone https://github.com/Vencord/Desktop
|
||||
cd Desktop
|
||||
git clone https://github.com/Vencord/Vesktop
|
||||
cd Vesktop
|
||||
|
||||
# Install Dependencies
|
||||
pnpm i
|
||||
@@ -62,11 +77,3 @@ pnpm package --linux pacman
|
||||
# Or package to a directory only
|
||||
pnpm package:dir
|
||||
```
|
||||
|
||||
## Motivation
|
||||
|
||||
The official Discord Desktop app is very resource heavy compared to Discord in your Browser. There are multiple alternative Electron apps (ArmCord, WebCord, probably more) that prove how much of a performance gain you can gain by using a custom app. ArmCord already supports Vencord but makes it pretty limited for us. Making our own standalone app gives us much more control.
|
||||
|
||||
This is just a random idea I (V) got, and might not actually ever be finished heh
|
||||
|
||||
Gluon also seems very attractive for this because of how lightweight it can be and because unlike electron, streaming just works out of the box like in any chromium browser. However, at the time of writing this, it still lacks some features necessary to make it work (synchronous ipc or a way to get node process variables into the onLoad function for instance, plus onLoad seems to load a little too late sometimes)
|
||||
|
||||
BIN
build/icon.icns
Normal file
BIN
build/icon.icns
Normal file
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
!macro preInit
|
||||
SetRegView 64
|
||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop"
|
||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop"
|
||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop"
|
||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop"
|
||||
SetRegView 32
|
||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop"
|
||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop"
|
||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop"
|
||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop"
|
||||
!macroend
|
||||
|
||||
179
meta/dev.vencord.Vesktop.metainfo.xml
Normal file
179
meta/dev.vencord.Vesktop.metainfo.xml
Normal file
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<component type="desktop-application">
|
||||
<!--Created with jdAppStreamEdit 7.1-->
|
||||
<id>dev.vencord.Vesktop</id>
|
||||
<name>Vesktop</name>
|
||||
<summary>Snappier Discord app with Vencord</summary>
|
||||
<developer_name>Vencord Contributors</developer_name>
|
||||
<launchable type="desktop-id">dev.vencord.Vesktop.desktop</launchable>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0</project_license>
|
||||
<project_group>Vencord</project_group>
|
||||
<description>
|
||||
<p>Vesktop is a cross platform desktop app aiming to give you a snappier Discord experience with Vencord pre-installed.</p>
|
||||
<p>Vesktop comes bundled with Venmic, a purpose-built library to provide functioning audio screenshare.</p>
|
||||
</description>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>Vencord settings page and about window open</caption>
|
||||
<image type="source">https://vencord.dev/assets/screenshots/vesktop-1-appstream.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<caption>A dialog showing screenshare options</caption>
|
||||
<image type="source">https://vencord.dev/assets/screenshots/vesktop-2-appstream.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<caption>A screenshot of a Discord server</caption>
|
||||
<image type="source">https://vencord.dev/assets/screenshots/vesktop-3-appstream.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<releases>
|
||||
<release version="1.5.0" date="2024-01-16" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v1.5.0</url>
|
||||
<description>
|
||||
<p>What's Changed</p>
|
||||
<ul>
|
||||
<li>fully renamed to Vesktop. You will likely have to login to Discord again. You might have to re-create your vesktop shortcut</li>
|
||||
<li>added option to disable smooth scrolling by @ZirixCZ</li>
|
||||
<li>added setting to disable hardware acceleration by @zt64</li>
|
||||
<li>fixed adding connections</li>
|
||||
<li>fixed / improved discord popouts</li>
|
||||
<li>you can now use the custom discord titlebar on linux/mac</li>
|
||||
<li>the splash window is now draggable</li>
|
||||
<li>now signed on mac</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.4.4" date="2023-12-02" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.4.4</url>
|
||||
<description>
|
||||
<p>What's Changed</p>
|
||||
<ul>
|
||||
<li>improve venmic system compatibility by @Curve</li>
|
||||
<li>Update steamdeck controller layout by @AAGaming00</li>
|
||||
<li>feat: Add option to disable smooth scrolling by @ZirixCZ</li>
|
||||
<li>unblur shiggy in splash screen by @viacoro</li>
|
||||
<li>update electron & arrpc @D3SOX</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.4.3" date="2023-11-01" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.4.3</url>
|
||||
</release>
|
||||
<release version="0.4.2" date="2023-10-26" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.4.2</url>
|
||||
</release>
|
||||
<release version="0.4.1" date="2023-10-24" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.4.1</url>
|
||||
</release>
|
||||
<release version="0.4.0" date="2023-10-21" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.4.0</url>
|
||||
</release>
|
||||
<release version="0.3.3" date="2023-09-30" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.3.3</url>
|
||||
</release>
|
||||
<release version="0.3.2" date="2023-09-25" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.3.2</url>
|
||||
</release>
|
||||
<release version="0.3.1" date="2023-09-25" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.3.1</url>
|
||||
</release>
|
||||
<release version="0.3.0" date="2023-08-16" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.3.0</url>
|
||||
</release>
|
||||
<release version="0.2.9" date="2023-08-12" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.9</url>
|
||||
</release>
|
||||
<release version="0.2.8" date="2023-08-02" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.8</url>
|
||||
</release>
|
||||
<release version="0.2.7" date="2023-07-26" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.7</url>
|
||||
</release>
|
||||
<release version="0.2.6" date="2023-07-04" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.6</url>
|
||||
</release>
|
||||
<release version="0.2.5" date="2023-06-26" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.5</url>
|
||||
</release>
|
||||
<release version="0.2.4" date="2023-06-25" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.4</url>
|
||||
</release>
|
||||
<release version="0.2.3" date="2023-06-23" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.3</url>
|
||||
</release>
|
||||
<release version="0.2.2" date="2023-06-21" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.2</url>
|
||||
</release>
|
||||
<release version="0.2.1" date="2023-06-21" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.1</url>
|
||||
</release>
|
||||
<release version="0.2.0" date="2023-05-03" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.2.0</url>
|
||||
</release>
|
||||
<release version="0.1.9" date="2023-04-27" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.9</url>
|
||||
</release>
|
||||
<release version="0.1.8" date="2023-04-15" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.8</url>
|
||||
</release>
|
||||
<release version="0.1.7" date="2023-04-15" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.7</url>
|
||||
</release>
|
||||
<release version="0.1.6" date="2023-04-11" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.6</url>
|
||||
</release>
|
||||
<release version="0.1.5" date="2023-04-10" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.5</url>
|
||||
</release>
|
||||
<release version="0.1.4" date="2023-04-09" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.4</url>
|
||||
</release>
|
||||
<release version="0.1.3" date="2023-04-06" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.3</url>
|
||||
</release>
|
||||
<release version="0.1.2" date="2023-04-05" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.2</url>
|
||||
</release>
|
||||
<release version="0.1.1" date="2023-04-04" type="stable">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.1</url>
|
||||
</release>
|
||||
<release version="0.1.0" date="2023-04-04" type="development">
|
||||
<url>https://github.com/Vencord/Vesktop/releases/tag/v0.1.0</url>
|
||||
</release>
|
||||
</releases>
|
||||
<url type="homepage">https://vencord.dev/</url>
|
||||
<url type="bugtracker">https://github.com/Vencord/Vesktop/issues</url>
|
||||
<url type="faq">https://vencord.dev/faq/</url>
|
||||
<url type="help">https://github.com/Vencord/Vesktop/issues</url>
|
||||
<url type="donation">https://github.com/sponsors/Vendicated</url>
|
||||
<url type="vcs-browser">https://github.com/Vencord/Vesktop</url>
|
||||
<categories>
|
||||
<category>InstantMessaging</category>
|
||||
<category>AudioVideo</category>
|
||||
</categories>
|
||||
<requires>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<display_length compare="ge">420</display_length>
|
||||
<internet>always</internet>
|
||||
</requires>
|
||||
<recommends>
|
||||
<control>voice</control>
|
||||
<display_length compare="ge">760</display_length>
|
||||
<display_length compare="le">1200</display_length>
|
||||
</recommends>
|
||||
<content_rating type="oars-1.1">
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
<content_attribute id="social-audio">intense</content_attribute>
|
||||
<content_attribute id="social-contacts">intense</content_attribute>
|
||||
<content_attribute id="social-info">intense</content_attribute>
|
||||
</content_rating>
|
||||
<keywords>
|
||||
<keyword>Discord</keyword>
|
||||
<keyword>Vencord</keyword>
|
||||
<keyword>Vesktop</keyword>
|
||||
<keyword>Privacy</keyword>
|
||||
<keyword>Mod</keyword>
|
||||
</keywords>
|
||||
</component>
|
||||
104
package.json
104
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "VencordDesktop",
|
||||
"version": "0.2.4",
|
||||
"name": "vesktop",
|
||||
"version": "1.5.1",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
@@ -20,44 +20,49 @@
|
||||
"start:watch": "pnpm build:dev && tsx scripts/startWatch.mts",
|
||||
"test": "pnpm lint && pnpm testTypes",
|
||||
"testTypes": "tsc --noEmit",
|
||||
"watch": "pnpm build --watch"
|
||||
"watch": "pnpm build --watch",
|
||||
"updateMeta": "tsx scripts/utils/updateMeta.mts"
|
||||
},
|
||||
"dependencies": {
|
||||
"arrpc": "github:OpenAsar/arrpc#3eb5d36a5e9295d3aeafc49975df5d399eb627fd"
|
||||
"arrpc": "github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@vencord/venmic": "^3.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/react": "^18.0.33",
|
||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"@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.0.3",
|
||||
"electron": "^25.2.0",
|
||||
"electron-builder": "^23.6.0",
|
||||
"esbuild": "^0.17.14",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"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",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-plugin-license-header": "^0.6.0",
|
||||
"eslint-plugin-path-alias": "^1.0.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"prettier": "^2.8.7",
|
||||
"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",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tsx": "^3.12.6",
|
||||
"type-fest": "^3.8.0",
|
||||
"typescript": "^5.0.2"
|
||||
"tsx": "^4.7.1",
|
||||
"type-fest": "^4.12.0",
|
||||
"typescript": "^5.4.2",
|
||||
"xml-formatter": "^3.6.2"
|
||||
},
|
||||
"packageManager": "pnpm@8.1.1",
|
||||
"packageManager": "pnpm@8.11.0",
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"pnpm": ">=8"
|
||||
},
|
||||
"build": {
|
||||
"appId": "dev.vencord.desktop",
|
||||
"productName": "Vencord Desktop",
|
||||
"appId": "dev.vencord.vesktop",
|
||||
"productName": "Vesktop",
|
||||
"files": [
|
||||
"!*",
|
||||
"dist/js",
|
||||
@@ -65,17 +70,43 @@
|
||||
"package.json",
|
||||
"LICENSE"
|
||||
],
|
||||
"beforePack": "scripts/build/sandboxFix.js",
|
||||
"linux": {
|
||||
"icon": "build/icon.icns",
|
||||
"category": "Network",
|
||||
"maintainer": "vendicated+vencord-desktop@riseup.net",
|
||||
"maintainer": "vendicated+vesktop@riseup.net",
|
||||
"target": [
|
||||
"deb",
|
||||
"tar.gz",
|
||||
"rpm",
|
||||
"AppImage"
|
||||
{
|
||||
"target": "deb",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "tar.gz",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "rpm",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"desktop": {
|
||||
"Name": "Vencord Desktop",
|
||||
"Name": "Vesktop",
|
||||
"GenericName": "Internet Messenger",
|
||||
"Type": "Application",
|
||||
"Categories": "Network;InstantMessaging;Chat;",
|
||||
@@ -92,7 +123,13 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"category": "Network"
|
||||
"category": "Network",
|
||||
"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
|
||||
}
|
||||
},
|
||||
"nsis": {
|
||||
"include": "build/installer.nsh",
|
||||
@@ -101,12 +138,11 @@
|
||||
"win": {
|
||||
"target": [
|
||||
"nsis",
|
||||
"portable"
|
||||
"zip"
|
||||
]
|
||||
},
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"releaseType": "release"
|
||||
"provider": "github"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2792
pnpm-lock.yaml
generated
2792
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,11 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { BuildContext, BuildOptions, context } from "esbuild";
|
||||
import { copyFile } from "fs/promises";
|
||||
|
||||
import vencordDep from "./vencordDep.mjs";
|
||||
|
||||
@@ -33,7 +34,23 @@ async function createContext(options: BuildOptions) {
|
||||
contexts.push(await context(options));
|
||||
}
|
||||
|
||||
async function copyVenmic() {
|
||||
if (process.platform !== "linux") return;
|
||||
|
||||
return Promise.all([
|
||||
copyFile(
|
||||
"./node_modules/@vencord/venmic/prebuilds/venmic-addon-linux-x64/node-napi-v7.node",
|
||||
"./static/dist/venmic-x64.node"
|
||||
),
|
||||
copyFile(
|
||||
"./node_modules/@vencord/venmic/prebuilds/venmic-addon-linux-arm64/node-napi-v7.node",
|
||||
"./static/dist/venmic-arm64.node"
|
||||
)
|
||||
]).catch(() => console.warn("Failed to copy venmic. Building without venmic support"));
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
copyVenmic(),
|
||||
createContext({
|
||||
...NodeCommonOpts,
|
||||
entryPoints: ["src/main/index.ts"],
|
||||
@@ -54,15 +71,13 @@ await Promise.all([
|
||||
}),
|
||||
createContext({
|
||||
...CommonOpts,
|
||||
globalName: "VencordDesktop",
|
||||
globalName: "Vesktop",
|
||||
entryPoints: ["src/renderer/index.ts"],
|
||||
outfile: "dist/js/renderer.js",
|
||||
format: "iife",
|
||||
inject: ["./scripts/build/injectReact.mjs"],
|
||||
jsxFactory: "VencordCreateElement",
|
||||
jsxFragment: "VencordFragment",
|
||||
// Work around https://github.com/evanw/esbuild/issues/2460
|
||||
tsconfig: "./scripts/build/tsconfig.esbuild.json",
|
||||
external: ["@vencord/types/*"],
|
||||
plugins: [vencordDep],
|
||||
footer: { js: "//# sourceURL=VCDRenderer" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
|
||||
74
scripts/build/sandboxFix.js
Normal file
74
scripts/build/sandboxFix.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
// Based on https://github.com/gergof/electron-builder-sandbox-fix/blob/master/lib/index.js
|
||||
|
||||
const fs = require("fs/promises");
|
||||
const path = require("path");
|
||||
let isApplied = false;
|
||||
|
||||
const hook = async () => {
|
||||
if (isApplied) return;
|
||||
isApplied = true;
|
||||
if (process.platform !== "linux") {
|
||||
// this fix is only required on linux
|
||||
return;
|
||||
}
|
||||
const AppImageTarget = require("app-builder-lib/out/targets/AppImageTarget");
|
||||
const oldBuildMethod = AppImageTarget.default.prototype.build;
|
||||
AppImageTarget.default.prototype.build = async function (...args) {
|
||||
console.log("Running AppImage builder hook", args);
|
||||
const oldPath = args[0];
|
||||
const newPath = oldPath + "-appimage-sandbox-fix";
|
||||
// just in case
|
||||
try {
|
||||
await fs.rm(newPath, {
|
||||
recursive: true
|
||||
});
|
||||
} catch {}
|
||||
|
||||
console.log("Copying to apply appimage fix", oldPath, newPath);
|
||||
await fs.cp(oldPath, newPath, {
|
||||
recursive: true
|
||||
});
|
||||
args[0] = newPath;
|
||||
|
||||
const executable = path.join(newPath, this.packager.executableName);
|
||||
|
||||
const loaderScript = `
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "\${BASH_SOURCE[0]}" )" && pwd )"
|
||||
IS_STEAMOS=0
|
||||
|
||||
if [[ "$SteamOS" == "1" && "$SteamGamepadUI" == "1" ]]; then
|
||||
echo "Running Vesktop on SteamOS, disabling sandbox"
|
||||
IS_STEAMOS=1
|
||||
fi
|
||||
|
||||
exec "$SCRIPT_DIR/${this.packager.executableName}.bin" "$([ "$IS_STEAMOS" == 1 ] && echo '--no-sandbox')" "$@"
|
||||
`.trim();
|
||||
|
||||
try {
|
||||
await fs.rename(executable, executable + ".bin");
|
||||
await fs.writeFile(executable, loaderScript);
|
||||
await fs.chmod(executable, 0o755);
|
||||
} catch (e) {
|
||||
console.error("failed to create loder for sandbox fix: " + e.message);
|
||||
throw new Error("Failed to create loader for sandbox fix");
|
||||
}
|
||||
|
||||
const ret = await oldBuildMethod.apply(this, args);
|
||||
|
||||
await fs.rm(newPath, {
|
||||
recursive: true
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = hook;
|
||||
@@ -1,7 +0,0 @@
|
||||
// Work around https://github.com/evanw/esbuild/issues/2460
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -8,4 +8,4 @@ import "./utils/dotenv";
|
||||
|
||||
import { spawnNodeModuleBin } from "./utils/spawn.mjs";
|
||||
|
||||
spawnNodeModuleBin("electron", ["."]);
|
||||
spawnNodeModuleBin("electron", [process.cwd(), ...(process.env.ELECTRON_LAUNCH_FLAGS?.split(" ") ?? [])]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
|
||||
93
scripts/utils/updateMeta.mts
Normal file
93
scripts/utils/updateMeta.mts
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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 { promises as fs } from "node:fs";
|
||||
|
||||
import { DOMParser, XMLSerializer } from "@xmldom/xmldom";
|
||||
import xmlFormat from "xml-formatter";
|
||||
|
||||
function generateDescription(description: string, descriptionNode: Element) {
|
||||
const lines = description.replace(/\r/g, "").split("\n");
|
||||
let currentList: Element | null = null;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
if (line.includes("New Contributors")) {
|
||||
// we're done, don't parse any more since the new contributors section is the last one
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.startsWith("## ")) {
|
||||
const pNode = descriptionNode.ownerDocument.createElement("p");
|
||||
pNode.textContent = line.slice(3);
|
||||
descriptionNode.appendChild(pNode);
|
||||
} else if (line.startsWith("* ")) {
|
||||
const liNode = descriptionNode.ownerDocument.createElement("li");
|
||||
liNode.textContent = line.slice(2).split("in https://github.com")[0].trim(); // don't include links to github
|
||||
|
||||
if (!currentList) {
|
||||
currentList = descriptionNode.ownerDocument.createElement("ul");
|
||||
}
|
||||
|
||||
currentList.appendChild(liNode);
|
||||
}
|
||||
|
||||
if (currentList && !lines[i + 1].startsWith("* ")) {
|
||||
descriptionNode.appendChild(currentList);
|
||||
currentList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const latestReleaseInformation = await fetch("https://api.github.com/repos/Vencord/Vesktop/releases/latest", {
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
"X-Github-Api-Version": "2022-11-28"
|
||||
}
|
||||
}).then(res => res.json());
|
||||
|
||||
const metaInfo = await fs.readFile("./meta/dev.vencord.Vesktop.metainfo.xml", "utf-8");
|
||||
|
||||
const parser = new DOMParser().parseFromString(metaInfo, "text/xml");
|
||||
|
||||
const releaseList = parser.getElementsByTagName("releases")[0];
|
||||
|
||||
for (let i = 0; i < releaseList.childNodes.length; i++) {
|
||||
const release = releaseList.childNodes[i] as Element;
|
||||
|
||||
if (release.nodeType === 1 && release.getAttribute("version") === latestReleaseInformation.name) {
|
||||
console.log("Latest release already added, nothing to be done");
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
const release = parser.createElement("release");
|
||||
release.setAttribute("version", latestReleaseInformation.name);
|
||||
release.setAttribute("date", latestReleaseInformation.published_at.split("T")[0]);
|
||||
release.setAttribute("type", "stable");
|
||||
|
||||
const releaseUrl = parser.createElement("url");
|
||||
releaseUrl.textContent = latestReleaseInformation.html_url;
|
||||
|
||||
release.appendChild(releaseUrl);
|
||||
|
||||
const description = parser.createElement("description");
|
||||
|
||||
// we're not using a full markdown parser here since we don't have a lot of formatting options to begin with
|
||||
generateDescription(latestReleaseInformation.body, description);
|
||||
|
||||
release.appendChild(description);
|
||||
|
||||
releaseList.insertBefore(release, releaseList.childNodes[0]);
|
||||
|
||||
const output = xmlFormat(new XMLSerializer().serializeToString(parser), {
|
||||
lineSeparator: "\n",
|
||||
collapseContent: true,
|
||||
indentation: " "
|
||||
});
|
||||
|
||||
await fs.writeFile("./meta/dev.vencord.Vesktop.metainfo.xml", output, "utf-8");
|
||||
7
src/globals.d.ts
vendored
7
src/globals.d.ts
vendored
@@ -1,13 +1,12 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
declare global {
|
||||
export var VencordDesktopNative: typeof import("preload/VencordDesktopNative").VencordDesktopNative;
|
||||
export var VencordDesktop: typeof import("renderer/index");
|
||||
export var vcdLS: typeof localStorage;
|
||||
export var VesktopNative: typeof import("preload/VesktopNative").VesktopNative;
|
||||
export var Vesktop: typeof import("renderer/index");
|
||||
export var VCDP: any;
|
||||
|
||||
export var IS_DEV: boolean;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { app, BrowserWindow } from "electron";
|
||||
import { readFileSync } from "fs";
|
||||
import { BrowserWindow } from "electron";
|
||||
import { join } from "path";
|
||||
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||
|
||||
@@ -15,14 +14,15 @@ export function createAboutWindow() {
|
||||
const about = new BrowserWindow({
|
||||
center: true,
|
||||
autoHideMenuBar: true,
|
||||
icon: ICON_PATH
|
||||
icon: ICON_PATH,
|
||||
webPreferences: {
|
||||
preload: join(__dirname, "updaterPreload.js")
|
||||
}
|
||||
});
|
||||
|
||||
makeLinksOpenExternally(about);
|
||||
|
||||
const html = readFileSync(join(VIEW_DIR, "about.html"), "utf-8").replaceAll("%VERSION%", app.getVersion());
|
||||
|
||||
about.loadURL("data:text/html;charset=utf-8," + html);
|
||||
about.loadFile(join(VIEW_DIR, "about.html"));
|
||||
|
||||
return about;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -23,11 +23,17 @@ let lastIndex: null | number = -1;
|
||||
|
||||
export function setBadgeCount(count: number) {
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
case "linux":
|
||||
if (count === -1) count = 0;
|
||||
app.setBadgeCount(count);
|
||||
break;
|
||||
case "darwin":
|
||||
if (count === 0) {
|
||||
app.dock.setBadge("");
|
||||
break;
|
||||
}
|
||||
app.dock.setBadge(count === -1 ? "•" : count.toString());
|
||||
break;
|
||||
case "win32":
|
||||
const [index, description] = getBadgeIndexAndDescription(count);
|
||||
if (lastIndex === index) break;
|
||||
|
||||
@@ -1,21 +1,38 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import Server from "arrpc";
|
||||
import { send as sendToBridge } from "arrpc/src/bridge";
|
||||
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;
|
||||
|
||||
server = await new Server();
|
||||
server.on("activity", sendToBridge);
|
||||
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);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { app } from "electron";
|
||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
||||
import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
interface AutoStart {
|
||||
@@ -17,7 +17,16 @@ interface AutoStart {
|
||||
function makeAutoStartLinux(): AutoStart {
|
||||
const configDir = process.env.XDG_CONFIG_HOME || join(process.env.HOME!, ".config");
|
||||
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 {
|
||||
isEnabled: () => existsSync(file),
|
||||
@@ -25,12 +34,11 @@ function makeAutoStartLinux(): AutoStart {
|
||||
const desktopFile = `
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Version=1.0
|
||||
Name=Vencord
|
||||
Comment=Vencord autostart script
|
||||
Exec=${process.execPath}
|
||||
Terminal=false
|
||||
Name=Vesktop
|
||||
Comment=Vesktop autostart script
|
||||
Exec=${commandLine}
|
||||
StartupNotify=false
|
||||
Terminal=false
|
||||
`.trim();
|
||||
|
||||
mkdirSync(dir, { recursive: true });
|
||||
|
||||
@@ -1,26 +1,63 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { app } from "electron";
|
||||
import { existsSync, readdirSync, renameSync, rmdirSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("userData"), "VencordDesktop");
|
||||
const LEGACY_DATA_DIR = join(app.getPath("appData"), "VencordDesktop", "VencordDesktop");
|
||||
export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("userData"));
|
||||
// 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"));
|
||||
|
||||
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
||||
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
|
||||
export const VENCORD_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json");
|
||||
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");
|
||||
|
||||
export const USER_AGENT = `VencordDesktop/${app.getVersion()} (https://github.com/Vencord/Desktop)`;
|
||||
export const USER_AGENT = `Vesktop/${app.getVersion()} (https://github.com/Vencord/Vesktop)`;
|
||||
|
||||
// dimensions shamelessly stolen from Discord Desktop :3
|
||||
export const MIN_WIDTH = 940;
|
||||
export const MIN_HEIGHT = 500;
|
||||
export const DEFAULT_WIDTH = 1280;
|
||||
export const DEFAULT_HEIGHT = 720;
|
||||
|
||||
export const DISCORD_HOSTNAMES = ["discord.com", "canary.discord.com", "ptb.discord.com"];
|
||||
|
||||
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"
|
||||
};
|
||||
|
||||
export const BrowserUserAgent = BrowserUserAgents[process.platform] || BrowserUserAgents.windows;
|
||||
|
||||
export const enum MessageBoxChoice {
|
||||
Default,
|
||||
Cancel
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -9,12 +9,13 @@ import { BrowserWindow } from "electron/main";
|
||||
import { copyFileSync, mkdirSync, readdirSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { SplashProps } from "shared/browserWinProperties";
|
||||
import { VIEW_DIR } from "shared/paths";
|
||||
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||
|
||||
import { autoStart } from "./autoStart";
|
||||
import { DATA_DIR } from "./constants";
|
||||
import { createWindows } from "./mainWindow";
|
||||
import { Settings } from "./settings";
|
||||
import { Settings, State } from "./settings";
|
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||
|
||||
interface Data {
|
||||
minimizeToTray: boolean;
|
||||
@@ -30,9 +31,12 @@ export function createFirstLaunchTour() {
|
||||
frame: true,
|
||||
autoHideMenuBar: true,
|
||||
height: 470,
|
||||
width: 550
|
||||
width: 550,
|
||||
icon: ICON_PATH
|
||||
});
|
||||
|
||||
makeLinksOpenExternally(win);
|
||||
|
||||
win.loadFile(join(VIEW_DIR, "first-launch.html"));
|
||||
win.webContents.addListener("console-message", (_e, _l, msg) => {
|
||||
if (msg === "cancel") return app.exit();
|
||||
@@ -40,9 +44,9 @@ export function createFirstLaunchTour() {
|
||||
if (!msg.startsWith("form:")) return;
|
||||
const data = JSON.parse(msg.slice(5)) as Data;
|
||||
|
||||
State.store.firstLaunch = false;
|
||||
Settings.store.minimizeToTray = data.minimizeToTray;
|
||||
Settings.store.discordBranch = data.discordBranch;
|
||||
Settings.store.firstLaunch = false;
|
||||
Settings.store.arRPC = data.richPresence;
|
||||
|
||||
if (data.autoStart) autoStart.enable();
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import "./ipc";
|
||||
|
||||
import { app, BrowserWindow } from "electron";
|
||||
import { app, BrowserWindow, nativeTheme } from "electron";
|
||||
import { checkUpdates } from "updater/main";
|
||||
|
||||
import { ICON_PATH } from "../shared/paths";
|
||||
import { DATA_DIR } from "./constants";
|
||||
import { createFirstLaunchTour } from "./firstLaunch";
|
||||
import { createWindows, mainWin } from "./mainWindow";
|
||||
import { registerMediaPermissionsHandler } from "./mediaPermissions";
|
||||
import { registerScreenShareHandler } from "./screenShare";
|
||||
import { Settings } from "./settings";
|
||||
import { Settings, State } from "./settings";
|
||||
import { isDeckGameMode } from "./utils/steamOS";
|
||||
|
||||
if (IS_DEV) {
|
||||
require("source-map-support").install();
|
||||
@@ -24,19 +25,27 @@ if (IS_DEV) {
|
||||
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
|
||||
|
||||
function init() {
|
||||
// <-- BEGIN COPY PASTED FROM DISCORD -->
|
||||
const { disableSmoothScroll, hardwareAcceleration } = Settings.store;
|
||||
|
||||
if (hardwareAcceleration === false) app.disableHardwareAcceleration();
|
||||
if (disableSmoothScroll) {
|
||||
app.commandLine.appendSwitch("disable-smooth-scrolling");
|
||||
}
|
||||
|
||||
// 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
|
||||
app.commandLine.appendSwitch(
|
||||
"disable-features",
|
||||
"WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService"
|
||||
"WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService,WidgetLayering"
|
||||
);
|
||||
|
||||
// <-- END COPY PASTED FROM DISCORD -->
|
||||
// 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";
|
||||
|
||||
app.on("second-instance", (_event, _cmdLine, _cwd, data: any) => {
|
||||
if (data.IS_DEV) app.quit();
|
||||
@@ -49,10 +58,11 @@ function init() {
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
checkUpdates();
|
||||
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.desktop");
|
||||
else if (process.platform === "darwin") app.dock.setIcon(ICON_PATH);
|
||||
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.vesktop");
|
||||
|
||||
registerScreenShareHandler();
|
||||
registerMediaPermissionsHandler();
|
||||
|
||||
bootstrap();
|
||||
|
||||
app.on("activate", () => {
|
||||
@@ -63,10 +73,10 @@ function init() {
|
||||
|
||||
if (!app.requestSingleInstanceLock({ IS_DEV })) {
|
||||
if (IS_DEV) {
|
||||
console.log("Vencord Desktop is already running. Quitting previous instance...");
|
||||
console.log("Vesktop is already running. Quitting previous instance...");
|
||||
init();
|
||||
} else {
|
||||
console.log("Vencord Desktop is already running. Quitting...");
|
||||
console.log("Vesktop is already running. Quitting...");
|
||||
app.quit();
|
||||
}
|
||||
} else {
|
||||
@@ -74,7 +84,7 @@ if (!app.requestSingleInstanceLock({ IS_DEV })) {
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
if (!Object.hasOwn(Settings.store, "firstLaunch")) {
|
||||
if (!Object.hasOwn(State.store, "firstLaunch")) {
|
||||
createFirstLaunchTour();
|
||||
} else {
|
||||
createWindows();
|
||||
|
||||
129
src/main/ipc.ts
129
src/main/ipc.ts
@@ -1,74 +1,99 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { app, dialog, ipcMain, session, shell } from "electron";
|
||||
import { existsSync, readFileSync, watch } from "fs";
|
||||
if (process.platform === "linux") import("./venmic");
|
||||
|
||||
import { execFile } from "child_process";
|
||||
import { app, BrowserWindow, clipboard, dialog, nativeImage, RelaunchOptions, session, shell } from "electron";
|
||||
import { mkdirSync, readFileSync, watch } from "fs";
|
||||
import { open, readFile } from "fs/promises";
|
||||
import { release } from "os";
|
||||
import { join } from "path";
|
||||
import { debounce } from "shared/utils/debounce";
|
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents";
|
||||
import { setBadgeCount } from "./appBadge";
|
||||
import { autoStart } from "./autoStart";
|
||||
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE } from "./constants";
|
||||
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
|
||||
import { mainWin } from "./mainWindow";
|
||||
import { Settings } from "./settings";
|
||||
import { handle, handleSync } from "./utils/ipcWrappers";
|
||||
import { PopoutWindows } from "./utils/popout";
|
||||
import { isDeckGameMode, showGamePage } from "./utils/steamOS";
|
||||
import { isValidVencordInstall } from "./utils/vencordLoader";
|
||||
|
||||
ipcMain.on(IpcEvents.GET_VENCORD_PRELOAD_FILE, e => {
|
||||
e.returnValue = join(VENCORD_FILES_DIR, "preload.js");
|
||||
});
|
||||
handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js"));
|
||||
handleSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, () =>
|
||||
readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8")
|
||||
);
|
||||
|
||||
ipcMain.on(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, e => {
|
||||
e.returnValue = readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8");
|
||||
});
|
||||
handleSync(IpcEvents.GET_RENDERER_SCRIPT, () => readFileSync(join(__dirname, "renderer.js"), "utf-8"));
|
||||
handleSync(IpcEvents.GET_RENDERER_CSS_FILE, () => join(__dirname, "renderer.css"));
|
||||
|
||||
ipcMain.on(IpcEvents.GET_RENDERER_SCRIPT, e => {
|
||||
e.returnValue = readFileSync(join(__dirname, "renderer.js"), "utf-8");
|
||||
});
|
||||
handleSync(IpcEvents.GET_SETTINGS, () => Settings.plain);
|
||||
handleSync(IpcEvents.GET_VERSION, () => app.getVersion());
|
||||
|
||||
ipcMain.on(IpcEvents.GET_RENDERER_CSS_FILE, e => {
|
||||
e.returnValue = join(__dirname, "renderer.css");
|
||||
});
|
||||
handleSync(
|
||||
IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY,
|
||||
() => process.platform === "win32" && Number(release().split(".").pop()) >= 22621
|
||||
);
|
||||
|
||||
ipcMain.on(IpcEvents.GET_SETTINGS, e => {
|
||||
e.returnValue = Settings.plain;
|
||||
});
|
||||
handleSync(IpcEvents.AUTOSTART_ENABLED, () => autoStart.isEnabled());
|
||||
handle(IpcEvents.ENABLE_AUTOSTART, autoStart.enable);
|
||||
handle(IpcEvents.DISABLE_AUTOSTART, autoStart.disable);
|
||||
|
||||
ipcMain.on(IpcEvents.GET_VERSION, e => {
|
||||
e.returnValue = app.getVersion();
|
||||
});
|
||||
|
||||
ipcMain.on(IpcEvents.AUTOSTART_ENABLED, e => {
|
||||
e.returnValue = autoStart.isEnabled();
|
||||
});
|
||||
ipcMain.handle(IpcEvents.ENABLE_AUTOSTART, autoStart.enable);
|
||||
ipcMain.handle(IpcEvents.DISABLE_AUTOSTART, autoStart.disable);
|
||||
|
||||
ipcMain.handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: string) => {
|
||||
handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: string) => {
|
||||
Settings.setData(settings, path);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.RELAUNCH, () => {
|
||||
app.relaunch();
|
||||
handle(IpcEvents.RELAUNCH, async () => {
|
||||
const options: RelaunchOptions = {
|
||||
args: process.argv.slice(1).concat(["--relaunch"])
|
||||
};
|
||||
if (isDeckGameMode) {
|
||||
// We can't properly relaunch when running under gamescope, but we can at least navigate to our page in Steam.
|
||||
await showGamePage();
|
||||
} else if (app.isPackaged && process.env.APPIMAGE) {
|
||||
execFile(process.env.APPIMAGE, options.args);
|
||||
} else {
|
||||
app.relaunch(options);
|
||||
}
|
||||
app.exit();
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SHOW_ITEM_IN_FOLDER, (_, path) => {
|
||||
handle(IpcEvents.SHOW_ITEM_IN_FOLDER, (_, path) => {
|
||||
shell.showItemInFolder(path);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.FOCUS, e => {
|
||||
e.sender.focus();
|
||||
handle(IpcEvents.FOCUS, () => {
|
||||
mainWin.show();
|
||||
mainWin.setSkipTaskbar(false);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.CLOSE, e => {
|
||||
e.sender.close();
|
||||
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();
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => {
|
||||
handle(IpcEvents.MINIMIZE, e => {
|
||||
mainWin.minimize();
|
||||
});
|
||||
|
||||
handle(IpcEvents.MAXIMIZE, e => {
|
||||
if (mainWin.isMaximized()) {
|
||||
mainWin.unmaximize();
|
||||
} else {
|
||||
mainWin.maximize();
|
||||
}
|
||||
});
|
||||
|
||||
handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => {
|
||||
const ses = session.defaultSession;
|
||||
|
||||
const available = ses.availableSpellCheckerLanguages;
|
||||
@@ -76,29 +101,34 @@ ipcMain.handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => {
|
||||
if (applicable.length) ses.setSpellCheckerLanguages(applicable);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SPELLCHECK_REPLACE_MISSPELLING, (e, word: string) => {
|
||||
handle(IpcEvents.SPELLCHECK_REPLACE_MISSPELLING, (e, word: string) => {
|
||||
e.sender.replaceMisspelling(word);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => {
|
||||
handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => {
|
||||
e.sender.session.addWordToSpellCheckerDictionary(word);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
||||
handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
||||
const res = await dialog.showOpenDialog(mainWin!, {
|
||||
properties: ["openDirectory"]
|
||||
});
|
||||
if (!res.filePaths.length) return "cancelled";
|
||||
|
||||
const dir = res.filePaths[0];
|
||||
for (const file of ["vencordDesktopMain.js", "preload.js", "vencordDesktopRenderer.js", "renderer.css"]) {
|
||||
if (!existsSync(join(dir, file))) return "invalid";
|
||||
}
|
||||
if (!isValidVencordInstall(dir)) return "invalid";
|
||||
|
||||
return dir;
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||
|
||||
handle(IpcEvents.CLIPBOARD_COPY_IMAGE, async (_, buf: ArrayBuffer, src: string) => {
|
||||
clipboard.write({
|
||||
html: `<img src="${src.replaceAll('"', '\\"')}">`,
|
||||
image: nativeImage.createFromBuffer(Buffer.from(buf))
|
||||
});
|
||||
});
|
||||
|
||||
function readCss() {
|
||||
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
||||
@@ -114,3 +144,12 @@ open(VENCORD_QUICKCSS_FILE, "a+").then(fd => {
|
||||
}, 50)
|
||||
);
|
||||
});
|
||||
|
||||
mkdirSync(VENCORD_THEMES_DIR, { recursive: true });
|
||||
watch(
|
||||
VENCORD_THEMES_DIR,
|
||||
{ persistent: false },
|
||||
debounce(() => {
|
||||
mainWin?.webContents.postMessage("VencordThemeUpdate", void 0);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,40 +1,84 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { app, BrowserWindow, BrowserWindowConstructorOptions, Menu, Tray } from "electron";
|
||||
import {
|
||||
app,
|
||||
BrowserWindow,
|
||||
BrowserWindowConstructorOptions,
|
||||
dialog,
|
||||
Menu,
|
||||
MenuItemConstructorOptions,
|
||||
nativeTheme,
|
||||
Tray
|
||||
} from "electron";
|
||||
import { rm } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { IpcEvents } from "shared/IpcEvents";
|
||||
import { isTruthy } from "shared/utils/guards";
|
||||
import { once } from "shared/utils/once";
|
||||
import type { SettingsStore } from "shared/utils/SettingsStore";
|
||||
|
||||
import { ICON_PATH } from "../shared/paths";
|
||||
import { createAboutWindow } from "./about";
|
||||
import { initArRPC } from "./arrpc";
|
||||
import { DEFAULT_HEIGHT, DEFAULT_WIDTH, MIN_HEIGHT, MIN_WIDTH, VENCORD_FILES_DIR } from "./constants";
|
||||
import { Settings, VencordSettings } from "./settings";
|
||||
import {
|
||||
BrowserUserAgent,
|
||||
DATA_DIR,
|
||||
DEFAULT_HEIGHT,
|
||||
DEFAULT_WIDTH,
|
||||
MessageBoxChoice,
|
||||
MIN_HEIGHT,
|
||||
MIN_WIDTH,
|
||||
VENCORD_FILES_DIR
|
||||
} from "./constants";
|
||||
import { Settings, State, VencordSettings } from "./settings";
|
||||
import { createSplashWindow } from "./splash";
|
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||
import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS";
|
||||
import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader";
|
||||
|
||||
let isQuitting = false;
|
||||
let tray: Tray;
|
||||
|
||||
applyDeckKeyboardFix();
|
||||
|
||||
app.on("before-quit", () => {
|
||||
isQuitting = true;
|
||||
});
|
||||
|
||||
export let mainWin: BrowserWindow;
|
||||
|
||||
function makeSettingsListenerHelpers<O extends object>(o: SettingsStore<O>) {
|
||||
const listeners = new Map<(data: any) => void, PropertyKey>();
|
||||
|
||||
const addListener: typeof o.addChangeListener = (path, cb) => {
|
||||
listeners.set(cb, path);
|
||||
o.addChangeListener(path, cb);
|
||||
};
|
||||
const removeAllListeners = () => {
|
||||
for (const [listener, path] of listeners) {
|
||||
o.removeChangeListener(path as any, listener);
|
||||
}
|
||||
|
||||
listeners.clear();
|
||||
};
|
||||
|
||||
return [addListener, removeAllListeners] as const;
|
||||
}
|
||||
|
||||
const [addSettingsListener, removeSettingsListeners] = makeSettingsListenerHelpers(Settings);
|
||||
const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings);
|
||||
|
||||
function initTray(win: BrowserWindow) {
|
||||
const trayMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: "Open",
|
||||
click() {
|
||||
win.show();
|
||||
},
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "About",
|
||||
@@ -48,6 +92,12 @@ function initTray(win: BrowserWindow) {
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Reset Vesktop",
|
||||
async click() {
|
||||
await clearData(win);
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "separator"
|
||||
},
|
||||
@@ -59,7 +109,7 @@ function initTray(win: BrowserWindow) {
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Quit Vencord Desktop",
|
||||
label: "Quit Vesktop",
|
||||
click() {
|
||||
isQuitting = true;
|
||||
app.quit();
|
||||
@@ -68,75 +118,132 @@ function initTray(win: BrowserWindow) {
|
||||
]);
|
||||
|
||||
tray = new Tray(ICON_PATH);
|
||||
tray.setToolTip("Vencord Desktop");
|
||||
tray.setToolTip("Vesktop");
|
||||
tray.setContextMenu(trayMenu);
|
||||
tray.on("click", () => win.show());
|
||||
|
||||
win.on("show", () => {
|
||||
trayMenu.items[0].enabled = false;
|
||||
});
|
||||
|
||||
win.on("hide", () => {
|
||||
trayMenu.items[0].enabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
async function clearData(win: BrowserWindow) {
|
||||
const { response } = await dialog.showMessageBox(win, {
|
||||
message: "Are you sure you want to reset Vesktop?",
|
||||
detail: "This will log you out, clear caches and reset all your settings!\n\nVesktop will automatically restart after this operation.",
|
||||
buttons: ["Yes", "No"],
|
||||
cancelId: MessageBoxChoice.Cancel,
|
||||
defaultId: MessageBoxChoice.Default,
|
||||
type: "warning"
|
||||
});
|
||||
|
||||
if (response === MessageBoxChoice.Cancel) return;
|
||||
|
||||
win.close();
|
||||
|
||||
await win.webContents.session.clearStorageData();
|
||||
await win.webContents.session.clearCache();
|
||||
await win.webContents.session.clearCodeCaches({});
|
||||
await rm(DATA_DIR, { force: true, recursive: true });
|
||||
|
||||
app.relaunch();
|
||||
app.quit();
|
||||
}
|
||||
|
||||
type MenuItemList = Array<MenuItemConstructorOptions | false>;
|
||||
|
||||
function initMenuBar(win: BrowserWindow) {
|
||||
const isWindows = process.platform === "win32";
|
||||
const isDarwin = process.platform === "darwin";
|
||||
const wantCtrlQ = !isWindows || VencordSettings.store.winCtrlQ;
|
||||
|
||||
const subMenu = [
|
||||
{
|
||||
label: "About Vesktop",
|
||||
click: createAboutWindow
|
||||
},
|
||||
{
|
||||
label: "Force Update Vencord",
|
||||
async click() {
|
||||
await downloadVencordFiles();
|
||||
app.relaunch();
|
||||
app.quit();
|
||||
},
|
||||
toolTip: "Vesktop will automatically restart after this operation"
|
||||
},
|
||||
{
|
||||
label: "Reset Vesktop",
|
||||
async click() {
|
||||
await clearData(win);
|
||||
},
|
||||
toolTip: "Vesktop will automatically restart after this operation"
|
||||
},
|
||||
{
|
||||
label: "Relaunch",
|
||||
accelerator: "CmdOrCtrl+Shift+R",
|
||||
click() {
|
||||
app.relaunch();
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
...(!isDarwin
|
||||
? []
|
||||
: ([
|
||||
{
|
||||
type: "separator"
|
||||
},
|
||||
{
|
||||
label: "Settings",
|
||||
accelerator: "CmdOrCtrl+,",
|
||||
async click() {
|
||||
mainWin.webContents.executeJavaScript(
|
||||
"Vencord.Webpack.Common.SettingsRouter.open('My Account')"
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "separator"
|
||||
},
|
||||
{
|
||||
role: "hide"
|
||||
},
|
||||
{
|
||||
role: "hideOthers"
|
||||
},
|
||||
{
|
||||
role: "unhide"
|
||||
},
|
||||
{
|
||||
type: "separator"
|
||||
}
|
||||
] satisfies MenuItemList)),
|
||||
{
|
||||
label: "Quit",
|
||||
accelerator: wantCtrlQ ? "CmdOrCtrl+Q" : void 0,
|
||||
visible: !isWindows,
|
||||
role: "quit",
|
||||
click() {
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
isWindows && {
|
||||
label: "Quit",
|
||||
accelerator: "Alt+F4",
|
||||
role: "quit",
|
||||
click() {
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
// See https://github.com/electron/electron/issues/14742 and https://github.com/electron/electron/issues/5256
|
||||
{
|
||||
label: "Zoom in (hidden, hack for Qwertz and others)",
|
||||
accelerator: "CmdOrCtrl+=",
|
||||
role: "zoomIn",
|
||||
visible: false
|
||||
}
|
||||
] satisfies MenuItemList;
|
||||
|
||||
const menu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: "Vencord Desktop",
|
||||
label: "Vesktop",
|
||||
role: "appMenu",
|
||||
submenu: [
|
||||
{
|
||||
label: "About Vencord Desktop",
|
||||
click: createAboutWindow
|
||||
},
|
||||
{
|
||||
label: "Force Update Vencord",
|
||||
async click() {
|
||||
await downloadVencordFiles();
|
||||
app.relaunch();
|
||||
app.quit();
|
||||
},
|
||||
toolTip: "Vencord Desktop will automatically restart after this operation"
|
||||
},
|
||||
{
|
||||
label: "Relaunch",
|
||||
accelerator: "CmdOrCtrl+Shift+R",
|
||||
click() {
|
||||
app.relaunch();
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Quit",
|
||||
accelerator: wantCtrlQ ? "CmdOrCtrl+Q" : void 0,
|
||||
visible: !isWindows,
|
||||
role: "quit",
|
||||
click() {
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Quit",
|
||||
accelerator: isWindows ? "Alt+F4" : void 0,
|
||||
visible: isWindows,
|
||||
role: "quit",
|
||||
click() {
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
// See https://github.com/electron/electron/issues/14742 and https://github.com/electron/electron/issues/5256
|
||||
{
|
||||
label: "Zoom in (hidden, hack for Qwertz and others)",
|
||||
accelerator: "CmdOrCtrl+=",
|
||||
role: "zoomIn",
|
||||
visible: false
|
||||
}
|
||||
]
|
||||
submenu: subMenu.filter(isTruthy)
|
||||
},
|
||||
{ role: "fileMenu" },
|
||||
{ role: "editMenu" },
|
||||
@@ -148,7 +255,10 @@ function initMenuBar(win: BrowserWindow) {
|
||||
}
|
||||
|
||||
function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
|
||||
const { x, y, width, height } = Settings.store.windowBounds ?? {};
|
||||
// We want the default window behaivour to apply in game mode since it expects everything to be fullscreen and maximized.
|
||||
if (isDeckGameMode) return {};
|
||||
|
||||
const { x, y, width, height } = State.store.windowBounds ?? {};
|
||||
|
||||
const options = {
|
||||
width: width ?? DEFAULT_WIDTH,
|
||||
@@ -168,10 +278,33 @@ function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
|
||||
return options;
|
||||
}
|
||||
|
||||
function getDarwinOptions(): BrowserWindowConstructorOptions {
|
||||
const options = {
|
||||
titleBarStyle: "hidden",
|
||||
trafficLightPosition: { x: 10, y: 10 }
|
||||
} as BrowserWindowConstructorOptions;
|
||||
|
||||
const { splashTheming, splashBackground } = Settings.store;
|
||||
const { macosTranslucency } = VencordSettings.store;
|
||||
|
||||
if (macosTranslucency) {
|
||||
options.vibrancy = "sidebar";
|
||||
options.backgroundColor = "#ffffff00";
|
||||
} else {
|
||||
if (splashTheming) {
|
||||
options.backgroundColor = splashBackground;
|
||||
} else {
|
||||
options.backgroundColor = nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff";
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function initWindowBoundsListeners(win: BrowserWindow) {
|
||||
const saveState = () => {
|
||||
Settings.store.maximized = win.isMaximized();
|
||||
Settings.store.minimized = win.isMinimized();
|
||||
State.store.maximized = win.isMaximized();
|
||||
State.store.minimized = win.isMinimized();
|
||||
};
|
||||
|
||||
win.on("maximize", saveState);
|
||||
@@ -179,7 +312,7 @@ function initWindowBoundsListeners(win: BrowserWindow) {
|
||||
win.on("unmaximize", saveState);
|
||||
|
||||
const saveBounds = () => {
|
||||
Settings.store.windowBounds = win.getBounds();
|
||||
State.store.windowBounds = win.getBounds();
|
||||
};
|
||||
|
||||
win.on("resize", saveBounds);
|
||||
@@ -187,11 +320,11 @@ function initWindowBoundsListeners(win: BrowserWindow) {
|
||||
}
|
||||
|
||||
function initSettingsListeners(win: BrowserWindow) {
|
||||
Settings.addChangeListener("tray", enable => {
|
||||
addSettingsListener("tray", enable => {
|
||||
if (enable) initTray(win);
|
||||
else tray?.destroy();
|
||||
});
|
||||
Settings.addChangeListener("disableMinSize", disable => {
|
||||
addSettingsListener("disableMinSize", disable => {
|
||||
if (disable) {
|
||||
// 0 no work
|
||||
win.setMinimumSize(1, 1);
|
||||
@@ -206,7 +339,7 @@ function initSettingsListeners(win: BrowserWindow) {
|
||||
}
|
||||
});
|
||||
|
||||
VencordSettings.addChangeListener("macosTranslucency", enabled => {
|
||||
addVencordSettingsListener("macosTranslucency", enabled => {
|
||||
if (enabled) {
|
||||
win.setVibrancy("sidebar");
|
||||
win.setBackgroundColor("#ffffff00");
|
||||
@@ -215,6 +348,10 @@ function initSettingsListeners(win: BrowserWindow) {
|
||||
win.setBackgroundColor("#ffffff");
|
||||
}
|
||||
});
|
||||
|
||||
addSettingsListener("enableMenu", enabled => {
|
||||
win.setAutoHideMenuBar(enabled ?? false);
|
||||
});
|
||||
}
|
||||
|
||||
function initSpellCheck(win: BrowserWindow) {
|
||||
@@ -224,9 +361,18 @@ function initSpellCheck(win: BrowserWindow) {
|
||||
}
|
||||
|
||||
function createMainWindow() {
|
||||
// Clear up previous settings listeners
|
||||
removeSettingsListeners();
|
||||
removeVencordSettingsListeners();
|
||||
|
||||
const { staticTitle, transparencyOption, enableMenu, customTitleBar } = Settings.store;
|
||||
|
||||
const { frameless, transparent } = VencordSettings.store;
|
||||
|
||||
const noFrame = frameless === true || customTitleBar === true;
|
||||
|
||||
const win = (mainWin = new BrowserWindow({
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
sandbox: false,
|
||||
@@ -236,22 +382,37 @@ function createMainWindow() {
|
||||
spellcheck: true
|
||||
},
|
||||
icon: ICON_PATH,
|
||||
frame: VencordSettings.store.frameless !== true,
|
||||
...(Settings.store.staticTitle ? { title: "Vencord" } : {}),
|
||||
...(VencordSettings.store.macosTranslucency
|
||||
? {
|
||||
vibrancy: "sidebar",
|
||||
backgroundColor: "#ffffff00"
|
||||
}
|
||||
: {}),
|
||||
...getWindowBoundsOptions()
|
||||
frame: !noFrame,
|
||||
...(transparent && {
|
||||
transparent: true,
|
||||
backgroundColor: "#00000000"
|
||||
}),
|
||||
...(transparencyOption &&
|
||||
transparencyOption !== "none" && {
|
||||
backgroundColor: "#00000000",
|
||||
backgroundMaterial: transparencyOption
|
||||
}),
|
||||
// Fix transparencyOption for custom discord titlebar
|
||||
...(customTitleBar &&
|
||||
transparencyOption &&
|
||||
transparencyOption !== "none" && {
|
||||
transparent: true
|
||||
}),
|
||||
...(staticTitle && { title: "Vesktop" }),
|
||||
...(process.platform === "darwin" && getDarwinOptions()),
|
||||
...getWindowBoundsOptions(),
|
||||
autoHideMenuBar: enableMenu
|
||||
}));
|
||||
win.setMenuBarVisibility(false);
|
||||
|
||||
win.on("close", e => {
|
||||
if (isQuitting || Settings.store.minimizeToTray === false || Settings.store.tray === false) return;
|
||||
const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false;
|
||||
if (isQuitting || (process.platform !== "darwin" && !useTray)) return;
|
||||
|
||||
e.preventDefault();
|
||||
win.hide();
|
||||
|
||||
if (process.platform === "darwin") app.hide();
|
||||
else win.hide();
|
||||
|
||||
return false;
|
||||
});
|
||||
@@ -259,15 +420,13 @@ function createMainWindow() {
|
||||
if (Settings.store.staticTitle) win.on("page-title-updated", e => e.preventDefault());
|
||||
|
||||
initWindowBoundsListeners(win);
|
||||
if (Settings.store.tray ?? true) initTray(win);
|
||||
if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win);
|
||||
initMenuBar(win);
|
||||
makeLinksOpenExternally(win);
|
||||
initSettingsListeners(win);
|
||||
initSpellCheck(win);
|
||||
|
||||
win.webContents.setUserAgent(
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
|
||||
);
|
||||
win.webContents.setUserAgent(BrowserUserAgent);
|
||||
|
||||
const subdomain =
|
||||
Settings.store.discordBranch === "canary" || Settings.store.discordBranch === "ptb"
|
||||
@@ -282,20 +441,35 @@ function createMainWindow() {
|
||||
const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDesktopMain.js")));
|
||||
|
||||
export async function createWindows() {
|
||||
const splash = createSplashWindow();
|
||||
|
||||
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);
|
||||
await ensureVencordFiles();
|
||||
runVencordMain();
|
||||
|
||||
mainWin = createMainWindow();
|
||||
|
||||
mainWin.once("ready-to-show", () => {
|
||||
mainWin.webContents.on("did-finish-load", () => {
|
||||
splash.destroy();
|
||||
mainWin!.show();
|
||||
|
||||
if (Settings.store.maximized) {
|
||||
mainWin!.maximize();
|
||||
if (!startMinimized) {
|
||||
mainWin!.show();
|
||||
if (State.store.maximized && !isDeckGameMode) mainWin!.maximize();
|
||||
}
|
||||
|
||||
if (isDeckGameMode) {
|
||||
// always use entire display
|
||||
mainWin!.setFullScreen(true);
|
||||
|
||||
askToApplySteamLayout(mainWin);
|
||||
}
|
||||
|
||||
mainWin.once("show", () => {
|
||||
if (State.store.maximized && !mainWin!.isMaximized() && !isDeckGameMode) {
|
||||
mainWin!.maximize();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
initArRPC();
|
||||
|
||||
24
src/main/mediaPermissions.ts
Normal file
24
src/main/mediaPermissions.ts
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 { session, systemPreferences } from "electron";
|
||||
|
||||
export function registerMediaPermissionsHandler() {
|
||||
if (process.platform !== "darwin") return;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
callback(granted);
|
||||
});
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { desktopCapturer, ipcMain, session, Streams } from "electron";
|
||||
import { desktopCapturer, session, Streams } from "electron";
|
||||
import type { StreamPick } from "renderer/components/ScreenSharePicker";
|
||||
import { IpcEvents } from "shared/IpcEvents";
|
||||
|
||||
import { handle } from "./utils/ipcWrappers";
|
||||
|
||||
const isWayland =
|
||||
process.platform === "linux" && (process.env.XDG_SESSION_TYPE === "wayland" || !!process.env.WAYLAND_DISPLAY);
|
||||
|
||||
export function registerScreenShareHandler() {
|
||||
ipcMain.handle(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, async (_, id: string) => {
|
||||
handle(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, async (_, id: string) => {
|
||||
const sources = await desktopCapturer.getSources({
|
||||
types: ["window", "screen"],
|
||||
thumbnailSize: {
|
||||
@@ -21,13 +26,19 @@ export function registerScreenShareHandler() {
|
||||
});
|
||||
|
||||
session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => {
|
||||
const sources = await desktopCapturer.getSources({
|
||||
types: ["window", "screen"],
|
||||
thumbnailSize: {
|
||||
width: 176,
|
||||
height: 99
|
||||
}
|
||||
});
|
||||
// request full resolution on wayland right away because we always only end up with one result anyway
|
||||
const width = isWayland ? 1920 : 176;
|
||||
const sources = await desktopCapturer
|
||||
.getSources({
|
||||
types: ["window", "screen"],
|
||||
thumbnailSize: {
|
||||
width,
|
||||
height: width * (9 / 16)
|
||||
}
|
||||
})
|
||||
.catch(err => console.error("Error during screenshare picker", err));
|
||||
|
||||
if (!sources) return callback({});
|
||||
|
||||
const data = sources.map(({ id, name, thumbnail }) => ({
|
||||
id,
|
||||
@@ -35,10 +46,28 @@ export function registerScreenShareHandler() {
|
||||
url: thumbnail.toDataURL()
|
||||
}));
|
||||
|
||||
if (isWayland) {
|
||||
const video = data[0];
|
||||
if (video) {
|
||||
const stream = await request.frame
|
||||
.executeJavaScript(
|
||||
`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify([video])},true)`
|
||||
)
|
||||
.catch(() => null);
|
||||
if (stream === null) return callback({});
|
||||
}
|
||||
|
||||
callback(video ? { video: sources[0] } : {});
|
||||
return;
|
||||
}
|
||||
|
||||
const choice = await request.frame
|
||||
.executeJavaScript(`VencordDesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`)
|
||||
.executeJavaScript(`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`)
|
||||
.then(e => e as StreamPick)
|
||||
.catch(() => null);
|
||||
.catch(e => {
|
||||
console.error("Error during screenshare picker", e);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (!choice) return callback({});
|
||||
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||
import { dirname, join } from "path";
|
||||
import type { Settings as TSettings } from "shared/settings";
|
||||
import type { Settings as TSettings, State as TState } from "shared/settings";
|
||||
import { SettingsStore } from "shared/utils/SettingsStore";
|
||||
|
||||
import { DATA_DIR, VENCORD_SETTINGS_FILE } from "./constants";
|
||||
|
||||
const SETTINGS_FILE = join(DATA_DIR, "settings.json");
|
||||
const STATE_FILE = join(DATA_DIR, "state.json");
|
||||
|
||||
function loadSettings<T extends object = any>(file: string, name: string) {
|
||||
let settings = {} as T;
|
||||
@@ -20,7 +21,7 @@ function loadSettings<T extends object = any>(file: string, name: string) {
|
||||
try {
|
||||
settings = JSON.parse(content);
|
||||
} catch (err) {
|
||||
console.error(`Failed to parse ${name} settings.json:`, err);
|
||||
console.error(`Failed to parse ${name}.json:`, err);
|
||||
}
|
||||
} catch {}
|
||||
|
||||
@@ -33,5 +34,31 @@ function loadSettings<T extends object = any>(file: string, name: string) {
|
||||
return store;
|
||||
}
|
||||
|
||||
export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vencord Desktop");
|
||||
export const VencordSettings = loadSettings<any>(VENCORD_SETTINGS_FILE, "Vencord");
|
||||
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");
|
||||
|
||||
@@ -1,18 +1,39 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { BrowserWindow } from "electron";
|
||||
import { join } from "path";
|
||||
import { SplashProps } from "shared/browserWinProperties";
|
||||
import { VIEW_DIR } from "shared/paths";
|
||||
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||
|
||||
export function createSplashWindow() {
|
||||
const splash = new BrowserWindow(SplashProps);
|
||||
import { Settings } from "./settings";
|
||||
|
||||
export function createSplashWindow(startMinimized = false) {
|
||||
const splash = new BrowserWindow({
|
||||
...SplashProps,
|
||||
icon: ICON_PATH,
|
||||
show: !startMinimized
|
||||
});
|
||||
|
||||
splash.loadFile(join(VIEW_DIR, "splash.html"));
|
||||
|
||||
const { splashBackground, splashColor, splashTheming } = Settings.store;
|
||||
|
||||
if (splashTheming) {
|
||||
if (splashColor) {
|
||||
const semiTransparentSplashColor = splashColor.replace("rgb(", "rgba(").replace(")", ", 0.2)");
|
||||
|
||||
splash.webContents.insertCSS(`body { --fg: ${splashColor} !important }`);
|
||||
splash.webContents.insertCSS(`body { --fg-semi-trans: ${semiTransparentSplashColor} !important }`);
|
||||
}
|
||||
|
||||
if (splashBackground) {
|
||||
splash.webContents.insertCSS(`body { --bg: ${splashBackground} !important }`);
|
||||
}
|
||||
}
|
||||
|
||||
return splash;
|
||||
}
|
||||
|
||||
@@ -1,45 +1,58 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { createWriteStream } from "fs";
|
||||
import type { IncomingMessage } from "http";
|
||||
import { get, RequestOptions } from "https";
|
||||
import { finished } from "stream/promises";
|
||||
import { Readable } from "stream";
|
||||
import { pipeline } from "stream/promises";
|
||||
import { setTimeout } from "timers/promises";
|
||||
|
||||
export async function downloadFile(url: string, file: string, options: RequestOptions = {}) {
|
||||
const res = await simpleReq(url, options);
|
||||
await finished(
|
||||
res.pipe(
|
||||
createWriteStream(file, {
|
||||
autoClose: true
|
||||
})
|
||||
)
|
||||
interface FetchieOptions {
|
||||
retryOnNetworkError?: boolean;
|
||||
}
|
||||
|
||||
export async function downloadFile(url: string, file: string, options: RequestInit = {}, fetchieOpts?: FetchieOptions) {
|
||||
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 = {}) {
|
||||
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);
|
||||
const ONE_MINUTE_MS = 1000 * 60;
|
||||
|
||||
resolve(res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function simpleGet(url: string, options: RequestOptions = {}) {
|
||||
const res = await simpleReq(url, options);
|
||||
|
||||
return new Promise<Buffer>((resolve, reject) => {
|
||||
const chunks = [] as Buffer[];
|
||||
|
||||
res.once("error", reject);
|
||||
res.on("data", chunk => chunks.push(chunk));
|
||||
res.once("end", () => resolve(Buffer.concat(chunks)));
|
||||
});
|
||||
export async function fetchie(url: string, options?: RequestInit, { retryOnNetworkError }: FetchieOptions = {}) {
|
||||
let res: Response | undefined;
|
||||
|
||||
try {
|
||||
res = await fetch(url, options);
|
||||
} catch (err) {
|
||||
if (retryOnNetworkError) {
|
||||
console.error("Failed to fetch", url + ".", "Gonna retry with backoff.");
|
||||
|
||||
for (let tries = 0, delayMs = 500; tries < 20; tries++, delayMs = Math.min(2 * delayMs, ONE_MINUTE_MS)) {
|
||||
await setTimeout(delayMs);
|
||||
try {
|
||||
res = await fetch(url, options);
|
||||
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);
|
||||
}
|
||||
|
||||
30
src/main/utils/ipcWrappers.ts
Normal file
30
src/main/utils/ipcWrappers.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 { 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);
|
||||
if (protocol === "file:") return;
|
||||
|
||||
if (!DISCORD_HOSTNAMES.includes(hostname)) throw new Error("ipc: Disallowed host " + hostname);
|
||||
}
|
||||
|
||||
export function handleSync(event: IpcEvents, cb: (e: IpcMainEvent, ...args: any[]) => any) {
|
||||
ipcMain.on(event, (e, ...args) => {
|
||||
validateSender(e.senderFrame);
|
||||
e.returnValue = cb(e, ...args);
|
||||
});
|
||||
}
|
||||
|
||||
export function handle(event: IpcEvents, cb: (e: IpcMainInvokeEvent, ...args: any[]) => any) {
|
||||
ipcMain.handle(event, (e, ...args) => {
|
||||
validateSender(e.senderFrame);
|
||||
return cb(e, ...args);
|
||||
});
|
||||
}
|
||||
@@ -1,40 +1,71 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { BrowserWindow, shell } from "electron";
|
||||
import { DISCORD_HOSTNAMES } from "main/constants";
|
||||
|
||||
import { Settings } from "../settings";
|
||||
import { createOrFocusPopup, setupPopout } from "./popout";
|
||||
import { execSteamURL, isDeckGameMode, steamOpenURL } from "./steamOS";
|
||||
|
||||
export function handleExternalUrl(url: string, protocol?: string): { action: "deny" | "allow" } {
|
||||
if (protocol == null) {
|
||||
try {
|
||||
protocol = new URL(url).protocol;
|
||||
} catch {
|
||||
return { action: "deny" };
|
||||
}
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
case "http:":
|
||||
case "https:":
|
||||
if (Settings.store.openLinksWithElectron) {
|
||||
return { action: "allow" };
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case "mailto:":
|
||||
case "spotify:":
|
||||
if (isDeckGameMode) {
|
||||
steamOpenURL(url);
|
||||
} else {
|
||||
shell.openExternal(url);
|
||||
}
|
||||
break;
|
||||
case "steam:":
|
||||
if (isDeckGameMode) {
|
||||
execSteamURL(url);
|
||||
} else {
|
||||
shell.openExternal(url);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return { action: "deny" };
|
||||
}
|
||||
|
||||
export function makeLinksOpenExternally(win: BrowserWindow) {
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
switch (url) {
|
||||
case "about:blank":
|
||||
case "https://discord.com/popout":
|
||||
return { action: "allow" };
|
||||
}
|
||||
|
||||
win.webContents.setWindowOpenHandler(({ url, frameName, features }) => {
|
||||
try {
|
||||
var { protocol } = new URL(url);
|
||||
var { protocol, hostname, pathname } = new URL(url);
|
||||
} catch {
|
||||
return { action: "deny" };
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
case "http:":
|
||||
case "https:":
|
||||
if (Settings.store.openLinksWithElectron) {
|
||||
return { action: "allow" };
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case "mailto:":
|
||||
case "steam:":
|
||||
case "spotify:":
|
||||
shell.openExternal(url);
|
||||
if (frameName.startsWith("DISCORD_") && pathname === "/popout" && DISCORD_HOSTNAMES.includes(hostname)) {
|
||||
return createOrFocusPopup(frameName, features);
|
||||
}
|
||||
|
||||
return { action: "deny" };
|
||||
if (url === "about:blank" || (frameName === "authorize" && DISCORD_HOSTNAMES.includes(hostname)))
|
||||
return { action: "allow" };
|
||||
|
||||
return handleExternalUrl(url, protocol);
|
||||
});
|
||||
|
||||
win.webContents.on("did-create-window", (win, { frameName }) => {
|
||||
if (frameName.startsWith("DISCORD_")) setupPopout(win, frameName);
|
||||
});
|
||||
}
|
||||
|
||||
116
src/main/utils/popout.ts
Normal file
116
src/main/utils/popout.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 { BrowserWindow, BrowserWindowConstructorOptions } from "electron";
|
||||
import { Settings } from "main/settings";
|
||||
|
||||
import { handleExternalUrl } from "./makeLinksOpenExternally";
|
||||
|
||||
const ALLOWED_FEATURES = new Set([
|
||||
"width",
|
||||
"height",
|
||||
"left",
|
||||
"top",
|
||||
"resizable",
|
||||
"movable",
|
||||
"alwaysOnTop",
|
||||
"frame",
|
||||
"transparent",
|
||||
"hasShadow",
|
||||
"closable",
|
||||
"skipTaskbar",
|
||||
"backgroundColor",
|
||||
"menubar",
|
||||
"toolbar",
|
||||
"location",
|
||||
"directories",
|
||||
"titleBarStyle"
|
||||
]);
|
||||
|
||||
const MIN_POPOUT_WIDTH = 320;
|
||||
const MIN_POPOUT_HEIGHT = 180;
|
||||
const DEFAULT_POPOUT_OPTIONS: BrowserWindowConstructorOptions = {
|
||||
title: "Discord Popout",
|
||||
backgroundColor: "#2f3136",
|
||||
minWidth: MIN_POPOUT_WIDTH,
|
||||
minHeight: MIN_POPOUT_HEIGHT,
|
||||
frame: Settings.store.customTitleBar !== true,
|
||||
titleBarStyle: process.platform === "darwin" ? "hidden" : undefined,
|
||||
trafficLightPosition:
|
||||
process.platform === "darwin"
|
||||
? {
|
||||
x: 10,
|
||||
y: 3
|
||||
}
|
||||
: undefined,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true
|
||||
},
|
||||
autoHideMenuBar: Settings.store.enableMenu
|
||||
};
|
||||
|
||||
export const PopoutWindows = new Map<string, BrowserWindow>();
|
||||
|
||||
function focusWindow(window: BrowserWindow) {
|
||||
window.setAlwaysOnTop(true);
|
||||
window.focus();
|
||||
window.setAlwaysOnTop(false);
|
||||
}
|
||||
|
||||
function parseFeatureValue(feature: string) {
|
||||
if (feature === "yes") return true;
|
||||
if (feature === "no") return false;
|
||||
|
||||
const n = Number(feature);
|
||||
if (!isNaN(n)) return n;
|
||||
|
||||
return feature;
|
||||
}
|
||||
|
||||
function parseWindowFeatures(features: string) {
|
||||
const keyValuesParsed = features.split(",");
|
||||
|
||||
return keyValuesParsed.reduce((features, feature) => {
|
||||
const [key, value] = feature.split("=");
|
||||
if (ALLOWED_FEATURES.has(key)) features[key] = parseFeatureValue(value);
|
||||
|
||||
return features;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function createOrFocusPopup(key: string, features: string) {
|
||||
const existingWindow = PopoutWindows.get(key);
|
||||
if (existingWindow) {
|
||||
focusWindow(existingWindow);
|
||||
return <const>{ action: "deny" };
|
||||
}
|
||||
|
||||
return <const>{
|
||||
action: "allow",
|
||||
overrideBrowserWindowOptions: {
|
||||
...DEFAULT_POPOUT_OPTIONS,
|
||||
...parseWindowFeatures(features)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function setupPopout(win: BrowserWindow, key: string) {
|
||||
win.setMenuBarVisibility(false);
|
||||
|
||||
PopoutWindows.set(key, win);
|
||||
|
||||
/* win.webContents.on("will-navigate", (evt, url) => {
|
||||
// maybe prevent if not origin match
|
||||
})*/
|
||||
|
||||
win.webContents.setWindowOpenHandler(({ url }) => handleExternalUrl(url));
|
||||
|
||||
win.once("closed", () => {
|
||||
win.removeAllListeners();
|
||||
PopoutWindows.delete(key);
|
||||
});
|
||||
}
|
||||
97
src/main/utils/steamOS.ts
Normal file
97
src/main/utils/steamOS.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 { BrowserWindow, dialog } from "electron";
|
||||
import { writeFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
||||
import { MessageBoxChoice } from "../constants";
|
||||
import { State } from "../settings";
|
||||
|
||||
// Bump this to re-show the prompt
|
||||
const layoutVersion = 2;
|
||||
// Get this from "show details" on the profile after exporting as a shared personal layout or using share with community
|
||||
const layoutId = "3080264545"; // Vesktop Layout v2
|
||||
const numberRegex = /^[0-9]*$/;
|
||||
|
||||
let steamPipeQueue = Promise.resolve();
|
||||
|
||||
export const isDeckGameMode = process.env.SteamOS === "1" && process.env.SteamGamepadUI === "1";
|
||||
|
||||
export function applyDeckKeyboardFix() {
|
||||
if (!isDeckGameMode) return;
|
||||
// Prevent constant virtual keyboard spam that eventually crashes Steam.
|
||||
process.env.GTK_IM_MODULE = "None";
|
||||
}
|
||||
|
||||
// For some reason SteamAppId is always 0 for non-steam apps so we do this insanity instead.
|
||||
function getAppId(): string | null {
|
||||
// /home/deck/.local/share/Steam/steamapps/shadercache/APPID/fozmediav1
|
||||
const path = process.env.STEAM_COMPAT_MEDIA_PATH;
|
||||
if (!path) return null;
|
||||
const pathElems = path?.split("/");
|
||||
const appId = pathElems[pathElems.length - 2];
|
||||
if (appId.match(numberRegex)) {
|
||||
console.log(`Got Steam App ID ${appId}`);
|
||||
return appId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function execSteamURL(url: string) {
|
||||
// This doesn't allow arbitrary execution despite the weird syntax.
|
||||
steamPipeQueue = steamPipeQueue.then(() =>
|
||||
writeFile(
|
||||
join(process.env.HOME || "/home/deck", ".steam", "steam.pipe"),
|
||||
// replace ' to prevent argument injection
|
||||
`'${process.env.HOME}/.local/share/Steam/ubuntu12_32/steam' '-ifrunning' '${url.replaceAll("'", "%27")}'\n`,
|
||||
"utf-8"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function steamOpenURL(url: string) {
|
||||
execSteamURL(`steam://openurl/${url}`);
|
||||
}
|
||||
|
||||
export async function showGamePage() {
|
||||
const appId = getAppId();
|
||||
if (!appId) return;
|
||||
await execSteamURL(`steam://nav/games/details/${appId}`);
|
||||
}
|
||||
|
||||
async function showLayout(appId: string) {
|
||||
execSteamURL(`steam://controllerconfig/${appId}/${layoutId}`);
|
||||
}
|
||||
|
||||
export async function askToApplySteamLayout(win: BrowserWindow) {
|
||||
const appId = getAppId();
|
||||
if (!appId) return;
|
||||
if (State.store.steamOSLayoutVersion === layoutVersion) return;
|
||||
const update = Boolean(State.store.steamOSLayoutVersion);
|
||||
|
||||
// Touch screen breaks in some menus when native touch mode is enabled on latest SteamOS beta, remove most of the update specific text once that's fixed.
|
||||
const { response } = await dialog.showMessageBox(win, {
|
||||
message: `${update ? "Update" : "Apply"} Vesktop Steam Input Layout?`,
|
||||
detail: `Would you like to ${update ? "Update" : "Apply"} Vesktop's recommended Steam Deck controller settings?
|
||||
${update ? "Click yes using the touchpad" : "Tap yes"}, then press the X button or tap Apply Layout to confirm.${
|
||||
update ? " Doing so will undo any customizations you have made." : ""
|
||||
}
|
||||
${update ? "Click" : "Tap"} no to keep your current layout.`,
|
||||
buttons: ["Yes", "No"],
|
||||
cancelId: MessageBoxChoice.Cancel,
|
||||
defaultId: MessageBoxChoice.Default,
|
||||
type: "question"
|
||||
});
|
||||
|
||||
if (State.store.steamOSLayoutVersion !== layoutVersion) {
|
||||
State.store.steamOSLayoutVersion = layoutVersion;
|
||||
}
|
||||
|
||||
if (response === MessageBoxChoice.Cancel) return;
|
||||
|
||||
await showLayout(appId);
|
||||
}
|
||||
@@ -1,19 +1,23 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { existsSync, mkdirSync } from "fs";
|
||||
import type { RequestOptions } from "https";
|
||||
import { join } from "path";
|
||||
|
||||
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 FILES_TO_DOWNLOAD = ["vencordDesktopMain.js", "preload.js", "vencordDesktopRenderer.js", "renderer.css"];
|
||||
export const FILES_TO_DOWNLOAD = [
|
||||
"vencordDesktopMain.js",
|
||||
"vencordDesktopPreload.js",
|
||||
"vencordDesktopRenderer.js",
|
||||
"vencordDesktopRenderer.css"
|
||||
];
|
||||
|
||||
export interface ReleaseData {
|
||||
name: string;
|
||||
@@ -26,32 +30,38 @@ export interface ReleaseData {
|
||||
}
|
||||
|
||||
export async function githubGet(endpoint: string) {
|
||||
const opts: RequestOptions = {
|
||||
const opts: RequestInit = {
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
"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() {
|
||||
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(
|
||||
assets
|
||||
.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 })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function isValidVencordInstall(dir: string) {
|
||||
return FILES_TO_DOWNLOAD.every(f => existsSync(join(dir, f)));
|
||||
}
|
||||
|
||||
export async function ensureVencordFiles() {
|
||||
if (existsSync(join(VENCORD_FILES_DIR, "vencordDesktopMain.js"))) return;
|
||||
if (isValidVencordInstall(VENCORD_FILES_DIR)) return;
|
||||
mkdirSync(VENCORD_FILES_DIR, { recursive: true });
|
||||
|
||||
await downloadVencordFiles();
|
||||
|
||||
92
src/main/venmic.ts
Normal file
92
src/main/venmic.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 type { PatchBay } 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<PatchBay["link"]>[0];
|
||||
|
||||
let initialized = false;
|
||||
let patchBay: import("@vencord/venmic").PatchBay | undefined;
|
||||
let isGlibcxxToOld = false;
|
||||
|
||||
function getRendererAudioServicePid() {
|
||||
return (
|
||||
app
|
||||
.getAppMetrics()
|
||||
.find(proc => proc.name === "Audio Service")
|
||||
?.pid?.toString() ?? "owo"
|
||||
);
|
||||
}
|
||||
|
||||
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, () => {
|
||||
const audioPid = getRendererAudioServicePid();
|
||||
const list = obtainVenmic()
|
||||
?.list()
|
||||
.filter(s => s["application.process.id"] !== audioPid)
|
||||
.map(s => s["application.name"]);
|
||||
|
||||
return list
|
||||
? { ok: true, targets: [...new Set(list)] } // Remove duplicates
|
||||
: { ok: false, isGlibcxxToOld };
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boolean) => {
|
||||
const pid = getRendererAudioServicePid();
|
||||
|
||||
const data: LinkData = {
|
||||
include: targets.map(target => ({ key: "application.name", value: target })),
|
||||
exclude: [{ key: "application.process.id", value: pid }]
|
||||
};
|
||||
|
||||
if (workaround) {
|
||||
data.workaround = [
|
||||
{ key: "application.process.id", value: pid },
|
||||
{ key: "media.name", value: "RecordStream" }
|
||||
];
|
||||
}
|
||||
|
||||
return obtainVenmic()?.link(data);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, (_, workaround?: boolean) => {
|
||||
const pid = getRendererAudioServicePid();
|
||||
|
||||
const data: LinkData = {
|
||||
exclude: [{ key: "application.process.id", value: pid }]
|
||||
};
|
||||
|
||||
if (workaround) {
|
||||
data.workaround = [
|
||||
{ key: "application.process.id", value: pid },
|
||||
{ key: "media.name", value: "RecordStream" }
|
||||
];
|
||||
}
|
||||
|
||||
return obtainVenmic()?.link(data);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.VIRT_MIC_STOP, () => obtainVenmic()?.unlink());
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { Settings } from "shared/settings";
|
||||
import type { LiteralUnion } from "type-fest";
|
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents";
|
||||
import { invoke, sendSync } from "./typedIpcs";
|
||||
import { invoke, sendSync } from "./typedIpc";
|
||||
|
||||
type SpellCheckerResultCallback = (word: string, suggestions: string[]) => void;
|
||||
|
||||
@@ -19,11 +19,12 @@ ipcRenderer.on(IpcEvents.SPELLCHECK_RESULT, (_, w: string, s: string[]) => {
|
||||
spellCheckCallbacks.forEach(cb => cb(w, s));
|
||||
});
|
||||
|
||||
export const VencordDesktopNative = {
|
||||
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)
|
||||
setBadgeCount: (count: number) => invoke<void>(IpcEvents.SET_BADGE_COUNT, count),
|
||||
supportsWindowsTransparency: () => sendSync<boolean>(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY)
|
||||
},
|
||||
autostart: {
|
||||
isEnabled: () => sendSync<boolean>(IpcEvents.AUTOSTART_ENABLED),
|
||||
@@ -50,9 +51,29 @@ export const VencordDesktopNative = {
|
||||
addToDictionary: (word: string) => invoke<void>(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, word)
|
||||
},
|
||||
win: {
|
||||
focus: () => invoke<void>(IpcEvents.FOCUS)
|
||||
focus: () => invoke<void>(IpcEvents.FOCUS),
|
||||
close: (key?: string) => invoke<void>(IpcEvents.CLOSE, key),
|
||||
minimize: () => invoke<void>(IpcEvents.MINIMIZE),
|
||||
maximize: () => invoke<void>(IpcEvents.MAXIMIZE)
|
||||
},
|
||||
capturer: {
|
||||
getLargeThumbnail: (id: string) => invoke<string>(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, id)
|
||||
},
|
||||
/** only available on Linux. */
|
||||
virtmic: {
|
||||
list: () =>
|
||||
invoke<{ ok: false; isGlibcxxToOld: boolean } | { ok: true; targets: string[] }>(IpcEvents.VIRT_MIC_LIST),
|
||||
start: (targets: string[], workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START, targets, workaround),
|
||||
startSystem: (workaround?: boolean) => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM, workaround),
|
||||
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)
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -8,9 +8,9 @@ import { contextBridge, ipcRenderer, webFrame } from "electron";
|
||||
import { readFileSync, watch } from "fs";
|
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents";
|
||||
import { VencordDesktopNative } from "./VencordDesktopNative";
|
||||
import { VesktopNative } from "./VesktopNative";
|
||||
|
||||
contextBridge.exposeInMainWorld("VencordDesktopNative", VencordDesktopNative);
|
||||
contextBridge.exposeInMainWorld("VesktopNative", VesktopNative);
|
||||
|
||||
require(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_FILE));
|
||||
|
||||
@@ -41,4 +41,4 @@ if (IS_DEV) {
|
||||
}
|
||||
// #endregion
|
||||
|
||||
VencordDesktopNative.spellcheck.setLanguages(window.navigator.languages);
|
||||
VesktopNative.spellcheck.setLanguages(window.navigator.languages);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -15,15 +15,19 @@ let NotificationSettingsStore: any;
|
||||
export function setBadge() {
|
||||
if (Settings.store.appBadge === false) return;
|
||||
|
||||
const mentionCount = GuildReadStateStore.getTotalMentionCount();
|
||||
const pendingRequests = RelationshipStore.getPendingCount();
|
||||
const hasUnread = GuildReadStateStore.hasAnyUnread();
|
||||
const disableUnreadBadge = NotificationSettingsStore.getDisableUnreadBadge();
|
||||
try {
|
||||
const mentionCount = GuildReadStateStore.getTotalMentionCount();
|
||||
const pendingRequests = RelationshipStore.getPendingCount();
|
||||
const hasUnread = GuildReadStateStore.hasAnyUnread();
|
||||
const disableUnreadBadge = NotificationSettingsStore.getDisableUnreadBadge();
|
||||
|
||||
let totalCount = mentionCount + pendingRequests;
|
||||
if (!totalCount && hasUnread && !disableUnreadBadge) totalCount = -1;
|
||||
let totalCount = mentionCount + pendingRequests;
|
||||
if (!totalCount && hasUnread && !disableUnreadBadge) totalCount = -1;
|
||||
|
||||
VencordDesktopNative.app.setBadgeCount(totalCount);
|
||||
VesktopNative.app.setBadgeCount(totalCount);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
let toFind = 3;
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import "./screenSharePicker.css";
|
||||
|
||||
import { classes, closeModal, Margins, Modals, openModal, useAwaiter } from "@vencord/types/utils";
|
||||
import { findByPropsLazy } from "@vencord/types/webpack";
|
||||
import { Button, Card, Forms, Switch, Text, useState } from "@vencord/types/webpack/common";
|
||||
import { closeModal, Margins, Modals, openModal, useAwaiter } from "@vencord/types/utils";
|
||||
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
FluxDispatcher,
|
||||
Forms,
|
||||
Select,
|
||||
Switch,
|
||||
Text,
|
||||
UserStore,
|
||||
useState
|
||||
} from "@vencord/types/webpack/common";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { isWindows } from "renderer/utils";
|
||||
import { addPatch } from "renderer/patches/shared";
|
||||
import { isLinux, isWindows } from "renderer/utils";
|
||||
|
||||
const StreamResolutions = ["720", "1080", "1440", "Source"] as const;
|
||||
const StreamResolutions = ["480", "720", "1080", "1440"] as const;
|
||||
const StreamFps = ["15", "30", "60"] as const;
|
||||
|
||||
const WarningIconClasses = findByPropsLazy("warning", "error", "container");
|
||||
const MediaEngineStore = findStoreLazy("MediaEngineStore");
|
||||
|
||||
export type StreamResolution = (typeof StreamResolutions)[number];
|
||||
export type StreamFps = (typeof StreamFps)[number];
|
||||
@@ -24,6 +35,8 @@ interface StreamSettings {
|
||||
resolution: StreamResolution;
|
||||
fps: StreamFps;
|
||||
audio: boolean;
|
||||
audioSource?: string;
|
||||
workaround?: boolean;
|
||||
}
|
||||
|
||||
export interface StreamPick extends StreamSettings {
|
||||
@@ -36,18 +49,77 @@ interface Source {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export function openScreenSharePicker(screens: Source[]) {
|
||||
let currentSettings: StreamSettings | null = null;
|
||||
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
find: "this.localWant=",
|
||||
replacement: {
|
||||
match: /this.localWant=/,
|
||||
replace: "$self.patchStreamQuality(this);$&"
|
||||
}
|
||||
}
|
||||
],
|
||||
patchStreamQuality(opts: any) {
|
||||
if (!currentSettings) return;
|
||||
|
||||
const framerate = Number(currentSettings.fps);
|
||||
const height = Number(currentSettings.resolution);
|
||||
const width = Math.round(height * (16 / 9));
|
||||
|
||||
Object.assign(opts, {
|
||||
bitrateMin: 500000,
|
||||
bitrateMax: 8000000,
|
||||
bitrateTarget: 600000
|
||||
});
|
||||
Object.assign(opts.capture, {
|
||||
framerate,
|
||||
width,
|
||||
height,
|
||||
pixelCount: height * width
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (isLinux) {
|
||||
onceReady.then(() => {
|
||||
FluxDispatcher.subscribe("STREAM_CLOSE", ({ streamKey }: { streamKey: string }) => {
|
||||
const owner = streamKey.split(":").at(-1);
|
||||
|
||||
if (owner !== UserStore.getCurrentUser().id) {
|
||||
return;
|
||||
}
|
||||
|
||||
VesktopNative.virtmic.stop();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function openScreenSharePicker(screens: Source[], skipPicker: boolean) {
|
||||
let didSubmit = false;
|
||||
return new Promise<StreamPick>((resolve, reject) => {
|
||||
const key = openModal(
|
||||
props => (
|
||||
<ModalComponent
|
||||
screens={screens}
|
||||
modalProps={props}
|
||||
submit={resolve}
|
||||
submit={async v => {
|
||||
didSubmit = true;
|
||||
if (v.audioSource && v.audioSource !== "None") {
|
||||
if (v.audioSource === "Entire System") {
|
||||
await VesktopNative.virtmic.startSystem(v.workaround);
|
||||
} else {
|
||||
await VesktopNative.virtmic.start([v.audioSource], v.workaround);
|
||||
}
|
||||
}
|
||||
resolve(v);
|
||||
}}
|
||||
close={() => {
|
||||
props.onClose();
|
||||
reject("Aborted");
|
||||
if (!didSubmit) reject("Aborted");
|
||||
}}
|
||||
skipPicker={skipPicker}
|
||||
/>
|
||||
),
|
||||
{
|
||||
@@ -78,16 +150,21 @@ function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScre
|
||||
function StreamSettings({
|
||||
source,
|
||||
settings,
|
||||
setSettings
|
||||
setSettings,
|
||||
skipPicker
|
||||
}: {
|
||||
source: Source;
|
||||
settings: StreamSettings;
|
||||
setSettings: Dispatch<SetStateAction<StreamSettings>>;
|
||||
skipPicker: boolean;
|
||||
}) {
|
||||
const [thumb] = useAwaiter(() => VencordDesktopNative.capturer.getLargeThumbnail(source.id), {
|
||||
fallbackValue: source.url,
|
||||
deps: [source.id]
|
||||
});
|
||||
const [thumb] = useAwaiter(
|
||||
() => (skipPicker ? Promise.resolve(source.url) : VesktopNative.capturer.getLargeThumbnail(source.id)),
|
||||
{
|
||||
fallbackValue: source.url,
|
||||
deps: [source.id]
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -100,12 +177,6 @@ function StreamSettings({
|
||||
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
|
||||
|
||||
<Card className="vcd-screen-picker-card">
|
||||
<Card className={classes(WarningIconClasses.container, WarningIconClasses.warning, Margins.bottom8)}>
|
||||
<Forms.FormText>
|
||||
Resolution and Frame Rate aren't implemented for now. Locked to 720p 30fps
|
||||
</Forms.FormText>
|
||||
</Card>
|
||||
|
||||
<div className="vcd-screen-picker-quality">
|
||||
<section>
|
||||
<Forms.FormTitle>Resolution</Forms.FormTitle>
|
||||
@@ -154,23 +225,97 @@ function StreamSettings({
|
||||
Stream With Audio
|
||||
</Switch>
|
||||
)}
|
||||
|
||||
{isLinux && (
|
||||
<AudioSourcePickerLinux
|
||||
audioSource={settings.audioSource}
|
||||
workaround={settings.workaround}
|
||||
setAudioSource={source => setSettings(s => ({ ...s, audioSource: source }))}
|
||||
setWorkaround={workaround => setSettings(s => ({ ...s, workaround: workaround }))}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AudioSourcePickerLinux({
|
||||
audioSource,
|
||||
workaround,
|
||||
setAudioSource,
|
||||
setWorkaround
|
||||
}: {
|
||||
audioSource?: string;
|
||||
workaround?: boolean;
|
||||
setAudioSource(s: string): void;
|
||||
setWorkaround(b: boolean): void;
|
||||
}) {
|
||||
const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), {
|
||||
fallbackValue: { ok: true, targets: [] }
|
||||
});
|
||||
const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null;
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Forms.FormTitle>Audio</Forms.FormTitle>
|
||||
{loading && <Forms.FormTitle>Loading Audio sources...</Forms.FormTitle>}
|
||||
{!sources.ok &&
|
||||
(sources.isGlibcxxToOld ? (
|
||||
<Forms.FormText>
|
||||
Failed to retrieve Audio Sources because your C++ library is too old to run venmic. If you would
|
||||
like to stream with Audio, see{" "}
|
||||
<a href="https://gist.github.com/Vendicated/b655044ffbb16b2716095a448c6d827a" target="_blank">
|
||||
this guide
|
||||
</a>
|
||||
</Forms.FormText>
|
||||
) : (
|
||||
<Forms.FormText>
|
||||
Failed to retrieve Audio Sources. If you would like to stream with Audio, make sure you're using
|
||||
Pipewire, not Pulseaudio
|
||||
</Forms.FormText>
|
||||
))}
|
||||
|
||||
{allSources && (
|
||||
<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>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function ModalComponent({
|
||||
screens,
|
||||
modalProps,
|
||||
submit,
|
||||
close
|
||||
close,
|
||||
skipPicker
|
||||
}: {
|
||||
screens: Source[];
|
||||
modalProps: any;
|
||||
submit: (data: StreamPick) => void;
|
||||
close: () => void;
|
||||
skipPicker: boolean;
|
||||
}) {
|
||||
const [selected, setSelected] = useState<string>();
|
||||
const [selected, setSelected] = useState<string | undefined>(skipPicker ? screens[0].id : void 0);
|
||||
const [settings, setSettings] = useState<StreamSettings>({
|
||||
resolution: "1080",
|
||||
fps: "60",
|
||||
@@ -192,6 +337,7 @@ function ModalComponent({
|
||||
source={screens.find(s => s.id === selected)!}
|
||||
settings={settings}
|
||||
setSettings={setSettings}
|
||||
skipPicker={skipPicker}
|
||||
/>
|
||||
)}
|
||||
</Modals.ModalContent>
|
||||
@@ -200,17 +346,38 @@ function ModalComponent({
|
||||
<Button
|
||||
disabled={!selected}
|
||||
onClick={() => {
|
||||
currentSettings = settings;
|
||||
|
||||
// If there are 2 connections, the second one is the existing stream.
|
||||
// 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 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({
|
||||
id: selected!,
|
||||
...settings
|
||||
});
|
||||
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Go Live
|
||||
</Button>
|
||||
|
||||
{selected ? (
|
||||
{selected && !skipPicker ? (
|
||||
<Button color={Button.Colors.TRANSPARENT} onClick={() => setSelected(void 0)}>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import "./settings.css";
|
||||
|
||||
import { Margins } from "@vencord/types/utils";
|
||||
import { Button, Forms, Select, Switch, Text, useState } from "@vencord/types/webpack/common";
|
||||
import { setBadge } from "renderer/appBadge";
|
||||
import { useSettings } from "renderer/settings";
|
||||
|
||||
export default function SettingsUi() {
|
||||
const Settings = useSettings();
|
||||
|
||||
const { autostart } = VencordDesktopNative;
|
||||
const [autoStartEnabled, setAutoStartEnabled] = useState(autostart.isEnabled());
|
||||
|
||||
const switches: [keyof typeof Settings, string, string, boolean?, (() => boolean)?][] = [
|
||||
["tray", "Tray Icon", "Add a tray icon for Vencord Desktop", true],
|
||||
[
|
||||
"minimizeToTray",
|
||||
"Minimize to tray",
|
||||
"Hitting X will make Vencord Desktop minimize to the tray instead of closing",
|
||||
true,
|
||||
() => Settings.tray ?? true
|
||||
],
|
||||
["arRPC", "Rich Presence", "Enables Rich Presence via arRPC", false],
|
||||
[
|
||||
"disableMinSize",
|
||||
"Disable minimum window size",
|
||||
"Allows you to make the window as small as your heart desires"
|
||||
],
|
||||
[
|
||||
"openLinksWithElectron",
|
||||
"Open Links in app (experimental)",
|
||||
"Opens links in a new Vencord Desktop window instead of your web browser"
|
||||
],
|
||||
["staticTitle", "Static Title", 'Makes the window title "Vencord" instead of changing to the current page']
|
||||
];
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Text variant="heading-lg/semibold" style={{ color: "var(--header-primary)" }} tag="h2">
|
||||
Vencord Desktop Settings
|
||||
</Text>
|
||||
|
||||
<Forms.FormTitle className={Margins.top16}>Discord Branch</Forms.FormTitle>
|
||||
<Select
|
||||
placeholder="Stable"
|
||||
options={[
|
||||
{ label: "Stable", value: "stable", default: true },
|
||||
{ label: "Canary", value: "canary" },
|
||||
{ label: "PTB", value: "ptb" }
|
||||
]}
|
||||
closeOnSelect={true}
|
||||
select={v => (Settings.discordBranch = v)}
|
||||
isSelected={v => v === Settings.discordBranch}
|
||||
serialize={s => s}
|
||||
/>
|
||||
|
||||
<Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} />
|
||||
|
||||
<Switch
|
||||
value={autoStartEnabled}
|
||||
onChange={async v => {
|
||||
await autostart[v ? "enable" : "disable"]();
|
||||
setAutoStartEnabled(v);
|
||||
}}
|
||||
note="Automatically start Vencord Desktop on computer start-up"
|
||||
>
|
||||
Start With System
|
||||
</Switch>
|
||||
|
||||
<Switch
|
||||
value={Settings.appBadge ?? true}
|
||||
onChange={v => {
|
||||
Settings.appBadge = v;
|
||||
if (v) setBadge();
|
||||
else VencordDesktopNative.app.setBadgeCount(0);
|
||||
}}
|
||||
note="Show mention badge on the app icon"
|
||||
>
|
||||
Notification Badge
|
||||
</Switch>
|
||||
|
||||
{switches.map(([key, text, note, def, predicate]) => (
|
||||
<Switch
|
||||
value={(Settings[key as any] ?? def ?? false) && predicate?.() !== false}
|
||||
disabled={predicate && !predicate()}
|
||||
onChange={v => (Settings[key as any] = v)}
|
||||
note={note}
|
||||
key={key}
|
||||
>
|
||||
{text}
|
||||
</Switch>
|
||||
))}
|
||||
|
||||
<Forms.FormTitle>Vencord Location</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
Vencord files are loaded from{" "}
|
||||
{Settings.vencordDir ? (
|
||||
<a
|
||||
href="about:blank"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
VencordDesktopNative.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 VencordDesktopNative.fileManager.selectVencordDir();
|
||||
switch (choice) {
|
||||
case "cancelled":
|
||||
case "invalid":
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
Settings.vencordDir = choice;
|
||||
}}
|
||||
>
|
||||
Change
|
||||
</Button>
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.RED}
|
||||
onClick={() => (Settings.vencordDir = void 0)}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
</Forms.FormSection>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
export * as ScreenShare from "./ScreenSharePicker";
|
||||
export { default as Settings } from "./Settings";
|
||||
|
||||
@@ -94,8 +94,6 @@
|
||||
gap: 1em;
|
||||
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.vcd-screen-picker-quality section {
|
||||
|
||||
26
src/renderer/components/settings/AutoStartToggle.tsx
Normal file
26
src/renderer/components/settings/AutoStartToggle.tsx
Normal file
@@ -0,0 +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
|
||||
*/
|
||||
|
||||
import { Switch, useState } from "@vencord/types/webpack/common";
|
||||
|
||||
import { SettingsComponent } from "./Settings";
|
||||
|
||||
export const AutoStartToggle: SettingsComponent = () => {
|
||||
const [autoStartEnabled, setAutoStartEnabled] = useState(VesktopNative.autostart.isEnabled());
|
||||
|
||||
return (
|
||||
<Switch
|
||||
value={autoStartEnabled}
|
||||
onChange={async v => {
|
||||
await VesktopNative.autostart[v ? "enable" : "disable"]();
|
||||
setAutoStartEnabled(v);
|
||||
}}
|
||||
note="Automatically start Vesktop on computer start-up"
|
||||
>
|
||||
Start With System
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
26
src/renderer/components/settings/DiscordBranchPicker.tsx
Normal file
26
src/renderer/components/settings/DiscordBranchPicker.tsx
Normal file
@@ -0,0 +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
|
||||
*/
|
||||
|
||||
import { Select } from "@vencord/types/webpack/common";
|
||||
|
||||
import { SettingsComponent } from "./Settings";
|
||||
|
||||
export const DiscordBranchPicker: SettingsComponent = ({ settings }) => {
|
||||
return (
|
||||
<Select
|
||||
placeholder="Stable"
|
||||
options={[
|
||||
{ label: "Stable", value: "stable", default: true },
|
||||
{ label: "Canary", value: "canary" },
|
||||
{ label: "PTB", value: "ptb" }
|
||||
]}
|
||||
closeOnSelect={true}
|
||||
select={v => (settings.discordBranch = v)}
|
||||
isSelected={v => v === settings.discordBranch}
|
||||
serialize={s => s}
|
||||
/>
|
||||
);
|
||||
};
|
||||
26
src/renderer/components/settings/NotificationBadgeToggle.tsx
Normal file
26
src/renderer/components/settings/NotificationBadgeToggle.tsx
Normal file
@@ -0,0 +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
|
||||
*/
|
||||
|
||||
import { Switch } from "@vencord/types/webpack/common";
|
||||
import { setBadge } from "renderer/appBadge";
|
||||
|
||||
import { SettingsComponent } from "./Settings";
|
||||
|
||||
export const NotificationBadgeToggle: SettingsComponent = ({ settings }) => {
|
||||
return (
|
||||
<Switch
|
||||
value={settings.appBadge ?? true}
|
||||
onChange={v => {
|
||||
settings.appBadge = v;
|
||||
if (v) setBadge();
|
||||
else VesktopNative.app.setBadgeCount(0);
|
||||
}}
|
||||
note="Show mention badge on the app icon"
|
||||
>
|
||||
Notification Badge
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
170
src/renderer/components/settings/Settings.tsx
Normal file
170
src/renderer/components/settings/Settings.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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 "./settings.css";
|
||||
|
||||
import { Forms, Switch, 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 { DiscordBranchPicker } from "./DiscordBranchPicker";
|
||||
import { NotificationBadgeToggle } from "./NotificationBadgeToggle";
|
||||
import { VencordLocationPicker } from "./VencordLocationPicker";
|
||||
import { WindowsTransparencyControls } from "./WindowsTransparencyControls";
|
||||
|
||||
interface BooleanSetting {
|
||||
key: keyof typeof Settings.store;
|
||||
title: string;
|
||||
description: string;
|
||||
defaultValue: boolean;
|
||||
disabled?(): boolean;
|
||||
invisible?(): boolean;
|
||||
}
|
||||
|
||||
export type SettingsComponent = ComponentType<{ settings: typeof Settings.store }>;
|
||||
|
||||
const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>> = {
|
||||
"Discord Branch": [DiscordBranchPicker],
|
||||
"System Startup & Performance": [
|
||||
AutoStartToggle,
|
||||
{
|
||||
key: "hardwareAcceleration",
|
||||
title: "Hardware Acceleration",
|
||||
description: "Enable hardware acceleration",
|
||||
defaultValue: true
|
||||
}
|
||||
],
|
||||
"User Interface": [
|
||||
{
|
||||
key: "customTitleBar",
|
||||
title: "Discord Titlebar",
|
||||
description: "Use Discord's custom title bar instead of the native system one. Requires a full restart.",
|
||||
defaultValue: isWindows
|
||||
},
|
||||
{
|
||||
key: "staticTitle",
|
||||
title: "Static Title",
|
||||
description: 'Makes the window title "Vesktop" instead of changing to the current page',
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
key: "enableMenu",
|
||||
title: "Enable Menu Bar",
|
||||
description: "Enables the application menu bar. Press ALT to toggle visibility.",
|
||||
defaultValue: false,
|
||||
disabled: () => Settings.store.customTitleBar ?? isWindows
|
||||
},
|
||||
{
|
||||
key: "splashTheming",
|
||||
title: "Splash theming",
|
||||
description: "Adapt the splash window colors to your custom theme",
|
||||
defaultValue: false
|
||||
},
|
||||
WindowsTransparencyControls
|
||||
],
|
||||
Behaviour: [
|
||||
{
|
||||
key: "tray",
|
||||
title: "Tray Icon",
|
||||
description: "Add a tray icon for Vesktop",
|
||||
defaultValue: true,
|
||||
invisible: () => isMac
|
||||
},
|
||||
{
|
||||
key: "minimizeToTray",
|
||||
title: "Minimize to tray",
|
||||
description: "Hitting X will make Vesktop minimize to the tray instead of closing",
|
||||
defaultValue: true,
|
||||
invisible: () => isMac,
|
||||
disabled: () => Settings.store.tray === false
|
||||
},
|
||||
{
|
||||
key: "disableMinSize",
|
||||
title: "Disable minimum window size",
|
||||
description: "Allows you to make the window as small as your heart desires",
|
||||
defaultValue: false
|
||||
},
|
||||
{
|
||||
key: "disableSmoothScroll",
|
||||
title: "Disable smooth scrolling",
|
||||
description: "Disables smooth scrolling",
|
||||
defaultValue: false
|
||||
}
|
||||
],
|
||||
"Notifications & Updates": [
|
||||
NotificationBadgeToggle,
|
||||
{
|
||||
key: "checkUpdates",
|
||||
title: "Check for updates",
|
||||
description: "Automatically check for Vesktop updates",
|
||||
defaultValue: true
|
||||
}
|
||||
],
|
||||
Miscelleanous: [
|
||||
{
|
||||
key: "arRPC",
|
||||
title: "Rich Presence",
|
||||
description: "Enables Rich Presence via arRPC",
|
||||
defaultValue: false
|
||||
},
|
||||
|
||||
{
|
||||
key: "openLinksWithElectron",
|
||||
title: "Open Links in app (experimental)",
|
||||
description: "Opens links in a new Vesktop window instead of your web browser",
|
||||
defaultValue: false
|
||||
}
|
||||
],
|
||||
"Vencord Location": [VencordLocationPicker]
|
||||
};
|
||||
|
||||
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 { defaultValue, title, description, key, disabled, invisible } = Setting;
|
||||
if (invisible?.()) return null;
|
||||
|
||||
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>
|
||||
));
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
62
src/renderer/components/settings/VencordLocationPicker.tsx
Normal file
62
src/renderer/components/settings/VencordLocationPicker.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 { Margins } from "@vencord/types/utils";
|
||||
import { Forms, Select } from "@vencord/types/webpack/common";
|
||||
|
||||
import { SettingsComponent } from "./Settings";
|
||||
|
||||
export const WindowsTransparencyControls: SettingsComponent = ({ settings }) => {
|
||||
if (!VesktopNative.app.supportsWindowsTransparency()) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Forms.FormTitle className={Margins.top16 + " " + 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>
|
||||
|
||||
<Select
|
||||
placeholder="None"
|
||||
options={[
|
||||
{
|
||||
label: "None",
|
||||
value: "none",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "Mica (incorporates system theme + desktop wallpaper to paint the background)",
|
||||
value: "mica"
|
||||
},
|
||||
{ label: "Tabbed (variant of Mica with stronger background tinting)", value: "tabbed" },
|
||||
{
|
||||
label: "Acrylic (blurs the window behind Vesktop for a translucent background)",
|
||||
value: "acrylic"
|
||||
}
|
||||
]}
|
||||
closeOnSelect={true}
|
||||
select={v => (settings.transparencyOption = v)}
|
||||
isSelected={v => v === settings.transparencyOption}
|
||||
serialize={s => s}
|
||||
/>
|
||||
|
||||
<Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -4,3 +4,11 @@
|
||||
gap: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.vcd-settings-section {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.vcd-settings-title {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import "./hideGarbage.css";
|
||||
|
||||
import { waitFor } from "@vencord/types/webpack";
|
||||
|
||||
import { isFirstRun, localStorage } from "./utils";
|
||||
import { isWindows, localStorage } from "./utils";
|
||||
|
||||
// Make clicking Notifications focus the window
|
||||
const originalSetOnClick = Object.getOwnPropertyDescriptor(Notification.prototype, "onclick")!.set!;
|
||||
@@ -16,18 +14,22 @@ Object.defineProperty(Notification.prototype, "onclick", {
|
||||
set(onClick) {
|
||||
originalSetOnClick.call(this, function (this: unknown) {
|
||||
onClick.apply(this, arguments);
|
||||
VencordDesktopNative.win.focus();
|
||||
VesktopNative.win.focus();
|
||||
});
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// Enable Desktop Notifications by default
|
||||
if (isFirstRun) {
|
||||
// Hide "Download Discord Desktop now!!!!" banner
|
||||
localStorage.setItem("hideNag", "true");
|
||||
// Hide "Download Discord Desktop now!!!!" banner
|
||||
localStorage.setItem("hideNag", "true");
|
||||
|
||||
waitFor("setDesktopType", m => {
|
||||
m.setDesktopType("all");
|
||||
});
|
||||
}
|
||||
// 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 {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* 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]) {
|
||||
[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;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,59 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import "./fixes";
|
||||
import "./appBadge";
|
||||
import "./patches";
|
||||
import "./themedSplash";
|
||||
|
||||
console.log("read if cute :3");
|
||||
|
||||
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 { Settings } from "./settings";
|
||||
export { Settings };
|
||||
|
||||
const arRPC = Vencord.Plugins.plugins["WebRichPresence (arRPC)"];
|
||||
const InviteActions = findByPropsLazy("resolveInvite");
|
||||
|
||||
arRPC.required = !!Settings.store.arRPC;
|
||||
export async function openInviteModal(code: string) {
|
||||
const { invite } = await InviteActions.resolveInvite(code, "Desktop Modal");
|
||||
if (!invite) return false;
|
||||
|
||||
Settings.addChangeListener("arRPC", v => {
|
||||
arRPC.required = !!v;
|
||||
if (v && !arRPC.started) Vencord.Plugins.startPlugin(arRPC);
|
||||
else if (arRPC.started) {
|
||||
Vencord.Plugins.stopPlugin(arRPC);
|
||||
}
|
||||
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;
|
||||
|
||||
customSettingsSections.push(() => ({
|
||||
section: "Vesktop",
|
||||
label: "Vesktop Settings",
|
||||
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 }));
|
||||
});
|
||||
|
||||
21
src/renderer/patches/enableNotificationsByDefault.ts
Normal file
21
src/renderer/patches/enableNotificationsByDefault.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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: '"NotificationSettingsStore',
|
||||
replacement: {
|
||||
// FIXME: fix eslint rule
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /\.isPlatformEmbedded(?=\?\i\.DesktopNotificationTypes\.ALL)/g,
|
||||
replace: "$&||true"
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1,8 +1,12 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* 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 "./screenShareAudio";
|
||||
import "./spellCheck";
|
||||
import "./windowsTitleBar";
|
||||
|
||||
29
src/renderer/patches/platformClass.tsx
Normal file
29
src/renderer/patches/platformClass.tsx
Normal file
@@ -0,0 +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
|
||||
*/
|
||||
|
||||
import { Settings } from "renderer/settings";
|
||||
import { isMac } from "renderer/utils";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
find: "platform-web",
|
||||
replacement: {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /(?<=" platform-overlay"\):)\i/,
|
||||
replace: "$self.getPlatformClass()"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
getPlatformClass() {
|
||||
if (Settings.store.customTitleBar) return "platform-win";
|
||||
if (isMac) return "platform-osx";
|
||||
return "platform-web";
|
||||
}
|
||||
});
|
||||
42
src/renderer/patches/screenShareAudio.ts
Normal file
42
src/renderer/patches/screenShareAudio.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 { isLinux } from "renderer/utils";
|
||||
|
||||
if (isLinux) {
|
||||
const original = navigator.mediaDevices.getDisplayMedia;
|
||||
|
||||
async function getVirtmic() {
|
||||
try {
|
||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||
const audioDevice = devices.find(({ label }) => label === "vencord-screen-share");
|
||||
return audioDevice?.deviceId;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
navigator.mediaDevices.getDisplayMedia = async function (opts) {
|
||||
const stream = await original.call(this, opts);
|
||||
const id = await getVirtmic();
|
||||
|
||||
if (id) {
|
||||
const audio = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
deviceId: {
|
||||
exact: id
|
||||
},
|
||||
autoGainControl: false,
|
||||
echoCancellation: false,
|
||||
noiseSuppression: false
|
||||
}
|
||||
});
|
||||
audio.getAudioTracks().forEach(t => stream.addTrack(t));
|
||||
}
|
||||
|
||||
return stream;
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -22,8 +22,8 @@ export function addPatch<P extends PatchData>(p: P) {
|
||||
if (typeof r.replace === "string") r.replace = r.replace.replaceAll("$self", "VCDP");
|
||||
}
|
||||
|
||||
patch.plugin = "VencordDesktop";
|
||||
Vencord.Plugins.patches.push(patch as Patch);
|
||||
patch.plugin = "Vesktop";
|
||||
Vencord.Plugins.patches.push(patch);
|
||||
}
|
||||
|
||||
Object.assign(VCDP, globals);
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { addContextMenuPatch } from "@vencord/types/api/ContextMenu";
|
||||
import { Menu } from "@vencord/types/webpack/common";
|
||||
import { findStoreLazy } from "@vencord/types/webpack";
|
||||
import { FluxDispatcher, Menu, useStateFromStores } from "@vencord/types/webpack/common";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
let word: string;
|
||||
let corrections: string[];
|
||||
|
||||
const SpellCheckStore = findStoreLazy("SpellcheckStore");
|
||||
|
||||
// Make spellcheck suggestions work
|
||||
addPatch({
|
||||
patches: [
|
||||
@@ -19,41 +22,60 @@ addPatch({
|
||||
find: ".enableSpellCheck)",
|
||||
replacement: {
|
||||
// if (isDesktop) { DiscordNative.onSpellcheck(openMenu(props)) } else { e.preventDefault(); openMenu(props) }
|
||||
match: /else\{.{1,3}\.preventDefault\(\);(.{1,3}\(.{1,3}\))\}/,
|
||||
match: /else (.{1,3})\.preventDefault\(\),(.{1,3}\(.{1,3}\))(?<=:(.{1,3})\.enableSpellCheck\).+?)/,
|
||||
// ... else { $self.onSlateContext(() => openMenu(props)) }
|
||||
replace: "else {$self.onSlateContext(() => $1)}"
|
||||
replace: "else {$self.onSlateContext($1, $3?.enableSpellCheck, () => $2)}"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
onSlateContext(openMenu: () => void) {
|
||||
onSlateContext(e: MouseEvent, hasSpellcheck: boolean | undefined, openMenu: () => void) {
|
||||
if (!hasSpellcheck) {
|
||||
e.preventDefault();
|
||||
openMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
const cb = (w: string, c: string[]) => {
|
||||
VencordDesktopNative.spellcheck.offSpellcheckResult(cb);
|
||||
VesktopNative.spellcheck.offSpellcheckResult(cb);
|
||||
word = w;
|
||||
corrections = c;
|
||||
openMenu();
|
||||
};
|
||||
VencordDesktopNative.spellcheck.onSpellcheckResult(cb);
|
||||
VesktopNative.spellcheck.onSpellcheckResult(cb);
|
||||
}
|
||||
});
|
||||
|
||||
addContextMenuPatch("textarea-context", children => () => {
|
||||
if (!word || !corrections?.length) return;
|
||||
addContextMenuPatch("textarea-context", children => {
|
||||
const spellCheckEnabled = useStateFromStores([SpellCheckStore], () => SpellCheckStore.isEnabled());
|
||||
const hasCorrections = Boolean(word && corrections?.length);
|
||||
|
||||
children.push(
|
||||
<Menu.MenuGroup>
|
||||
{corrections.map(c => (
|
||||
<Menu.MenuItem
|
||||
id={"vcd-spellcheck-suggestion-" + c}
|
||||
label={c}
|
||||
action={() => VencordDesktopNative.spellcheck.replaceMisspelling(c)}
|
||||
/>
|
||||
))}
|
||||
<Menu.MenuSeparator />
|
||||
<Menu.MenuItem
|
||||
id="vcd-spellcheck-learn"
|
||||
label={`Add ${word} to dictionary`}
|
||||
action={() => VencordDesktopNative.spellcheck.addToDictionary(word)}
|
||||
{hasCorrections && (
|
||||
<>
|
||||
{corrections.map(c => (
|
||||
<Menu.MenuItem
|
||||
id={"vcd-spellcheck-suggestion-" + c}
|
||||
label={c}
|
||||
action={() => VesktopNative.spellcheck.replaceMisspelling(c)}
|
||||
/>
|
||||
))}
|
||||
<Menu.MenuSeparator />
|
||||
<Menu.MenuItem
|
||||
id="vcd-spellcheck-learn"
|
||||
label={`Add ${word} to dictionary`}
|
||||
action={() => VesktopNative.spellcheck.addToDictionary(word)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Menu.MenuCheckboxItem
|
||||
id="vcd-spellcheck-enabled"
|
||||
label="Enable Spellcheck"
|
||||
checked={spellCheckEnabled}
|
||||
action={() => {
|
||||
FluxDispatcher.dispatch({ type: "SPELLCHECK_TOGGLE" });
|
||||
}}
|
||||
/>
|
||||
</Menu.MenuGroup>
|
||||
);
|
||||
|
||||
30
src/renderer/patches/windowsTitleBar.tsx
Normal file
30
src/renderer/patches/windowsTitleBar.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 { Settings } from "renderer/settings";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
if (Settings.store.customTitleBar)
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
find: ".wordmarkWindows",
|
||||
replacement: [
|
||||
{
|
||||
// TODO: Fix eslint rule
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /case \i\.\i\.WINDOWS:/,
|
||||
replace: 'case "WEB":'
|
||||
},
|
||||
...["close", "minimize", "maximize"].map(op => ({
|
||||
match: new RegExp(String.raw`\i\.\i\.${op}\b`),
|
||||
replace: `VesktopNative.win.${op}`
|
||||
}))
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { useEffect, useReducer } from "@vencord/types/webpack/common";
|
||||
import { SettingsStore } from "shared/utils/SettingsStore";
|
||||
|
||||
export const Settings = new SettingsStore(VencordDesktopNative.settings.get());
|
||||
Settings.addGlobalChangeListener((o, p) => VencordDesktopNative.settings.set(o, p));
|
||||
export const Settings = new SettingsStore(VesktopNative.settings.get());
|
||||
Settings.addGlobalChangeListener((o, p) => VesktopNative.settings.set(o, p));
|
||||
|
||||
export function useSettings() {
|
||||
const [, update] = useReducer(x => x + 1, 0);
|
||||
|
||||
46
src/renderer/themedSplash.ts
Normal file
46
src/renderer/themedSplash.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 { Settings } from "./settings";
|
||||
|
||||
function isValidColor(color: CSSStyleValue | undefined): color is CSSUnparsedValue & { [0]: string } {
|
||||
return color instanceof CSSUnparsedValue && typeof color[0] === "string" && CSS.supports("color", color[0]);
|
||||
}
|
||||
|
||||
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;
|
||||
span.remove();
|
||||
|
||||
return rgbColor;
|
||||
}
|
||||
|
||||
const updateSplashColors = () => {
|
||||
const bodyStyles = document.body.computedStyleMap();
|
||||
|
||||
const color = bodyStyles.get("--text-normal");
|
||||
const backgroundColor = bodyStyles.get("--background-primary");
|
||||
|
||||
if (isValidColor(color)) {
|
||||
Settings.store.splashColor = resolveColor(color[0]);
|
||||
}
|
||||
|
||||
if (isValidColor(backgroundColor)) {
|
||||
Settings.store.splashBackground = resolveColor(backgroundColor[0]);
|
||||
}
|
||||
};
|
||||
|
||||
if (document.readyState === "complete") {
|
||||
updateSplashColors();
|
||||
} else {
|
||||
window.addEventListener("load", updateSplashColors);
|
||||
}
|
||||
|
||||
window.addEventListener("beforeunload", updateSplashColors);
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
export const localStorage = (window.vcdLS = window.localStorage);
|
||||
export const { localStorage } = window;
|
||||
|
||||
export const isFirstRun = (() => {
|
||||
const key = "VCD_FIRST_RUN";
|
||||
@@ -16,3 +16,5 @@ export const isFirstRun = (() => {
|
||||
const { platform } = navigator;
|
||||
|
||||
export const isWindows = platform.startsWith("Win");
|
||||
export const isMac = platform.startsWith("Mac");
|
||||
export const isLinux = platform.startsWith("Linux");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -11,10 +11,13 @@ export const enum IpcEvents {
|
||||
GET_RENDERER_CSS_FILE = "VCD_GET_RENDERER_CSS_FILE",
|
||||
|
||||
GET_VERSION = "VCD_GET_VERSION",
|
||||
SUPPORTS_WINDOWS_TRANSPARENCY = "VCD_SUPPORTS_WINDOWS_TRANSPARENCY",
|
||||
|
||||
RELAUNCH = "VCD_RELAUNCH",
|
||||
CLOSE = "VCD_CLOSE",
|
||||
FOCUS = "VCD_FOCUS",
|
||||
MINIMIZE = "VCD_MINIMIZE",
|
||||
MAXIMIZE = "VCD_MAXIMIZE",
|
||||
|
||||
SHOW_ITEM_IN_FOLDER = "VCD_SHOW_ITEM_IN_FOLDER",
|
||||
GET_SETTINGS = "VCD_GET_SETTINGS",
|
||||
@@ -37,5 +40,14 @@ export const enum IpcEvents {
|
||||
|
||||
AUTOSTART_ENABLED = "VCD_AUTOSTART_ENABLED",
|
||||
ENABLE_AUTOSTART = "VCD_ENABLE_AUTOSTART",
|
||||
DISABLE_AUTOSTART = "VCD_DISABLE_AUTOSTART"
|
||||
DISABLE_AUTOSTART = "VCD_DISABLE_AUTOSTART",
|
||||
|
||||
VIRT_MIC_LIST = "VCD_VIRT_MIC_LIST",
|
||||
VIRT_MIC_START = "VCD_VIRT_MIC_START",
|
||||
VIRT_MIC_START_SYSTEM = "VCD_VIRT_MIC_START_ALL",
|
||||
VIRT_MIC_STOP = "VCD_VIRT_MIC_STOP",
|
||||
|
||||
ARRPC_ACTIVITY = "VCD_ARRPC_ACTIVITY",
|
||||
|
||||
CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
|
||||
41
src/shared/settings.d.ts
vendored
41
src/shared/settings.d.ts
vendored
@@ -1,25 +1,44 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import type { Rectangle } from "electron";
|
||||
|
||||
export interface Settings {
|
||||
discordBranch?: "stable" | "canary" | "ptb";
|
||||
vencordDir?: string;
|
||||
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
|
||||
tray?: boolean;
|
||||
minimizeToTray?: boolean;
|
||||
openLinksWithElectron?: boolean;
|
||||
staticTitle?: boolean;
|
||||
enableMenu?: boolean;
|
||||
disableSmoothScroll?: boolean;
|
||||
hardwareAcceleration?: boolean;
|
||||
arRPC?: boolean;
|
||||
appBadge?: boolean;
|
||||
disableMinSize?: boolean;
|
||||
|
||||
/** @deprecated use customTitleBar */
|
||||
discordWindowsTitleBar?: boolean;
|
||||
customTitleBar?: boolean;
|
||||
|
||||
checkUpdates?: boolean;
|
||||
|
||||
splashTheming?: boolean;
|
||||
splashColor?: string;
|
||||
splashBackground?: string;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
maximized?: boolean;
|
||||
minimized?: boolean;
|
||||
windowBounds?: Rectangle;
|
||||
discordBranch?: "stable" | "canary" | "ptb";
|
||||
openLinksWithElectron?: boolean;
|
||||
vencordDir?: string;
|
||||
disableMinSize?: boolean;
|
||||
tray?: boolean;
|
||||
minimizeToTray?: boolean;
|
||||
skippedUpdate?: string;
|
||||
staticTitle?: boolean;
|
||||
arRPC?: boolean;
|
||||
appBadge?: boolean;
|
||||
|
||||
skippedUpdate?: string;
|
||||
firstLaunch?: boolean;
|
||||
|
||||
steamOSLayoutVersion?: number;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
@@ -12,8 +12,8 @@ type ResolvePropDeep<T, P> = P extends `${infer Pre}.${infer Suf}`
|
||||
? ResolvePropDeep<T[Pre], Suf>
|
||||
: any
|
||||
: P extends keyof T
|
||||
? T[P]
|
||||
: any;
|
||||
? T[P]
|
||||
: any;
|
||||
|
||||
/**
|
||||
* The SettingsStore allows you to easily create a mutable store that
|
||||
@@ -144,4 +144,11 @@ export class SettingsStore<T extends object> {
|
||||
listeners.delete(cb);
|
||||
if (!listeners.size) this.pathListeners.delete(path as string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call all global change listeners
|
||||
*/
|
||||
public markAsChanged() {
|
||||
this.globalListeners.forEach(cb => cb(this.plain, ""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
|
||||
13
src/shared/utils/guards.ts
Normal file
13
src/shared/utils/guards.ts
Normal file
@@ -0,0 +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
|
||||
*/
|
||||
|
||||
export function isTruthy<T>(item: T): item is Exclude<T, 0 | "" | false | null | undefined> {
|
||||
return Boolean(item);
|
||||
}
|
||||
|
||||
export function isNonNullish<T>(item: T): item is Exclude<T, null | undefined> {
|
||||
return item != null;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
type Func = (...args: any[]) => any;
|
||||
|
||||
export function monkeyPatch<O extends object>(
|
||||
object: O,
|
||||
key: keyof O,
|
||||
replacement: (original: Func, ...args: any[]) => any
|
||||
): void {
|
||||
const original = object[key] as Func;
|
||||
|
||||
const replacer = (object[key] = function (this: unknown, ...args: any[]) {
|
||||
return replacement.call(this, original, ...args);
|
||||
} as any);
|
||||
|
||||
Object.defineProperties(replacer, Object.getOwnPropertyDescriptors(original));
|
||||
replacer.toString = () => original.toString();
|
||||
replacer.$$original = original;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
|
||||
9
src/shared/utils/sleep.ts
Normal file
9
src/shared/utils/sleep.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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 function sleep(ms: number): Promise<void> {
|
||||
return new Promise(r => setTimeout(r, ms));
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { app, BrowserWindow, ipcMain, shell } from "electron";
|
||||
import { Settings } from "main/settings";
|
||||
import { app, BrowserWindow, shell } from "electron";
|
||||
import { Settings, State } from "main/settings";
|
||||
import { handle } from "main/utils/ipcWrappers";
|
||||
import { makeLinksOpenExternally } from "main/utils/makeLinksOpenExternally";
|
||||
import { githubGet, ReleaseData } from "main/utils/vencordLoader";
|
||||
import { join } from "path";
|
||||
import { SplashProps } from "shared/browserWinProperties";
|
||||
import { IpcEvents } from "shared/IpcEvents";
|
||||
import { VIEW_DIR } from "shared/paths";
|
||||
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||
|
||||
export interface UpdateData {
|
||||
currentVersion: string;
|
||||
@@ -20,8 +21,8 @@ export interface UpdateData {
|
||||
|
||||
let updateData: UpdateData;
|
||||
|
||||
ipcMain.handle(IpcEvents.UPDATER_GET_DATA, () => updateData);
|
||||
ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
||||
handle(IpcEvents.UPDATER_GET_DATA, () => updateData);
|
||||
handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
||||
const portable = !!process.env.PORTABLE_EXECUTABLE_FILE;
|
||||
|
||||
const { assets } = updateData.release;
|
||||
@@ -35,7 +36,11 @@ ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
||||
return portable ? !isSetup : isSetup;
|
||||
})!.browser_download_url;
|
||||
case "darwin":
|
||||
return assets.find(a => a.name.endsWith(".dmg"))!.browser_download_url;
|
||||
return assets.find(a =>
|
||||
process.arch === "arm64"
|
||||
? a.name.endsWith("-arm64-mac.zip")
|
||||
: a.name.endsWith("-mac.zip") && !a.name.includes("arm64")
|
||||
)!.browser_download_url;
|
||||
case "linux":
|
||||
return updateData.release.html_url;
|
||||
default:
|
||||
@@ -46,8 +51,8 @@ ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
||||
shell.openExternal(url);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.UPDATE_IGNORE, () => {
|
||||
Settings.store.skippedUpdate = updateData.latestVersion;
|
||||
handle(IpcEvents.UPDATE_IGNORE, () => {
|
||||
State.store.skippedUpdate = updateData.latestVersion;
|
||||
});
|
||||
|
||||
function isOutdated(oldVersion: string, newVersion: string) {
|
||||
@@ -72,20 +77,21 @@ function isOutdated(oldVersion: string, newVersion: string) {
|
||||
}
|
||||
|
||||
export async function checkUpdates() {
|
||||
// if (IS_DEV) return;
|
||||
if (Settings.store.checkUpdates === false) return;
|
||||
|
||||
try {
|
||||
const raw = await githubGet("/repos/Vencord/Desktop/releases/latest");
|
||||
const data = JSON.parse(raw.toString("utf-8")) as ReleaseData;
|
||||
const raw = await githubGet("/repos/Vencord/Vesktop/releases/latest");
|
||||
const data: ReleaseData = await raw.json();
|
||||
|
||||
const oldVersion = app.getVersion();
|
||||
const newVersion = data.tag_name.replace(/^v/, "");
|
||||
if (Settings.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) {
|
||||
updateData = {
|
||||
currentVersion: oldVersion,
|
||||
latestVersion: newVersion,
|
||||
release: data
|
||||
};
|
||||
updateData = {
|
||||
currentVersion: oldVersion,
|
||||
latestVersion: newVersion,
|
||||
release: data
|
||||
};
|
||||
|
||||
if (State.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) {
|
||||
openNewUpdateWindow();
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -95,11 +101,19 @@ export async function checkUpdates() {
|
||||
|
||||
function openNewUpdateWindow() {
|
||||
const win = new BrowserWindow({
|
||||
...SplashProps,
|
||||
width: 500,
|
||||
autoHideMenuBar: true,
|
||||
alwaysOnTop: true,
|
||||
webPreferences: {
|
||||
preload: join(__dirname, "updaterPreload.js")
|
||||
}
|
||||
preload: join(__dirname, "updaterPreload.js"),
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
sandbox: true
|
||||
},
|
||||
icon: ICON_PATH
|
||||
});
|
||||
|
||||
makeLinksOpenExternally(win);
|
||||
|
||||
win.loadFile(join(VIEW_DIR, "updater.html"));
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { contextBridge } from "electron";
|
||||
import { invoke } from "preload/typedIpcs";
|
||||
import { invoke } from "preload/typedIpc";
|
||||
import { IpcEvents } from "shared/IpcEvents";
|
||||
|
||||
import type { UpdateData } from "./main";
|
||||
|
||||
2
static/dist/.gitignore
vendored
Normal file
2
static/dist/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
BIN
static/shiggy.gif
Normal file
BIN
static/shiggy.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -1,9 +1,9 @@
|
||||
<head>
|
||||
<link rel="stylesheet" href="./style.css" type="text/css" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
padding: 2em;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
"Open Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@@ -13,10 +13,10 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Vencord Desktop %VERSION%</h1>
|
||||
<h1 id="title">Vesktop</h1>
|
||||
<p>
|
||||
Vencord Desktop is a free/libre cross platform desktop app aiming to give you a snappier Discord experience with
|
||||
Vencord pre-installed
|
||||
Vesktop is a free/libre cross platform desktop app aiming to give you a snappier Discord experience with Vencord
|
||||
pre-installed
|
||||
</p>
|
||||
|
||||
<section>
|
||||
@@ -26,17 +26,17 @@
|
||||
<a href="https://vencord.dev" target="_blank">Vencord Website</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/Vencord/Desktop" target="_blank">Source Code</a>
|
||||
<a href="https://github.com/Vencord/Vesktop" target="_blank">Source Code</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/Vencord/Desktop/issues" target="_blank">Report bugs / Request features</a>
|
||||
<a href="https://github.com/Vencord/Vesktop/issues" target="_blank">Report bugs / Request features</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Acknowledgements</h2>
|
||||
<p>These awesome libraries empower Vencord Desktop</p>
|
||||
<p>These awesome libraries empower Vesktop</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/electron/electron" target="_blank">Electron</a>
|
||||
@@ -51,12 +51,25 @@
|
||||
<a href="https://github.com/OpenAsar/arrpc" target="_blank">arrpc</a>
|
||||
- An open implementation of Discord's Rich Presence server
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/Soundux/rohrkabel" target="_blank">rohrkabel</a>
|
||||
- A C++ RAII Pipewire-API Wrapper
|
||||
</li>
|
||||
<li>
|
||||
And many
|
||||
<a href="https://github.com/Vencord/Desktop/blob/main/pnpm-lock.yaml" target="_blank"
|
||||
<a href="https://github.com/Vencord/Vesktop/blob/main/pnpm-lock.yaml" target="_blank"
|
||||
>more awesome open source libraries</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</body>
|
||||
|
||||
<script type="module">
|
||||
const data = await Updater.getData();
|
||||
if (data.currentVersion) {
|
||||
const title = document.getElementById("title");
|
||||
|
||||
title.textContent += ` v${data.currentVersion}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,29 +1,10 @@
|
||||
<head>
|
||||
<link rel="stylesheet" href="./style.css" type="text/css" />
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg: white;
|
||||
--fg: black;
|
||||
--fg-secondary: #313338;
|
||||
--fg-semi-trans: rgb(0 0 0 / 0.2);
|
||||
--link: #006ce7;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: hsl(223 6.7% 20.6%);
|
||||
--fg: white;
|
||||
--fg-secondary: #b5bac1;
|
||||
--fg-semi-trans: rgb(255 255 255 / 0.2);
|
||||
--link: #00a8fc;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100vh;
|
||||
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
"Open Sans", "Helvetica Neue", sans-serif;
|
||||
margin: 0;
|
||||
padding: 1.5em;
|
||||
padding-bottom: 1em;
|
||||
|
||||
@@ -34,13 +15,9 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body,
|
||||
select {
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 0.3em;
|
||||
margin: -0.3em;
|
||||
border-radius: 6px;
|
||||
@@ -54,10 +31,6 @@
|
||||
margin: 1em 0 2em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link);
|
||||
}
|
||||
|
||||
form {
|
||||
display: grid;
|
||||
gap: 1em;
|
||||
@@ -131,7 +104,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Welcome to Vencord Desktop</h1>
|
||||
<h1>Welcome to Vesktop</h1>
|
||||
<p>Let's customise your experience!</p>
|
||||
|
||||
<form>
|
||||
@@ -147,7 +120,7 @@
|
||||
<label>
|
||||
<div>
|
||||
<h2>Start with System</h2>
|
||||
<span>Automatically open Vencord Desktop when your computer starts</span>
|
||||
<span>Automatically open Vesktop when your computer starts</span>
|
||||
</div>
|
||||
<input type="checkbox" name="autoStart" />
|
||||
</label>
|
||||
@@ -155,7 +128,10 @@
|
||||
<label>
|
||||
<div>
|
||||
<h2>Rich Presence</h2>
|
||||
<span>Enable Rich presence (game activity) via arRPC</span>
|
||||
<span
|
||||
>Enable Rich presence (game activity) via
|
||||
<a href="https://github.com/OpenAsar/arrpc" target="_blank">arRPC</a></span
|
||||
>
|
||||
</div>
|
||||
<input type="checkbox" name="richPresence" checked />
|
||||
</label>
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
<head>
|
||||
<style>
|
||||
* {
|
||||
user-select: none;
|
||||
}
|
||||
<link rel="stylesheet" href="./style.css" type="text/css" />
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
"Open Sans", "Helvetica Neue", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
@@ -18,19 +14,18 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: hsl(223 6.7% 20.6%);
|
||||
border-radius: 8px;
|
||||
border: 1px solid hsl(220 6.5% 18%);
|
||||
border: 1px solid var(--fg-semi-trans);
|
||||
}
|
||||
|
||||
p {
|
||||
color: rgb(219, 222, 225);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 6em;
|
||||
height: 6em;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -39,10 +34,10 @@
|
||||
<div class="wrapper">
|
||||
<img
|
||||
draggable="false"
|
||||
src="https://cdn.discordapp.com/emojis/1024751291504791654.gif?size=512"
|
||||
src="../shiggy.gif"
|
||||
alt="shiggy"
|
||||
role="presentation"
|
||||
/>
|
||||
<p>Loading Vencord Desktop...</p>
|
||||
<p>Loading Vesktop...</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
30
static/views/style.css
Normal file
30
static/views/style.css
Normal file
@@ -0,0 +1,30 @@
|
||||
:root {
|
||||
--bg: white;
|
||||
--fg: black;
|
||||
--fg-secondary: #313338;
|
||||
--fg-semi-trans: rgb(0 0 0 / 0.2);
|
||||
--link: #006ce7;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: hsl(223 6.7% 20.6%);
|
||||
--fg: white;
|
||||
--fg-secondary: #b5bac1;
|
||||
--fg-semi-trans: rgb(255 255 255 / 0.2);
|
||||
--link: #00a8fc;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
"Open Sans", "Helvetica Neue", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link);
|
||||
}
|
||||
@@ -1,22 +1,13 @@
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
"Open Sans", "Helvetica Neue", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: rgb(219, 222, 225);
|
||||
}
|
||||
<link rel="stylesheet" href="./style.css" type="text/css" />
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
background-color: #313338;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #248046;
|
||||
min-height: 100%;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
@@ -34,7 +25,7 @@
|
||||
button {
|
||||
cursor: pointer;
|
||||
padding: 0.5em;
|
||||
color: white;
|
||||
color: var(--fg);
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
@@ -60,12 +51,15 @@
|
||||
<div class="wrapper">
|
||||
<section>
|
||||
<h1>Update Available</h1>
|
||||
<p>There's a new update for Vencord Desktop! Update now to get new fixes and features!</p>
|
||||
<p>There's a new update for Vesktop! Update now to get new fixes and features!</p>
|
||||
<p>
|
||||
Current: <span id="current"></span>
|
||||
<br />
|
||||
Latest: <span id="latest"></span>
|
||||
</p>
|
||||
|
||||
<h2>Changelog</h2>
|
||||
<p id="changelog">Loading...</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -113,3 +107,17 @@
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import { micromark } from "https://esm.sh/micromark@3?bundle";
|
||||
import { gfm, gfmHtml } from "https://esm.sh/micromark-extension-gfm@2?bundle";
|
||||
|
||||
const changelog = (await Updater.getData()).release.body;
|
||||
if (changelog)
|
||||
document.getElementById("changelog").innerHTML = micromark(changelog, {
|
||||
extensions: [gfm()],
|
||||
htmlExtensions: [gfmHtml()]
|
||||
})
|
||||
.replace(/h1>/g, "h3>")
|
||||
.replace(/<a /g, '<a target="_blank" ');
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user