Compare commits
29 Commits
fix/start-
...
v1.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79cd2791f6 | ||
|
|
73de0fa535 | ||
|
|
83e74b98d5 | ||
|
|
2e4c834a90 | ||
|
|
2aa0b0fa20 | ||
|
|
3ac0ed3d78 | ||
|
|
eddc1de784 | ||
|
|
6483b3a3d9 | ||
|
|
38f0330eb2 | ||
|
|
15a49a31e1 | ||
|
|
33eb1af2be | ||
|
|
563ba1eebe | ||
|
|
62cf02e7b1 | ||
|
|
0f0bddbef9 | ||
|
|
0881143d57 | ||
|
|
effd950b2d | ||
|
|
4074e8d6ac | ||
|
|
779c8fa516 | ||
|
|
b059516707 | ||
|
|
dfa9d248d3 | ||
|
|
18925ad583 | ||
|
|
0b01732293 | ||
|
|
a89cd9d2ba | ||
|
|
2f35128acf | ||
|
|
7565eb39a4 | ||
|
|
d71d0c1cc4 | ||
|
|
dfc6970756 | ||
|
|
1429815fd1 | ||
|
|
aa397d003c |
@@ -22,7 +22,13 @@
|
||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||
"spaced-comment": ["error", "always", { "markers": ["!"] }],
|
||||
"yoda": "error",
|
||||
"prefer-destructuring": ["error", { "object": true, "array": false }],
|
||||
"prefer-destructuring": [
|
||||
"error",
|
||||
{
|
||||
"VariableDeclarator": { "array": false, "object": true },
|
||||
"AssignmentExpression": { "array": false, "object": false }
|
||||
}
|
||||
],
|
||||
"operator-assignment": ["error", "always"],
|
||||
"no-useless-computed-key": "error",
|
||||
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
||||
|
||||
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS/Distro: [e.g. Windows / Fedora Linux / MacOs]
|
||||
- Desktop Environment (linux only): [e.g. gnome, kde, sway]
|
||||
- Version: [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
9
.github/workflows/release.yml
vendored
9
.github/workflows/release.yml
vendored
@@ -37,7 +37,16 @@ jobs:
|
||||
run: pnpm build
|
||||
|
||||
- name: Run Electron Builder
|
||||
if: ${{ matrix.platform != 'mac' }}
|
||||
run: |
|
||||
pnpm electron-builder --${{ matrix.platform }} --publish always
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run Electron Builder
|
||||
if: ${{ matrix.platform == 'mac' }}
|
||||
run: |
|
||||
pnpm electron-builder --${{ matrix.platform }} --publish always
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK_: ${{ secrets.APPLE_SIGNING_CERT }}
|
||||
|
||||
10
README.md
10
README.md
@@ -3,14 +3,14 @@
|
||||
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
|
||||
|
||||
- Global Keybinds
|
||||
|
||||
Bug reports, feature requests & contributions are highly appreciated!!
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## Installing
|
||||
|
||||
### Windows
|
||||
@@ -39,7 +39,7 @@ Download Vesktop-VERSION.rpm from [releases](https://github.com/Vencord/Vesktop/
|
||||
|
||||
#### Other
|
||||
|
||||
Either download Vesktop-VERSION.AppImage and just run it directly or grab Vesktop-VERSION.tar.gz, extract it somewhere and run `vencorddesktop`.
|
||||
Either download Vesktop-VERSION.AppImage and just run it directly or grab Vesktop-VERSION.tar.gz, extract it somewhere and run `vesktop`.
|
||||
|
||||
If other packages are created, feel free to open an issue and we'll link them here.
|
||||
|
||||
@@ -68,7 +68,3 @@ pnpm package:dir
|
||||
## Motivation
|
||||
|
||||
The official Discord Desktop app is very resource heavy compared to Discord in your Browser. There are multiple alternative Electron apps (ArmCord, WebCord, probably more) that prove how much of a performance gain you can gain by using a custom app. ArmCord already supports Vencord but makes it pretty limited for us. Making our own standalone app gives us much more control.
|
||||
|
||||
This is just a random idea I (V) got, and might not actually ever be finished heh
|
||||
|
||||
Gluon also seems very attractive for this because of how lightweight it can be and because unlike electron, streaming just works out of the box like in any chromium browser. However, at the time of writing this, it still lacks some features necessary to make it work (synchronous ipc or a way to get node process variables into the onLoad function for instance, plus onLoad seems to load a little too late sometimes)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
!macro preInit
|
||||
SetRegView 64
|
||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop"
|
||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop"
|
||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop"
|
||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop"
|
||||
SetRegView 32
|
||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop"
|
||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\VencordDesktop"
|
||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop"
|
||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$LocalAppData\vesktop"
|
||||
!macroend
|
||||
|
||||
39
package.json
39
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "VencordDesktop",
|
||||
"version": "0.4.4",
|
||||
"name": "vesktop",
|
||||
"version": "1.5.0",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
@@ -24,35 +24,35 @@
|
||||
"updateMeta": "tsx scripts/utils/updateMeta.mts"
|
||||
},
|
||||
"dependencies": {
|
||||
"arrpc": "github:OpenAsar/arrpc#3e22fd776273afaa4a80c51deb86077ffdd4d2ae"
|
||||
"arrpc": "github:OpenAsar/arrpc#98879cae0565e6fce34e4cb6f544bf42c6a7e7c8"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@vencord/venmic": "^2.1.3"
|
||||
"@vencord/venmic": "^3.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
||||
"@types/node": "^20.10.0",
|
||||
"@types/react": "^18.2.39",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.1",
|
||||
"@typescript-eslint/parser": "^6.13.1",
|
||||
"@types/node": "^20.11.2",
|
||||
"@types/react": "^18.2.48",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
||||
"@typescript-eslint/parser": "^6.19.0",
|
||||
"@vencord/types": "^0.1.2",
|
||||
"dotenv": "^16.3.1",
|
||||
"electron": "^27.1.2",
|
||||
"electron": "^28.1.3",
|
||||
"electron-builder": "^24.9.1",
|
||||
"esbuild": "^0.19.8",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"esbuild": "^0.19.11",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-plugin-license-header": "^0.6.0",
|
||||
"eslint-plugin-path-alias": "^1.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"prettier": "^3.2.2",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tsx": "^4.6.0",
|
||||
"type-fest": "^4.8.2",
|
||||
"typescript": "^5.3.2",
|
||||
"tsx": "^4.7.0",
|
||||
"type-fest": "^4.9.0",
|
||||
"typescript": "^5.3.3",
|
||||
"xml-formatter": "^3.6.0"
|
||||
},
|
||||
"packageManager": "pnpm@8.11.0",
|
||||
@@ -61,7 +61,7 @@
|
||||
"pnpm": ">=8"
|
||||
},
|
||||
"build": {
|
||||
"appId": "dev.vencord.desktop",
|
||||
"appId": "dev.vencord.vesktop",
|
||||
"productName": "Vesktop",
|
||||
"files": [
|
||||
"!*",
|
||||
@@ -110,8 +110,7 @@
|
||||
"GenericName": "Internet Messenger",
|
||||
"Type": "Application",
|
||||
"Categories": "Network;InstantMessaging;Chat;",
|
||||
"Keywords": "discord;vencord;electron;chat;",
|
||||
"StartupWMClass": "VencordDesktop"
|
||||
"Keywords": "discord;vencord;electron;chat;"
|
||||
}
|
||||
},
|
||||
"mac": {
|
||||
|
||||
1365
pnpm-lock.yaml
generated
1365
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -10,51 +10,38 @@ import { join } from "path";
|
||||
|
||||
interface AutoStart {
|
||||
isEnabled(): boolean;
|
||||
wasAutoStarted(): boolean;
|
||||
enable(): void;
|
||||
disable(): void;
|
||||
}
|
||||
|
||||
const isFlatpak = process.env.FLATPAK_ID !== undefined;
|
||||
|
||||
function makeAutoStartLinux(): AutoStart {
|
||||
const configDir = process.env.XDG_CONFIG_HOME || join(process.env.HOME!, ".config");
|
||||
const dir = join(configDir, "autostart");
|
||||
const file = join(dir, "vencord.desktop");
|
||||
|
||||
return {
|
||||
isEnabled: () => existsSync(file), // TODO: flatpak
|
||||
wasAutoStarted: () => process.argv.includes("--autostart"),
|
||||
isEnabled: () => existsSync(file),
|
||||
enable() {
|
||||
if (isFlatpak) {
|
||||
} else {
|
||||
const desktopFile = `
|
||||
const desktopFile = `
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Version=1.0
|
||||
Name=Vencord
|
||||
Comment=Vencord autostart script
|
||||
Exec=${process.execPath} --autostart
|
||||
Exec=${process.execPath}
|
||||
Terminal=false
|
||||
StartupNotify=false
|
||||
`.trim();
|
||||
|
||||
mkdirSync(dir, { recursive: true });
|
||||
writeFileSync(file, desktopFile);
|
||||
}
|
||||
mkdirSync(dir, { recursive: true });
|
||||
writeFileSync(file, desktopFile);
|
||||
},
|
||||
disable: () => {
|
||||
if (isFlatpak) {
|
||||
} else {
|
||||
rmSync(file, { force: true });
|
||||
}
|
||||
}
|
||||
disable: () => rmSync(file, { force: true })
|
||||
};
|
||||
}
|
||||
|
||||
const autoStartWindowsMac: AutoStart = {
|
||||
isEnabled: () => app.getLoginItemSettings().openAtLogin,
|
||||
wasAutoStarted: () => app.getLoginItemSettings().wasOpenedAtLogin,
|
||||
enable: () => app.setLoginItemSettings({ openAtLogin: true }),
|
||||
disable: () => app.setLoginItemSettings({ openAtLogin: false })
|
||||
};
|
||||
|
||||
@@ -5,9 +5,29 @@
|
||||
*/
|
||||
|
||||
import { app } from "electron";
|
||||
import { existsSync, readdirSync, renameSync, rmdirSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("userData"), "VencordDesktop");
|
||||
const LEGACY_DATA_DIR = join(app.getPath("appData"), "VencordDesktop", "VencordDesktop");
|
||||
export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("userData"));
|
||||
// TODO: remove eventually
|
||||
if (existsSync(LEGACY_DATA_DIR)) {
|
||||
try {
|
||||
console.warn("Detected legacy settings dir", LEGACY_DATA_DIR + ".", "migrating to", DATA_DIR);
|
||||
for (const file of readdirSync(LEGACY_DATA_DIR)) {
|
||||
renameSync(join(LEGACY_DATA_DIR, file), join(DATA_DIR, file));
|
||||
}
|
||||
rmdirSync(LEGACY_DATA_DIR);
|
||||
renameSync(
|
||||
join(app.getPath("appData"), "VencordDesktop", "IndexedDB"),
|
||||
join(DATA_DIR, "sessionData", "IndexedDB")
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Migration failed", e);
|
||||
}
|
||||
}
|
||||
app.setPath("sessionData", join(DATA_DIR, "sessionData"));
|
||||
|
||||
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
||||
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
|
||||
export const VENCORD_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json");
|
||||
@@ -26,11 +46,13 @@ export const MIN_HEIGHT = 500;
|
||||
export const DEFAULT_WIDTH = 1280;
|
||||
export const DEFAULT_HEIGHT = 720;
|
||||
|
||||
export const DISCORD_HOSTNAMES = ["discord.com", "canary.discord.com", "ptb.discord.com"];
|
||||
|
||||
const 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",
|
||||
darwin: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
linux: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.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"
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
};
|
||||
|
||||
export const UserAgent = UserAgents[process.platform] || UserAgents.windows;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||
import { autoStart } from "./autoStart";
|
||||
import { DATA_DIR } from "./constants";
|
||||
import { createWindows } from "./mainWindow";
|
||||
import { Settings } from "./settings";
|
||||
import { Settings, State } from "./settings";
|
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||
|
||||
interface Data {
|
||||
@@ -44,9 +44,9 @@ export function createFirstLaunchTour() {
|
||||
if (!msg.startsWith("form:")) return;
|
||||
const data = JSON.parse(msg.slice(5)) as Data;
|
||||
|
||||
State.store.firstLaunch = false;
|
||||
Settings.store.minimizeToTray = data.minimizeToTray;
|
||||
Settings.store.discordBranch = data.discordBranch;
|
||||
Settings.store.firstLaunch = false;
|
||||
Settings.store.arRPC = data.richPresence;
|
||||
|
||||
if (data.autoStart) autoStart.enable();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import "./ipc";
|
||||
|
||||
import { app, BrowserWindow } from "electron";
|
||||
import { app, BrowserWindow, nativeTheme } from "electron";
|
||||
import { checkUpdates } from "updater/main";
|
||||
|
||||
import { DATA_DIR } from "./constants";
|
||||
@@ -14,7 +14,8 @@ import { createFirstLaunchTour } from "./firstLaunch";
|
||||
import { createWindows, mainWin } from "./mainWindow";
|
||||
import { registerMediaPermissionsHandler } from "./mediaPermissions";
|
||||
import { registerScreenShareHandler } from "./screenShare";
|
||||
import { Settings } from "./settings";
|
||||
import { Settings, State } from "./settings";
|
||||
import { isDeckGameMode } from "./utils/steamOS";
|
||||
|
||||
if (IS_DEV) {
|
||||
require("source-map-support").install();
|
||||
@@ -24,8 +25,9 @@ if (IS_DEV) {
|
||||
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
|
||||
|
||||
function init() {
|
||||
const { disableSmoothScroll } = Settings.store;
|
||||
const { disableSmoothScroll, hardwareAcceleration } = Settings.store;
|
||||
|
||||
if (hardwareAcceleration === false) app.disableHardwareAcceleration();
|
||||
if (disableSmoothScroll) {
|
||||
app.commandLine.appendSwitch("disable-smooth-scrolling");
|
||||
}
|
||||
@@ -42,6 +44,9 @@ function init() {
|
||||
"WinRetrieveSuggestionsOnlyOnDemand,HardwareMediaKeyHandling,MediaSessionService,WidgetLayering"
|
||||
);
|
||||
|
||||
// In the Flatpak on SteamOS the theme is detected as light, but SteamOS only has a dark mode, so we just override it
|
||||
if (isDeckGameMode) nativeTheme.themeSource = "dark";
|
||||
|
||||
app.on("second-instance", (_event, _cmdLine, _cwd, data: any) => {
|
||||
if (data.IS_DEV) app.quit();
|
||||
else if (mainWin) {
|
||||
@@ -53,7 +58,7 @@ function init() {
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
checkUpdates();
|
||||
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.desktop");
|
||||
if (process.platform === "win32") app.setAppUserModelId("dev.vencord.vesktop");
|
||||
|
||||
registerScreenShareHandler();
|
||||
registerMediaPermissionsHandler();
|
||||
@@ -79,7 +84,7 @@ if (!app.requestSingleInstanceLock({ IS_DEV })) {
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
if (!Object.hasOwn(Settings.store, "firstLaunch")) {
|
||||
if (!Object.hasOwn(State.store, "firstLaunch")) {
|
||||
createFirstLaunchTour();
|
||||
} else {
|
||||
createWindows();
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
if (process.platform === "linux") import("./virtmic");
|
||||
if (process.platform === "linux") import("./venmic");
|
||||
|
||||
import { execFile } from "child_process";
|
||||
import { app, BrowserWindow, dialog, RelaunchOptions, session, shell } from "electron";
|
||||
import { app, BrowserWindow, clipboard, dialog, nativeImage, RelaunchOptions, session, shell } from "electron";
|
||||
import { mkdirSync, readFileSync, watch } from "fs";
|
||||
import { open, readFile } from "fs/promises";
|
||||
import { release } from "os";
|
||||
@@ -21,6 +21,7 @@ import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./
|
||||
import { mainWin } from "./mainWindow";
|
||||
import { Settings } from "./settings";
|
||||
import { handle, handleSync } from "./utils/ipcWrappers";
|
||||
import { isDeckGameMode, showGamePage } from "./utils/steamOS";
|
||||
import { isValidVencordInstall } from "./utils/vencordLoader";
|
||||
|
||||
handleSync(IpcEvents.GET_VENCORD_PRELOAD_FILE, () => join(VENCORD_FILES_DIR, "vencordDesktopPreload.js"));
|
||||
@@ -47,11 +48,14 @@ handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: strin
|
||||
Settings.setData(settings, path);
|
||||
});
|
||||
|
||||
handle(IpcEvents.RELAUNCH, () => {
|
||||
handle(IpcEvents.RELAUNCH, async () => {
|
||||
const options: RelaunchOptions = {
|
||||
args: process.argv.slice(1).concat(["--relaunch"])
|
||||
};
|
||||
if (app.isPackaged && process.env.APPIMAGE) {
|
||||
if (isDeckGameMode) {
|
||||
// We can't properly relaunch when running under gamescope, but we can at least navigate to our page in Steam.
|
||||
await showGamePage();
|
||||
} else if (app.isPackaged && process.env.APPIMAGE) {
|
||||
execFile(process.env.APPIMAGE, options.args);
|
||||
} else {
|
||||
app.relaunch(options);
|
||||
@@ -116,6 +120,13 @@ handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
||||
|
||||
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||
|
||||
handle(IpcEvents.CLIPBOARD_COPY_IMAGE, async (_, buf: ArrayBuffer, src: string) => {
|
||||
clipboard.write({
|
||||
html: `<img src="${src.replaceAll('"', '\\"')}">`,
|
||||
image: nativeImage.createFromBuffer(Buffer.from(buf))
|
||||
});
|
||||
});
|
||||
|
||||
function readCss() {
|
||||
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import type { SettingsStore } from "shared/utils/SettingsStore";
|
||||
import { ICON_PATH } from "../shared/paths";
|
||||
import { createAboutWindow } from "./about";
|
||||
import { initArRPC } from "./arrpc";
|
||||
import { autoStart } from "./autoStart";
|
||||
import {
|
||||
DATA_DIR,
|
||||
DEFAULT_HEIGHT,
|
||||
@@ -35,7 +34,7 @@ import {
|
||||
UserAgent,
|
||||
VENCORD_FILES_DIR
|
||||
} from "./constants";
|
||||
import { Settings, VencordSettings } from "./settings";
|
||||
import { Settings, State, VencordSettings } from "./settings";
|
||||
import { createSplashWindow } from "./splash";
|
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||
import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS";
|
||||
@@ -79,8 +78,7 @@ function initTray(win: BrowserWindow) {
|
||||
label: "Open",
|
||||
click() {
|
||||
win.show();
|
||||
},
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "About",
|
||||
@@ -123,14 +121,6 @@ function initTray(win: BrowserWindow) {
|
||||
tray.setToolTip("Vesktop");
|
||||
tray.setContextMenu(trayMenu);
|
||||
tray.on("click", () => win.show());
|
||||
|
||||
win.on("show", () => {
|
||||
trayMenu.items[0].enabled = false;
|
||||
});
|
||||
|
||||
win.on("hide", () => {
|
||||
trayMenu.items[0].enabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
async function clearData(win: BrowserWindow) {
|
||||
@@ -211,7 +201,6 @@ function initMenuBar(win: BrowserWindow) {
|
||||
type: "separator"
|
||||
},
|
||||
{
|
||||
label: "Hide Vesktop", // Should probably remove the label, but it says "Hide VencordDesktop" instead of "Hide Vesktop"
|
||||
role: "hide"
|
||||
},
|
||||
{
|
||||
@@ -269,7 +258,7 @@ function getWindowBoundsOptions(): BrowserWindowConstructorOptions {
|
||||
// We want the default window behaivour to apply in game mode since it expects everything to be fullscreen and maximized.
|
||||
if (isDeckGameMode) return {};
|
||||
|
||||
const { x, y, width, height } = Settings.store.windowBounds ?? {};
|
||||
const { x, y, width, height } = State.store.windowBounds ?? {};
|
||||
|
||||
const options = {
|
||||
width: width ?? DEFAULT_WIDTH,
|
||||
@@ -314,8 +303,8 @@ function getDarwinOptions(): BrowserWindowConstructorOptions {
|
||||
|
||||
function initWindowBoundsListeners(win: BrowserWindow) {
|
||||
const saveState = () => {
|
||||
Settings.store.maximized = win.isMaximized();
|
||||
Settings.store.minimized = win.isMinimized();
|
||||
State.store.maximized = win.isMaximized();
|
||||
State.store.minimized = win.isMinimized();
|
||||
};
|
||||
|
||||
win.on("maximize", saveState);
|
||||
@@ -323,7 +312,7 @@ function initWindowBoundsListeners(win: BrowserWindow) {
|
||||
win.on("unmaximize", saveState);
|
||||
|
||||
const saveBounds = () => {
|
||||
Settings.store.windowBounds = win.getBounds();
|
||||
State.store.windowBounds = win.getBounds();
|
||||
};
|
||||
|
||||
win.on("resize", saveBounds);
|
||||
@@ -376,11 +365,11 @@ function createMainWindow() {
|
||||
removeSettingsListeners();
|
||||
removeVencordSettingsListeners();
|
||||
|
||||
const { staticTitle, transparencyOption, enableMenu, discordWindowsTitleBar } = Settings.store;
|
||||
const { staticTitle, transparencyOption, enableMenu, customTitleBar } = Settings.store;
|
||||
|
||||
const { frameless } = VencordSettings.store;
|
||||
|
||||
const noFrame = frameless === true || (process.platform === "win32" && discordWindowsTitleBar === true);
|
||||
const noFrame = frameless === true || customTitleBar === true;
|
||||
|
||||
const win = (mainWin = new BrowserWindow({
|
||||
show: false,
|
||||
@@ -397,7 +386,12 @@ function createMainWindow() {
|
||||
...(transparencyOption &&
|
||||
transparencyOption !== "none" && {
|
||||
backgroundColor: "#00000000",
|
||||
backgroundMaterial: transparencyOption,
|
||||
backgroundMaterial: transparencyOption
|
||||
}),
|
||||
// Fix transparencyOption for custom discord titlebar
|
||||
...(customTitleBar &&
|
||||
transparencyOption &&
|
||||
transparencyOption !== "none" && {
|
||||
transparent: true
|
||||
}),
|
||||
...(staticTitle && { title: "Vesktop" }),
|
||||
@@ -443,8 +437,8 @@ function createMainWindow() {
|
||||
const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDesktopMain.js")));
|
||||
|
||||
export async function createWindows() {
|
||||
const shouldStartMinimized = Settings.store.startMinimized && autoStart.wasAutoStarted();
|
||||
const splash = createSplashWindow(shouldStartMinimized);
|
||||
const startMinimized = process.argv.includes("--start-minimized");
|
||||
const splash = createSplashWindow(startMinimized);
|
||||
// SteamOS letterboxes and scales it terribly, so just full screen it
|
||||
if (isDeckGameMode) splash.setFullScreen(true);
|
||||
await ensureVencordFiles();
|
||||
@@ -455,7 +449,10 @@ export async function createWindows() {
|
||||
mainWin.webContents.on("did-finish-load", () => {
|
||||
splash.destroy();
|
||||
|
||||
if (!shouldStartMinimized || isDeckGameMode) mainWin!.show();
|
||||
if (!startMinimized) {
|
||||
mainWin!.show();
|
||||
if (State.store.maximized && !isDeckGameMode) mainWin!.maximize();
|
||||
}
|
||||
|
||||
if (isDeckGameMode) {
|
||||
// always use entire display
|
||||
@@ -463,12 +460,12 @@ export async function createWindows() {
|
||||
|
||||
askToApplySteamLayout(mainWin);
|
||||
}
|
||||
});
|
||||
|
||||
mainWin.once("show", () => {
|
||||
if (Settings.store.maximized && !mainWin!.isMaximized() && !isDeckGameMode) {
|
||||
mainWin!.maximize();
|
||||
}
|
||||
mainWin.once("show", () => {
|
||||
if (State.store.maximized && !mainWin!.isMaximized() && !isDeckGameMode) {
|
||||
mainWin!.maximize();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
initArRPC();
|
||||
|
||||
@@ -4,14 +4,15 @@
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||
import { dirname, join } from "path";
|
||||
import type { Settings as TSettings } from "shared/settings";
|
||||
import type { Settings as TSettings, State as TState } from "shared/settings";
|
||||
import { SettingsStore } from "shared/utils/SettingsStore";
|
||||
|
||||
import { DATA_DIR, VENCORD_SETTINGS_FILE } from "./constants";
|
||||
|
||||
const SETTINGS_FILE = join(DATA_DIR, "settings.json");
|
||||
const STATE_FILE = join(DATA_DIR, "state.json");
|
||||
|
||||
function loadSettings<T extends object = any>(file: string, name: string) {
|
||||
let settings = {} as T;
|
||||
@@ -20,7 +21,7 @@ function loadSettings<T extends object = any>(file: string, name: string) {
|
||||
try {
|
||||
settings = JSON.parse(content);
|
||||
} catch (err) {
|
||||
console.error(`Failed to parse ${name} settings.json:`, err);
|
||||
console.error(`Failed to parse ${name}.json:`, err);
|
||||
}
|
||||
} catch {}
|
||||
|
||||
@@ -33,5 +34,31 @@ function loadSettings<T extends object = any>(file: string, name: string) {
|
||||
return store;
|
||||
}
|
||||
|
||||
export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vesktop");
|
||||
export const VencordSettings = loadSettings<any>(VENCORD_SETTINGS_FILE, "Vencord");
|
||||
export const Settings = loadSettings<TSettings>(SETTINGS_FILE, "Vesktop settings");
|
||||
if (Object.hasOwn(Settings.plain, "discordWindowsTitleBar")) {
|
||||
Settings.plain.customTitleBar = Settings.plain.discordWindowsTitleBar;
|
||||
delete Settings.plain.discordWindowsTitleBar;
|
||||
Settings.markAsChanged();
|
||||
}
|
||||
|
||||
export const VencordSettings = loadSettings<any>(VENCORD_SETTINGS_FILE, "Vencord settings");
|
||||
|
||||
if (Object.hasOwn(Settings.plain, "firstLaunch") && !existsSync(STATE_FILE)) {
|
||||
console.warn("legacy state in settings.json detected. migrating to state.json");
|
||||
const state = {} as TState;
|
||||
for (const prop of [
|
||||
"firstLaunch",
|
||||
"maximized",
|
||||
"minimized",
|
||||
"skippedUpdate",
|
||||
"steamOSLayoutVersion",
|
||||
"windowBounds"
|
||||
] as const) {
|
||||
state[prop] = Settings.plain[prop];
|
||||
delete Settings.plain[prop];
|
||||
}
|
||||
Settings.markAsChanged();
|
||||
writeFileSync(STATE_FILE, JSON.stringify(state, null, 4));
|
||||
}
|
||||
|
||||
export const State = loadSettings<TState>(STATE_FILE, "Vesktop state");
|
||||
|
||||
@@ -5,20 +5,14 @@
|
||||
*/
|
||||
|
||||
import { ipcMain, IpcMainEvent, IpcMainInvokeEvent, WebFrameMain } from "electron";
|
||||
import { DISCORD_HOSTNAMES } from "main/constants";
|
||||
import { IpcEvents } from "shared/IpcEvents";
|
||||
|
||||
export function validateSender(frame: WebFrameMain) {
|
||||
const { hostname, protocol } = new URL(frame.url);
|
||||
if (protocol === "file:") return;
|
||||
|
||||
switch (hostname) {
|
||||
case "discord.com":
|
||||
case "ptb.discord.com":
|
||||
case "canary.discord.com":
|
||||
break;
|
||||
default:
|
||||
throw new Error("ipc: Disallowed host " + hostname);
|
||||
}
|
||||
if (!DISCORD_HOSTNAMES.includes(hostname)) throw new Error("ipc: Disallowed host " + hostname);
|
||||
}
|
||||
|
||||
export function handleSync(event: IpcEvents, cb: (e: IpcMainEvent, ...args: any[]) => any) {
|
||||
|
||||
@@ -5,36 +5,67 @@
|
||||
*/
|
||||
|
||||
import { BrowserWindow, shell } from "electron";
|
||||
import { DISCORD_HOSTNAMES } from "main/constants";
|
||||
|
||||
import { Settings } from "../settings";
|
||||
import { createOrFocusPopup, setupPopout } from "./popout";
|
||||
import { execSteamURL, isDeckGameMode, steamOpenURL } from "./steamOS";
|
||||
|
||||
export function handleExternalUrl(url: string, protocol?: string): { action: "deny" | "allow" } {
|
||||
if (protocol == null) {
|
||||
try {
|
||||
protocol = new URL(url).protocol;
|
||||
} catch {
|
||||
return { action: "deny" };
|
||||
}
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
case "http:":
|
||||
case "https:":
|
||||
if (Settings.store.openLinksWithElectron) {
|
||||
return { action: "allow" };
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case "mailto:":
|
||||
case "spotify:":
|
||||
if (isDeckGameMode) {
|
||||
steamOpenURL(url);
|
||||
} else {
|
||||
shell.openExternal(url);
|
||||
}
|
||||
break;
|
||||
case "steam:":
|
||||
if (isDeckGameMode) {
|
||||
execSteamURL(url);
|
||||
} else {
|
||||
shell.openExternal(url);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return { action: "deny" };
|
||||
}
|
||||
|
||||
export function makeLinksOpenExternally(win: BrowserWindow) {
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
switch (url) {
|
||||
case "about:blank":
|
||||
case "https://discord.com/popout":
|
||||
return { action: "allow" };
|
||||
}
|
||||
|
||||
win.webContents.setWindowOpenHandler(({ url, frameName, features }) => {
|
||||
try {
|
||||
var { protocol } = new URL(url);
|
||||
var { protocol, hostname, pathname } = new URL(url);
|
||||
} catch {
|
||||
return { action: "deny" };
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
case "http:":
|
||||
case "https:":
|
||||
if (Settings.store.openLinksWithElectron) {
|
||||
return { action: "allow" };
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case "mailto:":
|
||||
case "steam:":
|
||||
case "spotify:":
|
||||
shell.openExternal(url);
|
||||
if (frameName.startsWith("DISCORD_") && pathname === "/popout" && DISCORD_HOSTNAMES.includes(hostname)) {
|
||||
return createOrFocusPopup(frameName, features);
|
||||
}
|
||||
|
||||
return { action: "deny" };
|
||||
if (url === "about:blank" || (frameName === "authorize" && DISCORD_HOSTNAMES.includes(hostname)))
|
||||
return { action: "allow" };
|
||||
|
||||
return handleExternalUrl(url, protocol);
|
||||
});
|
||||
|
||||
win.webContents.on("did-create-window", (win, { frameName }) => {
|
||||
if (frameName.startsWith("DISCORD_")) setupPopout(win, frameName);
|
||||
});
|
||||
}
|
||||
|
||||
112
src/main/utils/popout.ts
Normal file
112
src/main/utils/popout.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { BrowserWindow, BrowserWindowConstructorOptions } from "electron";
|
||||
|
||||
import { handleExternalUrl } from "./makeLinksOpenExternally";
|
||||
|
||||
const ALLOWED_FEATURES = new Set([
|
||||
"width",
|
||||
"height",
|
||||
"left",
|
||||
"top",
|
||||
"resizable",
|
||||
"movable",
|
||||
"alwaysOnTop",
|
||||
"frame",
|
||||
"transparent",
|
||||
"hasShadow",
|
||||
"closable",
|
||||
"skipTaskbar",
|
||||
"backgroundColor",
|
||||
"menubar",
|
||||
"toolbar",
|
||||
"location",
|
||||
"directories",
|
||||
"titleBarStyle"
|
||||
]);
|
||||
|
||||
const MIN_POPOUT_WIDTH = 320;
|
||||
const MIN_POPOUT_HEIGHT = 180;
|
||||
const DEFAULT_POPOUT_OPTIONS: BrowserWindowConstructorOptions = {
|
||||
title: "Discord Popout",
|
||||
backgroundColor: "#2f3136",
|
||||
minWidth: MIN_POPOUT_WIDTH,
|
||||
minHeight: MIN_POPOUT_HEIGHT,
|
||||
frame: process.platform === "linux",
|
||||
titleBarStyle: process.platform === "darwin" ? "hidden" : undefined,
|
||||
trafficLightPosition:
|
||||
process.platform === "darwin"
|
||||
? {
|
||||
x: 10,
|
||||
y: 3
|
||||
}
|
||||
: undefined,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true
|
||||
}
|
||||
};
|
||||
|
||||
const PopoutWindows = new Map<string, BrowserWindow>();
|
||||
|
||||
function focusWindow(window: BrowserWindow) {
|
||||
window.setAlwaysOnTop(true);
|
||||
window.focus();
|
||||
window.setAlwaysOnTop(false);
|
||||
}
|
||||
|
||||
function parseFeatureValue(feature: string) {
|
||||
if (feature === "yes") return true;
|
||||
if (feature === "no") return false;
|
||||
|
||||
const n = Number(feature);
|
||||
if (!isNaN(n)) return n;
|
||||
|
||||
return feature;
|
||||
}
|
||||
|
||||
function parseWindowFeatures(features: string) {
|
||||
const keyValuesParsed = features.split(",");
|
||||
|
||||
return keyValuesParsed.reduce((features, feature) => {
|
||||
const [key, value] = feature.split("=");
|
||||
if (ALLOWED_FEATURES.has(key)) features[key] = parseFeatureValue(value);
|
||||
|
||||
return features;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function createOrFocusPopup(key: string, features: string) {
|
||||
const existingWindow = PopoutWindows.get(key);
|
||||
if (existingWindow) {
|
||||
focusWindow(existingWindow);
|
||||
return <const>{ action: "deny" };
|
||||
}
|
||||
|
||||
return <const>{
|
||||
action: "allow",
|
||||
overrideBrowserWindowOptions: {
|
||||
...DEFAULT_POPOUT_OPTIONS,
|
||||
...parseWindowFeatures(features)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function setupPopout(win: BrowserWindow, key: string) {
|
||||
PopoutWindows.set(key, win);
|
||||
|
||||
/* win.webContents.on("will-navigate", (evt, url) => {
|
||||
// maybe prevent if not origin match
|
||||
})*/
|
||||
|
||||
win.webContents.setWindowOpenHandler(({ url }) => handleExternalUrl(url));
|
||||
|
||||
win.once("closed", () => {
|
||||
win.removeAllListeners();
|
||||
PopoutWindows.delete(key);
|
||||
});
|
||||
}
|
||||
@@ -4,15 +4,12 @@
|
||||
* 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 { writeFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
||||
import { MessageBoxChoice } from "../constants";
|
||||
import { Settings } from "../settings";
|
||||
|
||||
const exec = promisify(callbackExec);
|
||||
import { State } from "../settings";
|
||||
|
||||
// Bump this to re-show the prompt
|
||||
const layoutVersion = 2;
|
||||
@@ -20,6 +17,8 @@ const layoutVersion = 2;
|
||||
const layoutId = "3080264545"; // Vesktop Layout v2
|
||||
const numberRegex = /^[0-9]*$/;
|
||||
|
||||
let steamPipeQueue = Promise.resolve();
|
||||
|
||||
export const isDeckGameMode = process.env.SteamOS === "1" && process.env.SteamGamepadUI === "1";
|
||||
|
||||
export function applyDeckKeyboardFix() {
|
||||
@@ -42,23 +41,37 @@ function getAppId(): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function execSteamURL(url: string): Promise<void> {
|
||||
await exec(`steam -ifrunning ${url}`);
|
||||
export function execSteamURL(url: string) {
|
||||
// This doesn't allow arbitrary execution despite the weird syntax.
|
||||
steamPipeQueue = steamPipeQueue.then(() =>
|
||||
writeFile(
|
||||
join(process.env.HOME || "/home/deck", ".steam", "steam.pipe"),
|
||||
// replace ' to prevent argument injection
|
||||
`'${process.env.HOME}/.local/share/Steam/ubuntu12_32/steam' '-ifrunning' '${url.replaceAll("'", "%27")}'\n`,
|
||||
"utf-8"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function steamOpenURL(url: string) {
|
||||
execSteamURL(`steam://openurl/${url}`);
|
||||
}
|
||||
|
||||
export async function showGamePage() {
|
||||
const appId = getAppId();
|
||||
if (!appId) return;
|
||||
await execSteamURL(`steam://nav/games/details/${appId}`);
|
||||
}
|
||||
|
||||
async function showLayout(appId: string) {
|
||||
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}`);
|
||||
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);
|
||||
if (State.store.steamOSLayoutVersion === layoutVersion) return;
|
||||
const update = Boolean(State.store.steamOSLayoutVersion);
|
||||
|
||||
// Touch screen breaks in some menus when native touch mode is enabled on latest SteamOS beta, remove most of the update specific text once that's fixed.
|
||||
const { response } = await dialog.showMessageBox(win, {
|
||||
@@ -74,8 +87,8 @@ ${update ? "Click" : "Tap"} no to keep your current layout.`,
|
||||
type: "question"
|
||||
});
|
||||
|
||||
if (Settings.store.steamOSLayoutVersion !== layoutVersion) {
|
||||
Settings.store.steamOSLayoutVersion = layoutVersion;
|
||||
if (State.store.steamOSLayoutVersion !== layoutVersion) {
|
||||
State.store.steamOSLayoutVersion = layoutVersion;
|
||||
}
|
||||
|
||||
if (response === MessageBoxChoice.Cancel) return;
|
||||
|
||||
@@ -51,27 +51,17 @@ ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => {
|
||||
: { 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, (_, targets: string[]) =>
|
||||
obtainVenmic()?.link({
|
||||
include: targets.map(target => ({ key: "application.name", value: target })),
|
||||
exclude: [{ key: "application.process.id", value: getRendererAudioServicePid() }]
|
||||
})
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
IpcEvents.VIRT_MIC_START_SYSTEM,
|
||||
() =>
|
||||
obtainVenmic()?.link({
|
||||
props: [
|
||||
{
|
||||
key: "application.process.id",
|
||||
value: getRendererAudioServicePid()
|
||||
}
|
||||
],
|
||||
mode: "exclude"
|
||||
})
|
||||
ipcMain.handle(IpcEvents.VIRT_MIC_START_SYSTEM, () =>
|
||||
obtainVenmic()?.link({
|
||||
exclude: [{ key: "application.process.id", value: getRendererAudioServicePid() }]
|
||||
})
|
||||
);
|
||||
|
||||
ipcMain.handle(IpcEvents.VIRT_MIC_STOP, () => obtainVenmic()?.unlink());
|
||||
@@ -71,5 +71,9 @@ export const VesktopNative = {
|
||||
onActivity(cb: (data: string) => void) {
|
||||
ipcRenderer.on(IpcEvents.ARRPC_ACTIVITY, (_, data: string) => cb(data));
|
||||
}
|
||||
},
|
||||
clipboard: {
|
||||
copyImage: (imageBuffer: Uint8Array, imageSrc: string) =>
|
||||
invoke<void>(IpcEvents.CLIPBOARD_COPY_IMAGE, imageBuffer, imageSrc)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Margins } from "@vencord/types/utils";
|
||||
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 { isMac } from "renderer/utils";
|
||||
import { isTruthy } from "shared/utils/guards";
|
||||
|
||||
export default function SettingsUi() {
|
||||
@@ -21,10 +21,10 @@ export default function SettingsUi() {
|
||||
const [autoStartEnabled, setAutoStartEnabled] = useState(autostart.isEnabled());
|
||||
|
||||
const allSwitches: Array<false | [keyof typeof Settings, string, string, boolean?, (() => boolean)?]> = [
|
||||
isWindows && [
|
||||
"discordWindowsTitleBar",
|
||||
[
|
||||
"customTitleBar",
|
||||
"Discord Titlebar",
|
||||
"Use Discord's custom title bar instead of the Windows one. Requires a full restart."
|
||||
"Use Discord's custom title bar instead of the native system one. Requires a full restart."
|
||||
],
|
||||
!isMac && ["tray", "Tray Icon", "Add a tray icon for Vesktop", true],
|
||||
!isMac && [
|
||||
@@ -43,14 +43,14 @@ export default function SettingsUi() {
|
||||
["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],
|
||||
["hardwareAcceleration", "Hardware Acceleration", "Enable hardware acceleration", true],
|
||||
["splashTheming", "Splash theming", "Adapt the splash window colors to your custom theme", false],
|
||||
[
|
||||
"openLinksWithElectron",
|
||||
"Open Links in app (experimental)",
|
||||
"Opens links in a new Vesktop window instead of your web browser"
|
||||
],
|
||||
["checkUpdates", "Check for updates", "Automatically check for Vesktop updates", true],
|
||||
["startMinimized", "Start minimized", "Vesktop remains in minimized mode on start", false]
|
||||
["checkUpdates", "Check for updates", "Automatically check for Vesktop updates", true]
|
||||
];
|
||||
|
||||
const switches = allSwitches.filter(isTruthy);
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
|
||||
import "./hideGarbage.css";
|
||||
|
||||
import { waitFor } from "@vencord/types/webpack";
|
||||
|
||||
import { isFirstRun, isWindows, localStorage } from "./utils";
|
||||
import { isWindows, localStorage } from "./utils";
|
||||
|
||||
// Make clicking Notifications focus the window
|
||||
const originalSetOnClick = Object.getOwnPropertyDescriptor(Notification.prototype, "onclick")!.set!;
|
||||
@@ -22,15 +20,8 @@ Object.defineProperty(Notification.prototype, "onclick", {
|
||||
configurable: true
|
||||
});
|
||||
|
||||
if (isFirstRun) {
|
||||
// Hide "Download Discord Desktop now!!!!" banner
|
||||
localStorage.setItem("hideNag", "true");
|
||||
|
||||
// Enable Desktop Notifications by default
|
||||
waitFor("setDesktopType", m => {
|
||||
m.setDesktopType("all");
|
||||
});
|
||||
}
|
||||
// Hide "Download Discord Desktop now!!!!" banner
|
||||
localStorage.setItem("hideNag", "true");
|
||||
|
||||
// FIXME: Remove eventually.
|
||||
// Originally, Vencord always used a Windows user agent. This seems to cause captchas
|
||||
|
||||
21
src/renderer/patches/enableNotificationsByDefault.ts
Normal file
21
src/renderer/patches/enableNotificationsByDefault.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
find: '"NotificationSettingsStore',
|
||||
replacement: {
|
||||
// FIXME: fix eslint rule
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /\.isPlatformEmbedded(?=\?\i\.DesktopNotificationTypes\.ALL)/g,
|
||||
replace: "$&||true"
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -5,7 +5,8 @@
|
||||
*/
|
||||
|
||||
// TODO: Possibly auto generate glob if we have more patches in the future
|
||||
import "./spellCheck";
|
||||
import "./enableNotificationsByDefault";
|
||||
import "./platformClass";
|
||||
import "./windowsTitleBar";
|
||||
import "./screenShareAudio";
|
||||
import "./spellCheck";
|
||||
import "./windowsTitleBar";
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Settings } from "renderer/settings";
|
||||
import { isMac, isWindows } from "renderer/utils";
|
||||
import { isMac } from "renderer/utils";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
@@ -22,8 +22,8 @@ addPatch({
|
||||
],
|
||||
|
||||
getPlatformClass() {
|
||||
if (Settings.store.customTitleBar) return "platform-win";
|
||||
if (isMac) return "platform-osx";
|
||||
if (isWindows && Settings.store.discordWindowsTitleBar) return "platform-win";
|
||||
return "platform-web";
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Settings } from "renderer/settings";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
if (Settings.store.discordWindowsTitleBar)
|
||||
if (Settings.store.customTitleBar)
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
|
||||
@@ -47,5 +47,7 @@ export const enum IpcEvents {
|
||||
VIRT_MIC_START_SYSTEM = "VCD_VIRT_MIC_START_ALL",
|
||||
VIRT_MIC_STOP = "VCD_VIRT_MIC_STOP",
|
||||
|
||||
ARRPC_ACTIVITY = "VCD_ARRPC_ACTIVITY"
|
||||
ARRPC_ACTIVITY = "VCD_ARRPC_ACTIVITY",
|
||||
|
||||
CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE"
|
||||
}
|
||||
|
||||
22
src/shared/settings.d.ts
vendored
22
src/shared/settings.d.ts
vendored
@@ -16,23 +16,29 @@ export interface Settings {
|
||||
staticTitle?: boolean;
|
||||
enableMenu?: boolean;
|
||||
disableSmoothScroll?: boolean;
|
||||
hardwareAcceleration?: boolean;
|
||||
arRPC?: boolean;
|
||||
appBadge?: boolean;
|
||||
discordWindowsTitleBar?: boolean;
|
||||
startMinimized?: boolean;
|
||||
|
||||
maximized?: boolean;
|
||||
minimized?: boolean;
|
||||
windowBounds?: Rectangle;
|
||||
disableMinSize?: boolean;
|
||||
|
||||
/** @deprecated use customTitleBar */
|
||||
discordWindowsTitleBar?: boolean;
|
||||
customTitleBar?: boolean;
|
||||
|
||||
checkUpdates?: boolean;
|
||||
skippedUpdate?: string;
|
||||
firstLaunch?: boolean;
|
||||
|
||||
splashTheming?: boolean;
|
||||
splashColor?: string;
|
||||
splashBackground?: string;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
maximized?: boolean;
|
||||
minimized?: boolean;
|
||||
windowBounds?: Rectangle;
|
||||
|
||||
skippedUpdate?: string;
|
||||
firstLaunch?: boolean;
|
||||
|
||||
steamOSLayoutVersion?: number;
|
||||
}
|
||||
|
||||
@@ -144,4 +144,11 @@ export class SettingsStore<T extends object> {
|
||||
listeners.delete(cb);
|
||||
if (!listeners.size) this.pathListeners.delete(path as string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call all global change listeners
|
||||
*/
|
||||
public markAsChanged() {
|
||||
this.globalListeners.forEach(cb => cb(this.plain, ""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { app, BrowserWindow, shell } from "electron";
|
||||
import { Settings } from "main/settings";
|
||||
import { Settings, State } from "main/settings";
|
||||
import { handle } from "main/utils/ipcWrappers";
|
||||
import { makeLinksOpenExternally } from "main/utils/makeLinksOpenExternally";
|
||||
import { githubGet, ReleaseData } from "main/utils/vencordLoader";
|
||||
@@ -52,7 +52,7 @@ handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
||||
});
|
||||
|
||||
handle(IpcEvents.UPDATE_IGNORE, () => {
|
||||
Settings.store.skippedUpdate = updateData.latestVersion;
|
||||
State.store.skippedUpdate = updateData.latestVersion;
|
||||
});
|
||||
|
||||
function isOutdated(oldVersion: string, newVersion: string) {
|
||||
@@ -91,7 +91,7 @@ export async function checkUpdates() {
|
||||
release: data
|
||||
};
|
||||
|
||||
if (Settings.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) {
|
||||
if (State.store.skippedUpdate !== newVersion && isOutdated(oldVersion, newVersion)) {
|
||||
openNewUpdateWindow();
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
<link rel="stylesheet" href="./style.css" type="text/css" />
|
||||
|
||||
<style>
|
||||
* {
|
||||
body {
|
||||
user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
|
||||
Reference in New Issue
Block a user