Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2efed86b71 | ||
|
|
816ba5d1d0 | ||
|
|
91e1cc02ce | ||
|
|
8630f79b06 | ||
|
|
79d73e23c0 | ||
|
|
18a77d45b8 | ||
|
|
f9ebc16656 | ||
|
|
0296f78731 | ||
|
|
b151631d03 | ||
|
|
d817950681 | ||
|
|
c0ba813041 |
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
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vencorddesktop",
|
||||
"name": "VencordDesktop",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.2",
|
||||
"description": "",
|
||||
"main": "dist/js/main.js",
|
||||
"scripts": {
|
||||
@@ -31,7 +31,9 @@
|
||||
"category": "Network"
|
||||
},
|
||||
"nsis": {
|
||||
"include": "build/installer.nsh"
|
||||
"include": "build/installer.nsh",
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"linux": {
|
||||
"category": "Network",
|
||||
|
||||
@@ -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",
|
||||
})
|
||||
]);
|
||||
|
||||
|
||||
@@ -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,31 @@
|
||||
import { app, ipcMain } from "electron";
|
||||
import { app, ipcMain, shell } from "electron";
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { GET_PRELOAD_FILE, RELAUNCH } from "../shared/IpcEvents";
|
||||
import { GET_RENDERER_SCRIPT, GET_SETTINGS, GET_VENCORD_PRELOAD_FILE, RELAUNCH, SET_SETTINGS, SHOW_IN_FOLDER } from "../shared/IpcEvents";
|
||||
import { VENCORD_FILES_DIR } from "./constants";
|
||||
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.on(GET_SETTINGS, e => {
|
||||
e.returnValue = PlainSettings;
|
||||
});
|
||||
|
||||
ipcMain.handle(SET_SETTINGS, (_, settings) => {
|
||||
setSettings(settings);
|
||||
});
|
||||
|
||||
ipcMain.handle(RELAUNCH, () => {
|
||||
app.relaunch();
|
||||
app.exit();
|
||||
});
|
||||
|
||||
ipcMain.handle(SHOW_IN_FOLDER, (_, path) => {
|
||||
shell.showItemInFolder(path);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { BrowserWindow, Menu, Tray, app, shell } from "electron";
|
||||
import { BrowserWindow, BrowserWindowConstructorOptions, Menu, Tray, app, shell } from "electron";
|
||||
import { join } from "path";
|
||||
import { ICON_PATH } from "../shared/paths";
|
||||
import { Settings } from "./settings";
|
||||
|
||||
let isQuitting = false;
|
||||
|
||||
@@ -67,6 +68,52 @@ function initTray(win: BrowserWindow) {
|
||||
});
|
||||
}
|
||||
|
||||
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({
|
||||
show: false,
|
||||
@@ -78,7 +125,8 @@ export function createMainWindow() {
|
||||
devTools: true,
|
||||
preload: join(__dirname, "preload.js")
|
||||
},
|
||||
icon: ICON_PATH
|
||||
icon: ICON_PATH,
|
||||
...getWindowBoundsOptions()
|
||||
});
|
||||
|
||||
win.on("close", e => {
|
||||
@@ -90,10 +138,15 @@ export function createMainWindow() {
|
||||
return false;
|
||||
});
|
||||
|
||||
initWindowBoundsListeners(win);
|
||||
initTray(win);
|
||||
initWindowOpenHandler(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;
|
||||
}
|
||||
|
||||
48
src/main/settings.ts
Normal file
48
src/main/settings.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
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";
|
||||
}
|
||||
|
||||
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,9 +1,17 @@
|
||||
import { ipcRenderer } from "electron";
|
||||
import { RELAUNCH } from "../shared/IpcEvents";
|
||||
import type { Settings } from "../main/settings";
|
||||
import { GET_SETTINGS, RELAUNCH, SET_SETTINGS, SHOW_IN_FOLDER } from "../shared/IpcEvents";
|
||||
|
||||
export const VencordDesktop = {
|
||||
app: {
|
||||
relaunch: () => ipcRenderer.invoke(RELAUNCH)
|
||||
},
|
||||
files: {
|
||||
showInFolder: (path: string) => ipcRenderer.invoke(SHOW_IN_FOLDER, path)
|
||||
},
|
||||
settings: {
|
||||
get: () => ipcRenderer.sendSync(GET_SETTINGS),
|
||||
set: (settings: typeof Settings) => ipcRenderer.invoke(SET_SETTINGS, settings)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { contextBridge, ipcRenderer } from "electron";
|
||||
import { GET_PRELOAD_FILE } from "../shared/IpcEvents";
|
||||
import { contextBridge, ipcRenderer, webFrame } from "electron";
|
||||
import { GET_RENDERER_SCRIPT, 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));
|
||||
|
||||
1
src/renderer/index.ts
Normal file
1
src/renderer/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
console.log("read if cute :3");
|
||||
@@ -1,2 +1,6 @@
|
||||
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 RELAUNCH = "VCD_RELAUNCH";
|
||||
export const SHOW_IN_FOLDER = "VCD_SHOW_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");
|
||||
|
||||
BIN
static/icon.png
Normal file
BIN
static/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.5 KiB |
Reference in New Issue
Block a user