108 Commits

Author SHA1 Message Date
Lewis Crichton
bb09596485 chore: prep for flatpak 2023-12-09 23:09:54 +00:00
Lewis Crichton
894ec4b902 chore: lint 2023-12-09 23:01:35 +00:00
Lewis Crichton
dd44602730 fix: make minimize only run on autostart 2023-12-09 23:00:47 +00:00
Michal Vaniš
40b952d8bf feat: Add start as minimized toggle to settings (#248)
Co-authored-by: TheKodeToad <TheKodeToad@proton.me>
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Lewis Crichton <lewi@lewisakura.moe>
2023-12-09 13:42:44 +00:00
Lewis Crichton
4974848a56 ci: pin to node v18.18.2 2023-12-09 13:34:04 +00:00
Lewis Crichton
623fa5d709 ci: notify us when PR is open [skip ci]
annoyingly, github actions can't skip pull request checks and it cannot
commit to the main branch due to protection, so we have to MANUALLY
accept the changes in. on the bright side, we can check what it's
generated in case it's wrong.
2023-12-04 22:35:29 +00:00
github-actions[bot]
fc33050496 Insert release changes for 2023-12-04 22:34:27 +00:00
Lewis Crichton
e02acda6fa ci(updateMeta): fix invalid appstream gen 2023-12-04 22:27:01 +00:00
Lewis Crichton
f11a72d4b5 Merge branch 'main' of gh:vencord/vesktop 2023-12-04 21:41:08 +00:00
Lewis Crichton
49cd558fa1 ci: let it run CI and do an automerge [skip ci] 2023-12-04 21:41:01 +00:00
github-actions[bot]
378a1f7464 Insert release changes for [skip ci] 2023-12-04 21:34:19 +00:00
Lewis Crichton
35d2dd6505 ci: is very hard to get right [skip ci] 2023-12-04 21:33:30 +00:00
Lewis Crichton
017c2c847f ci: fix command [skip ci] 2023-12-04 21:30:06 +00:00
Lewis Crichton
de2b4b7dd8 ci: create a PR to merge in metainfo automatically [skip ci] 2023-12-04 21:28:34 +00:00
Lewis Crichton
5dc5741771 ci: add metainfo auto updating 2023-12-04 21:16:43 +00:00
Lewis Crichton
971c490c41 merge 2023-12-04 21:06:17 +00:00
Lewis Crichton
0f804f9f6e feat: autogenerated release notes 2023-12-04 21:05:09 +00:00
Lewis Crichton
fdf454722f readme: add flathub badge (#269) 2023-12-04 14:17:46 +01:00
Vendicated
208158f4d1 fix shiggy looking creepy 2023-12-04 11:34:39 +01:00
Vendicated
032b94e81d fix ci part 2 2023-12-02 17:52:13 +01:00
Vendicated
4988163744 fix ci 2023-12-02 17:44:52 +01:00
Vendicated
ee7e71b9fb ci: stop using action-electron-builder 2023-12-02 17:41:38 +01:00
Vendicated
7efa54687a bump to v0.4.4 2023-12-02 17:27:20 +01:00
Nico
2917ff25e5 Bump dependencies (#258)
Co-authored-by: V <vendicated@riseup.net>
2023-12-02 17:17:59 +01:00
viacoro
e3839c35b7 unblur shiggy in splash screen (#221) 2023-12-02 17:11:10 +01:00
Vendicated
c39678d733 vesktop.desktop: remove faulty WMClass field 2023-12-02 17:10:33 +01:00
V
06dea79c74 Update README.md 2023-12-02 14:54:54 +01:00
Michal Vaniš
96b0652a06 feat: Add option to disable smooth scrolling (#255) 2023-11-24 22:14:23 +00:00
Noah
49e0411be6 chore(deps): bump venmic (#235)
* feat: update venmic

* chore(deps): bump venmic

* chore(deps): bump venmic

* chore(deps): bump venmic

* fix: update pnpm-lock
2023-11-17 00:53:13 +00:00
AAGaming
94819e6f16 Update steamdeck controller layout (#236) 2023-11-12 01:28:57 +00:00
Noah
a232af06ed feat: update venmic (#230)
* feat: update venmic

* chore(deps): bump venmic
2023-11-10 18:12:36 +01:00
AAGaming
b24535483e proper fix for disabling sandbox on steamos (#206)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-11-01 01:50:18 +00:00
Vendicated
9521c287b6 remove electron-builder-sandbox-fix 2023-11-01 00:13:28 +01:00
Vendicated
55b9edec39 Bump to v0.4.3 2023-10-31 23:18:24 +01:00
AAGaming
1e9c70eed9 SteamOS: fixes & official controller layout (#194) 2023-10-31 22:14:30 +01:00
Mars
3262e083fa fix positioning of traffic lights on darwin (#185)
Co-authored-by: V <vendicated@riseup.net>
2023-10-30 17:35:24 +00:00
Vendicated
ec1c719553 Bump venmic, improves support for older distros 2023-10-30 18:25:05 +01:00
Vendicated
cc62903b9c bump to v0.4.2 2023-10-27 00:43:02 +02:00
Vendicated
b17370cc7b add arm64 venmic binary 2023-10-27 00:42:50 +02:00
Vendicated
886d02f7c3 screenaudio: show better error if glibcxx too old 2023-10-27 00:27:35 +02:00
Nico
0cad71f6ae fix: adjust hiding download button to new discord update (#176) 2023-10-26 23:23:31 +02:00
Vendicated
b5eac15b42 Use correct OS in U-Agent ~ ~should fix captchas 2023-10-26 22:07:35 +02:00
rini
19c3112d52 fix spellcheck patch (#169) 2023-10-25 20:57:08 +02:00
Vendicated
1c308d0e2c Bump to v0.4.1 2023-10-25 00:32:06 +02:00
Vendicated
7f6db5eeda Linux ScreenAudio: Remove Duplicates 2023-10-25 00:31:53 +02:00
Vendicated
10b38e5b41 make updater window close button close proper window 2023-10-25 00:30:42 +02:00
wearr
28282d1d76 Use local shiggy gif for splash (#157)
Co-authored-by: wearrrrr <contact@wearr.dev>
Co-authored-by: V <vendicated@riseup.net>
2023-10-25 00:25:17 +02:00
MiMillieuh
e1512b72f4 Fix : VM class for AppImage has broken Dekstop integrations (#160) 2023-10-25 00:24:49 +02:00
AAGaming
46a2314173 fix turnary soup in main window options (#164) 2023-10-25 00:24:37 +02:00
Vendicated
e6dc026708 Fix for latest canary 2023-10-24 23:12:22 +02:00
Lewis Crichton
cac307d1fc ci: pin wgr to new version (#163) 2023-10-23 17:44:54 +00:00
Ryan Cao
4b8f374856 feat: allow disabling updates check (#155)
Co-authored-by: V <vendicated@riseup.net>
2023-10-22 17:10:25 +02:00
Jack
e6cc11fc0e Build arm64 Linux packages (#151) 2023-10-22 16:53:58 +02:00
Vendicated
43ca479fc8 bump to v0.4.0 2023-10-21 22:27:50 +02:00
Vendicated
7b0f64a9fc remove legacy vencorddesktop aliases 2023-10-21 22:25:16 +02:00
V
573a953a2f add linux audio screensharing (#130)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: kaitlynkitty <87152313+kaitlynkittyy@users.noreply.github.com>
Co-authored-by: Curve <fynnbwdt@gmail.com>
2023-10-21 22:15:55 +02:00
Ryan Cao
841cdcf672 feat: add splash window theming (#52)
Co-authored-by: V <vendicated@riseup.net>
2023-10-14 03:04:44 +00:00
Vendicated
0d93e08e99 arrpc: migrate from websocket + plugin to electron ipc 2023-10-13 04:02:10 +02:00
Tornike Khintibidze
c445c6194f MacOs: properly request microphone and camera permission (#121)
Co-authored-by: V <vendicated@riseup.net>
2023-10-12 04:24:16 +02:00
Nick
e29d293855 README: hyperlink Vencord (#143) 2023-10-11 17:24:39 +02:00
Vendicated
e13a4eacb1 bump deps 2023-10-11 17:21:32 +02:00
Vendicated
c070761f9d Bump electron to v27 2023-10-11 17:19:45 +02:00
Lewis Crichton
ae86c28247 ci: force fork user to be bot account (#141) 2023-10-07 16:19:42 +00:00
Lewis Crichton
9c95956c96 ci: allow manual submissions (#140) 2023-10-07 18:10:07 +02:00
Vendicated
45f56c63a0 bump to v0.3.3 2023-09-30 22:45:22 +02:00
Tornike Khintibidze
89af4316d3 Optimize the menu bar for macOS (#120)
Co-authored-by: V <vendicated@riseup.net>
2023-09-30 20:43:27 +00:00
Cynthia Foxwell
a9bfb857ae Bump Electron to 25.8.4 for CVE-2023-5217 (#134) 2023-09-30 19:38:32 +00:00
Vendicated
b9e411ac90 fix deb/rpm icon 2023-09-28 02:31:42 +02:00
Zyrouge
670de01938 fix: appimage not relaunching (#128) 2023-09-27 04:52:00 +02:00
Lewis Crichton
ef064eba3d ci: switch to vedantmgoyal2009/winget-releaser (#103)
Co-authored-by: V <vendicated@riseup.net>
2023-09-26 00:00:17 +02:00
V
d5f63da939 bump to v0.3.2 2023-09-25 22:11:53 +02:00
Pierre
2aadc61af9 Downgrade electron to v25.8.2 (#126) 2023-09-25 22:09:46 +02:00
Zyrouge
061fec44af fix icon missing in some windows (#124)
Co-authored-by: V <vendicated@riseup.net>
2023-09-25 01:19:54 +00:00
V
b876f450c3 bump arrpc 2023-09-25 03:17:50 +02:00
V
5f5febda9d bump electron for important security fixes 2023-09-25 03:12:51 +02:00
Lewis Crichton
94ba59afb5 fix: add correct WMClass to desktop file (#108)
resolves #96
2023-08-31 22:52:12 +02:00
V
566737017c ci: publish as draft 2023-08-27 22:40:37 +02:00
Lewis Crichton
196ee4e42c ci: winget (#101) 2023-08-27 22:27:23 +02:00
V
9bb02f8581 security: make ipc only allow discord origins 2023-08-25 15:32:06 +02:00
V
c76d7195a5 Bump to 0.3.0 2023-08-16 02:05:17 +02:00
V
50a103710d Add Vesktop section to Settings customSections 2023-08-16 02:02:23 +02:00
DaBluLite
e6e66e775c Fix platformClass: change "platform-windows" to "platform-win" (#94) 2023-08-14 13:55:47 +00:00
V
a5ec031a2f Fix windows titlebar on canary 2023-08-12 04:01:10 +02:00
V
4dceadbbd2 Bump to 0.2.9 (for real this time) 2023-08-12 03:15:37 +02:00
V
6ee920ff2c Show error toast when selecting invalid venchord dir 2023-08-12 03:15:21 +02:00
V
28ad4a6f73 Improve valid vencord install checks 2023-08-12 03:13:07 +02:00
V
c5ac3e64a6 Bump to 0.2.9 2023-08-12 03:09:38 +02:00
Justice Almanzar
6dc26aea6a vecord: download proper vesktop specific preload & renderer css (#90)
Co-authored-by: V <vendicated@riseup.net>
2023-08-12 03:09:14 +02:00
Hugo C
9003b94f85 mac: fix dock notification badge (#88) 2023-08-08 13:08:32 +02:00
V
f7b7931847 only pass partial info 2023-08-07 00:48:23 +02:00
V
b87bcaefe9 Wayland: Skip our screenshare screen picker 2023-08-07 00:39:38 +02:00
V
3108de7c79 Port Discord's windows title bar (#86) 2023-08-07 00:23:27 +02:00
Lewis Crichton
57006e7e52 feat: portable zip (#85) 2023-08-06 02:05:48 +02:00
Nickyux
8f1ea1f440 Update Settings Text to Vesktop (#84) 2023-08-05 17:18:12 +00:00
TymanWasTaken
26b6fb13d4 Update pnpm in package.json (#79)
Co-authored-by: V <vendicated@riseup.net>
2023-08-04 20:36:28 +02:00
V
e1971f55a0 bump arrpc; fixes false positives 2023-08-04 20:21:49 +02:00
V
ba2618878e add vencord themes watcher 2023-08-04 19:39:33 +02:00
V
87595deae7 Fix static title 2023-08-02 23:59:02 +02:00
V
6ab7030fdf Bump to v0.2.8 2023-08-02 23:26:12 +02:00
V
5ba84e8a0d but here's the bumper 2023-08-02 23:26:00 +02:00
V
61f9559984 Add Enable Menu setting 2023-07-28 21:54:17 +02:00
V
5fa9264bdb mac: Hide tray related settings 2023-07-28 21:45:22 +02:00
V
c9f0920f71 Bump arRPC 2023-07-28 21:39:50 +02:00
V
f502d04b5f Bump dependencies 2023-07-28 21:38:17 +02:00
Ryan Cao
08090e3764 feat: use standardized icon for macOS (#48)
Co-authored-by: V <vendicated@riseup.net>
2023-07-27 01:03:29 +00:00
Hugo C
a95f7f8fbd Remove macOS tray icon (#68)
Co-authored-by: V <vendicated@riseup.net>
2023-07-27 00:20:15 +00:00
Ryan Cao
9d62cc9437 fix: macOS updater URL for different architectures (#69)
Co-authored-by: V <vendicated@riseup.net>
2023-07-27 02:16:17 +02:00
V
2b4f7a07d2 Fix tray logic 2023-07-27 00:46:25 +02:00
56 changed files with 3024 additions and 1038 deletions

View File

@@ -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
View 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 }}

View File

@@ -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 }}

View File

@@ -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
View 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
View File

@@ -1,3 +1,6 @@
dist dist
node_modules node_modules
.env .env
.DS_Store
.idea/
.pnpm-store/

50
.vscode/settings.json vendored
View File

@@ -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"]
} }

View File

@@ -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!!
![image](https://user-images.githubusercontent.com/45497981/235024615-94565eaf-f412-4384-a3f5-d8cde7458f6d.png) ![](https://github.com/Vencord/Vesktop/assets/45497981/8608a899-96a9-4027-9725-2cb02ba189fd)
![grafik](https://github.com/Vencord/Vesktop/assets/45497981/8701e5de-52c4-4346-a990-719cb971642e)
## Installing ## Installing
@@ -21,6 +23,8 @@ Download and run Vesktop-VERSION.dmg from [releases](https://github.com/Vencord/
### Linux ### Linux
[![](https://dl.flathub.org/assets/badges/flathub-badge-en.svg)](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

Binary file not shown.

View 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 &amp; 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>

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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" }
}) })
]); ]);

View 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;

View File

@@ -1,7 +0,0 @@
// Work around https://github.com/evanw/esbuild/issues/2460
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"jsx": "react"
}
}

View File

@@ -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(" ") ?? [])]);

View 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");

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 })
}; };

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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", () => {

View File

@@ -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);
})
);

View File

@@ -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();
} }
}); });

View 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);
});
}

View File

@@ -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({});

View File

@@ -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;
} }

View 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
View 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);
}

View File

@@ -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
View 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());

View File

@@ -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));
}
} }
}; };

View File

@@ -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));

View 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;

View File

@@ -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>

View File

@@ -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;

View File

@@ -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 {}

View File

@@ -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;
} }

View File

@@ -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 }));
}); });

View File

@@ -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";

View File

@@ -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";
}
}); });

View 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;
};
}

View File

@@ -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)}"
} }

View 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}`
}))
]
}
]
});

View 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);

View File

@@ -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");

View File

@@ -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"
} }

View File

@@ -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;
} }

View File

@@ -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

View 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));
}

View File

@@ -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
View File

@@ -0,0 +1,2 @@
*
!.gitignore

BIN
static/shiggy.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -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"

View File

@@ -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"
/> />