Add ability to load Vencord from custom location (dev install)
This commit is contained in:
parent
591d380160
commit
805b6fbcc4
14 changed files with 456 additions and 310 deletions
|
@ -13,16 +13,19 @@
|
||||||
"package": "pnpm build && electron-builder",
|
"package": "pnpm build && electron-builder",
|
||||||
"package:dir": "pnpm build && electron-builder --dir",
|
"package:dir": "pnpm build && electron-builder --dir",
|
||||||
"start": "pnpm build && electron .",
|
"start": "pnpm build && electron .",
|
||||||
"start:watch": "tsx scripts/startWatch.mts",
|
"start:dev": "pnpm build --dev && electron .",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"watch": "pnpm build --watch"
|
"watch": "pnpm build --watch"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^18.15.11",
|
"@types/node": "^18.15.11",
|
||||||
|
"@types/react": "^18.0.33",
|
||||||
"electron": "^23.2.0",
|
"electron": "^23.2.0",
|
||||||
"electron-builder": "^23.6.0",
|
"electron-builder": "^23.6.0",
|
||||||
"esbuild": "^0.17.14",
|
"esbuild": "^0.17.14",
|
||||||
|
"source-map-support": "^0.5.21",
|
||||||
"tsx": "^3.12.6",
|
"tsx": "^3.12.6",
|
||||||
|
"type-fest": "^3.8.0",
|
||||||
"typescript": "^5.0.2"
|
"typescript": "^5.0.2"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
|
|
620
pnpm-lock.yaml
620
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,9 @@
|
||||||
import { BuildContext, BuildOptions, context } from "esbuild";
|
import { BuildContext, BuildOptions, context } from "esbuild";
|
||||||
|
|
||||||
|
const isDev = process.argv.includes("--dev");
|
||||||
|
|
||||||
const CommonOpts: BuildOptions = {
|
const CommonOpts: BuildOptions = {
|
||||||
minify: true,
|
minify: !isDev,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
sourcemap: "linked",
|
sourcemap: "linked",
|
||||||
logLevel: "info"
|
logLevel: "info"
|
||||||
|
@ -13,6 +15,9 @@ const NodeCommonOpts: BuildOptions = {
|
||||||
platform: "node",
|
platform: "node",
|
||||||
external: ["electron"],
|
external: ["electron"],
|
||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
|
define: {
|
||||||
|
IS_DEV: JSON.stringify(isDev)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const contexts = [] as BuildContext[];
|
const contexts = [] as BuildContext[];
|
||||||
|
|
2
src/globals.d.ts
vendored
2
src/globals.d.ts
vendored
|
@ -4,6 +4,8 @@ declare global {
|
||||||
// TODO
|
// TODO
|
||||||
export var Vencord: any;
|
export var Vencord: any;
|
||||||
export var vcdLS: typeof localStorage;
|
export var vcdLS: typeof localStorage;
|
||||||
|
|
||||||
|
export var IS_DEV: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { };
|
export { };
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
|
||||||
export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR ?? join(app.getPath("userData"), "VencordDesktop");
|
export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR || join(app.getPath("userData"), "VencordDesktop");
|
||||||
export const VENCORD_FILES_DIR = join(DATA_DIR, "vencordDist");
|
// needs to be inline require because of circular dependency
|
||||||
|
// as otherwise "DATA_DIR" (which is used by ./settings) will be uninitialised
|
||||||
|
export const VENCORD_FILES_DIR = require("./settings").Settings.vencordDir || join(DATA_DIR, "vencordDist");
|
||||||
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
export const VENCORD_SETTINGS_DIR = join(DATA_DIR, "settings");
|
||||||
export const VENCORD_QUICKCSS_FILE = join(VENCORD_SETTINGS_DIR, "quickCss.css");
|
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_SETTINGS_FILE = join(VENCORD_SETTINGS_DIR, "settings.json");
|
||||||
|
|
|
@ -3,16 +3,20 @@ import { join } from "path";
|
||||||
import { ICON_PATH } from "../shared/paths";
|
import { ICON_PATH } from "../shared/paths";
|
||||||
import { once } from "../shared/utils/once";
|
import { once } from "../shared/utils/once";
|
||||||
import { DATA_DIR, VENCORD_FILES_DIR } from "./constants";
|
import { DATA_DIR, VENCORD_FILES_DIR } from "./constants";
|
||||||
import "./ipc";
|
|
||||||
import { createMainWindow } from "./mainWindow";
|
import { createMainWindow } from "./mainWindow";
|
||||||
import { Settings } from "./settings";
|
import { Settings } from "./settings";
|
||||||
import { createSplashWindow } from "./splash";
|
import { createSplashWindow } from "./splash";
|
||||||
import { ensureVencordFiles } from "./utils/vencordLoader";
|
import { ensureVencordFiles } from "./utils/vencordLoader";
|
||||||
|
|
||||||
|
import "./ipc";
|
||||||
|
if (IS_DEV) {
|
||||||
|
require("source-map-support").install();
|
||||||
|
}
|
||||||
|
|
||||||
// Make the Vencord files use our DATA_DIR
|
// Make the Vencord files use our DATA_DIR
|
||||||
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
|
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
|
||||||
|
|
||||||
const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "main.js")));
|
const runVencordMain = once(() => require(join(VENCORD_FILES_DIR, "vencordDesktopMain.js")));
|
||||||
|
|
||||||
let mainWin: BrowserWindow | null = null;
|
let mainWin: BrowserWindow | null = null;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { app, ipcMain, shell } from "electron";
|
import { app, dialog, ipcMain, shell } from "electron";
|
||||||
import { readFileSync, watch } from "fs";
|
import { existsSync, readFileSync, watch } from "fs";
|
||||||
import { open, readFile } from "fs/promises";
|
import { open, readFile } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { debounce } from "shared/utils/debounce";
|
import { debounce } from "shared/utils/debounce";
|
||||||
|
@ -12,6 +12,10 @@ ipcMain.on(IpcEvents.GET_VENCORD_PRELOAD_FILE, e => {
|
||||||
e.returnValue = join(VENCORD_FILES_DIR, "preload.js");
|
e.returnValue = join(VENCORD_FILES_DIR, "preload.js");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on(IpcEvents.GET_VENCORD_RENDERER_SCRIPT, e => {
|
||||||
|
e.returnValue = readFileSync(join(VENCORD_FILES_DIR, "vencordDesktopRenderer.js"), "utf-8");
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.on(IpcEvents.GET_RENDERER_SCRIPT, e => {
|
ipcMain.on(IpcEvents.GET_RENDERER_SCRIPT, e => {
|
||||||
e.returnValue = readFileSync(join(__dirname, "renderer.js"), "utf-8");
|
e.returnValue = readFileSync(join(__dirname, "renderer.js"), "utf-8");
|
||||||
});
|
});
|
||||||
|
@ -39,6 +43,20 @@ ipcMain.handle(IpcEvents.FOCUS, () => {
|
||||||
mainWin?.focus();
|
mainWin?.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.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";
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
});
|
||||||
|
|
||||||
function readCss() {
|
function readCss() {
|
||||||
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,31 @@
|
||||||
import { app, ipcRenderer } from "electron";
|
import { app, ipcRenderer } from "electron";
|
||||||
import type { Settings } from "../main/settings";
|
import type { Settings } from "shared/settings";
|
||||||
|
import type { LiteralUnion } from "type-fest";
|
||||||
import { IpcEvents } from "../shared/IpcEvents";
|
import { IpcEvents } from "../shared/IpcEvents";
|
||||||
|
|
||||||
|
function invoke<T = any>(event: IpcEvents, ...args: any[]) {
|
||||||
|
return ipcRenderer.invoke(event, ...args) as Promise<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendSync<T = any>(event: IpcEvents, ...args: any[]) {
|
||||||
|
return ipcRenderer.sendSync(event, ...args) as T;
|
||||||
|
}
|
||||||
|
|
||||||
export const VencordDesktopNative = {
|
export const VencordDesktopNative = {
|
||||||
app: {
|
app: {
|
||||||
relaunch: () => ipcRenderer.invoke(IpcEvents.RELAUNCH),
|
relaunch: () => invoke<void>(IpcEvents.RELAUNCH),
|
||||||
getVersion: () => app.getVersion()
|
getVersion: () => app.getVersion()
|
||||||
},
|
},
|
||||||
fileManager: {
|
fileManager: {
|
||||||
showItemInFolder: (path: string) => ipcRenderer.invoke(IpcEvents.SHOW_ITEM_IN_FOLDER, path)
|
showItemInFolder: (path: string) => invoke<void>(IpcEvents.SHOW_ITEM_IN_FOLDER, path),
|
||||||
|
selectVencordDir: () => invoke<LiteralUnion<"cancelled" | "invalid", string>>(IpcEvents.SELECT_VENCORD_DIR),
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
get: () => ipcRenderer.sendSync(IpcEvents.GET_SETTINGS),
|
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),
|
||||||
set: (settings: typeof Settings) => ipcRenderer.invoke(IpcEvents.SET_SETTINGS, settings)
|
set: (settings: Settings) => invoke<void>(IpcEvents.SET_SETTINGS, settings)
|
||||||
},
|
},
|
||||||
win: {
|
win: {
|
||||||
focus: () => ipcRenderer.invoke(IpcEvents.FOCUS)
|
focus: () => invoke<void>(IpcEvents.FOCUS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,5 +6,6 @@ contextBridge.exposeInMainWorld("VencordDesktopNative", VencordDesktopNative);
|
||||||
|
|
||||||
require(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_FILE));
|
require(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_PRELOAD_FILE));
|
||||||
|
|
||||||
|
webFrame.executeJavaScript(ipcRenderer.sendSync(IpcEvents.GET_VENCORD_RENDERER_SCRIPT));
|
||||||
webFrame.executeJavaScript(ipcRenderer.sendSync(IpcEvents.GET_RENDERER_SCRIPT));
|
webFrame.executeJavaScript(ipcRenderer.sendSync(IpcEvents.GET_RENDERER_SCRIPT));
|
||||||
ipcRenderer.invoke(IpcEvents.GET_RENDERER_STYLES).then(s => webFrame.insertCSS(s));
|
ipcRenderer.invoke(IpcEvents.GET_RENDERER_STYLES).then(s => webFrame.insertCSS(s));
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import { getValueAndOnChange, useSettings } from "renderer/settings";
|
import { getValueAndOnChange, useSettings } from "renderer/settings";
|
||||||
import { Common } from "../vencord";
|
import { Common, Util } from "../vencord";
|
||||||
|
|
||||||
|
import "./settings.css";
|
||||||
|
|
||||||
|
const { Margins } = Util;
|
||||||
|
|
||||||
export default function SettingsUi() {
|
export default function SettingsUi() {
|
||||||
const Settings = useSettings();
|
const Settings = useSettings();
|
||||||
const { Forms: { FormSection, FormText, FormDivider, FormSwitch, FormTitle }, Text, Select } = Common;
|
const { Forms: { FormSection, FormText, FormDivider, FormSwitch, FormTitle }, Text, Select, Button } = Common;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormSection>
|
<FormSection>
|
||||||
|
@ -11,7 +15,9 @@ export default function SettingsUi() {
|
||||||
Vencord Desktop Settings
|
Vencord Desktop Settings
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<FormTitle>Discord Branch</FormTitle>
|
<FormTitle className={Margins.top16}>
|
||||||
|
Discord Branch
|
||||||
|
</FormTitle>
|
||||||
<Select
|
<Select
|
||||||
placeholder="Stable"
|
placeholder="Stable"
|
||||||
options={[
|
options={[
|
||||||
|
@ -25,12 +31,58 @@ export default function SettingsUi() {
|
||||||
serialize={s => s}
|
serialize={s => s}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FormDivider className={Margins.top16 + " " + Margins.bottom16} />
|
||||||
|
|
||||||
<FormSwitch
|
<FormSwitch
|
||||||
{...getValueAndOnChange("openLinksWithElectron")}
|
{...getValueAndOnChange("openLinksWithElectron")}
|
||||||
note={"This will open links in a new window instead of your WebBrowser"}
|
note={"This will open links in a new window instead of your WebBrowser"}
|
||||||
>
|
>
|
||||||
Open Links in app
|
Open Links in app
|
||||||
</FormSwitch>
|
</FormSwitch>
|
||||||
|
|
||||||
|
<FormTitle>Vencord Desktop Location</FormTitle>
|
||||||
|
<FormText>
|
||||||
|
Files are loaded from
|
||||||
|
{" "}
|
||||||
|
{Settings.vencordDir
|
||||||
|
? (
|
||||||
|
<a
|
||||||
|
href="about:blank"
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
VencordDesktopNative.fileManager.showItemInFolder(Settings.vencordDir!);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Settings.vencordDir}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
: "the default location"
|
||||||
|
}
|
||||||
|
</FormText>
|
||||||
|
<div className="vcd-location-btns">
|
||||||
|
<Button
|
||||||
|
size={Button.Sizes.SMALL}
|
||||||
|
onClick={async () => {
|
||||||
|
const choice = await VencordDesktopNative.fileManager.selectVencordDir();
|
||||||
|
switch (choice) {
|
||||||
|
case "cancelled":
|
||||||
|
case "invalid":
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Settings.vencordDir = choice;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Change
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size={Button.Sizes.SMALL}
|
||||||
|
color={Button.Colors.RED}
|
||||||
|
onClick={() => Settings.vencordDir = void 0}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
6
src/renderer/components/settings.css
Normal file
6
src/renderer/components/settings.css
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.vcd-location-btns {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 0.5em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
// TODO: Taaaips when?
|
// FIXME: this is terrible
|
||||||
|
|
||||||
const { Webpack, Plugins } = Vencord;
|
const { Webpack, Plugins, Util } = Vencord;
|
||||||
const { Common } = Webpack;
|
const { Common } = Webpack;
|
||||||
const { plugins } = Plugins;
|
const { plugins } = Plugins;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Webpack,
|
Webpack,
|
||||||
Common,
|
Common,
|
||||||
|
Util,
|
||||||
Plugins,
|
Plugins,
|
||||||
plugins
|
plugins
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export const enum IpcEvents {
|
export const enum IpcEvents {
|
||||||
GET_VENCORD_PRELOAD_FILE = "VCD_GET_VC_PRELOAD_FILE",
|
GET_VENCORD_PRELOAD_FILE = "VCD_GET_VC_PRELOAD_FILE",
|
||||||
|
GET_VENCORD_RENDERER_SCRIPT = "VCD_GET_VC_RENDERER_SCRIPT",
|
||||||
GET_RENDERER_SCRIPT = "VCD_GET_RENDERER_SCRIPT",
|
GET_RENDERER_SCRIPT = "VCD_GET_RENDERER_SCRIPT",
|
||||||
GET_RENDERER_STYLES = "VCD_GET_RENDERER_STYLES",
|
GET_RENDERER_STYLES = "VCD_GET_RENDERER_STYLES",
|
||||||
|
|
||||||
|
@ -9,5 +10,7 @@ export const enum IpcEvents {
|
||||||
SHOW_ITEM_IN_FOLDER = "VCD_SHOW_ITEM_IN_FOLDER",
|
SHOW_ITEM_IN_FOLDER = "VCD_SHOW_ITEM_IN_FOLDER",
|
||||||
GET_SETTINGS = "VCD_GET_SETTINGS",
|
GET_SETTINGS = "VCD_GET_SETTINGS",
|
||||||
SET_SETTINGS = "VCD_SET_SETTINGS",
|
SET_SETTINGS = "VCD_SET_SETTINGS",
|
||||||
|
|
||||||
|
SELECT_VENCORD_DIR = "VCD_SELECT_VENCORD_DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
src/shared/settings.d.ts
vendored
1
src/shared/settings.d.ts
vendored
|
@ -9,4 +9,5 @@ export interface Settings {
|
||||||
};
|
};
|
||||||
discordBranch?: "stable" | "canary" | "ptb";
|
discordBranch?: "stable" | "canary" | "ptb";
|
||||||
openLinksWithElectron?: boolean;
|
openLinksWithElectron?: boolean;
|
||||||
|
vencordDir?: string;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue