117 Commits

Author SHA1 Message Date
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
V
be9642eff1 Bump to v0.2.7 2023-07-26 02:28:23 +02:00
Ryan Cao
d884b7d3d6 feat: add "Reset Vesktop" option to menu & tray (#53)
Co-authored-by: V <vendicated@riseup.net>
2023-07-14 00:14:48 +00:00
Ryan Cao
6d35be79b8 feat: use inset title bar style on macOS (#47)
Co-authored-by: V <vendicated@riseup.net>
2023-07-14 00:07:48 +00:00
Ryan Cao
70ca06eb16 fix: remove duplicate quit items on macOS (#49)
Co-authored-by: V <vendicated@riseup.net>
2023-07-13 17:07:19 +00:00
V
210ddbae06 Rename to Vesktop (#57) 2023-07-13 19:03:13 +02:00
V
3ee57831b9 Fix maximise button being wrongly disabled 2023-07-12 18:56:50 +02:00
Ryan Cao
72f83c3ac4 feat: add hide menu bar items on macOS (#50)
Co-authored-by: V <vendicated@riseup.net>
2023-07-11 21:35:15 +02:00
Ryan Cao
13d87dc85e fix: improve app hiding functionality (#51) 2023-07-11 20:57:10 +02:00
Flag
0415cb77f7 feat: implement transparency for Windows (#46) 2023-07-11 20:45:30 +02:00
V
b4da701080 Bump to 0.2.6 2023-07-05 00:49:43 +02:00
V
ed2361ff22 Don't show Menubar on alt press
Closes #45
2023-07-03 22:25:11 +02:00
V
c587e93c56 oop oop 2023-07-03 22:22:35 +02:00
V
6ba0896a6e Implement Screenshare Quality & fps settings 2023-07-03 22:12:26 +02:00
V
843b57e03e Support invite modal launch from web 2023-06-30 18:57:28 +02:00
V
49fb4c68b6 Updater Popup: Add Changelog; Make about page prettier 2023-06-26 01:42:51 +02:00
V
477ecbb4ba Cleanup 2023-06-26 00:41:52 +02:00
V
4abce9d084 Fix some Context Menus being broken by regression 2023-06-26 00:05:08 +02:00
V
7f54858b27 Add SpellCheck toggle in textarea context menu 2023-06-25 16:48:46 +02:00
V
be176fab71 Update README.md (#42) 2023-06-25 04:55:06 +02:00
V
e60f04bb79 Fix DevTools context menu 2023-06-25 04:20:26 +02:00
V
17f1c4ff6f Bump to v0.2.4 2023-06-25 03:45:44 +02:00
V
a7ded71404 Add SpellCheck suggestions 2023-06-25 03:44:19 +02:00
V
31799ccfb0 Add Notification badge 2023-06-23 17:20:54 +02:00
V
dde696627e Bump to v0.2.3 2023-06-23 16:04:09 +02:00
V
50b2e864c2 Update UserAgents 2023-06-23 16:03:31 +02:00
V
dfa007669b arrpc: Install from GitHub instead of npm 2023-06-23 16:01:50 +02:00
V
4b27c67e83 Bump electron from v23 to v25 2023-06-23 15:56:26 +02:00
V
f58ed485a9 Improve defaults, Add Rich Presence to first launch confi 2023-06-22 15:55:04 +02:00
90 changed files with 3506 additions and 1282 deletions

View File

@@ -4,3 +4,5 @@
# all permissions at the defaults (public repos read only, 0 permissions):
# https://github.com/settings/personal-access-tokens/new
GITHUB_TOKEN=
ELECTRON_LAUNCH_FLAGS="--ozone-platform-hint=auto --enable-webrtc-pipewire-capturer --enable-features=WaylandWindowDecorations"

View File

@@ -12,17 +12,32 @@ jobs:
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
include:
- os: macos-latest
platform: mac
- os: ubuntu-latest
platform: linux
- os: windows-latest
platform: windows
steps:
- name: Check out Git repository
uses: actions/checkout@v3
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json
- uses: actions/setup-node@v3
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Run Electron Builder
uses: samuelmeuli/action-electron-builder@e4b12cd06ddf023422f1ac4e39632bd76f6e6928
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE: true
run: |
pnpm electron-builder --${{ matrix.platform }} --publish always
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

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
node_modules
.env
.DS_Store
.idea/
.pnpm-store/

View File

@@ -20,5 +20,6 @@
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
},
"cSpell.words": ["Vesktop"]
}

View File

@@ -1,25 +1,25 @@
# Vencord Desktop
# Vesktop
Vencord Desktop is a cross platform desktop app aiming to give you a snappier Discord experience with Vencord pre-installed
Vencord Desktop is currently in beta
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**:
- Global Keybinds
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
### Windows
Download and run Vencord-Desktop-Setup-VERSION.exe from [releases](https://github.com/Vencord/Desktop/releases/latest)
Download and run Vesktop-Setup-VERSION.exe from [releases](https://github.com/Vencord/Vesktop/releases/latest)
### Mac
Download and run Vencord-Desktop-VERSION.dmg from [releases](https://github.com/Vencord/Desktop/releases/latest)
Download and run Vesktop-VERSION.dmg from [releases](https://github.com/Vencord/Vesktop/releases/latest)
### Linux
@@ -29,15 +29,15 @@ Install [vencord-desktop-git](https://aur.archlinux.org/packages/vencord-desktop
#### Ubuntu/Debian based
Download Vencord-Desktop-VERSION.deb from [releases](https://github.com/Vencord/Desktop/releases/latest)
Download Vesktop-VERSION.deb from [releases](https://github.com/Vencord/Vesktop/releases/latest)
#### Fedora/RHEL based
Download Vencord-Desktop-VERSION.rpm from [releases](https://github.com/Vencord/Desktop/releases/latest)
Download Vesktop-VERSION.rpm from [releases](https://github.com/Vencord/Vesktop/releases/latest)
#### Other
Either download Vencord-Desktop-VERSION.AppImage and just run it directly or grab Vencord-Desktop-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
@@ -46,8 +46,8 @@ A flatpak is planned, if you want packages for other repos, feel free to create
Packaging will create builds in the dist/ folder. You can then install them like mentioned above or distribute them
```sh
git clone https://github.com/Vencord/Desktop
cd Desktop
git clone https://github.com/Vencord/Vesktop
cd Vesktop
# Install Dependencies
pnpm i

BIN
build/icon.icns Normal file

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{
"name": "VencordDesktop",
"version": "0.2.2",
"version": "0.4.4",
"private": true,
"description": "",
"keywords": [],
@@ -23,41 +23,44 @@
"watch": "pnpm build --watch"
},
"dependencies": {
"arrpc": "^3.1.0"
"arrpc": "github:OpenAsar/arrpc#3e22fd776273afaa4a80c51deb86077ffdd4d2ae"
},
"optionalDependencies": {
"@vencord/venmic": "^2.1.3"
},
"devDependencies": {
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
"@types/node": "^18.15.11",
"@types/react": "^18.0.33",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@types/node": "^20.10.0",
"@types/react": "^18.2.39",
"@typescript-eslint/eslint-plugin": "^6.13.1",
"@typescript-eslint/parser": "^6.13.1",
"@vencord/types": "^0.1.2",
"dotenv": "^16.0.3",
"electron": "^23.2.0",
"electron-builder": "^23.6.0",
"esbuild": "^0.17.14",
"eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",
"dotenv": "^16.3.1",
"electron": "^27.1.2",
"electron-builder": "^24.9.1",
"esbuild": "^0.19.8",
"eslint": "^8.54.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-license-header": "^0.6.0",
"eslint-plugin-path-alias": "^1.0.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"prettier": "^2.8.7",
"eslint-plugin-unused-imports": "^3.0.0",
"prettier": "^3.1.0",
"source-map-support": "^0.5.21",
"tsx": "^3.12.6",
"type-fest": "^3.8.0",
"typescript": "^5.0.2"
"tsx": "^4.6.0",
"type-fest": "^4.8.2",
"typescript": "^5.3.2"
},
"packageManager": "pnpm@8.1.1",
"packageManager": "pnpm@8.11.0",
"engines": {
"node": ">=18",
"pnpm": ">=8"
},
"build": {
"appId": "dev.vencord.desktop",
"productName": "Vencord Desktop",
"productName": "Vesktop",
"files": [
"!*",
"dist/js",
@@ -65,21 +68,48 @@
"package.json",
"LICENSE"
],
"beforePack": "scripts/build/sandboxFix.js",
"linux": {
"icon": "build/icon.icns",
"category": "Network",
"maintainer": "vendicated+vencord-desktop@riseup.net",
"maintainer": "vendicated+vesktop@riseup.net",
"target": [
"deb",
"tar.gz",
"rpm",
"AppImage"
{
"target": "deb",
"arch": [
"x64",
"arm64"
]
},
{
"target": "tar.gz",
"arch": [
"x64",
"arm64"
]
},
{
"target": "rpm",
"arch": [
"x64",
"arm64"
]
},
{
"target": "AppImage",
"arch": [
"x64",
"arm64"
]
}
],
"desktop": {
"Name": "Vencord Desktop",
"Name": "Vesktop",
"GenericName": "Internet Messenger",
"Type": "Application",
"Categories": "Network;InstantMessaging;Chat;",
"Keywords": "discord;vencord;electron;chat;"
"Keywords": "discord;vencord;electron;chat;",
"StartupWMClass": "VencordDesktop"
}
},
"mac": {
@@ -92,7 +122,13 @@
]
}
],
"category": "Network"
"category": "Network",
"extendInfo": {
"NSMicrophoneUsageDescription": "This app needs access to the microphone",
"NSCameraUsageDescription": "This app needs access to the camera",
"com.apple.security.device.audio-input": true,
"com.apple.security.device.camera": true
}
},
"nsis": {
"include": "build/installer.nsh",
@@ -101,12 +137,11 @@
"win": {
"target": [
"nsis",
"portable"
"zip"
]
},
"publish": {
"provider": "github",
"releaseType": "release"
"provider": "github"
}
}
}

2389
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,11 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { BuildContext, BuildOptions, context } from "esbuild";
import { copyFile } from "fs/promises";
import vencordDep from "./vencordDep.mjs";
@@ -33,7 +34,23 @@ async function createContext(options: BuildOptions) {
contexts.push(await context(options));
}
async function copyVenmic() {
if (process.platform !== "linux") return;
return Promise.all([
copyFile(
"./node_modules/@vencord/venmic/prebuilds/venmic-addon-linux-x64/node-napi-v7.node",
"./static/dist/venmic-x64.node"
),
copyFile(
"./node_modules/@vencord/venmic/prebuilds/venmic-addon-linux-arm64/node-napi-v7.node",
"./static/dist/venmic-arm64.node"
)
]).catch(() => console.warn("Failed to copy venmic. Building without venmic support"));
}
await Promise.all([
copyVenmic(),
createContext({
...NodeCommonOpts,
entryPoints: ["src/main/index.ts"],
@@ -54,15 +71,13 @@ await Promise.all([
}),
createContext({
...CommonOpts,
globalName: "VencordDesktop",
globalName: "Vesktop",
entryPoints: ["src/renderer/index.ts"],
outfile: "dist/js/renderer.js",
format: "iife",
inject: ["./scripts/build/injectReact.mjs"],
jsxFactory: "VencordCreateElement",
jsxFragment: "VencordFragment",
// Work around https://github.com/evanw/esbuild/issues/2460
tsconfig: "./scripts/build/tsconfig.esbuild.json",
external: ["@vencord/types/*"],
plugins: [vencordDep],
footer: { js: "//# sourceURL=VCDRenderer" }

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

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

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

View File

@@ -1,5 +1,5 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -8,4 +8,4 @@ import "./utils/dotenv";
import { spawnNodeModuleBin } from "./utils/spawn.mjs";
spawnNodeModuleBin("electron", ["."]);
spawnNodeModuleBin("electron", [".", ...(process.env.ELECTRON_LAUNCH_FLAGS?.split(" ") ?? [])]);

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

8
src/globals.d.ts vendored
View File

@@ -1,13 +1,13 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
declare global {
export var VencordDesktopNative: typeof import("preload/VencordDesktopNative").VencordDesktopNative;
export var VencordDesktop: typeof import("renderer/index");
export var vcdLS: typeof localStorage;
export var VesktopNative: typeof import("preload/VesktopNative").VesktopNative;
export var Vesktop: typeof import("renderer/index");
export var VCDP: any;
export var IS_DEV: boolean;
}

View File

@@ -1,13 +1,12 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { app, BrowserWindow } from "electron";
import { readFileSync } from "fs";
import { BrowserWindow } from "electron";
import { join } from "path";
import { ICON_PATH, STATIC_DIR } from "shared/paths";
import { ICON_PATH, VIEW_DIR } from "shared/paths";
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
@@ -15,14 +14,15 @@ export function createAboutWindow() {
const about = new BrowserWindow({
center: true,
autoHideMenuBar: true,
icon: ICON_PATH
icon: ICON_PATH,
webPreferences: {
preload: join(__dirname, "updaterPreload.js")
}
});
makeLinksOpenExternally(about);
const html = readFileSync(join(STATIC_DIR, "about.html"), "utf-8").replaceAll("%VERSION%", app.getVersion());
about.loadURL("data:text/html;charset=utf-8," + html);
about.loadFile(join(VIEW_DIR, "about.html"));
return about;
}

56
src/main/appBadge.ts Normal file
View File

@@ -0,0 +1,56 @@
/*
* 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, NativeImage, nativeImage } from "electron";
import { join } from "path";
import { BADGE_DIR } from "shared/paths";
const imgCache = new Map<number, NativeImage>();
function loadBadge(index: number) {
const cached = imgCache.get(index);
if (cached) return cached;
const img = nativeImage.createFromPath(join(BADGE_DIR, `${index}.ico`));
imgCache.set(index, img);
return img;
}
let lastIndex: null | number = -1;
export function setBadgeCount(count: number) {
switch (process.platform) {
case "linux":
if (count === -1) count = 0;
app.setBadgeCount(count);
break;
case "darwin":
if (count === 0) {
app.dock.setBadge("");
break;
}
app.dock.setBadge(count === -1 ? "•" : count.toString());
break;
case "win32":
const [index, description] = getBadgeIndexAndDescription(count);
if (lastIndex === index) break;
lastIndex = index;
// circular import shenanigans
const { mainWin } = require("./mainWindow") as typeof import("./mainWindow");
mainWin.setOverlayIcon(index === null ? null : loadBadge(index), description);
break;
}
}
function getBadgeIndexAndDescription(count: number): [number | null, string] {
if (count === -1) return [11, "Unread Messages"];
if (count === 0) return [null, "No Notifications"];
const index = Math.max(1, Math.min(count, 10));
return [index, `${index} Notification`];
}

View File

@@ -1,21 +1,38 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import Server from "arrpc";
import { send as sendToBridge } from "arrpc/src/bridge";
import { IpcEvents } from "shared/IpcEvents";
import { mainWin } from "./mainWindow";
import { Settings } from "./settings";
let server: any;
const inviteCodeRegex = /^(\w|-)+$/;
export async function initArRPC() {
if (server || !Settings.store.arRPC) return;
server = await new Server();
server.on("activity", sendToBridge);
try {
server = await new Server();
server.on("activity", (data: any) => mainWin.webContents.send(IpcEvents.ARRPC_ACTIVITY, JSON.stringify(data)));
server.on("invite", (invite: string, callback: (valid: boolean) => void) => {
invite = String(invite);
if (!inviteCodeRegex.test(invite)) return callback(false);
mainWin.webContents
// Safety: Result of JSON.stringify should always be safe to equal
// Also, just to be super super safe, invite is regex validated above
.executeJavaScript(`Vesktop.openInviteModal(${JSON.stringify(invite)})`)
.then(callback);
});
} catch (e) {
console.error("Failed to start arRPC server", e);
}
}
Settings.addChangeListener("arRPC", initArRPC);

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -11,16 +11,31 @@ 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_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
export const VENCORD_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json");
export const VENCORD_THEMES_DIR = join(DATA_DIR, "themes");
// needs to be inline require because of circular dependency
// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised
export const VENCORD_FILES_DIR =
(require("./settings") as typeof import("./settings")).Settings.store.vencordDir || join(DATA_DIR, "vencordDist");
export const USER_AGENT = `VencordDesktop/${app.getVersion()} (https://github.com/Vencord/Electron)`;
export const USER_AGENT = `Vesktop/${app.getVersion()} (https://github.com/Vencord/Vesktop)`;
// dimensions shamelessly stolen from Discord Desktop :3
export const MIN_WIDTH = 940;
export const MIN_HEIGHT = 500;
export const DEFAULT_WIDTH = 1280;
export const DEFAULT_HEIGHT = 720;
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

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -9,18 +9,20 @@ import { BrowserWindow } from "electron/main";
import { copyFileSync, mkdirSync, readdirSync } from "fs";
import { join } from "path";
import { SplashProps } from "shared/browserWinProperties";
import { STATIC_DIR } from "shared/paths";
import { ICON_PATH, VIEW_DIR } from "shared/paths";
import { autoStart } from "./autoStart";
import { DATA_DIR } from "./constants";
import { createWindows } from "./mainWindow";
import { Settings } from "./settings";
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
interface Data {
minimizeToTray: boolean;
discordBranch: "stable" | "canary" | "ptb";
autoStart: boolean;
importSettings: boolean;
richPresence: boolean;
}
export function createFirstLaunchTour() {
@@ -28,11 +30,14 @@ export function createFirstLaunchTour() {
...SplashProps,
frame: true,
autoHideMenuBar: true,
height: 420,
width: 550
height: 470,
width: 550,
icon: ICON_PATH
});
win.loadFile(join(STATIC_DIR, "first-launch.html"));
makeLinksOpenExternally(win);
win.loadFile(join(VIEW_DIR, "first-launch.html"));
win.webContents.addListener("console-message", (_e, _l, msg) => {
if (msg === "cancel") return app.exit();
@@ -42,6 +47,7 @@ export function createFirstLaunchTour() {
Settings.store.minimizeToTray = data.minimizeToTray;
Settings.store.discordBranch = data.discordBranch;
Settings.store.firstLaunch = false;
Settings.store.arRPC = data.richPresence;
if (data.autoStart) autoStart.enable();

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -9,10 +9,10 @@ import "./ipc";
import { app, BrowserWindow } from "electron";
import { checkUpdates } from "updater/main";
import { ICON_PATH } from "../shared/paths";
import { DATA_DIR } from "./constants";
import { createFirstLaunchTour } from "./firstLaunch";
import { createWindows, mainWin } from "./mainWindow";
import { registerMediaPermissionsHandler } from "./mediaPermissions";
import { registerScreenShareHandler } from "./screenShare";
import { Settings } from "./settings";
@@ -24,20 +24,24 @@ if (IS_DEV) {
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
function init() {
// <-- BEGIN COPY PASTED FROM DISCORD -->
const { disableSmoothScroll } = Settings.store;
if (disableSmoothScroll) {
app.commandLine.appendSwitch("disable-smooth-scrolling");
}
// work around chrome 66 disabling autoplay by default
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
// WinRetrieveSuggestionsOnlyOnDemand: Work around electron 13 bug w/ async spellchecking on Windows.
// HardwareMediaKeyHandling,MediaSessionService: Prevent Discord from registering as a media service.
//
// WidgetLayering (Vencord Added): Fix DevTools context menus https://github.com/electron/electron/issues/38790
app.commandLine.appendSwitch(
"disable-features",
"WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService"
"WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService,WidgetLayering"
);
// <-- END COPY PASTED FROM DISCORD -->
app.on("second-instance", (_event, _cmdLine, _cwd, data: any) => {
if (data.IS_DEV) app.quit();
else if (mainWin) {
@@ -50,9 +54,10 @@ function init() {
app.whenReady().then(async () => {
checkUpdates();
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.desktop");
else if (process.platform === "darwin") app.dock.setIcon(ICON_PATH);
registerScreenShareHandler();
registerMediaPermissionsHandler();
bootstrap();
app.on("activate", () => {
@@ -63,10 +68,10 @@ function init() {
if (!app.requestSingleInstanceLock({ IS_DEV })) {
if (IS_DEV) {
console.log("Vencord Desktop is already running. Quitting previous instance...");
console.log("Vesktop is already running. Quitting previous instance...");
init();
} else {
console.log("Vencord Desktop is already running. Quitting...");
console.log("Vesktop is already running. Quitting...");
app.quit();
}
} else {

View File

@@ -1,73 +1,92 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { app, dialog, ipcMain, session, shell } from "electron";
import { existsSync, readFileSync, watch } from "fs";
if (process.platform === "linux") import("./virtmic");
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 { release } from "os";
import { join } from "path";
import { debounce } from "shared/utils/debounce";
import { IpcEvents } from "../shared/IpcEvents";
import { setBadgeCount } from "./appBadge";
import { autoStart } from "./autoStart";
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE } from "./constants";
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
import { mainWin } from "./mainWindow";
import { Settings } from "./settings";
import { handle, handleSync } from "./utils/ipcWrappers";
import { isValidVencordInstall } from "./utils/vencordLoader";
ipcMain.on(IpcEvents.GET_VENCORD_PRELOAD_FILE, e => {
e.returnValue = join(VENCORD_FILES_DIR, "preload.js");
});
handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js"));
handleSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, () =>
readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8")
);
ipcMain.on(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, e => {
e.returnValue = readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8");
});
handleSync(IpcEvents.GET_RENDERER_SCRIPT, () => readFileSync(join(__dirname, "renderer.js"), "utf-8"));
handleSync(IpcEvents.GET_RENDERER_CSS_FILE, () => join(__dirname, "renderer.css"));
ipcMain.on(IpcEvents.GET_RENDERER_SCRIPT, e => {
e.returnValue = readFileSync(join(__dirname, "renderer.js"), "utf-8");
});
handleSync(IpcEvents.GET_SETTINGS, () => Settings.plain);
handleSync(IpcEvents.GET_VERSION, () => app.getVersion());
ipcMain.on(IpcEvents.GET_RENDERER_CSS_FILE, e => {
e.returnValue = join(__dirname, "renderer.css");
});
handleSync(
IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY,
() => process.platform === "win32" && Number(release().split(".").pop()) >= 22621
);
ipcMain.on(IpcEvents.GET_SETTINGS, e => {
e.returnValue = Settings.plain;
});
handleSync(IpcEvents.AUTOSTART_ENABLED, () => autoStart.isEnabled());
handle(IpcEvents.ENABLE_AUTOSTART, autoStart.enable);
handle(IpcEvents.DISABLE_AUTOSTART, autoStart.disable);
ipcMain.on(IpcEvents.GET_VERSION, e => {
e.returnValue = app.getVersion();
});
ipcMain.on(IpcEvents.AUTOSTART_ENABLED, e => {
e.returnValue = autoStart.isEnabled();
});
ipcMain.handle(IpcEvents.ENABLE_AUTOSTART, autoStart.enable);
ipcMain.handle(IpcEvents.DISABLE_AUTOSTART, autoStart.disable);
ipcMain.handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: string) => {
handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: string) => {
Settings.setData(settings, path);
});
ipcMain.handle(IpcEvents.RELAUNCH, () => {
app.relaunch();
handle(IpcEvents.RELAUNCH, () => {
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();
});
ipcMain.handle(IpcEvents.SHOW_ITEM_IN_FOLDER, (_, path) => {
handle(IpcEvents.SHOW_ITEM_IN_FOLDER, (_, path) => {
shell.showItemInFolder(path);
});
ipcMain.handle(IpcEvents.FOCUS, e => {
e.sender.focus();
handle(IpcEvents.FOCUS, () => {
if (process.platform === "win32") mainWin.minimize(); // Windows is weird
mainWin.restore();
mainWin.show();
});
ipcMain.handle(IpcEvents.CLOSE, e => {
e.sender.close();
handle(IpcEvents.CLOSE, e => {
(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 available = ses.availableSpellCheckerLanguages;
@@ -75,20 +94,28 @@ ipcMain.handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => {
if (applicable.length) ses.setSpellCheckerLanguages(applicable);
});
ipcMain.handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
handle(IpcEvents.SPELLCHECK_REPLACE_MISSPELLING, (e, word: string) => {
e.sender.replaceMisspelling(word);
});
handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => {
e.sender.session.addWordToSpellCheckerDictionary(word);
});
handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
const res = await dialog.showOpenDialog(mainWin!, {
properties: ["openDirectory"]
});
if (!res.filePaths.length) return "cancelled";
const dir = res.filePaths[0];
for (const file of ["vencordDesktopMain.js", "preload.js", "vencordDesktopRenderer.js", "renderer.css"]) {
if (!existsSync(join(dir, file))) return "invalid";
}
if (!isValidVencordInstall(dir)) return "invalid";
return dir;
});
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
function readCss() {
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
}
@@ -103,3 +130,12 @@ open(VENCORD_QUICKCSS_FILE, "a+").then(fd => {
}, 50)
);
});
mkdirSync(VENCORD_THEMES_DIR, { recursive: true });
watch(
VENCORD_THEMES_DIR,
{ persistent: false },
debounce(() => {
mainWin?.webContents.postMessage("VencordThemeUpdate", void 0);
})
);

View File

@@ -1,31 +1,77 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { app, BrowserWindow, BrowserWindowConstructorOptions, Menu, Tray } from "electron";
import {
app,
BrowserWindow,
BrowserWindowConstructorOptions,
dialog,
Menu,
MenuItemConstructorOptions,
nativeTheme,
Tray
} from "electron";
import { rm } from "fs/promises";
import { join } from "path";
import { IpcEvents } from "shared/IpcEvents";
import { isTruthy } from "shared/utils/guards";
import { once } from "shared/utils/once";
import type { SettingsStore } from "shared/utils/SettingsStore";
import { ICON_PATH } from "../shared/paths";
import { createAboutWindow } from "./about";
import { initArRPC } from "./arrpc";
import { DEFAULT_HEIGHT, DEFAULT_WIDTH, MIN_HEIGHT, MIN_WIDTH, VENCORD_FILES_DIR } from "./constants";
import {
DATA_DIR,
DEFAULT_HEIGHT,
DEFAULT_WIDTH,
MessageBoxChoice,
MIN_HEIGHT,
MIN_WIDTH,
UserAgent,
VENCORD_FILES_DIR
} from "./constants";
import { Settings, VencordSettings } from "./settings";
import { createSplashWindow } from "./splash";
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS";
import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader";
let isQuitting = false;
let tray: Tray;
applyDeckKeyboardFix();
app.on("before-quit", () => {
isQuitting = true;
});
export let mainWin: BrowserWindow;
function makeSettingsListenerHelpers<O extends object>(o: SettingsStore<O>) {
const listeners = new Map<(data: any) => void, PropertyKey>();
const addListener: typeof o.addChangeListener = (path, cb) => {
listeners.set(cb, path);
o.addChangeListener(path, cb);
};
const removeAllListeners = () => {
for (const [listener, path] of listeners) {
o.removeChangeListener(path as any, listener);
}
listeners.clear();
};
return [addListener, removeAllListeners] as const;
}
const [addSettingsListener, removeSettingsListeners] = makeSettingsListenerHelpers(Settings);
const [addVencordSettingsListener, removeVencordSettingsListeners] = makeSettingsListenerHelpers(VencordSettings);
function initTray(win: BrowserWindow) {
const trayMenu = Menu.buildFromTemplate([
{
@@ -47,6 +93,12 @@ function initTray(win: BrowserWindow) {
app.quit();
}
},
{
label: "Reset Vesktop",
async click() {
await clearData(win);
}
},
{
type: "separator"
},
@@ -58,7 +110,7 @@ function initTray(win: BrowserWindow) {
}
},
{
label: "Quit Vencord Desktop",
label: "Quit Vesktop",
click() {
isQuitting = true;
app.quit();
@@ -67,7 +119,7 @@ function initTray(win: BrowserWindow) {
]);
tray = new Tray(ICON_PATH);
tray.setToolTip("Vencord Desktop");
tray.setToolTip("Vesktop");
tray.setContextMenu(trayMenu);
tray.on("click", () => win.show());
@@ -80,62 +132,128 @@ function initTray(win: BrowserWindow) {
});
}
async function clearData(win: BrowserWindow) {
const { response } = await dialog.showMessageBox(win, {
message: "Are you sure you want to reset Vesktop?",
detail: "This will log you out, clear caches and reset all your settings!\n\nVesktop will automatically restart after this operation.",
buttons: ["Yes", "No"],
cancelId: MessageBoxChoice.Cancel,
defaultId: MessageBoxChoice.Default,
type: "warning"
});
if (response === MessageBoxChoice.Cancel) return;
win.close();
await win.webContents.session.clearStorageData();
await win.webContents.session.clearCache();
await win.webContents.session.clearCodeCaches({});
await rm(DATA_DIR, { force: true, recursive: true });
app.relaunch();
app.quit();
}
type MenuItemList = Array<MenuItemConstructorOptions | false>;
function initMenuBar(win: BrowserWindow) {
const isWindows = process.platform === "win32";
const isDarwin = process.platform === "darwin";
const wantCtrlQ = !isWindows || VencordSettings.store.winCtrlQ;
const subMenu = [
{
label: "About Vesktop",
click: createAboutWindow
},
{
label: "Force Update Vencord",
async click() {
await downloadVencordFiles();
app.relaunch();
app.quit();
},
toolTip: "Vesktop will automatically restart after this operation"
},
{
label: "Reset Vesktop",
async click() {
await clearData(win);
},
toolTip: "Vesktop will automatically restart after this operation"
},
{
label: "Relaunch",
accelerator: "CmdOrCtrl+Shift+R",
click() {
app.relaunch();
app.quit();
}
},
...(!isDarwin
? []
: ([
{
type: "separator"
},
{
label: "Settings",
accelerator: "CmdOrCtrl+,",
async click() {
mainWin.webContents.executeJavaScript(
"Vencord.Webpack.Common.SettingsRouter.open('My Account')"
);
}
},
{
type: "separator"
},
{
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",
accelerator: wantCtrlQ ? "CmdOrCtrl+Q" : void 0,
visible: !isWindows,
role: "quit",
click() {
app.quit();
}
},
isWindows && {
label: "Quit",
accelerator: "Alt+F4",
role: "quit",
click() {
app.quit();
}
},
// See https://github.com/electron/electron/issues/14742 and https://github.com/electron/electron/issues/5256
{
label: "Zoom in (hidden, hack for Qwertz and others)",
accelerator: "CmdOrCtrl+=",
role: "zoomIn",
visible: false
}
] satisfies MenuItemList;
const menu = Menu.buildFromTemplate([
{
label: "Vencord Desktop",
label: "Vesktop",
role: "appMenu",
submenu: [
{
label: "About Vencord Desktop",
click: createAboutWindow
},
{
label: "Force Update Vencord",
async click() {
await downloadVencordFiles();
app.relaunch();
app.quit();
},
toolTip: "Vencord Desktop will automatically restart after this operation"
},
{
label: "Relaunch",
accelerator: "CmdOrCtrl+Shift+R",
click() {
app.relaunch();
app.quit();
}
},
{
label: "Quit",
accelerator: wantCtrlQ ? "CmdOrCtrl+Q" : void 0,
visible: !isWindows,
role: "quit",
click() {
app.quit();
}
},
{
label: "Quit",
accelerator: isWindows ? "Alt+F4" : void 0,
visible: isWindows,
role: "quit",
click() {
app.quit();
}
},
// See https://github.com/electron/electron/issues/14742 and https://github.com/electron/electron/issues/5256
{
label: "Zoom in (hidden, hack for Qwertz and others)",
accelerator: "CmdOrCtrl+=",
role: "zoomIn",
visible: false
}
]
submenu: subMenu.filter(isTruthy)
},
{ role: "fileMenu" },
{ role: "editMenu" },
@@ -147,6 +265,9 @@ function initMenuBar(win: BrowserWindow) {
}
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 options = {
@@ -167,6 +288,29 @@ function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
return options;
}
function getDarwinOptions(): BrowserWindowConstructorOptions {
const options = {
titleBarStyle: "hidden",
trafficLightPosition: { x: 10, y: 10 }
} as BrowserWindowConstructorOptions;
const { splashTheming, splashBackground } = Settings.store;
const { macosTranslucency } = VencordSettings.store;
if (macosTranslucency) {
options.vibrancy = "sidebar";
options.backgroundColor = "#ffffff00";
} else {
if (splashTheming) {
options.backgroundColor = splashBackground;
} else {
options.backgroundColor = nativeTheme.shouldUseDarkColors ? "#313338" : "#ffffff";
}
}
return options;
}
function initWindowBoundsListeners(win: BrowserWindow) {
const saveState = () => {
Settings.store.maximized = win.isMaximized();
@@ -186,11 +330,11 @@ function initWindowBoundsListeners(win: BrowserWindow) {
}
function initSettingsListeners(win: BrowserWindow) {
Settings.addChangeListener("tray", enable => {
addSettingsListener("tray", enable => {
if (enable) initTray(win);
else tray?.destroy();
});
Settings.addChangeListener("disableMinSize", disable => {
addSettingsListener("disableMinSize", disable => {
if (disable) {
// 0 no work
win.setMinimumSize(1, 1);
@@ -205,7 +349,7 @@ function initSettingsListeners(win: BrowserWindow) {
}
});
VencordSettings.addChangeListener("macosTranslucency", enabled => {
addVencordSettingsListener("macosTranslucency", enabled => {
if (enabled) {
win.setVibrancy("sidebar");
win.setBackgroundColor("#ffffff00");
@@ -214,36 +358,62 @@ function initSettingsListeners(win: BrowserWindow) {
win.setBackgroundColor("#ffffff");
}
});
addSettingsListener("enableMenu", enabled => {
win.setAutoHideMenuBar(enabled ?? false);
});
}
function initSpellCheck(win: BrowserWindow) {
win.webContents.on("context-menu", (_, data) => {
win.webContents.send(IpcEvents.SPELLCHECK_RESULT, data.misspelledWord, data.dictionarySuggestions);
});
}
function createMainWindow() {
// Clear up previous settings listeners
removeSettingsListeners();
removeVencordSettingsListeners();
const { staticTitle, transparencyOption, enableMenu, discordWindowsTitleBar } = Settings.store;
const { frameless } = VencordSettings.store;
const noFrame = frameless === true || (process.platform === "win32" && discordWindowsTitleBar === true);
const win = (mainWin = new BrowserWindow({
show: false,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: false,
sandbox: false,
contextIsolation: true,
devTools: true,
preload: join(__dirname, "preload.js")
preload: join(__dirname, "preload.js"),
spellcheck: true
},
icon: ICON_PATH,
frame: VencordSettings.store.frameless !== true,
...(Settings.store.staticTitle ? { title: "Vencord" } : {}),
...(VencordSettings.store.macosTranslucency
? {
vibrancy: "sidebar",
backgroundColor: "#ffffff00"
}
: {}),
...getWindowBoundsOptions()
frame: !noFrame,
...(transparencyOption &&
transparencyOption !== "none" && {
backgroundColor: "#00000000",
backgroundMaterial: transparencyOption,
transparent: true
}),
...(staticTitle && { title: "Vesktop" }),
...(process.platform === "darwin" && getDarwinOptions()),
...getWindowBoundsOptions(),
autoHideMenuBar: enableMenu
}));
win.setMenuBarVisibility(false);
win.on("close", e => {
if (isQuitting || Settings.store.minimizeToTray === false || Settings.store.tray === false) return;
const useTray = !isDeckGameMode && Settings.store.minimizeToTray !== false && Settings.store.tray !== false;
if (isQuitting || (process.platform !== "darwin" && !useTray)) return;
e.preventDefault();
win.hide();
if (process.platform === "darwin") app.hide();
else win.hide();
return false;
});
@@ -251,14 +421,13 @@ function createMainWindow() {
if (Settings.store.staticTitle) win.on("page-title-updated", e => e.preventDefault());
initWindowBoundsListeners(win);
if (Settings.store.tray ?? true) initTray(win);
if (!isDeckGameMode && (Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win);
initMenuBar(win);
makeLinksOpenExternally(win);
initSettingsListeners(win);
initSpellCheck(win);
win.webContents.setUserAgent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
);
win.webContents.setUserAgent(UserAgent);
const subdomain =
Settings.store.discordBranch === "canary" || Settings.store.discordBranch === "ptb"
@@ -274,19 +443,27 @@ const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDeskto
export async function createWindows() {
const splash = createSplashWindow();
// SteamOS letterboxes and scales it terribly, so just full screen it
if (isDeckGameMode) splash.setFullScreen(true);
await ensureVencordFiles();
runVencordMain();
mainWin = createMainWindow();
mainWin.once("ready-to-show", () => {
mainWin.webContents.on("did-finish-load", () => {
splash.destroy();
mainWin!.show();
if (Settings.store.maximized) {
if (Settings.store.maximized && !isDeckGameMode) {
mainWin!.maximize();
}
if (isDeckGameMode) {
// always use entire display
mainWin!.setFullScreen(true);
askToApplySteamLayout(mainWin);
}
});
initArRPC();

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

@@ -1,15 +1,20 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { desktopCapturer, ipcMain, session, Streams } from "electron";
import { desktopCapturer, session, Streams } from "electron";
import type { StreamPick } from "renderer/components/ScreenSharePicker";
import { IpcEvents } from "shared/IpcEvents";
import { handle } from "./utils/ipcWrappers";
const isWayland =
process.platform === "linux" && (process.env.XDG_SESSION_TYPE === "wayland" || !!process.env.WAYLAND_DISPLAY);
export function registerScreenShareHandler() {
ipcMain.handle(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, async (_, id: string) => {
handle(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, async (_, id: string) => {
const sources = await desktopCapturer.getSources({
types: ["window", "screen"],
thumbnailSize: {
@@ -21,13 +26,19 @@ export function registerScreenShareHandler() {
});
session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => {
const sources = await desktopCapturer.getSources({
types: ["window", "screen"],
thumbnailSize: {
width: 176,
height: 99
}
});
// request full resolution on wayland right away because we always only end up with one result anyway
const width = isWayland ? 1920 : 176;
const sources = await desktopCapturer
.getSources({
types: ["window", "screen"],
thumbnailSize: {
width,
height: width * (9 / 16)
}
})
.catch(err => console.error("Error during screenshare picker", err));
if (!sources) return callback({});
const data = sources.map(({ id, name, thumbnail }) => ({
id,
@@ -35,10 +46,28 @@ export function registerScreenShareHandler() {
url: thumbnail.toDataURL()
}));
if (isWayland) {
const video = data[0];
if (video) {
const stream = await request.frame
.executeJavaScript(
`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify([video])},true)`
)
.catch(() => null);
if (stream === null) return callback({});
}
callback(video ? { video: sources[0] } : {});
return;
}
const choice = await request.frame
.executeJavaScript(`VencordDesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`)
.executeJavaScript(`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`)
.then(e => e as StreamPick)
.catch(() => null);
.catch(e => {
console.error("Error during screenshare picker", e);
return null;
});
if (!choice) return callback({});

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -33,5 +33,5 @@ function loadSettings<T extends object = any>(file: string, name: string) {
return store;
}
export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vencord Desktop");
export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vesktop");
export const VencordSettings = loadSettings<any>(VENCORD_SETTINGS_FILE, "Vencord");

View File

@@ -1,18 +1,38 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { BrowserWindow } from "electron";
import { join } from "path";
import { SplashProps } from "shared/browserWinProperties";
import { STATIC_DIR } from "shared/paths";
import { ICON_PATH, VIEW_DIR } from "shared/paths";
import { Settings } from "./settings";
export function createSplashWindow() {
const splash = new BrowserWindow(SplashProps);
const splash = new BrowserWindow({
...SplashProps,
icon: ICON_PATH
});
splash.loadFile(join(STATIC_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;
}

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

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

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

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

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -13,7 +13,12 @@ import { downloadFile, simpleGet } from "./http";
const API_BASE = "https://api.github.com";
const FILES_TO_DOWNLOAD = ["vencordDesktopMain.js", "preload.js", "vencordDesktopRenderer.js", "renderer.css"];
export const FILES_TO_DOWNLOAD = [
"vencordDesktopMain.js",
"vencordDesktopPreload.js",
"vencordDesktopRenderer.js",
"vencordDesktopRenderer.css"
];
export interface ReleaseData {
name: string;
@@ -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() {
if (existsSync(join(VENCORD_FILES_DIR, "vencordDesktopMain.js"))) return;
if (isValidVencordInstall(VENCORD_FILES_DIR)) return;
mkdirSync(VENCORD_FILES_DIR, { recursive: true });
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

@@ -1,41 +0,0 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import type { Settings } from "shared/settings";
import type { LiteralUnion } from "type-fest";
import { IpcEvents } from "../shared/IpcEvents";
import { invoke, sendSync } from "./typedIpcs";
export const VencordDesktopNative = {
app: {
relaunch: () => invoke<void>(IpcEvents.RELAUNCH),
getVersion: () => sendSync<void>(IpcEvents.GET_VERSION)
},
autostart: {
isEnabled: () => sendSync<boolean>(IpcEvents.AUTOSTART_ENABLED),
enable: () => invoke<void>(IpcEvents.ENABLE_AUTOSTART),
disable: () => invoke<void>(IpcEvents.DISABLE_AUTOSTART)
},
fileManager: {
showItemInFolder: (path: string) => invoke<void>(IpcEvents.SHOW_ITEM_IN_FOLDER, path),
selectVencordDir: () => invoke<LiteralUnion<"cancelled" | "invalid", string>>(IpcEvents.SELECT_VENCORD_DIR)
},
settings: {
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),
set: (settings: Settings, path?: string) => invoke<void>(IpcEvents.SET_SETTINGS, settings, path)
},
spellcheck: {
setLanguages: (languages: readonly string[]) => invoke<void>(IpcEvents.SPELLCHECK_SET_LANGUAGES, languages)
// todo: perhaps add ways to learn words
},
win: {
focus: () => invoke<void>(IpcEvents.FOCUS)
},
capturer: {
getLargeThumbnail: (id: string) => invoke<string>(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, id)
}
};

View File

@@ -0,0 +1,75 @@
/*
* 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 { ipcRenderer } from "electron";
import type { Settings } from "shared/settings";
import type { LiteralUnion } from "type-fest";
import { IpcEvents } from "../shared/IpcEvents";
import { invoke, sendSync } from "./typedIpc";
type SpellCheckerResultCallback = (word: string, suggestions: string[]) => void;
const spellCheckCallbacks = new Set<SpellCheckerResultCallback>();
ipcRenderer.on(IpcEvents.SPELLCHECK_RESULT, (_, w: string, s: string[]) => {
spellCheckCallbacks.forEach(cb => cb(w, s));
});
export const VesktopNative = {
app: {
relaunch: () => invoke<void>(IpcEvents.RELAUNCH),
getVersion: () => sendSync<void>(IpcEvents.GET_VERSION),
setBadgeCount: (count: number) => invoke<void>(IpcEvents.SET_BADGE_COUNT, count),
supportsWindowsTransparency: () => sendSync<boolean>(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY)
},
autostart: {
isEnabled: () => sendSync<boolean>(IpcEvents.AUTOSTART_ENABLED),
enable: () => invoke<void>(IpcEvents.ENABLE_AUTOSTART),
disable: () => invoke<void>(IpcEvents.DISABLE_AUTOSTART)
},
fileManager: {
showItemInFolder: (path: string) => invoke<void>(IpcEvents.SHOW_ITEM_IN_FOLDER, path),
selectVencordDir: () => invoke<LiteralUnion<"cancelled" | "invalid", string>>(IpcEvents.SELECT_VENCORD_DIR)
},
settings: {
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),
set: (settings: Settings, path?: string) => invoke<void>(IpcEvents.SET_SETTINGS, settings, path)
},
spellcheck: {
setLanguages: (languages: readonly string[]) => invoke<void>(IpcEvents.SPELLCHECK_SET_LANGUAGES, languages),
onSpellcheckResult(cb: SpellCheckerResultCallback) {
spellCheckCallbacks.add(cb);
},
offSpellcheckResult(cb: SpellCheckerResultCallback) {
spellCheckCallbacks.delete(cb);
},
replaceMisspelling: (word: string) => invoke<void>(IpcEvents.SPELLCHECK_REPLACE_MISSPELLING, word),
addToDictionary: (word: string) => invoke<void>(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, word)
},
win: {
focus: () => invoke<void>(IpcEvents.FOCUS),
close: () => invoke<void>(IpcEvents.CLOSE),
minimize: () => invoke<void>(IpcEvents.MINIMIZE),
maximize: () => invoke<void>(IpcEvents.MAXIMIZE)
},
capturer: {
getLargeThumbnail: (id: string) => invoke<string>(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, id)
},
/** only available on Linux. */
virtmic: {
list: () =>
invoke<{ ok: false; isGlibcxxToOld: boolean } | { ok: true; targets: string[] }>(IpcEvents.VIRT_MIC_LIST),
start: (targets: string[]) => 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

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -8,9 +8,9 @@ import { contextBridge, ipcRenderer, webFrame } from "electron";
import { readFileSync, watch } from "fs";
import { IpcEvents } from "../shared/IpcEvents";
import { VencordDesktopNative } from "./VencordDesktopNative";
import { VesktopNative } from "./VesktopNative";
contextBridge.exposeInMainWorld("VencordDesktopNative", VencordDesktopNative);
contextBridge.exposeInMainWorld("VesktopNative", VesktopNative);
require(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_FILE));
@@ -41,4 +41,4 @@ if (IS_DEV) {
}
// #endregion
VencordDesktopNative.spellcheck.setLanguages(window.navigator.languages);
VesktopNative.spellcheck.setLanguages(window.navigator.languages);

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

47
src/renderer/appBadge.ts Normal file
View File

@@ -0,0 +1,47 @@
/*
* 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 { filters, waitFor } from "@vencord/types/webpack";
import { RelationshipStore } from "@vencord/types/webpack/common";
import { Settings } from "./settings";
let GuildReadStateStore: any;
let NotificationSettingsStore: any;
export function setBadge() {
if (Settings.store.appBadge === false) return;
try {
const mentionCount = GuildReadStateStore.getTotalMentionCount();
const pendingRequests = RelationshipStore.getPendingCount();
const hasUnread = GuildReadStateStore.hasAnyUnread();
const disableUnreadBadge = NotificationSettingsStore.getDisableUnreadBadge();
let totalCount = mentionCount + pendingRequests;
if (!totalCount && hasUnread && !disableUnreadBadge) totalCount = -1;
VesktopNative.app.setBadgeCount(totalCount);
} catch (e) {
console.error(e);
}
}
let toFind = 3;
function waitForAndSubscribeToStore(name: string, cb?: (m: any) => void) {
waitFor(filters.byStoreName(name), store => {
cb?.(store);
store.addChangeListener(setBadge);
toFind--;
if (toFind === 0) setBadge();
});
}
waitForAndSubscribeToStore("GuildReadStateStore", store => (GuildReadStateStore = store));
waitForAndSubscribeToStore("NotificationSettingsStore", store => (NotificationSettingsStore = store));
waitForAndSubscribeToStore("RelationshipStore");

View File

@@ -1,21 +1,32 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import "./screenSharePicker.css";
import { classes, closeModal, Margins, Modals, openModal, useAwaiter } from "@vencord/types/utils";
import { findByPropsLazy } from "@vencord/types/webpack";
import { Button, Card, Forms, Switch, Text, useState } from "@vencord/types/webpack/common";
import { closeModal, Modals, openModal, useAwaiter } from "@vencord/types/utils";
import { findStoreLazy, onceReady } from "@vencord/types/webpack";
import {
Button,
Card,
FluxDispatcher,
Forms,
Select,
Switch,
Text,
UserStore,
useState
} from "@vencord/types/webpack/common";
import type { Dispatch, SetStateAction } from "react";
import { isWindows } from "renderer/utils";
import { addPatch } from "renderer/patches/shared";
import { isLinux, isWindows } from "renderer/utils";
const StreamResolutions = ["720", "1080", "1440", "Source"] as const;
const StreamResolutions = ["480", "720", "1080", "1440"] as const;
const StreamFps = ["15", "30", "60"] as const;
const WarningIconClasses = findByPropsLazy("warning", "error", "container");
const MediaEngineStore = findStoreLazy("MediaEngineStore");
export type StreamResolution = (typeof StreamResolutions)[number];
export type StreamFps = (typeof StreamFps)[number];
@@ -24,6 +35,7 @@ interface StreamSettings {
resolution: StreamResolution;
fps: StreamFps;
audio: boolean;
audioSource?: string;
}
export interface StreamPick extends StreamSettings {
@@ -36,18 +48,74 @@ interface Source {
url: string;
}
export function openScreenSharePicker(screens: Source[]) {
let currentSettings: StreamSettings | null = null;
addPatch({
patches: [
{
find: "this.localWant=",
replacement: {
match: /this.localWant=/,
replace: "$self.patchStreamQuality(this);$&"
}
}
],
patchStreamQuality(opts: any) {
if (!currentSettings) return;
const framerate = Number(currentSettings.fps);
const height = Number(currentSettings.resolution);
const width = Math.round(height * (16 / 9));
Object.assign(opts, {
bitrateMin: 500000,
bitrateMax: 8000000,
bitrateTarget: 600000
});
Object.assign(opts.capture, {
framerate,
width,
height,
pixelCount: height * width
});
}
});
if (isLinux) {
onceReady.then(() => {
FluxDispatcher.subscribe("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) => {
const key = openModal(
props => (
<ModalComponent
screens={screens}
modalProps={props}
submit={resolve}
submit={async v => {
didSubmit = true;
if (v.audioSource && v.audioSource !== "None") {
if (v.audioSource === "Entire System") {
await VesktopNative.virtmic.startSystem();
} else {
await VesktopNative.virtmic.start([v.audioSource]);
}
}
resolve(v);
}}
close={() => {
props.onClose();
reject("Aborted");
if (!didSubmit) reject("Aborted");
}}
skipPicker={skipPicker}
/>
),
{
@@ -78,16 +146,21 @@ function ScreenPicker({ screens, chooseScreen }: { screens: Source[]; chooseScre
function StreamSettings({
source,
settings,
setSettings
setSettings,
skipPicker
}: {
source: Source;
settings: StreamSettings;
setSettings: Dispatch<SetStateAction<StreamSettings>>;
skipPicker: boolean;
}) {
const [thumb] = useAwaiter(() => VencordDesktopNative.capturer.getLargeThumbnail(source.id), {
fallbackValue: source.url,
deps: [source.id]
});
const [thumb] = useAwaiter(
() => (skipPicker ? Promise.resolve(source.url) : VesktopNative.capturer.getLargeThumbnail(source.id)),
{
fallbackValue: source.url,
deps: [source.id]
}
);
return (
<div>
@@ -100,12 +173,6 @@ function StreamSettings({
<Forms.FormTitle>Stream Settings</Forms.FormTitle>
<Card className="vcd-screen-picker-card">
<Card className={classes(WarningIconClasses.container, WarningIconClasses.warning, Margins.bottom8)}>
<Forms.FormText>
Resolution and Frame Rate aren't implemented for now. Locked to 720p 30fps
</Forms.FormText>
</Card>
<div className="vcd-screen-picker-quality">
<section>
<Forms.FormTitle>Resolution</Forms.FormTitle>
@@ -154,23 +221,76 @@ function StreamSettings({
Stream With Audio
</Switch>
)}
{isLinux && (
<AudioSourcePickerLinux
audioSource={settings.audioSource}
setAudioSource={source => setSettings(s => ({ ...s, audioSource: source }))}
/>
)}
</Card>
</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({
screens,
modalProps,
submit,
close
close,
skipPicker
}: {
screens: Source[];
modalProps: any;
submit: (data: StreamPick) => void;
close: () => void;
skipPicker: boolean;
}) {
const [selected, setSelected] = useState<string>();
const [selected, setSelected] = useState<string | undefined>(skipPicker ? screens[0].id : void 0);
const [settings, setSettings] = useState<StreamSettings>({
resolution: "1080",
fps: "60",
@@ -192,6 +312,7 @@ function ModalComponent({
source={screens.find(s => s.id === selected)!}
settings={settings}
setSettings={setSettings}
skipPicker={skipPicker}
/>
)}
</Modals.ModalContent>
@@ -200,17 +321,38 @@ function ModalComponent({
<Button
disabled={!selected}
onClick={() => {
currentSettings = settings;
// If there are 2 connections, the second one is the existing stream.
// In that case, we patch its quality
const conn = [...MediaEngineStore.getMediaEngine().connections][1];
if (conn && conn.videoStreamParameters.length > 0) {
const height = Number(settings.resolution);
const width = Math.round(height * (16 / 9));
Object.assign(conn.videoStreamParameters[0], {
maxFrameRate: Number(settings.fps),
maxPixelCount: width * height,
maxBitrate: 8000000,
maxResolution: {
type: "fixed",
width,
height
}
});
}
submit({
id: selected!,
...settings
});
close();
}}
>
Go Live
</Button>
{selected ? (
{selected && !skipPicker ? (
<Button color={Button.Colors.TRANSPARENT} onClick={() => setSelected(void 0)}>
Back
</Button>

View File

@@ -1,27 +1,36 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import "./settings.css";
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 { useSettings } from "renderer/settings";
import { isMac, isWindows } from "renderer/utils";
import { isTruthy } from "shared/utils/guards";
export default function SettingsUi() {
const Settings = useSettings();
const supportsWindowsTransparency = VesktopNative.app.supportsWindowsTransparency();
const { autostart } = VencordDesktopNative;
const { autostart } = VesktopNative;
const [autoStartEnabled, setAutoStartEnabled] = useState(autostart.isEnabled());
const switches: [keyof typeof Settings, string, string, boolean?, (() => boolean)?][] = [
["tray", "Tray Icon", "Add a tray icon for Vencord Desktop", true],
[
const allSwitches: Array<false | [keyof typeof Settings, string, string, boolean?, (() => boolean)?]> = [
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",
"Minimize to tray",
"Hitting X will make Vencord Desktop minimize to the tray instead of closing",
"Hitting X will make Vesktop minimize to the tray instead of closing",
true,
() => Settings.tray ?? true
],
@@ -31,21 +40,27 @@ export default function SettingsUi() {
"Disable minimum window size",
"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",
"Open Links in app (experimental)",
"Opens links in a new Vencord Desktop 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]
];
const switches = allSwitches.filter(isTruthy);
return (
<Forms.FormSection>
<Text variant="heading-lg/semibold" style={{ color: "var(--header-primary)" }} tag="h2">
Vencord Desktop Settings
Vesktop Settings
</Text>
<Forms.FormTitle className={Margins.top16}>Discord Branch</Forms.FormTitle>
<Forms.FormTitle className={Margins.top16 + " " + Margins.bottom8}>Discord Branch</Forms.FormTitle>
<Select
placeholder="Stable"
options={[
@@ -67,11 +82,23 @@ export default function SettingsUi() {
await autostart[v ? "enable" : "disable"]();
setAutoStartEnabled(v);
}}
note="Automatically start Vencord Desktop on computer start-up"
note="Automatically start Vesktop on computer start-up"
>
Start With System
</Switch>
<Switch
value={Settings.appBadge ?? true}
onChange={v => {
Settings.appBadge = v;
if (v) setBadge();
else VesktopNative.app.setBadgeCount(0);
}}
note="Show mention badge on the app icon"
>
Notification Badge
</Switch>
{switches.map(([key, text, note, def, predicate]) => (
<Switch
value={(Settings[key as any] ?? def ?? false) && predicate?.() !== false}
@@ -84,6 +111,43 @@ export default function SettingsUi() {
</Switch>
))}
{supportsWindowsTransparency && (
<>
<Forms.FormTitle className={Margins.top16 + " " + Margins.bottom8}>
Transparency Options
</Forms.FormTitle>
<Forms.FormText className={Margins.bottom8}>
Requires a full restart. You will need a theme that supports transparency for this to work.
</Forms.FormText>
<Select
placeholder="None"
options={[
{
label: "None",
value: "none",
default: true
},
{
label: "Mica (incorporates system theme + desktop wallpaper to paint the background)",
value: "mica"
},
{ label: "Tabbed (variant of Mica with stronger background tinting)", value: "tabbed" },
{
label: "Acrylic (blurs the window behind Vesktop for a translucent background)",
value: "acrylic"
}
]}
closeOnSelect={true}
select={v => (Settings.transparencyOption = v)}
isSelected={v => v === Settings.transparencyOption}
serialize={s => s}
/>
<Forms.FormDivider className={Margins.top16 + " " + Margins.bottom16} />
</>
)}
<Forms.FormTitle>Vencord Location</Forms.FormTitle>
<Forms.FormText>
Vencord files are loaded from{" "}
@@ -92,7 +156,7 @@ export default function SettingsUi() {
href="about:blank"
onClick={e => {
e.preventDefault();
VencordDesktopNative.fileManager.showItemInFolder(Settings.vencordDir!);
VesktopNative.fileManager.showItemInFolder(Settings.vencordDir!);
}}
>
{Settings.vencordDir}
@@ -105,11 +169,17 @@ export default function SettingsUi() {
<Button
size={Button.Sizes.SMALL}
onClick={async () => {
const choice = await VencordDesktopNative.fileManager.selectVencordDir();
const choice = await VesktopNative.fileManager.selectVencordDir();
switch (choice) {
case "cancelled":
return;
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;
}
Settings.vencordDir = choice;

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

View File

@@ -94,8 +94,6 @@
gap: 1em;
margin-bottom: 0.5em;
opacity: 0.3;
}
.vcd-screen-picker-quality section {

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -8,7 +8,7 @@ import "./hideGarbage.css";
import { waitFor } from "@vencord/types/webpack";
import { isFirstRun, localStorage } from "./utils";
import { isFirstRun, isWindows, localStorage } from "./utils";
// Make clicking Notifications focus the window
const originalSetOnClick = Object.getOwnPropertyDescriptor(Notification.prototype, "onclick")!.set!;
@@ -16,18 +16,29 @@ Object.defineProperty(Notification.prototype, "onclick", {
set(onClick) {
originalSetOnClick.call(this, function (this: unknown) {
onClick.apply(this, arguments);
VencordDesktopNative.win.focus();
VesktopNative.win.focus();
});
},
configurable: true
});
// Enable Desktop Notifications by default
if (isFirstRun) {
// Hide "Download Discord Desktop now!!!!" banner
localStorage.setItem("hideNag", "true");
// Enable Desktop Notifications by default
waitFor("setDesktopType", m => {
m.setDesktopType("all");
});
}
// FIXME: Remove eventually.
// Originally, Vencord always used a Windows user agent. This seems to cause captchas
// Now, we use a platform specific UA - HOWEVER, discord FOR SOME REASON????? caches
// device props in localStorage. This code fixes their cache to properly update the platform in SuperProps
if (!isWindows)
try {
const deviceProperties = localStorage.getItem("deviceProperties");
if (deviceProperties && JSON.parse(deviceProperties).os === "Windows")
localStorage.removeItem("deviceProperties");
} catch {}

View File

@@ -1,5 +1,5 @@
/* Download Desktop button in guilds list */
[class|=listItem]:has([data-list-item-id=guildsnav___app-download-button]),
[class|=listItem]:has(+ [class|=listItem] [data-list-item-id=guildsnav___app-download-button]) {
[class^=listItem_]:has([data-list-item-id=guildsnav___app-download-button]),
[class^=listItem_]:has(+ [class^=listItem_] [data-list-item-id=guildsnav___app-download-button]) {
display: none;
}

View File

@@ -1,25 +1,59 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import "./fixes";
import "./appBadge";
import "./patches";
import "./themedSplash";
console.log("read if cute :3");
export * as Components from "./components";
import { findByPropsLazy } from "@vencord/types/webpack";
import { FluxDispatcher } from "@vencord/types/webpack/common";
import SettingsUi from "./components/Settings";
import { Settings } from "./settings";
export { Settings };
const arRPC = Vencord.Plugins.plugins["WebRichPresence (arRPC)"];
const InviteActions = findByPropsLazy("resolveInvite");
arRPC.required = !!Settings.store.arRPC;
export async function openInviteModal(code: string) {
const { invite } = await InviteActions.resolveInvite(code, "Desktop Modal");
if (!invite) return false;
Settings.addChangeListener("arRPC", v => {
arRPC.required = !!v;
if (v && !arRPC.started) Vencord.Plugins.startPlugin(arRPC);
else if (arRPC.started) {
Vencord.Plugins.stopPlugin(arRPC);
}
VesktopNative.win.focus();
FluxDispatcher.dispatch({
type: "INVITE_MODAL_OPEN",
invite,
code,
context: "APP"
});
return true;
}
const customSettingsSections = (
Vencord.Plugins.plugins.Settings as any as { customSections: ((ID: Record<string, unknown>) => any)[] }
).customSections;
customSettingsSections.push(() => ({
section: "Vesktop",
label: "Vesktop Settings",
element: SettingsUi,
className: "vc-vesktop-settings"
}));
const arRPC = Vencord.Plugins.plugins["WebRichPresence (arRPC)"] as any as {
handleEvent(e: MessageEvent): void;
};
VesktopNative.arrpc.onActivity(data => {
if (!Settings.store.arRPC) return;
arRPC.handleEvent(new MessageEvent("message", { data }));
});

View File

@@ -0,0 +1,11 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
// TODO: Possibly auto generate glob if we have more patches in the future
import "./spellCheck";
import "./platformClass";
import "./windowsTitleBar";
import "./screenShareAudio";

View File

@@ -0,0 +1,29 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { Settings } from "renderer/settings";
import { isMac, isWindows } from "renderer/utils";
import { addPatch } from "./shared";
addPatch({
patches: [
{
find: "platform-web",
replacement: {
// eslint-disable-next-line no-useless-escape
match: /(?<=" platform-overlay"\):)\i/,
replace: "$self.getPlatformClass()"
}
}
],
getPlatformClass() {
if (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

@@ -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 { Patch } from "@vencord/types/utils/types";
window.VCDP = {};
interface PatchData {
patches: Omit<Patch, "plugin">[];
[key: string]: any;
}
export function addPatch<P extends PatchData>(p: P) {
const { patches, ...globals } = p;
for (const patch of patches as Patch[]) {
if (!Array.isArray(patch.replacement)) patch.replacement = [patch.replacement];
for (const r of patch.replacement) {
if (typeof r.replace === "string") r.replace = r.replace.replaceAll("$self", "VCDP");
}
patch.plugin = "Vesktop";
Vencord.Plugins.patches.push(patch);
}
Object.assign(VCDP, globals);
}

View File

@@ -0,0 +1,83 @@
/*
* 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 { addContextMenuPatch } from "@vencord/types/api/ContextMenu";
import { findStoreLazy } from "@vencord/types/webpack";
import { ContextMenu, FluxDispatcher, Menu } from "@vencord/types/webpack/common";
import { addPatch } from "./shared";
let word: string;
let corrections: string[];
const SpellCheckStore = findStoreLazy("SpellcheckStore");
// Make spellcheck suggestions work
addPatch({
patches: [
{
find: ".enableSpellCheck)",
replacement: {
// if (isDesktop) { DiscordNative.onSpellcheck(openMenu(props)) } else { e.preventDefault(); openMenu(props) }
match: /else (.{1,3})\.preventDefault\(\),(.{1,3}\(.{1,3}\))(?<=:(.{1,3})\.enableSpellCheck\).+?)/,
// ... else { $self.onSlateContext(() => openMenu(props)) }
replace: "else {$self.onSlateContext($1, $3?.enableSpellCheck, () => $2)}"
}
}
],
onSlateContext(e: MouseEvent, hasSpellcheck: boolean | undefined, openMenu: () => void) {
if (!hasSpellcheck) {
e.preventDefault();
openMenu();
return;
}
const cb = (w: string, c: string[]) => {
VesktopNative.spellcheck.offSpellcheckResult(cb);
word = w;
corrections = c;
openMenu();
};
VesktopNative.spellcheck.onSpellcheckResult(cb);
}
});
addContextMenuPatch("textarea-context", children => () => {
const hasCorrections = Boolean(word && corrections?.length);
children.push(
<Menu.MenuGroup>
{hasCorrections && (
<>
{corrections.map(c => (
<Menu.MenuItem
id={"vcd-spellcheck-suggestion-" + c}
label={c}
action={() => VesktopNative.spellcheck.replaceMisspelling(c)}
/>
))}
<Menu.MenuSeparator />
<Menu.MenuItem
id="vcd-spellcheck-learn"
label={`Add ${word} to dictionary`}
action={() => VesktopNative.spellcheck.addToDictionary(word)}
/>
</>
)}
<Menu.MenuCheckboxItem
id="vcd-spellcheck-enabled"
label="Enable Spellcheck"
checked={SpellCheckStore.isEnabled()}
action={() => {
FluxDispatcher.dispatch({ type: "SPELLCHECK_TOGGLE" });
// Haven't found a good way to update state, so just close for now 🤷‍♀️
ContextMenu.close();
}}
/>
</Menu.MenuGroup>
);
});

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

@@ -1,14 +1,14 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { useEffect, useReducer } from "@vencord/types/webpack/common";
import { SettingsStore } from "shared/utils/SettingsStore";
export const Settings = new SettingsStore(VencordDesktopNative.settings.get());
Settings.addGlobalChangeListener((o, p) => VencordDesktopNative.settings.set(o, p));
export const Settings = new SettingsStore(VesktopNative.settings.get());
Settings.addGlobalChangeListener((o, p) => VesktopNative.settings.set(o, p));
export function useSettings() {
const [, update] = useReducer(x => x + 1, 0);

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

@@ -1,10 +1,10 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
export const localStorage = (window.vcdLS = window.localStorage);
export const { localStorage } = window;
export const isFirstRun = (() => {
const key = "VCD_FIRST_RUN";
@@ -16,3 +16,5 @@ export const isFirstRun = (() => {
const { platform } = navigator;
export const isWindows = platform.startsWith("Win");
export const isMac = platform.startsWith("Mac");
export const isLinux = platform.startsWith("Linux");

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -11,10 +11,13 @@ export const enum IpcEvents {
GET_RENDERER_CSS_FILE = "VCD_GET_RENDERER_CSS_FILE",
GET_VERSION = "VCD_GET_VERSION",
SUPPORTS_WINDOWS_TRANSPARENCY = "VCD_SUPPORTS_WINDOWS_TRANSPARENCY",
RELAUNCH = "VCD_RELAUNCH",
CLOSE = "VCD_CLOSE",
FOCUS = "VCD_FOCUS",
MINIMIZE = "VCD_MINIMIZE",
MAXIMIZE = "VCD_MAXIMIZE",
SHOW_ITEM_IN_FOLDER = "VCD_SHOW_ITEM_IN_FOLDER",
GET_SETTINGS = "VCD_GET_SETTINGS",
@@ -27,10 +30,22 @@ export const enum IpcEvents {
UPDATE_IGNORE = "VCD_UPDATE_IGNORE",
SPELLCHECK_SET_LANGUAGES = "VCD_SPELLCHECK_SET_LANGUAGES",
SPELLCHECK_RESULT = "VCD_SPELLCHECK_RESULT",
SPELLCHECK_REPLACE_MISSPELLING = "VCD_SPELLCHECK_REPLACE_MISSPELLING",
SPELLCHECK_ADD_TO_DICTIONARY = "VCD_SPELLCHECK_ADD_TO_DICTIONARY",
SET_BADGE_COUNT = "VCD_SET_BADGE_COUNT",
CAPTURER_GET_LARGE_THUMBNAIL = "VCD_CAPTURER_GET_LARGE_THUMBNAIL",
AUTOSTART_ENABLED = "VCD_AUTOSTART_ENABLED",
ENABLE_AUTOSTART = "VCD_ENABLE_AUTOSTART",
DISABLE_AUTOSTART = "VCD_DISABLE_AUTOSTART"
DISABLE_AUTOSTART = "VCD_DISABLE_AUTOSTART",
VIRT_MIC_LIST = "VCD_VIRT_MIC_LIST",
VIRT_MIC_START = "VCD_VIRT_MIC_START",
VIRT_MIC_START_SYSTEM = "VCD_VIRT_MIC_START_ALL",
VIRT_MIC_STOP = "VCD_VIRT_MIC_STOP",
ARRPC_ACTIVITY = "VCD_ARRPC_ACTIVITY"
}

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

View File

@@ -1,10 +1,12 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { join } from "path";
export const STATIC_DIR = /* @__PURE__ */ join(__dirname, "..", "..", "static");
export const VIEW_DIR = /* @__PURE__ */ join(STATIC_DIR, "views");
export const BADGE_DIR = /* @__PURE__ */ join(STATIC_DIR, "badges");
export const ICON_PATH = /* @__PURE__ */ join(STATIC_DIR, "icon.png");

View File

@@ -1,24 +1,37 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import type { Rectangle } from "electron";
export interface Settings {
discordBranch?: "stable" | "canary" | "ptb";
vencordDir?: string;
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
tray?: boolean;
minimizeToTray?: boolean;
openLinksWithElectron?: boolean;
staticTitle?: boolean;
enableMenu?: boolean;
disableSmoothScroll?: boolean;
arRPC?: boolean;
appBadge?: boolean;
discordWindowsTitleBar?: boolean;
maximized?: boolean;
minimized?: boolean;
windowBounds?: Rectangle;
discordBranch?: "stable" | "canary" | "ptb";
openLinksWithElectron?: boolean;
vencordDir?: string;
disableMinSize?: boolean;
tray?: boolean;
minimizeToTray?: boolean;
skippedUpdate?: string;
staticTitle?: boolean;
arRPC?: boolean;
checkUpdates?: boolean;
skippedUpdate?: string;
firstLaunch?: boolean;
splashTheming?: boolean;
splashColor?: string;
splashBackground?: string;
steamOSLayoutVersion?: number;
}

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
@@ -12,8 +12,8 @@ type ResolvePropDeep<T, P> = P extends `${infer Pre}.${infer Suf}`
? ResolvePropDeep<T[Pre], Suf>
: any
: P extends keyof T
? T[P]
: any;
? T[P]
: any;
/**
* The SettingsStore allows you to easily create a mutable store that

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

View File

@@ -0,0 +1,13 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
export function isTruthy<T>(item: T): item is Exclude<T, 0 | "" | false | null | undefined> {
return Boolean(item);
}
export function isNonNullish<T>(item: T): item is Exclude<T, null | undefined> {
return item != null;
}

View File

@@ -1,23 +0,0 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
type Func = (...args: any[]) => any;
export function monkeyPatch<O extends object>(
object: O,
key: keyof O,
replacement: (original: Func, ...args: any[]) => any
): void {
const original = object[key] as Func;
const replacer = (object[key] = function (this: unknown, ...args: any[]) {
return replacement.call(this, original, ...args);
} as any);
Object.defineProperties(replacer, Object.getOwnPropertyDescriptors(original));
replacer.toString = () => original.toString();
replacer.$$original = original;
}

View File

@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/

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

@@ -1,16 +1,17 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { app, BrowserWindow, ipcMain, shell } from "electron";
import { app, BrowserWindow, shell } from "electron";
import { Settings } from "main/settings";
import { handle } from "main/utils/ipcWrappers";
import { makeLinksOpenExternally } from "main/utils/makeLinksOpenExternally";
import { githubGet, ReleaseData } from "main/utils/vencordLoader";
import { join } from "path";
import { SplashProps } from "shared/browserWinProperties";
import { IpcEvents } from "shared/IpcEvents";
import { STATIC_DIR } from "shared/paths";
import { ICON_PATH, VIEW_DIR } from "shared/paths";
export interface UpdateData {
currentVersion: string;
@@ -20,8 +21,8 @@ export interface UpdateData {
let updateData: UpdateData;
ipcMain.handle(IpcEvents.UPDATER_GET_DATA, () => updateData);
ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
handle(IpcEvents.UPDATER_GET_DATA, () => updateData);
handle(IpcEvents.UPDATER_DOWNLOAD, () => {
const portable = !!process.env.PORTABLE_EXECUTABLE_FILE;
const { assets } = updateData.release;
@@ -35,7 +36,11 @@ ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
return portable ? !isSetup : isSetup;
})!.browser_download_url;
case "darwin":
return assets.find(a => a.name.endsWith(".dmg"))!.browser_download_url;
return assets.find(a =>
process.arch === "arm64"
? a.name.endsWith("-arm64-mac.zip")
: a.name.endsWith("-mac.zip") && !a.name.includes("arm64")
)!.browser_download_url;
case "linux":
return updateData.release.html_url;
default:
@@ -46,7 +51,7 @@ ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
shell.openExternal(url);
});
ipcMain.handle(IpcEvents.UPDATE_IGNORE, () => {
handle(IpcEvents.UPDATE_IGNORE, () => {
Settings.store.skippedUpdate = updateData.latestVersion;
});
@@ -72,20 +77,21 @@ function isOutdated(oldVersion: string, newVersion: string) {
}
export async function checkUpdates() {
// if (IS_DEV) return;
if (Settings.store.checkUpdates === false) return;
try {
const raw = await githubGet("/repos/Vencord/Desktop/releases/latest");
const raw = await githubGet("/repos/Vencord/Vesktop/releases/latest");
const data = JSON.parse(raw.toString("utf-8")) as ReleaseData;
const oldVersion = app.getVersion();
const newVersion = data.tag_name.replace(/^v/, "");
if (Settings.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) {
updateData = {
currentVersion: oldVersion,
latestVersion: newVersion,
release: data
};
updateData = {
currentVersion: oldVersion,
latestVersion: newVersion,
release: data
};
if (Settings.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) {
openNewUpdateWindow();
}
} catch (e) {
@@ -95,11 +101,19 @@ export async function checkUpdates() {
function openNewUpdateWindow() {
const win = new BrowserWindow({
...SplashProps,
width: 500,
autoHideMenuBar: true,
alwaysOnTop: true,
webPreferences: {
preload: join(__dirname, "updaterPreload.js")
}
preload: join(__dirname, "updaterPreload.js"),
nodeIntegration: false,
contextIsolation: true,
sandbox: true
},
icon: ICON_PATH
});
win.loadFile(join(STATIC_DIR, "updater.html"));
makeLinksOpenExternally(win);
win.loadFile(join(VIEW_DIR, "updater.html"));
}

View File

@@ -1,11 +1,11 @@
/*
* SPDX-License-Identifier: GPL-3.0
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
* Copyright (c) 2023 Vendicated and Vencord contributors
*/
import { contextBridge } from "electron";
import { invoke } from "preload/typedIpcs";
import { invoke } from "preload/typedIpc";
import { IpcEvents } from "shared/IpcEvents";
import type { UpdateData } from "./main";

BIN
static/badges/1.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/10.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/11.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/2.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/3.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/4.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/5.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/6.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/7.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/8.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/badges/9.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

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

@@ -1,9 +1,9 @@
<head>
<link rel="stylesheet" href="./style.css" type="text/css" />
<style>
body {
padding: 2em;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
}
h1 {
@@ -13,10 +13,10 @@
</head>
<body>
<h1>Vencord Desktop %VERSION%</h1>
<h1 id="title">Vesktop</h1>
<p>
Vencord Desktop is a free/libre cross platform desktop app aiming to give you a snappier Discord experience with
Vencord pre-installed
Vesktop is a free/libre cross platform desktop app aiming to give you a snappier Discord experience with Vencord
pre-installed
</p>
<section>
@@ -26,17 +26,17 @@
<a href="https://vencord.dev" target="_blank">Vencord Website</a>
</li>
<li>
<a href="https://github.com/Vencord/Desktop" target="_blank">Source Code</a>
<a href="https://github.com/Vencord/Vesktop" target="_blank">Source Code</a>
</li>
<li>
<a href="https://github.com/Vencord/Desktop/issues" target="_blank">Report bugs / Request features</a>
<a href="https://github.com/Vencord/Vesktop/issues" target="_blank">Report bugs / Request features</a>
</li>
</ul>
</section>
<section>
<h2>Acknowledgements</h2>
<p>These awesome libraries empower Vencord Desktop</p>
<p>These awesome libraries empower Vesktop</p>
<ul>
<li>
<a href="https://github.com/electron/electron" target="_blank">Electron</a>
@@ -51,12 +51,25 @@
<a href="https://github.com/OpenAsar/arrpc" target="_blank">arrpc</a>
- An open implementation of Discord's Rich Presence server
</li>
<li>
<a href="https://github.com/Soundux/rohrkabel" target="_blank">rohrkabel</a>
- A C++ RAII Pipewire-API Wrapper
</li>
<li>
And many
<a href="https://github.com/Vencord/Desktop/blob/main/pnpm-lock.yaml" target="_blank"
<a href="https://github.com/Vencord/Vesktop/blob/main/pnpm-lock.yaml" target="_blank"
>more awesome open source libraries</a
>
</li>
</ul>
</section>
</body>
<script type="module">
const data = await Updater.getData();
if (data.currentVersion) {
const title = document.getElementById("title");
title.textContent += ` v${data.currentVersion}`;
}
</script>

View File

@@ -1,27 +1,10 @@
<head>
<link rel="stylesheet" href="./style.css" type="text/css" />
<style>
:root {
--bg: white;
--fg: black;
--fg-secondary: #313338;
--fg-semi-trans: rgb(0 0 0 / 0.2);
}
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(223 6.7% 20.6%);
--fg: white;
--fg-secondary: #b5bac1;
--fg-semi-trans: rgb(255 255 255 / 0.2);
}
}
body {
height: 100vh;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
margin: 0;
padding: 1.5em;
padding-bottom: 1em;
@@ -32,13 +15,9 @@
box-sizing: border-box;
}
body,
select {
background: var(--bg);
color: var(--fg);
}
select {
padding: 0.3em;
margin: -0.3em;
border-radius: 6px;
@@ -125,7 +104,7 @@
</head>
<body>
<h1>Welcome to Vencord Desktop</h1>
<h1>Welcome to Vesktop</h1>
<p>Let's customise your experience!</p>
<form>
@@ -141,9 +120,20 @@
<label>
<div>
<h2>Start with System</h2>
<span>Automatically open Vencord Desktop when your computer starts</span>
<span>Automatically open Vesktop when your computer starts</span>
</div>
<input type="checkbox" name="autoStart" checked />
<input type="checkbox" name="autoStart" />
</label>
<label>
<div>
<h2>Rich Presence</h2>
<span
>Enable Rich presence (game activity) via
<a href="https://github.com/OpenAsar/arrpc" target="_blank">arRPC</a></span
>
</div>
<input type="checkbox" name="richPresence" checked />
</label>
<label>
@@ -163,7 +153,7 @@
</label>
</form>
<div id="buttons">
<button id="cancel">Cancel</button>
<button id="cancel">Quit</button>
<button id="submit">Submit</button>
</div>
</body>

View File

@@ -1,16 +1,11 @@
<head>
<link rel="stylesheet" href="./style.css" type="text/css" />
<style>
* {
user-select: none;
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
margin: 0;
padding: 0;
}
.wrapper {
box-sizing: border-box;
height: 100%;
@@ -18,19 +13,18 @@
flex-direction: column;
justify-content: center;
align-items: center;
background-color: hsl(223 6.7% 20.6%);
border-radius: 8px;
border: 1px solid hsl(220 6.5% 18%);
border: 1px solid var(--fg-semi-trans);
}
p {
color: rgb(219, 222, 225);
text-align: center;
}
img {
width: 6em;
height: 6em;
image-rendering: pixelated;
}
</style>
</head>
@@ -39,10 +33,10 @@
<div class="wrapper">
<img
draggable="false"
src="https://cdn.discordapp.com/emojis/1024751291504791654.gif?size=512"
src="../shiggy.gif"
alt="shiggy"
role="presentation"
/>
<p>Loading Vencord Desktop...</p>
<p>Loading Vesktop...</p>
</div>
</body>

30
static/views/style.css Normal file
View File

@@ -0,0 +1,30 @@
:root {
--bg: white;
--fg: black;
--fg-secondary: #313338;
--fg-semi-trans: rgb(0 0 0 / 0.2);
--link: #006ce7;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(223 6.7% 20.6%);
--fg: white;
--fg-secondary: #b5bac1;
--fg-semi-trans: rgb(255 255 255 / 0.2);
--link: #00a8fc;
}
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
margin: 0;
padding: 0;
background: var(--bg);
color: var(--fg);
}
a {
color: var(--link);
}

View File

@@ -1,22 +1,13 @@
<head>
<style>
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
"Open Sans", "Helvetica Neue", sans-serif;
margin: 0;
padding: 0;
color: rgb(219, 222, 225);
}
<link rel="stylesheet" href="./style.css" type="text/css" />
<style>
.wrapper {
display: flex;
flex-direction: column;
justify-content: space-between;
box-sizing: border-box;
height: 100%;
background-color: #313338;
border-radius: 8px;
border: 1px solid #248046;
min-height: 100%;
padding: 1em;
}
@@ -34,7 +25,7 @@
button {
cursor: pointer;
padding: 0.5em;
color: white;
color: var(--fg);
border: none;
border-radius: 3px;
font-weight: bold;
@@ -60,12 +51,15 @@
<div class="wrapper">
<section>
<h1>Update Available</h1>
<p>There's a new update for Vencord Desktop! Update now to get new fixes and features!</p>
<p>There's a new update for Vesktop! Update now to get new fixes and features!</p>
<p>
Current: <span id="current"></span>
<br />
Latest: <span id="latest"></span>
</p>
<h2>Changelog</h2>
<p id="changelog">Loading...</p>
</section>
<section>
@@ -113,3 +107,17 @@
});
}
</script>
<script type="module">
import { micromark } from "https://esm.sh/micromark@3?bundle";
import { gfm, gfmHtml } from "https://esm.sh/micromark-extension-gfm@2?bundle";
const changelog = (await Updater.getData()).release.body;
if (changelog)
document.getElementById("changelog").innerHTML = micromark(changelog, {
extensions: [gfm()],
htmlExtensions: [gfmHtml()]
})
.replace(/h1>/g, "h3>")
.replace(/<a /g, '<a target="_blank" ');
</script>