Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34eb14f9ed | ||
|
|
44438441d6 | ||
|
|
bbe670fc92 | ||
|
|
3da4c02129 | ||
|
|
19c43289f6 | ||
|
|
1980606e03 | ||
|
|
17150503d2 | ||
|
|
fe58dcfa51 | ||
|
|
f357ee4260 | ||
|
|
9d144a11be | ||
|
|
d9f2b15e84 | ||
|
|
cf23b6d028 | ||
|
|
2efed86b71 | ||
|
|
816ba5d1d0 | ||
|
|
91e1cc02ce | ||
|
|
8630f79b06 | ||
|
|
79d73e23c0 | ||
|
|
18a77d45b8 | ||
|
|
f9ebc16656 | ||
|
|
0296f78731 | ||
|
|
b151631d03 | ||
|
|
d817950681 | ||
|
|
c0ba813041 |
9
.github/workflows/release.yml
vendored
9
.github/workflows/release.yml
vendored
@@ -1,6 +1,9 @@
|
||||
name: Build/release
|
||||
name: Release
|
||||
|
||||
on: push
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@@ -22,4 +25,4 @@ jobs:
|
||||
uses: samuelmeuli/action-electron-builder@e4b12cd06ddf023422f1ac4e39632bd76f6e6928
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
RELEASE: true
|
||||
|
||||
50
README.md
50
README.md
@@ -1,6 +1,54 @@
|
||||
# Vencord Desktop
|
||||
|
||||
A standalone Electron app that loads Discord & Vencord (very early and unfinished)
|
||||
A standalone Electron app that loads Discord & Vencord
|
||||
|
||||
Vencord Desktop is currently in very early alpha. Bug reports, feature requests & contributions are highly appreciated!!
|
||||
|
||||
## Installing
|
||||
|
||||
### Windows
|
||||
|
||||
Download and run Vencord-Desktop-Setup-VERSION.exe from [releases](https://github.com/Vencord/Desktop/releases/latest)
|
||||
|
||||
### Mac
|
||||
|
||||
Download and run Vencord-Desktop-VERSION.dmg from [releases](https://github.com/Vencord/Desktop/releases/latest)
|
||||
|
||||
### Linux
|
||||
|
||||
#### Ubuntu/Debian based
|
||||
|
||||
Download Vencord-Desktop-VERSION.deb from [releases](https://github.com/Vencord/Desktop/releases/latest)
|
||||
|
||||
#### Fedora/RHEL based
|
||||
|
||||
Download Vencord-Desktop-VERSION.rpm from [releases](https://github.com/Vencord/Desktop/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`.
|
||||
|
||||
An AUR package and flatpak are planned, if you want packages for other repos, feel free to create them and they can be linked as unofficial here
|
||||
|
||||
## Building
|
||||
|
||||
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
|
||||
|
||||
# Install Dependencies
|
||||
pnpm i
|
||||
|
||||
# Either run it without packaging
|
||||
pnpm start
|
||||
|
||||
# Or package
|
||||
pnpm package
|
||||
# Or package to a directory only
|
||||
pnpm package:dir
|
||||
```
|
||||
|
||||
## Motivation
|
||||
|
||||
|
||||
44
package.json
44
package.json
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"name": "vencorddesktop",
|
||||
"name": "VencordDesktop",
|
||||
"version": "0.1.3",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"license": "GPL-3.0",
|
||||
"author": "Vendicated <vendicated@riseup.net>",
|
||||
"main": "dist/js/main.js",
|
||||
"scripts": {
|
||||
"build": "tsx scripts/build.mts",
|
||||
"watch": "pnpm build --watch",
|
||||
"package": "pnpm build && electron-builder",
|
||||
"package:dir": "pnpm build && electron-builder --dir",
|
||||
"start": "pnpm build && electron .",
|
||||
"start:watch": "tsx scripts/startWatch.mts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"watch": "pnpm build --watch"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Vendicated <vendicated@riseup.net>",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.15.11",
|
||||
"electron": "^23.2.0",
|
||||
@@ -26,13 +26,13 @@
|
||||
},
|
||||
"build": {
|
||||
"appId": "dev.vencord.desktop",
|
||||
"productName": "Vencord Desktop",
|
||||
"mac": {
|
||||
"category": "Network"
|
||||
},
|
||||
"nsis": {
|
||||
"include": "build/installer.nsh"
|
||||
},
|
||||
"files": [
|
||||
"!*",
|
||||
"dist/js",
|
||||
"static",
|
||||
"package.json",
|
||||
"LICENSE"
|
||||
],
|
||||
"linux": {
|
||||
"category": "Network",
|
||||
"maintainer": "vendicated+vencord-desktop@riseup.net",
|
||||
@@ -43,12 +43,14 @@
|
||||
"AppImage"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"!*",
|
||||
"dist/js",
|
||||
"static",
|
||||
"package.json",
|
||||
"LICENSE"
|
||||
]
|
||||
"mac": {
|
||||
"category": "Network"
|
||||
},
|
||||
"nsis": {
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"include": "build/installer.nsh",
|
||||
"oneClick": false
|
||||
},
|
||||
"productName": "Vencord Desktop"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { BuildContext, BuildOptions, context } from "esbuild";
|
||||
|
||||
const NodeCommonOpts: BuildOptions = {
|
||||
format: "cjs",
|
||||
platform: "node",
|
||||
external: ["electron"],
|
||||
const CommonOpts: BuildOptions = {
|
||||
minify: true,
|
||||
bundle: true,
|
||||
sourcemap: "linked",
|
||||
logLevel: "info"
|
||||
};
|
||||
|
||||
const NodeCommonOpts: BuildOptions = {
|
||||
...CommonOpts,
|
||||
format: "cjs",
|
||||
platform: "node",
|
||||
external: ["electron"],
|
||||
target: ["esnext"],
|
||||
};
|
||||
|
||||
const contexts = [] as BuildContext[];
|
||||
async function createContext(options: BuildOptions) {
|
||||
contexts.push(await context(options));
|
||||
@@ -25,6 +30,12 @@ await Promise.all([
|
||||
...NodeCommonOpts,
|
||||
entryPoints: ["src/preload/index.ts"],
|
||||
outfile: "dist/js/preload.js"
|
||||
}),
|
||||
createContext({
|
||||
...CommonOpts,
|
||||
entryPoints: ["src/renderer/index.ts"],
|
||||
outfile: "dist/js/renderer.js",
|
||||
format: "iife",
|
||||
})
|
||||
]);
|
||||
|
||||
|
||||
8
src/globals.d.ts
vendored
Normal file
8
src/globals.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
declare global {
|
||||
export var VencordDesktop: typeof import("./preload/VencordDesktop").VencordDesktop;
|
||||
// TODO
|
||||
export var Vencord: any;
|
||||
export var vcdLS: typeof localStorage;
|
||||
}
|
||||
|
||||
export { };
|
||||
18
src/main/about.ts
Normal file
18
src/main/about.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { BrowserWindow } from "electron";
|
||||
import { join } from "path";
|
||||
import { ICON_PATH, STATIC_DIR } from "shared/paths";
|
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||
|
||||
export function createAboutWindow() {
|
||||
const about = new BrowserWindow({
|
||||
center: true,
|
||||
autoHideMenuBar: true,
|
||||
icon: ICON_PATH
|
||||
});
|
||||
|
||||
makeLinksOpenExternally(about);
|
||||
|
||||
about.loadFile(join(STATIC_DIR, "about.html"));
|
||||
|
||||
return about;
|
||||
}
|
||||
@@ -3,5 +3,8 @@ import { join } from "path";
|
||||
|
||||
export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR ?? join(app.getPath("userData"), "VencordDesktop");
|
||||
export const VENCORD_FILES_DIR = join(DATA_DIR, "vencordDist");
|
||||
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 USER_AGENT = `VencordDesktop/${app.getVersion()} (https://github.com/Vencord/Electron)`;
|
||||
|
||||
@@ -9,7 +9,9 @@ import { DATA_DIR, VENCORD_FILES_DIR } from "./constants";
|
||||
import { once } from "../shared/utils/once";
|
||||
import { ensureVencordFiles } from "./utils/vencordLoader";
|
||||
|
||||
import { ICON_PATH } from "../shared/paths";
|
||||
import "./ipc";
|
||||
import { Settings } from "./settings";
|
||||
|
||||
// Make the Vencord files use our DATA_DIR
|
||||
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
|
||||
@@ -25,11 +27,17 @@ if (!app.requestSingleInstanceLock()) {
|
||||
app.on("second-instance", () => {
|
||||
if (mainWin) {
|
||||
if (mainWin.isMinimized()) mainWin.restore();
|
||||
if (!mainWin.isVisible()) mainWin.show();
|
||||
mainWin.focus();
|
||||
}
|
||||
});
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
if (process.platform === "win32")
|
||||
app.setAppUserModelId("dev.vencord.desktop");
|
||||
else if (process.platform === "darwin")
|
||||
app.dock.setIcon(ICON_PATH);
|
||||
|
||||
createWindows();
|
||||
|
||||
app.on('activate', () => {
|
||||
@@ -49,6 +57,10 @@ async function createWindows() {
|
||||
mainWin.once("ready-to-show", () => {
|
||||
splash.destroy();
|
||||
mainWin!.show();
|
||||
|
||||
if (Settings.maximized) {
|
||||
mainWin!.maximize();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,51 @@
|
||||
import { app, ipcMain } from "electron";
|
||||
import { app, ipcMain, shell } from "electron";
|
||||
import { readFileSync, watch } from "fs";
|
||||
import { open, readFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { GET_PRELOAD_FILE, RELAUNCH } from "../shared/IpcEvents";
|
||||
import { VENCORD_FILES_DIR } from "./constants";
|
||||
import { debounce } from "shared/utils/debounce";
|
||||
import { FOCUS, GET_RENDERER_SCRIPT, GET_RENDERER_STYLES, GET_SETTINGS, GET_VENCORD_PRELOAD_FILE, RELAUNCH, SET_SETTINGS, SHOW_ITEM_IN_FOLDER } from "../shared/IpcEvents";
|
||||
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE } from "./constants";
|
||||
import { mainWin } from "./mainWindow";
|
||||
import { PlainSettings, setSettings } from "./settings";
|
||||
|
||||
ipcMain.on(GET_PRELOAD_FILE, e => {
|
||||
ipcMain.on(GET_VENCORD_PRELOAD_FILE, e => {
|
||||
e.returnValue = join(VENCORD_FILES_DIR, "preload.js");
|
||||
});
|
||||
|
||||
ipcMain.on(GET_RENDERER_SCRIPT, e => {
|
||||
e.returnValue = readFileSync(join(__dirname, "renderer.js"), "utf-8");
|
||||
});
|
||||
|
||||
ipcMain.handle(GET_RENDERER_STYLES, () => readFile(join(__dirname, "renderer.css"), "utf-8"));
|
||||
|
||||
ipcMain.on(GET_SETTINGS, e => {
|
||||
e.returnValue = PlainSettings;
|
||||
});
|
||||
|
||||
ipcMain.handle(SET_SETTINGS, (_, settings) => {
|
||||
setSettings(settings);
|
||||
});
|
||||
|
||||
ipcMain.handle(RELAUNCH, () => {
|
||||
app.relaunch();
|
||||
app.exit();
|
||||
});
|
||||
|
||||
ipcMain.handle(SHOW_ITEM_IN_FOLDER, (_, path) => {
|
||||
shell.showItemInFolder(path);
|
||||
});
|
||||
|
||||
ipcMain.handle(FOCUS, () => {
|
||||
mainWin?.focus();
|
||||
});
|
||||
|
||||
function readCss() {
|
||||
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
||||
}
|
||||
|
||||
open(VENCORD_QUICKCSS_FILE, "a+").then(fd => {
|
||||
fd.close();
|
||||
watch(VENCORD_QUICKCSS_FILE, { persistent: false }, debounce(async () => {
|
||||
mainWin?.webContents.postMessage("VencordQuickCssUpdate", await readCss());
|
||||
}, 50));
|
||||
});
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { BrowserWindow, Menu, Tray, app, shell } from "electron";
|
||||
import { BrowserWindow, BrowserWindowConstructorOptions, Menu, Tray, app } from "electron";
|
||||
import { join } from "path";
|
||||
import { ICON_PATH } from "../shared/paths";
|
||||
import { createAboutWindow } from "./about";
|
||||
import { Settings } from "./settings";
|
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||
import { downloadVencordFiles } from "./utils/vencordLoader";
|
||||
|
||||
let isQuitting = false;
|
||||
|
||||
@@ -8,32 +12,7 @@ app.on("before-quit", () => {
|
||||
isQuitting = true;
|
||||
});
|
||||
|
||||
function initWindowOpenHandler(win: BrowserWindow) {
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
switch (url) {
|
||||
case "about:blank":
|
||||
case "https://discord.com/popout":
|
||||
return { action: "allow" };
|
||||
}
|
||||
|
||||
try {
|
||||
var protocol = new URL(url).protocol;
|
||||
} catch {
|
||||
return { action: "deny" };
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
case "http:":
|
||||
case "https:":
|
||||
case "mailto:":
|
||||
case "steam:":
|
||||
case "spotify:":
|
||||
shell.openExternal(url);
|
||||
}
|
||||
|
||||
return { action: "deny" };
|
||||
});
|
||||
}
|
||||
export let mainWin: BrowserWindow;
|
||||
|
||||
function initTray(win: BrowserWindow) {
|
||||
const trayMenu = Menu.buildFromTemplate([
|
||||
@@ -44,6 +23,28 @@ function initTray(win: BrowserWindow) {
|
||||
},
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
label: "About",
|
||||
click: createAboutWindow
|
||||
},
|
||||
{
|
||||
label: "Update Vencord",
|
||||
async click() {
|
||||
await downloadVencordFiles();
|
||||
app.relaunch();
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "separator"
|
||||
},
|
||||
{
|
||||
label: "Relaunch",
|
||||
click() {
|
||||
app.relaunch();
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Quit Vencord Desktop",
|
||||
click() {
|
||||
@@ -67,8 +68,151 @@ function initTray(win: BrowserWindow) {
|
||||
});
|
||||
}
|
||||
|
||||
function initMenuBar(win: BrowserWindow) {
|
||||
console.log(process.platform);
|
||||
const menu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: "Vencord Desktop",
|
||||
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: "Toggle Developer Tools",
|
||||
accelerator: "CmdOrCtrl+Shift+I",
|
||||
click() {
|
||||
BrowserWindow.getFocusedWindow()!.webContents.toggleDevTools();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Toggle Developer Tools (Hidden)",
|
||||
accelerator: "F12",
|
||||
visible: false,
|
||||
click() {
|
||||
BrowserWindow.getFocusedWindow()!.webContents.toggleDevTools();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Reload Window",
|
||||
accelerator: "CmdOrCtrl+R",
|
||||
click() {
|
||||
BrowserWindow.getFocusedWindow()!.webContents.reload();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Relaunch",
|
||||
accelerator: "CmdOrCtrl+Shift+R",
|
||||
click() {
|
||||
app.relaunch();
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Quit",
|
||||
accelerator: process.platform === "win32" ? void 0 : "CmdOrCtrl+Q",
|
||||
// TODO: Setting
|
||||
visible: process.platform !== "win32",
|
||||
click() {
|
||||
app.quit();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Quit",
|
||||
accelerator: "Alt+F4",
|
||||
visible: process.platform === "win32",
|
||||
acceleratorWorksWhenHidden: false,
|
||||
click() {
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Zoom",
|
||||
submenu: [
|
||||
{
|
||||
label: "Zoom in",
|
||||
accelerator: "CmdOrCtrl+Plus",
|
||||
role: "zoomIn"
|
||||
},
|
||||
// Fix for zoom in on keyboards with dedicated + like QWERTZ (or numpad)
|
||||
// See https://github.com/electron/electron/issues/14742 and https://github.com/electron/electron/issues/5256
|
||||
{
|
||||
label: "Zoom in",
|
||||
accelerator: "CmdOrCtrl+=",
|
||||
role: "zoomIn",
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
label: "Zoom out",
|
||||
accelerator: "CmdOrCtrl+-",
|
||||
role: "zoomOut"
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
|
||||
Menu.setApplicationMenu(menu);
|
||||
}
|
||||
|
||||
function getWindowBoundsOptions() {
|
||||
const options = {} as BrowserWindowConstructorOptions;
|
||||
|
||||
const { x, y, width, height } = Settings.windowBounds ?? {};
|
||||
if (x != null && y != null) {
|
||||
options.x = x;
|
||||
options.y = y;
|
||||
}
|
||||
|
||||
if (width) options.width = width;
|
||||
if (height) options.height = height;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function initWindowBoundsListeners(win: BrowserWindow) {
|
||||
win.on("maximize", () => {
|
||||
Settings.maximized = true;
|
||||
Settings.minimized = false;
|
||||
});
|
||||
|
||||
win.on("minimize", () => {
|
||||
Settings.minimized = true;
|
||||
});
|
||||
|
||||
win.on("unmaximize", () => {
|
||||
Settings.maximized = false;
|
||||
Settings.minimized = false;
|
||||
});
|
||||
|
||||
const saveBounds = () => {
|
||||
const [width, height] = win.getSize();
|
||||
const [x, y] = win.getPosition();
|
||||
|
||||
Settings.windowBounds = {
|
||||
width,
|
||||
height,
|
||||
x,
|
||||
y
|
||||
};
|
||||
};
|
||||
|
||||
win.on("resize", saveBounds);
|
||||
win.on("move", saveBounds);
|
||||
}
|
||||
|
||||
export function createMainWindow() {
|
||||
const win = new BrowserWindow({
|
||||
const win = mainWin = new BrowserWindow({
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
@@ -78,7 +222,8 @@ export function createMainWindow() {
|
||||
devTools: true,
|
||||
preload: join(__dirname, "preload.js")
|
||||
},
|
||||
icon: ICON_PATH
|
||||
icon: ICON_PATH,
|
||||
...getWindowBoundsOptions()
|
||||
});
|
||||
|
||||
win.on("close", e => {
|
||||
@@ -90,10 +235,16 @@ export function createMainWindow() {
|
||||
return false;
|
||||
});
|
||||
|
||||
initWindowBoundsListeners(win);
|
||||
initTray(win);
|
||||
initWindowOpenHandler(win);
|
||||
initMenuBar(win);
|
||||
makeLinksOpenExternally(win);
|
||||
|
||||
win.loadURL("https://discord.com/app");
|
||||
const subdomain = Settings.discordBranch === "canary" || Settings.discordBranch === "ptb"
|
||||
? `${Settings.discordBranch}.`
|
||||
: "";
|
||||
|
||||
win.loadURL(`https://${subdomain}discord.com/app`);
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
49
src/main/settings.ts
Normal file
49
src/main/settings.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { DATA_DIR } from "./constants";
|
||||
|
||||
const SETTINGS_FILE = join(DATA_DIR, "settings.json");
|
||||
|
||||
interface Settings {
|
||||
maximized?: boolean;
|
||||
minimized?: boolean;
|
||||
windowBounds?: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
discordBranch?: "stable" | "canary" | "ptb";
|
||||
openLinksWithElectron?: boolean;
|
||||
}
|
||||
|
||||
export let PlainSettings = {} as Settings;
|
||||
try {
|
||||
const content = readFileSync(SETTINGS_FILE, "utf8");
|
||||
try {
|
||||
PlainSettings = JSON.parse(content);
|
||||
} catch (err) {
|
||||
console.error("Failed to parse settings.json:", err);
|
||||
}
|
||||
} catch { }
|
||||
|
||||
function makeSettingsProxy(settings: Settings) {
|
||||
return new Proxy(settings, {
|
||||
set(target, prop, value) {
|
||||
Reflect.set(target, prop, value);
|
||||
|
||||
writeFileSync(SETTINGS_FILE, JSON.stringify(target, null, 4));
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export let Settings = makeSettingsProxy(PlainSettings);
|
||||
|
||||
export function setSettings(settings: Settings) {
|
||||
writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 4));
|
||||
PlainSettings = settings;
|
||||
Settings = makeSettingsProxy(settings);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BrowserWindow } from "electron";
|
||||
import { join } from "path";
|
||||
import { STATIC_DIR } from "shared/paths";
|
||||
|
||||
export function createSplashWindow() {
|
||||
const splash = new BrowserWindow({
|
||||
@@ -12,7 +13,7 @@ export function createSplashWindow() {
|
||||
maximizable: false
|
||||
});
|
||||
|
||||
splash.loadFile(join(__dirname, "..", "..", "static", "splash.html"));
|
||||
splash.loadFile(join(STATIC_DIR, "splash.html"));
|
||||
|
||||
return splash;
|
||||
}
|
||||
|
||||
32
src/main/utils/makeLinksOpenExternally.ts
Normal file
32
src/main/utils/makeLinksOpenExternally.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { BrowserWindow, shell } from "electron";
|
||||
import { Settings } from "../settings";
|
||||
|
||||
export function makeLinksOpenExternally(win: BrowserWindow) {
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
switch (url) {
|
||||
case "about:blank":
|
||||
case "https://discord.com/popout":
|
||||
return { action: "allow" };
|
||||
}
|
||||
|
||||
try {
|
||||
var protocol = new URL(url).protocol;
|
||||
} catch {
|
||||
return { action: "deny" };
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
case "http:":
|
||||
case "https:":
|
||||
if (Settings.openLinksWithElectron) {
|
||||
return { action: "allow" };
|
||||
}
|
||||
case "mailto:":
|
||||
case "steam:":
|
||||
case "spotify:":
|
||||
shell.openExternal(url);
|
||||
}
|
||||
|
||||
return { action: "deny" };
|
||||
});
|
||||
}
|
||||
@@ -1,9 +1,20 @@
|
||||
import { ipcRenderer } from "electron";
|
||||
import { RELAUNCH } from "../shared/IpcEvents";
|
||||
import type { Settings } from "../main/settings";
|
||||
import { FOCUS, GET_SETTINGS, RELAUNCH, SET_SETTINGS, SHOW_ITEM_IN_FOLDER } from "../shared/IpcEvents";
|
||||
|
||||
export const VencordDesktop = {
|
||||
app: {
|
||||
relaunch: () => ipcRenderer.invoke(RELAUNCH)
|
||||
},
|
||||
fileManager: {
|
||||
showItemInFolder: (path: string) => ipcRenderer.invoke(SHOW_ITEM_IN_FOLDER, path)
|
||||
},
|
||||
settings: {
|
||||
get: () => ipcRenderer.sendSync(GET_SETTINGS),
|
||||
set: (settings: typeof Settings) => ipcRenderer.invoke(SET_SETTINGS, settings)
|
||||
},
|
||||
win: {
|
||||
focus: () => ipcRenderer.invoke(FOCUS)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { contextBridge, ipcRenderer } from "electron";
|
||||
import { GET_PRELOAD_FILE } from "../shared/IpcEvents";
|
||||
import { contextBridge, ipcRenderer, webFrame } from "electron";
|
||||
import { GET_RENDERER_SCRIPT, GET_RENDERER_STYLES, GET_VENCORD_PRELOAD_FILE } from "../shared/IpcEvents";
|
||||
import { VencordDesktop } from "./VencordDesktop";
|
||||
|
||||
contextBridge.exposeInMainWorld("VencordDesktop", VencordDesktop);
|
||||
|
||||
require(ipcRenderer.sendSync(GET_PRELOAD_FILE));
|
||||
require(ipcRenderer.sendSync(GET_VENCORD_PRELOAD_FILE));
|
||||
|
||||
webFrame.executeJavaScript(ipcRenderer.sendSync(GET_RENDERER_SCRIPT));
|
||||
ipcRenderer.invoke(GET_RENDERER_STYLES).then(s => webFrame.insertCSS(s));
|
||||
|
||||
0
src/renderer/constants.ts
Normal file
0
src/renderer/constants.ts
Normal file
24
src/renderer/fixes.ts
Normal file
24
src/renderer/fixes.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import "./hideGarbage.css";
|
||||
import { isFirstRun, localStorage } from "./utils";
|
||||
|
||||
// Make clicking Notifications focus the window
|
||||
const originalSetOnClick = Object.getOwnPropertyDescriptor(Notification.prototype, "onclick")!.set!;
|
||||
Object.defineProperty(Notification.prototype, "onclick", {
|
||||
set(onClick) {
|
||||
originalSetOnClick.call(this, function (this: unknown) {
|
||||
onClick.apply(this, arguments);
|
||||
VencordDesktop.win.focus();
|
||||
});
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// Enable Desktop Notifications by default
|
||||
if (isFirstRun) {
|
||||
// Hide "Download Discord Desktop now!!!!" banner
|
||||
localStorage.setItem("hideNag", "true");
|
||||
|
||||
Vencord.Webpack.waitFor("setDesktopType", m => {
|
||||
m.setDesktopType("all");
|
||||
});
|
||||
}
|
||||
5
src/renderer/hideGarbage.css
Normal file
5
src/renderer/hideGarbage.css
Normal file
@@ -0,0 +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]) {
|
||||
display: none;
|
||||
}
|
||||
3
src/renderer/index.ts
Normal file
3
src/renderer/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import "./fixes";
|
||||
|
||||
console.log("read if cute :3");
|
||||
8
src/renderer/utils.ts
Normal file
8
src/renderer/utils.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const localStorage = window.vcdLS = window.localStorage;
|
||||
|
||||
export const isFirstRun = (() => {
|
||||
const key = "VCD_FIRST_RUN";
|
||||
if (localStorage.getItem(key) !== null) return false;
|
||||
localStorage.setItem(key, "false");
|
||||
return true;
|
||||
})();
|
||||
@@ -1,2 +1,10 @@
|
||||
export const GET_PRELOAD_FILE = "VCD_GET_PRELOAD_FILE";
|
||||
export const GET_VENCORD_PRELOAD_FILE = "VCD_GET_VC_PRELOAD_FILE";
|
||||
export const GET_RENDERER_SCRIPT = "VCD_GET_RENDERER_SCRIPT";
|
||||
export const GET_RENDERER_STYLES = "VCD_GET_RENDERER_STYLES";
|
||||
|
||||
export const RELAUNCH = "VCD_RELAUNCH";
|
||||
export const FOCUS = "VC_FOCUS";
|
||||
|
||||
export const SHOW_ITEM_IN_FOLDER = "VCD_SHOW_ITEM_IN_FOLDER";
|
||||
export const GET_SETTINGS = "VCD_GET_SETTINGS";
|
||||
export const SET_SETTINGS = "VCD_SET_SETTINGS";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { join } from "path";
|
||||
|
||||
export const STATIC_DIR = join(__dirname, "..", "..", "static");
|
||||
export const ICON_PATH = join(STATIC_DIR, "icon.ico");
|
||||
export const ICON_PATH = join(STATIC_DIR, "icon.png");
|
||||
|
||||
7
src/shared/utils/debounce.ts
Normal file
7
src/shared/utils/debounce.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export function debounce<T extends Function>(func: T, delay = 300): T {
|
||||
let timeout: NodeJS.Timeout;
|
||||
return function (...args: any[]) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => { func(...args); }, delay);
|
||||
} as any;
|
||||
}
|
||||
34
static/about.html
Normal file
34
static/about.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
padding: 2em;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
"Open Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>About Vencord Desktop</h1>
|
||||
<p>
|
||||
Vencord Desktop is a free/libre cross platform desktop app aiming to give you a snappier Discord experience with
|
||||
Vencord pre-installed
|
||||
</p>
|
||||
|
||||
<h2>Links</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://vencord.dev">Vencord Website</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/Vencord/Desktop" target="_blank">Source Code</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/Vencord/Desktop/issues" target="_blank">Report bugs / Request features</a>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
BIN
static/icon.png
Normal file
BIN
static/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "esnext", "esnext.array", "esnext.asynciterable", "esnext.symbol"],
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"target": "ESNEXT",
|
||||
"jsx": "preserve",
|
||||
|
||||
"baseUrl": "./src/"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user