Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
c9c90c9732
62 changed files with 334 additions and 228 deletions
|
|
@ -27,7 +27,7 @@ import type { Settings } from "@api/Settings";
|
|||
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: {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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/Equicord.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/Equicord.css"),
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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^"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "equicord",
|
||||
"private": "true",
|
||||
"version": "1.13.9.1",
|
||||
"version": "1.13.11",
|
||||
"description": "The other cutest Discord client mod",
|
||||
"homepage": "https://github.com/Equicord/Equicord#readme",
|
||||
"bugs": {
|
||||
|
|
|
|||
1
packages/discord-types/src/utils.d.ts
vendored
1
packages/discord-types/src/utils.d.ts
vendored
|
|
@ -276,6 +276,7 @@ export interface UsernameUtils {
|
|||
humanizeStatus: any;
|
||||
}
|
||||
|
||||
// TODO: fix type
|
||||
export class DisplayProfile {
|
||||
userId: string;
|
||||
banner?: string;
|
||||
|
|
|
|||
1
pnpm-lock.yaml
generated
1
pnpm-lock.yaml
generated
|
|
@ -2143,7 +2143,6 @@ packages:
|
|||
engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
|
||||
deprecated: |-
|
||||
You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.
|
||||
|
||||
(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
|
||||
|
||||
qified@0.5.2:
|
||||
|
|
|
|||
|
|
@ -185,14 +185,7 @@ async function buildExtension(target, files) {
|
|||
}
|
||||
|
||||
const appendCssRuntime = readFile("dist/Equicord.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/Equicord.user.js", cssRuntime);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ import { constants as FsConstants, readFileSync } from "fs";
|
|||
import { access, readdir, readFile } from "fs/promises";
|
||||
import { minify as minifyHtml } from "html-minifier-terser";
|
||||
import { dirname, join, relative, resolve } from "path";
|
||||
import { optimize as optimizeSvg } from "svgo";
|
||||
import { fileURLToPath } from "url";
|
||||
import { promisify } from "util";
|
||||
|
||||
import { getPluginTarget } from "../utils.mjs";
|
||||
import { builtinModules } from "module";
|
||||
|
||||
const PackageJSON = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "../../package.json"), "utf-8"));
|
||||
|
||||
|
|
@ -261,7 +261,7 @@ export const fileUrlPlugin = {
|
|||
build.onLoad({ filter, namespace: "file-uri" }, async ({ pluginData: { path, uri } }) => {
|
||||
const { searchParams } = new URL(uri);
|
||||
const base64 = searchParams.has("base64");
|
||||
const minify = IS_STANDALONE === true && searchParams.has("minify");
|
||||
const minify = searchParams.has("minify");
|
||||
const noTrim = searchParams.get("trim") === "false";
|
||||
|
||||
const encoding = base64 ? "base64" : "utf-8";
|
||||
|
|
@ -283,6 +283,12 @@ export const fileUrlPlugin = {
|
|||
removeStyleLinkTypeAttributes: true,
|
||||
useShortDoctype: true
|
||||
});
|
||||
} else if (path.endsWith(".svg")) {
|
||||
content = optimizeSvg(await readFile(path, "utf-8"), {
|
||||
datauri: base64 ? "base64" : void 0,
|
||||
multipass: true,
|
||||
floatPrecision: 2,
|
||||
}).data;
|
||||
} else if (/[mc]?[jt]sx?$/.test(path)) {
|
||||
const res = await esbuild.build({
|
||||
entryPoints: [path],
|
||||
|
|
@ -294,7 +300,7 @@ export const fileUrlPlugin = {
|
|||
throw new Error(`Don't know how to minify file type: ${path}`);
|
||||
}
|
||||
|
||||
if (base64)
|
||||
if (base64 && !content.startsWith("data:"))
|
||||
content = Buffer.from(content).toString("base64");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
@ -39,7 +39,7 @@ import { get as dsGet } from "./api/DataStore";
|
|||
import { NotificationData, showNotification } from "./api/Notifications";
|
||||
import { initPluginManager, PMLogger, startAllPlugins } from "./api/PluginManager";
|
||||
import { PlainSettings, Settings, SettingsStore } from "./api/Settings";
|
||||
import { getCloudSettings, putCloudSettings } from "./api/SettingsSync/cloudSync";
|
||||
import { getCloudSettings, putCloudSettings, shouldCloudSync } from "./api/SettingsSync/cloudSync";
|
||||
import { localStorage } from "./utils/localStorage";
|
||||
import { relaunch } from "./utils/native";
|
||||
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
|
||||
|
|
@ -53,6 +53,10 @@ if (IS_REPORTER) {
|
|||
|
||||
async function syncSettings() {
|
||||
// Check if cloud auth exists for current user before attempting sync
|
||||
if (localStorage.Vencord_cloudSyncDirection === undefined) {
|
||||
// by default, sync bi-directionally
|
||||
localStorage.Vencord_cloudSyncDirection = "both";
|
||||
}
|
||||
const hasCloudAuth = await dsGet("Vencord_cloudSecret");
|
||||
if (!hasCloudAuth) {
|
||||
if (Settings.cloud.authenticated) {
|
||||
|
|
@ -87,13 +91,12 @@ async function syncSettings() {
|
|||
|
||||
if (
|
||||
Settings.cloud.settingsSync && // if it's enabled
|
||||
Settings.cloud.authenticated // if cloud integrations are enabled
|
||||
Settings.cloud.authenticated && // if cloud integrations are enabled
|
||||
localStorage.Vencord_cloudSyncDirection !== "manual" // if we're not in manual mode
|
||||
) {
|
||||
if (localStorage.Vencord_settingsDirty) {
|
||||
if (localStorage.Vencord_settingsDirty && shouldCloudSync("push")) {
|
||||
await putCloudSettings();
|
||||
delete localStorage.Vencord_settingsDirty;
|
||||
} else if (await getCloudSettings(false)) {
|
||||
// if we synchronized something (false means no sync)
|
||||
} else if (shouldCloudSync("pull") && await getCloudSettings(false)) { // if we synchronized something (false means no sync)
|
||||
// we show a notification here instead of allowing getCloudSettings() to show one to declutter the amount of
|
||||
// potential notifications that might occur. getCloudSettings() will always send a notification regardless if
|
||||
// there was an error to notify the user, but besides that we only want to show one notification instead of all
|
||||
|
|
@ -108,9 +111,8 @@ async function syncSettings() {
|
|||
}
|
||||
|
||||
const saveSettingsOnFrequentAction = debounce(async () => {
|
||||
if (Settings.cloud.settingsSync && Settings.cloud.authenticated) {
|
||||
if (Settings.cloud.settingsSync && Settings.cloud.authenticated && shouldCloudSync("push")) {
|
||||
await putCloudSettings();
|
||||
delete localStorage.Vencord_settingsDirty;
|
||||
}
|
||||
}, 60_000);
|
||||
|
||||
|
|
@ -192,16 +194,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 });
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
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 { 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<T = any>(event: IpcEvents, ...args: any[]) {
|
||||
export function invoke<T = any>(event: IpcEvents, ...args: any[]) {
|
||||
return ipcRenderer.invoke(event, ...args) as Promise<T>;
|
||||
}
|
||||
|
||||
|
|
@ -31,8 +31,12 @@ for (const [plugin, methods] of Object.entries(pluginIpcMap)) {
|
|||
|
||||
export default {
|
||||
themes: {
|
||||
uploadTheme: (fileName: string, fileData: string) => invoke<void>(IpcEvents.UPLOAD_THEME, fileName, fileData),
|
||||
deleteTheme: (fileName: string) => invoke<void>(IpcEvents.DELETE_THEME, fileName),
|
||||
uploadTheme: async (fileName: string, fileData: string): Promise<void> => {
|
||||
throw new Error("uploadTheme is WEB only");
|
||||
},
|
||||
deleteTheme: async (fileName: string): Promise<void> => {
|
||||
throw new Error("deleteTheme is WEB only");
|
||||
},
|
||||
getThemesDir: () => invoke<string>(IpcEvents.GET_THEMES_DIR),
|
||||
getThemesList: () => invoke<Array<{ fileName: string; content: string; }>>(IpcEvents.GET_THEMES_LIST),
|
||||
getThemeData: (fileName: string) => invoke<string | undefined>(IpcEvents.GET_THEME_DATA, fileName),
|
||||
|
|
@ -70,11 +74,18 @@ export default {
|
|||
|
||||
openFile: () => invoke<void>(IpcEvents.OPEN_QUICKCSS),
|
||||
openEditor: () => invoke<void>(IpcEvents.OPEN_MONACO_EDITOR),
|
||||
getEditorTheme: () => sendSync<string>(IpcEvents.GET_MONACO_THEME),
|
||||
},
|
||||
|
||||
native: {
|
||||
getVersions: () => process.versions as Partial<NodeJS.ProcessVersions>,
|
||||
openExternal: (url: string) => invoke<void>(IpcEvents.OPEN_EXTERNAL, url)
|
||||
openExternal: (url: string) => invoke<void>(IpcEvents.OPEN_EXTERNAL, url),
|
||||
getRendererCss: () => invoke<string>(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: {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,8 @@ export interface ProfileBadge {
|
|||
key?: string;
|
||||
|
||||
/**
|
||||
* Allows dynamically returning multiple badges
|
||||
* Allows dynamically returning multiple badges.
|
||||
* May call hooks but then you must not use shouldShow
|
||||
*/
|
||||
getBadges?(userInfo: BadgeUserArgs): ProfileBadge[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@
|
|||
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { Settings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
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, ListScrollerThin, React, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { showNotification } from "@api/Notifications";
|
||||
import { PlainSettings, Settings } from "@api/Settings";
|
||||
import { localStorage } from "@utils/localStorage";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { openUserSettingsPanel } from "@webpack/common";
|
||||
|
|
@ -16,6 +17,12 @@ import { exportSettings, importSettings } from "./offline";
|
|||
|
||||
const logger = new Logger("SettingsSync:Cloud", "#39b7e0");
|
||||
|
||||
export function shouldCloudSync(direction: "push" | "pull") {
|
||||
const localDirection = localStorage.Vencord_cloudSyncDirection;
|
||||
|
||||
return localDirection === direction || localDirection === "both";
|
||||
}
|
||||
|
||||
export async function putCloudSettings(manual?: boolean) {
|
||||
const settings = await exportSettings({ syncDataStore: false, minify: true });
|
||||
|
||||
|
|
@ -52,6 +59,8 @@ export async function putCloudSettings(manual?: boolean) {
|
|||
noPersist: true,
|
||||
});
|
||||
}
|
||||
|
||||
delete localStorage.Vencord_settingsDirty;
|
||||
} catch (e: any) {
|
||||
logger.error("Failed to sync up", e);
|
||||
showNotification({
|
||||
|
|
@ -151,6 +160,8 @@ export async function getCloudSettings(shouldNotify = true, force = false) {
|
|||
noPersist: true
|
||||
});
|
||||
|
||||
delete localStorage.Vencord_settingsDirty;
|
||||
|
||||
return true;
|
||||
} catch (e: any) {
|
||||
logger.error("Failed to sync down", e);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,14 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<string, unknown> | 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<string>();
|
||||
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(" ");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 { enabledThemeLinks, enabledThemes } = Settings;
|
||||
|
||||
|
|
@ -89,7 +81,6 @@ async function initThemes() {
|
|||
document.addEventListener("DOMContentLoaded", () => {
|
||||
if (IS_USERSCRIPT) return;
|
||||
|
||||
initSystemValues();
|
||||
initThemes();
|
||||
|
||||
toggle(Settings.useQuickCss);
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -124,11 +124,11 @@
|
|||
}
|
||||
|
||||
.vc-btn-positive {
|
||||
background-color: var(--redesign-button-positive-background);
|
||||
background-color: var(--redesign-button-positive-background, var(--green-430));
|
||||
color: var(--white);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--redesign-button-positive-pressed-background);
|
||||
background-color: var(--redesign-button-positive-pressed-background, var(--green-460));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -686,3 +686,31 @@ export function MagnifyingGlassIcon(props: IconProps) {
|
|||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function CloudDownloadIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill={props.fill || "currentColor"}
|
||||
d="M6.5 20Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.83 7.13 7.39 5.75 8.95 4.38 11 4.08V12.15L9.4 10.6L8 12L12 16L16 12L14.6 10.6L13 12.15V4.08Q15.58 4.43 17.29 6.39 19 8.35 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20Z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
||||
export function CloudUploadIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill={props.fill || "currentColor"}
|
||||
d="M11 20H6.5Q4.22 20 2.61 18.43 1 16.85 1 14.58 1 12.63 2.17 11.1 3.35 9.57 5.25 9.15 5.88 6.85 7.75 5.43 9.63 4 12 4 14.93 4 16.96 6.04 19 8.07 19 11 20.73 11.2 21.86 12.5 23 13.78 23 15.5 23 17.38 21.69 18.69 20.38 20 18.5 20H13V12.85L14.6 14.4L16 13L12 9L8 13L9.4 14.4L11 12.85Z"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
|
||||
const marginCls = classNameFactory("vc-margin-");
|
||||
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@
|
|||
|
||||
import "./AddonCard.css";
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { AddonBadge } from "@components/settings/PluginBadge";
|
||||
import { Switch } from "@components/Switch";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { Tooltip, useRef } from "@webpack/common";
|
||||
import type { MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
|
|
|
|||
|
|
@ -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-");
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@
|
|||
|
||||
import "./SpecialCard.css";
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Card } from "@components/Card";
|
||||
import { Divider } from "@components/Divider";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { Clickable } from "@webpack/common";
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@
|
|||
import "./ContributorModal.css";
|
||||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { HeadingPrimary } from "@components/Heading";
|
||||
import { Link } from "@components/Link";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { EquicordDevsById, VencordDevsById } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { fetchUserProfile } from "@utils/discord";
|
||||
import { pluralise } from "@utils/misc";
|
||||
import { ModalContent, ModalFooter, ModalRoot, openModal } from "@utils/modal";
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import "./PluginModal.css";
|
|||
|
||||
import { generateId } from "@api/Commands";
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { Button } from "@components/Button";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
|
|
@ -28,6 +27,7 @@ import { Flex } from "@components/Flex";
|
|||
import { Paragraph } from "@components/Paragraph";
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { classes } from "@utils/misc";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { DefinedSettings, PluginOptionBase } from "@utils/types";
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import "./styles.css";
|
|||
import * as DataStore from "@api/DataStore";
|
||||
import { isPluginEnabled, stopPlugin } from "@api/PluginManager";
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Button } from "@components/Button";
|
||||
import { Card } from "@components/Card";
|
||||
import { Divider } from "@components/Divider";
|
||||
|
|
@ -31,6 +30,7 @@ import { Paragraph } from "@components/Paragraph";
|
|||
import { SettingsTab } from "@components/settings";
|
||||
import { debounce } from "@shared/debounce";
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1em;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.vc-cloud-erase-data-danger-btn {
|
||||
|
|
@ -79,6 +80,16 @@
|
|||
background-color: var(--redesign-button-danger-background);
|
||||
}
|
||||
|
||||
.vc-cloud-icon-with-button {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
padding-inline: 0.5em 1em;
|
||||
}
|
||||
|
||||
.vc-cloud-button-icon {
|
||||
height: 1.25em;
|
||||
}
|
||||
|
||||
.vc-settings-modal {
|
||||
padding: 1.5em !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -22,16 +22,18 @@ 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, unlink } from "fs/promises";
|
||||
import { join, normalize } from "path";
|
||||
|
||||
import { registerCspIpcHandlers } from "./csp/manager";
|
||||
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();
|
||||
|
|
@ -43,12 +45,8 @@ export function ensureSafePath(basePath: string, path: string) {
|
|||
return normalizedPath.startsWith(normalizedBasePath) ? normalizedPath : null;
|
||||
}
|
||||
|
||||
async function readCss() {
|
||||
try {
|
||||
return await readFile(QUICKCSS_PATH, "utf-8");
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
function readCss() {
|
||||
return readFile(QUICK_CSS_PATH, "utf-8").catch(() => "");
|
||||
}
|
||||
|
||||
async function listThemes(): Promise<{ fileName: string; content: string; }[]> {
|
||||
|
|
@ -67,7 +65,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 {
|
||||
|
|
@ -83,7 +81,7 @@ 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_DIR, () => THEMES_DIR);
|
||||
|
|
@ -94,33 +92,52 @@ ipcMain.handle(IpcEvents.DELETE_THEME, (_, fileName) => {
|
|||
if (!safePath) return Promise.reject(`Unsafe path ${fileName}`);
|
||||
return unlink(safePath);
|
||||
});
|
||||
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 = "Equicord QuickCSS Editor";
|
||||
|
|
@ -146,3 +163,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");
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
@ -107,8 +106,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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export const DATA_DIR = process.env.EQUICORD_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 DEV_MIGRATED = join(SETTINGS_DIR, "migration");
|
||||
|
|
|
|||
|
|
@ -26,11 +26,8 @@ import { copyWithToast } from "@utils/discord";
|
|||
import { Logger } from "@utils/Logger";
|
||||
import { shouldShowContributorBadge, shouldShowEquicordContributorBadge } from "@utils/misc";
|
||||
import definePlugin from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { ContextMenuApi, Menu, Toasts, UserStore } from "@webpack/common";
|
||||
|
||||
import { ContextMenuApi, Forms, Menu, Toasts, UserStore } from "@webpack/common";
|
||||
import Plugins, { PluginMeta } from "~plugins";
|
||||
|
||||
import { EquicordDonorModal, VencordDonorModal } from "./modals";
|
||||
|
||||
const CONTRIBUTOR_BADGE = "https://cdn.discordapp.com/emojis/1092089799109775453.png?size=64";
|
||||
|
|
@ -131,16 +128,13 @@ export default definePlugin({
|
|||
authors: [Devs.Megu, Devs.Ven, Devs.TheSun],
|
||||
required: true,
|
||||
patches: [
|
||||
{
|
||||
find: ".MODAL]:26",
|
||||
replacement: {
|
||||
match: /(?=;return 0===(\i)\.length\?)(?<=(\i)\.useMemo.+?)/,
|
||||
replace: ";$1=$2.useMemo(()=>[...$self.getBadges(arguments[0].displayProfile),...$1],[$1])"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "#{intl::PROFILE_USER_BADGES}",
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=\{[^}]*?)badges:\i(?=[^}]*?}=(\i))/,
|
||||
replace: "_$&=$self.useBadges($1.displayProfile).concat($1.badges)"
|
||||
},
|
||||
{
|
||||
match: /alt:" ","aria-hidden":!0,src:.{0,50}(\i).iconSrc/,
|
||||
replace: "...$1.props,$&"
|
||||
|
|
@ -155,13 +149,6 @@ export default definePlugin({
|
|||
replace: "...$self.getBadgeMouseEventHandlers($1),$&"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: "profileCardUsernameRow,children:",
|
||||
replacement: {
|
||||
match: /badges:(\i)(?<=displayProfile:(\i).+?)/,
|
||||
replace: "badges:[...$self.getBadges($2),...$1]"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
|
@ -197,15 +184,14 @@ export default definePlugin({
|
|||
clearInterval(intervalId);
|
||||
},
|
||||
|
||||
getBadges(props: { userId: string; user?: User; guildId: string; }) {
|
||||
if (!props) return [];
|
||||
// doesn't use hooks itself, but some plugins might do so in their getBadges function
|
||||
useBadges(profile: { userId: string; guildId: string; }) {
|
||||
if (!profile) return [];
|
||||
|
||||
try {
|
||||
props.userId ??= props.user?.id!;
|
||||
|
||||
return _getBadges(props);
|
||||
return _getBadges(profile);
|
||||
} catch (e) {
|
||||
new Logger("BadgeAPI#hasBadges").error(e);
|
||||
new Logger("BadgeAPI#useBadges").error(e);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { HeadingPrimary, HeadingSecondary } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
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, ThemeStore, useStateFromStores } from "@webpack/common";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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-");
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import { Divider } from "@components/Divider";
|
||||
import { Heading } from "@components/Heading";
|
||||
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, TextInput, Toasts, useMemo, useState } from "@webpack/common";
|
||||
|
|
|
|||
3
src/plugins/platformIndicators/icons/desktopIcon.svg
Normal file
3
src/plugins/platformIndicators/icons/desktopIcon.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg height="20" width="20" viewBox="0 0 24 24" fill="#123456" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 2.5c-1.103 0-2 .897-2 2v11c0 1.104.897 2 2 2h7v2H7v2h10v-2h-4v-2h7c1.103 0 2-.896 2-2v-11c0-1.103-.897-2-2-2H4Zm16 2v9H4v-9h16Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 254 B |
3
src/plugins/platformIndicators/icons/embeddedIcon.svg
Normal file
3
src/plugins/platformIndicators/icons/embeddedIcon.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg height="20" width="20" viewBox="0 0 50 50" fill="#123456" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.8 2.7 9 3.1V47h3.3c1.7 0 6.2.3 10 .7l6.7.6V2l-4.2.2c-2.4.1-6.9.3-10 .5zm1.8 6.4c1 1.7-1.3 3.6-2.7 2.2C12.7 10.1 13.5 8 15 8c.5 0 1.2.5 1.6 1.1zM16 33c0 6-.4 10-1 10s-1-4-1-10 .4-10 1-10 1 4 1 10zm15-8v23.3l3.8-.7c2-.3 4.7-.6 6-.6H43V3h-2.2c-1.3 0-4-.3-6-.6L31 1.7V25z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 395 B |
3
src/plugins/platformIndicators/icons/mobileIcon.svg
Normal file
3
src/plugins/platformIndicators/icons/mobileIcon.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg height="17" width="17" viewBox="0 0 1000 1500" fill="#123456" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 187 0 L 813 0 C 916.277 0 1000 83.723 1000 187 L 1000 1313 C 1000 1416.277 916.277 1500 813 1500 L 187 1500 C 83.723 1500 0 1416.277 0 1313 L 0 187 C 0 83.723 83.723 0 187 0 Z M 125 1000 L 875 1000 L 875 250 L 125 250 Z M 500 1125 C 430.964 1125 375 1180.964 375 1250 C 375 1319.036 430.964 1375 500 1375 C 569.036 1375 625 1319.036 625 1250 C 625 1180.964 569.036 1125 500 1125 Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 510 B |
3
src/plugins/platformIndicators/icons/webIcon.svg
Normal file
3
src/plugins/platformIndicators/icons/webIcon.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg height="20" width="20" viewBox="0 0 24 24" fill="#123456" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93Zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 424 B |
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { Toasts, UserStore } from "@webpack/common";
|
||||
|
||||
import { Auth } from "./auth";
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import "./styles.css";
|
|||
|
||||
import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Heading, HeadingPrimary } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import "./styles.css";
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { getGuildAcronym, openImageModal, openUserProfile } from "@utils/discord";
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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-");
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@
|
|||
import "./style.css";
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs, EquicordDevs } 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";
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
import "./styles.css";
|
||||
|
||||
import { definePluginSettings, migratePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { isPluginEnabled } from "@api/PluginManager";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import { BaseText } from "@components/BaseText";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import ShowHiddenChannelsPlugin from "@plugins/showHiddenChannels";
|
||||
|
|
|
|||
|
|
@ -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, "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<string>(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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@
|
|||
*/
|
||||
|
||||
export const enum IpcEvents {
|
||||
INIT_FILE_WATCHERS = "VencordInitFileWatchers",
|
||||
QUICK_CSS_UPDATE = "VencordQuickCssUpdate",
|
||||
THEME_UPDATE = "VencordThemeUpdate",
|
||||
OPEN_QUICKCSS = "VencordOpenQuickCss",
|
||||
GET_QUICK_CSS = "VencordGetQuickCss",
|
||||
SET_QUICK_CSS = "VencordSetQuickCss",
|
||||
UPLOAD_THEME = "VencordUploadTheme",
|
||||
|
|
@ -30,23 +31,24 @@ export const enum IpcEvents {
|
|||
GET_SETTINGS_DIR = "VencordGetSettingsDir",
|
||||
GET_SETTINGS = "VencordGetSettings",
|
||||
SET_SETTINGS = "VencordSetSettings",
|
||||
THEME_UPDATE = "VencordThemeUpdate",
|
||||
OPEN_EXTERNAL = "VencordOpenExternal",
|
||||
OPEN_QUICKCSS = "VencordOpenQuickCss",
|
||||
GET_UPDATES = "VencordGetUpdates",
|
||||
GET_REPO = "VencordGetRepo",
|
||||
UPDATE = "VencordUpdate",
|
||||
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",
|
||||
|
||||
OPEN_THEMES_FOLDER = "VencordOpenThemesFolder",
|
||||
OPEN_SETTINGS_FOLDER = "VencordOpenSettingsFolder",
|
||||
GET_RENDERER_CSS = "VencordGetRendererCss",
|
||||
RENDERER_CSS_UPDATE = "VencordRendererCssUpdate",
|
||||
PRELOAD_GET_RENDERER_JS = "VencordPreloadGetRendererJs",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string, unknown> | 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<string>();
|
||||
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(" ");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<void>(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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue