Compare commits
108 Commits
v0.2.7
...
fix/start-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb09596485 | ||
|
|
894ec4b902 | ||
|
|
dd44602730 | ||
|
|
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 |
@@ -4,3 +4,5 @@
|
|||||||
# all permissions at the defaults (public repos read only, 0 permissions):
|
# all permissions at the defaults (public repos read only, 0 permissions):
|
||||||
# https://github.com/settings/personal-access-tokens/new
|
# https://github.com/settings/personal-access-tokens/new
|
||||||
GITHUB_TOKEN=
|
GITHUB_TOKEN=
|
||||||
|
|
||||||
|
ELECTRON_LAUNCH_FLAGS="--ozone-platform-hint=auto --enable-webrtc-pipewire-capturer --enable-features=WaylandWindowDecorations"
|
||||||
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 }}
|
||||||
31
.github/workflows/release.yml
vendored
31
.github/workflows/release.yml
vendored
@@ -12,17 +12,32 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
include:
|
||||||
|
- os: macos-latest
|
||||||
|
platform: mac
|
||||||
|
- os: ubuntu-latest
|
||||||
|
platform: linux
|
||||||
|
- os: windows-latest
|
||||||
|
platform: windows
|
||||||
|
|
||||||
steps:
|
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:
|
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
|
- name: Run Electron Builder
|
||||||
uses: samuelmeuli/action-electron-builder@e4b12cd06ddf023422f1ac4e39632bd76f6e6928
|
run: |
|
||||||
with:
|
pnpm electron-builder --${{ matrix.platform }} --publish always
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
env:
|
||||||
RELEASE: true
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -14,10 +14,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
|
- 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
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18.18.2
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
|
||||||
- name: Install dependencies
|
- 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
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
.env
|
.env
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.pnpm-store/
|
||||||
50
.vscode/settings.json
vendored
50
.vscode/settings.json
vendored
@@ -1,25 +1,25 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": "explicit"
|
||||||
},
|
},
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[javascript]": {
|
"[javascript]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[typescriptreact]": {
|
"[typescriptreact]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[javascriptreact]": {
|
"[javascriptreact]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[jsonc]": {
|
"[jsonc]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"cSpell.words": ["Vesktop"]
|
"cSpell.words": ["Vesktop"]
|
||||||
}
|
}
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -1,13 +1,15 @@
|
|||||||
# Vesktop
|
# Vesktop
|
||||||
|
|
||||||
Vesktop is a cross platform desktop app aiming to give you a snappier Discord experience with Vencord pre-installed
|
Vesktop is a cross platform desktop app aiming to give you a snappier Discord experience with [Vencord](https://github.com/Vendicated/Vencord) pre-installed
|
||||||
|
|
||||||
**Not yet supported**:
|
**Not yet supported**:
|
||||||
- Global Keybinds
|
- Global Keybinds
|
||||||
|
|
||||||
Bug reports, feature requests & contributions are highly appreciated!!
|
Bug reports, feature requests & contributions are highly appreciated!!
|
||||||
|
|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
@@ -21,6 +23,8 @@ Download and run Vesktop-VERSION.dmg from [releases](https://github.com/Vencord/
|
|||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
|
[](https://flathub.org/apps/dev.vencord.Vesktop)
|
||||||
|
|
||||||
#### Arch based
|
#### Arch based
|
||||||
|
|
||||||
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)
|
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)
|
||||||
@@ -37,7 +41,7 @@ Download Vesktop-VERSION.rpm from [releases](https://github.com/Vencord/Vesktop/
|
|||||||
|
|
||||||
Either download Vesktop-VERSION.AppImage and just run it directly or grab Vesktop-VERSION.tar.gz, extract it somewhere and run `vencorddesktop`.
|
Either download Vesktop-VERSION.AppImage and just run it directly or grab Vesktop-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
|
If other packages are created, feel free to open an issue and we'll link them here.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|||||||
BIN
build/icon.icns
Normal file
BIN
build/icon.icns
Normal file
Binary file not shown.
167
meta/dev.vencord.Vesktop.metainfo.xml
Normal file
167
meta/dev.vencord.Vesktop.metainfo.xml
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<?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="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>
|
||||||
|
<internet>always</internet>
|
||||||
|
</recommends>
|
||||||
|
<supports>
|
||||||
|
<internet>always</internet>
|
||||||
|
</supports>
|
||||||
|
<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>
|
||||||
95
package.json
95
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "VencordDesktop",
|
"name": "VencordDesktop",
|
||||||
"version": "0.2.7",
|
"version": "0.4.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "",
|
"description": "",
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
@@ -20,37 +20,42 @@
|
|||||||
"start:watch": "pnpm build:dev && tsx scripts/startWatch.mts",
|
"start:watch": "pnpm build:dev && tsx scripts/startWatch.mts",
|
||||||
"test": "pnpm lint && pnpm testTypes",
|
"test": "pnpm lint && pnpm testTypes",
|
||||||
"testTypes": "tsc --noEmit",
|
"testTypes": "tsc --noEmit",
|
||||||
"watch": "pnpm build --watch"
|
"watch": "pnpm build --watch",
|
||||||
|
"updateMeta": "tsx scripts/utils/updateMeta.mts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"arrpc": "github:OpenAsar/arrpc#061d473dc742266fc7f61587ed18e666543fed1e"
|
"arrpc": "github:OpenAsar/arrpc#3e22fd776273afaa4a80c51deb86077ffdd4d2ae"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@vencord/venmic": "^2.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
||||||
"@types/node": "^18.15.11",
|
"@types/node": "^20.10.0",
|
||||||
"@types/react": "^18.0.33",
|
"@types/react": "^18.2.39",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
"@typescript-eslint/eslint-plugin": "^6.13.1",
|
||||||
"@typescript-eslint/parser": "^5.57.1",
|
"@typescript-eslint/parser": "^6.13.1",
|
||||||
"@vencord/types": "^0.1.2",
|
"@vencord/types": "^0.1.2",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.3.1",
|
||||||
"electron": "^25.2.0",
|
"electron": "^27.1.2",
|
||||||
"electron-builder": "^23.6.0",
|
"electron-builder": "^24.9.1",
|
||||||
"esbuild": "^0.17.14",
|
"esbuild": "^0.19.8",
|
||||||
"eslint": "^8.38.0",
|
"eslint": "^8.54.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-import-resolver-alias": "^1.1.2",
|
"eslint-import-resolver-alias": "^1.1.2",
|
||||||
"eslint-plugin-license-header": "^0.6.0",
|
"eslint-plugin-license-header": "^0.6.0",
|
||||||
"eslint-plugin-path-alias": "^1.0.0",
|
"eslint-plugin-path-alias": "^1.0.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.0.1",
|
||||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||||
"eslint-plugin-unused-imports": "^2.0.0",
|
"eslint-plugin-unused-imports": "^3.0.0",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^3.1.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"tsx": "^3.12.6",
|
"tsx": "^4.6.0",
|
||||||
"type-fest": "^3.8.0",
|
"type-fest": "^4.8.2",
|
||||||
"typescript": "^5.0.2"
|
"typescript": "^5.3.2",
|
||||||
|
"xml-formatter": "^3.6.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.1.1",
|
"packageManager": "pnpm@8.11.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18",
|
"node": ">=18",
|
||||||
"pnpm": ">=8"
|
"pnpm": ">=8"
|
||||||
@@ -65,21 +70,48 @@
|
|||||||
"package.json",
|
"package.json",
|
||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
|
"beforePack": "scripts/build/sandboxFix.js",
|
||||||
"linux": {
|
"linux": {
|
||||||
|
"icon": "build/icon.icns",
|
||||||
"category": "Network",
|
"category": "Network",
|
||||||
"maintainer": "vendicated+vesktop@riseup.net",
|
"maintainer": "vendicated+vesktop@riseup.net",
|
||||||
"target": [
|
"target": [
|
||||||
"deb",
|
{
|
||||||
"tar.gz",
|
"target": "deb",
|
||||||
"rpm",
|
"arch": [
|
||||||
"AppImage"
|
"x64",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "tar.gz",
|
||||||
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "rpm",
|
||||||
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "AppImage",
|
||||||
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"desktop": {
|
"desktop": {
|
||||||
"Name": "Vesktop",
|
"Name": "Vesktop",
|
||||||
"GenericName": "Internet Messenger",
|
"GenericName": "Internet Messenger",
|
||||||
"Type": "Application",
|
"Type": "Application",
|
||||||
"Categories": "Network;InstantMessaging;Chat;",
|
"Categories": "Network;InstantMessaging;Chat;",
|
||||||
"Keywords": "discord;vencord;electron;chat;"
|
"Keywords": "discord;vencord;electron;chat;",
|
||||||
|
"StartupWMClass": "VencordDesktop"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
@@ -92,7 +124,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": {
|
"nsis": {
|
||||||
"include": "build/installer.nsh",
|
"include": "build/installer.nsh",
|
||||||
@@ -101,12 +139,11 @@
|
|||||||
"win": {
|
"win": {
|
||||||
"target": [
|
"target": [
|
||||||
"nsis",
|
"nsis",
|
||||||
"portable"
|
"zip"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"publish": {
|
"publish": {
|
||||||
"provider": "github",
|
"provider": "github"
|
||||||
"releaseType": "release"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2326
pnpm-lock.yaml
generated
2326
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { BuildContext, BuildOptions, context } from "esbuild";
|
import { BuildContext, BuildOptions, context } from "esbuild";
|
||||||
|
import { copyFile } from "fs/promises";
|
||||||
|
|
||||||
import vencordDep from "./vencordDep.mjs";
|
import vencordDep from "./vencordDep.mjs";
|
||||||
|
|
||||||
@@ -33,7 +34,23 @@ async function createContext(options: BuildOptions) {
|
|||||||
contexts.push(await context(options));
|
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([
|
await Promise.all([
|
||||||
|
copyVenmic(),
|
||||||
createContext({
|
createContext({
|
||||||
...NodeCommonOpts,
|
...NodeCommonOpts,
|
||||||
entryPoints: ["src/main/index.ts"],
|
entryPoints: ["src/main/index.ts"],
|
||||||
@@ -61,15 +78,9 @@ await Promise.all([
|
|||||||
inject: ["./scripts/build/injectReact.mjs"],
|
inject: ["./scripts/build/injectReact.mjs"],
|
||||||
jsxFactory: "VencordCreateElement",
|
jsxFactory: "VencordCreateElement",
|
||||||
jsxFragment: "VencordFragment",
|
jsxFragment: "VencordFragment",
|
||||||
// Work around https://github.com/evanw/esbuild/issues/2460
|
|
||||||
tsconfig: "./scripts/build/tsconfig.esbuild.json",
|
|
||||||
external: ["@vencord/types/*"],
|
external: ["@vencord/types/*"],
|
||||||
plugins: [vencordDep],
|
plugins: [vencordDep],
|
||||||
// TODO: remove legacy name once main Vencord codebase has migrated and some time has passed.
|
footer: { js: "//# sourceURL=VCDRenderer" }
|
||||||
// this 0 is very important. we run this script via webFrame.executeJavaScript and the last
|
|
||||||
// expression will be the return value. Without the 0, the return value would be Vesktop which
|
|
||||||
// leads to "An object could not be cloned"
|
|
||||||
footer: { js: ";window.VencordDesktop=Vesktop;0 \n//# sourceURL=VCDRenderer" }
|
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,4 +8,4 @@ import "./utils/dotenv";
|
|||||||
|
|
||||||
import { spawnNodeModuleBin } from "./utils/spawn.mjs";
|
import { spawnNodeModuleBin } from "./utils/spawn.mjs";
|
||||||
|
|
||||||
spawnNodeModuleBin("electron", ["."]);
|
spawnNodeModuleBin("electron", [".", ...(process.env.ELECTRON_LAUNCH_FLAGS?.split(" ") ?? [])]);
|
||||||
|
|||||||
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");
|
||||||
@@ -23,11 +23,17 @@ let lastIndex: null | number = -1;
|
|||||||
|
|
||||||
export function setBadgeCount(count: number) {
|
export function setBadgeCount(count: number) {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case "darwin":
|
|
||||||
case "linux":
|
case "linux":
|
||||||
if (count === -1) count = 0;
|
if (count === -1) count = 0;
|
||||||
app.setBadgeCount(count);
|
app.setBadgeCount(count);
|
||||||
break;
|
break;
|
||||||
|
case "darwin":
|
||||||
|
if (count === 0) {
|
||||||
|
app.dock.setBadge("");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
app.dock.setBadge(count === -1 ? "•" : count.toString());
|
||||||
|
break;
|
||||||
case "win32":
|
case "win32":
|
||||||
const [index, description] = getBadgeIndexAndDescription(count);
|
const [index, description] = getBadgeIndexAndDescription(count);
|
||||||
if (lastIndex === index) break;
|
if (lastIndex === index) break;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Server from "arrpc";
|
import Server from "arrpc";
|
||||||
|
import { IpcEvents } from "shared/IpcEvents";
|
||||||
|
|
||||||
import { mainWin } from "./mainWindow";
|
import { mainWin } from "./mainWindow";
|
||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
@@ -17,11 +18,8 @@ export async function initArRPC() {
|
|||||||
if (server || !Settings.store.arRPC) return;
|
if (server || !Settings.store.arRPC) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// This module starts a server as a side effect, so it needs to be lazy imported
|
|
||||||
const { send: sendToBridge } = await import("arrpc/src/bridge");
|
|
||||||
|
|
||||||
server = await new Server();
|
server = await new Server();
|
||||||
server.on("activity", sendToBridge);
|
server.on("activity", (data: any) => mainWin.webContents.send(IpcEvents.ARRPC_ACTIVITY, JSON.stringify(data)));
|
||||||
server.on("invite", (invite: string, callback: (valid: boolean) => void) => {
|
server.on("invite", (invite: string, callback: (valid: boolean) => void) => {
|
||||||
invite = String(invite);
|
invite = String(invite);
|
||||||
if (!inviteCodeRegex.test(invite)) return callback(false);
|
if (!inviteCodeRegex.test(invite)) return callback(false);
|
||||||
|
|||||||
@@ -10,38 +10,51 @@ import { join } from "path";
|
|||||||
|
|
||||||
interface AutoStart {
|
interface AutoStart {
|
||||||
isEnabled(): boolean;
|
isEnabled(): boolean;
|
||||||
|
wasAutoStarted(): boolean;
|
||||||
enable(): void;
|
enable(): void;
|
||||||
disable(): void;
|
disable(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFlatpak = process.env.FLATPAK_ID !== undefined;
|
||||||
|
|
||||||
function makeAutoStartLinux(): AutoStart {
|
function makeAutoStartLinux(): AutoStart {
|
||||||
const configDir = process.env.XDG_CONFIG_HOME || join(process.env.HOME!, ".config");
|
const configDir = process.env.XDG_CONFIG_HOME || join(process.env.HOME!, ".config");
|
||||||
const dir = join(configDir, "autostart");
|
const dir = join(configDir, "autostart");
|
||||||
const file = join(dir, "vencord.desktop");
|
const file = join(dir, "vencord.desktop");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isEnabled: () => existsSync(file),
|
isEnabled: () => existsSync(file), // TODO: flatpak
|
||||||
|
wasAutoStarted: () => process.argv.includes("--autostart"),
|
||||||
enable() {
|
enable() {
|
||||||
const desktopFile = `
|
if (isFlatpak) {
|
||||||
|
} else {
|
||||||
|
const desktopFile = `
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Version=1.0
|
Version=1.0
|
||||||
Name=Vencord
|
Name=Vencord
|
||||||
Comment=Vencord autostart script
|
Comment=Vencord autostart script
|
||||||
Exec=${process.execPath}
|
Exec=${process.execPath} --autostart
|
||||||
Terminal=false
|
Terminal=false
|
||||||
StartupNotify=false
|
StartupNotify=false
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
mkdirSync(dir, { recursive: true });
|
mkdirSync(dir, { recursive: true });
|
||||||
writeFileSync(file, desktopFile);
|
writeFileSync(file, desktopFile);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
disable: () => rmSync(file, { force: true })
|
disable: () => {
|
||||||
|
if (isFlatpak) {
|
||||||
|
} else {
|
||||||
|
rmSync(file, { force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const autoStartWindowsMac: AutoStart = {
|
const autoStartWindowsMac: AutoStart = {
|
||||||
isEnabled: () => app.getLoginItemSettings().openAtLogin,
|
isEnabled: () => app.getLoginItemSettings().openAtLogin,
|
||||||
|
wasAutoStarted: () => app.getLoginItemSettings().wasOpenedAtLogin,
|
||||||
enable: () => app.setLoginItemSettings({ openAtLogin: true }),
|
enable: () => app.setLoginItemSettings({ openAtLogin: true }),
|
||||||
disable: () => app.setLoginItemSettings({ openAtLogin: false })
|
disable: () => app.setLoginItemSettings({ openAtLogin: false })
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("u
|
|||||||
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
||||||
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
|
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
|
||||||
export const VENCORD_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json");
|
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
|
// needs to be inline require because of circular dependency
|
||||||
// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised
|
// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised
|
||||||
@@ -24,3 +25,17 @@ export const MIN_WIDTH = 940;
|
|||||||
export const MIN_HEIGHT = 500;
|
export const MIN_HEIGHT = 500;
|
||||||
export const DEFAULT_WIDTH = 1280;
|
export const DEFAULT_WIDTH = 1280;
|
||||||
export const DEFAULT_HEIGHT = 720;
|
export const DEFAULT_HEIGHT = 720;
|
||||||
|
|
||||||
|
const UserAgents = {
|
||||||
|
darwin: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
|
||||||
|
linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
|
||||||
|
windows:
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UserAgent = UserAgents[process.platform] || UserAgents.windows;
|
||||||
|
|
||||||
|
export const enum MessageBoxChoice {
|
||||||
|
Default,
|
||||||
|
Cancel
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { BrowserWindow } from "electron/main";
|
|||||||
import { copyFileSync, mkdirSync, readdirSync } from "fs";
|
import { copyFileSync, mkdirSync, readdirSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { SplashProps } from "shared/browserWinProperties";
|
import { SplashProps } from "shared/browserWinProperties";
|
||||||
import { VIEW_DIR } from "shared/paths";
|
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||||
|
|
||||||
import { autoStart } from "./autoStart";
|
import { autoStart } from "./autoStart";
|
||||||
import { DATA_DIR } from "./constants";
|
import { DATA_DIR } from "./constants";
|
||||||
@@ -31,7 +31,8 @@ export function createFirstLaunchTour() {
|
|||||||
frame: true,
|
frame: true,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
height: 470,
|
height: 470,
|
||||||
width: 550
|
width: 550,
|
||||||
|
icon: ICON_PATH
|
||||||
});
|
});
|
||||||
|
|
||||||
makeLinksOpenExternally(win);
|
makeLinksOpenExternally(win);
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import "./ipc";
|
|||||||
import { app, BrowserWindow } from "electron";
|
import { app, BrowserWindow } from "electron";
|
||||||
import { checkUpdates } from "updater/main";
|
import { checkUpdates } from "updater/main";
|
||||||
|
|
||||||
import { ICON_PATH } from "../shared/paths";
|
|
||||||
import { DATA_DIR } from "./constants";
|
import { DATA_DIR } from "./constants";
|
||||||
import { createFirstLaunchTour } from "./firstLaunch";
|
import { createFirstLaunchTour } from "./firstLaunch";
|
||||||
import { createWindows, mainWin } from "./mainWindow";
|
import { createWindows, mainWin } from "./mainWindow";
|
||||||
|
import { registerMediaPermissionsHandler } from "./mediaPermissions";
|
||||||
import { registerScreenShareHandler } from "./screenShare";
|
import { registerScreenShareHandler } from "./screenShare";
|
||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
|
|
||||||
@@ -24,6 +24,12 @@ if (IS_DEV) {
|
|||||||
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
|
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
const { disableSmoothScroll } = Settings.store;
|
||||||
|
|
||||||
|
if (disableSmoothScroll) {
|
||||||
|
app.commandLine.appendSwitch("disable-smooth-scrolling");
|
||||||
|
}
|
||||||
|
|
||||||
// work around chrome 66 disabling autoplay by default
|
// work around chrome 66 disabling autoplay by default
|
||||||
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
|
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
|
||||||
|
|
||||||
@@ -48,9 +54,10 @@ function init() {
|
|||||||
app.whenReady().then(async () => {
|
app.whenReady().then(async () => {
|
||||||
checkUpdates();
|
checkUpdates();
|
||||||
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.desktop");
|
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.desktop");
|
||||||
else if (process.platform === "darwin") app.dock.setIcon(ICON_PATH);
|
|
||||||
|
|
||||||
registerScreenShareHandler();
|
registerScreenShareHandler();
|
||||||
|
registerMediaPermissionsHandler();
|
||||||
|
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|
||||||
app.on("activate", () => {
|
app.on("activate", () => {
|
||||||
|
|||||||
111
src/main/ipc.ts
111
src/main/ipc.ts
@@ -4,8 +4,11 @@
|
|||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app, dialog, ipcMain, session, shell } from "electron";
|
if (process.platform === "linux") import("./virtmic");
|
||||||
import { existsSync, readFileSync, watch } from "fs";
|
|
||||||
|
import { execFile } from "child_process";
|
||||||
|
import { app, BrowserWindow, dialog, RelaunchOptions, session, shell } from "electron";
|
||||||
|
import { mkdirSync, readFileSync, watch } from "fs";
|
||||||
import { open, readFile } from "fs/promises";
|
import { open, readFile } from "fs/promises";
|
||||||
import { release } from "os";
|
import { release } from "os";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
@@ -14,69 +17,76 @@ import { debounce } from "shared/utils/debounce";
|
|||||||
import { IpcEvents } from "../shared/IpcEvents";
|
import { IpcEvents } from "../shared/IpcEvents";
|
||||||
import { setBadgeCount } from "./appBadge";
|
import { setBadgeCount } from "./appBadge";
|
||||||
import { autoStart } from "./autoStart";
|
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 { mainWin } from "./mainWindow";
|
||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
|
import { handle, handleSync } from "./utils/ipcWrappers";
|
||||||
|
import { isValidVencordInstall } from "./utils/vencordLoader";
|
||||||
|
|
||||||
ipcMain.on(IpcEvents.GET_VENCORD_PRELOAD_FILE, e => {
|
handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js"));
|
||||||
e.returnValue = join(VENCORD_FILES_DIR, "preload.js");
|
handleSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, () =>
|
||||||
});
|
readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8")
|
||||||
|
);
|
||||||
|
|
||||||
ipcMain.on(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, e => {
|
handleSync(IpcEvents.GET_RENDERER_SCRIPT, () => readFileSync(join(__dirname, "renderer.js"), "utf-8"));
|
||||||
e.returnValue = readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8");
|
handleSync(IpcEvents.GET_RENDERER_CSS_FILE, () => join(__dirname, "renderer.css"));
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on(IpcEvents.GET_RENDERER_SCRIPT, e => {
|
handleSync(IpcEvents.GET_SETTINGS, () => Settings.plain);
|
||||||
e.returnValue = readFileSync(join(__dirname, "renderer.js"), "utf-8");
|
handleSync(IpcEvents.GET_VERSION, () => app.getVersion());
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on(IpcEvents.GET_RENDERER_CSS_FILE, e => {
|
handleSync(
|
||||||
e.returnValue = join(__dirname, "renderer.css");
|
IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY,
|
||||||
});
|
() => process.platform === "win32" && Number(release().split(".").pop()) >= 22621
|
||||||
|
);
|
||||||
|
|
||||||
ipcMain.on(IpcEvents.GET_SETTINGS, e => {
|
handleSync(IpcEvents.AUTOSTART_ENABLED, () => autoStart.isEnabled());
|
||||||
e.returnValue = Settings.plain;
|
handle(IpcEvents.ENABLE_AUTOSTART, autoStart.enable);
|
||||||
});
|
handle(IpcEvents.DISABLE_AUTOSTART, autoStart.disable);
|
||||||
|
|
||||||
ipcMain.on(IpcEvents.GET_VERSION, e => {
|
handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: string) => {
|
||||||
e.returnValue = app.getVersion();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY, e => {
|
|
||||||
e.returnValue = process.platform === "win32" && Number(release().split(".").pop()) >= 22621;
|
|
||||||
});
|
|
||||||
|
|
||||||
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) => {
|
|
||||||
Settings.setData(settings, path);
|
Settings.setData(settings, path);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.RELAUNCH, () => {
|
handle(IpcEvents.RELAUNCH, () => {
|
||||||
app.relaunch();
|
const options: RelaunchOptions = {
|
||||||
|
args: process.argv.slice(1).concat(["--relaunch"])
|
||||||
|
};
|
||||||
|
if (app.isPackaged && process.env.APPIMAGE) {
|
||||||
|
execFile(process.env.APPIMAGE, options.args);
|
||||||
|
} else {
|
||||||
|
app.relaunch(options);
|
||||||
|
}
|
||||||
app.exit();
|
app.exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.SHOW_ITEM_IN_FOLDER, (_, path) => {
|
handle(IpcEvents.SHOW_ITEM_IN_FOLDER, (_, path) => {
|
||||||
shell.showItemInFolder(path);
|
shell.showItemInFolder(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.FOCUS, () => {
|
handle(IpcEvents.FOCUS, () => {
|
||||||
if (process.platform === "win32") mainWin.minimize(); // Windows is weird
|
if (process.platform === "win32") mainWin.minimize(); // Windows is weird
|
||||||
|
|
||||||
mainWin.restore();
|
mainWin.restore();
|
||||||
mainWin.show();
|
mainWin.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.CLOSE, e => {
|
handle(IpcEvents.CLOSE, e => {
|
||||||
e.sender.close();
|
(BrowserWindow.fromWebContents(e.sender) ?? e.sender).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 ses = session.defaultSession;
|
||||||
|
|
||||||
const available = ses.availableSpellCheckerLanguages;
|
const available = ses.availableSpellCheckerLanguages;
|
||||||
@@ -84,29 +94,27 @@ ipcMain.handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => {
|
|||||||
if (applicable.length) ses.setSpellCheckerLanguages(applicable);
|
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);
|
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);
|
e.sender.session.addWordToSpellCheckerDictionary(word);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
||||||
const res = await dialog.showOpenDialog(mainWin!, {
|
const res = await dialog.showOpenDialog(mainWin!, {
|
||||||
properties: ["openDirectory"]
|
properties: ["openDirectory"]
|
||||||
});
|
});
|
||||||
if (!res.filePaths.length) return "cancelled";
|
if (!res.filePaths.length) return "cancelled";
|
||||||
|
|
||||||
const dir = res.filePaths[0];
|
const dir = res.filePaths[0];
|
||||||
for (const file of ["vencordDesktopMain.js", "preload.js", "vencordDesktopRenderer.js", "renderer.css"]) {
|
if (!isValidVencordInstall(dir)) return "invalid";
|
||||||
if (!existsSync(join(dir, file))) return "invalid";
|
|
||||||
}
|
|
||||||
|
|
||||||
return dir;
|
return dir;
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||||
|
|
||||||
function readCss() {
|
function readCss() {
|
||||||
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
||||||
@@ -122,3 +130,12 @@ open(VENCORD_QUICKCSS_FILE, "a+").then(fd => {
|
|||||||
}, 50)
|
}, 50)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mkdirSync(VENCORD_THEMES_DIR, { recursive: true });
|
||||||
|
watch(
|
||||||
|
VENCORD_THEMES_DIR,
|
||||||
|
{ persistent: false },
|
||||||
|
debounce(() => {
|
||||||
|
mainWin?.webContents.postMessage("VencordThemeUpdate", void 0);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
dialog,
|
dialog,
|
||||||
Menu,
|
Menu,
|
||||||
MenuItemConstructorOptions,
|
MenuItemConstructorOptions,
|
||||||
|
nativeTheme,
|
||||||
Tray
|
Tray
|
||||||
} from "electron";
|
} from "electron";
|
||||||
import { rm } from "fs/promises";
|
import { rm } from "fs/promises";
|
||||||
@@ -23,15 +24,28 @@ import type { SettingsStore } from "shared/utils/SettingsStore";
|
|||||||
import { ICON_PATH } from "../shared/paths";
|
import { ICON_PATH } from "../shared/paths";
|
||||||
import { createAboutWindow } from "./about";
|
import { createAboutWindow } from "./about";
|
||||||
import { initArRPC } from "./arrpc";
|
import { initArRPC } from "./arrpc";
|
||||||
import { DATA_DIR, DEFAULT_HEIGHT, DEFAULT_WIDTH, MIN_HEIGHT, MIN_WIDTH, VENCORD_FILES_DIR } from "./constants";
|
import { autoStart } from "./autoStart";
|
||||||
|
import {
|
||||||
|
DATA_DIR,
|
||||||
|
DEFAULT_HEIGHT,
|
||||||
|
DEFAULT_WIDTH,
|
||||||
|
MessageBoxChoice,
|
||||||
|
MIN_HEIGHT,
|
||||||
|
MIN_WIDTH,
|
||||||
|
UserAgent,
|
||||||
|
VENCORD_FILES_DIR
|
||||||
|
} from "./constants";
|
||||||
import { Settings, VencordSettings } from "./settings";
|
import { Settings, VencordSettings } from "./settings";
|
||||||
import { createSplashWindow } from "./splash";
|
import { createSplashWindow } from "./splash";
|
||||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||||
|
import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS";
|
||||||
import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader";
|
import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader";
|
||||||
|
|
||||||
let isQuitting = false;
|
let isQuitting = false;
|
||||||
let tray: Tray;
|
let tray: Tray;
|
||||||
|
|
||||||
|
applyDeckKeyboardFix();
|
||||||
|
|
||||||
app.on("before-quit", () => {
|
app.on("before-quit", () => {
|
||||||
isQuitting = true;
|
isQuitting = true;
|
||||||
});
|
});
|
||||||
@@ -119,11 +133,6 @@ function initTray(win: BrowserWindow) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const enum MessageBoxChoice {
|
|
||||||
Default,
|
|
||||||
Cancel
|
|
||||||
}
|
|
||||||
|
|
||||||
async function clearData(win: BrowserWindow) {
|
async function clearData(win: BrowserWindow) {
|
||||||
const { response } = await dialog.showMessageBox(win, {
|
const { response } = await dialog.showMessageBox(win, {
|
||||||
message: "Are you sure you want to reset Vesktop?",
|
message: "Are you sure you want to reset Vesktop?",
|
||||||
@@ -147,6 +156,8 @@ async function clearData(win: BrowserWindow) {
|
|||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MenuItemList = Array<MenuItemConstructorOptions | false>;
|
||||||
|
|
||||||
function initMenuBar(win: BrowserWindow) {
|
function initMenuBar(win: BrowserWindow) {
|
||||||
const isWindows = process.platform === "win32";
|
const isWindows = process.platform === "win32";
|
||||||
const isDarwin = process.platform === "darwin";
|
const isDarwin = process.platform === "darwin";
|
||||||
@@ -181,14 +192,38 @@ function initMenuBar(win: BrowserWindow) {
|
|||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isDarwin && {
|
...(!isDarwin
|
||||||
label: "Hide",
|
? []
|
||||||
role: "hide"
|
: ([
|
||||||
},
|
{
|
||||||
isDarwin && {
|
type: "separator"
|
||||||
label: "Hide others",
|
},
|
||||||
role: "hideOthers"
|
{
|
||||||
},
|
label: "Settings",
|
||||||
|
accelerator: "CmdOrCtrl+,",
|
||||||
|
async click() {
|
||||||
|
mainWin.webContents.executeJavaScript(
|
||||||
|
"Vencord.Webpack.Common.SettingsRouter.open('My Account')"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Hide Vesktop", // Should probably remove the label, but it says "Hide VencordDesktop" instead of "Hide Vesktop"
|
||||||
|
role: "hide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "hideOthers"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "unhide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator"
|
||||||
|
}
|
||||||
|
] satisfies MenuItemList)),
|
||||||
{
|
{
|
||||||
label: "Quit",
|
label: "Quit",
|
||||||
accelerator: wantCtrlQ ? "CmdOrCtrl+Q" : void 0,
|
accelerator: wantCtrlQ ? "CmdOrCtrl+Q" : void 0,
|
||||||
@@ -213,7 +248,7 @@ function initMenuBar(win: BrowserWindow) {
|
|||||||
role: "zoomIn",
|
role: "zoomIn",
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
] satisfies Array<MenuItemConstructorOptions | false>;
|
] satisfies MenuItemList;
|
||||||
|
|
||||||
const menu = Menu.buildFromTemplate([
|
const menu = Menu.buildFromTemplate([
|
||||||
{
|
{
|
||||||
@@ -231,6 +266,9 @@ function initMenuBar(win: BrowserWindow) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
|
function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
|
||||||
|
// 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 } = Settings.store.windowBounds ?? {};
|
const { x, y, width, height } = Settings.store.windowBounds ?? {};
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
@@ -251,6 +289,29 @@ function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
|
|||||||
return options;
|
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) {
|
function initWindowBoundsListeners(win: BrowserWindow) {
|
||||||
const saveState = () => {
|
const saveState = () => {
|
||||||
Settings.store.maximized = win.isMaximized();
|
Settings.store.maximized = win.isMaximized();
|
||||||
@@ -298,6 +359,10 @@ function initSettingsListeners(win: BrowserWindow) {
|
|||||||
win.setBackgroundColor("#ffffff");
|
win.setBackgroundColor("#ffffff");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addSettingsListener("enableMenu", enabled => {
|
||||||
|
win.setAutoHideMenuBar(enabled ?? false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function initSpellCheck(win: BrowserWindow) {
|
function initSpellCheck(win: BrowserWindow) {
|
||||||
@@ -311,8 +376,12 @@ function createMainWindow() {
|
|||||||
removeSettingsListeners();
|
removeSettingsListeners();
|
||||||
removeVencordSettingsListeners();
|
removeVencordSettingsListeners();
|
||||||
|
|
||||||
const { staticTitle, transparencyOption } = Settings.store;
|
const { staticTitle, transparencyOption, enableMenu, discordWindowsTitleBar } = Settings.store;
|
||||||
const { frameless, macosTranslucency } = VencordSettings.store;
|
|
||||||
|
const { frameless } = VencordSettings.store;
|
||||||
|
|
||||||
|
const noFrame = frameless === true || (process.platform === "win32" && discordWindowsTitleBar === true);
|
||||||
|
|
||||||
const win = (mainWin = new BrowserWindow({
|
const win = (mainWin = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
@@ -324,28 +393,22 @@ function createMainWindow() {
|
|||||||
spellcheck: true
|
spellcheck: true
|
||||||
},
|
},
|
||||||
icon: ICON_PATH,
|
icon: ICON_PATH,
|
||||||
frame: frameless !== true,
|
frame: !noFrame,
|
||||||
...(transparencyOption && transparencyOption !== "none"
|
...(transparencyOption &&
|
||||||
? {
|
transparencyOption !== "none" && {
|
||||||
backgroundColor: "#00000000",
|
backgroundColor: "#00000000",
|
||||||
backgroundMaterial: Settings.store.transparencyOption,
|
backgroundMaterial: transparencyOption,
|
||||||
transparent: true
|
transparent: true
|
||||||
}
|
}),
|
||||||
: {}),
|
...(staticTitle && { title: "Vesktop" }),
|
||||||
...(staticTitle ? { title: "Vencord" } : {}),
|
...(process.platform === "darwin" && getDarwinOptions()),
|
||||||
...(macosTranslucency
|
...getWindowBoundsOptions(),
|
||||||
? {
|
autoHideMenuBar: enableMenu
|
||||||
vibrancy: "sidebar",
|
|
||||||
backgroundColor: "#ffffff00"
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(process.platform === "darwin" ? { titleBarStyle: "hiddenInset" } : {}),
|
|
||||||
...getWindowBoundsOptions()
|
|
||||||
}));
|
}));
|
||||||
win.setMenuBarVisibility(false);
|
win.setMenuBarVisibility(false);
|
||||||
|
|
||||||
win.on("close", e => {
|
win.on("close", e => {
|
||||||
const useTray = Settings.store.minimizeToTray && Settings.store.tray;
|
const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false;
|
||||||
if (isQuitting || (process.platform !== "darwin" && !useTray)) return;
|
if (isQuitting || (process.platform !== "darwin" && !useTray)) return;
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -359,15 +422,13 @@ function createMainWindow() {
|
|||||||
if (Settings.store.staticTitle) win.on("page-title-updated", e => e.preventDefault());
|
if (Settings.store.staticTitle) win.on("page-title-updated", e => e.preventDefault());
|
||||||
|
|
||||||
initWindowBoundsListeners(win);
|
initWindowBoundsListeners(win);
|
||||||
if (Settings.store.tray ?? true) initTray(win);
|
if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win);
|
||||||
initMenuBar(win);
|
initMenuBar(win);
|
||||||
makeLinksOpenExternally(win);
|
makeLinksOpenExternally(win);
|
||||||
initSettingsListeners(win);
|
initSettingsListeners(win);
|
||||||
initSpellCheck(win);
|
initSpellCheck(win);
|
||||||
|
|
||||||
win.webContents.setUserAgent(
|
win.webContents.setUserAgent(UserAgent);
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
|
|
||||||
);
|
|
||||||
|
|
||||||
const subdomain =
|
const subdomain =
|
||||||
Settings.store.discordBranch === "canary" || Settings.store.discordBranch === "ptb"
|
Settings.store.discordBranch === "canary" || Settings.store.discordBranch === "ptb"
|
||||||
@@ -382,18 +443,30 @@ function createMainWindow() {
|
|||||||
const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDesktopMain.js")));
|
const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDesktopMain.js")));
|
||||||
|
|
||||||
export async function createWindows() {
|
export async function createWindows() {
|
||||||
const splash = createSplashWindow();
|
const shouldStartMinimized = Settings.store.startMinimized && autoStart.wasAutoStarted();
|
||||||
|
const splash = createSplashWindow(shouldStartMinimized);
|
||||||
|
// SteamOS letterboxes and scales it terribly, so just full screen it
|
||||||
|
if (isDeckGameMode) splash.setFullScreen(true);
|
||||||
await ensureVencordFiles();
|
await ensureVencordFiles();
|
||||||
runVencordMain();
|
runVencordMain();
|
||||||
|
|
||||||
mainWin = createMainWindow();
|
mainWin = createMainWindow();
|
||||||
|
|
||||||
mainWin.once("ready-to-show", () => {
|
mainWin.webContents.on("did-finish-load", () => {
|
||||||
splash.destroy();
|
splash.destroy();
|
||||||
mainWin!.show();
|
|
||||||
|
|
||||||
if (Settings.store.maximized) {
|
if (!shouldStartMinimized || isDeckGameMode) mainWin!.show();
|
||||||
|
|
||||||
|
if (isDeckGameMode) {
|
||||||
|
// always use entire display
|
||||||
|
mainWin!.setFullScreen(true);
|
||||||
|
|
||||||
|
askToApplySteamLayout(mainWin);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWin.once("show", () => {
|
||||||
|
if (Settings.store.maximized && !mainWin!.isMaximized() && !isDeckGameMode) {
|
||||||
mainWin!.maximize();
|
mainWin!.maximize();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -4,12 +4,17 @@
|
|||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* 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 type { StreamPick } from "renderer/components/ScreenSharePicker";
|
||||||
import { IpcEvents } from "shared/IpcEvents";
|
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() {
|
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({
|
const sources = await desktopCapturer.getSources({
|
||||||
types: ["window", "screen"],
|
types: ["window", "screen"],
|
||||||
thumbnailSize: {
|
thumbnailSize: {
|
||||||
@@ -21,13 +26,19 @@ export function registerScreenShareHandler() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => {
|
session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => {
|
||||||
const sources = await desktopCapturer.getSources({
|
// request full resolution on wayland right away because we always only end up with one result anyway
|
||||||
types: ["window", "screen"],
|
const width = isWayland ? 1920 : 176;
|
||||||
thumbnailSize: {
|
const sources = await desktopCapturer
|
||||||
width: 176,
|
.getSources({
|
||||||
height: 99
|
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 }) => ({
|
const data = sources.map(({ id, name, thumbnail }) => ({
|
||||||
id,
|
id,
|
||||||
@@ -35,10 +46,28 @@ export function registerScreenShareHandler() {
|
|||||||
url: thumbnail.toDataURL()
|
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
|
const choice = await request.frame
|
||||||
.executeJavaScript(`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`)
|
.executeJavaScript(`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`)
|
||||||
.then(e => e as StreamPick)
|
.then(e => e as StreamPick)
|
||||||
.catch(() => null);
|
.catch(e => {
|
||||||
|
console.error("Error during screenshare picker", e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
if (!choice) return callback({});
|
if (!choice) return callback({});
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,33 @@
|
|||||||
import { BrowserWindow } from "electron";
|
import { BrowserWindow } from "electron";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { SplashProps } from "shared/browserWinProperties";
|
import { SplashProps } from "shared/browserWinProperties";
|
||||||
import { VIEW_DIR } from "shared/paths";
|
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||||
|
|
||||||
export function createSplashWindow() {
|
import { Settings } from "./settings";
|
||||||
const splash = new BrowserWindow(SplashProps);
|
|
||||||
|
export function createSplashWindow(startMinimized = false) {
|
||||||
|
const splash = new BrowserWindow({
|
||||||
|
...SplashProps,
|
||||||
|
icon: ICON_PATH,
|
||||||
|
show: !startMinimized
|
||||||
|
});
|
||||||
|
|
||||||
splash.loadFile(join(VIEW_DIR, "splash.html"));
|
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;
|
return splash;
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/main/utils/ipcWrappers.ts
Normal file
36
src/main/utils/ipcWrappers.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0
|
||||||
|
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||||
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ipcMain, IpcMainEvent, IpcMainInvokeEvent, WebFrameMain } from "electron";
|
||||||
|
import { IpcEvents } from "shared/IpcEvents";
|
||||||
|
|
||||||
|
export function validateSender(frame: WebFrameMain) {
|
||||||
|
const { hostname, protocol } = new URL(frame.url);
|
||||||
|
if (protocol === "file:") return;
|
||||||
|
|
||||||
|
switch (hostname) {
|
||||||
|
case "discord.com":
|
||||||
|
case "ptb.discord.com":
|
||||||
|
case "canary.discord.com":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
84
src/main/utils/steamOS.ts
Normal file
84
src/main/utils/steamOS.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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 { exec as callbackExec } from "child_process";
|
||||||
|
import { BrowserWindow, dialog } from "electron";
|
||||||
|
import { sleep } from "shared/utils/sleep";
|
||||||
|
import { promisify } from "util";
|
||||||
|
|
||||||
|
import { MessageBoxChoice } from "../constants";
|
||||||
|
import { Settings } from "../settings";
|
||||||
|
|
||||||
|
const exec = promisify(callbackExec);
|
||||||
|
|
||||||
|
// 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]*$/;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function execSteamURL(url: string): Promise<void> {
|
||||||
|
await exec(`steam -ifrunning ${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showLayout(appId: string) {
|
||||||
|
await execSteamURL(`steam://controllerconfig/${appId}/${layoutId}`);
|
||||||
|
// because the UI doesn't consistently reload after the data for the config has loaded...
|
||||||
|
// HOW HAS NOBODY AT VALVE RUN INTO THIS YET
|
||||||
|
await sleep(100);
|
||||||
|
await execSteamURL(`steam://controllerconfig/${appId}/${layoutId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function askToApplySteamLayout(win: BrowserWindow) {
|
||||||
|
const appId = getAppId();
|
||||||
|
if (!appId) return;
|
||||||
|
if (Settings.store.steamOSLayoutVersion === layoutVersion) return;
|
||||||
|
const update = Boolean(Settings.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 (Settings.store.steamOSLayoutVersion !== layoutVersion) {
|
||||||
|
Settings.store.steamOSLayoutVersion = layoutVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response === MessageBoxChoice.Cancel) return;
|
||||||
|
|
||||||
|
await showLayout(appId);
|
||||||
|
}
|
||||||
@@ -13,7 +13,12 @@ import { downloadFile, simpleGet } from "./http";
|
|||||||
|
|
||||||
const API_BASE = "https://api.github.com";
|
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 {
|
export interface ReleaseData {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -50,8 +55,12 @@ export async function downloadVencordFiles() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidVencordInstall(dir: string) {
|
||||||
|
return FILES_TO_DOWNLOAD.every(f => existsSync(join(dir, f)));
|
||||||
|
}
|
||||||
|
|
||||||
export async function ensureVencordFiles() {
|
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 });
|
mkdirSync(VENCORD_FILES_DIR, { recursive: true });
|
||||||
|
|
||||||
await downloadVencordFiles();
|
await downloadVencordFiles();
|
||||||
|
|||||||
77
src/main/virtmic.ts
Normal file
77
src/main/virtmic.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0
|
||||||
|
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||||
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { app, ipcMain } from "electron";
|
||||||
|
import { join } from "path";
|
||||||
|
import { IpcEvents } from "shared/IpcEvents";
|
||||||
|
import { STATIC_DIR } from "shared/paths";
|
||||||
|
|
||||||
|
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[]) =>
|
||||||
|
obtainVenmic()?.link({
|
||||||
|
props: targets.map(target => ({ key: "application.name", value: target })),
|
||||||
|
mode: "include"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
ipcMain.handle(
|
||||||
|
IpcEvents.VIRT_MIC_START_SYSTEM,
|
||||||
|
() =>
|
||||||
|
obtainVenmic()?.link({
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
key: "application.process.id",
|
||||||
|
value: getRendererAudioServicePid()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
mode: "exclude"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
ipcMain.handle(IpcEvents.VIRT_MIC_STOP, () => obtainVenmic()?.unlink());
|
||||||
@@ -51,9 +51,25 @@ export const VesktopNative = {
|
|||||||
addToDictionary: (word: string) => invoke<void>(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, word)
|
addToDictionary: (word: string) => invoke<void>(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, word)
|
||||||
},
|
},
|
||||||
win: {
|
win: {
|
||||||
focus: () => invoke<void>(IpcEvents.FOCUS)
|
focus: () => invoke<void>(IpcEvents.FOCUS),
|
||||||
|
close: () => invoke<void>(IpcEvents.CLOSE),
|
||||||
|
minimize: () => invoke<void>(IpcEvents.MINIMIZE),
|
||||||
|
maximize: () => invoke<void>(IpcEvents.MAXIMIZE)
|
||||||
},
|
},
|
||||||
capturer: {
|
capturer: {
|
||||||
getLargeThumbnail: (id: string) => invoke<string>(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, id)
|
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[]) => invoke<void>(IpcEvents.VIRT_MIC_START, targets),
|
||||||
|
startSystem: () => invoke<void>(IpcEvents.VIRT_MIC_START_SYSTEM),
|
||||||
|
stop: () => invoke<void>(IpcEvents.VIRT_MIC_STOP)
|
||||||
|
},
|
||||||
|
arrpc: {
|
||||||
|
onActivity(cb: (data: string) => void) {
|
||||||
|
ipcRenderer.on(IpcEvents.ARRPC_ACTIVITY, (_, data: string) => cb(data));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import { IpcEvents } from "../shared/IpcEvents";
|
|||||||
import { VesktopNative } from "./VesktopNative";
|
import { VesktopNative } from "./VesktopNative";
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("VesktopNative", VesktopNative);
|
contextBridge.exposeInMainWorld("VesktopNative", VesktopNative);
|
||||||
// TODO: remove legacy alias once main Vencord codebase has migrated and some time has passed
|
|
||||||
contextBridge.exposeInMainWorld("VencordDesktopNative", VesktopNative);
|
|
||||||
|
|
||||||
require(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_FILE));
|
require(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_FILE));
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,19 @@ let NotificationSettingsStore: any;
|
|||||||
export function setBadge() {
|
export function setBadge() {
|
||||||
if (Settings.store.appBadge === false) return;
|
if (Settings.store.appBadge === false) return;
|
||||||
|
|
||||||
const mentionCount = GuildReadStateStore.getTotalMentionCount();
|
try {
|
||||||
const pendingRequests = RelationshipStore.getPendingCount();
|
const mentionCount = GuildReadStateStore.getTotalMentionCount();
|
||||||
const hasUnread = GuildReadStateStore.hasAnyUnread();
|
const pendingRequests = RelationshipStore.getPendingCount();
|
||||||
const disableUnreadBadge = NotificationSettingsStore.getDisableUnreadBadge();
|
const hasUnread = GuildReadStateStore.hasAnyUnread();
|
||||||
|
const disableUnreadBadge = NotificationSettingsStore.getDisableUnreadBadge();
|
||||||
|
|
||||||
let totalCount = mentionCount + pendingRequests;
|
let totalCount = mentionCount + pendingRequests;
|
||||||
if (!totalCount && hasUnread && !disableUnreadBadge) totalCount = -1;
|
if (!totalCount && hasUnread && !disableUnreadBadge) totalCount = -1;
|
||||||
|
|
||||||
VesktopNative.app.setBadgeCount(totalCount);
|
VesktopNative.app.setBadgeCount(totalCount);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let toFind = 3;
|
let toFind = 3;
|
||||||
|
|||||||
@@ -7,11 +7,21 @@
|
|||||||
import "./screenSharePicker.css";
|
import "./screenSharePicker.css";
|
||||||
|
|
||||||
import { closeModal, Modals, openModal, useAwaiter } from "@vencord/types/utils";
|
import { closeModal, Modals, openModal, useAwaiter } from "@vencord/types/utils";
|
||||||
import { findStoreLazy } from "@vencord/types/webpack";
|
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
|
||||||
import { Button, Card, Forms, Switch, Text, useState } from "@vencord/types/webpack/common";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
FluxDispatcher,
|
||||||
|
Forms,
|
||||||
|
Select,
|
||||||
|
Switch,
|
||||||
|
Text,
|
||||||
|
UserStore,
|
||||||
|
useState
|
||||||
|
} from "@vencord/types/webpack/common";
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
import type { Dispatch, SetStateAction } from "react";
|
||||||
import { addPatch } from "renderer/patches/shared";
|
import { addPatch } from "renderer/patches/shared";
|
||||||
import { isWindows } from "renderer/utils";
|
import { isLinux, isWindows } from "renderer/utils";
|
||||||
|
|
||||||
const StreamResolutions = ["480", "720", "1080", "1440"] as const;
|
const StreamResolutions = ["480", "720", "1080", "1440"] as const;
|
||||||
const StreamFps = ["15", "30", "60"] as const;
|
const StreamFps = ["15", "30", "60"] as const;
|
||||||
@@ -25,6 +35,7 @@ interface StreamSettings {
|
|||||||
resolution: StreamResolution;
|
resolution: StreamResolution;
|
||||||
fps: StreamFps;
|
fps: StreamFps;
|
||||||
audio: boolean;
|
audio: boolean;
|
||||||
|
audioSource?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StreamPick extends StreamSettings {
|
export interface StreamPick extends StreamSettings {
|
||||||
@@ -70,18 +81,41 @@ addPatch({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export function openScreenSharePicker(screens: Source[]) {
|
if (isLinux) {
|
||||||
|
onceReady.then(() => {
|
||||||
|
FluxDispatcher.subscribe("VOICE_STATE_UPDATES", e => {
|
||||||
|
for (const state of e.voiceStates) {
|
||||||
|
if (state.userId === UserStore.getCurrentUser().id && state.oldChannelId && !state.channelId)
|
||||||
|
VesktopNative.virtmic.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openScreenSharePicker(screens: Source[], skipPicker: boolean) {
|
||||||
|
let didSubmit = false;
|
||||||
return new Promise<StreamPick>((resolve, reject) => {
|
return new Promise<StreamPick>((resolve, reject) => {
|
||||||
const key = openModal(
|
const key = openModal(
|
||||||
props => (
|
props => (
|
||||||
<ModalComponent
|
<ModalComponent
|
||||||
screens={screens}
|
screens={screens}
|
||||||
modalProps={props}
|
modalProps={props}
|
||||||
submit={resolve}
|
submit={async v => {
|
||||||
|
didSubmit = true;
|
||||||
|
if (v.audioSource && v.audioSource !== "None") {
|
||||||
|
if (v.audioSource === "Entire System") {
|
||||||
|
await VesktopNative.virtmic.startSystem();
|
||||||
|
} else {
|
||||||
|
await VesktopNative.virtmic.start([v.audioSource]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve(v);
|
||||||
|
}}
|
||||||
close={() => {
|
close={() => {
|
||||||
props.onClose();
|
props.onClose();
|
||||||
reject("Aborted");
|
if (!didSubmit) reject("Aborted");
|
||||||
}}
|
}}
|
||||||
|
skipPicker={skipPicker}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
@@ -112,16 +146,21 @@ function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScre
|
|||||||
function StreamSettings({
|
function StreamSettings({
|
||||||
source,
|
source,
|
||||||
settings,
|
settings,
|
||||||
setSettings
|
setSettings,
|
||||||
|
skipPicker
|
||||||
}: {
|
}: {
|
||||||
source: Source;
|
source: Source;
|
||||||
settings: StreamSettings;
|
settings: StreamSettings;
|
||||||
setSettings: Dispatch<SetStateAction<StreamSettings>>;
|
setSettings: Dispatch<SetStateAction<StreamSettings>>;
|
||||||
|
skipPicker: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [thumb] = useAwaiter(() => VesktopNative.capturer.getLargeThumbnail(source.id), {
|
const [thumb] = useAwaiter(
|
||||||
fallbackValue: source.url,
|
() => (skipPicker ? Promise.resolve(source.url) : VesktopNative.capturer.getLargeThumbnail(source.id)),
|
||||||
deps: [source.id]
|
{
|
||||||
});
|
fallbackValue: source.url,
|
||||||
|
deps: [source.id]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -182,23 +221,76 @@ function StreamSettings({
|
|||||||
Stream With Audio
|
Stream With Audio
|
||||||
</Switch>
|
</Switch>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isLinux && (
|
||||||
|
<AudioSourcePickerLinux
|
||||||
|
audioSource={settings.audioSource}
|
||||||
|
setAudioSource={source => setSettings(s => ({ ...s, audioSource: source }))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AudioSourcePickerLinux({
|
||||||
|
audioSource,
|
||||||
|
setAudioSource
|
||||||
|
}: {
|
||||||
|
audioSource?: string;
|
||||||
|
setAudioSource(s: string): 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}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function ModalComponent({
|
function ModalComponent({
|
||||||
screens,
|
screens,
|
||||||
modalProps,
|
modalProps,
|
||||||
submit,
|
submit,
|
||||||
close
|
close,
|
||||||
|
skipPicker
|
||||||
}: {
|
}: {
|
||||||
screens: Source[];
|
screens: Source[];
|
||||||
modalProps: any;
|
modalProps: any;
|
||||||
submit: (data: StreamPick) => void;
|
submit: (data: StreamPick) => void;
|
||||||
close: () => 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>({
|
const [settings, setSettings] = useState<StreamSettings>({
|
||||||
resolution: "1080",
|
resolution: "1080",
|
||||||
fps: "60",
|
fps: "60",
|
||||||
@@ -220,6 +312,7 @@ function ModalComponent({
|
|||||||
source={screens.find(s => s.id === selected)!}
|
source={screens.find(s => s.id === selected)!}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
setSettings={setSettings}
|
setSettings={setSettings}
|
||||||
|
skipPicker={skipPicker}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modals.ModalContent>
|
</Modals.ModalContent>
|
||||||
@@ -259,7 +352,7 @@ function ModalComponent({
|
|||||||
Go Live
|
Go Live
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{selected ? (
|
{selected && !skipPicker ? (
|
||||||
<Button color={Button.Colors.TRANSPARENT} onClick={() => setSelected(void 0)}>
|
<Button color={Button.Colors.TRANSPARENT} onClick={() => setSelected(void 0)}>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -7,9 +7,11 @@
|
|||||||
import "./settings.css";
|
import "./settings.css";
|
||||||
|
|
||||||
import { Margins } from "@vencord/types/utils";
|
import { Margins } from "@vencord/types/utils";
|
||||||
import { Button, Forms, Select, Switch, Text, useState } from "@vencord/types/webpack/common";
|
import { Button, Forms, Select, Switch, Text, Toasts, useState } from "@vencord/types/webpack/common";
|
||||||
import { setBadge } from "renderer/appBadge";
|
import { setBadge } from "renderer/appBadge";
|
||||||
import { useSettings } from "renderer/settings";
|
import { useSettings } from "renderer/settings";
|
||||||
|
import { isMac, isWindows } from "renderer/utils";
|
||||||
|
import { isTruthy } from "shared/utils/guards";
|
||||||
|
|
||||||
export default function SettingsUi() {
|
export default function SettingsUi() {
|
||||||
const Settings = useSettings();
|
const Settings = useSettings();
|
||||||
@@ -18,9 +20,14 @@ export default function SettingsUi() {
|
|||||||
const { autostart } = VesktopNative;
|
const { autostart } = VesktopNative;
|
||||||
const [autoStartEnabled, setAutoStartEnabled] = useState(autostart.isEnabled());
|
const [autoStartEnabled, setAutoStartEnabled] = useState(autostart.isEnabled());
|
||||||
|
|
||||||
const switches: [keyof typeof Settings, string, string, boolean?, (() => boolean)?][] = [
|
const allSwitches: Array<false | [keyof typeof Settings, string, string, boolean?, (() => boolean)?]> = [
|
||||||
["tray", "Tray Icon", "Add a tray icon for Vesktop", true],
|
isWindows && [
|
||||||
[
|
"discordWindowsTitleBar",
|
||||||
|
"Discord Titlebar",
|
||||||
|
"Use Discord's custom title bar instead of the Windows one. Requires a full restart."
|
||||||
|
],
|
||||||
|
!isMac && ["tray", "Tray Icon", "Add a tray icon for Vesktop", true],
|
||||||
|
!isMac && [
|
||||||
"minimizeToTray",
|
"minimizeToTray",
|
||||||
"Minimize to tray",
|
"Minimize to tray",
|
||||||
"Hitting X will make Vesktop minimize to the tray instead of closing",
|
"Hitting X will make Vesktop minimize to the tray instead of closing",
|
||||||
@@ -33,14 +40,21 @@ export default function SettingsUi() {
|
|||||||
"Disable minimum window size",
|
"Disable minimum window size",
|
||||||
"Allows you to make the window as small as your heart desires"
|
"Allows you to make the window as small as your heart desires"
|
||||||
],
|
],
|
||||||
|
["staticTitle", "Static Title", 'Makes the window title "Vesktop" instead of changing to the current page'],
|
||||||
|
["enableMenu", "Enable Menu Bar", "Enables the application menu bar. Press ALT to toggle visibility."],
|
||||||
|
["disableSmoothScroll", "Disable smooth scrolling", "Disables smooth scrolling in Vesktop", false],
|
||||||
|
["splashTheming", "Splash theming", "Adapt the splash window colors to your custom theme", false],
|
||||||
[
|
[
|
||||||
"openLinksWithElectron",
|
"openLinksWithElectron",
|
||||||
"Open Links in app (experimental)",
|
"Open Links in app (experimental)",
|
||||||
"Opens links in a new Vesktop window instead of your web browser"
|
"Opens links in a new Vesktop window instead of your web browser"
|
||||||
],
|
],
|
||||||
["staticTitle", "Static Title", 'Makes the window title "Vencord" instead of changing to the current page']
|
["checkUpdates", "Check for updates", "Automatically check for Vesktop updates", true],
|
||||||
|
["startMinimized", "Start minimized", "Vesktop remains in minimized mode on start", false]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const switches = allSwitches.filter(isTruthy);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Forms.FormSection>
|
<Forms.FormSection>
|
||||||
<Text variant="heading-lg/semibold" style={{ color: "var(--header-primary)" }} tag="h2">
|
<Text variant="heading-lg/semibold" style={{ color: "var(--header-primary)" }} tag="h2">
|
||||||
@@ -159,8 +173,14 @@ export default function SettingsUi() {
|
|||||||
const choice = await VesktopNative.fileManager.selectVencordDir();
|
const choice = await VesktopNative.fileManager.selectVencordDir();
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case "cancelled":
|
case "cancelled":
|
||||||
|
return;
|
||||||
case "invalid":
|
case "invalid":
|
||||||
// TODO
|
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;
|
return;
|
||||||
}
|
}
|
||||||
Settings.vencordDir = choice;
|
Settings.vencordDir = choice;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import "./hideGarbage.css";
|
|||||||
|
|
||||||
import { waitFor } from "@vencord/types/webpack";
|
import { waitFor } from "@vencord/types/webpack";
|
||||||
|
|
||||||
import { isFirstRun, localStorage } from "./utils";
|
import { isFirstRun, isWindows, localStorage } from "./utils";
|
||||||
|
|
||||||
// Make clicking Notifications focus the window
|
// Make clicking Notifications focus the window
|
||||||
const originalSetOnClick = Object.getOwnPropertyDescriptor(Notification.prototype, "onclick")!.set!;
|
const originalSetOnClick = Object.getOwnPropertyDescriptor(Notification.prototype, "onclick")!.set!;
|
||||||
@@ -31,3 +31,14 @@ if (isFirstRun) {
|
|||||||
m.setDesktopType("all");
|
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 */
|
/* Download Desktop button in guilds list */
|
||||||
[class|=listItem]:has([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]) {
|
[class^=listItem_]:has(+ [class^=listItem_] [data-list-item-id=guildsnav___app-download-button]) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import "./fixes";
|
import "./fixes";
|
||||||
import "./appBadge";
|
import "./appBadge";
|
||||||
import "./patches";
|
import "./patches";
|
||||||
|
import "./themedSplash";
|
||||||
|
|
||||||
console.log("read if cute :3");
|
console.log("read if cute :3");
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ export * as Components from "./components";
|
|||||||
import { findByPropsLazy } from "@vencord/types/webpack";
|
import { findByPropsLazy } from "@vencord/types/webpack";
|
||||||
import { FluxDispatcher } from "@vencord/types/webpack/common";
|
import { FluxDispatcher } from "@vencord/types/webpack/common";
|
||||||
|
|
||||||
|
import SettingsUi from "./components/Settings";
|
||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
export { Settings };
|
export { Settings };
|
||||||
|
|
||||||
@@ -35,14 +37,23 @@ export async function openInviteModal(code: string) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const arRPC = Vencord.Plugins.plugins["WebRichPresence (arRPC)"];
|
const customSettingsSections = (
|
||||||
|
Vencord.Plugins.plugins.Settings as any as { customSections: ((ID: Record<string, unknown>) => any)[] }
|
||||||
|
).customSections;
|
||||||
|
|
||||||
arRPC.required = !!Settings.store.arRPC;
|
customSettingsSections.push(() => ({
|
||||||
|
section: "Vesktop",
|
||||||
|
label: "Vesktop Settings",
|
||||||
|
element: SettingsUi,
|
||||||
|
className: "vc-vesktop-settings"
|
||||||
|
}));
|
||||||
|
|
||||||
Settings.addChangeListener("arRPC", v => {
|
const arRPC = Vencord.Plugins.plugins["WebRichPresence (arRPC)"] as any as {
|
||||||
arRPC.required = !!v;
|
handleEvent(e: MessageEvent): void;
|
||||||
if (v && !arRPC.started) Vencord.Plugins.startPlugin(arRPC);
|
};
|
||||||
else if (arRPC.started) {
|
|
||||||
Vencord.Plugins.stopPlugin(arRPC);
|
VesktopNative.arrpc.onActivity(data => {
|
||||||
}
|
if (!Settings.store.arRPC) return;
|
||||||
|
|
||||||
|
arRPC.handleEvent(new MessageEvent("message", { data }));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,3 +7,5 @@
|
|||||||
// TODO: Possibly auto generate glob if we have more patches in the future
|
// TODO: Possibly auto generate glob if we have more patches in the future
|
||||||
import "./spellCheck";
|
import "./spellCheck";
|
||||||
import "./platformClass";
|
import "./platformClass";
|
||||||
|
import "./windowsTitleBar";
|
||||||
|
import "./screenShareAudio";
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Settings } from "renderer/settings";
|
||||||
|
import { isMac, isWindows } from "renderer/utils";
|
||||||
|
|
||||||
import { addPatch } from "./shared";
|
import { addPatch } from "./shared";
|
||||||
|
|
||||||
addPatch({
|
addPatch({
|
||||||
@@ -18,5 +21,9 @@ addPatch({
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
getPlatformClass: () => (navigator.platform.toLowerCase().startsWith("mac") ? "platform-osx" : "platform-web")
|
getPlatformClass() {
|
||||||
|
if (isMac) return "platform-osx";
|
||||||
|
if (isWindows && Settings.store.discordWindowsTitleBar) return "platform-win";
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ addPatch({
|
|||||||
find: ".enableSpellCheck)",
|
find: ".enableSpellCheck)",
|
||||||
replacement: {
|
replacement: {
|
||||||
// if (isDesktop) { DiscordNative.onSpellcheck(openMenu(props)) } else { e.preventDefault(); openMenu(props) }
|
// if (isDesktop) { DiscordNative.onSpellcheck(openMenu(props)) } else { e.preventDefault(); openMenu(props) }
|
||||||
match: /else\{(.{1,3})\.preventDefault\(\);(.{1,3}\(.{1,3}\))\}(?<=:(.{1,3})\.enableSpellCheck\).+?)/,
|
match: /else (.{1,3})\.preventDefault\(\),(.{1,3}\(.{1,3}\))(?<=:(.{1,3})\.enableSpellCheck\).+?)/,
|
||||||
// ... else { $self.onSlateContext(() => openMenu(props)) }
|
// ... else { $self.onSlateContext(() => openMenu(props)) }
|
||||||
replace: "else {$self.onSlateContext($1, $3?.enableSpellCheck, () => $2)}"
|
replace: "else {$self.onSlateContext($1, $3?.enableSpellCheck, () => $2)}"
|
||||||
}
|
}
|
||||||
|
|||||||
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.discordWindowsTitleBar)
|
||||||
|
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}`
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
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);
|
||||||
@@ -16,3 +16,5 @@ export const isFirstRun = (() => {
|
|||||||
const { platform } = navigator;
|
const { platform } = navigator;
|
||||||
|
|
||||||
export const isWindows = platform.startsWith("Win");
|
export const isWindows = platform.startsWith("Win");
|
||||||
|
export const isMac = platform.startsWith("Mac");
|
||||||
|
export const isLinux = platform.startsWith("Linux");
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ export const enum IpcEvents {
|
|||||||
RELAUNCH = "VCD_RELAUNCH",
|
RELAUNCH = "VCD_RELAUNCH",
|
||||||
CLOSE = "VCD_CLOSE",
|
CLOSE = "VCD_CLOSE",
|
||||||
FOCUS = "VCD_FOCUS",
|
FOCUS = "VCD_FOCUS",
|
||||||
|
MINIMIZE = "VCD_MINIMIZE",
|
||||||
|
MAXIMIZE = "VCD_MAXIMIZE",
|
||||||
|
|
||||||
SHOW_ITEM_IN_FOLDER = "VCD_SHOW_ITEM_IN_FOLDER",
|
SHOW_ITEM_IN_FOLDER = "VCD_SHOW_ITEM_IN_FOLDER",
|
||||||
GET_SETTINGS = "VCD_GET_SETTINGS",
|
GET_SETTINGS = "VCD_GET_SETTINGS",
|
||||||
@@ -38,5 +40,12 @@ export const enum IpcEvents {
|
|||||||
|
|
||||||
AUTOSTART_ENABLED = "VCD_AUTOSTART_ENABLED",
|
AUTOSTART_ENABLED = "VCD_AUTOSTART_ENABLED",
|
||||||
ENABLE_AUTOSTART = "VCD_ENABLE_AUTOSTART",
|
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"
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/shared/settings.d.ts
vendored
30
src/shared/settings.d.ts
vendored
@@ -7,20 +7,32 @@
|
|||||||
import type { Rectangle } from "electron";
|
import type { Rectangle } from "electron";
|
||||||
|
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
|
discordBranch?: "stable" | "canary" | "ptb";
|
||||||
|
vencordDir?: string;
|
||||||
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
|
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
|
||||||
|
tray?: boolean;
|
||||||
|
minimizeToTray?: boolean;
|
||||||
|
openLinksWithElectron?: boolean;
|
||||||
|
staticTitle?: boolean;
|
||||||
|
enableMenu?: boolean;
|
||||||
|
disableSmoothScroll?: boolean;
|
||||||
|
arRPC?: boolean;
|
||||||
|
appBadge?: boolean;
|
||||||
|
discordWindowsTitleBar?: boolean;
|
||||||
|
startMinimized?: boolean;
|
||||||
|
|
||||||
maximized?: boolean;
|
maximized?: boolean;
|
||||||
minimized?: boolean;
|
minimized?: boolean;
|
||||||
windowBounds?: Rectangle;
|
windowBounds?: Rectangle;
|
||||||
discordBranch?: "stable" | "canary" | "ptb";
|
|
||||||
openLinksWithElectron?: boolean;
|
|
||||||
vencordDir?: string;
|
|
||||||
disableMinSize?: boolean;
|
disableMinSize?: boolean;
|
||||||
tray?: boolean;
|
|
||||||
minimizeToTray?: boolean;
|
|
||||||
skippedUpdate?: string;
|
|
||||||
staticTitle?: boolean;
|
|
||||||
arRPC?: boolean;
|
|
||||||
appBadge?: boolean;
|
|
||||||
|
|
||||||
|
checkUpdates?: boolean;
|
||||||
|
skippedUpdate?: string;
|
||||||
firstLaunch?: boolean;
|
firstLaunch?: boolean;
|
||||||
|
|
||||||
|
splashTheming?: boolean;
|
||||||
|
splashColor?: string;
|
||||||
|
splashBackground?: string;
|
||||||
|
|
||||||
|
steamOSLayoutVersion?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ type ResolvePropDeep<T, P> = P extends `${infer Pre}.${infer Suf}`
|
|||||||
? ResolvePropDeep<T[Pre], Suf>
|
? ResolvePropDeep<T[Pre], Suf>
|
||||||
: any
|
: any
|
||||||
: P extends keyof T
|
: P extends keyof T
|
||||||
? T[P]
|
? T[P]
|
||||||
: any;
|
: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SettingsStore allows you to easily create a mutable store that
|
* The SettingsStore allows you to easily create a mutable store that
|
||||||
|
|||||||
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));
|
||||||
|
}
|
||||||
@@ -4,13 +4,14 @@
|
|||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app, BrowserWindow, ipcMain, shell } from "electron";
|
import { app, BrowserWindow, shell } from "electron";
|
||||||
import { Settings } from "main/settings";
|
import { Settings } from "main/settings";
|
||||||
|
import { handle } from "main/utils/ipcWrappers";
|
||||||
import { makeLinksOpenExternally } from "main/utils/makeLinksOpenExternally";
|
import { makeLinksOpenExternally } from "main/utils/makeLinksOpenExternally";
|
||||||
import { githubGet, ReleaseData } from "main/utils/vencordLoader";
|
import { githubGet, ReleaseData } from "main/utils/vencordLoader";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { IpcEvents } from "shared/IpcEvents";
|
import { IpcEvents } from "shared/IpcEvents";
|
||||||
import { VIEW_DIR } from "shared/paths";
|
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||||
|
|
||||||
export interface UpdateData {
|
export interface UpdateData {
|
||||||
currentVersion: string;
|
currentVersion: string;
|
||||||
@@ -20,8 +21,8 @@ export interface UpdateData {
|
|||||||
|
|
||||||
let updateData: UpdateData;
|
let updateData: UpdateData;
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.UPDATER_GET_DATA, () => updateData);
|
handle(IpcEvents.UPDATER_GET_DATA, () => updateData);
|
||||||
ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
||||||
const portable = !!process.env.PORTABLE_EXECUTABLE_FILE;
|
const portable = !!process.env.PORTABLE_EXECUTABLE_FILE;
|
||||||
|
|
||||||
const { assets } = updateData.release;
|
const { assets } = updateData.release;
|
||||||
@@ -35,7 +36,11 @@ ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
|||||||
return portable ? !isSetup : isSetup;
|
return portable ? !isSetup : isSetup;
|
||||||
})!.browser_download_url;
|
})!.browser_download_url;
|
||||||
case "darwin":
|
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":
|
case "linux":
|
||||||
return updateData.release.html_url;
|
return updateData.release.html_url;
|
||||||
default:
|
default:
|
||||||
@@ -46,7 +51,7 @@ ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
|||||||
shell.openExternal(url);
|
shell.openExternal(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.UPDATE_IGNORE, () => {
|
handle(IpcEvents.UPDATE_IGNORE, () => {
|
||||||
Settings.store.skippedUpdate = updateData.latestVersion;
|
Settings.store.skippedUpdate = updateData.latestVersion;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -72,7 +77,8 @@ function isOutdated(oldVersion: string, newVersion: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function checkUpdates() {
|
export async function checkUpdates() {
|
||||||
// if (IS_DEV) return;
|
if (Settings.store.checkUpdates === false) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const raw = await githubGet("/repos/Vencord/Vesktop/releases/latest");
|
const raw = await githubGet("/repos/Vencord/Vesktop/releases/latest");
|
||||||
const data = JSON.parse(raw.toString("utf-8")) as ReleaseData;
|
const data = JSON.parse(raw.toString("utf-8")) as ReleaseData;
|
||||||
@@ -103,7 +109,8 @@ function openNewUpdateWindow() {
|
|||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
sandbox: true
|
sandbox: true
|
||||||
}
|
},
|
||||||
|
icon: ICON_PATH
|
||||||
});
|
});
|
||||||
|
|
||||||
makeLinksOpenExternally(win);
|
makeLinksOpenExternally(win);
|
||||||
|
|||||||
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 |
@@ -51,6 +51,10 @@
|
|||||||
<a href="https://github.com/OpenAsar/arrpc" target="_blank">arrpc</a>
|
<a href="https://github.com/OpenAsar/arrpc" target="_blank">arrpc</a>
|
||||||
- An open implementation of Discord's Rich Presence server
|
- An open implementation of Discord's Rich Presence server
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://github.com/Soundux/rohrkabel" target="_blank">rohrkabel</a>
|
||||||
|
- A C++ RAII Pipewire-API Wrapper
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
And many
|
And many
|
||||||
<a href="https://github.com/Vencord/Vesktop/blob/main/pnpm-lock.yaml" target="_blank"
|
<a href="https://github.com/Vencord/Vesktop/blob/main/pnpm-lock.yaml" target="_blank"
|
||||||
|
|||||||
@@ -22,8 +22,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 6em;
|
width: 128px;
|
||||||
height: 6em;
|
height: 128px;
|
||||||
|
image-rendering: pixelated;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<img
|
<img
|
||||||
draggable="false"
|
draggable="false"
|
||||||
src="https://cdn.discordapp.com/emojis/1024751291504791654.gif?size=512"
|
src="../shiggy.gif"
|
||||||
alt="shiggy"
|
alt="shiggy"
|
||||||
role="presentation"
|
role="presentation"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user