Monaco for Discord Desktop
This commit is contained in:
parent
23d4cae123
commit
44f6f71c3e
11 changed files with 173 additions and 90 deletions
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import esbuild from "esbuild";
|
import esbuild from "esbuild";
|
||||||
import { commonOpts, gitHashPlugin, globPlugins, makeAllPackagesExternalPlugin } from "./common.mjs";
|
import { commonOpts, fileIncludePlugin, gitHashPlugin, globPlugins, makeAllPackagesExternalPlugin } from "./common.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.BuildOptions}
|
* @type {esbuild.BuildOptions}
|
||||||
|
@ -30,7 +30,7 @@ const nodeCommonOpts = {
|
||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
minify: true,
|
minify: true,
|
||||||
sourcemap: "linked",
|
sourcemap: "linked",
|
||||||
plugins: [makeAllPackagesExternalPlugin],
|
plugins: [...commonOpts.plugins, makeAllPackagesExternalPlugin],
|
||||||
};
|
};
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
@ -55,7 +55,8 @@ await Promise.all([
|
||||||
external: ["plugins", "git-hash"],
|
external: ["plugins", "git-hash"],
|
||||||
plugins: [
|
plugins: [
|
||||||
globPlugins,
|
globPlugins,
|
||||||
gitHashPlugin
|
gitHashPlugin,
|
||||||
|
fileIncludePlugin
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
IS_WEB: "false"
|
IS_WEB: "false"
|
||||||
|
@ -65,6 +66,6 @@ await Promise.all([
|
||||||
console.error("Build failed");
|
console.error("Build failed");
|
||||||
console.error(err.message);
|
console.error(err.message);
|
||||||
// make ci fail
|
// make ci fail
|
||||||
if (!watch)
|
if (!commonOpts.watch)
|
||||||
process.exitCode = 1;
|
process.exitCode = 1;
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,7 +23,7 @@ import yazl from "yazl";
|
||||||
import esbuild from "esbuild";
|
import esbuild from "esbuild";
|
||||||
// wtf is this assert syntax
|
// wtf is this assert syntax
|
||||||
import PackageJSON from "../../package.json" assert { type: "json" };
|
import PackageJSON from "../../package.json" assert { type: "json" };
|
||||||
import { commonOpts, gitHashPlugin, globPlugins } from "./common.mjs";
|
import { commonOpts, fileIncludePlugin, gitHashPlugin, globPlugins } from "./common.mjs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.BuildOptions}
|
* @type {esbuild.BuildOptions}
|
||||||
|
@ -36,7 +36,8 @@ const commonOptions = {
|
||||||
external: ["plugins", "git-hash"],
|
external: ["plugins", "git-hash"],
|
||||||
plugins: [
|
plugins: [
|
||||||
globPlugins,
|
globPlugins,
|
||||||
gitHashPlugin
|
gitHashPlugin,
|
||||||
|
fileIncludePlugin
|
||||||
],
|
],
|
||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
define: {
|
define: {
|
||||||
|
|
|
@ -19,22 +19,11 @@
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
import esbuild from "esbuild";
|
import esbuild from "esbuild";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
import { readdir } from "fs/promises";
|
import { readdir, readFile } from "fs/promises";
|
||||||
|
import { join } from "path";
|
||||||
|
|
||||||
const watch = process.argv.includes("--watch");
|
const watch = process.argv.includes("--watch");
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {esbuild.BuildOptions}
|
|
||||||
*/
|
|
||||||
export const commonOpts = {
|
|
||||||
logLevel: "info",
|
|
||||||
bundle: true,
|
|
||||||
watch,
|
|
||||||
minify: !watch,
|
|
||||||
sourcemap: watch ? "inline" : "",
|
|
||||||
legalComments: "linked"
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294
|
// https://github.com/evanw/esbuild/issues/619#issuecomment-751995294
|
||||||
/**
|
/**
|
||||||
* @type {esbuild.Plugin}
|
* @type {esbuild.Plugin}
|
||||||
|
@ -103,3 +92,39 @@ export const gitHashPlugin = {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {esbuild.Plugin}
|
||||||
|
*/
|
||||||
|
export const fileIncludePlugin = {
|
||||||
|
name: "file-include-plugin",
|
||||||
|
setup: build => {
|
||||||
|
const filter = /^@fileContent\/.+$/;
|
||||||
|
build.onResolve({ filter }, args => ({
|
||||||
|
namespace: "include-file",
|
||||||
|
path: args.path,
|
||||||
|
pluginData: {
|
||||||
|
path: join(args.resolveDir, args.path.slice("include-file/".length))
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
build.onLoad({ filter, namespace: "include-file" }, async ({ pluginData: { path } }) => {
|
||||||
|
const [name, format] = path.split(";");
|
||||||
|
return {
|
||||||
|
contents: `export default ${JSON.stringify(await readFile(name, format ?? "utf-8"))}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {esbuild.BuildOptions}
|
||||||
|
*/
|
||||||
|
export const commonOpts = {
|
||||||
|
logLevel: "info",
|
||||||
|
bundle: true,
|
||||||
|
watch,
|
||||||
|
minify: !watch,
|
||||||
|
sourcemap: watch ? "inline" : "",
|
||||||
|
legalComments: "linked",
|
||||||
|
plugins: [fileIncludePlugin]
|
||||||
|
};
|
||||||
|
|
|
@ -16,80 +16,26 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import { IpcEvents } from "../utils";
|
import { IpcEvents } from "../utils";
|
||||||
import { debounce } from "../utils/debounce";
|
import { debounce } from "../utils/debounce";
|
||||||
import { find } from "../webpack/webpack";
|
import { find } from "../webpack/webpack";
|
||||||
|
import monacoHtml from "@fileContent/monacoWin.html";
|
||||||
|
import { Queue } from "../utils/Queue";
|
||||||
|
|
||||||
|
const queue = new Queue();
|
||||||
const setCss = debounce((css: string) => {
|
const setCss = debounce((css: string) => {
|
||||||
VencordNative.ipc.invoke(IpcEvents.SET_QUICK_CSS, css);
|
queue.add(() => VencordNative.ipc.invoke(IpcEvents.SET_QUICK_CSS, css));
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME: Discord Desktop support.
|
|
||||||
// open() fails to create the popup and returns null. Probably have to
|
|
||||||
// do some logic in main
|
|
||||||
|
|
||||||
// adapted from https://stackoverflow.com/a/63179814
|
|
||||||
export async function launchMonacoEditor() {
|
export async function launchMonacoEditor() {
|
||||||
const win = open("about:blank", void 0, "popup,width=1000,height=1000")!;
|
const win = open("about:blank", void 0, "popup,width=1000,height=1000")!;
|
||||||
|
|
||||||
|
win.setCss = setCss;
|
||||||
win.getCurrentCss = () => VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS);
|
win.getCurrentCss = () => VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS);
|
||||||
win.callback = (editor: any) => {
|
win.getTheme = () => find(m => m.ProtoClass?.typeName.endsWith("PreloadedUserSettings"))
|
||||||
editor.onDidChangeModelContent(() =>
|
.getCurrentValue().appearance.theme === 1
|
||||||
setCss(editor.getValue())
|
? "vs-dark"
|
||||||
);
|
: "vs-light";
|
||||||
};
|
|
||||||
|
|
||||||
let { theme } = find(m => m.ProtoClass?.typeName.endsWith("PreloadedUserSettings"))
|
win.document.write(monacoHtml);
|
||||||
.getCurrentValue().appearance;
|
|
||||||
theme = theme === 1 ? "vs-dark" : "vs-light";
|
|
||||||
|
|
||||||
// problem?
|
|
||||||
win.document.write(`
|
|
||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>QuickCss Editor</title>
|
|
||||||
<link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/editor/editor.main.min.css">
|
|
||||||
<style>
|
|
||||||
html, body, #container {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="container"></div>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/loader.min.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var editor;
|
|
||||||
require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs' }});
|
|
||||||
require(["vs/editor/editor.main"], () => {
|
|
||||||
getCurrentCss().then(css => {
|
|
||||||
callback(editor = monaco.editor.create(document.getElementById('container'), {
|
|
||||||
value: css,
|
|
||||||
language: 'css',
|
|
||||||
theme: '${theme}',
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("resize", () => {
|
|
||||||
// make monaco re-layout
|
|
||||||
editor.layout();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
`);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ export default ErrorBoundary.wrap(function Settings() {
|
||||||
Launch Directory
|
Launch Directory
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => VencordNative.ipc.invoke(IpcEvents.OPEN_QUICKCSS)}
|
onClick={() => VencordNative.ipc.invoke(IpcEvents.OPEN_MONACO_EDITOR)}
|
||||||
size={Button.Sizes.SMALL}
|
size={Button.Sizes.SMALL}
|
||||||
disabled={settingsDir === "Loading..."}
|
disabled={settingsDir === "Loading..."}
|
||||||
>
|
>
|
||||||
|
|
52
src/components/monacoWin.html
Normal file
52
src/components/monacoWin.html
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>QuickCss Editor</title>
|
||||||
|
<link rel="stylesheet" data-name="vs/editor/editor.main"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/editor/editor.main.min.css">
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#container {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="container"></div>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/loader.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs' } });
|
||||||
|
require(["vs/editor/editor.main"], () => {
|
||||||
|
getCurrentCss().then(css => {
|
||||||
|
var editor = monaco.editor.create(document.getElementById('container'), {
|
||||||
|
value: css,
|
||||||
|
language: 'css',
|
||||||
|
theme: getTheme(),
|
||||||
|
});
|
||||||
|
editor.onDidChangeModelContent(() =>
|
||||||
|
setCss(editor.getValue())
|
||||||
|
);
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
// make monaco re-layout
|
||||||
|
editor.layout();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -22,8 +22,10 @@ import { open, readFile, writeFile } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { debounce } from "../utils/debounce";
|
import { debounce } from "../utils/debounce";
|
||||||
import IpcEvents from "../utils/IpcEvents";
|
import IpcEvents from "../utils/IpcEvents";
|
||||||
|
import monacoHtml from "@fileContent/../components/monacoWin.html;base64";
|
||||||
|
|
||||||
import "./updater";
|
import "./updater";
|
||||||
|
import { Queue } from "../utils/Queue";
|
||||||
|
|
||||||
const DATA_DIR = process.env.VENCORD_USER_DATA_DIR ?? (
|
const DATA_DIR = process.env.VENCORD_USER_DATA_DIR ?? (
|
||||||
process.env.DISCORD_USER_DATA_DIR
|
process.env.DISCORD_USER_DATA_DIR
|
||||||
|
@ -71,15 +73,19 @@ ipcMain.handle(IpcEvents.OPEN_EXTERNAL, (_, url) => {
|
||||||
shell.openExternal(url);
|
shell.openExternal(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const cssWriteQueue = new Queue();
|
||||||
|
const settingsWriteQueue = new Queue();
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.GET_QUICK_CSS, () => readCss());
|
ipcMain.handle(IpcEvents.GET_QUICK_CSS, () => readCss());
|
||||||
|
ipcMain.handle(IpcEvents.SET_QUICK_CSS, (_, css) =>
|
||||||
|
cssWriteQueue.add(() => writeFile(QUICKCSS_PATH, css))
|
||||||
|
);
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.GET_SETTINGS_DIR, () => SETTINGS_DIR);
|
ipcMain.handle(IpcEvents.GET_SETTINGS_DIR, () => SETTINGS_DIR);
|
||||||
ipcMain.on(IpcEvents.GET_SETTINGS, e => e.returnValue = readSettings());
|
ipcMain.on(IpcEvents.GET_SETTINGS, e => e.returnValue = readSettings());
|
||||||
|
|
||||||
let settingsWriteQueue = Promise.resolve();
|
|
||||||
ipcMain.handle(IpcEvents.SET_SETTINGS, (_, s) => {
|
ipcMain.handle(IpcEvents.SET_SETTINGS, (_, s) => {
|
||||||
settingsWriteQueue = settingsWriteQueue.then(() => writeFile(SETTINGS_FILE, s));
|
settingsWriteQueue.add(() => writeFile(SETTINGS_FILE, s));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,3 +97,13 @@ export function initIpc(mainWindow: BrowserWindow) {
|
||||||
}, 50));
|
}, 50));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ipcMain.handle(IpcEvents.OPEN_MONACO_EDITOR, async () => {
|
||||||
|
const win = new BrowserWindow({
|
||||||
|
title: "QuickCss Editor",
|
||||||
|
webPreferences: {
|
||||||
|
preload: join(__dirname, "preload.js"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await win.loadURL(`data:text/html;base64,${monacoHtml}`);
|
||||||
|
});
|
||||||
|
|
5
src/modules.d.ts
vendored
5
src/modules.d.ts
vendored
|
@ -28,3 +28,8 @@ declare module "git-hash" {
|
||||||
const hash: string;
|
const hash: string;
|
||||||
export default hash;
|
export default hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "@fileContent/*" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { readFileSync } from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import VencordNative from "./VencordNative";
|
import VencordNative from "./VencordNative";
|
||||||
import IpcEvents from "./utils/IpcEvents";
|
import IpcEvents from "./utils/IpcEvents";
|
||||||
|
import { debounce } from "./utils/debounce";
|
||||||
|
|
||||||
if (electron.desktopCapturer === void 0) {
|
if (electron.desktopCapturer === void 0) {
|
||||||
// Fix for desktopCapturer being main only in Electron 17+
|
// Fix for desktopCapturer being main only in Electron 17+
|
||||||
|
@ -39,6 +40,14 @@ if (electron.desktopCapturer === void 0) {
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("VencordNative", VencordNative);
|
contextBridge.exposeInMainWorld("VencordNative", VencordNative);
|
||||||
|
|
||||||
webFrame.executeJavaScript(readFileSync(join(__dirname, "renderer.js"), "utf-8"));
|
if (location.protocol !== "data:") {
|
||||||
|
// Discord
|
||||||
require(process.env.DISCORD_PRELOAD!);
|
webFrame.executeJavaScript(readFileSync(join(__dirname, "renderer.js"), "utf-8"));
|
||||||
|
require(process.env.DISCORD_PRELOAD!);
|
||||||
|
} else {
|
||||||
|
// Monaco Popout
|
||||||
|
contextBridge.exposeInMainWorld("setCss", debounce(s => VencordNative.ipc.invoke(IpcEvents.SET_QUICK_CSS, s)));
|
||||||
|
contextBridge.exposeInMainWorld("getCurrentCss", () => VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS));
|
||||||
|
// shrug
|
||||||
|
contextBridge.exposeInMainWorld("getTheme", () => "vs-dark");
|
||||||
|
}
|
||||||
|
|
|
@ -43,5 +43,6 @@ export default strEnum({
|
||||||
GET_HASHES: "VencordGetHashes",
|
GET_HASHES: "VencordGetHashes",
|
||||||
UPDATE: "VencordUpdate",
|
UPDATE: "VencordUpdate",
|
||||||
BUILD: "VencordBuild",
|
BUILD: "VencordBuild",
|
||||||
GET_DESKTOP_CAPTURE_SOURCES: "VencordGetDesktopCaptureSources"
|
GET_DESKTOP_CAPTURE_SOURCES: "VencordGetDesktopCaptureSources",
|
||||||
|
OPEN_MONACO_EDITOR: "VencordOpenMonacoEditor"
|
||||||
} as const);
|
} as const);
|
||||||
|
|
27
src/utils/Queue.ts
Normal file
27
src/utils/Queue.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 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 { Promisable } from "type-fest";
|
||||||
|
|
||||||
|
export class Queue {
|
||||||
|
private promise = Promise.resolve();
|
||||||
|
|
||||||
|
add(func: () => Promisable<void>) {
|
||||||
|
this.promise = this.promise.then(func);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue