Add additional build flavours for Vencord Desktop (#765)
This commit is contained in:
parent
5bb08bdb64
commit
6b26c12bfa
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -42,7 +42,7 @@ jobs:
|
||||||
|
|
||||||
- name: Clean up obsolete files
|
- name: Clean up obsolete files
|
||||||
run: |
|
run: |
|
||||||
rm -rf dist/extension* Vencord.user.css
|
rm -rf dist/extension* Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
|
||||||
|
|
||||||
- name: Get some values needed for the release
|
- name: Get some values needed for the release
|
||||||
id: release_values
|
id: release_values
|
||||||
|
|
|
@ -48,6 +48,7 @@ const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.j
|
||||||
const sourcemap = watch ? "inline" : "external";
|
const sourcemap = watch ? "inline" : "external";
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
// common preload
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...nodeCommonOpts,
|
...nodeCommonOpts,
|
||||||
entryPoints: ["src/preload.ts"],
|
entryPoints: ["src/preload.ts"],
|
||||||
|
@ -55,12 +56,19 @@ await Promise.all([
|
||||||
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
|
footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") },
|
||||||
sourcemap,
|
sourcemap,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// Discord Desktop main & renderer
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...nodeCommonOpts,
|
...nodeCommonOpts,
|
||||||
entryPoints: ["src/patcher.ts"],
|
entryPoints: ["src/main/index.ts"],
|
||||||
outfile: "dist/patcher.js",
|
outfile: "dist/patcher.js",
|
||||||
footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") },
|
footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") },
|
||||||
sourcemap,
|
sourcemap,
|
||||||
|
define: {
|
||||||
|
...defines,
|
||||||
|
IS_DISCORD_DESKTOP: true,
|
||||||
|
IS_VENCORD_DESKTOP: false
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
...commonOpts,
|
...commonOpts,
|
||||||
|
@ -77,7 +85,43 @@ await Promise.all([
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_WEB: false
|
IS_WEB: false,
|
||||||
|
IS_DISCORD_DESKTOP: true,
|
||||||
|
IS_VENCORD_DESKTOP: false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Vencord Desktop main & renderer
|
||||||
|
esbuild.build({
|
||||||
|
...nodeCommonOpts,
|
||||||
|
entryPoints: ["src/main/index.ts"],
|
||||||
|
outfile: "dist/vencordDesktopMain.js",
|
||||||
|
footer: { js: "//# sourceURL=VencordDesktopMain\n" + sourceMapFooter("vencordDesktopMain") },
|
||||||
|
sourcemap,
|
||||||
|
define: {
|
||||||
|
...defines,
|
||||||
|
IS_DISCORD_DESKTOP: false,
|
||||||
|
IS_VENCORD_DESKTOP: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
esbuild.build({
|
||||||
|
...commonOpts,
|
||||||
|
entryPoints: ["src/Vencord.ts"],
|
||||||
|
outfile: "dist/vencordDesktopRenderer.js",
|
||||||
|
format: "iife",
|
||||||
|
target: ["esnext"],
|
||||||
|
footer: { js: "//# sourceURL=VencordDesktopRenderer\n" + sourceMapFooter("vencordDesktopRenderer") },
|
||||||
|
globalName: "Vencord",
|
||||||
|
sourcemap,
|
||||||
|
plugins: [
|
||||||
|
globPlugins,
|
||||||
|
...commonOpts.plugins
|
||||||
|
],
|
||||||
|
define: {
|
||||||
|
...defines,
|
||||||
|
IS_WEB: false,
|
||||||
|
IS_DISCORD_DESKTOP: false,
|
||||||
|
IS_VENCORD_DESKTOP: true
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
]).catch(err => {
|
]).catch(err => {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import "./webpack/patchWebpack";
|
||||||
import { showNotification } from "./api/Notifications";
|
import { showNotification } from "./api/Notifications";
|
||||||
import { PlainSettings, Settings } from "./api/settings";
|
import { PlainSettings, Settings } from "./api/settings";
|
||||||
import { patches, PMLogger, startAllPlugins } from "./plugins";
|
import { patches, PMLogger, startAllPlugins } from "./plugins";
|
||||||
import { checkForUpdates, rebuild, update, UpdateLogger } from "./utils/updater";
|
import { checkForUpdates, rebuild, update,UpdateLogger } from "./utils/updater";
|
||||||
import { onceReady } from "./webpack";
|
import { onceReady } from "./webpack";
|
||||||
import { SettingsRouter } from "./webpack/common";
|
import { SettingsRouter } from "./webpack/common";
|
||||||
|
|
||||||
|
@ -56,8 +56,12 @@ async function init() {
|
||||||
permanent: true,
|
permanent: true,
|
||||||
noPersist: true,
|
noPersist: true,
|
||||||
onClick() {
|
onClick() {
|
||||||
if (needsFullRestart)
|
if (needsFullRestart) {
|
||||||
window.DiscordNative.app.relaunch();
|
if (IS_DISCORD_DESKTOP)
|
||||||
|
window.DiscordNative.app.relaunch();
|
||||||
|
else
|
||||||
|
window.VencordDesktop.app.relaunch();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
@ -96,7 +100,7 @@ async function init() {
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
if (!IS_WEB && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) {
|
if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) {
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
document.head.append(Object.assign(document.createElement("style"), {
|
document.head.append(Object.assign(document.createElement("style"), {
|
||||||
id: "vencord-native-titlebar-style",
|
id: "vencord-native-titlebar-style",
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { handleComponentFailed } from "@components/handleComponentFailed";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes, useAwaiter } from "@utils/misc";
|
import { classes, useAwaiter } from "@utils/misc";
|
||||||
|
import { relaunch } from "@utils/native";
|
||||||
import { changes, checkForUpdates, getRepo, isNewer, rebuild, update, updateError, UpdateLogger } from "@utils/updater";
|
import { changes, checkForUpdates, getRepo, isNewer, rebuild, update, updateError, UpdateLogger } from "@utils/updater";
|
||||||
import { Alerts, Button, Card, Forms, Parser, React, Switch, Toasts } from "@webpack/common";
|
import { Alerts, Button, Card, Forms, Parser, React, Switch, Toasts } from "@webpack/common";
|
||||||
|
|
||||||
|
@ -133,7 +134,7 @@ function Updatable(props: CommonProps) {
|
||||||
cancelText: "Not now!",
|
cancelText: "Not now!",
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
if (needFullRestart)
|
if (needFullRestart)
|
||||||
window.DiscordNative.app.relaunch();
|
relaunch();
|
||||||
else
|
else
|
||||||
location.reload();
|
location.reload();
|
||||||
r();
|
r();
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { ErrorCard } from "@components/ErrorCard";
|
||||||
import IpcEvents from "@utils/IpcEvents";
|
import IpcEvents from "@utils/IpcEvents";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { identity, useAwaiter } from "@utils/misc";
|
import { identity, useAwaiter } from "@utils/misc";
|
||||||
|
import { relaunch } from "@utils/native";
|
||||||
import { Button, Card, Forms, React, Select, Slider, Switch } from "@webpack/common";
|
import { Button, Card, Forms, React, Select, Slider, Switch } from "@webpack/common";
|
||||||
|
|
||||||
const cl = classNameFactory("vc-settings-");
|
const cl = classNameFactory("vc-settings-");
|
||||||
|
@ -100,7 +101,7 @@ function VencordSettings() {
|
||||||
) : (
|
) : (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => window.DiscordNative.app.relaunch()}
|
onClick={relaunch}
|
||||||
size={Button.Sizes.SMALL}>
|
size={Button.Sizes.SMALL}>
|
||||||
Restart Client
|
Restart Client
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -111,6 +112,7 @@ function VencordSettings() {
|
||||||
Open QuickCSS File
|
Open QuickCSS File
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
// FIXME: Vencord Desktop support
|
||||||
onClick={() => window.DiscordNative.fileManager.showItemInFolder(settingsDir)}
|
onClick={() => window.DiscordNative.fileManager.showItemInFolder(settingsDir)}
|
||||||
size={Button.Sizes.SMALL}
|
size={Button.Sizes.SMALL}
|
||||||
disabled={settingsDirPending}>
|
disabled={settingsDirPending}>
|
||||||
|
|
3
src/globals.d.ts
vendored
3
src/globals.d.ts
vendored
|
@ -35,6 +35,8 @@ declare global {
|
||||||
export var IS_WEB: boolean;
|
export var IS_WEB: boolean;
|
||||||
export var IS_DEV: boolean;
|
export var IS_DEV: boolean;
|
||||||
export var IS_STANDALONE: boolean;
|
export var IS_STANDALONE: boolean;
|
||||||
|
export var IS_DISCORD_DESKTOP: boolean;
|
||||||
|
export var IS_VENCORD_DESKTOP: boolean;
|
||||||
|
|
||||||
export var VencordNative: typeof import("./VencordNative").default;
|
export var VencordNative: typeof import("./VencordNative").default;
|
||||||
export var Vencord: typeof import("./Vencord");
|
export var Vencord: typeof import("./Vencord");
|
||||||
|
@ -54,6 +56,7 @@ declare global {
|
||||||
* If you really must use it, mark your plugin as Desktop App only by naming it Foo.desktop.ts(x)
|
* If you really must use it, mark your plugin as Desktop App only by naming it Foo.desktop.ts(x)
|
||||||
*/
|
*/
|
||||||
export var DiscordNative: any;
|
export var DiscordNative: any;
|
||||||
|
export var VencordDesktop: any;
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
webpackChunkdiscord_app: {
|
webpackChunkdiscord_app: {
|
||||||
|
|
108
src/main/index.ts
Normal file
108
src/main/index.ts
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { app, protocol, session } from "electron";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
|
import { getSettings } from "./ipcMain";
|
||||||
|
import { IS_VANILLA } from "./utils/constants";
|
||||||
|
import { installExt } from "./utils/extensions";
|
||||||
|
|
||||||
|
if (IS_VENCORD_DESKTOP || !IS_VANILLA) {
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
// Source Maps! Maybe there's a better way but since the renderer is executed
|
||||||
|
// from a string I don't think any other form of sourcemaps would work
|
||||||
|
protocol.registerFileProtocol("vencord", ({ url: unsafeUrl }, cb) => {
|
||||||
|
let url = unsafeUrl.slice("vencord://".length);
|
||||||
|
if (url.endsWith("/")) url = url.slice(0, -1);
|
||||||
|
switch (url) {
|
||||||
|
case "renderer.js.map":
|
||||||
|
case "preload.js.map":
|
||||||
|
case "patcher.js.map": // doubt
|
||||||
|
cb(join(__dirname, url));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cb({ statusCode: 403 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (getSettings().enableReactDevtools)
|
||||||
|
installExt("fmkadmapgofadopljbjfkapdkoienihi")
|
||||||
|
.then(() => console.info("[Vencord] Installed React Developer Tools"))
|
||||||
|
.catch(err => console.error("[Vencord] Failed to install React Developer Tools", err));
|
||||||
|
} catch { }
|
||||||
|
|
||||||
|
|
||||||
|
// Remove CSP
|
||||||
|
type PolicyResult = Record<string, string[]>;
|
||||||
|
|
||||||
|
const parsePolicy = (policy: string): PolicyResult => {
|
||||||
|
const result: PolicyResult = {};
|
||||||
|
policy.split(";").forEach(directive => {
|
||||||
|
const [directiveKey, ...directiveValue] = directive.trim().split(/\s+/g);
|
||||||
|
if (directiveKey && !Object.prototype.hasOwnProperty.call(result, directiveKey)) {
|
||||||
|
result[directiveKey] = directiveValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
const stringifyPolicy = (policy: PolicyResult): string =>
|
||||||
|
Object.entries(policy)
|
||||||
|
.filter(([, values]) => values?.length)
|
||||||
|
.map(directive => directive.flat().join(" "))
|
||||||
|
.join("; ");
|
||||||
|
|
||||||
|
function patchCsp(headers: Record<string, string[]>, header: string) {
|
||||||
|
if (header in headers) {
|
||||||
|
const csp = parsePolicy(headers[header][0]);
|
||||||
|
|
||||||
|
for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) {
|
||||||
|
csp[directive] = ["*", "blob:", "data:", "'unsafe-inline'"];
|
||||||
|
}
|
||||||
|
// TODO: Restrict this to only imported packages with fixed version.
|
||||||
|
// Perhaps auto generate with esbuild
|
||||||
|
csp["script-src"] ??= [];
|
||||||
|
csp["script-src"].push("'unsafe-eval'", "https://unpkg.com", "https://cdnjs.cloudflare.com");
|
||||||
|
headers[header] = [stringifyPolicy(csp)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.defaultSession.webRequest.onHeadersReceived(({ responseHeaders, resourceType }, cb) => {
|
||||||
|
if (responseHeaders) {
|
||||||
|
if (resourceType === "mainFrame")
|
||||||
|
patchCsp(responseHeaders, "content-security-policy");
|
||||||
|
|
||||||
|
// Fix hosts that don't properly set the css content type, such as
|
||||||
|
// raw.githubusercontent.com
|
||||||
|
if (resourceType === "stylesheet")
|
||||||
|
responseHeaders["content-type"] = ["text/css"];
|
||||||
|
}
|
||||||
|
cb({ cancel: false, responseHeaders });
|
||||||
|
});
|
||||||
|
|
||||||
|
// assign a noop to onHeadersReceived to prevent other mods from adding their own incompatible ones.
|
||||||
|
// For instance, OpenAsar adds their own that doesn't fix content-type for stylesheets which makes it
|
||||||
|
// impossible to load css from github raw despite our fix above
|
||||||
|
session.defaultSession.webRequest.onHeadersReceived = () => { };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_DISCORD_DESKTOP) {
|
||||||
|
require("./patcher");
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ import { join } from "path";
|
||||||
|
|
||||||
import monacoHtml from "~fileContent/../components/monacoWin.html;base64";
|
import monacoHtml from "~fileContent/../components/monacoWin.html;base64";
|
||||||
|
|
||||||
import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, SETTINGS_DIR, SETTINGS_FILE } from "./constants";
|
import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, SETTINGS_DIR, SETTINGS_FILE } from "./utils/constants";
|
||||||
|
|
||||||
mkdirSync(SETTINGS_DIR, { recursive: true });
|
mkdirSync(SETTINGS_DIR, { recursive: true });
|
||||||
|
|
||||||
|
@ -44,6 +44,14 @@ export function readSettings() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSettings(): typeof import("@api/settings").Settings {
|
||||||
|
try {
|
||||||
|
return JSON.parse(readSettings());
|
||||||
|
} catch {
|
||||||
|
return {} as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.OPEN_QUICKCSS, () => shell.openPath(QUICKCSS_PATH));
|
ipcMain.handle(IpcEvents.OPEN_QUICKCSS, () => shell.openPath(QUICKCSS_PATH));
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.OPEN_EXTERNAL, (_, url) => {
|
ipcMain.handle(IpcEvents.OPEN_EXTERNAL, (_, url) => {
|
|
@ -17,12 +17,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { onceDefined } from "@utils/onceDefined";
|
import { onceDefined } from "@utils/onceDefined";
|
||||||
import electron, { app, BrowserWindowConstructorOptions, Menu, protocol, session } from "electron";
|
import electron, { app, BrowserWindowConstructorOptions, Menu } from "electron";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
|
|
||||||
import { initIpc } from "./ipcMain";
|
import { getSettings, initIpc } from "./ipcMain";
|
||||||
import { installExt } from "./ipcMain/extensions";
|
import { IS_VANILLA } from "./utils/constants";
|
||||||
import { readSettings } from "./ipcMain/index";
|
|
||||||
|
|
||||||
console.log("[Vencord] Starting up...");
|
console.log("[Vencord] Starting up...");
|
||||||
|
|
||||||
|
@ -41,11 +40,8 @@ require.main!.filename = join(asarPath, discordPkg.main);
|
||||||
// @ts-ignore Untyped method? Dies from cringe
|
// @ts-ignore Untyped method? Dies from cringe
|
||||||
app.setAppPath(asarPath);
|
app.setAppPath(asarPath);
|
||||||
|
|
||||||
if (!process.argv.includes("--vanilla")) {
|
if (!IS_VANILLA) {
|
||||||
let settings: typeof import("@api/settings").Settings = {} as any;
|
const settings = getSettings();
|
||||||
try {
|
|
||||||
settings = JSON.parse(readSettings());
|
|
||||||
} catch { }
|
|
||||||
|
|
||||||
// Repatch after host updates on Windows
|
// Repatch after host updates on Windows
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
|
@ -116,84 +112,6 @@ if (!process.argv.includes("--vanilla")) {
|
||||||
);
|
);
|
||||||
|
|
||||||
process.env.DATA_DIR = join(app.getPath("userData"), "..", "Vencord");
|
process.env.DATA_DIR = join(app.getPath("userData"), "..", "Vencord");
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
|
||||||
// Source Maps! Maybe there's a better way but since the renderer is executed
|
|
||||||
// from a string I don't think any other form of sourcemaps would work
|
|
||||||
protocol.registerFileProtocol("vencord", ({ url: unsafeUrl }, cb) => {
|
|
||||||
let url = unsafeUrl.slice("vencord://".length);
|
|
||||||
if (url.endsWith("/")) url = url.slice(0, -1);
|
|
||||||
switch (url) {
|
|
||||||
case "renderer.js.map":
|
|
||||||
case "preload.js.map":
|
|
||||||
case "patcher.js.map": // doubt
|
|
||||||
cb(join(__dirname, url));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
cb({ statusCode: 403 });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (settings?.enableReactDevtools)
|
|
||||||
installExt("fmkadmapgofadopljbjfkapdkoienihi")
|
|
||||||
.then(() => console.info("[Vencord] Installed React Developer Tools"))
|
|
||||||
.catch(err => console.error("[Vencord] Failed to install React Developer Tools", err));
|
|
||||||
} catch { }
|
|
||||||
|
|
||||||
|
|
||||||
// Remove CSP
|
|
||||||
type PolicyResult = Record<string, string[]>;
|
|
||||||
|
|
||||||
const parsePolicy = (policy: string): PolicyResult => {
|
|
||||||
const result: PolicyResult = {};
|
|
||||||
policy.split(";").forEach(directive => {
|
|
||||||
const [directiveKey, ...directiveValue] = directive.trim().split(/\s+/g);
|
|
||||||
if (directiveKey && !Object.prototype.hasOwnProperty.call(result, directiveKey)) {
|
|
||||||
result[directiveKey] = directiveValue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
const stringifyPolicy = (policy: PolicyResult): string =>
|
|
||||||
Object.entries(policy)
|
|
||||||
.filter(([, values]) => values?.length)
|
|
||||||
.map(directive => directive.flat().join(" "))
|
|
||||||
.join("; ");
|
|
||||||
|
|
||||||
function patchCsp(headers: Record<string, string[]>, header: string) {
|
|
||||||
if (header in headers) {
|
|
||||||
const csp = parsePolicy(headers[header][0]);
|
|
||||||
|
|
||||||
for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) {
|
|
||||||
csp[directive] = ["*", "blob:", "data:", "'unsafe-inline'"];
|
|
||||||
}
|
|
||||||
// TODO: Restrict this to only imported packages with fixed version.
|
|
||||||
// Perhaps auto generate with esbuild
|
|
||||||
csp["script-src"] ??= [];
|
|
||||||
csp["script-src"].push("'unsafe-eval'", "https://unpkg.com", "https://cdnjs.cloudflare.com");
|
|
||||||
headers[header] = [stringifyPolicy(csp)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
session.defaultSession.webRequest.onHeadersReceived(({ responseHeaders, resourceType }, cb) => {
|
|
||||||
if (responseHeaders) {
|
|
||||||
if (resourceType === "mainFrame")
|
|
||||||
patchCsp(responseHeaders, "content-security-policy");
|
|
||||||
|
|
||||||
// Fix hosts that don't properly set the css content type, such as
|
|
||||||
// raw.githubusercontent.com
|
|
||||||
if (resourceType === "stylesheet")
|
|
||||||
responseHeaders["content-type"] = ["text/css"];
|
|
||||||
}
|
|
||||||
cb({ cancel: false, responseHeaders });
|
|
||||||
});
|
|
||||||
|
|
||||||
// assign a noop to onHeadersReceived to prevent other mods from adding their own incompatible ones.
|
|
||||||
// For instance, OpenAsar adds their own that doesn't fix content-type for stylesheets which makes it
|
|
||||||
// impossible to load css from github raw despite our fix above
|
|
||||||
session.defaultSession.webRequest.onHeadersReceived = () => { };
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
console.log("[Vencord] Running in vanilla mode. Not loading Vencord");
|
console.log("[Vencord] Running in vanilla mode. Not loading Vencord");
|
||||||
}
|
}
|
|
@ -25,7 +25,7 @@ import { join } from "path";
|
||||||
import gitHash from "~git-hash";
|
import gitHash from "~git-hash";
|
||||||
import gitRemote from "~git-remote";
|
import gitRemote from "~git-remote";
|
||||||
|
|
||||||
import { get } from "../simpleGet";
|
import { get } from "../utils/simpleGet";
|
||||||
import { calculateHashes, serializeErrors } from "./common";
|
import { calculateHashes, serializeErrors } from "./common";
|
||||||
|
|
||||||
const API_BASE = `https://api.github.com/repos/${gitRemote}`;
|
const API_BASE = `https://api.github.com/repos/${gitRemote}`;
|
||||||
|
@ -57,6 +57,13 @@ async function calculateGitChanges() {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FILES_TO_DOWNLOAD = [
|
||||||
|
IS_DISCORD_DESKTOP ? "patcher.js" : "vencordDesktopMain.js",
|
||||||
|
"preload.js",
|
||||||
|
IS_DISCORD_DESKTOP ? "renderer.js" : "vencordDesktopRenderer.js",
|
||||||
|
"renderer.css"
|
||||||
|
];
|
||||||
|
|
||||||
async function fetchUpdates() {
|
async function fetchUpdates() {
|
||||||
const release = await githubGet("/releases/latest");
|
const release = await githubGet("/releases/latest");
|
||||||
|
|
||||||
|
@ -66,7 +73,7 @@ async function fetchUpdates() {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
data.assets.forEach(({ name, browser_download_url }) => {
|
data.assets.forEach(({ name, browser_download_url }) => {
|
||||||
if (["patcher.js", "preload.js", "renderer.js", "renderer.css"].some(s => name.startsWith(s))) {
|
if (FILES_TO_DOWNLOAD.some(s => name.startsWith(s))) {
|
||||||
PendingUpdates.push([name, browser_download_url]);
|
PendingUpdates.push([name, browser_download_url]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -75,8 +82,17 @@ async function fetchUpdates() {
|
||||||
|
|
||||||
async function applyUpdates() {
|
async function applyUpdates() {
|
||||||
await Promise.all(PendingUpdates.map(
|
await Promise.all(PendingUpdates.map(
|
||||||
async ([name, data]) => writeFile(join(__dirname, name), await get(data)))
|
async ([name, data]) => writeFile(
|
||||||
);
|
join(
|
||||||
|
__dirname,
|
||||||
|
IS_VENCORD_DESKTOP
|
||||||
|
// vencordDesktopRenderer.js -> renderer.js
|
||||||
|
? name.replace(/vencordDesktop(\w)/, (_, c) => c.toLowerCase())
|
||||||
|
: name
|
||||||
|
),
|
||||||
|
await get(data)
|
||||||
|
)
|
||||||
|
));
|
||||||
PendingUpdates = [];
|
PendingUpdates = [];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
|
@ -33,3 +33,5 @@ export const ALLOWED_PROTOCOLS = [
|
||||||
"steam:",
|
"steam:",
|
||||||
"spotify:"
|
"spotify:"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const IS_VANILLA = /* @__PURE__ */ process.argv.includes("--vanilla");
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
import { relaunch } from "@utils/native";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import * as Webpack from "@webpack";
|
import * as Webpack from "@webpack";
|
||||||
import { extract, filters, findAll, search } from "@webpack";
|
import { extract, filters, findAll, search } from "@webpack";
|
||||||
|
@ -77,7 +78,7 @@ export default definePlugin({
|
||||||
Settings: Vencord.Settings,
|
Settings: Vencord.Settings,
|
||||||
Api: Vencord.Api,
|
Api: Vencord.Api,
|
||||||
reload: () => location.reload(),
|
reload: () => location.reload(),
|
||||||
restart: IS_WEB ? WEB_ONLY("restart") : window.DiscordNative.app.relaunch
|
restart: IS_WEB ? WEB_ONLY("restart") : relaunch
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -168,6 +168,7 @@ export default definePlugin({
|
||||||
get additionalInfo() {
|
get additionalInfo() {
|
||||||
if (IS_DEV) return " (Dev)";
|
if (IS_DEV) return " (Dev)";
|
||||||
if (IS_WEB) return " (Web)";
|
if (IS_WEB) return " (Web)";
|
||||||
|
if (IS_VENCORD_DESKTOP) return " (Vencord Desktop)";
|
||||||
if (IS_STANDALONE) return " (Standalone)";
|
if (IS_STANDALONE) return " (Standalone)";
|
||||||
return "";
|
return "";
|
||||||
},
|
},
|
||||||
|
|
|
@ -54,7 +54,9 @@ if (location.protocol !== "data:") {
|
||||||
document.getElementById("vencord-css-core")!.textContent = readFileSync(rendererCss, "utf-8");
|
document.getElementById("vencord-css-core")!.textContent = readFileSync(rendererCss, "utf-8");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
require(process.env.DISCORD_PRELOAD!);
|
|
||||||
|
if (process.env.DISCORD_PRELOAD)
|
||||||
|
require(process.env.DISCORD_PRELOAD);
|
||||||
} else {
|
} else {
|
||||||
// Monaco Popout
|
// Monaco Popout
|
||||||
contextBridge.exposeInMainWorld("setCss", debounce(s => VencordNative.ipc.invoke(IpcEvents.SET_QUICK_CSS, s)));
|
contextBridge.exposeInMainWorld("setCss", debounce(s => VencordNative.ipc.invoke(IpcEvents.SET_QUICK_CSS, s)));
|
||||||
|
|
24
src/utils/native.ts
Normal file
24
src/utils/native.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function relaunch() {
|
||||||
|
if (IS_DISCORD_DESKTOP)
|
||||||
|
window.DiscordNative.app.relaunch();
|
||||||
|
else
|
||||||
|
window.VencordDesktop.app.relaunch();
|
||||||
|
}
|
|
@ -47,7 +47,9 @@ export async function downloadSettingsBackup() {
|
||||||
const backup = await exportSettings();
|
const backup = await exportSettings();
|
||||||
const data = new TextEncoder().encode(backup);
|
const data = new TextEncoder().encode(backup);
|
||||||
|
|
||||||
if (IS_WEB) {
|
if (IS_DISCORD_DESKTOP) {
|
||||||
|
DiscordNative.fileManager.saveWithDialog(data, filename);
|
||||||
|
} else {
|
||||||
const file = new File([data], filename, { type: "application/json" });
|
const file = new File([data], filename, { type: "application/json" });
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.href = URL.createObjectURL(file);
|
a.href = URL.createObjectURL(file);
|
||||||
|
@ -59,8 +61,6 @@ export async function downloadSettingsBackup() {
|
||||||
URL.revokeObjectURL(a.href);
|
URL.revokeObjectURL(a.href);
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
DiscordNative.fileManager.saveWithDialog(data, filename);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,24 @@ const toastFailure = (err: any) => Toasts.show({
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function uploadSettingsBackup(showToast = true): Promise<void> {
|
export async function uploadSettingsBackup(showToast = true): Promise<void> {
|
||||||
if (IS_WEB) {
|
if (IS_DISCORD_DESKTOP) {
|
||||||
|
const [file] = await DiscordNative.fileManager.openFiles({
|
||||||
|
filters: [
|
||||||
|
{ name: "Vencord Settings Backup", extensions: ["json"] },
|
||||||
|
{ name: "all", extensions: ["*"] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
try {
|
||||||
|
await importSettings(new TextDecoder().decode(file.data));
|
||||||
|
if (showToast) toastSuccess();
|
||||||
|
} catch (err) {
|
||||||
|
new Logger("SettingsSync").error(err);
|
||||||
|
if (showToast) toastFailure(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "file";
|
input.type = "file";
|
||||||
input.style.display = "none";
|
input.style.display = "none";
|
||||||
|
@ -102,22 +119,5 @@ export async function uploadSettingsBackup(showToast = true): Promise<void> {
|
||||||
document.body.appendChild(input);
|
document.body.appendChild(input);
|
||||||
input.click();
|
input.click();
|
||||||
setImmediate(() => document.body.removeChild(input));
|
setImmediate(() => document.body.removeChild(input));
|
||||||
} else {
|
|
||||||
const [file] = await DiscordNative.fileManager.openFiles({
|
|
||||||
filters: [
|
|
||||||
{ name: "Vencord Settings Backup", extensions: ["json"] },
|
|
||||||
{ name: "all", extensions: ["*"] }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
try {
|
|
||||||
await importSettings(new TextDecoder().decode(file.data));
|
|
||||||
if (showToast) toastSuccess();
|
|
||||||
} catch (err) {
|
|
||||||
new Logger("SettingsSync").error(err);
|
|
||||||
if (showToast) toastFailure(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import gitHash from "~git-hash";
|
||||||
|
|
||||||
import IpcEvents from "./IpcEvents";
|
import IpcEvents from "./IpcEvents";
|
||||||
import Logger from "./Logger";
|
import Logger from "./Logger";
|
||||||
|
import { relaunch } from "./native";
|
||||||
import { IpcRes } from "./types";
|
import { IpcRes } from "./types";
|
||||||
|
|
||||||
export const UpdateLogger = /* #__PURE__*/ new Logger("Updater", "white");
|
export const UpdateLogger = /* #__PURE__*/ new Logger("Updater", "white");
|
||||||
|
@ -90,8 +91,10 @@ export async function maybePromptToUpdate(confirmMessage: string, checkForDev =
|
||||||
if (wantsUpdate) {
|
if (wantsUpdate) {
|
||||||
await update();
|
await update();
|
||||||
const needFullRestart = await rebuild();
|
const needFullRestart = await rebuild();
|
||||||
if (needFullRestart) DiscordNative.app.relaunch();
|
if (needFullRestart)
|
||||||
else location.reload();
|
relaunch();
|
||||||
|
else
|
||||||
|
location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -67,7 +67,7 @@ export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
|
||||||
instance.pop();
|
instance.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_DEV && !IS_WEB) {
|
if (IS_DEV && IS_DISCORD_DESKTOP) {
|
||||||
var devToolsOpen = false;
|
var devToolsOpen = false;
|
||||||
// At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed
|
// At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
Loading…
Reference in a new issue