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 { commonOpts, gitHashPlugin, globPlugins, makeAllPackagesExternalPlugin } from "./common.mjs";
|
||||
import { commonOpts, fileIncludePlugin, gitHashPlugin, globPlugins, makeAllPackagesExternalPlugin } from "./common.mjs";
|
||||
|
||||
/**
|
||||
* @type {esbuild.BuildOptions}
|
||||
|
@ -30,7 +30,7 @@ const nodeCommonOpts = {
|
|||
target: ["esnext"],
|
||||
minify: true,
|
||||
sourcemap: "linked",
|
||||
plugins: [makeAllPackagesExternalPlugin],
|
||||
plugins: [...commonOpts.plugins, makeAllPackagesExternalPlugin],
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
|
@ -55,7 +55,8 @@ await Promise.all([
|
|||
external: ["plugins", "git-hash"],
|
||||
plugins: [
|
||||
globPlugins,
|
||||
gitHashPlugin
|
||||
gitHashPlugin,
|
||||
fileIncludePlugin
|
||||
],
|
||||
define: {
|
||||
IS_WEB: "false"
|
||||
|
@ -65,6 +66,6 @@ await Promise.all([
|
|||
console.error("Build failed");
|
||||
console.error(err.message);
|
||||
// make ci fail
|
||||
if (!watch)
|
||||
if (!commonOpts.watch)
|
||||
process.exitCode = 1;
|
||||
});
|
||||
|
|
|
@ -23,7 +23,7 @@ import yazl from "yazl";
|
|||
import esbuild from "esbuild";
|
||||
// wtf is this assert syntax
|
||||
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}
|
||||
|
@ -36,7 +36,8 @@ const commonOptions = {
|
|||
external: ["plugins", "git-hash"],
|
||||
plugins: [
|
||||
globPlugins,
|
||||
gitHashPlugin
|
||||
gitHashPlugin,
|
||||
fileIncludePlugin
|
||||
],
|
||||
target: ["esnext"],
|
||||
define: {
|
||||
|
|
|
@ -19,22 +19,11 @@
|
|||
import { execSync } from "child_process";
|
||||
import esbuild from "esbuild";
|
||||
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");
|
||||
|
||||
/**
|
||||
* @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
|
||||
/**
|
||||
* @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/>.
|
||||
*/
|
||||
|
||||
|
||||
import { IpcEvents } from "../utils";
|
||||
import { debounce } from "../utils/debounce";
|
||||
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) => {
|
||||
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() {
|
||||
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.callback = (editor: any) => {
|
||||
editor.onDidChangeModelContent(() =>
|
||||
setCss(editor.getValue())
|
||||
);
|
||||
};
|
||||
win.getTheme = () => find(m => m.ProtoClass?.typeName.endsWith("PreloadedUserSettings"))
|
||||
.getCurrentValue().appearance.theme === 1
|
||||
? "vs-dark"
|
||||
: "vs-light";
|
||||
|
||||
let { theme } = find(m => m.ProtoClass?.typeName.endsWith("PreloadedUserSettings"))
|
||||
.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>
|
||||
|
||||
`);
|
||||
win.document.write(monacoHtml);
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ export default ErrorBoundary.wrap(function Settings() {
|
|||
Launch Directory
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => VencordNative.ipc.invoke(IpcEvents.OPEN_QUICKCSS)}
|
||||
onClick={() => VencordNative.ipc.invoke(IpcEvents.OPEN_MONACO_EDITOR)}
|
||||
size={Button.Sizes.SMALL}
|
||||
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 { debounce } from "../utils/debounce";
|
||||
import IpcEvents from "../utils/IpcEvents";
|
||||
import monacoHtml from "@fileContent/../components/monacoWin.html;base64";
|
||||
|
||||
import "./updater";
|
||||
import { Queue } from "../utils/Queue";
|
||||
|
||||
const DATA_DIR = process.env.VENCORD_USER_DATA_DIR ?? (
|
||||
process.env.DISCORD_USER_DATA_DIR
|
||||
|
@ -71,15 +73,19 @@ ipcMain.handle(IpcEvents.OPEN_EXTERNAL, (_, url) => {
|
|||
shell.openExternal(url);
|
||||
});
|
||||
|
||||
const cssWriteQueue = new Queue();
|
||||
const settingsWriteQueue = new Queue();
|
||||
|
||||
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.on(IpcEvents.GET_SETTINGS, e => e.returnValue = readSettings());
|
||||
|
||||
let settingsWriteQueue = Promise.resolve();
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
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 VencordNative from "./VencordNative";
|
||||
import IpcEvents from "./utils/IpcEvents";
|
||||
import { debounce } from "./utils/debounce";
|
||||
|
||||
if (electron.desktopCapturer === void 0) {
|
||||
// Fix for desktopCapturer being main only in Electron 17+
|
||||
|
@ -39,6 +40,14 @@ if (electron.desktopCapturer === void 0) {
|
|||
|
||||
contextBridge.exposeInMainWorld("VencordNative", VencordNative);
|
||||
|
||||
if (location.protocol !== "data:") {
|
||||
// Discord
|
||||
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",
|
||||
UPDATE: "VencordUpdate",
|
||||
BUILD: "VencordBuild",
|
||||
GET_DESKTOP_CAPTURE_SOURCES: "VencordGetDesktopCaptureSources"
|
||||
GET_DESKTOP_CAPTURE_SOURCES: "VencordGetDesktopCaptureSources",
|
||||
OPEN_MONACO_EDITOR: "VencordOpenMonacoEditor"
|
||||
} 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