Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45f56c63a0 | ||
|
|
89af4316d3 | ||
|
|
a9bfb857ae | ||
|
|
b9e411ac90 | ||
|
|
670de01938 | ||
|
|
ef064eba3d | ||
|
|
d5f63da939 | ||
|
|
2aadc61af9 | ||
|
|
061fec44af | ||
|
|
b876f450c3 | ||
|
|
5f5febda9d | ||
|
|
94ba59afb5 | ||
|
|
566737017c | ||
|
|
196ee4e42c | ||
|
|
9bb02f8581 | ||
|
|
c76d7195a5 | ||
|
|
50a103710d | ||
|
|
e6e66e775c | ||
|
|
a5ec031a2f | ||
|
|
4dceadbbd2 | ||
|
|
6ee920ff2c | ||
|
|
28ad4a6f73 | ||
|
|
c5ac3e64a6 | ||
|
|
6dc26aea6a | ||
|
|
9003b94f85 | ||
|
|
f7b7931847 | ||
|
|
b87bcaefe9 | ||
|
|
3108de7c79 | ||
|
|
57006e7e52 | ||
|
|
8f1ea1f440 | ||
|
|
26b6fb13d4 | ||
|
|
e1971f55a0 | ||
|
|
ba2618878e | ||
|
|
87595deae7 | ||
|
|
6ab7030fdf | ||
|
|
5ba84e8a0d | ||
|
|
61f9559984 | ||
|
|
5fa9264bdb | ||
|
|
c9f0920f71 | ||
|
|
f502d04b5f | ||
|
|
08090e3764 | ||
|
|
a95f7f8fbd | ||
|
|
9d62cc9437 | ||
|
|
2b4f7a07d2 |
17
.github/workflows/winget-submission.yml
vendored
Normal file
17
.github/workflows/winget-submission.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Submit to Winget Community Repo
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
winget:
|
||||
name: Publish winget package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Submit package to Winget Community Repo
|
||||
uses: vedantmgoyal2009/winget-releaser@52ef3f3028ed79a9606d7678d0a88d295bc0c690 # v2
|
||||
with:
|
||||
identifier: Vencord.Vesktop
|
||||
token: ${{ secrets.WINGET_PAT }}
|
||||
installers-regex: '\.exe$'
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
dist
|
||||
node_modules
|
||||
.env
|
||||
.DS_Store
|
||||
.idea/
|
||||
BIN
build/icon.icns
Normal file
BIN
build/icon.icns
Normal file
Binary file not shown.
47
package.json
47
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "VencordDesktop",
|
||||
"version": "0.2.7",
|
||||
"version": "0.3.3",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
@@ -23,34 +23,34 @@
|
||||
"watch": "pnpm build --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"arrpc": "github:OpenAsar/arrpc#061d473dc742266fc7f61587ed18e666543fed1e"
|
||||
"arrpc": "github:OpenAsar/arrpc#89f4da610ccfac93f461826a446a17cd3b23953d"
|
||||
},
|
||||
"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.4.6",
|
||||
"@types/react": "^18.2.18",
|
||||
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
||||
"@typescript-eslint/parser": "^6.2.1",
|
||||
"@vencord/types": "^0.1.2",
|
||||
"dotenv": "^16.0.3",
|
||||
"electron": "^25.2.0",
|
||||
"electron-builder": "^23.6.0",
|
||||
"esbuild": "^0.17.14",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"electron": "^25.8.4",
|
||||
"electron-builder": "^24.6.3",
|
||||
"esbuild": "^0.18.17",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-config-prettier": "^8.9.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.0",
|
||||
"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.0.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"tsx": "^3.12.6",
|
||||
"type-fest": "^3.8.0",
|
||||
"typescript": "^5.0.2"
|
||||
"tsx": "^3.12.7",
|
||||
"type-fest": "^4.1.0",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"packageManager": "pnpm@8.1.1",
|
||||
"packageManager": "pnpm@8.6.11",
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"pnpm": ">=8"
|
||||
@@ -66,6 +66,7 @@
|
||||
"LICENSE"
|
||||
],
|
||||
"linux": {
|
||||
"icon": "build/icon.icns",
|
||||
"category": "Network",
|
||||
"maintainer": "vendicated+vesktop@riseup.net",
|
||||
"target": [
|
||||
@@ -79,7 +80,8 @@
|
||||
"GenericName": "Internet Messenger",
|
||||
"Type": "Application",
|
||||
"Categories": "Network;InstantMessaging;Chat;",
|
||||
"Keywords": "discord;vencord;electron;chat;"
|
||||
"Keywords": "discord;vencord;electron;chat;",
|
||||
"WMClass": "VencordDesktop"
|
||||
}
|
||||
},
|
||||
"mac": {
|
||||
@@ -101,12 +103,11 @@
|
||||
"win": {
|
||||
"target": [
|
||||
"nsis",
|
||||
"portable"
|
||||
"zip"
|
||||
]
|
||||
},
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"releaseType": "release"
|
||||
"provider": "github"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1525
pnpm-lock.yaml
generated
1525
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -23,11 +23,17 @@ let lastIndex: null | number = -1;
|
||||
|
||||
export function setBadgeCount(count: number) {
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
case "linux":
|
||||
if (count === -1) count = 0;
|
||||
app.setBadgeCount(count);
|
||||
break;
|
||||
case "darwin":
|
||||
if (count === 0) {
|
||||
app.dock.setBadge("");
|
||||
break;
|
||||
}
|
||||
app.dock.setBadge(count === -1 ? "•" : count.toString());
|
||||
break;
|
||||
case "win32":
|
||||
const [index, description] = getBadgeIndexAndDescription(count);
|
||||
if (lastIndex === index) break;
|
||||
|
||||
@@ -11,6 +11,7 @@ export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("u
|
||||
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
||||
export const VENCORD_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
|
||||
|
||||
@@ -9,7 +9,7 @@ import { BrowserWindow } from "electron/main";
|
||||
import { copyFileSync, mkdirSync, readdirSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { SplashProps } from "shared/browserWinProperties";
|
||||
import { VIEW_DIR } from "shared/paths";
|
||||
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||
|
||||
import { autoStart } from "./autoStart";
|
||||
import { DATA_DIR } from "./constants";
|
||||
@@ -31,7 +31,8 @@ export function createFirstLaunchTour() {
|
||||
frame: true,
|
||||
autoHideMenuBar: true,
|
||||
height: 470,
|
||||
width: 550
|
||||
width: 550,
|
||||
icon: ICON_PATH
|
||||
});
|
||||
|
||||
makeLinksOpenExternally(win);
|
||||
|
||||
@@ -9,7 +9,6 @@ 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";
|
||||
@@ -48,7 +47,6 @@ 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();
|
||||
bootstrap();
|
||||
|
||||
109
src/main/ipc.ts
109
src/main/ipc.ts
@@ -4,8 +4,9 @@
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { app, dialog, ipcMain, session, shell } from "electron";
|
||||
import { existsSync, readFileSync, watch } from "fs";
|
||||
import { execFile } from "child_process";
|
||||
import { app, 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";
|
||||
@@ -14,69 +15,76 @@ 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.SUPPORTS_WINDOWS_TRANSPARENCY, e => {
|
||||
e.returnValue = process.platform === "win32" && Number(release().split(".").pop()) >= 22621;
|
||||
});
|
||||
|
||||
ipcMain.on(IpcEvents.AUTOSTART_ENABLED, e => {
|
||||
e.returnValue = autoStart.isEnabled();
|
||||
});
|
||||
ipcMain.handle(IpcEvents.ENABLE_AUTOSTART, autoStart.enable);
|
||||
ipcMain.handle(IpcEvents.DISABLE_AUTOSTART, autoStart.disable);
|
||||
|
||||
ipcMain.handle(IpcEvents.SET_SETTINGS, (_, settings: typeof Settings.store, path?: string) => {
|
||||
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, () => {
|
||||
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 => {
|
||||
mainWin.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;
|
||||
@@ -84,29 +92,27 @@ ipcMain.handle(IpcEvents.SPELLCHECK_SET_LANGUAGES, (_, languages: string[]) => {
|
||||
if (applicable.length) ses.setSpellCheckerLanguages(applicable);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SPELLCHECK_REPLACE_MISSPELLING, (e, word: string) => {
|
||||
handle(IpcEvents.SPELLCHECK_REPLACE_MISSPELLING, (e, word: string) => {
|
||||
e.sender.replaceMisspelling(word);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => {
|
||||
handle(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, (e, word: string) => {
|
||||
e.sender.session.addWordToSpellCheckerDictionary(word);
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
||||
handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
||||
const res = await dialog.showOpenDialog(mainWin!, {
|
||||
properties: ["openDirectory"]
|
||||
});
|
||||
if (!res.filePaths.length) return "cancelled";
|
||||
|
||||
const dir = res.filePaths[0];
|
||||
for (const file of ["vencordDesktopMain.js", "preload.js", "vencordDesktopRenderer.js", "renderer.css"]) {
|
||||
if (!existsSync(join(dir, file))) return "invalid";
|
||||
}
|
||||
if (!isValidVencordInstall(dir)) return "invalid";
|
||||
|
||||
return dir;
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||
handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||
|
||||
function readCss() {
|
||||
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
||||
@@ -122,3 +128,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);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -147,6 +147,8 @@ async function clearData(win: BrowserWindow) {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
type MenuItemList = Array<MenuItemConstructorOptions | false>;
|
||||
|
||||
function initMenuBar(win: BrowserWindow) {
|
||||
const isWindows = process.platform === "win32";
|
||||
const isDarwin = process.platform === "darwin";
|
||||
@@ -181,14 +183,38 @@ function initMenuBar(win: BrowserWindow) {
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
isDarwin && {
|
||||
label: "Hide",
|
||||
role: "hide"
|
||||
},
|
||||
isDarwin && {
|
||||
label: "Hide others",
|
||||
role: "hideOthers"
|
||||
},
|
||||
...(!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,
|
||||
@@ -213,7 +239,7 @@ function initMenuBar(win: BrowserWindow) {
|
||||
role: "zoomIn",
|
||||
visible: false
|
||||
}
|
||||
] satisfies Array<MenuItemConstructorOptions | false>;
|
||||
] satisfies MenuItemList;
|
||||
|
||||
const menu = Menu.buildFromTemplate([
|
||||
{
|
||||
@@ -298,6 +324,10 @@ function initSettingsListeners(win: BrowserWindow) {
|
||||
win.setBackgroundColor("#ffffff");
|
||||
}
|
||||
});
|
||||
|
||||
addSettingsListener("enableMenu", enabled => {
|
||||
win.setAutoHideMenuBar(enabled ?? false);
|
||||
});
|
||||
}
|
||||
|
||||
function initSpellCheck(win: BrowserWindow) {
|
||||
@@ -311,8 +341,11 @@ function createMainWindow() {
|
||||
removeSettingsListeners();
|
||||
removeVencordSettingsListeners();
|
||||
|
||||
const { staticTitle, transparencyOption } = Settings.store;
|
||||
const { staticTitle, transparencyOption, enableMenu, discordWindowsTitleBar } = Settings.store;
|
||||
const { frameless, macosTranslucency } = VencordSettings.store;
|
||||
|
||||
const noFrame = frameless === true || (process.platform === "win32" && discordWindowsTitleBar === true);
|
||||
|
||||
const win = (mainWin = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
@@ -324,7 +357,7 @@ function createMainWindow() {
|
||||
spellcheck: true
|
||||
},
|
||||
icon: ICON_PATH,
|
||||
frame: frameless !== true,
|
||||
frame: !noFrame,
|
||||
...(transparencyOption && transparencyOption !== "none"
|
||||
? {
|
||||
backgroundColor: "#00000000",
|
||||
@@ -332,7 +365,7 @@ function createMainWindow() {
|
||||
transparent: true
|
||||
}
|
||||
: {}),
|
||||
...(staticTitle ? { title: "Vencord" } : {}),
|
||||
...(staticTitle ? { title: "Vesktop" } : {}),
|
||||
...(macosTranslucency
|
||||
? {
|
||||
vibrancy: "sidebar",
|
||||
@@ -340,12 +373,13 @@ function createMainWindow() {
|
||||
}
|
||||
: {}),
|
||||
...(process.platform === "darwin" ? { titleBarStyle: "hiddenInset" } : {}),
|
||||
...getWindowBoundsOptions()
|
||||
...getWindowBoundsOptions(),
|
||||
autoHideMenuBar: enableMenu
|
||||
}));
|
||||
win.setMenuBarVisibility(false);
|
||||
|
||||
win.on("close", e => {
|
||||
const useTray = Settings.store.minimizeToTray && Settings.store.tray;
|
||||
const useTray = Settings.store.minimizeToTray !== false && Settings.store.tray !== false;
|
||||
if (isQuitting || (process.platform !== "darwin" && !useTray)) return;
|
||||
|
||||
e.preventDefault();
|
||||
@@ -359,7 +393,7 @@ function createMainWindow() {
|
||||
if (Settings.store.staticTitle) win.on("page-title-updated", e => e.preventDefault());
|
||||
|
||||
initWindowBoundsListeners(win);
|
||||
if (Settings.store.tray ?? true) initTray(win);
|
||||
if ((Settings.store.tray ?? true) && process.platform !== "darwin") initTray(win);
|
||||
initMenuBar(win);
|
||||
makeLinksOpenExternally(win);
|
||||
initSettingsListeners(win);
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
* 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";
|
||||
|
||||
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: {
|
||||
@@ -29,12 +31,22 @@ export function registerScreenShareHandler() {
|
||||
}
|
||||
});
|
||||
|
||||
const isWayland =
|
||||
process.platform === "linux" &&
|
||||
(process.env.XDG_SESSION_TYPE === "wayland" || !!process.env.WAYLAND_DISPLAY);
|
||||
|
||||
const data = sources.map(({ id, name, thumbnail }) => ({
|
||||
id,
|
||||
name,
|
||||
url: thumbnail.toDataURL()
|
||||
}));
|
||||
|
||||
if (isWayland) {
|
||||
const video = data[0];
|
||||
callback(video ? { video } : {});
|
||||
return;
|
||||
}
|
||||
|
||||
const choice = await request.frame
|
||||
.executeJavaScript(`Vesktop.Components.ScreenShare.openScreenSharePicker(${JSON.stringify(data)})`)
|
||||
.then(e => e as StreamPick)
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
import { BrowserWindow } from "electron";
|
||||
import { join } from "path";
|
||||
import { SplashProps } from "shared/browserWinProperties";
|
||||
import { VIEW_DIR } from "shared/paths";
|
||||
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||
|
||||
export function createSplashWindow() {
|
||||
const splash = new BrowserWindow(SplashProps);
|
||||
const splash = new BrowserWindow({
|
||||
...SplashProps,
|
||||
icon: ICON_PATH
|
||||
});
|
||||
|
||||
splash.loadFile(join(VIEW_DIR, "splash.html"));
|
||||
|
||||
|
||||
36
src/main/utils/ipcWrappers.ts
Normal file
36
src/main/utils/ipcWrappers.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { ipcMain, IpcMainEvent, IpcMainInvokeEvent, WebFrameMain } from "electron";
|
||||
import { IpcEvents } from "shared/IpcEvents";
|
||||
|
||||
export function validateSender(frame: WebFrameMain) {
|
||||
const { hostname, protocol } = new URL(frame.url);
|
||||
if (protocol === "file:") return;
|
||||
|
||||
switch (hostname) {
|
||||
case "discord.com":
|
||||
case "ptb.discord.com":
|
||||
case "canary.discord.com":
|
||||
break;
|
||||
default:
|
||||
throw new Error("ipc: Disallowed host " + hostname);
|
||||
}
|
||||
}
|
||||
|
||||
export function handleSync(event: IpcEvents, cb: (e: IpcMainEvent, ...args: any[]) => any) {
|
||||
ipcMain.on(event, (e, ...args) => {
|
||||
validateSender(e.senderFrame);
|
||||
e.returnValue = cb(e, ...args);
|
||||
});
|
||||
}
|
||||
|
||||
export function handle(event: IpcEvents, cb: (e: IpcMainInvokeEvent, ...args: any[]) => any) {
|
||||
ipcMain.handle(event, (e, ...args) => {
|
||||
validateSender(e.senderFrame);
|
||||
return cb(e, ...args);
|
||||
});
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -51,7 +51,10 @@ export const VesktopNative = {
|
||||
addToDictionary: (word: string) => invoke<void>(IpcEvents.SPELLCHECK_ADD_TO_DICTIONARY, word)
|
||||
},
|
||||
win: {
|
||||
focus: () => invoke<void>(IpcEvents.FOCUS)
|
||||
focus: () => invoke<void>(IpcEvents.FOCUS),
|
||||
close: () => invoke<void>(IpcEvents.CLOSE),
|
||||
minimize: () => invoke<void>(IpcEvents.MINIMIZE),
|
||||
maximize: () => invoke<void>(IpcEvents.MAXIMIZE)
|
||||
},
|
||||
capturer: {
|
||||
getLargeThumbnail: (id: string) => invoke<string>(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, id)
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
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();
|
||||
@@ -18,9 +20,14 @@ export default function SettingsUi() {
|
||||
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 Vesktop", 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 Vesktop minimize to the tray instead of closing",
|
||||
@@ -33,14 +40,17 @@ 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."],
|
||||
[
|
||||
"openLinksWithElectron",
|
||||
"Open Links in app (experimental)",
|
||||
"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']
|
||||
]
|
||||
];
|
||||
|
||||
const switches = allSwitches.filter(isTruthy);
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Text variant="heading-lg/semibold" style={{ color: "var(--header-primary)" }} tag="h2">
|
||||
@@ -159,8 +169,14 @@ export default function SettingsUi() {
|
||||
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;
|
||||
|
||||
@@ -3,3 +3,8 @@
|
||||
[class|=listItem]:has(+ [class|=listItem] [data-list-item-id=guildsnav___app-download-button]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* FIXME: Remove after 23/08/23 */
|
||||
.vc-desktop-settings {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ 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 };
|
||||
|
||||
@@ -35,6 +36,17 @@ export async function openInviteModal(code: string) {
|
||||
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)"];
|
||||
|
||||
arRPC.required = !!Settings.store.arRPC;
|
||||
|
||||
@@ -7,3 +7,4 @@
|
||||
// TODO: Possibly auto generate glob if we have more patches in the future
|
||||
import "./spellCheck";
|
||||
import "./platformClass";
|
||||
import "./windowsTitleBar";
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { Settings } from "renderer/settings";
|
||||
import { isMac, isWindows } from "renderer/utils";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
addPatch({
|
||||
@@ -18,5 +21,9 @@ addPatch({
|
||||
}
|
||||
],
|
||||
|
||||
getPlatformClass: () => (navigator.platform.toLowerCase().startsWith("mac") ? "platform-osx" : "platform-web")
|
||||
getPlatformClass() {
|
||||
if (isMac) return "platform-osx";
|
||||
if (isWindows && Settings.store.discordWindowsTitleBar) return "platform-win";
|
||||
return "platform-web";
|
||||
}
|
||||
});
|
||||
|
||||
30
src/renderer/patches/windowsTitleBar.tsx
Normal file
30
src/renderer/patches/windowsTitleBar.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { Settings } from "renderer/settings";
|
||||
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
if (Settings.store.discordWindowsTitleBar)
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
find: ".wordmarkWindows",
|
||||
replacement: [
|
||||
{
|
||||
// TODO: Fix eslint rule
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
match: /case \i\.\i\.WINDOWS:/,
|
||||
replace: 'case "WEB":'
|
||||
},
|
||||
...["close", "minimize", "maximize"].map(op => ({
|
||||
match: new RegExp(String.raw`\i\.\i\.${op}\b`),
|
||||
replace: `VesktopNative.win.${op}`
|
||||
}))
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -16,3 +16,4 @@ export const isFirstRun = (() => {
|
||||
const { platform } = navigator;
|
||||
|
||||
export const isWindows = platform.startsWith("Win");
|
||||
export const isMac = platform.startsWith("Mac");
|
||||
|
||||
@@ -16,6 +16,8 @@ export const enum IpcEvents {
|
||||
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",
|
||||
|
||||
21
src/shared/settings.d.ts
vendored
21
src/shared/settings.d.ts
vendored
@@ -7,20 +7,23 @@
|
||||
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;
|
||||
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;
|
||||
appBadge?: boolean;
|
||||
|
||||
skippedUpdate?: string;
|
||||
firstLaunch?: boolean;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
* 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 { IpcEvents } from "shared/IpcEvents";
|
||||
import { VIEW_DIR } from "shared/paths";
|
||||
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||
|
||||
export interface UpdateData {
|
||||
currentVersion: string;
|
||||
@@ -20,8 +21,8 @@ export interface UpdateData {
|
||||
|
||||
let updateData: UpdateData;
|
||||
|
||||
ipcMain.handle(IpcEvents.UPDATER_GET_DATA, () => updateData);
|
||||
ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
||||
handle(IpcEvents.UPDATER_GET_DATA, () => updateData);
|
||||
handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
||||
const portable = !!process.env.PORTABLE_EXECUTABLE_FILE;
|
||||
|
||||
const { assets } = updateData.release;
|
||||
@@ -35,7 +36,11 @@ ipcMain.handle(IpcEvents.UPDATER_DOWNLOAD, () => {
|
||||
return portable ? !isSetup : isSetup;
|
||||
})!.browser_download_url;
|
||||
case "darwin":
|
||||
return assets.find(a => a.name.endsWith(".dmg"))!.browser_download_url;
|
||||
return assets.find(a =>
|
||||
process.arch === "arm64"
|
||||
? a.name.endsWith("-arm64-mac.zip")
|
||||
: a.name.endsWith("-mac.zip") && !a.name.includes("arm64")
|
||||
)!.browser_download_url;
|
||||
case "linux":
|
||||
return updateData.release.html_url;
|
||||
default:
|
||||
@@ -46,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;
|
||||
});
|
||||
|
||||
@@ -103,7 +108,8 @@ function openNewUpdateWindow() {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
sandbox: true
|
||||
}
|
||||
},
|
||||
icon: ICON_PATH
|
||||
});
|
||||
|
||||
makeLinksOpenExternally(win);
|
||||
|
||||
Reference in New Issue
Block a user