23 Commits

Author SHA1 Message Date
Vendicated
34eb14f9ed i hate workflows 2023-04-06 05:14:28 +02:00
Vendicated
44438441d6 Bump to v0.1.3 2023-04-06 03:52:22 +02:00
Vendicated
bbe670fc92 Tray: Add separator 2023-04-06 03:52:06 +02:00
Toad
3da4c02129 Tray: Add more buttons (#2) 2023-04-06 00:08:28 +02:00
Vendicated
19c43289f6 Make QuickCSS work 2023-04-05 20:01:31 +02:00
Vendicated
1980606e03 Add about page & setting to open links with electron 2023-04-05 17:55:49 +02:00
Vendicated
17150503d2 Add Menu 2023-04-05 17:25:29 +02:00
Vendicated
fe58dcfa51 package.json cleanup 2023-04-05 16:57:09 +02:00
Vendicated
f357ee4260 forgor to add tsconfig 2023-04-05 16:56:33 +02:00
Vendicated
9d144a11be Enable Desktop Notifications on first run 2023-04-05 16:51:19 +02:00
Vendicated
d9f2b15e84 Hide Download Desktop app button 2023-04-05 05:31:44 +02:00
Vendicated
cf23b6d028 Make clicking notifications focus VC Desktop 2023-04-05 05:19:48 +02:00
Vendicated
2efed86b71 v0.1.2 2023-04-05 04:36:37 +02:00
Vendicated
816ba5d1d0 Make it possible to reopen app after close to tray 2023-04-05 04:25:17 +02:00
Vendicated
91e1cc02ce windows: fix notification title & icon 2023-04-05 04:20:13 +02:00
Vendicated
8630f79b06 make nsis installer configurable 2023-04-05 04:20:02 +02:00
Vendicated
79d73e23c0 Init renderer, expose settings via ipc, init canary/ptb support 2023-04-04 04:40:03 +02:00
Vendicated
18a77d45b8 Remember window position & size 2023-04-04 04:17:38 +02:00
Vendicated
f9ebc16656 Add Build & Install instructions 2023-04-04 03:35:31 +02:00
Vendicated
0296f78731 Bump!! 2023-04-04 03:01:21 +02:00
Vendicated
b151631d03 make crapple happy 2: Electric Boogaloo 2023-04-04 03:01:11 +02:00
Vendicated
d817950681 make crapple happy 2023-04-04 02:53:09 +02:00
Vendicated
c0ba813041 switch to png icon 2023-04-04 02:26:55 +02:00
26 changed files with 566 additions and 71 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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
View 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
View 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;
}

View File

@@ -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)`;

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

24
src/renderer/fixes.ts Normal file
View 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");
});
}

View 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
View File

@@ -0,0 +1,3 @@
import "./fixes";
console.log("read if cute :3");

8
src/renderer/utils.ts Normal file
View 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;
})();

View File

@@ -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";

View File

@@ -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");

View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

16
tsconfig.json Normal file
View 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/**/*"]
}