From ce8e48bb6db20476ecc743a4002b44af42e5cec5 Mon Sep 17 00:00:00 2001 From: V Date: Sat, 20 Dec 2025 01:02:43 +0100 Subject: [PATCH] Unify styles into single root node & support preload sandboxing (#3797) --- browser/VencordNativeStub.ts | 29 ++++-- browser/background.js | 2 +- browser/content.js | 7 +- browser/modifyResponseHeaders.json | 5 +- scripts/build/buildWeb.mjs | 9 +- src/Vencord.ts | 7 +- src/VencordNative.ts | 27 ++++-- src/api/Notifications/notificationLog.tsx | 2 +- src/api/Styles.ts | 88 +++++++++++++------ src/api/Themes.ts | 17 +--- src/components/BaseText.tsx | 2 +- src/components/Button.tsx | 2 +- src/components/Card.tsx | 2 +- src/components/Switch.tsx | 2 +- src/components/css.ts | 15 ---- src/components/margins.ts | 2 +- src/components/settings/AddonCard.tsx | 2 +- src/components/settings/QuickAction.tsx | 2 +- src/components/settings/SpecialCard.tsx | 2 +- .../tabs/plugins/ContributorModal.tsx | 2 +- .../settings/tabs/plugins/PluginModal.tsx | 2 +- .../settings/tabs/plugins/UIElements.tsx | 2 +- .../tabs/plugins/components/Common.tsx | 2 +- .../settings/tabs/plugins/index.tsx | 2 +- .../settings/tabs/themes/LocalThemesTab.tsx | 2 +- src/main/ipcMain.ts | 63 +++++++++---- src/main/patcher.ts | 5 +- src/main/utils/constants.ts | 2 +- .../clientTheme/components/Settings.tsx | 2 +- src/plugins/customRPC/RpcSettings.tsx | 2 +- src/plugins/decor/ui/index.ts | 2 +- .../imageZoom/components/Magnifier.tsx | 2 +- src/plugins/memberCount/index.tsx | 2 +- src/plugins/messageLogger/HistoryModal.tsx | 2 +- src/plugins/permissionsViewer/utils.ts | 2 +- .../pinDms/components/CreateCategoryModal.tsx | 2 +- src/plugins/reviewDB/utils.tsx | 2 +- src/plugins/sendTimestamps/index.tsx | 2 +- src/plugins/serverInfo/GuildInfoModal.tsx | 2 +- .../shikiCodeblocks.desktop/utils/misc.ts | 2 +- src/plugins/showHiddenChannels/index.tsx | 2 +- src/plugins/sortFriendRequests/index.tsx | 2 +- .../spotifyControls/PlayerComponent.tsx | 2 +- src/plugins/translate/utils.ts | 2 +- src/plugins/userVoiceShow/components.tsx | 2 +- src/plugins/voiceMessages/utils.ts | 2 +- src/preload.ts | 37 ++------ src/shared/IpcEvents.ts | 12 +-- src/utils/css.ts | 25 +++++- src/utils/web-metadata.ts | 9 +- 50 files changed, 239 insertions(+), 186 deletions(-) delete mode 100644 src/components/css.ts diff --git a/browser/VencordNativeStub.ts b/browser/VencordNativeStub.ts index 64961275..77272d41 100644 --- a/browser/VencordNativeStub.ts +++ b/browser/VencordNativeStub.ts @@ -28,7 +28,7 @@ import { getThemeInfo } from "@main/themes"; import { debounce } from "@shared/debounce"; import { localStorage } from "@utils/localStorage"; import { getStylusWebStoreUrl } from "@utils/web"; -import { EXTENSION_BASE_URL } from "@utils/web-metadata"; +import { EXTENSION_BASE_URL, metaReady, RENDERER_CSS_URL } from "@utils/web-metadata"; // listeners for ipc.on const cssListeners = new Set<(css: string) => void>(); @@ -55,7 +55,18 @@ window.VencordNative = { native: { getVersions: () => ({}), - openExternal: async (url) => void open(url, "_blank") + openExternal: async (url) => void open(url, "_blank"), + getRendererCss: async () => { + if (IS_USERSCRIPT) + // need to wait for next tick for _vcUserScriptRendererCss to be set + return Promise.resolve().then(() => window._vcUserScriptRendererCss); + + await metaReady; + + return fetch(RENDERER_CSS_URL) + .then(res => res.text()); + }, + onRendererCssUpdate: NOOP, }, updater: { @@ -92,18 +103,20 @@ window.VencordNative = { return; } - const { getTheme, Theme } = require("@utils/discord"); - win.baseUrl = EXTENSION_BASE_URL; win.setCss = setCssDebounced; win.getCurrentCss = () => VencordNative.quickCss.get(); - win.getTheme = () => - getTheme() === Theme.Light - ? "vs-light" - : "vs-dark"; + win.getTheme = this.getEditorTheme; win.document.write(monacoHtmlLocal); }, + getEditorTheme: () => { + const { getTheme, Theme } = require("@utils/discord"); + + return getTheme() === Theme.Light + ? "vs-light" + : "vs-dark"; + } }, settings: { diff --git a/browser/background.js b/browser/background.js index 1f2d5ec1..2489f823 100644 --- a/browser/background.js +++ b/browser/background.js @@ -12,7 +12,7 @@ chrome.webRequest.onHeadersReceived.addListener( ({ responseHeaders, type, url }) => { if (!responseHeaders) return; - if (type === "main_frame") { + if (type === "main_frame" && url.includes("discord.com")) { // In main frame requests, the CSP needs to be removed to enable fetching of custom css // as desired by the user removeFirst(responseHeaders, h => h.name.toLowerCase() === "content-security-policy"); diff --git a/browser/content.js b/browser/content.js index 2b51ebab..fff893ba 100644 --- a/browser/content.js +++ b/browser/content.js @@ -2,20 +2,15 @@ if (typeof browser === "undefined") { var browser = chrome; } -const style = document.createElement("link"); -style.type = "text/css"; -style.rel = "stylesheet"; -style.href = browser.runtime.getURL("dist/Vencord.css"); - document.addEventListener( "DOMContentLoaded", () => { - document.body.insertAdjacentElement("afterend", style); window.postMessage({ type: "vencord:meta", meta: { EXTENSION_VERSION: browser.runtime.getManifest().version, EXTENSION_BASE_URL: browser.runtime.getURL(""), + RENDERER_CSS_URL: browser.runtime.getURL("dist/Vencord.css"), } }); }, diff --git a/browser/modifyResponseHeaders.json b/browser/modifyResponseHeaders.json index 5b0b3e37..ffe34add 100644 --- a/browser/modifyResponseHeaders.json +++ b/browser/modifyResponseHeaders.json @@ -15,7 +15,8 @@ ] }, "condition": { - "resourceTypes": ["main_frame", "sub_frame"] + "resourceTypes": ["main_frame", "sub_frame"], + "urlFilter": "||discord.com^" } }, { @@ -32,7 +33,7 @@ }, "condition": { "resourceTypes": ["stylesheet"], - "urlFilter": "https://raw.githubusercontent.com/*" + "urlFilter": "||raw.githubusercontent.com^" } } ] diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index e8269477..6cbb1644 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -176,14 +176,7 @@ async function buildExtension(target, files) { } const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content => { - const cssRuntime = ` -;document.addEventListener("DOMContentLoaded", () => document.body.insertAdjacentElement("afterend", - Object.assign(document.createElement("style"), { - textContent: \`${content.replaceAll("`", "\\`")}\`, - id: "vencord-css-core" - }) -), { once: true }); -`; + const cssRuntime = `unsafeWindow._vcUserScriptRendererCss=\`${content.replaceAll("`", "\\`")}\``; return appendFile("dist/Vencord.user.js", cssRuntime); }); diff --git a/src/Vencord.ts b/src/Vencord.ts index dcef98aa..d451e9f2 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -28,7 +28,7 @@ export * as Webpack from "./webpack"; export * as WebpackPatcher from "./webpack/patchWebpack"; export { PlainSettings, Settings }; -import { addVencordUiStyles } from "@components/css"; +import { coreStyleRootNode, initStyles } from "@api/Styles"; import { openSettingsTabModal, UpdaterTab } from "@components/settings"; import { debounce } from "@shared/debounce"; import { IS_WINDOWS } from "@utils/constants"; @@ -178,16 +178,15 @@ async function init() { } initPluginManager(); +initStyles(); startAllPlugins(StartAt.Init); init(); document.addEventListener("DOMContentLoaded", () => { - addVencordUiStyles(); - startAllPlugins(StartAt.DOMContentLoaded); // FIXME if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && IS_WINDOWS) { - createAndAppendStyle("vencord-native-titlebar-style").textContent = "[class*=titleBar]{display: none!important}"; + createAndAppendStyle("vencord-native-titlebar-style", coreStyleRootNode).textContent = "[class*=titleBar]{display: none!important}"; } }, { once: true }); diff --git a/src/VencordNative.ts b/src/VencordNative.ts index 048b30c7..e30f83e8 100644 --- a/src/VencordNative.ts +++ b/src/VencordNative.ts @@ -5,14 +5,14 @@ */ import type { Settings } from "@api/Settings"; -import { CspRequestResult } from "@main/csp/manager"; -import { PluginIpcMappings } from "@main/ipcPlugins"; +import type { CspRequestResult } from "@main/csp/manager"; +import type { PluginIpcMappings } from "@main/ipcPlugins"; import type { UserThemeHeader } from "@main/themes"; import { IpcEvents } from "@shared/IpcEvents"; -import { IpcRes } from "@utils/types"; -import { ipcRenderer } from "electron"; +import type { IpcRes } from "@utils/types"; +import { ipcRenderer } from "electron/renderer"; -function invoke(event: IpcEvents, ...args: any[]) { +export function invoke(event: IpcEvents, ...args: any[]) { return ipcRenderer.invoke(event, ...args) as Promise; } @@ -32,8 +32,12 @@ for (const [plugin, methods] of Object.entries(pluginIpcMap)) { export default { themes: { - uploadTheme: (fileName: string, fileData: string) => invoke(IpcEvents.UPLOAD_THEME, fileName, fileData), - deleteTheme: (fileName: string) => invoke(IpcEvents.DELETE_THEME, fileName), + uploadTheme: async (fileName: string, fileData: string): Promise => { + throw new Error("uploadTheme is WEB only"); + }, + deleteTheme: async (fileName: string): Promise => { + throw new Error("deleteTheme is WEB only"); + }, getThemesList: () => invoke>(IpcEvents.GET_THEMES_LIST), getThemeData: (fileName: string) => invoke(IpcEvents.GET_THEME_DATA, fileName), getSystemValues: () => invoke>(IpcEvents.GET_THEME_SYSTEM_VALUES), @@ -69,11 +73,18 @@ export default { openFile: () => invoke(IpcEvents.OPEN_QUICKCSS), openEditor: () => invoke(IpcEvents.OPEN_MONACO_EDITOR), + getEditorTheme: () => sendSync(IpcEvents.GET_MONACO_THEME), }, native: { getVersions: () => process.versions as Partial, - openExternal: (url: string) => invoke(IpcEvents.OPEN_EXTERNAL, url) + openExternal: (url: string) => invoke(IpcEvents.OPEN_EXTERNAL, url), + getRendererCss: () => invoke(IpcEvents.GET_RENDERER_CSS), + onRendererCssUpdate: (cb: (newCss: string) => void) => { + if (!IS_DEV) return; + + ipcRenderer.on(IpcEvents.RENDERER_CSS_UPDATE, (_e, newCss: string) => cb(newCss)); + } }, csp: { diff --git a/src/api/Notifications/notificationLog.tsx b/src/api/Notifications/notificationLog.tsx index dca7679e..5b6ef676 100644 --- a/src/api/Notifications/notificationLog.tsx +++ b/src/api/Notifications/notificationLog.tsx @@ -18,9 +18,9 @@ import * as DataStore from "@api/DataStore"; import { Settings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import { Flex } from "@components/Flex"; import { openNotificationSettingsModal } from "@components/settings/tabs/vencord/NotificationSettings"; +import { classNameFactory } from "@utils/css"; import { closeModal, ModalCloseButton, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { useAwaiter } from "@utils/react"; import { Alerts, Button, Forms, ListScrollerThin, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common"; diff --git a/src/api/Styles.ts b/src/api/Styles.ts index fb40f72d..967cef64 100644 --- a/src/api/Styles.ts +++ b/src/api/Styles.ts @@ -16,6 +16,14 @@ * along with this program. If not, see . */ +import { generateTextCss } from "@components/BaseText"; +import { generateMarginCss } from "@components/margins"; +import { classNameFactory as _classNameFactory, classNameToSelector, createAndAppendStyle } from "@utils/css"; + +// Backwards compat for Vesktop +/** @deprecated Import this from `@utils/css` instead */ +export const classNameFactory = _classNameFactory; + export interface Style { name: string; source: string; @@ -25,6 +33,57 @@ export interface Style { export const styleMap = window.VencordStyles ??= new Map(); +export const vencordRootNode = document.createElement("vencord-root"); +/** + * Houses all Vencord core styles. This includes all imported css files + */ +export const coreStyleRootNode = document.createElement("vencord-styles"); +/** + * Houses all plugin specific managed styles + */ +export const managedStyleRootNode = document.createElement("vencord-managed-styles"); +/** + * Houses the user's themes and quick css + */ +export const userStyleRootNode = document.createElement("vencord-user-styles"); + +vencordRootNode.style.display = "none"; +vencordRootNode.append(coreStyleRootNode, managedStyleRootNode, userStyleRootNode); + +export function initStyles() { + const osValuesNode = createAndAppendStyle("vencord-os-theme-values", coreStyleRootNode); + createAndAppendStyle("vencord-text", coreStyleRootNode).textContent = generateTextCss(); + const rendererCssNode = createAndAppendStyle("vencord-css-core", coreStyleRootNode); + const vesktopCssNode = IS_VESKTOP ? createAndAppendStyle("vesktop-css-core", coreStyleRootNode) : null; + createAndAppendStyle("vencord-margins", coreStyleRootNode).textContent = generateMarginCss(); + + VencordNative.native.getRendererCss().then(css => rendererCssNode.textContent = css); + if (IS_DEV) { + VencordNative.native.onRendererCssUpdate(newCss => { + rendererCssNode.textContent = newCss; + }); + } + + if (IS_VESKTOP && VesktopNative.app.getRendererCss) { + VesktopNative.app.getRendererCss().then(css => vesktopCssNode!.textContent = css); + VesktopNative.app.onRendererCssUpdate(newCss => { + vesktopCssNode!.textContent = newCss; + }); + } + + VencordNative.themes.getSystemValues().then(values => { + const variables = Object.entries(values) + .filter(([, v]) => !!v) + .map(([k, v]) => `--${k}: ${v};`) + .join(""); + osValuesNode.textContent = `:root{${variables}}`; + }); +} + +document.addEventListener("DOMContentLoaded", () => { + document.documentElement.append(vencordRootNode); +}, { once: true }); + export function requireStyle(name: string) { const style = styleMap.get(name); if (!style) throw new Error(`Style "${name}" does not exist`); @@ -53,7 +112,7 @@ export function enableStyle(name: string) { } compileStyle(style); - document.head.appendChild(style.dom); + managedStyleRootNode.appendChild(style.dom); return true; } @@ -135,31 +194,4 @@ export const compileStyle = (style: Style) => { }); }; -/** - * @param name The classname - * @param prefix A prefix to add each class, defaults to `""` - * @return A css selector for the classname - * @example - * classNameToSelector("foo bar") // => ".foo.bar" - */ -export const classNameToSelector = (name: string, prefix = "") => name.split(" ").map(n => `.${prefix}${n}`).join(""); -type ClassNameFactoryArg = string | string[] | Record | false | null | undefined | 0 | ""; -/** - * @param prefix The prefix to add to each class, defaults to `""` - * @returns A classname generator function - * @example - * const cl = classNameFactory("plugin-"); - * - * cl("base", ["item", "editable"], { selected: null, disabled: true }) - * // => "plugin-base plugin-item plugin-editable plugin-disabled" - */ -export const classNameFactory = (prefix: string = "") => (...args: ClassNameFactoryArg[]) => { - const classNames = new Set(); - for (const arg of args) { - if (arg && typeof arg === "string") classNames.add(arg); - else if (Array.isArray(arg)) arg.forEach(name => classNames.add(name)); - else if (arg && typeof arg === "object") Object.entries(arg).forEach(([name, value]) => value && classNames.add(name)); - } - return Array.from(classNames, name => prefix + name).join(" "); -}; diff --git a/src/api/Themes.ts b/src/api/Themes.ts index 9fdfdba5..dca7d8c0 100644 --- a/src/api/Themes.ts +++ b/src/api/Themes.ts @@ -20,23 +20,15 @@ import { Settings, SettingsStore } from "@api/Settings"; import { createAndAppendStyle } from "@utils/css"; import { ThemeStore } from "@vencord/discord-types"; +import { userStyleRootNode } from "./Styles"; + let style: HTMLStyleElement; let themesStyle: HTMLStyleElement; -async function initSystemValues() { - const values = await VencordNative.themes.getSystemValues(); - const variables = Object.entries(values) - .filter(([, v]) => v !== "#") - .map(([k, v]) => `--${k}: ${v};`) - .join(""); - - createAndAppendStyle("vencord-os-theme-values").textContent = `:root{${variables}}`; -} - async function toggle(isEnabled: boolean) { if (!style) { if (isEnabled) { - style = createAndAppendStyle("vencord-custom-css"); + style = createAndAppendStyle("vencord-custom-css", userStyleRootNode); VencordNative.quickCss.addChangeListener(css => { style.textContent = css; // At the time of writing this, changing textContent resets the disabled state @@ -49,7 +41,7 @@ async function toggle(isEnabled: boolean) { } async function initThemes() { - themesStyle ??= createAndAppendStyle("vencord-themes"); + themesStyle ??= createAndAppendStyle("vencord-themes", userStyleRootNode); const { themeLinks, enabledThemes } = Settings; @@ -89,7 +81,6 @@ async function initThemes() { document.addEventListener("DOMContentLoaded", () => { if (IS_USERSCRIPT) return; - initSystemValues(); initThemes(); toggle(Settings.useQuickCss); diff --git a/src/components/BaseText.tsx b/src/components/BaseText.tsx index 1abddf7a..7ce49d69 100644 --- a/src/components/BaseText.tsx +++ b/src/components/BaseText.tsx @@ -6,7 +6,7 @@ import "./BaseText.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { classes } from "@utils/misc"; import type { Text as DiscordText } from "@vencord/discord-types"; import type { ComponentPropsWithoutRef, ReactNode } from "react"; diff --git a/src/components/Button.tsx b/src/components/Button.tsx index b4390c23..2cfbd83d 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -6,7 +6,7 @@ import "./Button.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { classes } from "@utils/misc"; import type { Button as DiscordButton } from "@vencord/discord-types"; import type { ComponentPropsWithRef } from "react"; diff --git a/src/components/Card.tsx b/src/components/Card.tsx index be32d978..dfa6caaa 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -6,7 +6,7 @@ import "./Card.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { classes } from "@utils/misc"; import { ComponentPropsWithRef } from "react"; diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx index df630ca7..2c1d8353 100644 --- a/src/components/Switch.tsx +++ b/src/components/Switch.tsx @@ -18,7 +18,7 @@ import "./Switch.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { classes } from "@utils/misc"; import { useState } from "@webpack/common"; import type { FocusEvent } from "react"; diff --git a/src/components/css.ts b/src/components/css.ts deleted file mode 100644 index 51151b1f..00000000 --- a/src/components/css.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2025 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { createAndAppendStyle } from "@utils/css"; - -import { generateTextCss } from "./BaseText"; -import { generateMarginCss } from "./margins"; - -export function addVencordUiStyles() { - createAndAppendStyle("vencord-text", document.head).textContent = generateTextCss(); - createAndAppendStyle("vencord-margins").textContent = generateMarginCss(); -} diff --git a/src/components/margins.ts b/src/components/margins.ts index 24138d2a..1ac7aceb 100644 --- a/src/components/margins.ts +++ b/src/components/margins.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; const marginCls = classNameFactory("vc-margin-"); diff --git a/src/components/settings/AddonCard.tsx b/src/components/settings/AddonCard.tsx index 0dc69d6f..22413da4 100644 --- a/src/components/settings/AddonCard.tsx +++ b/src/components/settings/AddonCard.tsx @@ -18,9 +18,9 @@ import "./AddonCard.css"; -import { classNameFactory } from "@api/Styles"; import { AddonBadge } from "@components/settings/PluginBadge"; import { Switch } from "@components/Switch"; +import { classNameFactory } from "@utils/css"; import { Text, useRef } from "@webpack/common"; import type { MouseEventHandler, ReactNode } from "react"; diff --git a/src/components/settings/QuickAction.tsx b/src/components/settings/QuickAction.tsx index f4acbb69..49b893a5 100644 --- a/src/components/settings/QuickAction.tsx +++ b/src/components/settings/QuickAction.tsx @@ -6,8 +6,8 @@ import "./QuickAction.css"; -import { classNameFactory } from "@api/Styles"; import { Card } from "@components/Card"; +import { classNameFactory } from "@utils/css"; import type { ComponentType, PropsWithChildren, ReactNode } from "react"; const cl = classNameFactory("vc-settings-quickActions-"); diff --git a/src/components/settings/SpecialCard.tsx b/src/components/settings/SpecialCard.tsx index 16c3cac0..cf03f420 100644 --- a/src/components/settings/SpecialCard.tsx +++ b/src/components/settings/SpecialCard.tsx @@ -18,9 +18,9 @@ import "./SpecialCard.css"; -import { classNameFactory } from "@api/Styles"; import { Card } from "@components/Card"; import { Divider } from "@components/Divider"; +import { classNameFactory } from "@utils/css"; import { Clickable, Forms } from "@webpack/common"; import type { PropsWithChildren } from "react"; diff --git a/src/components/settings/tabs/plugins/ContributorModal.tsx b/src/components/settings/tabs/plugins/ContributorModal.tsx index 4794a084..31ed77f3 100644 --- a/src/components/settings/tabs/plugins/ContributorModal.tsx +++ b/src/components/settings/tabs/plugins/ContributorModal.tsx @@ -7,10 +7,10 @@ import "./ContributorModal.css"; import { useSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Link } from "@components/Link"; import { DevsById } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { fetchUserProfile } from "@utils/discord"; import { classes, pluralise } from "@utils/misc"; import { ModalContent, ModalRoot, openModal } from "@utils/modal"; diff --git a/src/components/settings/tabs/plugins/PluginModal.tsx b/src/components/settings/tabs/plugins/PluginModal.tsx index 2a3109b8..46c9132c 100644 --- a/src/components/settings/tabs/plugins/PluginModal.tsx +++ b/src/components/settings/tabs/plugins/PluginModal.tsx @@ -20,11 +20,11 @@ import "./PluginModal.css"; import { generateId } from "@api/Commands"; import { useSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { debounce } from "@shared/debounce"; import { gitRemote } from "@shared/vencordUserAgent"; +import { classNameFactory } from "@utils/css"; import { proxyLazy } from "@utils/lazy"; import { Margins } from "@utils/margins"; import { classes, isObjectEmpty } from "@utils/misc"; diff --git a/src/components/settings/tabs/plugins/UIElements.tsx b/src/components/settings/tabs/plugins/UIElements.tsx index df85ef73..1e4600ac 100644 --- a/src/components/settings/tabs/plugins/UIElements.tsx +++ b/src/components/settings/tabs/plugins/UIElements.tsx @@ -9,12 +9,12 @@ import "./UIElements.css"; import { ChatBarButtonMap } from "@api/ChatButtons"; import { MessagePopoverButtonMap } from "@api/MessagePopover"; import { SettingsPluginUiElements, useSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import { BaseText } from "@components/BaseText"; import { Card } from "@components/Card"; import { PlaceholderIcon } from "@components/Icons"; import { Paragraph } from "@components/Paragraph"; import { Switch } from "@components/Switch"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { ModalContent, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; diff --git a/src/components/settings/tabs/plugins/components/Common.tsx b/src/components/settings/tabs/plugins/components/Common.tsx index c7c2471a..0280714f 100644 --- a/src/components/settings/tabs/plugins/components/Common.tsx +++ b/src/components/settings/tabs/plugins/components/Common.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { classes } from "@utils/misc"; import { wordsFromCamel, wordsToTitle } from "@utils/text"; import { DefinedSettings, PluginOptionBase } from "@utils/types"; diff --git a/src/components/settings/tabs/plugins/index.tsx b/src/components/settings/tabs/plugins/index.tsx index e28d3202..92fd3454 100644 --- a/src/components/settings/tabs/plugins/index.tsx +++ b/src/components/settings/tabs/plugins/index.tsx @@ -21,7 +21,6 @@ import "./styles.css"; import * as DataStore from "@api/DataStore"; import { isPluginEnabled } from "@api/PluginManager"; import { useSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import { Card } from "@components/Card"; import { Divider } from "@components/Divider"; import ErrorBoundary from "@components/ErrorBoundary"; @@ -29,6 +28,7 @@ import { HeadingTertiary } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab"; import { ChangeList } from "@utils/ChangeList"; +import { classNameFactory } from "@utils/css"; import { isTruthy } from "@utils/guards"; import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; diff --git a/src/components/settings/tabs/themes/LocalThemesTab.tsx b/src/components/settings/tabs/themes/LocalThemesTab.tsx index 02554b68..2f3a812b 100644 --- a/src/components/settings/tabs/themes/LocalThemesTab.tsx +++ b/src/components/settings/tabs/themes/LocalThemesTab.tsx @@ -6,7 +6,6 @@ import { isPluginEnabled } from "@api/PluginManager"; import { Settings, useSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import { Card } from "@components/Card"; import { Flex } from "@components/Flex"; import { FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons"; @@ -15,6 +14,7 @@ import { QuickAction, QuickActionCard } from "@components/settings/QuickAction"; import { openPluginModal } from "@components/settings/tabs/plugins/PluginModal"; import { UserThemeHeader } from "@main/themes"; import ClientThemePlugin from "@plugins/clientTheme"; +import { classNameFactory } from "@utils/css"; import { findLazy } from "@webpack"; import { Forms, useEffect, useRef, useState } from "@webpack/common"; import type { ComponentType, Ref, SyntheticEvent } from "react"; diff --git a/src/main/ipcMain.ts b/src/main/ipcMain.ts index 6990cea9..272da090 100644 --- a/src/main/ipcMain.ts +++ b/src/main/ipcMain.ts @@ -22,17 +22,19 @@ import "./settings"; import { debounce } from "@shared/debounce"; import { IpcEvents } from "@shared/IpcEvents"; -import { BrowserWindow, ipcMain, shell, systemPreferences } from "electron"; +import { BrowserWindow, ipcMain, nativeTheme, shell, systemPreferences } from "electron"; import monacoHtml from "file://monacoWin.html?minify&base64"; -import { FSWatcher, mkdirSync, watch, writeFileSync } from "fs"; +import { FSWatcher, mkdirSync, readFileSync, watch, writeFileSync } from "fs"; import { open, readdir, readFile } from "fs/promises"; import { join, normalize } from "path"; import { registerCspIpcHandlers } from "./csp/manager"; import { getThemeInfo, stripBOM, UserThemeHeader } from "./themes"; -import { ALLOWED_PROTOCOLS, QUICKCSS_PATH, SETTINGS_DIR, THEMES_DIR } from "./utils/constants"; +import { ALLOWED_PROTOCOLS, QUICK_CSS_PATH, SETTINGS_DIR, THEMES_DIR } from "./utils/constants"; import { makeLinksOpenExternally } from "./utils/externalLinks"; +const RENDERER_CSS_PATH = join(__dirname, IS_VESKTOP ? "vencordDesktopRenderer.css" : "renderer.css"); + mkdirSync(THEMES_DIR, { recursive: true }); registerCspIpcHandlers(); @@ -45,7 +47,7 @@ export function ensureSafePath(basePath: string, path: string) { } function readCss() { - return readFile(QUICKCSS_PATH, "utf-8").catch(() => ""); + return readFile(QUICK_CSS_PATH, "utf-8").catch(() => ""); } async function listThemes(): Promise { @@ -72,7 +74,7 @@ function getThemeData(fileName: string) { return readFile(safePath, "utf-8"); } -ipcMain.handle(IpcEvents.OPEN_QUICKCSS, () => shell.openPath(QUICKCSS_PATH)); +ipcMain.handle(IpcEvents.OPEN_QUICKCSS, () => shell.openPath(QUICK_CSS_PATH)); ipcMain.handle(IpcEvents.OPEN_EXTERNAL, (_, url) => { try { @@ -89,38 +91,57 @@ ipcMain.handle(IpcEvents.OPEN_EXTERNAL, (_, url) => { ipcMain.handle(IpcEvents.GET_QUICK_CSS, () => readCss()); ipcMain.handle(IpcEvents.SET_QUICK_CSS, (_, css) => - writeFileSync(QUICKCSS_PATH, css) + writeFileSync(QUICK_CSS_PATH, css) ); ipcMain.handle(IpcEvents.GET_THEMES_LIST, () => listThemes()); ipcMain.handle(IpcEvents.GET_THEME_DATA, (_, fileName) => getThemeData(fileName)); -ipcMain.handle(IpcEvents.GET_THEME_SYSTEM_VALUES, () => ({ - // win & mac only - "os-accent-color": `#${systemPreferences.getAccentColor?.() || ""}` -})); +ipcMain.handle(IpcEvents.GET_THEME_SYSTEM_VALUES, () => { + let accentColor = systemPreferences.getAccentColor?.() ?? ""; + + if (accentColor.length && accentColor[0] !== "#") { + accentColor = `#${accentColor}`; + } + + return { + "os-accent-color": accentColor + }; +}); ipcMain.handle(IpcEvents.OPEN_THEMES_FOLDER, () => shell.openPath(THEMES_DIR)); ipcMain.handle(IpcEvents.OPEN_SETTINGS_FOLDER, () => shell.openPath(SETTINGS_DIR)); -export function initIpc(mainWindow: BrowserWindow) { +ipcMain.handle(IpcEvents.INIT_FILE_WATCHERS, ({ sender }) => { let quickCssWatcher: FSWatcher | undefined; + let rendererCssWatcher: FSWatcher | undefined; - open(QUICKCSS_PATH, "a+").then(fd => { + open(QUICK_CSS_PATH, "a+").then(fd => { fd.close(); - quickCssWatcher = watch(QUICKCSS_PATH, { persistent: false }, debounce(async () => { - mainWindow.webContents.postMessage(IpcEvents.QUICK_CSS_UPDATE, await readCss()); + quickCssWatcher = watch(QUICK_CSS_PATH, { persistent: false }, debounce(async () => { + sender.postMessage(IpcEvents.QUICK_CSS_UPDATE, await readCss()); }, 50)); }).catch(() => { }); const themesWatcher = watch(THEMES_DIR, { persistent: false }, debounce(() => { - mainWindow.webContents.postMessage(IpcEvents.THEME_UPDATE, void 0); + sender.postMessage(IpcEvents.THEME_UPDATE, void 0); })); - mainWindow.once("closed", () => { + if (IS_DEV) { + rendererCssWatcher = watch(RENDERER_CSS_PATH, { persistent: false }, async () => { + sender.postMessage(IpcEvents.RENDERER_CSS_UPDATE, await readFile(RENDERER_CSS_PATH, "utf-8")); + }); + } + + sender.once("destroyed", () => { quickCssWatcher?.close(); themesWatcher.close(); + rendererCssWatcher?.close(); }); -} +}); + +ipcMain.on(IpcEvents.GET_MONACO_THEME, e => { + e.returnValue = nativeTheme.shouldUseDarkColors ? "vs-dark" : "vs-light"; +}); ipcMain.handle(IpcEvents.OPEN_MONACO_EDITOR, async () => { const title = "Vencord QuickCSS Editor"; @@ -146,3 +167,11 @@ ipcMain.handle(IpcEvents.OPEN_MONACO_EDITOR, async () => { await win.loadURL(`data:text/html;base64,${monacoHtml}`); }); + +ipcMain.handle(IpcEvents.GET_RENDERER_CSS, () => readFile(RENDERER_CSS_PATH, "utf-8")); + +if (IS_DISCORD_DESKTOP) { + ipcMain.on(IpcEvents.PRELOAD_GET_RENDERER_JS, e => { + e.returnValue = readFileSync(join(__dirname, "renderer.js"), "utf-8"); + }); +} diff --git a/src/main/patcher.ts b/src/main/patcher.ts index 8868caa7..09a646a6 100644 --- a/src/main/patcher.ts +++ b/src/main/patcher.ts @@ -20,7 +20,6 @@ import { onceDefined } from "@shared/onceDefined"; import electron, { app, BrowserWindowConstructorOptions, Menu } from "electron"; import { dirname, join } from "path"; -import { initIpc } from "./ipcMain"; import { RendererSettings } from "./settings"; import { IS_VANILLA } from "./utils/constants"; @@ -71,7 +70,7 @@ if (!IS_VANILLA) { constructor(options: BrowserWindowConstructorOptions) { if (options?.webPreferences?.preload && options.title) { const original = options.webPreferences.preload; - options.webPreferences.preload = join(__dirname, IS_DISCORD_DESKTOP ? "preload.js" : "vencordDesktopPreload.js"); + options.webPreferences.preload = join(__dirname, "preload.js"); options.webPreferences.sandbox = false; // work around discord unloading when in background options.webPreferences.backgroundThrottling = false; @@ -109,8 +108,6 @@ if (!IS_VANILLA) { // Disable the Electron call entirely so that Discord can't dynamically change the size this.setMinimumSize = (width: number, height: number) => { }; } - - initIpc(this); } else super(options); } } diff --git a/src/main/utils/constants.ts b/src/main/utils/constants.ts index 42875202..08432ddf 100644 --- a/src/main/utils/constants.ts +++ b/src/main/utils/constants.ts @@ -26,7 +26,7 @@ export const DATA_DIR = process.env.VENCORD_USER_DATA_DIR ?? ( ); export const SETTINGS_DIR = join(DATA_DIR, "settings"); export const THEMES_DIR = join(DATA_DIR, "themes"); -export const QUICKCSS_PATH = join(SETTINGS_DIR, "quickCss.css"); +export const QUICK_CSS_PATH = join(SETTINGS_DIR, "quickCss.css"); export const SETTINGS_FILE = join(SETTINGS_DIR, "settings.json"); export const NATIVE_SETTINGS_FILE = join(SETTINGS_DIR, "native-settings.json"); export const ALLOWED_PROTOCOLS = [ diff --git a/src/plugins/clientTheme/components/Settings.tsx b/src/plugins/clientTheme/components/Settings.tsx index 7035387e..e600681f 100644 --- a/src/plugins/clientTheme/components/Settings.tsx +++ b/src/plugins/clientTheme/components/Settings.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; import { ErrorCard } from "@components/ErrorCard"; import { relativeLuminance } from "@plugins/clientTheme/utils/colorUtils"; import { createOrUpdateThemeColorVars } from "@plugins/clientTheme/utils/styleUtils"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { findByCodeLazy, findStoreLazy } from "@webpack"; import { Button, ColorPicker, Forms, ThemeStore, useStateFromStores } from "@webpack/common"; diff --git a/src/plugins/customRPC/RpcSettings.tsx b/src/plugins/customRPC/RpcSettings.tsx index 019cc431..bdca847d 100644 --- a/src/plugins/customRPC/RpcSettings.tsx +++ b/src/plugins/customRPC/RpcSettings.tsx @@ -7,11 +7,11 @@ import "./settings.css"; import { isPluginEnabled } from "@api/PluginManager"; -import { classNameFactory } from "@api/Styles"; import { Divider } from "@components/Divider"; import { Heading } from "@components/Heading"; import { resolveError } from "@components/settings/tabs/plugins/components/Common"; import { debounce } from "@shared/debounce"; +import { classNameFactory } from "@utils/css"; import { ActivityType } from "@vencord/discord-types/enums"; import { Select, Text, TextInput, useState } from "@webpack/common"; diff --git a/src/plugins/decor/ui/index.ts b/src/plugins/decor/ui/index.ts index 62040508..44e58ecc 100644 --- a/src/plugins/decor/ui/index.ts +++ b/src/plugins/decor/ui/index.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { extractAndLoadChunksLazy, findByPropsLazy } from "@webpack"; export const cl = classNameFactory("vc-decor-"); diff --git a/src/plugins/imageZoom/components/Magnifier.tsx b/src/plugins/imageZoom/components/Magnifier.tsx index 7ca83a6d..f69ab4b3 100644 --- a/src/plugins/imageZoom/components/Magnifier.tsx +++ b/src/plugins/imageZoom/components/Magnifier.tsx @@ -16,11 +16,11 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { settings } from "@plugins/imageZoom"; import { ELEMENT_ID } from "@plugins/imageZoom/constants"; import { waitFor } from "@plugins/imageZoom/utils/waitFor"; +import { classNameFactory } from "@utils/css"; import { FluxDispatcher, useLayoutEffect, useMemo, useRef, useState } from "@webpack/common"; interface Vec2 { diff --git a/src/plugins/memberCount/index.tsx b/src/plugins/memberCount/index.tsx index e5dac9d0..ae9c1793 100644 --- a/src/plugins/memberCount/index.tsx +++ b/src/plugins/memberCount/index.tsx @@ -19,9 +19,9 @@ import "./style.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { FluxStore } from "@vencord/discord-types"; import { findStoreLazy } from "@webpack"; diff --git a/src/plugins/messageLogger/HistoryModal.tsx b/src/plugins/messageLogger/HistoryModal.tsx index 5c49d72a..4da925a5 100644 --- a/src/plugins/messageLogger/HistoryModal.tsx +++ b/src/plugins/messageLogger/HistoryModal.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; diff --git a/src/plugins/permissionsViewer/utils.ts b/src/plugins/permissionsViewer/utils.ts index c3f61672..c9280b1b 100644 --- a/src/plugins/permissionsViewer/utils.ts +++ b/src/plugins/permissionsViewer/utils.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Guild, GuildMember, Role } from "@vencord/discord-types"; import { findByPropsLazy } from "@webpack"; import { GuildRoleStore } from "@webpack/common"; diff --git a/src/plugins/pinDms/components/CreateCategoryModal.tsx b/src/plugins/pinDms/components/CreateCategoryModal.tsx index e18ad2ab..4f2df28b 100644 --- a/src/plugins/pinDms/components/CreateCategoryModal.tsx +++ b/src/plugins/pinDms/components/CreateCategoryModal.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; import { Divider } from "@components/Divider"; import { DEFAULT_COLOR, SWATCHES } from "@plugins/pinDms/constants"; import { categoryLen, createCategory, getCategory } from "@plugins/pinDms/data"; +import { classNameFactory } from "@utils/css"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal"; import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack"; import { Button, ColorPicker, Forms, Text, TextInput, Toasts, useMemo, useState } from "@webpack/common"; diff --git a/src/plugins/reviewDB/utils.tsx b/src/plugins/reviewDB/utils.tsx index 916adc45..ec7609c5 100644 --- a/src/plugins/reviewDB/utils.tsx +++ b/src/plugins/reviewDB/utils.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Toasts, UserStore } from "@webpack/common"; import { Auth } from "./auth"; diff --git a/src/plugins/sendTimestamps/index.tsx b/src/plugins/sendTimestamps/index.tsx index 4e7b8bf0..9f49d5a3 100644 --- a/src/plugins/sendTimestamps/index.tsx +++ b/src/plugins/sendTimestamps/index.tsx @@ -20,8 +20,8 @@ import "./styles.css"; import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import { Devs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { getTheme, insertTextIntoChatInputBox, Theme } from "@utils/discord"; import { Margins } from "@utils/margins"; import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; diff --git a/src/plugins/serverInfo/GuildInfoModal.tsx b/src/plugins/serverInfo/GuildInfoModal.tsx index ac57e976..0ed5548c 100644 --- a/src/plugins/serverInfo/GuildInfoModal.tsx +++ b/src/plugins/serverInfo/GuildInfoModal.tsx @@ -6,7 +6,7 @@ import "./styles.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { getGuildAcronym, openImageModal, openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; import { ModalRoot, ModalSize, openModal } from "@utils/modal"; diff --git a/src/plugins/shikiCodeblocks.desktop/utils/misc.ts b/src/plugins/shikiCodeblocks.desktop/utils/misc.ts index ee782ec0..1b2fd27c 100644 --- a/src/plugins/shikiCodeblocks.desktop/utils/misc.ts +++ b/src/plugins/shikiCodeblocks.desktop/utils/misc.ts @@ -16,10 +16,10 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@api/Styles"; import { resolveLang } from "@plugins/shikiCodeblocks.desktop/api/languages"; import { HighlighterProps } from "@plugins/shikiCodeblocks.desktop/components/Highlighter"; import { HljsSetting } from "@plugins/shikiCodeblocks.desktop/types"; +import { classNameFactory } from "@utils/css"; import { hljs } from "@webpack/common"; export const cl = classNameFactory("vc-shiki-"); diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index dd17ebac..6d4a4238 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -19,9 +19,9 @@ import "./style.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; import type { Channel, Role } from "@vencord/discord-types"; diff --git a/src/plugins/sortFriendRequests/index.tsx b/src/plugins/sortFriendRequests/index.tsx index 42af9a3a..03fe4231 100644 --- a/src/plugins/sortFriendRequests/index.tsx +++ b/src/plugins/sortFriendRequests/index.tsx @@ -19,9 +19,9 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { User } from "@vencord/discord-types"; import { DateUtils, RelationshipStore, Text, TooltipContainer } from "@webpack/common"; diff --git a/src/plugins/spotifyControls/PlayerComponent.tsx b/src/plugins/spotifyControls/PlayerComponent.tsx index 44968675..40d035c4 100644 --- a/src/plugins/spotifyControls/PlayerComponent.tsx +++ b/src/plugins/spotifyControls/PlayerComponent.tsx @@ -19,12 +19,12 @@ import "./spotifyStyles.css"; import { Settings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; import { Flex } from "@components/Flex"; import { CopyIcon, ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Icons"; import { Paragraph } from "@components/Paragraph"; import { Span } from "@components/Span"; import { debounce } from "@shared/debounce"; +import { classNameFactory } from "@utils/css"; import { copyWithToast, openImageModal } from "@utils/discord"; import { classes } from "@utils/misc"; import { ContextMenuApi, FluxDispatcher, Menu, React, useEffect, useState, useStateFromStores } from "@webpack/common"; diff --git a/src/plugins/translate/utils.ts b/src/plugins/translate/utils.ts index 9bb175ad..4ee16c93 100644 --- a/src/plugins/translate/utils.ts +++ b/src/plugins/translate/utils.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { onlyOnce } from "@utils/onlyOnce"; import { PluginNative } from "@utils/types"; import { showToast, Toasts } from "@webpack/common"; diff --git a/src/plugins/userVoiceShow/components.tsx b/src/plugins/userVoiceShow/components.tsx index 75c56195..e126fcf3 100644 --- a/src/plugins/userVoiceShow/components.tsx +++ b/src/plugins/userVoiceShow/components.tsx @@ -5,9 +5,9 @@ */ import { isPluginEnabled } from "@api/PluginManager"; -import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import ShowHiddenChannelsPlugin from "@plugins/showHiddenChannels"; +import { classNameFactory } from "@utils/css"; import { classes } from "@utils/misc"; import { Channel } from "@vencord/discord-types"; import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; diff --git a/src/plugins/voiceMessages/utils.ts b/src/plugins/voiceMessages/utils.ts index ef571bbf..5a6bfa85 100644 --- a/src/plugins/voiceMessages/utils.ts +++ b/src/plugins/voiceMessages/utils.ts @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { findStoreLazy } from "@webpack"; export const MediaEngineStore = findStoreLazy("MediaEngineStore"); diff --git a/src/preload.ts b/src/preload.ts index e79eb02c..5f7adb99 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -17,48 +17,25 @@ */ import { debounce } from "@shared/debounce"; -import { contextBridge, webFrame } from "electron"; -import { readFileSync, watch } from "fs"; -import { join } from "path"; +import { IpcEvents } from "@shared/IpcEvents"; +import { contextBridge, webFrame } from "electron/renderer"; -import VencordNative from "./VencordNative"; +import VencordNative, { invoke, sendSync } from "./VencordNative"; contextBridge.exposeInMainWorld("VencordNative", VencordNative); // Discord if (location.protocol !== "data:") { - // #region cssInsert - const rendererCss = join(__dirname, IS_VESKTOP ? "vencordDesktopRenderer.css" : "renderer.css"); - - const style = document.createElement("style"); - style.id = "vencord-css-core"; - style.textContent = readFileSync(rendererCss, "utf-8"); - - if (document.readyState === "complete") { - document.documentElement.appendChild(style); - } else { - document.addEventListener("DOMContentLoaded", () => document.documentElement.appendChild(style), { - once: true - }); - } - - if (IS_DEV) { - // persistent means keep process running if watcher is the only thing still running - // which we obviously don't want - watch(rendererCss, { persistent: false }, () => { - document.getElementById("vencord-css-core")!.textContent = readFileSync(rendererCss, "utf-8"); - }); - } - // #endregion + invoke(IpcEvents.INIT_FILE_WATCHERS); if (IS_DISCORD_DESKTOP) { - webFrame.executeJavaScript(readFileSync(join(__dirname, "renderer.js"), "utf-8")); + webFrame.executeJavaScript(sendSync(IpcEvents.PRELOAD_GET_RENDERER_JS)); + // Not supported in sandboxed preload scripts but Discord doesn't support it either so who cares require(process.env.DISCORD_PRELOAD!); } } // Monaco popout else { contextBridge.exposeInMainWorld("setCss", debounce(VencordNative.quickCss.set)); contextBridge.exposeInMainWorld("getCurrentCss", VencordNative.quickCss.get); - // shrug - contextBridge.exposeInMainWorld("getTheme", () => "vs-dark"); + contextBridge.exposeInMainWorld("getTheme", VencordNative.quickCss.getEditorTheme); } diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index e7eb7594..0dca60b5 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -17,6 +17,8 @@ */ export const enum IpcEvents { + INIT_FILE_WATCHERS = "VencordInitFileWatchers", + OPEN_QUICKCSS = "VencordOpenQuickCss", GET_QUICK_CSS = "VencordGetQuickCss", SET_QUICK_CSS = "VencordSetQuickCss", @@ -28,8 +30,6 @@ export const enum IpcEvents { GET_THEMES_LIST = "VencordGetThemesList", GET_THEME_DATA = "VencordGetThemeData", GET_THEME_SYSTEM_VALUES = "VencordGetThemeSystemValues", - UPLOAD_THEME = "VencordUploadTheme", - DELETE_THEME = "VencordDeleteTheme", THEME_UPDATE = "VencordThemeUpdate", OPEN_EXTERNAL = "VencordOpenExternal", @@ -42,13 +42,15 @@ export const enum IpcEvents { BUILD = "VencordBuild", OPEN_MONACO_EDITOR = "VencordOpenMonacoEditor", + GET_MONACO_THEME = "VencordGetMonacoTheme", GET_PLUGIN_IPC_METHOD_MAP = "VencordGetPluginIpcMethodMap", - OPEN_IN_APP__RESOLVE_REDIRECT = "VencordOIAResolveRedirect", - VOICE_MESSAGES_READ_RECORDING = "VencordVMReadRecording", - CSP_IS_DOMAIN_ALLOWED = "VencordCspIsDomainAllowed", CSP_REMOVE_OVERRIDE = "VencordCspRemoveOverride", CSP_REQUEST_ADD_OVERRIDE = "VencordCspRequestAddOverride", + + GET_RENDERER_CSS = "VencordGetRendererCss", + RENDERER_CSS_UPDATE = "VencordRendererCssUpdate", + PRELOAD_GET_RENDERER_JS = "VencordPreloadGetRendererJs", } diff --git a/src/utils/css.ts b/src/utils/css.ts index 3c46b7a4..80fd3ded 100644 --- a/src/utils/css.ts +++ b/src/utils/css.ts @@ -4,9 +4,32 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -export function createAndAppendStyle(id: string, target = document.documentElement) { +export function createAndAppendStyle(id: string, target: HTMLElement) { const style = document.createElement("style"); style.id = id; target.append(style); return style; } + +export const classNameToSelector = (name: string, prefix = "") => name.split(" ").map(n => `.${prefix}${n}`).join(""); + +export type ClassNameFactoryArg = string | string[] | Record | false | null | undefined | 0 | ""; + +/** + * @param prefix The prefix to add to each class, defaults to `""` + * @returns A classname generator function + * @example + * const cl = classNameFactory("plugin-"); + * + * cl("base", ["item", "editable"], { selected: null, disabled: true }) + * // => "plugin-base plugin-item plugin-editable plugin-disabled" + */ +export const classNameFactory = (prefix: string = "") => (...args: ClassNameFactoryArg[]) => { + const classNames = new Set(); + for (const arg of args) { + if (arg && typeof arg === "string") classNames.add(arg); + else if (Array.isArray(arg)) arg.forEach(name => classNames.add(name)); + else if (arg && typeof arg === "object") Object.entries(arg).forEach(([name, value]) => value && classNames.add(name)); + } + return Array.from(classNames, name => prefix + name).join(" "); +}; diff --git a/src/utils/web-metadata.ts b/src/utils/web-metadata.ts index cca07ad4..8e39766f 100644 --- a/src/utils/web-metadata.ts +++ b/src/utils/web-metadata.ts @@ -4,14 +4,19 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -export let EXTENSION_BASE_URL: string; export let EXTENSION_VERSION: string; +export let EXTENSION_BASE_URL: string; +export let RENDERER_CSS_URL: string; + +let resolveMetaReady: Function; +export const metaReady = new Promise(res => resolveMetaReady = res); if (IS_EXTENSION) { const listener = (e: MessageEvent) => { if (e.data?.type === "vencord:meta") { - ({ EXTENSION_BASE_URL, EXTENSION_VERSION } = e.data.meta); + ({ EXTENSION_BASE_URL, EXTENSION_VERSION, RENDERER_CSS_URL } = e.data.meta); window.removeEventListener("message", listener); + resolveMetaReady(); } };