From c3edc31db35bd79bc79d51619a24ceb2dea02c31 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 17 Dec 2025 09:17:39 -0500 Subject: [PATCH 001/108] SupportHelper: Fix Broken Hash --- src/plugins/_core/supportHelper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index 5a88cc883..bd932e0dc 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -135,7 +135,7 @@ async function generateDebugInfoMessage() { let clientString = `${clientInfo.name}`; clientString += `${clientInfo.version ? ` v${clientInfo.version}` : ""}`; clientString += `${clientInfo.info ? ` • ${clientInfo.info}` : ""}`; - clientString += `${clientInfo.shortHash ? ` • [${clientInfo.shortHash}](${clientInfo.hash})` : ""}`; + clientString += `${clientInfo.shortHash ? ` • [${clientInfo.shortHash}]()` : ""}`; const spoofInfo = IS_EQUIBOP ? tryOrElse(() => VesktopNative.app.getPlatformSpoofInfo?.(), null) : null; const platformDisplay = spoofInfo?.spoofed From 6e3af4921daf26833a92c21ed340bbdbcfc855d0 Mon Sep 17 00:00:00 2001 From: Looly <193342835+Loolybooly@users.noreply.github.com> Date: Wed, 17 Dec 2025 18:47:00 +0200 Subject: [PATCH 002/108] PolishWording: Fix 'i' capitalization (#573) * Fix 'i' capitalization * Remove redundant capitalization fix for 'i' Removed duplicate capitalization fix for stand-alone 'i'. * Fix spacing in polishWording index.ts --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com> --- src/equicordplugins/polishWording/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/equicordplugins/polishWording/index.ts b/src/equicordplugins/polishWording/index.ts index 6334e92ed..04c51d83c 100644 --- a/src/equicordplugins/polishWording/index.ts +++ b/src/equicordplugins/polishWording/index.ts @@ -275,8 +275,8 @@ function capitalize(textInput: string): string { } } - // We'll fix capitalization of I's - result = result.replace(/\bi[\b']/g, "I"); + // Stand-alone i's turn into I, unless there's the word 'is' after, cus then it's a vowel and not someone talking about themselves + result = result.replace(/\bi\b(?!\s+is\b)(?=['\s]|$)/g, "I"); return result; } From 576101ca0b17c86350b9ffdd93b625f0244e9969 Mon Sep 17 00:00:00 2001 From: prism Date: Wed, 17 Dec 2025 17:50:43 +0100 Subject: [PATCH 003/108] feat(ElementHighlighter): add plugin (#572) * feat(ElementHighlighter): add plugin * fix lint * t --- .../elementHighlighter.dev/index.tsx | 463 ++++++++++++++++++ .../elementHighlighter.dev/styles.css | 104 ++++ .../elementHighlighter/index.tsx | 463 ++++++++++++++++++ .../elementHighlighter/styles.css | 104 ++++ 4 files changed, 1134 insertions(+) create mode 100644 src/equicordplugins/elementHighlighter.dev/index.tsx create mode 100644 src/equicordplugins/elementHighlighter.dev/styles.css create mode 100644 src/equicordplugins/elementHighlighter/index.tsx create mode 100644 src/equicordplugins/elementHighlighter/styles.css diff --git a/src/equicordplugins/elementHighlighter.dev/index.tsx b/src/equicordplugins/elementHighlighter.dev/index.tsx new file mode 100644 index 000000000..aef7aac03 --- /dev/null +++ b/src/equicordplugins/elementHighlighter.dev/index.tsx @@ -0,0 +1,463 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./styles.css"; + +import { definePluginSettings } from "@api/Settings"; +import { classNameFactory } from "@api/Styles"; +import { Button } from "@components/Button"; +import { EquicordDevs, IS_MAC } from "@utils/constants"; +import { copyWithToast } from "@utils/discord"; +import definePlugin, { OptionType } from "@utils/types"; +import { findComponentByCodeLazy } from "@webpack"; +import { useEffect, useState } from "@webpack/common"; + +const KeybindShortcut = findComponentByCodeLazy(".combo,", ".key,"); + +const cl = classNameFactory("vc-element-highlight-"); + +const DEFAULT_KEYBIND = ["mod", "shift", "y"]; + +let active = false; +let overlay: HTMLDivElement | null = null; +let tooltip: HTMLDivElement | null = null; +let lastTarget: Element | null = null; +let pendingFrame = 0; +let pendingX = 0; +let pendingY = 0; +let pendingElement: Element | null = null; + +const colorCache = new WeakMap(); +let cachedRules: { selector: string; specificity: number; color: string }[] = []; + +function KeybindRecorder() { + const [isListening, setIsListening] = useState(false); + const currentKeybind = settings.use(["keybind"]).keybind; + + useEffect(() => { + if (!isListening) return; + + const handleKeyDown = (event: KeyboardEvent) => { + event.preventDefault(); + event.stopPropagation(); + + if (["Control", "Shift", "Alt", "Meta"].includes(event.key)) return; + + const keys: string[] = []; + if (event.metaKey) keys.push("meta"); + if (event.ctrlKey) keys.push(IS_MAC ? "control" : "mod"); + if (event.shiftKey) keys.push("shift"); + if (event.altKey) keys.push("alt"); + + let mainKey = event.key.toLowerCase(); + if (mainKey === " ") mainKey = "space"; + if (mainKey === "escape") mainKey = "esc"; + + keys.push(mainKey); + settings.store.keybind = keys; + setIsListening(false); + }; + + const handleBlur = () => setIsListening(false); + + document.addEventListener("keydown", handleKeyDown, true); + window.addEventListener("blur", handleBlur); + + return () => { + document.removeEventListener("keydown", handleKeyDown, true); + window.removeEventListener("blur", handleBlur); + }; + }, [isListening]); + + return ( +
+ + +
+ ); +} + +const settings = definePluginSettings({ + keybind: { + description: "Toggle Highlighter", + type: OptionType.COMPONENT, + default: DEFAULT_KEYBIND, + component: KeybindRecorder + }, + showClasses: { + description: "Display the element's CSS class names in the tooltip", + type: OptionType.BOOLEAN, + default: false + }, + showId: { + description: "Display the element's ID attribute in the tooltip", + type: OptionType.BOOLEAN, + default: false + }, + showFont: { + description: "Display the computed font family and font size", + type: OptionType.BOOLEAN, + default: false + }, + showPadding: { + description: "Display the element's padding values", + type: OptionType.BOOLEAN, + default: false + }, + showMargin: { + description: "Display the element's margin values", + type: OptionType.BOOLEAN, + default: false + }, + showBorderRadius: { + description: "Display the element's border-radius values", + type: OptionType.BOOLEAN, + default: false + }, + showPosition: { + description: "Display the element's CSS position type and z-index", + type: OptionType.BOOLEAN, + default: false + }, + showDisplay: { + description: "Display the element's display type along with flex or grid properties", + type: OptionType.BOOLEAN, + default: false + } +}); + +function calcSpecificity(sel: string): number { + const ids = (sel.match(/#[\w-]+/g) ?? []).length; + const classes = (sel.match(/\.[\w-]+/g) ?? []).length + (sel.match(/\[[\w-]+/g) ?? []).length; + const tags = (sel.match(/(^|[\s>+~])[\w-]+/g) ?? []).length; + return ids * 10000 + classes * 100 + tags; +} + +function cacheRules() { + cachedRules = []; + for (const sheet of document.styleSheets) { + try { + for (const rule of sheet.cssRules) { + if (!(rule instanceof CSSStyleRule)) continue; + const color = rule.style.getPropertyValue("color"); + if (!color) continue; + + for (const sel of rule.selectorText.split(",")) { + const s = sel.trim(); + cachedRules.push({ selector: s, specificity: calcSpecificity(s), color }); + } + } + } catch { } + } + cachedRules.sort((a, b) => a.specificity - b.specificity); +} + +function getColorVarFromElement(el: Element): string | null { + if (el instanceof HTMLElement) { + const inline = el.style.getPropertyValue("color"); + if (inline) return inline; + } + + const styleAttr = el.getAttribute("style"); + if (styleAttr) { + const match = styleAttr.match(/color\s*:\s*([^;]+)/); + if (match) return match[1].trim(); + } + + let result: string | null = null; + for (const r of cachedRules) { + try { + if (el.matches(r.selector)) result = r.color; + } catch { } + } + return result; +} + +function getColorVar(el: Element): string | null { + const cached = colorCache.get(el); + if (cached !== undefined) return cached; + + let current: Element | null = el; + while (current) { + const color = getColorVarFromElement(current); + if (color) { + colorCache.set(el, color); + return color; + } + current = current.parentElement; + } + colorCache.set(el, null); + return null; +} + +function rgbToHex(rgb: string): string { + const match = rgb.match(/\d+/g); + if (!match || match.length < 3) return rgb; + const [r, g, b] = match.map(Number); + return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; +} + +function formatBoxValue(value: string): string | null { + if (value === "0px" || value === "0px 0px 0px 0px") return null; + return value; +} + +function buildTooltipContent(el: Element, computed: CSSStyleDeclaration, rect: DOMRect): string { + const tag = el.tagName.toLowerCase(); + const colorVar = getColorVar(el) ?? computed.color; + const hex = rgbToHex(computed.color); + + let html = `<${tag}> `; + html += `${Math.round(rect.width)}x${Math.round(rect.height)}`; + html += `
${colorVar}
`; + html += `
${hex}
`; + + const { store } = settings; + + if (store.showId) { + const { id } = el; + if (id) html += `
id: #${id}
`; + } + + if (store.showClasses) { + const classes = el.className; + if (classes && typeof classes === "string") { + const truncated = classes.length > 60 ? classes.slice(0, 60) + "…" : classes; + html += `
class: ${truncated}
`; + } + } + + if (store.showFont) { + const font = computed.fontFamily.split(",")[0].replace(/["']/g, ""); + const size = computed.fontSize; + html += `
font: ${font} ${size}
`; + } + + if (store.showPadding) { + const padding = formatBoxValue(computed.padding); + if (padding) html += `
padding: ${padding}
`; + } + + if (store.showMargin) { + const margin = formatBoxValue(computed.margin); + if (margin) html += `
margin: ${margin}
`; + } + + if (store.showBorderRadius) { + const radius = formatBoxValue(computed.borderRadius); + if (radius) html += `
radius: ${radius}
`; + } + + if (store.showPosition) { + const pos = computed.position; + if (pos !== "static") { + const zIndex = computed.zIndex !== "auto" ? ` z:${computed.zIndex}` : ""; + html += `
position: ${pos}${zIndex}
`; + } + } + + if (store.showDisplay) { + const { display } = computed; + let extra = ""; + if (display === "flex" || display === "inline-flex") { + extra = ` ${computed.flexDirection} gap:${computed.gap}`; + } else if (display === "grid" || display === "inline-grid") { + extra = ` gap:${computed.gap}`; + } + html += `
display: ${display}${extra}
`; + } + + return html; +} + +function updateFrame() { + pendingFrame = 0; + const el = pendingElement; + if (!el || !overlay || !tooltip) return; + + const rect = el.getBoundingClientRect(); + + overlay.style.transform = `translate3d(${rect.left}px,${rect.top}px,0)`; + overlay.style.width = `${rect.width}px`; + overlay.style.height = `${rect.height}px`; + + if (el !== lastTarget) { + lastTarget = el; + const computed = getComputedStyle(el); + tooltip.innerHTML = buildTooltipContent(el, computed, rect); + } + + positionTooltip(pendingX, pendingY); +} + +function positionTooltip(x: number, y: number) { + if (!tooltip) return; + + const tw = tooltip.offsetWidth; + const th = tooltip.offsetHeight; + const margin = 15; + const edge = 10; + const vw = window.innerWidth; + const vh = window.innerHeight; + + let left = x + margin; + let top = y + margin; + + if (left + tw + edge > vw) { + left = x - margin - tw; + if (left < edge) left = Math.max(edge, vw - tw - edge); + } + + if (top + th + edge > vh) { + top = y - margin - th; + if (top < edge) top = Math.max(edge, vh - th - edge); + } + + tooltip.style.transform = `translate3d(${left}px,${top}px,0)`; +} + +function scheduleFrame(el: Element, x: number, y: number) { + pendingElement = el; + pendingX = x; + pendingY = y; + if (!pendingFrame) { + pendingFrame = requestAnimationFrame(updateFrame); + } +} + +function onMouseMove(e: MouseEvent) { + const el = document.elementFromPoint(e.clientX, e.clientY); + if (!el || el === overlay || el === tooltip || tooltip?.contains(el)) return; + scheduleFrame(el, e.clientX, e.clientY); +} + +function onClick(e: MouseEvent) { + e.preventDefault(); + e.stopPropagation(); + + const el = document.elementFromPoint(e.clientX, e.clientY); + if (!el || el === overlay || el === tooltip) return; + + const colorVar = getColorVar(el); + if (colorVar) copyWithToast(colorVar, "Copied color to clipboard!"); +} + +function onKeyDown(e: KeyboardEvent) { + if (e.key === "Escape") disable(); +} + +function enable() { + active = true; + cacheRules(); + + overlay = document.createElement("div"); + overlay.className = cl("overlay"); + document.body.appendChild(overlay); + + tooltip = document.createElement("div"); + tooltip.className = cl("tooltip"); + document.body.appendChild(tooltip); + + document.addEventListener("mousemove", onMouseMove, { capture: true, passive: true }); + document.addEventListener("click", onClick, true); + document.addEventListener("keydown", onKeyDown); + document.body.classList.add(cl("active")); +} + +function disable() { + active = false; + lastTarget = null; + pendingElement = null; + + if (pendingFrame) { + cancelAnimationFrame(pendingFrame); + pendingFrame = 0; + } + + overlay?.remove(); + overlay = null; + tooltip?.remove(); + tooltip = null; + + document.removeEventListener("mousemove", onMouseMove, true); + document.removeEventListener("click", onClick, true); + document.removeEventListener("keydown", onKeyDown); + document.body.classList.remove(cl("active")); +} + +function getConfiguredKeybind(): string[] { + const raw = settings.store.keybind; + if (Array.isArray(raw) && raw.length > 0) return raw; + return DEFAULT_KEYBIND; +} + +function matchesKeybind(e: KeyboardEvent): boolean { + const keybind = getConfiguredKeybind(); + const pressed = e.key.toLowerCase(); + const code = e.code.toLowerCase().replace("key", "").replace("digit", ""); + + let hasNonModifier = false; + for (const key of keybind) { + switch (key) { + case "mod": + if (!(IS_MAC ? e.metaKey : e.ctrlKey)) return false; + break; + case "control": + case "ctrl": + if (!e.ctrlKey) return false; + break; + case "meta": + case "cmd": + if (!e.metaKey) return false; + break; + case "shift": + if (!e.shiftKey) return false; + break; + case "alt": + if (!e.altKey) return false; + break; + default: + hasNonModifier = true; + if (pressed !== key && code !== key) return false; + } + } + return hasNonModifier; +} + +function onToggle(e: KeyboardEvent) { + if (matchesKeybind(e)) { + e.preventDefault(); + active ? disable() : enable(); + } +} + +export default definePlugin({ + name: "ElementHighlighter", + description: "Highlight and inspect elements easily.", + authors: [EquicordDevs.Prism], + settings, + + start() { + document.addEventListener("keydown", onToggle); + }, + + stop() { + document.removeEventListener("keydown", onToggle); + if (active) disable(); + } +}); diff --git a/src/equicordplugins/elementHighlighter.dev/styles.css b/src/equicordplugins/elementHighlighter.dev/styles.css new file mode 100644 index 000000000..919933b20 --- /dev/null +++ b/src/equicordplugins/elementHighlighter.dev/styles.css @@ -0,0 +1,104 @@ +.vc-element-highlight-active { + cursor: crosshair !important; +} + +.vc-element-highlight-overlay { + position: fixed; + top: 0; + left: 0; + z-index: 999998; + border: 2px solid var(--text-brand); + background: var(--brand-15a); + border-radius: 4px; + pointer-events: none; + will-change: transform, width, height; + contain: layout style; +} + +.vc-element-highlight-tooltip { + position: fixed; + top: 0; + left: 0; + z-index: 999999; + background: rgb(0 0 0 / 70%); + backdrop-filter: blur(12px); + border: 1px solid rgb(255 255 255 / 10%); + border-radius: 6px; + padding: 6px 10px; + font-family: var(--font-code); + font-size: 12px; + line-height: 1.4; + color: var(--text-default); + pointer-events: none; + white-space: nowrap; + will-change: transform; + contain: layout style; +} + +.vc-element-highlight-tag { + color: var(--text-muted); +} + +.vc-element-highlight-size { + color: var(--text-muted); + margin-left: 6px; +} + +.vc-element-highlight-color { + margin-top: 4px; +} + +.vc-element-highlight-swatch { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 2px; + border: 1px solid rgb(255 255 255 / 20%); + vertical-align: middle; + margin-right: 6px; + background: + linear-gradient(var(--c), var(--c)), + repeating-conic-gradient(rgb(255 255 255 / 20%) 0% 25%, transparent 0% 50%) 0 0 / 4px 4px; +} + +.vc-element-highlight-hex { + color: var(--text-muted); + font-size: 11px; + margin-top: 2px; +} + +.vc-element-highlight-info { + color: var(--text-muted); + font-size: 11px; + margin-top: 2px; + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; +} + +.vc-element-highlight-label { + color: var(--text-subtle); +} + +.vc-element-highlight-keybind-row { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; +} + +.vc-element-highlight-keybind-button { + display: flex; + align-items: center; + background: transparent; + border: none; + padding: 0; + cursor: pointer; +} + +.vc-element-highlight-keybind-button.vc-element-highlight-listening { + padding: 4px 8px; + background-color: var(--input-background); + border-radius: 4px; + color: var(--text-muted); +} diff --git a/src/equicordplugins/elementHighlighter/index.tsx b/src/equicordplugins/elementHighlighter/index.tsx new file mode 100644 index 000000000..abbf65abc --- /dev/null +++ b/src/equicordplugins/elementHighlighter/index.tsx @@ -0,0 +1,463 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./styles.css"; + +import { definePluginSettings } from "@api/Settings"; +import { classNameFactory } from "@api/Styles"; +import { Button } from "@components/Button"; +import { EquicordDevs, IS_MAC } from "@utils/constants"; +import { copyWithToast } from "@utils/discord"; +import definePlugin, { OptionType } from "@utils/types"; +import { findComponentByCodeLazy } from "@webpack"; +import { useEffect, useState } from "@webpack/common"; + +const KeybindShortcut = findComponentByCodeLazy(".combo,", ".key,"); + +const cl = classNameFactory("vc-element-highlight-"); + +const DEFAULT_KEYBIND = ["mod", "shift", "y"]; + +let active = false; +let overlay: HTMLDivElement | null = null; +let tooltip: HTMLDivElement | null = null; +let lastTarget: Element | null = null; +let pendingFrame = 0; +let pendingX = 0; +let pendingY = 0; +let pendingElement: Element | null = null; + +const colorCache = new WeakMap(); +let cachedRules: { selector: string; specificity: number; color: string }[] = []; + +function KeybindRecorder() { + const [isListening, setIsListening] = useState(false); + const currentKeybind = settings.use(["keybind"]).keybind; + + useEffect(() => { + if (!isListening) return; + + const handleKeyDown = (event: KeyboardEvent) => { + event.preventDefault(); + event.stopPropagation(); + + if (["Control", "Shift", "Alt", "Meta"].includes(event.key)) return; + + const keys: string[] = []; + if (event.metaKey) keys.push("meta"); + if (event.ctrlKey) keys.push(IS_MAC ? "control" : "mod"); + if (event.shiftKey) keys.push("shift"); + if (event.altKey) keys.push("alt"); + + let mainKey = event.key.toLowerCase(); + if (mainKey === " ") mainKey = "space"; + if (mainKey === "escape") mainKey = "esc"; + + keys.push(mainKey); + settings.store.keybind = keys; + setIsListening(false); + }; + + const handleBlur = () => setIsListening(false); + + document.addEventListener("keydown", handleKeyDown, true); + window.addEventListener("blur", handleBlur); + + return () => { + document.removeEventListener("keydown", handleKeyDown, true); + window.removeEventListener("blur", handleBlur); + }; + }, [isListening]); + + return ( +
+ + +
+ ); +} + +const settings = definePluginSettings({ + keybind: { + description: "Toggle Highlighter", + type: OptionType.COMPONENT, + default: DEFAULT_KEYBIND, + component: KeybindRecorder + }, + showClasses: { + description: "Display the element's CSS class names in the tooltip.", + type: OptionType.BOOLEAN, + default: false + }, + showId: { + description: "Display the element's ID attribute in the tooltip.", + type: OptionType.BOOLEAN, + default: false + }, + showFont: { + description: "Display the computed font family and font size.", + type: OptionType.BOOLEAN, + default: false + }, + showPadding: { + description: "Display the element's padding values.", + type: OptionType.BOOLEAN, + default: false + }, + showMargin: { + description: "Display the element's margin values.", + type: OptionType.BOOLEAN, + default: false + }, + showBorderRadius: { + description: "Display the element's border-radius values.", + type: OptionType.BOOLEAN, + default: false + }, + showPosition: { + description: "Display the element's CSS position type and z-index.", + type: OptionType.BOOLEAN, + default: false + }, + showDisplay: { + description: "Display the element's display type along with flex or grid properties.", + type: OptionType.BOOLEAN, + default: false + } +}); + +function calcSpecificity(sel: string): number { + const ids = (sel.match(/#[\w-]+/g) ?? []).length; + const classes = (sel.match(/\.[\w-]+/g) ?? []).length + (sel.match(/\[[\w-]+/g) ?? []).length; + const tags = (sel.match(/(^|[\s>+~])[\w-]+/g) ?? []).length; + return ids * 10000 + classes * 100 + tags; +} + +function cacheRules() { + cachedRules = []; + for (const sheet of document.styleSheets) { + try { + for (const rule of sheet.cssRules) { + if (!(rule instanceof CSSStyleRule)) continue; + const color = rule.style.getPropertyValue("color"); + if (!color) continue; + + for (const sel of rule.selectorText.split(",")) { + const s = sel.trim(); + cachedRules.push({ selector: s, specificity: calcSpecificity(s), color }); + } + } + } catch { } + } + cachedRules.sort((a, b) => a.specificity - b.specificity); +} + +function getColorVarFromElement(el: Element): string | null { + if (el instanceof HTMLElement) { + const inline = el.style.getPropertyValue("color"); + if (inline) return inline; + } + + const styleAttr = el.getAttribute("style"); + if (styleAttr) { + const match = styleAttr.match(/color\s*:\s*([^;]+)/); + if (match) return match[1].trim(); + } + + let result: string | null = null; + for (const r of cachedRules) { + try { + if (el.matches(r.selector)) result = r.color; + } catch { } + } + return result; +} + +function getColorVar(el: Element): string | null { + const cached = colorCache.get(el); + if (cached !== undefined) return cached; + + let current: Element | null = el; + while (current) { + const color = getColorVarFromElement(current); + if (color) { + colorCache.set(el, color); + return color; + } + current = current.parentElement; + } + colorCache.set(el, null); + return null; +} + +function rgbToHex(rgb: string): string { + const match = rgb.match(/\d+/g); + if (!match || match.length < 3) return rgb; + const [r, g, b] = match.map(Number); + return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; +} + +function formatBoxValue(value: string): string | null { + if (value === "0px" || value === "0px 0px 0px 0px") return null; + return value; +} + +function buildTooltipContent(el: Element, computed: CSSStyleDeclaration, rect: DOMRect): string { + const tag = el.tagName.toLowerCase(); + const colorVar = getColorVar(el) ?? computed.color; + const hex = rgbToHex(computed.color); + + let html = `<${tag}> `; + html += `${Math.round(rect.width)}x${Math.round(rect.height)}`; + html += `
${colorVar}
`; + html += `
${hex}
`; + + const { store } = settings; + + if (store.showId) { + const { id } = el; + if (id) html += `
id: #${id}
`; + } + + if (store.showClasses) { + const classes = el.className; + if (classes && typeof classes === "string") { + const truncated = classes.length > 60 ? classes.slice(0, 60) + "…" : classes; + html += `
class: ${truncated}
`; + } + } + + if (store.showFont) { + const font = computed.fontFamily.split(",")[0].replace(/["']/g, ""); + const size = computed.fontSize; + html += `
font: ${font} ${size}
`; + } + + if (store.showPadding) { + const padding = formatBoxValue(computed.padding); + if (padding) html += `
padding: ${padding}
`; + } + + if (store.showMargin) { + const margin = formatBoxValue(computed.margin); + if (margin) html += `
margin: ${margin}
`; + } + + if (store.showBorderRadius) { + const radius = formatBoxValue(computed.borderRadius); + if (radius) html += `
radius: ${radius}
`; + } + + if (store.showPosition) { + const pos = computed.position; + if (pos !== "static") { + const zIndex = computed.zIndex !== "auto" ? ` z:${computed.zIndex}` : ""; + html += `
position: ${pos}${zIndex}
`; + } + } + + if (store.showDisplay) { + const { display } = computed; + let extra = ""; + if (display === "flex" || display === "inline-flex") { + extra = ` ${computed.flexDirection} gap:${computed.gap}`; + } else if (display === "grid" || display === "inline-grid") { + extra = ` gap:${computed.gap}`; + } + html += `
display: ${display}${extra}
`; + } + + return html; +} + +function updateFrame() { + pendingFrame = 0; + const el = pendingElement; + if (!el || !overlay || !tooltip) return; + + const rect = el.getBoundingClientRect(); + + overlay.style.transform = `translate3d(${rect.left}px,${rect.top}px,0)`; + overlay.style.width = `${rect.width}px`; + overlay.style.height = `${rect.height}px`; + + if (el !== lastTarget) { + lastTarget = el; + const computed = getComputedStyle(el); + tooltip.innerHTML = buildTooltipContent(el, computed, rect); + } + + positionTooltip(pendingX, pendingY); +} + +function positionTooltip(x: number, y: number) { + if (!tooltip) return; + + const tw = tooltip.offsetWidth; + const th = tooltip.offsetHeight; + const margin = 15; + const edge = 10; + const vw = window.innerWidth; + const vh = window.innerHeight; + + let left = x + margin; + let top = y + margin; + + if (left + tw + edge > vw) { + left = x - margin - tw; + if (left < edge) left = Math.max(edge, vw - tw - edge); + } + + if (top + th + edge > vh) { + top = y - margin - th; + if (top < edge) top = Math.max(edge, vh - th - edge); + } + + tooltip.style.transform = `translate3d(${left}px,${top}px,0)`; +} + +function scheduleFrame(el: Element, x: number, y: number) { + pendingElement = el; + pendingX = x; + pendingY = y; + if (!pendingFrame) { + pendingFrame = requestAnimationFrame(updateFrame); + } +} + +function onMouseMove(e: MouseEvent) { + const el = document.elementFromPoint(e.clientX, e.clientY); + if (!el || el === overlay || el === tooltip || tooltip?.contains(el)) return; + scheduleFrame(el, e.clientX, e.clientY); +} + +function onClick(e: MouseEvent) { + e.preventDefault(); + e.stopPropagation(); + + const el = document.elementFromPoint(e.clientX, e.clientY); + if (!el || el === overlay || el === tooltip) return; + + const colorVar = getColorVar(el); + if (colorVar) copyWithToast(colorVar, "Copied color to clipboard!"); +} + +function onKeyDown(e: KeyboardEvent) { + if (e.key === "Escape") disable(); +} + +function enable() { + active = true; + cacheRules(); + + overlay = document.createElement("div"); + overlay.className = cl("overlay"); + document.body.appendChild(overlay); + + tooltip = document.createElement("div"); + tooltip.className = cl("tooltip"); + document.body.appendChild(tooltip); + + document.addEventListener("mousemove", onMouseMove, { capture: true, passive: true }); + document.addEventListener("click", onClick, true); + document.addEventListener("keydown", onKeyDown); + document.body.classList.add(cl("active")); +} + +function disable() { + active = false; + lastTarget = null; + pendingElement = null; + + if (pendingFrame) { + cancelAnimationFrame(pendingFrame); + pendingFrame = 0; + } + + overlay?.remove(); + overlay = null; + tooltip?.remove(); + tooltip = null; + + document.removeEventListener("mousemove", onMouseMove, true); + document.removeEventListener("click", onClick, true); + document.removeEventListener("keydown", onKeyDown); + document.body.classList.remove(cl("active")); +} + +function getConfiguredKeybind(): string[] { + const raw = settings.store.keybind; + if (Array.isArray(raw) && raw.length > 0) return raw; + return DEFAULT_KEYBIND; +} + +function matchesKeybind(e: KeyboardEvent): boolean { + const keybind = getConfiguredKeybind(); + const pressed = e.key.toLowerCase(); + const code = e.code.toLowerCase().replace("key", "").replace("digit", ""); + + let hasNonModifier = false; + for (const key of keybind) { + switch (key) { + case "mod": + if (!(IS_MAC ? e.metaKey : e.ctrlKey)) return false; + break; + case "control": + case "ctrl": + if (!e.ctrlKey) return false; + break; + case "meta": + case "cmd": + if (!e.metaKey) return false; + break; + case "shift": + if (!e.shiftKey) return false; + break; + case "alt": + if (!e.altKey) return false; + break; + default: + hasNonModifier = true; + if (pressed !== key && code !== key) return false; + } + } + return hasNonModifier; +} + +function onToggle(e: KeyboardEvent) { + if (matchesKeybind(e)) { + e.preventDefault(); + active ? disable() : enable(); + } +} + +export default definePlugin({ + name: "ElementHighlighter", + description: "Highlight and inspect elements easily.", + authors: [EquicordDevs.Prism], + settings, + + start() { + document.addEventListener("keydown", onToggle); + }, + + stop() { + document.removeEventListener("keydown", onToggle); + if (active) disable(); + } +}); diff --git a/src/equicordplugins/elementHighlighter/styles.css b/src/equicordplugins/elementHighlighter/styles.css new file mode 100644 index 000000000..919933b20 --- /dev/null +++ b/src/equicordplugins/elementHighlighter/styles.css @@ -0,0 +1,104 @@ +.vc-element-highlight-active { + cursor: crosshair !important; +} + +.vc-element-highlight-overlay { + position: fixed; + top: 0; + left: 0; + z-index: 999998; + border: 2px solid var(--text-brand); + background: var(--brand-15a); + border-radius: 4px; + pointer-events: none; + will-change: transform, width, height; + contain: layout style; +} + +.vc-element-highlight-tooltip { + position: fixed; + top: 0; + left: 0; + z-index: 999999; + background: rgb(0 0 0 / 70%); + backdrop-filter: blur(12px); + border: 1px solid rgb(255 255 255 / 10%); + border-radius: 6px; + padding: 6px 10px; + font-family: var(--font-code); + font-size: 12px; + line-height: 1.4; + color: var(--text-default); + pointer-events: none; + white-space: nowrap; + will-change: transform; + contain: layout style; +} + +.vc-element-highlight-tag { + color: var(--text-muted); +} + +.vc-element-highlight-size { + color: var(--text-muted); + margin-left: 6px; +} + +.vc-element-highlight-color { + margin-top: 4px; +} + +.vc-element-highlight-swatch { + display: inline-block; + width: 10px; + height: 10px; + border-radius: 2px; + border: 1px solid rgb(255 255 255 / 20%); + vertical-align: middle; + margin-right: 6px; + background: + linear-gradient(var(--c), var(--c)), + repeating-conic-gradient(rgb(255 255 255 / 20%) 0% 25%, transparent 0% 50%) 0 0 / 4px 4px; +} + +.vc-element-highlight-hex { + color: var(--text-muted); + font-size: 11px; + margin-top: 2px; +} + +.vc-element-highlight-info { + color: var(--text-muted); + font-size: 11px; + margin-top: 2px; + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; +} + +.vc-element-highlight-label { + color: var(--text-subtle); +} + +.vc-element-highlight-keybind-row { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; +} + +.vc-element-highlight-keybind-button { + display: flex; + align-items: center; + background: transparent; + border: none; + padding: 0; + cursor: pointer; +} + +.vc-element-highlight-keybind-button.vc-element-highlight-listening { + padding: 4px 8px; + background-color: var(--input-background); + border-radius: 4px; + color: var(--text-muted); +} From b5f13a0946062b60cbe08f3ca11927fd13789f00 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:48:07 -0500 Subject: [PATCH 004/108] SettingsSync: Fix Errors and Formatting --- src/api/SettingsSync/offline.ts | 29 ++++++---------------- src/equicordplugins/polishWording/index.ts | 1 - 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/api/SettingsSync/offline.ts b/src/api/SettingsSync/offline.ts index 2e0cbea9d..3b114407c 100644 --- a/src/api/SettingsSync/offline.ts +++ b/src/api/SettingsSync/offline.ts @@ -56,8 +56,8 @@ export async function importSettings(data: string, type: BackupType = "all", clo switch (type) { case "all": { - if (!cloud && (!("settings" in parsed) || !("quickCss" in parsed) || !("dataStore" in parsed))) - throw new Error("Invalid Settings. Missing one or more required keys."); + if (!cloud && (!("settings" in parsed))) + throw new Error("Invalid Settings. Plugin settings is required for this import try a different one."); if (parsed.settings) { Object.assign(PlainSettings, parsed.settings); @@ -68,10 +68,10 @@ export async function importSettings(data: string, type: BackupType = "all", clo break; } case "plugins": { - if (!parsed.settings?.settings) throw new Error("Plugin settings missing"); + if (!parsed.settings) throw new Error("Plugin settings missing"); - Object.assign(PlainSettings, parsed.settings.settings); - await VencordNative.settings.set(parsed.settings.settings); + Object.assign(PlainSettings, parsed.settings); + await VencordNative.settings.set(parsed.settings); break; } case "css": { @@ -99,7 +99,7 @@ export async function exportSettings({ syncDataStore = true, type = "all", minif return JSON.stringify({ settings, quickCss, ...(syncDataStore && { dataStore }) }, null, minify ? undefined : 4); } case "plugins": { - return JSON.stringify({ settings: { settings } }, null, minify ? undefined : 4); + return JSON.stringify({ settings }, null, minify ? undefined : 4); } case "css": { return JSON.stringify({ quickCss }, null, minify ? undefined : 4); @@ -110,21 +110,6 @@ export async function exportSettings({ syncDataStore = true, type = "all", minif } } -export async function exportPlugins({ minify }: { minify?: boolean; } = {}) { - const { plugins } = VencordNative.settings.get(); - return JSON.stringify({ settings: { plugins } }, null, minify ? undefined : 4); -} - -export async function exportCSS({ minify }: { minify?: boolean; } = {}) { - const quickCss = await VencordNative.quickCss.get(); - return JSON.stringify({ quickCss }, null, minify ? undefined : 4); -} - -export async function exportDataStores({ minify }: { minify?: boolean; } = {}) { - const dataStore = await DataStore.entries(); - return JSON.stringify({ dataStore }, null, minify ? undefined : 4); -} - export async function downloadSettingsBackup(type: BackupType = "all", { minify }: { minify?: boolean; } = {}) { const backup = await exportSettings({ minify, type }); const filename = `equicord-${type}-backup-${moment().format("YYYY-MM-DD")}.json`; @@ -141,7 +126,7 @@ export async function uploadSettingsBackup(type: BackupType = "all", showToast = if (IS_DISCORD_DESKTOP) { const [file] = await DiscordNative.fileManager.openFiles({ filters: [ - { name: "Vencord Settings Backup", extensions: ["json"] }, + { name: "Equicord Settings Backup", extensions: ["json"] }, { name: "all", extensions: ["*"] } ] }); diff --git a/src/equicordplugins/polishWording/index.ts b/src/equicordplugins/polishWording/index.ts index 04c51d83c..57f925901 100644 --- a/src/equicordplugins/polishWording/index.ts +++ b/src/equicordplugins/polishWording/index.ts @@ -275,7 +275,6 @@ function capitalize(textInput: string): string { } } - // Stand-alone i's turn into I, unless there's the word 'is' after, cus then it's a vowel and not someone talking about themselves result = result.replace(/\bi\b(?!\s+is\b)(?=['\s]|$)/g, "I"); return result; From 002ab8f4f7ad78e0342f08559a1da56947a6dc5b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:58:37 -0500 Subject: [PATCH 005/108] SupportHelper: Update Debug Plugin Note --- src/plugins/_core/supportHelper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index bd932e0dc..4be9d552d 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -164,7 +164,7 @@ async function generateDebugInfoMessage() { potentiallyProblematicPlugins.push("CustomIdle"); } - const potentiallyProblematicPluginsNote = "-# Note: said plugin(s) might be the not be the cause of your problem. They are just plug-ins that cause common issues."; + const potentiallyProblematicPluginsNote = "-# Note: These plugins might not be the cause of your problem. They are simply plugins that cause common issues."; const commonIssues = { "Activity Sharing Disabled": tryOrElse(() => !ShowCurrentGame.getSetting(), false), From 0cafa9a0820d37ef16c2b6a84a74ca8f47b9c4be Mon Sep 17 00:00:00 2001 From: Etorix <92535668+EtorixDev@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:23:05 -0800 Subject: [PATCH 006/108] Fix Activity Quest check. (#575) --- src/equicordplugins/questify/index.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/equicordplugins/questify/index.tsx b/src/equicordplugins/questify/index.tsx index fb17c4710..16a3412dc 100644 --- a/src/equicordplugins/questify/index.tsx +++ b/src/equicordplugins/questify/index.tsx @@ -923,6 +923,12 @@ function getQuestAcceptedButtonProps(quest: Quest, text: string, disabled: boole }; } +function isIncompatibleActivity(quest: Quest): boolean { + return !!Object.keys(quest.config.taskConfigV2?.tasks || {}).some(taskType => { + return "ACHIEVEMENT_IN_ACTIVITY" === taskType; + }); +} + export default definePlugin({ name: "Questify", description: "Enhance your Quest experience with a suite of features, or disable them entirely if they're not your thing.", @@ -949,6 +955,7 @@ export default definePlugin({ processQuestForAutoComplete, getQuestAcceptedButtonProps, getQuestAcceptedButtonText, + isIncompatibleActivity, getQuestPanelOverride, setLastFilterChoices, getLastFilterChoices, @@ -1278,7 +1285,7 @@ export default definePlugin({ // Start Play Game Quests. // Video Quests are handled in the next patch group. match: /(?<=onClick:async\(\)=>{)/, - replace: "const startingAutoComplete=arguments[0].isVideoQuest?false:!$self.processQuestForAutoComplete(arguments[0].quest);" + replace: "const startingAutoComplete=(arguments[0].isVideoQuest||$self.isIncompatibleActivity(arguments[0].quest))?false:!$self.processQuestForAutoComplete(arguments[0].quest);" }, { // The "Resume (XX:XX)" text is changed to "Watching (XX:XX)" if the Quest is active. From e8fd3dc3efa587a1f71b86255753a502e9a24c99 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 17 Dec 2025 22:31:13 +0100 Subject: [PATCH 007/108] Fix MemberCount --- src/plugins/memberCount/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/memberCount/index.tsx b/src/plugins/memberCount/index.tsx index e2d78e2b0..e5dac9d02 100644 --- a/src/plugins/memberCount/index.tsx +++ b/src/plugins/memberCount/index.tsx @@ -72,7 +72,7 @@ export default definePlugin({ replacement: [ { match: /(?<=var\{className:(\i),.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/, - replace: ":[$1?.startsWith('members')?$self.render():null,$2", + replace: ":[$1?.includes('members')?$self.render():null,$2", }, ], predicate: () => settings.store.memberList From 16f343be52bf85dd765d1064ac5ad3b40a0694d7 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 17 Dec 2025 22:56:07 +0100 Subject: [PATCH 008/108] FixImageQuality: fix videos & don't animate gifs if app unfocused --- src/plugins/fixImagesQuality/index.tsx | 40 +++++++++++++++++--------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/plugins/fixImagesQuality/index.tsx b/src/plugins/fixImagesQuality/index.tsx index 4c3e6eeb3..fc9859d9b 100644 --- a/src/plugins/fixImagesQuality/index.tsx +++ b/src/plugins/fixImagesQuality/index.tsx @@ -7,6 +7,7 @@ import { definePluginSettings } from "@api/Settings"; import { Card } from "@components/Card"; import { Flex } from "@components/Flex"; +import { Margins } from "@components/margins"; import { Paragraph } from "@components/Paragraph"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; @@ -15,7 +16,7 @@ import definePlugin, { OptionType } from "@utils/types"; const settings = definePluginSettings({ originalImagesInChat: { type: OptionType.BOOLEAN, - description: "Also load the original image in Chat", + description: "Also load the original image in Chat. WARNING: Read the caveats above", default: false, } }); @@ -31,7 +32,7 @@ export default definePlugin({ find: ".handleImageLoad)", replacement: { match: /getSrc\(\i\)\{/, - replace: "$&var _vcSrc=$self.getSrc(this.props);if(_vcSrc)return _vcSrc;" + replace: "$&var _vcSrc=$self.getSrc(this.props,arguments[1]);if(_vcSrc)return _vcSrc;" } } ], @@ -47,36 +48,49 @@ export default definePlugin({
  • — In the image modal, the original image will be loaded.
  • + You can also enable original image in chat, but beware of the following caveats: - You can also enable original image in chat, but this may cause performance issues! +
      +
    • — Animated images (GIF, WebP, etc.) in chat will always animate, regardless of if the App is focused.
    • +
    • — May cause lag.
    • +
    ); }, - getSrc(props: { src: string; mediaLayoutType: string; width: number; height: number; }) { - if (!props) return; + getSrc(props: { src: string; mediaLayoutType: string; width: number; height: number; contentType: string; }, freeze?: boolean) { + if (!props?.src) return; try { - if (!settings.store.originalImagesInChat && props.mediaLayoutType === "MOSAIC") { + const { contentType, height, mediaLayoutType, src, width } = props; + if (!contentType?.startsWith("image/") || src.startsWith("data:")) return; + + console.log({ src, freeze }); + const url = new URL(src); + url.searchParams.set("animated", String(!freeze)); + + if (!settings.store.originalImagesInChat && mediaLayoutType === "MOSAIC") { // make sure the image is not too large - const pixels = props.width * props.height; + const pixels = width * height; const limit = 2000 * 1200; - if (pixels <= limit) return props.src; + if (pixels <= limit) + return url.toString(); const scale = Math.sqrt(pixels / limit); - const url = new URL(props.src); - url.searchParams.set("width", Math.round(props.width / scale).toString()); - url.searchParams.set("height", Math.round(props.height / scale).toString()); + + url.searchParams.set("width", Math.round(width / scale).toString()); + url.searchParams.set("height", Math.round(height / scale).toString()); return url.toString(); } - return props.src?.replace("https://media.discordapp.net/attachments/", "https://cdn.discordapp.com/attachments/"); + url.hostname = "cdn.discordapp.com"; + return url.toString(); } catch (e) { new Logger("FixImagesQuality").error("Failed to make image src", e); - return props.src; + return; } } }); From 74f106c9fc17012ae1fde303f58581383a6e62bc Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 17 Dec 2025 22:56:59 +0100 Subject: [PATCH 009/108] ImageZoom: load attachments from cdn instead of media proxy --- src/plugins/imageZoom/components/Magnifier.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/imageZoom/components/Magnifier.tsx b/src/plugins/imageZoom/components/Magnifier.tsx index dbe196072..7ca83a6d3 100644 --- a/src/plugins/imageZoom/components/Magnifier.tsx +++ b/src/plugins/imageZoom/components/Magnifier.tsx @@ -162,6 +162,9 @@ export const Magnifier = ErrorBoundary.wrap(({ instance, size: i const imageSrc = useMemo(() => { try { const imageUrl = new URL(instance.props.src); + if (imageUrl.pathname.startsWith("/attachments/")) + imageUrl.hostname = "cdn.discordapp.com"; + imageUrl.searchParams.set("animated", "true"); return imageUrl.toString(); } catch { From 5f5e7c34f82eaf12403cc3675bdc152700a5faa8 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 17 Dec 2025 22:57:53 +0100 Subject: [PATCH 010/108] remove stray console.log --- src/plugins/fixImagesQuality/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/fixImagesQuality/index.tsx b/src/plugins/fixImagesQuality/index.tsx index fc9859d9b..29aaabad4 100644 --- a/src/plugins/fixImagesQuality/index.tsx +++ b/src/plugins/fixImagesQuality/index.tsx @@ -67,7 +67,6 @@ export default definePlugin({ const { contentType, height, mediaLayoutType, src, width } = props; if (!contentType?.startsWith("image/") || src.startsWith("data:")) return; - console.log({ src, freeze }); const url = new URL(src); url.searchParams.set("animated", String(!freeze)); From cf785bd5babcabaa1beeb280d2df82dde32db108 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:59:14 -0500 Subject: [PATCH 011/108] ScheduledMessages: Remove Logger --- src/equicordplugins/scheduledMessages/index.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/equicordplugins/scheduledMessages/index.tsx b/src/equicordplugins/scheduledMessages/index.tsx index 640170b80..5d60fb6c7 100644 --- a/src/equicordplugins/scheduledMessages/index.tsx +++ b/src/equicordplugins/scheduledMessages/index.tsx @@ -9,7 +9,6 @@ import "./styles.css"; import { MessageOptions } from "@api/MessageEvents"; import { definePluginSettings } from "@api/Settings"; import { EquicordDevs } from "@utils/constants"; -import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; import { isScheduleModeEnabled, ScheduledMessagesButton, setScheduleModeEnabled } from "./components/ChatBarButton"; @@ -30,8 +29,6 @@ import { stopScheduler } from "./utils"; -const logger = new Logger("ScheduledMessages"); - export const settings = definePluginSettings({ maxMessagesPerMinute: { type: OptionType.SLIDER, @@ -66,28 +63,21 @@ export const settings = definePluginSettings({ function handleReactionEvent(event: FluxReactionEvent): void { const { messageId, channelId, emoji } = event; - logger.info("handleReactionEvent:", { type: event.type, messageId, optimistic: event.optimistic, emoji: emoji?.name }); - if (!messageId || !channelId || !emoji) { - logger.warn("handleReactionEvent: Missing required fields"); - return; - } + if (!messageId || !channelId || !emoji) return; const isPhantom = isPhantomMessage(messageId); - logger.info("handleReactionEvent: isPhantomMessage =", isPhantom); if (!isPhantom) return; if (event.optimistic) { // User-initiated reaction change - update our data - logger.info("handleReactionEvent: Processing optimistic", event.type); if (event.type === "MESSAGE_REACTION_ADD") { handleReactionAdd(messageId, channelId, emoji); } else if (event.type === "MESSAGE_REACTION_REMOVE") { handleReactionRemove(messageId, channelId, emoji); } } else { - logger.info("handleReactionEvent: Non-optimistic event, scheduling resync"); setTimeout(() => { resyncPhantomReactions(messageId, channelId); }, 50); From a819d35ac34009c59b89c2e0c63f785ceb6372f5 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 17 Dec 2025 23:34:52 +0100 Subject: [PATCH 012/108] ExpressionCloner: fix "Failed to resize asset below the maximum size" error --- src/plugins/expressionCloner/index.tsx | 36 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/plugins/expressionCloner/index.tsx b/src/plugins/expressionCloner/index.tsx index 11a596bb8..9df5f10a2 100644 --- a/src/plugins/expressionCloner/index.tsx +++ b/src/plugins/expressionCloner/index.tsx @@ -62,6 +62,9 @@ const PremiumTierStickerLimitMap = { 3: 60 } as const; +const MAX_EMOJI_SIZE_BYTES = 256 * 1024; +const MAX_STICKER_SIZE_BYTES = 512 * 1024; + function getGuildMaxStickerSlots(guild: Guild) { if (guild.features.has("MORE_STICKERS") && guild.premiumTier === 3) return 120; @@ -69,11 +72,11 @@ function getGuildMaxStickerSlots(guild: Guild) { return PremiumTierStickerLimitMap[guild.premiumTier] ?? PremiumTierStickerLimitMap[0]; } -function getUrl(data: Data) { +function getUrl(data: Data, size: number) { if (data.t === "Emoji") - return `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${data.id}.${data.isAnimated ? "gif" : "png"}?size=4096&lossless=true`; + return `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${data.id}.${data.isAnimated ? "gif" : "png"}?size=${size}&lossless=true`; - return `${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.${StickerExtMap[data.format_type]}?size=4096&lossless=true`; + return `${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.${StickerExtMap[data.format_type]}?size=${size}&lossless=true`; } async function fetchSticker(id: string) { @@ -97,7 +100,7 @@ async function cloneSticker(guildId: string, sticker: Sticker) { data.append("name", sticker.name); data.append("tags", sticker.tags); data.append("description", sticker.description); - data.append("file", await fetchBlob(getUrl(sticker))); + data.append("file", await fetchBlob(sticker)); const { body } = await RestAPI.post({ url: Constants.Endpoints.GUILD_STICKER_PACKS(guildId), @@ -115,7 +118,7 @@ async function cloneSticker(guildId: string, sticker: Sticker) { } async function cloneEmoji(guildId: string, emoji: Emoji) { - const data = await fetchBlob(getUrl(emoji)); + const data = await fetchBlob(emoji); const dataUrl = await new Promise(resolve => { const reader = new FileReader(); @@ -161,12 +164,23 @@ function getGuildCandidates(data: Data) { }).sort((a, b) => a.name.localeCompare(b.name)); } -async function fetchBlob(url: string) { - const res = await fetch(url); - if (!res.ok) - throw new Error(`Failed to fetch ${url} - ${res.status}`); +async function fetchBlob(data: Data) { + const MAX_SIZE = data.t === "Sticker" + ? MAX_STICKER_SIZE_BYTES + : MAX_EMOJI_SIZE_BYTES; - return res.blob(); + for (let size = 4096; size >= 16; size /= 2) { + const url = getUrl(data, size); + const res = await fetch(url); + if (!res.ok) + throw new Error(`Failed to fetch ${url} - ${res.status}`); + + const blob = await res.blob(); + if (blob.size <= MAX_SIZE) + return blob; + } + + throw new Error(`Failed to fetch ${data.t} within size limit of ${MAX_SIZE / 1000}kB`); } async function doClone(guildId: string, data: Sticker | Emoji) { @@ -312,7 +326,7 @@ function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable { const res = await fetchData(); const data = { t: type, ...res } as Sticker | Emoji; - const url = getUrl(data); + const url = getUrl(data, 128); return modalProps => ( From 717f217ab03f88bd7d1f05d58d770cd9ceff6bcd Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 18 Dec 2025 10:42:26 -0500 Subject: [PATCH 013/108] ContributorModal: Fix Link Spacing --- src/components/settings/tabs/plugins/ContributorModal.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/settings/tabs/plugins/ContributorModal.css b/src/components/settings/tabs/plugins/ContributorModal.css index ef3b2ddec..2727fa878 100644 --- a/src/components/settings/tabs/plugins/ContributorModal.css +++ b/src/components/settings/tabs/plugins/ContributorModal.css @@ -41,6 +41,8 @@ } .vc-author-modal-links { + display: flex; + gap: 6px; margin-left: auto; } From 7a22c8cf5f27f29cdaee008c10b31c93a7d86b49 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 18 Dec 2025 10:59:39 -0500 Subject: [PATCH 014/108] MemberListDecoratorsAPI: Fix Patch --- src/plugins/_api/memberListDecorators/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/_api/memberListDecorators/index.tsx b/src/plugins/_api/memberListDecorators/index.tsx index 90f09d8fa..8bb778b8a 100644 --- a/src/plugins/_api/memberListDecorators/index.tsx +++ b/src/plugins/_api/memberListDecorators/index.tsx @@ -41,8 +41,8 @@ export default definePlugin({ { find: "PrivateChannel.renderAvatar", replacement: { - match: /decorators:(\i\.isSystemDM\(\)\?.+?:null)/, - replace: "decorators:[Vencord.Api.MemberListDecorators.__getDecorators(arguments[0],'dm'),$1]" + match: /((?<=:|\[)\i\.isSystemDM\(\)\?.+?:null)/, + replace: "Vencord.Api.MemberListDecorators.__getDecorators(arguments[0],'dm'),$1" } } ] From 89e3dcc58e024dc1b8d667d5758d1b5611237124 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 18 Dec 2025 13:18:23 -0500 Subject: [PATCH 015/108] Revert "MemberListDecoratorsAPI: Fix Patch" This reverts commit 7a22c8cf5f27f29cdaee008c10b31c93a7d86b49. --- src/plugins/_api/memberListDecorators/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/_api/memberListDecorators/index.tsx b/src/plugins/_api/memberListDecorators/index.tsx index 8bb778b8a..90f09d8fa 100644 --- a/src/plugins/_api/memberListDecorators/index.tsx +++ b/src/plugins/_api/memberListDecorators/index.tsx @@ -41,8 +41,8 @@ export default definePlugin({ { find: "PrivateChannel.renderAvatar", replacement: { - match: /((?<=:|\[)\i\.isSystemDM\(\)\?.+?:null)/, - replace: "Vencord.Api.MemberListDecorators.__getDecorators(arguments[0],'dm'),$1" + match: /decorators:(\i\.isSystemDM\(\)\?.+?:null)/, + replace: "decorators:[Vencord.Api.MemberListDecorators.__getDecorators(arguments[0],'dm'),$1]" } } ] From 851e542b207e5635d4a8daff531d1ca868344bf2 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Thu, 18 Dec 2025 20:03:33 -0500 Subject: [PATCH 016/108] ElementHighlighter: Remove Dupe --- .../elementHighlighter/index.tsx | 463 ------------------ .../elementHighlighter/styles.css | 104 ---- 2 files changed, 567 deletions(-) delete mode 100644 src/equicordplugins/elementHighlighter/index.tsx delete mode 100644 src/equicordplugins/elementHighlighter/styles.css diff --git a/src/equicordplugins/elementHighlighter/index.tsx b/src/equicordplugins/elementHighlighter/index.tsx deleted file mode 100644 index abbf65abc..000000000 --- a/src/equicordplugins/elementHighlighter/index.tsx +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import "./styles.css"; - -import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; -import { Button } from "@components/Button"; -import { EquicordDevs, IS_MAC } from "@utils/constants"; -import { copyWithToast } from "@utils/discord"; -import definePlugin, { OptionType } from "@utils/types"; -import { findComponentByCodeLazy } from "@webpack"; -import { useEffect, useState } from "@webpack/common"; - -const KeybindShortcut = findComponentByCodeLazy(".combo,", ".key,"); - -const cl = classNameFactory("vc-element-highlight-"); - -const DEFAULT_KEYBIND = ["mod", "shift", "y"]; - -let active = false; -let overlay: HTMLDivElement | null = null; -let tooltip: HTMLDivElement | null = null; -let lastTarget: Element | null = null; -let pendingFrame = 0; -let pendingX = 0; -let pendingY = 0; -let pendingElement: Element | null = null; - -const colorCache = new WeakMap(); -let cachedRules: { selector: string; specificity: number; color: string }[] = []; - -function KeybindRecorder() { - const [isListening, setIsListening] = useState(false); - const currentKeybind = settings.use(["keybind"]).keybind; - - useEffect(() => { - if (!isListening) return; - - const handleKeyDown = (event: KeyboardEvent) => { - event.preventDefault(); - event.stopPropagation(); - - if (["Control", "Shift", "Alt", "Meta"].includes(event.key)) return; - - const keys: string[] = []; - if (event.metaKey) keys.push("meta"); - if (event.ctrlKey) keys.push(IS_MAC ? "control" : "mod"); - if (event.shiftKey) keys.push("shift"); - if (event.altKey) keys.push("alt"); - - let mainKey = event.key.toLowerCase(); - if (mainKey === " ") mainKey = "space"; - if (mainKey === "escape") mainKey = "esc"; - - keys.push(mainKey); - settings.store.keybind = keys; - setIsListening(false); - }; - - const handleBlur = () => setIsListening(false); - - document.addEventListener("keydown", handleKeyDown, true); - window.addEventListener("blur", handleBlur); - - return () => { - document.removeEventListener("keydown", handleKeyDown, true); - window.removeEventListener("blur", handleBlur); - }; - }, [isListening]); - - return ( -
    - - -
    - ); -} - -const settings = definePluginSettings({ - keybind: { - description: "Toggle Highlighter", - type: OptionType.COMPONENT, - default: DEFAULT_KEYBIND, - component: KeybindRecorder - }, - showClasses: { - description: "Display the element's CSS class names in the tooltip.", - type: OptionType.BOOLEAN, - default: false - }, - showId: { - description: "Display the element's ID attribute in the tooltip.", - type: OptionType.BOOLEAN, - default: false - }, - showFont: { - description: "Display the computed font family and font size.", - type: OptionType.BOOLEAN, - default: false - }, - showPadding: { - description: "Display the element's padding values.", - type: OptionType.BOOLEAN, - default: false - }, - showMargin: { - description: "Display the element's margin values.", - type: OptionType.BOOLEAN, - default: false - }, - showBorderRadius: { - description: "Display the element's border-radius values.", - type: OptionType.BOOLEAN, - default: false - }, - showPosition: { - description: "Display the element's CSS position type and z-index.", - type: OptionType.BOOLEAN, - default: false - }, - showDisplay: { - description: "Display the element's display type along with flex or grid properties.", - type: OptionType.BOOLEAN, - default: false - } -}); - -function calcSpecificity(sel: string): number { - const ids = (sel.match(/#[\w-]+/g) ?? []).length; - const classes = (sel.match(/\.[\w-]+/g) ?? []).length + (sel.match(/\[[\w-]+/g) ?? []).length; - const tags = (sel.match(/(^|[\s>+~])[\w-]+/g) ?? []).length; - return ids * 10000 + classes * 100 + tags; -} - -function cacheRules() { - cachedRules = []; - for (const sheet of document.styleSheets) { - try { - for (const rule of sheet.cssRules) { - if (!(rule instanceof CSSStyleRule)) continue; - const color = rule.style.getPropertyValue("color"); - if (!color) continue; - - for (const sel of rule.selectorText.split(",")) { - const s = sel.trim(); - cachedRules.push({ selector: s, specificity: calcSpecificity(s), color }); - } - } - } catch { } - } - cachedRules.sort((a, b) => a.specificity - b.specificity); -} - -function getColorVarFromElement(el: Element): string | null { - if (el instanceof HTMLElement) { - const inline = el.style.getPropertyValue("color"); - if (inline) return inline; - } - - const styleAttr = el.getAttribute("style"); - if (styleAttr) { - const match = styleAttr.match(/color\s*:\s*([^;]+)/); - if (match) return match[1].trim(); - } - - let result: string | null = null; - for (const r of cachedRules) { - try { - if (el.matches(r.selector)) result = r.color; - } catch { } - } - return result; -} - -function getColorVar(el: Element): string | null { - const cached = colorCache.get(el); - if (cached !== undefined) return cached; - - let current: Element | null = el; - while (current) { - const color = getColorVarFromElement(current); - if (color) { - colorCache.set(el, color); - return color; - } - current = current.parentElement; - } - colorCache.set(el, null); - return null; -} - -function rgbToHex(rgb: string): string { - const match = rgb.match(/\d+/g); - if (!match || match.length < 3) return rgb; - const [r, g, b] = match.map(Number); - return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; -} - -function formatBoxValue(value: string): string | null { - if (value === "0px" || value === "0px 0px 0px 0px") return null; - return value; -} - -function buildTooltipContent(el: Element, computed: CSSStyleDeclaration, rect: DOMRect): string { - const tag = el.tagName.toLowerCase(); - const colorVar = getColorVar(el) ?? computed.color; - const hex = rgbToHex(computed.color); - - let html = `<${tag}> `; - html += `${Math.round(rect.width)}x${Math.round(rect.height)}`; - html += `
    ${colorVar}
    `; - html += `
    ${hex}
    `; - - const { store } = settings; - - if (store.showId) { - const { id } = el; - if (id) html += `
    id: #${id}
    `; - } - - if (store.showClasses) { - const classes = el.className; - if (classes && typeof classes === "string") { - const truncated = classes.length > 60 ? classes.slice(0, 60) + "…" : classes; - html += `
    class: ${truncated}
    `; - } - } - - if (store.showFont) { - const font = computed.fontFamily.split(",")[0].replace(/["']/g, ""); - const size = computed.fontSize; - html += `
    font: ${font} ${size}
    `; - } - - if (store.showPadding) { - const padding = formatBoxValue(computed.padding); - if (padding) html += `
    padding: ${padding}
    `; - } - - if (store.showMargin) { - const margin = formatBoxValue(computed.margin); - if (margin) html += `
    margin: ${margin}
    `; - } - - if (store.showBorderRadius) { - const radius = formatBoxValue(computed.borderRadius); - if (radius) html += `
    radius: ${radius}
    `; - } - - if (store.showPosition) { - const pos = computed.position; - if (pos !== "static") { - const zIndex = computed.zIndex !== "auto" ? ` z:${computed.zIndex}` : ""; - html += `
    position: ${pos}${zIndex}
    `; - } - } - - if (store.showDisplay) { - const { display } = computed; - let extra = ""; - if (display === "flex" || display === "inline-flex") { - extra = ` ${computed.flexDirection} gap:${computed.gap}`; - } else if (display === "grid" || display === "inline-grid") { - extra = ` gap:${computed.gap}`; - } - html += `
    display: ${display}${extra}
    `; - } - - return html; -} - -function updateFrame() { - pendingFrame = 0; - const el = pendingElement; - if (!el || !overlay || !tooltip) return; - - const rect = el.getBoundingClientRect(); - - overlay.style.transform = `translate3d(${rect.left}px,${rect.top}px,0)`; - overlay.style.width = `${rect.width}px`; - overlay.style.height = `${rect.height}px`; - - if (el !== lastTarget) { - lastTarget = el; - const computed = getComputedStyle(el); - tooltip.innerHTML = buildTooltipContent(el, computed, rect); - } - - positionTooltip(pendingX, pendingY); -} - -function positionTooltip(x: number, y: number) { - if (!tooltip) return; - - const tw = tooltip.offsetWidth; - const th = tooltip.offsetHeight; - const margin = 15; - const edge = 10; - const vw = window.innerWidth; - const vh = window.innerHeight; - - let left = x + margin; - let top = y + margin; - - if (left + tw + edge > vw) { - left = x - margin - tw; - if (left < edge) left = Math.max(edge, vw - tw - edge); - } - - if (top + th + edge > vh) { - top = y - margin - th; - if (top < edge) top = Math.max(edge, vh - th - edge); - } - - tooltip.style.transform = `translate3d(${left}px,${top}px,0)`; -} - -function scheduleFrame(el: Element, x: number, y: number) { - pendingElement = el; - pendingX = x; - pendingY = y; - if (!pendingFrame) { - pendingFrame = requestAnimationFrame(updateFrame); - } -} - -function onMouseMove(e: MouseEvent) { - const el = document.elementFromPoint(e.clientX, e.clientY); - if (!el || el === overlay || el === tooltip || tooltip?.contains(el)) return; - scheduleFrame(el, e.clientX, e.clientY); -} - -function onClick(e: MouseEvent) { - e.preventDefault(); - e.stopPropagation(); - - const el = document.elementFromPoint(e.clientX, e.clientY); - if (!el || el === overlay || el === tooltip) return; - - const colorVar = getColorVar(el); - if (colorVar) copyWithToast(colorVar, "Copied color to clipboard!"); -} - -function onKeyDown(e: KeyboardEvent) { - if (e.key === "Escape") disable(); -} - -function enable() { - active = true; - cacheRules(); - - overlay = document.createElement("div"); - overlay.className = cl("overlay"); - document.body.appendChild(overlay); - - tooltip = document.createElement("div"); - tooltip.className = cl("tooltip"); - document.body.appendChild(tooltip); - - document.addEventListener("mousemove", onMouseMove, { capture: true, passive: true }); - document.addEventListener("click", onClick, true); - document.addEventListener("keydown", onKeyDown); - document.body.classList.add(cl("active")); -} - -function disable() { - active = false; - lastTarget = null; - pendingElement = null; - - if (pendingFrame) { - cancelAnimationFrame(pendingFrame); - pendingFrame = 0; - } - - overlay?.remove(); - overlay = null; - tooltip?.remove(); - tooltip = null; - - document.removeEventListener("mousemove", onMouseMove, true); - document.removeEventListener("click", onClick, true); - document.removeEventListener("keydown", onKeyDown); - document.body.classList.remove(cl("active")); -} - -function getConfiguredKeybind(): string[] { - const raw = settings.store.keybind; - if (Array.isArray(raw) && raw.length > 0) return raw; - return DEFAULT_KEYBIND; -} - -function matchesKeybind(e: KeyboardEvent): boolean { - const keybind = getConfiguredKeybind(); - const pressed = e.key.toLowerCase(); - const code = e.code.toLowerCase().replace("key", "").replace("digit", ""); - - let hasNonModifier = false; - for (const key of keybind) { - switch (key) { - case "mod": - if (!(IS_MAC ? e.metaKey : e.ctrlKey)) return false; - break; - case "control": - case "ctrl": - if (!e.ctrlKey) return false; - break; - case "meta": - case "cmd": - if (!e.metaKey) return false; - break; - case "shift": - if (!e.shiftKey) return false; - break; - case "alt": - if (!e.altKey) return false; - break; - default: - hasNonModifier = true; - if (pressed !== key && code !== key) return false; - } - } - return hasNonModifier; -} - -function onToggle(e: KeyboardEvent) { - if (matchesKeybind(e)) { - e.preventDefault(); - active ? disable() : enable(); - } -} - -export default definePlugin({ - name: "ElementHighlighter", - description: "Highlight and inspect elements easily.", - authors: [EquicordDevs.Prism], - settings, - - start() { - document.addEventListener("keydown", onToggle); - }, - - stop() { - document.removeEventListener("keydown", onToggle); - if (active) disable(); - } -}); diff --git a/src/equicordplugins/elementHighlighter/styles.css b/src/equicordplugins/elementHighlighter/styles.css deleted file mode 100644 index 919933b20..000000000 --- a/src/equicordplugins/elementHighlighter/styles.css +++ /dev/null @@ -1,104 +0,0 @@ -.vc-element-highlight-active { - cursor: crosshair !important; -} - -.vc-element-highlight-overlay { - position: fixed; - top: 0; - left: 0; - z-index: 999998; - border: 2px solid var(--text-brand); - background: var(--brand-15a); - border-radius: 4px; - pointer-events: none; - will-change: transform, width, height; - contain: layout style; -} - -.vc-element-highlight-tooltip { - position: fixed; - top: 0; - left: 0; - z-index: 999999; - background: rgb(0 0 0 / 70%); - backdrop-filter: blur(12px); - border: 1px solid rgb(255 255 255 / 10%); - border-radius: 6px; - padding: 6px 10px; - font-family: var(--font-code); - font-size: 12px; - line-height: 1.4; - color: var(--text-default); - pointer-events: none; - white-space: nowrap; - will-change: transform; - contain: layout style; -} - -.vc-element-highlight-tag { - color: var(--text-muted); -} - -.vc-element-highlight-size { - color: var(--text-muted); - margin-left: 6px; -} - -.vc-element-highlight-color { - margin-top: 4px; -} - -.vc-element-highlight-swatch { - display: inline-block; - width: 10px; - height: 10px; - border-radius: 2px; - border: 1px solid rgb(255 255 255 / 20%); - vertical-align: middle; - margin-right: 6px; - background: - linear-gradient(var(--c), var(--c)), - repeating-conic-gradient(rgb(255 255 255 / 20%) 0% 25%, transparent 0% 50%) 0 0 / 4px 4px; -} - -.vc-element-highlight-hex { - color: var(--text-muted); - font-size: 11px; - margin-top: 2px; -} - -.vc-element-highlight-info { - color: var(--text-muted); - font-size: 11px; - margin-top: 2px; - max-width: 300px; - overflow: hidden; - text-overflow: ellipsis; -} - -.vc-element-highlight-label { - color: var(--text-subtle); -} - -.vc-element-highlight-keybind-row { - display: flex; - flex-direction: row; - align-items: center; - gap: 8px; -} - -.vc-element-highlight-keybind-button { - display: flex; - align-items: center; - background: transparent; - border: none; - padding: 0; - cursor: pointer; -} - -.vc-element-highlight-keybind-button.vc-element-highlight-listening { - padding: 4px 8px; - background-color: var(--input-background); - border-radius: 4px; - color: var(--text-muted); -} From bf3edaed6ae0cb838ca083e1227f73365fa113b7 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 19 Dec 2025 19:22:25 +0100 Subject: [PATCH 017/108] Fix GameActivityToggle --- src/plugins/gameActivityToggle/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 0df692a06..794ddc802 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -28,7 +28,7 @@ import { Menu } from "@webpack/common"; import managedStyle from "./style.css?managed"; -const Button = findComponentByCodeLazy(".NONE,disabled:", ".PANEL_BUTTON"); +const Button = findComponentByCodeLazy(".greenTooltip,", ".greenTooltipContent"); const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; From ea271496a3c1d8b092f32d37efa2f5f5e016d21e Mon Sep 17 00:00:00 2001 From: Instellate <63735043+Instellate@users.noreply.github.com> Date: Sat, 20 Dec 2025 01:30:58 +0700 Subject: [PATCH 018/108] PlatformIndicators: fix & refactor (#3827) Co-authored-by: Vendicated --- package.json | 1 + packages/discord-types/src/utils.d.ts | 1 + pnpm-lock.yaml | 111 ++++++++++++++++++ scripts/build/common.mjs | 9 +- src/api/Badges.ts | 3 +- src/plugins/_api/badges/index.tsx | 30 ++--- .../platformIndicators/icons/desktopIcon.svg | 3 + .../platformIndicators/icons/embeddedIcon.svg | 3 + .../platformIndicators/icons/mobileIcon.svg | 3 + .../platformIndicators/icons/webIcon.svg | 3 + src/plugins/platformIndicators/index.tsx | 100 ++++++++++------ 11 files changed, 208 insertions(+), 59 deletions(-) create mode 100644 src/plugins/platformIndicators/icons/desktopIcon.svg create mode 100644 src/plugins/platformIndicators/icons/embeddedIcon.svg create mode 100644 src/plugins/platformIndicators/icons/mobileIcon.svg create mode 100644 src/plugins/platformIndicators/icons/webIcon.svg diff --git a/package.json b/package.json index f6c2e7e12..68b88fb10 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "standalone-electron-types": "^34.2.0", "stylelint": "^16.25.0", "stylelint-config-standard": "^39.0.1", + "svgo": "^4.0.0", "ts-patch": "^3.3.0", "ts-pattern": "^5.6.0", "tsx": "^4.20.6", diff --git a/packages/discord-types/src/utils.d.ts b/packages/discord-types/src/utils.d.ts index 14b8fa507..a51ffea20 100644 --- a/packages/discord-types/src/utils.d.ts +++ b/packages/discord-types/src/utils.d.ts @@ -274,6 +274,7 @@ export interface UsernameUtils { humanizeStatus: any; } +// TODO: fix type export class DisplayProfile { userId: string; banner?: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92c50e4e5..0a0905955 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -110,6 +110,9 @@ importers: stylelint-config-standard: specifier: ^39.0.1 version: 39.0.1(stylelint@16.25.0(typescript@5.9.3)) + svgo: + specifier: ^4.0.0 + version: 4.0.0 ts-patch: specifier: ^3.3.0 version: 3.3.0 @@ -964,6 +967,9 @@ packages: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -1045,6 +1051,10 @@ packages: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -1075,15 +1085,30 @@ packages: resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} engines: {node: '>=12 || >=16'} + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + css-tree@3.1.0: resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -1177,9 +1202,22 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dompurify@3.1.7: resolution: {integrity: sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==} + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} @@ -1979,6 +2017,9 @@ packages: mathml-tag-names@2.1.3: resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} @@ -2051,6 +2092,9 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -2330,6 +2374,9 @@ packages: safe-regex@1.1.0: resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} + sax@1.4.3: + resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2520,6 +2567,11 @@ packages: svg-tags@1.0.0: resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + svgo@4.0.0: + resolution: {integrity: sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==} + engines: {node: '>=16'} + hasBin: true + table@6.9.0: resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} engines: {node: '>=10.0.0'} @@ -3454,6 +3506,8 @@ snapshots: basic-ftp@5.0.5: {} + boolbase@1.0.0: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -3558,6 +3612,8 @@ snapshots: commander@10.0.1: {} + commander@11.1.0: {} + commander@2.20.3: {} component-emitter@1.3.1: {} @@ -3583,13 +3639,32 @@ snapshots: css-functions-list@3.2.3: {} + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + css-tree@3.1.0: dependencies: mdn-data: 2.12.2 source-map-js: 1.2.1 + css-what@6.2.2: {} + cssesc@3.0.0: {} + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + csstype@3.1.3: {} csstype@3.2.1: {} @@ -3673,8 +3748,26 @@ snapshots: dependencies: esutils: 2.0.3 + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + dompurify@3.1.7: {} + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dot-case@3.0.4: dependencies: no-case: 3.0.4 @@ -4651,6 +4744,8 @@ snapshots: mathml-tag-names@2.1.3: {} + mdn-data@2.0.28: {} + mdn-data@2.12.2: {} meow@13.2.0: {} @@ -4721,6 +4816,10 @@ snapshots: normalize-path@3.0.0: {} + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + object-assign@4.1.1: {} object-copy@0.1.0: @@ -5038,6 +5137,8 @@ snapshots: dependencies: ret: 0.1.15 + sax@1.4.3: {} + semver@6.3.1: {} semver@7.7.1: {} @@ -5317,6 +5418,16 @@ snapshots: svg-tags@1.0.0: {} + svgo@4.0.0: + dependencies: + commander: 11.1.0 + css-select: 5.2.2 + css-tree: 3.1.0 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.4.3 + table@6.9.0: dependencies: ajv: 8.17.1 diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 7f2f5da53..4ea634633 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -26,6 +26,7 @@ import esbuild, { build, context } from "esbuild"; import { constants as FsConstants, readFileSync } from "fs"; import { access, readdir, readFile } from "fs/promises"; import { minify as minifyHtml } from "html-minifier-terser"; +import { optimize as optimizeSvg } from 'svgo'; import { join, relative, resolve } from "path"; import { promisify } from "util"; @@ -278,6 +279,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], @@ -289,7 +296,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"); } diff --git a/src/api/Badges.ts b/src/api/Badges.ts index f909b0e38..c5923cf6c 100644 --- a/src/api/Badges.ts +++ b/src/api/Badges.ts @@ -47,7 +47,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[]; } diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index de62d9b4e..7095ee731 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -31,7 +31,6 @@ import { Margins } from "@utils/margins"; import { shouldShowContributorBadge } from "@utils/misc"; import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal"; import definePlugin from "@utils/types"; -import { User } from "@vencord/discord-types"; import { ContextMenuApi, Forms, Menu, Toasts, UserStore } from "@webpack/common"; const CONTRIBUTOR_BADGE = "https://cdn.discordapp.com/emojis/1092089799109775453.png?size=64"; @@ -88,16 +87,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,$&" @@ -112,13 +108,6 @@ export default definePlugin({ replace: "...$self.getBadgeMouseEventHandlers($1),$&" } ] - }, - { - find: "profileCardUsernameRow,children:", - replacement: { - match: /badges:(\i)(?<=displayProfile:(\i).+?)/, - replace: "badges:[...$self.getBadges($2),...$1]" - } } ], @@ -151,15 +140,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 []; } }, diff --git a/src/plugins/platformIndicators/icons/desktopIcon.svg b/src/plugins/platformIndicators/icons/desktopIcon.svg new file mode 100644 index 000000000..2e531270c --- /dev/null +++ b/src/plugins/platformIndicators/icons/desktopIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/plugins/platformIndicators/icons/embeddedIcon.svg b/src/plugins/platformIndicators/icons/embeddedIcon.svg new file mode 100644 index 000000000..84a81aa10 --- /dev/null +++ b/src/plugins/platformIndicators/icons/embeddedIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/plugins/platformIndicators/icons/mobileIcon.svg b/src/plugins/platformIndicators/icons/mobileIcon.svg new file mode 100644 index 000000000..f8ada0cc2 --- /dev/null +++ b/src/plugins/platformIndicators/icons/mobileIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/plugins/platformIndicators/icons/webIcon.svg b/src/plugins/platformIndicators/icons/webIcon.svg new file mode 100644 index 000000000..2b5519fce --- /dev/null +++ b/src/plugins/platformIndicators/icons/webIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index eb51b5e79..dc6d1332b 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -24,9 +24,14 @@ import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecor import { Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { DiscordPlatform, User } from "@vencord/discord-types"; +import { DiscordPlatform, OnlineStatus, User } from "@vencord/discord-types"; import { filters, findStoreLazy, mapMangledModuleLazy } from "@webpack"; import { AuthenticationStore, PresenceStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; +import desktopIcon from "file://icons/desktopIcon.svg?minify"; +import embeddedIcon from "file://icons/embeddedIcon.svg?minify"; +import mobileIcon from "file://icons/mobileIcon.svg?minify"; +import webIcon from "file://icons/webIcon.svg?minify"; +import type { JSX } from "react"; export interface Session { sessionId: string; @@ -42,42 +47,56 @@ export interface Session { const SessionsStore = findStoreLazy("SessionsStore") as { getSessions(): Record; }; +const { useStatusFillColor } = mapMangledModuleLazy(".concat(.5625*", { + useStatusFillColor: filters.byCode(".hex") +}); -function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) { +function Icon(svg: string, size = 20) { return ({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) => ( - {(tooltipProps: any) => ( - ( + - - + src={"data:image/svg+xml;utf8," + encodeURIComponent(svg.replace("#000000", color))} + height={size - (small ? 3 : 0)} + width={size - (small ? 3 : 0)} + /> )} ); } -const Icons = { - desktop: Icon("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"), - web: Icon("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"), - mobile: Icon("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", { viewBox: "0 0 1000 1500", height: 17, width: 17 }), - embedded: Icon("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", { viewBox: "0 0 50 50" }), -} satisfies Record; +type IconData = { + svg: string, + size: number, + component: (props: { color: string, tooltip: string, small: boolean; }) => JSX.Element; +}; -const { useStatusFillColor } = mapMangledModuleLazy(".concat(.5625*", { - useStatusFillColor: filters.byCode(".hex") -}); +const Icons: Record = { + desktop: iconData(desktopIcon), + web: iconData(webIcon), + mobile: iconData(mobileIcon), + embedded: iconData(embeddedIcon), +}; -const PlatformIcon = ({ platform, status, small }: { platform: DiscordPlatform, status: string; small: boolean; }) => { - const tooltip = platform === "embedded" +function iconData(svg: string, size: number = 20): IconData { + return { + svg, + size, + component: Icon(svg, size), + }; +} + +function getPlatformTooltip(platform: DiscordPlatform): string { + return platform === "embedded" ? "Console" : platform[0].toUpperCase() + platform.slice(1); +} - const Icon = Icons[platform] ?? Icons.desktop; +const PlatformIcon = ({ platform, status, small }: { platform: DiscordPlatform, status: OnlineStatus; small: boolean; }) => { + const tooltip = getPlatformTooltip(platform as DiscordPlatform); + + const Icon = (Icons[platform] ?? Icons.desktop).component; return ; }; @@ -107,6 +126,14 @@ function ensureOwnStatus(user: User) { } function getBadges({ userId }: BadgeUserArgs): ProfileBadge[] { + const colorMap = { + online: useStatusFillColor("online"), + idle: useStatusFillColor("idle"), + dnd: useStatusFillColor("dnd"), + offline: useStatusFillColor("offline"), + streaming: useStatusFillColor("streaming"), + }; + const user = UserStore.getUser(userId); if (!user || user.bot) return []; @@ -116,19 +143,20 @@ function getBadges({ userId }: BadgeUserArgs): ProfileBadge[] { const status = PresenceStore.getClientStatus(user.id); if (!status) return []; - return Object.entries(status).map(([platform, status]) => ({ - component: () => ( - - - - ), - key: `vc-platform-indicator-${platform}` - })); + return Object.entries(status).map(([platform, status]) => { + const tooltip = getPlatformTooltip(platform as DiscordPlatform); + + const icon = Icons[platform as DiscordPlatform] ?? Icons.desktop; + + return { + description: tooltip, + iconSrc: "data:image/svg+xml;utf8," + encodeURIComponent(icon.svg.replace("#000000", colorMap[status] ?? colorMap.offline)), + props: { + style: { width: icon.size, height: icon.size }, + }, + key: `vc-platform-indicator-${platform}`, + } satisfies ProfileBadge; + }); } const PlatformIndicator = ({ user, small = false }: { user: User; small?: boolean; }) => { From 2d5c391902ec1bd50d1d5f0c7958f43429b98f6d Mon Sep 17 00:00:00 2001 From: prism Date: Fri, 19 Dec 2025 21:27:51 +0100 Subject: [PATCH 019/108] UserAreaAPI: fix patch (#580) * UserAreaAPI: fix patch * more uniq --- src/api/UserArea.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/UserArea.tsx b/src/api/UserArea.tsx index 4701d8de8..7f8a3cbb1 100644 --- a/src/api/UserArea.tsx +++ b/src/api/UserArea.tsx @@ -9,7 +9,7 @@ import { Logger } from "@utils/Logger"; import { findComponentByCodeLazy } from "@webpack"; import type { ComponentType, MouseEventHandler, ReactNode } from "react"; -const PanelButton = findComponentByCodeLazy(".NONE,disabled:", ".PANEL_BUTTON") as ComponentType; +const PanelButton = findComponentByCodeLazy("tooltipPositionKey", ".greenTooltip") as ComponentType; export interface UserAreaButtonProps { icon: ReactNode; From 7ac0187a8a3624fc7fde698f269a1152901040ec Mon Sep 17 00:00:00 2001 From: prism Date: Fri, 19 Dec 2025 21:29:06 +0100 Subject: [PATCH 020/108] ContributorModal: move links to footer (#579) * ContributorModal: move links to footer * add better context to reporter * Revert "add better context to reporter" This reverts commit 6f9081b31dae804c289fb7759d7401feff8b069b. --- .../tabs/plugins/ContributorModal.css | 8 +- .../tabs/plugins/ContributorModal.tsx | 100 ++++++++++-------- 2 files changed, 59 insertions(+), 49 deletions(-) diff --git a/src/components/settings/tabs/plugins/ContributorModal.css b/src/components/settings/tabs/plugins/ContributorModal.css index 2727fa878..f35d49f57 100644 --- a/src/components/settings/tabs/plugins/ContributorModal.css +++ b/src/components/settings/tabs/plugins/ContributorModal.css @@ -40,9 +40,13 @@ border-radius: 50%; } +.vc-author-modal-root+[class*="footer"] { + padding: 16px; +} + .vc-author-modal-links { display: flex; - gap: 6px; + gap: 4px; margin-left: auto; } @@ -50,4 +54,4 @@ display: grid; gap: 0.5em; margin-top: 0.75em; -} \ No newline at end of file +} diff --git a/src/components/settings/tabs/plugins/ContributorModal.tsx b/src/components/settings/tabs/plugins/ContributorModal.tsx index 8bdfbbd13..7ae459395 100644 --- a/src/components/settings/tabs/plugins/ContributorModal.tsx +++ b/src/components/settings/tabs/plugins/ContributorModal.tsx @@ -14,8 +14,8 @@ import { Link } from "@components/Link"; import { Paragraph } from "@components/Paragraph"; import { EquicordDevsById, VencordDevsById } from "@utils/constants"; import { fetchUserProfile } from "@utils/discord"; -import { classes, pluralise } from "@utils/misc"; -import { ModalContent, ModalRoot, openModal } from "@utils/modal"; +import { pluralise } from "@utils/misc"; +import { ModalContent, ModalFooter, ModalRoot, openModal } from "@utils/modal"; import { User } from "@vencord/discord-types"; import { showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common"; @@ -30,9 +30,7 @@ export function openContributorModal(user: User) { openModal(modalProps => - - - + ); @@ -67,53 +65,61 @@ function ContributorModal({ user }: { user: User; }) { const ContributedHyperLink = contributed; + const hasLinks = website || githubName; + return ( <> -
    - - {user.username} - -
    - {website && ( - - )} - {githubName && ( - - )} + +
    + + {user.username}
    -
    - {plugins.length ? ( - - {user.username} has {ContributedHyperLink} to {pluralise(plugins.length, "plugin")}! - - ) : ( - - {user.username} has not made any plugins. They likely {ContributedHyperLink} in other ways! - - )} + {plugins.length ? ( + + {user.username} has {ContributedHyperLink} to {pluralise(plugins.length, "plugin")}! + + ) : ( + + {user.username} has not made any plugins. They likely {ContributedHyperLink} in other ways! + + )} - {!!plugins.length && ( -
    - {plugins.map(p => - showToast("Restart to apply changes!")} - /> - )} -
    + {!!plugins.length && ( +
    + {plugins.map(p => + showToast("Restart to apply changes!")} + /> + )} +
    + )} + + + {hasLinks && ( + +
    + {website && ( + + )} + {githubName && ( + + )} +
    +
    )} ); From a61e7ece756bbe8a52cb072d46b75a98b02c96cd Mon Sep 17 00:00:00 2001 From: Etorix <92535668+EtorixDev@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:15:39 -0800 Subject: [PATCH 021/108] SMYN & Questify: Fix patches. (#581) * Fix patches. * Update. * Update. * Lint. --- src/equicordplugins/questify/index.tsx | 4 ++-- src/plugins/showMeYourName/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/equicordplugins/questify/index.tsx b/src/equicordplugins/questify/index.tsx index 16a3412dc..8be753c19 100644 --- a/src/equicordplugins/questify/index.tsx +++ b/src/equicordplugins/questify/index.tsx @@ -1012,11 +1012,11 @@ export default definePlugin({ { // Hides the Quest icon from members list items when // a user is playing a game tied to an active Quest. - find: "HANG_STATUS});", + find: "),\"activity-\".concat", group: true, replacement: [ { - match: /(?=if\(\(0,.{0,30}?isBlockedOrIgnored)/, + match: /(?<=voiceActivityChannel:\i\?\i:null}\);)/, replace: "const shouldHideMembersListActivelyPlayingIcon=$self.shouldHideMembersListActivelyPlayingIcon();" }, { diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index 29e998867..7c798952c 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -969,7 +969,7 @@ export default definePlugin({ // Tags *should* contain the guild ID nested in its structure, but on the first time // loading a guild member's preview profile, it will be undefined. This patch bypasses // that by passing the guild ID as its own prop. - find: ".hasAvatarForGuild(null==", + find: "\"UserProfilePopoutBody\"}", replacement: { match: /(pronouns,tags:)/, replace: "pronouns,guildId:arguments[0]?.guild?.id??null,tags:" @@ -977,7 +977,7 @@ export default definePlugin({ }, { // Same as above, but for bot members. - find: "BotUserProfilePopoutBody\"}", + find: "popularApplicationCommandIds)!=null&&null", replacement: { match: /(pronouns,tags:)/, replace: "pronouns,guildId:arguments[0]?.guild?.id??null,tags:" From bc769692dce907b845106fd07f75d97e6e0598b0 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:52:37 -0500 Subject: [PATCH 022/108] Questify: Remove Patch --- src/equicordplugins/questify/index.tsx | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/equicordplugins/questify/index.tsx b/src/equicordplugins/questify/index.tsx index 8be753c19..c39f3de76 100644 --- a/src/equicordplugins/questify/index.tsx +++ b/src/equicordplugins/questify/index.tsx @@ -925,7 +925,7 @@ function getQuestAcceptedButtonProps(quest: Quest, text: string, disabled: boole function isIncompatibleActivity(quest: Quest): boolean { return !!Object.keys(quest.config.taskConfigV2?.tasks || {}).some(taskType => { - return "ACHIEVEMENT_IN_ACTIVITY" === taskType; + return taskType === "ACHIEVEMENT_IN_ACTIVITY"; }); } @@ -1025,22 +1025,6 @@ export default definePlugin({ } ] }, - { - // Same as above, probably? Not sure when - // each function is used, so patching both. - find: '"StackedActivityStatus"})', - group: true, - replacement: [ - { - match: /(?=if\(\i\)return null;let \i=function)/, - replace: "const shouldHideMembersListActivelyPlayingIcon=$self.shouldHideMembersListActivelyPlayingIcon();" - }, - { - match: /(?<=\i\(\),\i&&)/, - replace: "!shouldHideMembersListActivelyPlayingIcon&&" - } - ] - }, { // Hides the Quest badge on user profiles. find: ".MODAL]:26", From 6ad57d76c1aab9e4ed907cdfc29a27e640f52e16 Mon Sep 17 00:00:00 2001 From: prism Date: Fri, 19 Dec 2025 23:01:17 +0100 Subject: [PATCH 023/108] IconViewer: refactor (#582) * IconViewer: refactor * t --- src/equicordplugins/iconViewer/IconModal.tsx | 112 ------ src/equicordplugins/iconViewer/IconsTab.css | 122 ------- src/equicordplugins/iconViewer/IconsTab.tsx | 89 ----- .../iconViewer/components/IconsTab.tsx | 132 +++++++ .../iconViewer/components/Modals.tsx | 331 ++++++++++++++++++ src/equicordplugins/iconViewer/index.tsx | 17 +- src/equicordplugins/iconViewer/names.ts | 10 +- src/equicordplugins/iconViewer/rawModal.tsx | 69 ---- src/equicordplugins/iconViewer/saveModal.tsx | 154 -------- src/equicordplugins/iconViewer/styles.css | 140 ++++++++ .../iconViewer/subComponents.tsx | 121 ------- src/equicordplugins/iconViewer/types.ts | 17 + src/equicordplugins/iconViewer/utils.ts | 49 +++ src/equicordplugins/iconViewer/utils.tsx | 106 ------ 14 files changed, 682 insertions(+), 787 deletions(-) delete mode 100644 src/equicordplugins/iconViewer/IconModal.tsx delete mode 100644 src/equicordplugins/iconViewer/IconsTab.css delete mode 100644 src/equicordplugins/iconViewer/IconsTab.tsx create mode 100644 src/equicordplugins/iconViewer/components/IconsTab.tsx create mode 100644 src/equicordplugins/iconViewer/components/Modals.tsx delete mode 100644 src/equicordplugins/iconViewer/rawModal.tsx delete mode 100644 src/equicordplugins/iconViewer/saveModal.tsx create mode 100644 src/equicordplugins/iconViewer/styles.css delete mode 100644 src/equicordplugins/iconViewer/subComponents.tsx create mode 100644 src/equicordplugins/iconViewer/types.ts create mode 100644 src/equicordplugins/iconViewer/utils.ts delete mode 100644 src/equicordplugins/iconViewer/utils.tsx diff --git a/src/equicordplugins/iconViewer/IconModal.tsx b/src/equicordplugins/iconViewer/IconModal.tsx deleted file mode 100644 index 98556ab44..000000000 --- a/src/equicordplugins/iconViewer/IconModal.tsx +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { CodeBlock } from "@components/CodeBlock"; -import { Margins } from "@utils/margins"; -import { classes } from "@utils/misc"; -import { - ModalCloseButton, - ModalContent, - ModalFooter, - ModalHeader, - ModalProps, - ModalRoot, - ModalSize, - openModal -} from "@utils/modal"; -import * as t from "@vencord/discord-types"; -import { Button, FluxDispatcher, TooltipContainer, useCallback, useEffect, useState } from "@webpack/common"; - -import { IconsFinds } from "./names"; -import { openRawModal } from "./rawModal"; -import { openSaveModal } from "./saveModal"; -import { ModalHeaderTitle } from "./subComponents"; -import { _cssColors, cssColors, iconSizes } from "./utils"; - -const defaultColor = 209; - - -function ModalComponent(props: { iconName: string; Icon: t.Icon; } & ModalProps) { - const [color, SetColor] = useState(defaultColor); - - const onKeyDown = useCallback((e: KeyboardEvent) => { - if (e.key === "ArrowLeft" || e.key === "ArrowRight") { - e.preventDefault(); - if (e.key === "ArrowLeft") { - SetColor(color + -1); - } else if (e.key === "ArrowRight") { - SetColor(color + 1); - } - } - }, [color]); - - const onColorChange = useCallback((e: { type: string; color: string; }) => { - SetColor(_cssColors.indexOf(e.color)); - }, [color]); - - useEffect(() => { - document.addEventListener("keydown", onKeyDown); - // @ts-ignore - FluxDispatcher.subscribe("ICONVIEWER_COLOR_CHANGE", onColorChange); - return () => { - document.removeEventListener("keydown", onKeyDown); - // @ts-ignore - FluxDispatcher.unsubscribe("ICONVIEWER_COLOR_CHANGE", onColorChange); - }; - }, [onKeyDown]); - if (color < 0 || color >= cssColors.length) { - SetColor(0); - } - const { iconName, Icon } = props; - return ( - - - - - - {IconsFinds[iconName] ? -
    - -
    - : null - } -
    -
    - -
    -
    - {iconSizes.map((size, idx) => - - - - )} -
    -
    -
    - - - - -
    ); -} - -export function openIconModal(iconName: string, Icon: t.Icon) { - openModal(props => ); -} - diff --git a/src/equicordplugins/iconViewer/IconsTab.css b/src/equicordplugins/iconViewer/IconsTab.css deleted file mode 100644 index 38daa42ec..000000000 --- a/src/equicordplugins/iconViewer/IconsTab.css +++ /dev/null @@ -1,122 +0,0 @@ -.vc-icon-modal-codeblock { - margin-left: 10%; - margin-top: 30px; -} - -.vc-icon-icon { - margin-left: 5%; - color: var(--interactive-icon-default); - transition: transform 0.15s ease, color 0.15s ease; -} - -.vc-icon-container:hover .vc-icon-icon { - transform: scale(1.020); - filter: brightness(1.5); -} - -.vc-icon-modal-main-container { - display: flex; -} - -.vc-ic-unordered-list li { - margin-left: 5%; - list-style: disc; -} - -.vc-icon-other-icon-sizes { - height: 32px; - display: flex; - margin-top: 15%; - margin-left: 5%; -} - -.vc-ic-icon-modal-root { - height: 450px; - width: 700px; -} - -.vc-icons-tab-grid-container { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); - gap: 8px; -} - -.vc-icon-modal-size-ex-icon { - margin-right: 5%; -} - -.vc-icon-modal-icon { - height: 164px; - width: 164px; -} - -.vc-icon-tab-search-bar-grid { - display: grid; - height: 50px; - gap: 10px; - grid-template-columns: 1fr 10px; -} - -.vc-icon-display-box { - height: 164px; - width: 164px; - margin-top: 5%; - margin-left: 15%; - background-image: repeating-linear-gradient(45deg, - #ffffff1a 0, - #ffffff1a 10px, - #0000001a 10px, - #0000001a 20px); - border-radius: 10px; -} - -.vc-icon-container { - margin-top: 5px; - padding: 15px; - border-radius: 5px; - border: 3px solid transparent; - box-sizing: border-box; -} - -.vc-icon-container:hover { - border-radius: 5px; - border: 3px solid var(--background-base-low); - box-sizing: border-box; -} - -.vc-icon-title { - font-size: 0.8em; - margin-top: 0; - text-align: center; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.vc-icon-modal-color-tooltip:hover { - background-color: var(--background-feedback-info); -} - -.vc-save-modal { - margin-top: 10%; - display: grid; - grid-template-columns: auto 1fr; - gap: 10px; -} - -.vc-save-select-option-1 { - margin-bottom: 5%; -} - -.vc-save-select-option-2 { - margin-top: 5%; -} - -.vc-ic-modals-root { - border-radius: 25px; -} - -.vc-ic-modals-footer { - border-bottom-left-radius: 25px; - border-bottom-right-radius: 25px; -} diff --git a/src/equicordplugins/iconViewer/IconsTab.tsx b/src/equicordplugins/iconViewer/IconsTab.tsx deleted file mode 100644 index a67bde976..000000000 --- a/src/equicordplugins/iconViewer/IconsTab.tsx +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2022 Vendicated and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ - -import "./IconsTab.css"; - -import { Heading } from "@components/Heading"; -import { SettingsTab, wrapTab } from "@components/settings"; -import { Margins } from "@utils/margins"; -import { classes } from "@utils/misc"; -import * as t from "@vencord/discord-types"; -import { Button, Clickable, React, TextInput, TooltipContainer } from "@webpack/common"; - -import { openIconModal } from "./IconModal"; -import { getNameByIcon } from "./names"; -import { findAllIcons, IconsDef } from "./utils"; - -export let Icons: IconsDef | null = null; - -function searchMatch(search: string, name: string, Icon: t.Icon, searchbyFunction: boolean): boolean { - if (search === "") return true; - if (searchbyFunction) { - return String(Icon).includes(search); - } - const words = name.replace(/([A-Z]([a-z]+)?)/g, " $1").toLowerCase().split(" "); - const searchKeywords = search.toLowerCase().split(" "); - return searchKeywords.every(keyword => words.includes(keyword)) || words.every(keyword => searchKeywords.includes(keyword)) || name.toLowerCase().includes(search.toLowerCase()); -} - - -function RenderIcons({ search, searchbyFunction }: { search: string; searchbyFunction: boolean; }) { - if (Icons === null) { - const OrgIcons = Array.from(new Set(findAllIcons())); - Icons = Object.fromEntries(Object.keys(OrgIcons).map(k => [String(getNameByIcon(OrgIcons[k], k)), OrgIcons[k]])) as IconsDef; - } - return
    - {Object.entries(Icons).map(([iconName, Icon], index) => - searchMatch(search, iconName, Icon, searchbyFunction) && -
    - openIconModal(iconName, Icon)}> -
    - -
    -
    - {iconName} -
    -
    - )}
    ; -} - -function IconsTab() { - const [search, setSearch] = React.useState(""); - const [searchByFunction, setSearchByFunction] = React.useState(false); - const MemoRenderIcons = React.memo(RenderIcons); - - return ( - -
    - - - - -
    - -
    - ); -} - -export default wrapTab(IconsTab, "IconsTab"); diff --git a/src/equicordplugins/iconViewer/components/IconsTab.tsx b/src/equicordplugins/iconViewer/components/IconsTab.tsx new file mode 100644 index 000000000..6f846dabc --- /dev/null +++ b/src/equicordplugins/iconViewer/components/IconsTab.tsx @@ -0,0 +1,132 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "../styles.css"; + +import { Button } from "@components/Button"; +import { Heading } from "@components/Heading"; +import { SettingsTab, wrapTab } from "@components/settings"; +import { debounce } from "@shared/debounce"; +import { Margins } from "@utils/margins"; +import { classes } from "@utils/misc"; +import { useIntersection } from "@utils/react"; +import { Icon } from "@vencord/discord-types"; +import { Clickable, TextInput, TooltipContainer, useCallback, useEffect, useMemo, useState } from "@webpack/common"; + +import { getNameByIcon, IconsFinds } from "../names"; +import { IconsDef } from "../types"; +import { findAllIcons } from "../utils"; +import { openIconModal } from "./Modals"; + +let cachedIcons: IconsDef | null = null; + +function getIcons(): IconsDef { + if (cachedIcons) return cachedIcons; + + const allIcons = Array.from(new Set(findAllIcons())); + cachedIcons = Object.fromEntries( + allIcons.map((icon, idx) => [getNameByIcon(icon, String(idx)), icon]) + ); + return cachedIcons; +} + +function searchMatch(search: string, name: string, Icon: Icon, searchByFunction: boolean): boolean { + if (!search) return true; + + if (searchByFunction) return String(Icon).includes(search); + + const words = name.replace(/([A-Z]([a-z]+)?)/g, " $1").toLowerCase().split(" "); + const keywords = search.toLowerCase().split(" "); + return keywords.every(k => words.includes(k)) || + words.every(w => keywords.includes(w)) || + name.toLowerCase().includes(search.toLowerCase()); +} + +function IconItem({ iconName, Icon }: { iconName: string; Icon: Icon; }) { + return ( +
    + openIconModal(iconName, Icon, IconsFinds[iconName])}> +
    + +
    +
    + {iconName} +
    + ); +} + +function IconsTab() { + const [searchInput, setSearchInput] = useState(""); + const [search, setSearch] = useState(""); + const [searchByFunction, setSearchByFunction] = useState(false); + + const icons = useMemo(() => getIcons(), []); + + const debouncedSetSearch = useMemo( + () => debounce((query: string) => setSearch(query), 150), + [] + ); + + const onSearch = useCallback((query: string) => { + setSearchInput(query); + debouncedSetSearch(query); + }, [debouncedSetSearch]); + + const filteredIcons = useMemo(() => + Object.entries(icons).filter(([name, Icon]) => searchMatch(search, name, Icon, searchByFunction)), + [icons, search, searchByFunction] + ); + + const iconsToLoad = 120; + const [visibleCount, setVisibleCount] = useState(iconsToLoad); + + useEffect(() => { + setVisibleCount(iconsToLoad); + }, [search, searchByFunction]); + + const loadMore = useCallback(() => { + setVisibleCount(v => Math.min(v + iconsToLoad, filteredIcons.length)); + }, [filteredIcons.length]); + + const [sentinelRef, isSentinelVisible] = useIntersection(); + + useEffect(() => { + if (isSentinelVisible && visibleCount < filteredIcons.length) { + loadMore(); + } + }, [isSentinelVisible, visibleCount, filteredIcons.length, loadMore]); + + const visibleIcons = filteredIcons.slice(0, visibleCount); + + return ( + +
    + + + + +
    +
    + {visibleIcons.map(([iconName, Icon]) => ( + + ))} +
    + {visibleCount < filteredIcons.length && ( +
    + )} + + ); +} + +export default wrapTab(IconsTab, "IconsTab"); diff --git a/src/equicordplugins/iconViewer/components/Modals.tsx b/src/equicordplugins/iconViewer/components/Modals.tsx new file mode 100644 index 000000000..10ae4547e --- /dev/null +++ b/src/equicordplugins/iconViewer/components/Modals.tsx @@ -0,0 +1,331 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { BaseText } from "@components/BaseText"; +import { Button } from "@components/Button"; +import { Flex } from "@components/Flex"; +import { HeadingSecondary } from "@components/Heading"; +import { Paragraph } from "@components/Paragraph"; +import { copyWithToast, getIntlMessage } from "@utils/discord"; +import { Logger } from "@utils/Logger"; +import { + ModalContent, + ModalFooter, + ModalHeader, + ModalProps, + ModalRoot, + ModalSize, + openModal +} from "@utils/modal"; +import { saveFile } from "@utils/web"; +import { Icon } from "@vencord/discord-types"; +import { findComponentByCodeLazy } from "@webpack"; +import { + ContextMenuApi, + createRoot, + FluxDispatcher, + Menu, + ReactDOM, + TooltipContainer, + useCallback, + useEffect, + useMemo, + useState +} from "@webpack/common"; + +import { cssColors, getCssColorKeys, iconSizes, iconSizesInPx } from "../utils"; + +const logger = new Logger("IconViewer"); +const CloseButton = findComponentByCodeLazy("CLOSE_BUTTON_LABEL"); +const BugIcon = findComponentByCodeLazy("1.1.27.1.37 0a6.66 6.6"); + +const FORMAT_EXTENSIONS: Record = { + "image/png": "png", + "image/jpeg": "jpeg", + "image/gif": "gif", + "image/webp": "webp", + "image/svg+xml": "svg", + "image/avif": "avif" +}; + +function useColorNavigation(initialColor: number) { + const [color, setColor] = useState(initialColor); + + const onKeyDown = useCallback((e: KeyboardEvent) => { + if (e.key === "ArrowLeft" || e.key === "ArrowRight") { + e.preventDefault(); + setColor(c => { + const next = c + (e.key === "ArrowLeft" ? -1 : 1); + const max = getCssColorKeys().length; + return next < 0 ? max - 1 : next >= max ? 0 : next; + }); + } + }, []); + + const onColorChange = useCallback((e: { color: string; }) => { + const keys = getCssColorKeys(); + const idx = keys.indexOf(e.color); + if (idx !== -1) setColor(idx); + }, []); + + useEffect(() => { + document.addEventListener("keydown", onKeyDown); + FluxDispatcher.subscribe("ICONVIEWER_COLOR_CHANGE", onColorChange); + return () => { + document.removeEventListener("keydown", onKeyDown); + FluxDispatcher.unsubscribe("ICONVIEWER_COLOR_CHANGE", onColorChange); + }; + }, [onKeyDown, onColorChange]); + + return [color, setColor] as const; +} + +function ColorContextMenu({ colorKeys }: { colorKeys: string[]; }) { + const [query, setQuery] = useState(""); + const filtered = colorKeys.filter(k => + !query || k.toLowerCase().includes(query.toLowerCase()) + ); + + return ( + FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })} + aria-label="Icon Viewer Colors" + > + ( + + )} + /> + + {filtered.map(colorKey => ( + FluxDispatcher.dispatch({ type: "ICONVIEWER_COLOR_CHANGE", color: colorKey })} + /> + ))} + + ); +} + +function convertToHtml(component: React.ReactElement): string { + const container = document.createElement("div"); + const root = createRoot(container); + ReactDOM.flushSync(() => root.render(component)); + const content = container.innerHTML; + root.unmount(); + return content; +} + +function saveIcon(iconName: string, icon: Element | string, color: number, size: number, type: string) { + const colorName = cssColors[color]?.name ?? "unknown"; + const ext = FORMAT_EXTENSIONS[type] ?? "png"; + const filename = `${iconName}-${colorName}-${size}px.${ext}`; + + if (typeof icon === "string") { + saveFile(new File([icon], filename, { type: "text/plain" })); + return; + } + + for (const el of icon.children) { + const fill = el.getAttribute("fill"); + if (fill?.startsWith("var(")) { + el.setAttribute("fill", getComputedStyle(icon).getPropertyValue(fill.slice(4, -1))); + } + } + + const canvas = document.createElement("canvas"); + canvas.width = size; + canvas.height = size; + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + const img = new Image(); + img.onload = () => { + ctx.drawImage(img, 0, 0, size, size); + const link = document.createElement("a"); + link.download = filename; + link.href = canvas.toDataURL(type); + link.click(); + }; + img.src = `data:image/svg+xml;base64,${btoa(icon.outerHTML)}`; +} + +function OtherContextMenu({ iconName, Icon, color, findCode }: { iconName: string; Icon: Icon; color: number; findCode: string | null; }) { + const colorData = cssColors[color]; + + const handleSave = (type: string) => { + const size = iconSizesInPx.lg; + const iconEl = type === "image/svg+xml" + ? convertToHtml() + : document.querySelector(".vc-ic-icon-preview .vc-ic-icon-large") as Element | null; + + if (iconEl) saveIcon(iconName, iconEl, color, size, type); + }; + + return ( + FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })} + aria-label="Icon Options" + > + logger.info(Icon)} + /> + + handleSave("image/png")} + /> + handleSave("image/svg+xml")} + /> + handleSave("image/jpeg")} + /> + handleSave("image/webp")} + /> + handleSave("image/gif")} + /> + handleSave("image/avif")} + /> + + + ); +} + +function IconModal({ iconName, Icon, findPattern, onClose, transitionState }: { iconName: string; Icon: Icon; findPattern?: string; } & ModalProps) { + const [color, setColor] = useColorNavigation(209); + const colorData = cssColors[color]; + const colorKeys = useMemo(() => getCssColorKeys(), []); + + const findCode = findPattern + ? `const ${iconName}Icon = findComponentByCode(${JSON.stringify(findPattern)})` + : null; + + const openColorMenu = (e: React.MouseEvent) => { + ContextMenuApi.openContextMenu(e, () => ); + }; + + const onWheel = useCallback((e: React.WheelEvent) => { + e.preventDefault(); + const max = colorKeys.length; + setColor(c => { + const next = c + (e.deltaY > 0 ? 1 : -1); + return next < 0 ? max - 1 : next >= max ? 0 : next; + }); + }, [colorKeys.length, setColor]); + + const openOtherMenu = (e: React.MouseEvent) => { + ContextMenuApi.openContextMenu(e, () => ( + + )); + }; + + return ( + + +
    + {iconName} +
    +
    + +
    +
    + + +
    + +
    + + + {iconSizes.map(size => ( + + + + ))} + + + + {colorData?.name} + + + +
    +
    + + + + +
    + ); +} + +export function openIconModal(iconName: string, Icon: Icon, findPattern?: string) { + openModal(props => ); +} + +export function SettingsAbout() { + return ( + <> + Features + +
      +
    • Preview icons
    • +
    • Copy icon names and CSS variables
    • +
    • Download icons in different formats (SVG, PNG, GIF, etc.)
    • +
    • Copy pre-made icon finds for your plugins
    • +
    • Find icons by function context
    • +
    • Search for colors by right-clicking the color name
    • +
    +
    + Special thanks + +
      +
    • krystalskullofficial._.
    • +
    • davr1
    • +
    • suffocate
    • +
    +
    + + ); +} diff --git a/src/equicordplugins/iconViewer/index.tsx b/src/equicordplugins/iconViewer/index.tsx index 738d9c248..f79651cfa 100644 --- a/src/equicordplugins/iconViewer/index.tsx +++ b/src/equicordplugins/iconViewer/index.tsx @@ -1,6 +1,6 @@ /* * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors + * Copyright (c) 2025 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ @@ -10,13 +10,12 @@ import { EquicordDevs } from "@utils/constants"; import definePlugin, { StartAt } from "@utils/types"; import { openUserSettingsPanel } from "@webpack/common"; -import IconsTab from "./IconsTab"; -import { SettingsAbout } from "./subComponents"; - +import IconsTab from "./components/IconsTab"; +import { SettingsAbout } from "./components/Modals"; export default definePlugin({ name: "IconViewer", - description: "Adds a new tab to settings, to preview all icons", + description: "Adds a new tab to settings to preview all icons.", authors: [EquicordDevs.iamme], dependencies: ["Settings"], startAt: StartAt.WebpackReady, @@ -46,9 +45,9 @@ export default definePlugin({ }, stop() { const { customEntries, customSections } = SettingsPlugin; - const entry = customEntries.findIndex(entry => entry.key === "equicord_icon_viewer"); - const section = customSections.findIndex(section => section({} as any).id === "IconViewer"); - if (entry !== -1) customEntries.splice(entry, 1); - if (section !== -1) customSections.splice(section, 1); + const entryIdx = customEntries.findIndex(e => e.key === "equicord_icon_viewer"); + const sectionIdx = customSections.findIndex(s => s({} as any).id === "IconViewer"); + if (entryIdx !== -1) customEntries.splice(entryIdx, 1); + if (sectionIdx !== -1) customSections.splice(sectionIdx, 1); }, }); diff --git a/src/equicordplugins/iconViewer/names.ts b/src/equicordplugins/iconViewer/names.ts index 70cddc34f..d1ef744ed 100644 --- a/src/equicordplugins/iconViewer/names.ts +++ b/src/equicordplugins/iconViewer/names.ts @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import * as t from "@vencord/discord-types"; +import { Icon } from "@vencord/discord-types"; // name: pattern -export const IconsFinds = { +export const IconsFinds: Record = { Discord: "1.6 5.64-2.87", XboxNeutral: "8.68-.62c.89-.81 1.5", PlaystationNeutral: "2.04Zm-9.35", @@ -137,12 +137,12 @@ export const IconsFinds = { // 13l4.91-8.05a1.8 -export const namePatterns = new Map(Object.entries(IconsFinds).map(([name, pattern]) => [name, pattern])); +const namePatterns = new Map(Object.entries(IconsFinds)); -export function getNameByIcon(Icon: t.Icon, defaultName: any) { +export function getNameByIcon(Icon: Icon, defaultName: string) { for (const [name, pattern] of namePatterns) { if (String(Icon).includes(pattern)) { - namePatterns.delete(name); // remove pattern from map after being found prevent overshadowing + namePatterns.delete(name); return name; } } diff --git a/src/equicordplugins/iconViewer/rawModal.tsx b/src/equicordplugins/iconViewer/rawModal.tsx deleted file mode 100644 index 70edebbcb..000000000 --- a/src/equicordplugins/iconViewer/rawModal.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { CodeBlock } from "@components/CodeBlock"; -import { copyToClipboard } from "@utils/clipboard"; -import { Margins } from "@utils/margins"; -import { classes } from "@utils/misc"; -import { - ModalCloseButton, - ModalContent, - ModalFooter, - ModalHeader, - ModalProps, - ModalRoot, - ModalSize, - openModal -} from "@utils/modal"; -import * as t from "@vencord/discord-types"; -import { Button, Toasts } from "@webpack/common"; - -import { ModalHeaderTitle } from "./subComponents"; - - - -function ModalComponent(props: { func: Function; iconName: string; color: number; } & ModalProps) { - const { func, iconName, color } = props; - return ( - - - - - -
    - -
    -
    - - - - -
    ); -} - -export function openRawModal(iconName: string, Icon: t.Icon, colorIndex: number) { - openModal(props => ); -} - diff --git a/src/equicordplugins/iconViewer/saveModal.tsx b/src/equicordplugins/iconViewer/saveModal.tsx deleted file mode 100644 index 24064e3de..000000000 --- a/src/equicordplugins/iconViewer/saveModal.tsx +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { Paragraph } from "@components/Paragraph"; -import { - ModalCloseButton, - ModalContent, - ModalFooter, - ModalHeader, - ModalProps, - ModalRoot, - ModalSize, - openModal -} from "@utils/modal"; -import * as t from "@vencord/discord-types"; -import { Button, Select, TextInput, useCallback, useEffect, useState } from "@webpack/common"; - -import { ModalHeaderTitle } from "./subComponents"; -import { convertComponentToHtml, cssColors, iconSizesInPx, saveIcon } from "./utils"; - -type IDivElement = React.DetailedHTMLProps, HTMLDivElement>; - -export function NumericComponent({ onChange, value, className, style }: { onChange: (value: number) => void, value: number; className?: string; style?: React.CSSProperties; }) { - const handleChange = (value: string) => { - const newValue = Number(value); - if (isNaN(newValue)) return; - onChange(newValue); - }; - - return ( -
    - -
    - ); -} - -export function SelectComponent({ option, onChange, onError, className }: IDivElement & { option: any, onChange: (value: any) => void, onError: (msg: string | null) => void; className?: string; }) { - const [state, setState] = useState(option.options?.find(o => o.default)?.value ?? null); - const [error, setError] = useState(null); - - useEffect(() => onError(error), [error]); - - const handleChange = (newValue: any) => { - const isValid = option.isValid?.call({}, newValue) ?? true; - if (!isValid) setError("Invalid input provided."); - else { - setError(null); - setState(newValue); - onChange(newValue); - } - }; - - return (
    - v === localStorage.Vencord_cloudSyncDirection} + serialize={v => String(v)} + select={v => { + localStorage.Vencord_cloudSyncDirection = v; + }} + closeOnSelect={true} + /> +
    + + + putCloudSettings(true)} + Icon={CloudUploadIcon} + > + Upload Settings + + + {({ onMouseLeave, onMouseEnter }) => ( + getCloudSettings(true, true)} + Icon={CloudDownloadIcon} + > + Download Settings + + )} + + + + + ); +} + +function ResetSection() { + const { authenticated, settingsSync } = useSettings(["cloud.authenticated", "cloud.settingsSync"]).cloud; + + return ( +
    + + + + deleteCloudSettings()} + Icon={DeleteIcon} > - Delete Cloud Settings - -
    + Delete Settings from Cloud + + Alerts.show({ + title: "Are you sure?", + body: "Once your data is erased, we cannot recover it. There's no going back!", + onConfirm: eraseAllCloudData, + confirmText: "Erase it!", + confirmColor: "vc-cloud-erase-data-danger-btn", + cancelText: "Nevermind" + })} + Icon={DeleteIcon} + > + Delete your Cloud Account + + ); } function CloudTab() { - const settings = useSettings(["cloud.authenticated", "cloud.url"]); - return ( -
    - - Vencord comes with a cloud integration that adds goodies like settings sync across devices. - It respects your privacy, and - the source code is AGPL 3.0 licensed so you - can host it yourself. - - { - if (v) - authorizeCloud(); - else - settings.cloud.authenticated = v; - }} - /> - Backend URL - - Which backend to use when using cloud integrations. - - { - settings.cloud.url = v; - settings.cloud.authenticated = false; - deauthorizeCloud(); - }} - validate={validateUrl} - /> - - - - - - - -
    - + + + + + + +
    ); } From 59eb0579951ae337b0c90af1cb7ebf3407df287d Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 20 Dec 2025 01:25:57 +0100 Subject: [PATCH 026/108] bump to v1.13.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 68b88fb10..8ac6ace26 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.13.9", + "version": "1.13.10", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From e5df9b394e1183bd76fe0b09efda228063ad4dca Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 20 Dec 2025 01:44:49 +0100 Subject: [PATCH 027/108] fix PlatformIndicators --- package.json | 2 +- scripts/build/common.mjs | 4 ++-- src/plugins/platformIndicators/icons/desktopIcon.svg | 2 +- src/plugins/platformIndicators/icons/embeddedIcon.svg | 2 +- src/plugins/platformIndicators/icons/mobileIcon.svg | 2 +- src/plugins/platformIndicators/icons/webIcon.svg | 2 +- src/plugins/platformIndicators/index.tsx | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 8ac6ace26..32340a1f5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.13.10", + "version": "1.13.11", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 4ea634633..26866558e 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -257,7 +257,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"; @@ -281,7 +281,7 @@ export const fileUrlPlugin = { }); } else if (path.endsWith(".svg")) { content = optimizeSvg(await readFile(path, "utf-8"), { - datauri: base64 ? 'base64' : void 0, + datauri: base64 ? "base64" : void 0, multipass: true, floatPrecision: 2, }).data; diff --git a/src/plugins/platformIndicators/icons/desktopIcon.svg b/src/plugins/platformIndicators/icons/desktopIcon.svg index 2e531270c..df7ac3312 100644 --- a/src/plugins/platformIndicators/icons/desktopIcon.svg +++ b/src/plugins/platformIndicators/icons/desktopIcon.svg @@ -1,3 +1,3 @@ - + diff --git a/src/plugins/platformIndicators/icons/embeddedIcon.svg b/src/plugins/platformIndicators/icons/embeddedIcon.svg index 84a81aa10..ce6f62ddb 100644 --- a/src/plugins/platformIndicators/icons/embeddedIcon.svg +++ b/src/plugins/platformIndicators/icons/embeddedIcon.svg @@ -1,3 +1,3 @@ - + diff --git a/src/plugins/platformIndicators/icons/mobileIcon.svg b/src/plugins/platformIndicators/icons/mobileIcon.svg index f8ada0cc2..28b23a522 100644 --- a/src/plugins/platformIndicators/icons/mobileIcon.svg +++ b/src/plugins/platformIndicators/icons/mobileIcon.svg @@ -1,3 +1,3 @@ - + diff --git a/src/plugins/platformIndicators/icons/webIcon.svg b/src/plugins/platformIndicators/icons/webIcon.svg index 2b5519fce..82627ca90 100644 --- a/src/plugins/platformIndicators/icons/webIcon.svg +++ b/src/plugins/platformIndicators/icons/webIcon.svg @@ -1,3 +1,3 @@ - + diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index dc6d1332b..def3e5c94 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -57,7 +57,7 @@ function Icon(svg: string, size = 20) { {tooltipProps => ( @@ -150,7 +150,7 @@ function getBadges({ userId }: BadgeUserArgs): ProfileBadge[] { return { description: tooltip, - iconSrc: "data:image/svg+xml;utf8," + encodeURIComponent(icon.svg.replace("#000000", colorMap[status] ?? colorMap.offline)), + iconSrc: "data:image/svg+xml;utf8," + encodeURIComponent(icon.svg.replace("#123456", colorMap[status] ?? colorMap.offline)), props: { style: { width: icon.size, height: icon.size }, }, From ce8e48bb6db20476ecc743a4002b44af42e5cec5 Mon Sep 17 00:00:00 2001 From: V Date: Sat, 20 Dec 2025 01:02:43 +0100 Subject: [PATCH 028/108] 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 649612759..77272d41c 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 1f2d5ec17..2489f8238 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 2b51ebabf..fff893ba2 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 5b0b3e376..ffe34adda 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 e8269477a..6cbb16446 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 dcef98aa8..d451e9f28 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 048b30c7d..e30f83e83 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 dca7679ef..5b6ef6767 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 fb40f72d8..967cef640 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 9fdfdba5f..dca7d8c09 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 1abddf7a9..7ce49d69f 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 b4390c232..2cfbd83d2 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 be32d978d..dfa6caaa0 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 df630ca75..2c1d8353e 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 51151b1fe..000000000 --- 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 24138d2a8..1ac7aceb4 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 0dc69d6f8..22413da47 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 f4acbb69f..49b893a5a 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 16c3cac05..cf03f4208 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 4794a0846..31ed77f32 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 2a3109b82..46c9132c7 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 df85ef738..1e4600ac2 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 c7c2471ab..0280714f3 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 e28d32021..92fd34541 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 02554b680..2f3a812bc 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 6990cea90..272da0905 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 8868caa76..09a646a6f 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 428752021..08432ddff 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 7035387ec..e600681fd 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 019cc431b..bdca847d2 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 62040508f..44e58eccf 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 7ca83a6d3..f69ab4b37 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 e5dac9d02..ae9c17936 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 5c49d72a7..4da925a59 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 c3f616727..c9280b1b4 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 e18ad2ab9..4f2df28bd 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 916adc45e..ec7609c50 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 4e7b8bf00..9f49d5a3a 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 ac57e9764..0ed5548c9 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 ee782ec05..1b2fd27c5 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 dd17ebac0..6d4a4238d 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 42af9a3af..03fe42311 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 449686755..40d035c48 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 9bb175adf..4ee16c93e 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 75c561956..e126fcf35 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 ef571bbf3..5a6bfa85c 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 e79eb02cc..5f7adb991 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 e7eb7594f..0dca60b59 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 3c46b7a4d..80fd3dedf 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 cca07ad4c..8e39766fd 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(); } }; From 64e4d7763e074f7bc01ea41e0e89f52fc60c9e15 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 19 Dec 2025 20:42:04 -0500 Subject: [PATCH 029/108] BetterActivities: Fix Patch --- src/equicordplugins/betterActivities/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/betterActivities/index.tsx b/src/equicordplugins/betterActivities/index.tsx index 5e8ad5f30..ecc66cd8a 100644 --- a/src/equicordplugins/betterActivities/index.tsx +++ b/src/equicordplugins/betterActivities/index.tsx @@ -50,7 +50,7 @@ export default definePlugin({ // Show all activities in the user popout/sidebar find: '"UserProfilePopoutBody"', replacement: { - match: /((\i)=.{0,10}(\i)\.id\).*?,)\i\?.{0,250}onClose:\i\}\)/, + match: /((\i)=.{0,10}(\i)\.id\).*?,onInteraction:\i\}\),).{0,250}onClose:\i\}\)/, replace: "$1$self.showAllActivitiesComponent({ activity: $2, user: $3 })" }, predicate: () => settings.store.userPopout From 3af56515c9eabbf8fe32556dcb642b070d525d5b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 19 Dec 2025 21:02:19 -0500 Subject: [PATCH 030/108] BetterActivities: Update CSS --- .../betterActivities/components/CarouselControls.tsx | 8 ++++---- .../betterActivities/patch-helpers/activityList.tsx | 11 ++++++----- .../betterActivities/patch-helpers/popout.tsx | 9 +++++---- src/equicordplugins/betterActivities/styles.css | 9 +++++---- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/equicordplugins/betterActivities/components/CarouselControls.tsx b/src/equicordplugins/betterActivities/components/CarouselControls.tsx index be9d7a728..b4188d83e 100644 --- a/src/equicordplugins/betterActivities/components/CarouselControls.tsx +++ b/src/equicordplugins/betterActivities/components/CarouselControls.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { CarouselControlsProps } from "@equicordplugins/betterActivities/types"; -import { cl } from "@equicordplugins/betterActivities/utils"; import { React, Tooltip } from "@webpack/common"; +import { CarouselControlsProps } from "../types"; +import { cl } from "../utils"; import { Caret } from "./Caret"; export function CarouselControls({ activities, currentActivity, onActivityChange }: CarouselControlsProps) { @@ -43,12 +43,12 @@ export function CarouselControls({ activities, currentActivity, onActivityChange ; }} -
    +
    {activities.map((activity, index) => (
    onActivityChange(activity)} - className={`dot ${currentActivity === activity ? "selected" : ""}`} /> + className={cl("controls-dot", currentActivity === activity && "controls-selected")} /> ))}
    diff --git a/src/equicordplugins/betterActivities/patch-helpers/activityList.tsx b/src/equicordplugins/betterActivities/patch-helpers/activityList.tsx index a8c9cfb91..aad18d8d8 100644 --- a/src/equicordplugins/betterActivities/patch-helpers/activityList.tsx +++ b/src/equicordplugins/betterActivities/patch-helpers/activityList.tsx @@ -5,15 +5,16 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { ActivityTooltip } from "@equicordplugins/betterActivities/components/ActivityTooltip"; -import { TwitchIcon } from "@equicordplugins/betterActivities/components/TwitchIcon"; -import { settings } from "@equicordplugins/betterActivities/settings"; -import { ActivityListIcon, ActivityListProps, ApplicationIcon, IconCSSProperties } from "@equicordplugins/betterActivities/types"; -import { cl, getApplicationIcons } from "@equicordplugins/betterActivities/utils"; import { findComponentByCodeLazy } from "@webpack"; import { React, Tooltip } from "@webpack/common"; import { JSX } from "react"; +import { ActivityTooltip } from "../components/ActivityTooltip"; +import { TwitchIcon } from "../components/TwitchIcon"; +import { settings } from "../settings"; +import { ActivityListIcon, ActivityListProps, ApplicationIcon, IconCSSProperties } from "../types"; +import { cl, getApplicationIcons } from "../utils"; + // Discord no longer shows an icon here by default but we use the one from the popout now here const DefaultActivityIcon = findComponentByCodeLazy("M5 2a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3V5a3 3 0 0 0-3-3H5Zm6.81 7c-.54 0-1 .26-1.23.61A1 1 0 0 1 8.92 8.5 3.49 3.49 0 0 1 11.82 7c1.81 0 3.43 1.38 3.43 3.25 0 1.45-.98 2.61-2.27 3.06a1 1 0 0 1-1.96.37l-.19-1a1 1 0 0 1 .98-1.18c.87 0 1.44-.63 1.44-1.25S12.68 9 11.81 9ZM13 16a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm7-10.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM18.5 20a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM7 18.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM5.5 7a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"); diff --git a/src/equicordplugins/betterActivities/patch-helpers/popout.tsx b/src/equicordplugins/betterActivities/patch-helpers/popout.tsx index 43e570919..530073554 100644 --- a/src/equicordplugins/betterActivities/patch-helpers/popout.tsx +++ b/src/equicordplugins/betterActivities/patch-helpers/popout.tsx @@ -5,14 +5,15 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { CarouselControls } from "@equicordplugins/betterActivities/components/CarouselControls"; -import { settings } from "@equicordplugins/betterActivities/settings"; -import { AllActivitiesProps } from "@equicordplugins/betterActivities/types"; -import { ActivityView, getActivityApplication } from "@equicordplugins/betterActivities/utils"; import { Activity } from "@vencord/discord-types"; import { PresenceStore, React, useEffect, useMemo, UserStore, useState, useStateFromStores } from "@webpack/common"; import { JSX } from "react"; +import { CarouselControls } from "../components/CarouselControls"; +import { settings } from "../settings"; +import { AllActivitiesProps } from "../types"; +import { ActivityView, getActivityApplication } from "../utils"; + export function showAllActivitiesComponent({ activity, user, ...props }: Readonly): JSX.Element | null { const currentUser = UserStore.getCurrentUser(); if (!currentUser) return null; diff --git a/src/equicordplugins/betterActivities/styles.css b/src/equicordplugins/betterActivities/styles.css index 09fc8d656..6163ee17b 100644 --- a/src/equicordplugins/betterActivities/styles.css +++ b/src/equicordplugins/betterActivities/styles.css @@ -45,6 +45,7 @@ border-radius: 3px; flex: 1 0; margin-top: 10px; + background-color: var(--user-profile-overlay-background); } .vc-bactivities-controls [class*="vc-activities-caret-"] { @@ -65,12 +66,12 @@ background: var(--input-border-default, var(--input-border)); } -.vc-bactivities-controls .carousel { +.vc-bactivities-controls .vc-bactivities-controls-carousel { display: flex; align-items: center; } -.vc-bactivities-controls .carousel .dot { +.vc-bactivities-controls .vc-bactivities-controls-carousel .vc-bactivities-controls-dot { margin: 0 4px; width: 10px; cursor: pointer; @@ -81,11 +82,11 @@ opacity: 0.6; } -.vc-bactivities-controls .carousel .dot:hover:not(.selected) { +.vc-bactivities-controls .vc-bactivities-controls-carousel .vc-bactivities-controls-dot:hover:not(.vc-bactivities-controls-selected) { opacity: 1; } -.vc-bactivities-controls .carousel .dot.selected { +.vc-bactivities-controls .vc-bactivities-controls-carousel .vc-bactivities-controls-dot.vc-bactivities-controls-selected { opacity: 1; background: var(--dot-color, var(--brand-500)); } From 8e7de7187e7a3a5c9f2ef03583cab7c0a85391ba Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 19 Dec 2025 21:28:20 -0500 Subject: [PATCH 031/108] fixes --- scripts/build/common.mjs | 7 ------- src/components/ThemeSettings/ThemesTab.tsx | 2 +- .../ThemeSettings/components/SettingBooleanComponent.tsx | 2 +- .../ThemeSettings/components/SettingColorComponent.tsx | 2 +- src/components/settings/OnlineThemeCard.tsx | 2 +- .../settings/tabs/changelog/NewPluginsSection.tsx | 2 +- src/components/settings/tabs/plugins/PluginCard.tsx | 2 +- src/components/settings/tabs/vencord/index.tsx | 2 +- src/equicordplugins/betterActivities/utils.tsx | 2 +- src/equicordplugins/betterBanReasons/index.tsx | 2 +- .../channelTabs/components/BookmarkContainer.tsx | 2 +- src/equicordplugins/channelTabs/components/ChannelTab.tsx | 2 +- .../channelTabs/components/ChannelTabsContainer.tsx | 2 +- src/equicordplugins/channelTabs/util/tabs.tsx | 2 +- src/equicordplugins/commandPalette/CommandPicker.tsx | 2 +- src/equicordplugins/commandPalette/ui.tsx | 2 +- src/equicordplugins/contentWarning/index.tsx | 2 +- .../customSounds/SoundOverrideComponent.tsx | 2 +- src/equicordplugins/customSounds/index.tsx | 2 +- src/equicordplugins/customUserColors/SetColorModal.tsx | 2 +- src/equicordplugins/elementHighlighter.dev/index.tsx | 4 ++-- src/equicordplugins/exitSounds/index.tsx | 2 +- src/equicordplugins/ghosted/GhostedUsersModal.tsx | 2 +- src/equicordplugins/ghosted/index.tsx | 2 +- src/equicordplugins/githubRepos/index.tsx | 2 +- src/equicordplugins/globalBadges/utils.ts | 2 +- .../hideServers/components/HiddenServersButton.tsx | 2 +- .../hideServers/components/HiddenServersMenu.tsx | 2 +- src/equicordplugins/iRememberYou/components/ui.tsx | 2 +- src/equicordplugins/ignoreCalls/index.tsx | 2 +- src/equicordplugins/inRole/RoleMembersModal.tsx | 2 +- .../keyboardNavigation/components/CommandPalette.tsx | 2 +- .../keyboardNavigation/components/MultipleChoice.tsx | 2 +- src/equicordplugins/keyboardNavigation/index.tsx | 2 +- src/equicordplugins/keywordNotify/index.tsx | 2 +- src/equicordplugins/loginWithQR/ui/index.ts | 2 +- src/equicordplugins/mediaPlaybackSpeed/index.tsx | 2 +- .../messageLoggerEnhanced/components/FolderSelectInput.tsx | 2 +- .../messageLoggerEnhanced/components/LogsModal.tsx | 2 +- src/equicordplugins/moreStickers/utils.tsx | 2 +- src/equicordplugins/moreUserTags/index.tsx | 2 +- .../musicControls/spotify/lyrics/components/util.tsx | 2 +- .../musicControls/tidal/lyrics/components/util.tsx | 2 +- src/equicordplugins/newPluginsManager/NewPluginsModal.tsx | 2 +- src/equicordplugins/questify/utils/misc.tsx | 2 +- src/equicordplugins/recentDMSwitcher/index.tsx | 2 +- .../remix/editor/components/SettingColorComponent.tsx | 2 +- src/equicordplugins/repeatMessages/index.tsx | 2 +- .../scheduledMessages/components/MessageAccessory.tsx | 2 +- .../scheduledMessages/components/ScheduleTimeModal.tsx | 2 +- .../scheduledMessages/components/ViewScheduledModal.tsx | 2 +- src/equicordplugins/soundBoardLogger/utils.ts | 2 +- src/equicordplugins/themeLibrary/utils/settings.tsx | 2 +- src/equicordplugins/timezones/TimezoneModal.tsx | 2 +- src/equicordplugins/translatePlus/misc/types.ts | 2 +- src/equicordplugins/unitConverter/ConverterAccessory.tsx | 2 +- src/equicordplugins/urlHighlighter/index.tsx | 2 +- src/equicordplugins/userpfp/index.tsx | 2 +- src/equicordplugins/voiceChannelLog.dev/index.tsx | 2 +- src/equicordplugins/whosWatching/index.tsx | 2 +- src/plugins/_api/badges/index.tsx | 4 +++- src/plugins/platformIndicators/icons/desktopIcon.svg | 3 --- src/plugins/platformIndicators/icons/embeddedIcon.svg | 3 --- src/plugins/platformIndicators/icons/mobileIcon.svg | 3 --- src/plugins/platformIndicators/icons/webIcon.svg | 3 --- src/plugins/serverInfo/GuildInfoModal.tsx | 2 +- src/plugins/serverListIndicators/index.tsx | 2 +- src/plugins/sortFriendRequests/index.tsx | 2 +- src/plugins/userVoiceShow/components.tsx | 2 +- src/plugins/usrbg/index.tsx | 2 +- src/plugins/voiceMessages/index.tsx | 2 +- 71 files changed, 69 insertions(+), 86 deletions(-) delete mode 100644 src/plugins/platformIndicators/icons/desktopIcon.svg delete mode 100644 src/plugins/platformIndicators/icons/embeddedIcon.svg delete mode 100644 src/plugins/platformIndicators/icons/mobileIcon.svg delete mode 100644 src/plugins/platformIndicators/icons/webIcon.svg diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 9df5c521e..a792858f9 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -27,7 +27,6 @@ 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"; @@ -283,12 +282,6 @@ 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], diff --git a/src/components/ThemeSettings/ThemesTab.tsx b/src/components/ThemeSettings/ThemesTab.tsx index bde731b08..393b45a0f 100644 --- a/src/components/ThemeSettings/ThemesTab.tsx +++ b/src/components/ThemeSettings/ThemesTab.tsx @@ -20,7 +20,7 @@ import "./themesStyles.css"; import { isPluginEnabled } from "@api/PluginManager"; import { Settings, useSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Alert } from "@components/Alert"; import { Button } from "@components/Button"; import { Divider } from "@components/Divider"; diff --git a/src/components/ThemeSettings/components/SettingBooleanComponent.tsx b/src/components/ThemeSettings/components/SettingBooleanComponent.tsx index c17a72628..49cc5dec8 100644 --- a/src/components/ThemeSettings/components/SettingBooleanComponent.tsx +++ b/src/components/ThemeSettings/components/SettingBooleanComponent.tsx @@ -6,7 +6,7 @@ import "./styles.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { FormSwitch } from "@components/FormSwitch"; interface Props { diff --git a/src/components/ThemeSettings/components/SettingColorComponent.tsx b/src/components/ThemeSettings/components/SettingColorComponent.tsx index 86be59edd..e01228c5f 100644 --- a/src/components/ThemeSettings/components/SettingColorComponent.tsx +++ b/src/components/ThemeSettings/components/SettingColorComponent.tsx @@ -6,7 +6,7 @@ import "./colorStyles.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { findByCodeLazy } from "@webpack"; import { ColorPicker, useMemo } from "@webpack/common"; diff --git a/src/components/settings/OnlineThemeCard.tsx b/src/components/settings/OnlineThemeCard.tsx index abbbc8bc6..4753192b4 100644 --- a/src/components/settings/OnlineThemeCard.tsx +++ b/src/components/settings/OnlineThemeCard.tsx @@ -18,7 +18,7 @@ import "./AddonCard.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Switch } from "@components/settings"; import { AddonBadge } from "@components/settings/PluginBadge"; diff --git a/src/components/settings/tabs/changelog/NewPluginsSection.tsx b/src/components/settings/tabs/changelog/NewPluginsSection.tsx index fc3a3eba4..83f7db8e4 100644 --- a/src/components/settings/tabs/changelog/NewPluginsSection.tsx +++ b/src/components/settings/tabs/changelog/NewPluginsSection.tsx @@ -5,7 +5,7 @@ */ import { useSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { Card } from "@components/Card"; import { Heading } from "@components/Heading"; diff --git a/src/components/settings/tabs/plugins/PluginCard.tsx b/src/components/settings/tabs/plugins/PluginCard.tsx index e9275c376..dab524871 100644 --- a/src/components/settings/tabs/plugins/PluginCard.tsx +++ b/src/components/settings/tabs/plugins/PluginCard.tsx @@ -6,7 +6,7 @@ import { showNotice } from "@api/Notices"; import { isPluginEnabled, pluginRequiresRestart, startDependenciesRecursive, startPlugin, stopPlugin } from "@api/PluginManager"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { CogWheel, InfoIcon } from "@components/Icons"; import { AddonCard } from "@components/settings/AddonCard"; import { Logger } from "@utils/Logger"; diff --git a/src/components/settings/tabs/vencord/index.tsx b/src/components/settings/tabs/vencord/index.tsx index 79dc4f987..2d2f8468d 100644 --- a/src/components/settings/tabs/vencord/index.tsx +++ b/src/components/settings/tabs/vencord/index.tsx @@ -8,7 +8,7 @@ import "./VencordTab.css"; import { openNotificationLogModal } from "@api/Notifications/notificationLog"; import { useSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Alert } from "@components/Alert"; import { Button } from "@components/Button"; import { Divider } from "@components/Divider"; diff --git a/src/equicordplugins/betterActivities/utils.tsx b/src/equicordplugins/betterActivities/utils.tsx index 61de0479a..e01ca488b 100644 --- a/src/equicordplugins/betterActivities/utils.tsx +++ b/src/equicordplugins/betterActivities/utils.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Activity, Application } from "@vencord/discord-types"; import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; diff --git a/src/equicordplugins/betterBanReasons/index.tsx b/src/equicordplugins/betterBanReasons/index.tsx index 749d7791f..2ca9315b4 100644 --- a/src/equicordplugins/betterBanReasons/index.tsx +++ b/src/equicordplugins/betterBanReasons/index.tsx @@ -7,7 +7,7 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Heading } from "@components/Heading"; import { DeleteIcon, PlusIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; diff --git a/src/equicordplugins/channelTabs/components/BookmarkContainer.tsx b/src/equicordplugins/channelTabs/components/BookmarkContainer.tsx index c51cb239c..e54177681 100644 --- a/src/equicordplugins/channelTabs/components/BookmarkContainer.tsx +++ b/src/equicordplugins/channelTabs/components/BookmarkContainer.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { BasicChannelTabsProps, Bookmark, BookmarkFolder, BookmarkProps, isBookmarkFolder, isTabSelected, navigateToBookmark, openedTabs, settings, switchChannel, useBookmarks } from "@equicordplugins/channelTabs/util"; import { CircleQuestionIcon, DiscoveryIcon, EnvelopeIcon, FriendsIcon, NitroIcon, QuestIcon, ShopIcon } from "@equicordplugins/channelTabs/util/icons"; diff --git a/src/equicordplugins/channelTabs/components/ChannelTab.tsx b/src/equicordplugins/channelTabs/components/ChannelTab.tsx index c9ac7c9ed..2cb02900b 100644 --- a/src/equicordplugins/channelTabs/components/ChannelTab.tsx +++ b/src/equicordplugins/channelTabs/components/ChannelTab.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { ChannelTabsProps, closeTab, isTabSelected, moveDraggedTabs, moveToTab, openedTabs, settings } from "@equicordplugins/channelTabs/util"; import { ActivityIcon, CircleQuestionIcon, DiscoveryIcon, EnvelopeIcon, FriendsIcon, ICYMIIcon, NitroIcon, QuestIcon, ShopIcon } from "@equicordplugins/channelTabs/util/icons"; diff --git a/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx b/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx index a35372709..05ad9e253 100644 --- a/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx +++ b/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Flex } from "@components/Flex"; import { Heading } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; diff --git a/src/equicordplugins/channelTabs/util/tabs.tsx b/src/equicordplugins/channelTabs/util/tabs.tsx index 529af04c0..c9ff42172 100644 --- a/src/equicordplugins/channelTabs/util/tabs.tsx +++ b/src/equicordplugins/channelTabs/util/tabs.tsx @@ -6,7 +6,7 @@ import { DataStore } from "@api/index"; import { isPluginEnabled } from "@api/PluginManager"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { NavigationRouter, SelectedChannelStore, SelectedGuildStore, showToast, Toasts, useEffect, useRef, useState } from "@webpack/common"; import { JSX } from "react"; diff --git a/src/equicordplugins/commandPalette/CommandPicker.tsx b/src/equicordplugins/commandPalette/CommandPicker.tsx index e999d76f1..e2e13c703 100644 --- a/src/equicordplugins/commandPalette/CommandPicker.tsx +++ b/src/equicordplugins/commandPalette/CommandPicker.tsx @@ -6,7 +6,7 @@ import "./style.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { ModalRoot, ModalSize, openModal } from "@utils/modal"; import { React, TextInput, useCallback, useEffect, useMemo, useRef, useState } from "@webpack/common"; diff --git a/src/equicordplugins/commandPalette/ui.tsx b/src/equicordplugins/commandPalette/ui.tsx index 35b93cce7..f9bcac0b6 100644 --- a/src/equicordplugins/commandPalette/ui.tsx +++ b/src/equicordplugins/commandPalette/ui.tsx @@ -6,7 +6,7 @@ import "./style.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { IS_LINUX, IS_MAC, IS_WINDOWS } from "@utils/constants"; import { closeAllModals, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { React, TextInput, useCallback, useEffect, useMemo, useRef, useState } from "@webpack/common"; diff --git a/src/equicordplugins/contentWarning/index.tsx b/src/equicordplugins/contentWarning/index.tsx index cdebb1bdb..ee41523f1 100644 --- a/src/equicordplugins/contentWarning/index.tsx +++ b/src/equicordplugins/contentWarning/index.tsx @@ -8,7 +8,7 @@ import "./styles.css"; import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Flex } from "@components/Flex"; import { HeadingTertiary } from "@components/Heading"; import { DeleteIcon } from "@components/Icons"; diff --git a/src/equicordplugins/customSounds/SoundOverrideComponent.tsx b/src/equicordplugins/customSounds/SoundOverrideComponent.tsx index 187b2c4b5..ee4c095d6 100644 --- a/src/equicordplugins/customSounds/SoundOverrideComponent.tsx +++ b/src/equicordplugins/customSounds/SoundOverrideComponent.tsx @@ -5,7 +5,7 @@ */ import { AudioPlayerInterface, playAudio } from "@api/AudioPlayer"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Card } from "@components/Card"; import { FormSwitch } from "@components/FormSwitch"; import { Heading } from "@components/Heading"; diff --git a/src/equicordplugins/customSounds/index.tsx b/src/equicordplugins/customSounds/index.tsx index 38e593432..0c6fbd470 100644 --- a/src/equicordplugins/customSounds/index.tsx +++ b/src/equicordplugins/customSounds/index.tsx @@ -9,7 +9,7 @@ import "./styles.css"; import { AudioProcessor, PreprocessAudioData } from "@api/AudioPlayer"; import { get as getFromDataStore } from "@api/DataStore"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Heading } from "@components/Heading"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType, StartAt } from "@utils/types"; diff --git a/src/equicordplugins/customUserColors/SetColorModal.tsx b/src/equicordplugins/customUserColors/SetColorModal.tsx index ff03c3d43..092425732 100644 --- a/src/equicordplugins/customUserColors/SetColorModal.tsx +++ b/src/equicordplugins/customUserColors/SetColorModal.tsx @@ -5,7 +5,7 @@ */ import { set } from "@api/DataStore"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { HeadingPrimary, HeadingSecondary } from "@components/Heading"; import { Margins } from "@utils/margins"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; diff --git a/src/equicordplugins/elementHighlighter.dev/index.tsx b/src/equicordplugins/elementHighlighter.dev/index.tsx index aef7aac03..b1eb7a04b 100644 --- a/src/equicordplugins/elementHighlighter.dev/index.tsx +++ b/src/equicordplugins/elementHighlighter.dev/index.tsx @@ -7,7 +7,7 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { EquicordDevs, IS_MAC } from "@utils/constants"; import { copyWithToast } from "@utils/discord"; @@ -31,7 +31,7 @@ let pendingY = 0; let pendingElement: Element | null = null; const colorCache = new WeakMap(); -let cachedRules: { selector: string; specificity: number; color: string }[] = []; +let cachedRules: { selector: string; specificity: number; color: string; }[] = []; function KeybindRecorder() { const [isListening, setIsListening] = useState(false); diff --git a/src/equicordplugins/exitSounds/index.tsx b/src/equicordplugins/exitSounds/index.tsx index c6b2605e7..378012509 100644 --- a/src/equicordplugins/exitSounds/index.tsx +++ b/src/equicordplugins/exitSounds/index.tsx @@ -9,7 +9,7 @@ import "./styles.css"; import { playAudio } from "@api/AudioPlayer"; import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; diff --git a/src/equicordplugins/ghosted/GhostedUsersModal.tsx b/src/equicordplugins/ghosted/GhostedUsersModal.tsx index 630ce5ddf..337634f56 100644 --- a/src/equicordplugins/ghosted/GhostedUsersModal.tsx +++ b/src/equicordplugins/ghosted/GhostedUsersModal.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize } from "@utils/modal"; import { findByPropsLazy } from "@webpack"; import { Avatar, Button, ChannelStore, MessageStore, React, Text, UserStore } from "@webpack/common"; diff --git a/src/equicordplugins/ghosted/index.tsx b/src/equicordplugins/ghosted/index.tsx index 7e48f6b16..0318ee9d5 100644 --- a/src/equicordplugins/ghosted/index.tsx +++ b/src/equicordplugins/ghosted/index.tsx @@ -8,7 +8,7 @@ import "./styles.css"; import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList"; import { definePluginSettings, migratePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs, EquicordDevs } from "@utils/constants"; import { closeModal, openModal } from "@utils/modal"; diff --git a/src/equicordplugins/githubRepos/index.tsx b/src/equicordplugins/githubRepos/index.tsx index c528b97f3..f1386cf4d 100644 --- a/src/equicordplugins/githubRepos/index.tsx +++ b/src/equicordplugins/githubRepos/index.tsx @@ -7,7 +7,7 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import ErrorBoundary from "@components/ErrorBoundary"; import { EquicordDevs } from "@utils/constants"; diff --git a/src/equicordplugins/globalBadges/utils.ts b/src/equicordplugins/globalBadges/utils.ts index 8271166ad..b38c2b04e 100644 --- a/src/equicordplugins/globalBadges/utils.ts +++ b/src/equicordplugins/globalBadges/utils.ts @@ -6,7 +6,7 @@ import "./styles.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { settings } from "./settings"; diff --git a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx index e58f5b6a9..eaac41711 100644 --- a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx +++ b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx @@ -6,7 +6,7 @@ import "./style.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { HiddenServersStore } from "@equicordplugins/hideServers/HiddenServersStore"; import { Button, GuildStore, useStateFromStores } from "@webpack/common"; diff --git a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx index f627bebbb..2af6015aa 100644 --- a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx +++ b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Heading } from "@components/Heading"; import { HiddenServersStore } from "@equicordplugins/hideServers/HiddenServersStore"; diff --git a/src/equicordplugins/iRememberYou/components/ui.tsx b/src/equicordplugins/iRememberYou/components/ui.tsx index 41dabc345..c0dacde26 100644 --- a/src/equicordplugins/iRememberYou/components/ui.tsx +++ b/src/equicordplugins/iRememberYou/components/ui.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Card } from "@components/Card"; import { HeadingPrimary } from "@components/Heading"; diff --git a/src/equicordplugins/ignoreCalls/index.tsx b/src/equicordplugins/ignoreCalls/index.tsx index e0f23a244..d407e9032 100644 --- a/src/equicordplugins/ignoreCalls/index.tsx +++ b/src/equicordplugins/ignoreCalls/index.tsx @@ -8,7 +8,7 @@ import "./styles.css"; import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { ErrorBoundary } from "@components/index"; import { Devs, EquicordDevs } from "@utils/constants"; diff --git a/src/equicordplugins/inRole/RoleMembersModal.tsx b/src/equicordplugins/inRole/RoleMembersModal.tsx index 8b0615f13..79a5adcf2 100644 --- a/src/equicordplugins/inRole/RoleMembersModal.tsx +++ b/src/equicordplugins/inRole/RoleMembersModal.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Paragraph } from "@components/Paragraph"; import { ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; diff --git a/src/equicordplugins/keyboardNavigation/components/CommandPalette.tsx b/src/equicordplugins/keyboardNavigation/components/CommandPalette.tsx index efaf5e9f9..1d61636c5 100644 --- a/src/equicordplugins/keyboardNavigation/components/CommandPalette.tsx +++ b/src/equicordplugins/keyboardNavigation/components/CommandPalette.tsx @@ -6,7 +6,7 @@ import "./style.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { actions } from "@equicordplugins/keyboardNavigation/commands"; import { Logger } from "@utils/Logger"; import { closeAllModals, ModalRoot, ModalSize, openModal } from "@utils/modal"; diff --git a/src/equicordplugins/keyboardNavigation/components/MultipleChoice.tsx b/src/equicordplugins/keyboardNavigation/components/MultipleChoice.tsx index 88f70abbb..c311ff510 100644 --- a/src/equicordplugins/keyboardNavigation/components/MultipleChoice.tsx +++ b/src/equicordplugins/keyboardNavigation/components/MultipleChoice.tsx @@ -6,7 +6,7 @@ import "./style.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { ButtonAction } from "@equicordplugins/keyboardNavigation/commands"; import { closeAllModals, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { React, TextInput, useEffect, useState } from "@webpack/common"; diff --git a/src/equicordplugins/keyboardNavigation/index.tsx b/src/equicordplugins/keyboardNavigation/index.tsx index 75bb65d7e..39c6b7b8c 100644 --- a/src/equicordplugins/keyboardNavigation/index.tsx +++ b/src/equicordplugins/keyboardNavigation/index.tsx @@ -5,7 +5,7 @@ */ import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Devs } from "@utils/constants"; import { closeAllModals } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; diff --git a/src/equicordplugins/keywordNotify/index.tsx b/src/equicordplugins/keywordNotify/index.tsx index 5f98923f9..ed161e44f 100644 --- a/src/equicordplugins/keywordNotify/index.tsx +++ b/src/equicordplugins/keywordNotify/index.tsx @@ -8,7 +8,7 @@ import "./style.css"; import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Flex } from "@components/Flex"; import { FormSwitch } from "@components/FormSwitch"; import { Heading, HeadingTertiary } from "@components/Heading"; diff --git a/src/equicordplugins/loginWithQR/ui/index.ts b/src/equicordplugins/loginWithQR/ui/index.ts index 1277db7a5..0683fa372 100644 --- a/src/equicordplugins/loginWithQR/ui/index.ts +++ b/src/equicordplugins/loginWithQR/ui/index.ts @@ -6,7 +6,7 @@ import "./styles.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { findComponentByCodeLazy } from "@webpack"; import { ComponentType, HTMLAttributes } from "react"; diff --git a/src/equicordplugins/mediaPlaybackSpeed/index.tsx b/src/equicordplugins/mediaPlaybackSpeed/index.tsx index 12d13f45d..396913d0e 100644 --- a/src/equicordplugins/mediaPlaybackSpeed/index.tsx +++ b/src/equicordplugins/mediaPlaybackSpeed/index.tsx @@ -7,9 +7,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, { makeRange, OptionType } from "@utils/types"; import { ContextMenuApi, FluxDispatcher, Menu, React, Tooltip, useEffect } from "@webpack/common"; import { RefObject } from "react"; diff --git a/src/equicordplugins/messageLoggerEnhanced/components/FolderSelectInput.tsx b/src/equicordplugins/messageLoggerEnhanced/components/FolderSelectInput.tsx index 28f1e31e6..11de8ed9e 100644 --- a/src/equicordplugins/messageLoggerEnhanced/components/FolderSelectInput.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/components/FolderSelectInput.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Heading } from "@components/Heading"; import { DEFAULT_IMAGE_CACHE_DIR } from "@equicordplugins/messageLoggerEnhanced/utils/constants"; import { copyWithToast } from "@utils/discord"; diff --git a/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx b/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx index 04fbf62e0..3e73d97a1 100644 --- a/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Flex } from "@components/Flex"; import { InfoIcon } from "@components/Icons"; diff --git a/src/equicordplugins/moreStickers/utils.tsx b/src/equicordplugins/moreStickers/utils.tsx index 0af5de359..e2e501bfa 100644 --- a/src/equicordplugins/moreStickers/utils.tsx +++ b/src/equicordplugins/moreStickers/utils.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import type { FFmpeg } from "@ffmpeg/ffmpeg"; import { waitFor } from "@webpack"; import { React } from "@webpack/common"; diff --git a/src/equicordplugins/moreUserTags/index.tsx b/src/equicordplugins/moreUserTags/index.tsx index ddf0db266..2f6a8efc4 100644 --- a/src/equicordplugins/moreUserTags/index.tsx +++ b/src/equicordplugins/moreUserTags/index.tsx @@ -6,7 +6,7 @@ import "./styles.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Devs, EquicordDevs } from "@utils/constants"; import { getCurrentChannel, getIntlMessage } from "@utils/discord"; import definePlugin from "@utils/types"; diff --git a/src/equicordplugins/musicControls/spotify/lyrics/components/util.tsx b/src/equicordplugins/musicControls/spotify/lyrics/components/util.tsx index 895ea8bb4..b390277bf 100644 --- a/src/equicordplugins/musicControls/spotify/lyrics/components/util.tsx +++ b/src/equicordplugins/musicControls/spotify/lyrics/components/util.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { settings } from "@equicordplugins/musicControls/settings"; import { SpotifyLrcStore } from "@equicordplugins/musicControls/spotify/lyrics/providers/store"; import { SyncedLyric } from "@equicordplugins/musicControls/spotify/lyrics/providers/types"; diff --git a/src/equicordplugins/musicControls/tidal/lyrics/components/util.tsx b/src/equicordplugins/musicControls/tidal/lyrics/components/util.tsx index f2cbd6fbb..04968b02f 100644 --- a/src/equicordplugins/musicControls/tidal/lyrics/components/util.tsx +++ b/src/equicordplugins/musicControls/tidal/lyrics/components/util.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { settings } from "@equicordplugins/musicControls/settings"; import { TidalLrcStore } from "@equicordplugins/musicControls/tidal/lyrics/providers/store"; import { EnhancedLyric } from "@equicordplugins/musicControls/tidal/lyrics/types"; diff --git a/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx b/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx index 9f1c60510..6644d889d 100644 --- a/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx +++ b/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx @@ -7,7 +7,7 @@ import "./styles.css"; import { Settings, useSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Button } from "@components/Button"; import ErrorBoundary from "@components/ErrorBoundary"; diff --git a/src/equicordplugins/questify/utils/misc.tsx b/src/equicordplugins/questify/utils/misc.tsx index a1ac6701a..539de8d94 100644 --- a/src/equicordplugins/questify/utils/misc.tsx +++ b/src/equicordplugins/questify/utils/misc.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { questIsIgnored, settings } from "@equicordplugins/questify/settings"; import { Logger } from "@utils/Logger"; import { findByPropsLazy } from "@webpack"; diff --git a/src/equicordplugins/recentDMSwitcher/index.tsx b/src/equicordplugins/recentDMSwitcher/index.tsx index a7d080968..a063f77f5 100644 --- a/src/equicordplugins/recentDMSwitcher/index.tsx +++ b/src/equicordplugins/recentDMSwitcher/index.tsx @@ -8,7 +8,7 @@ import "./styles.css"; import * as DataStore from "@api/DataStore"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { EquicordDevs, IS_MAC } from "@utils/constants"; import { closeModal, openModal } from "@utils/modal"; import definePlugin, { makeRange, OptionType } from "@utils/types"; diff --git a/src/equicordplugins/remix/editor/components/SettingColorComponent.tsx b/src/equicordplugins/remix/editor/components/SettingColorComponent.tsx index fa159ee10..a91d7b77a 100644 --- a/src/equicordplugins/remix/editor/components/SettingColorComponent.tsx +++ b/src/equicordplugins/remix/editor/components/SettingColorComponent.tsx @@ -9,7 +9,7 @@ import "./colorStyles.css"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { ColorPicker } from "@webpack/common"; const cl = classNameFactory("vc-remix-settings-color-"); diff --git a/src/equicordplugins/repeatMessages/index.tsx b/src/equicordplugins/repeatMessages/index.tsx index 1ab3a17d9..a13596e90 100644 --- a/src/equicordplugins/repeatMessages/index.tsx +++ b/src/equicordplugins/repeatMessages/index.tsx @@ -6,7 +6,7 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { migratePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Devs, EquicordDevs } from "@utils/constants"; import { sendMessage } from "@utils/discord"; import { useForceUpdater } from "@utils/react"; diff --git a/src/equicordplugins/scheduledMessages/components/MessageAccessory.tsx b/src/equicordplugins/scheduledMessages/components/MessageAccessory.tsx index fe69de80c..0bf5a14f2 100644 --- a/src/equicordplugins/scheduledMessages/components/MessageAccessory.tsx +++ b/src/equicordplugins/scheduledMessages/components/MessageAccessory.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { ExtendedMessage } from "../types"; import { phantomMessageMap } from "../utils"; diff --git a/src/equicordplugins/scheduledMessages/components/ScheduleTimeModal.tsx b/src/equicordplugins/scheduledMessages/components/ScheduleTimeModal.tsx index 4b540e0fa..466452116 100644 --- a/src/equicordplugins/scheduledMessages/components/ScheduleTimeModal.tsx +++ b/src/equicordplugins/scheduledMessages/components/ScheduleTimeModal.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import ErrorBoundary from "@components/ErrorBoundary"; import { Heading } from "@components/Heading"; diff --git a/src/equicordplugins/scheduledMessages/components/ViewScheduledModal.tsx b/src/equicordplugins/scheduledMessages/components/ViewScheduledModal.tsx index eb6204967..b83a83e00 100644 --- a/src/equicordplugins/scheduledMessages/components/ViewScheduledModal.tsx +++ b/src/equicordplugins/scheduledMessages/components/ViewScheduledModal.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import ErrorBoundary from "@components/ErrorBoundary"; import { Heading } from "@components/Heading"; diff --git a/src/equicordplugins/soundBoardLogger/utils.ts b/src/equicordplugins/soundBoardLogger/utils.ts index f25200a8f..51e7eaf1c 100644 --- a/src/equicordplugins/soundBoardLogger/utils.ts +++ b/src/equicordplugins/soundBoardLogger/utils.ts @@ -5,7 +5,7 @@ */ import { playAudio } from "@api/AudioPlayer"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { proxyLazy } from "@utils/lazy"; import { LazyComponent } from "@utils/react"; import { saveFile } from "@utils/web"; diff --git a/src/equicordplugins/themeLibrary/utils/settings.tsx b/src/equicordplugins/themeLibrary/utils/settings.tsx index 3c13daa46..6ca48a45d 100644 --- a/src/equicordplugins/themeLibrary/utils/settings.tsx +++ b/src/equicordplugins/themeLibrary/utils/settings.tsx @@ -6,7 +6,7 @@ import * as DataStore from "@api/DataStore"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { HeadingSecondary } from "@components/Heading"; import { OpenExternalIcon } from "@components/Icons"; import { Paragraph } from "@components/Paragraph"; diff --git a/src/equicordplugins/timezones/TimezoneModal.tsx b/src/equicordplugins/timezones/TimezoneModal.tsx index 3da79bf86..b9dde2bf0 100644 --- a/src/equicordplugins/timezones/TimezoneModal.tsx +++ b/src/equicordplugins/timezones/TimezoneModal.tsx @@ -5,7 +5,7 @@ */ import * as DataStore from "@api/DataStore"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { HeadingPrimary, HeadingSecondary } from "@components/Heading"; import { Margins } from "@utils/margins"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; diff --git a/src/equicordplugins/translatePlus/misc/types.ts b/src/equicordplugins/translatePlus/misc/types.ts index dde38179e..291be4576 100644 --- a/src/equicordplugins/translatePlus/misc/types.ts +++ b/src/equicordplugins/translatePlus/misc/types.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; export const cl = classNameFactory("eq-trans-"); diff --git a/src/equicordplugins/unitConverter/ConverterAccessory.tsx b/src/equicordplugins/unitConverter/ConverterAccessory.tsx index 1ec77ada4..2cc736ff1 100644 --- a/src/equicordplugins/unitConverter/ConverterAccessory.tsx +++ b/src/equicordplugins/unitConverter/ConverterAccessory.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Message } from "@vencord/discord-types"; import { useState } from "@webpack/common"; diff --git a/src/equicordplugins/urlHighlighter/index.tsx b/src/equicordplugins/urlHighlighter/index.tsx index ad525c9e5..41065042f 100644 --- a/src/equicordplugins/urlHighlighter/index.tsx +++ b/src/equicordplugins/urlHighlighter/index.tsx @@ -7,7 +7,7 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import ErrorBoundary from "@components/ErrorBoundary"; import { Heading } from "@components/Heading"; diff --git a/src/equicordplugins/userpfp/index.tsx b/src/equicordplugins/userpfp/index.tsx index 10fd0967a..61683c5b8 100644 --- a/src/equicordplugins/userpfp/index.tsx +++ b/src/equicordplugins/userpfp/index.tsx @@ -7,7 +7,7 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { Flex } from "@components/Flex"; import { Heart } from "@components/Heart"; diff --git a/src/equicordplugins/voiceChannelLog.dev/index.tsx b/src/equicordplugins/voiceChannelLog.dev/index.tsx index 5f8a20044..3c6d25961 100644 --- a/src/equicordplugins/voiceChannelLog.dev/index.tsx +++ b/src/equicordplugins/voiceChannelLog.dev/index.tsx @@ -6,7 +6,7 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { Message, User } from "@vencord/discord-types"; diff --git a/src/equicordplugins/whosWatching/index.tsx b/src/equicordplugins/whosWatching/index.tsx index d503fb0c7..a49730b38 100644 --- a/src/equicordplugins/whosWatching/index.tsx +++ b/src/equicordplugins/whosWatching/index.tsx @@ -5,7 +5,7 @@ */ import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Heading, HeadingSecondary } from "@components/Heading"; diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index ff9117865..0573a4967 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -26,8 +26,10 @@ import { copyWithToast } from "@utils/discord"; import { Logger } from "@utils/Logger"; import { shouldShowContributorBadge, shouldShowEquicordContributorBadge } from "@utils/misc"; import definePlugin from "@utils/types"; -import { ContextMenuApi, Forms, Menu, Toasts, UserStore } from "@webpack/common"; +import { ContextMenuApi, 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"; diff --git a/src/plugins/platformIndicators/icons/desktopIcon.svg b/src/plugins/platformIndicators/icons/desktopIcon.svg deleted file mode 100644 index df7ac3312..000000000 --- a/src/plugins/platformIndicators/icons/desktopIcon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/plugins/platformIndicators/icons/embeddedIcon.svg b/src/plugins/platformIndicators/icons/embeddedIcon.svg deleted file mode 100644 index ce6f62ddb..000000000 --- a/src/plugins/platformIndicators/icons/embeddedIcon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/plugins/platformIndicators/icons/mobileIcon.svg b/src/plugins/platformIndicators/icons/mobileIcon.svg deleted file mode 100644 index 28b23a522..000000000 --- a/src/plugins/platformIndicators/icons/mobileIcon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/plugins/platformIndicators/icons/webIcon.svg b/src/plugins/platformIndicators/icons/webIcon.svg deleted file mode 100644 index 82627ca90..000000000 --- a/src/plugins/platformIndicators/icons/webIcon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/plugins/serverInfo/GuildInfoModal.tsx b/src/plugins/serverInfo/GuildInfoModal.tsx index 5b074e723..6258edc1c 100644 --- a/src/plugins/serverInfo/GuildInfoModal.tsx +++ b/src/plugins/serverInfo/GuildInfoModal.tsx @@ -6,9 +6,9 @@ import "./styles.css"; -import { classNameFactory } from "@utils/css"; import { Heading } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; +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/serverListIndicators/index.tsx b/src/plugins/serverListIndicators/index.tsx index 2aa4793f7..f9982fd40 100644 --- a/src/plugins/serverListIndicators/index.tsx +++ b/src/plugins/serverListIndicators/index.tsx @@ -20,7 +20,7 @@ import "./styles.css"; import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs, EquicordDevs } from "@utils/constants"; diff --git a/src/plugins/sortFriendRequests/index.tsx b/src/plugins/sortFriendRequests/index.tsx index b3bedb1bd..dab83d5ac 100644 --- a/src/plugins/sortFriendRequests/index.tsx +++ b/src/plugins/sortFriendRequests/index.tsx @@ -19,11 +19,11 @@ import "./styles.css"; import { definePluginSettings, migratePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Devs, EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { User } from "@vencord/discord-types"; import { DateUtils, RelationshipStore, TooltipContainer } from "@webpack/common"; diff --git a/src/plugins/userVoiceShow/components.tsx b/src/plugins/userVoiceShow/components.tsx index e121dde6f..1280bf9ec 100644 --- a/src/plugins/userVoiceShow/components.tsx +++ b/src/plugins/userVoiceShow/components.tsx @@ -5,10 +5,10 @@ */ import { isPluginEnabled } from "@api/PluginManager"; -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; 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/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index ad95a53b5..3ea93d484 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -19,7 +19,7 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; diff --git a/src/plugins/voiceMessages/index.tsx b/src/plugins/voiceMessages/index.tsx index 27421efc6..5ce278fb6 100644 --- a/src/plugins/voiceMessages/index.tsx +++ b/src/plugins/voiceMessages/index.tsx @@ -20,7 +20,7 @@ import "./styles.css"; import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; +import { classNameFactory } from "@utils/css"; import { Card } from "@components/Card"; import { Heading } from "@components/Heading"; import { Microphone } from "@components/Icons"; From f253c77df79503c71ece54aa3ea483e22726471a Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 19 Dec 2025 21:29:25 -0500 Subject: [PATCH 032/108] fix css path --- src/main/utils/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/utils/constants.ts b/src/main/utils/constants.ts index 81d9e595d..d962356dc 100644 --- a/src/main/utils/constants.ts +++ b/src/main/utils/constants.ts @@ -61,7 +61,7 @@ if (IS_DEV) { setTimeout(() => { try { if (existsSync(settings)) copyFileSync(settings, SETTINGS_FILE); - if (existsSync(quickCss)) copyFileSync(quickCss, QUICKCSS_PATH); + if (existsSync(quickCss)) copyFileSync(quickCss, QUICK_CSS_PATH); writeFileSync(DEV_MIGRATED, "migrated"); app.relaunch(); app.exit(0); From 21c8f030eb51c35233449db6a3864036414d7ab4 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 19 Dec 2025 21:32:50 -0500 Subject: [PATCH 033/108] Misc: Formatting --- src/components/ThemeSettings/ThemesTab.tsx | 2 +- .../ThemeSettings/components/SettingBooleanComponent.tsx | 2 +- src/components/settings/OnlineThemeCard.tsx | 2 +- src/components/settings/tabs/changelog/NewPluginsSection.tsx | 2 +- src/components/settings/tabs/plugins/PluginCard.tsx | 2 +- src/components/settings/tabs/vencord/index.tsx | 2 +- src/equicordplugins/betterBanReasons/index.tsx | 2 +- .../channelTabs/components/BookmarkContainer.tsx | 2 +- src/equicordplugins/channelTabs/components/ChannelTab.tsx | 2 +- .../channelTabs/components/ChannelTabsContainer.tsx | 2 +- src/equicordplugins/commandPalette/ui.tsx | 2 +- src/equicordplugins/contentWarning/index.tsx | 2 +- src/equicordplugins/customSounds/SoundOverrideComponent.tsx | 2 +- src/equicordplugins/customSounds/index.tsx | 2 +- src/equicordplugins/customUserColors/SetColorModal.tsx | 2 +- src/equicordplugins/elementHighlighter.dev/index.tsx | 2 +- src/equicordplugins/exitSounds/index.tsx | 2 +- src/equicordplugins/ghosted/index.tsx | 2 +- src/equicordplugins/githubRepos/index.tsx | 2 +- .../hideServers/components/HiddenServersButton.tsx | 2 +- .../hideServers/components/HiddenServersMenu.tsx | 2 +- src/equicordplugins/iRememberYou/components/ui.tsx | 2 +- src/equicordplugins/ignoreCalls/index.tsx | 2 +- src/equicordplugins/inRole/RoleMembersModal.tsx | 2 +- .../keyboardNavigation/components/CommandPalette.tsx | 2 +- .../keyboardNavigation/components/MultipleChoice.tsx | 2 +- src/equicordplugins/keyboardNavigation/index.tsx | 2 +- src/equicordplugins/keywordNotify/index.tsx | 2 +- .../messageLoggerEnhanced/components/FolderSelectInput.tsx | 2 +- .../messageLoggerEnhanced/components/LogsModal.tsx | 2 +- src/equicordplugins/moreStickers/utils.tsx | 2 +- src/equicordplugins/moreUserTags/index.tsx | 2 +- .../musicControls/spotify/lyrics/components/util.tsx | 2 +- .../musicControls/tidal/lyrics/components/util.tsx | 2 +- src/equicordplugins/newPluginsManager/NewPluginsModal.tsx | 2 +- src/equicordplugins/questify/utils/misc.tsx | 2 +- src/equicordplugins/recentDMSwitcher/index.tsx | 2 +- src/equicordplugins/repeatMessages/index.tsx | 2 +- .../scheduledMessages/components/ScheduleTimeModal.tsx | 2 +- .../scheduledMessages/components/ViewScheduledModal.tsx | 2 +- src/equicordplugins/themeLibrary/utils/settings.tsx | 2 +- src/equicordplugins/timezones/TimezoneModal.tsx | 2 +- src/equicordplugins/urlHighlighter/index.tsx | 2 +- src/equicordplugins/userpfp/index.tsx | 2 +- src/equicordplugins/voiceChannelLog.dev/index.tsx | 2 +- src/equicordplugins/whosWatching/index.tsx | 2 +- src/plugins/serverListIndicators/index.tsx | 2 +- src/plugins/usrbg/index.tsx | 2 +- src/plugins/voiceMessages/index.tsx | 2 +- 49 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/components/ThemeSettings/ThemesTab.tsx b/src/components/ThemeSettings/ThemesTab.tsx index 393b45a0f..31ca471ac 100644 --- a/src/components/ThemeSettings/ThemesTab.tsx +++ b/src/components/ThemeSettings/ThemesTab.tsx @@ -20,7 +20,6 @@ import "./themesStyles.css"; import { isPluginEnabled } from "@api/PluginManager"; import { Settings, useSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Alert } from "@components/Alert"; import { Button } from "@components/Button"; import { Divider } from "@components/Divider"; @@ -34,6 +33,7 @@ import { Paragraph } from "@components/Paragraph"; import { AddonCard, openPluginModal, QuickAction, QuickActionCard, SettingsTab, wrapTab } from "@components/settings"; import { OnlineThemeCard } from "@components/settings/OnlineThemeCard"; import { CspBlockedUrls, useCspErrors } from "@utils/cspViolations"; +import { classNameFactory } from "@utils/css"; import { copyWithToast, openInviteModal } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; diff --git a/src/components/ThemeSettings/components/SettingBooleanComponent.tsx b/src/components/ThemeSettings/components/SettingBooleanComponent.tsx index 49cc5dec8..717a95761 100644 --- a/src/components/ThemeSettings/components/SettingBooleanComponent.tsx +++ b/src/components/ThemeSettings/components/SettingBooleanComponent.tsx @@ -6,8 +6,8 @@ import "./styles.css"; -import { classNameFactory } from "@utils/css"; import { FormSwitch } from "@components/FormSwitch"; +import { classNameFactory } from "@utils/css"; interface Props { label: string; diff --git a/src/components/settings/OnlineThemeCard.tsx b/src/components/settings/OnlineThemeCard.tsx index 4753192b4..62dbc297e 100644 --- a/src/components/settings/OnlineThemeCard.tsx +++ b/src/components/settings/OnlineThemeCard.tsx @@ -18,10 +18,10 @@ import "./AddonCard.css"; -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Switch } from "@components/settings"; import { AddonBadge } from "@components/settings/PluginBadge"; +import { classNameFactory } from "@utils/css"; import { useRef } from "@webpack/common"; import type { MouseEventHandler, ReactNode } from "react"; diff --git a/src/components/settings/tabs/changelog/NewPluginsSection.tsx b/src/components/settings/tabs/changelog/NewPluginsSection.tsx index 83f7db8e4..a86c562c8 100644 --- a/src/components/settings/tabs/changelog/NewPluginsSection.tsx +++ b/src/components/settings/tabs/changelog/NewPluginsSection.tsx @@ -5,13 +5,13 @@ */ import { useSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { Card } from "@components/Card"; import { Heading } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; import { PluginCard } from "@components/settings/tabs/plugins/PluginCard"; import { ChangeList } from "@utils/ChangeList"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { useForceUpdater } from "@utils/react"; import { React, Tooltip } from "@webpack/common"; diff --git a/src/components/settings/tabs/plugins/PluginCard.tsx b/src/components/settings/tabs/plugins/PluginCard.tsx index dab524871..7c5ab1076 100644 --- a/src/components/settings/tabs/plugins/PluginCard.tsx +++ b/src/components/settings/tabs/plugins/PluginCard.tsx @@ -6,9 +6,9 @@ import { showNotice } from "@api/Notices"; import { isPluginEnabled, pluginRequiresRestart, startDependenciesRecursive, startPlugin, stopPlugin } from "@api/PluginManager"; -import { classNameFactory } from "@utils/css"; import { CogWheel, InfoIcon } from "@components/Icons"; import { AddonCard } from "@components/settings/AddonCard"; +import { classNameFactory } from "@utils/css"; import { Logger } from "@utils/Logger"; import { isObjectEmpty } from "@utils/misc"; import { Plugin } from "@utils/types"; diff --git a/src/components/settings/tabs/vencord/index.tsx b/src/components/settings/tabs/vencord/index.tsx index 2d2f8468d..ed116cee4 100644 --- a/src/components/settings/tabs/vencord/index.tsx +++ b/src/components/settings/tabs/vencord/index.tsx @@ -8,7 +8,6 @@ import "./VencordTab.css"; import { openNotificationLogModal } from "@api/Notifications/notificationLog"; import { useSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Alert } from "@components/Alert"; import { Button } from "@components/Button"; import { Divider } from "@components/Divider"; @@ -22,6 +21,7 @@ import { QuickAction, QuickActionCard } from "@components/settings/QuickAction"; import { SpecialCard } from "@components/settings/SpecialCard"; import { gitRemote } from "@shared/vencordUserAgent"; import { DONOR_ROLE_ID, GUILD_ID, IS_MAC, IS_WINDOWS, VC_DONOR_ROLE_ID, VC_GUILD_ID } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { identity, isAnyPluginDev } from "@utils/misc"; import { relaunch } from "@utils/native"; diff --git a/src/equicordplugins/betterBanReasons/index.tsx b/src/equicordplugins/betterBanReasons/index.tsx index 2ca9315b4..68e849e1c 100644 --- a/src/equicordplugins/betterBanReasons/index.tsx +++ b/src/equicordplugins/betterBanReasons/index.tsx @@ -7,10 +7,10 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Heading } from "@components/Heading"; import { DeleteIcon, PlusIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { Button, TextInput } from "@webpack/common"; diff --git a/src/equicordplugins/channelTabs/components/BookmarkContainer.tsx b/src/equicordplugins/channelTabs/components/BookmarkContainer.tsx index e54177681..b2abc1c71 100644 --- a/src/equicordplugins/channelTabs/components/BookmarkContainer.tsx +++ b/src/equicordplugins/channelTabs/components/BookmarkContainer.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { BasicChannelTabsProps, Bookmark, BookmarkFolder, BookmarkProps, isBookmarkFolder, isTabSelected, navigateToBookmark, openedTabs, settings, switchChannel, useBookmarks } from "@equicordplugins/channelTabs/util"; import { CircleQuestionIcon, DiscoveryIcon, EnvelopeIcon, FriendsIcon, NitroIcon, QuestIcon, ShopIcon } from "@equicordplugins/channelTabs/util/icons"; +import { classNameFactory } from "@utils/css"; import { getGuildAcronym, getIntlMessage } from "@utils/discord"; import { classes } from "@utils/misc"; import { closeModal, openModal } from "@utils/modal"; diff --git a/src/equicordplugins/channelTabs/components/ChannelTab.tsx b/src/equicordplugins/channelTabs/components/ChannelTab.tsx index 2cb02900b..b2d823787 100644 --- a/src/equicordplugins/channelTabs/components/ChannelTab.tsx +++ b/src/equicordplugins/channelTabs/components/ChannelTab.tsx @@ -4,11 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { ChannelTabsProps, closeTab, isTabSelected, moveDraggedTabs, moveToTab, openedTabs, settings } from "@equicordplugins/channelTabs/util"; import { ActivityIcon, CircleQuestionIcon, DiscoveryIcon, EnvelopeIcon, FriendsIcon, ICYMIIcon, NitroIcon, QuestIcon, ShopIcon } from "@equicordplugins/channelTabs/util/icons"; import { activeQuestIntervals } from "@equicordplugins/questify"; // sorry murphy! +import { classNameFactory } from "@utils/css"; import { getGuildAcronym, getIntlMessage, getUniqueUsername } from "@utils/discord"; import { classes } from "@utils/misc"; import { Channel, Guild, User } from "@vencord/discord-types"; diff --git a/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx b/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx index 05ad9e253..067d37bc8 100644 --- a/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx +++ b/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx @@ -4,12 +4,12 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { Flex } from "@components/Flex"; import { Heading } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; import { BasicChannelTabsProps, ChannelTabsProps, clearStaleNavigationContext, closeTab, createTab, handleChannelSwitch, isNavigationFromSource, isTabSelected, moveToTab, openedTabs, openStartupTabs, saveTabs, settings, setUpdaterFunction, useGhostTabs } from "@equicordplugins/channelTabs/util"; import { IS_MAC } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { classes } from "@utils/misc"; import { useForceUpdater } from "@utils/react"; import { findComponentByCodeLazy, findStoreLazy } from "@webpack"; diff --git a/src/equicordplugins/commandPalette/ui.tsx b/src/equicordplugins/commandPalette/ui.tsx index f9bcac0b6..256d1bf05 100644 --- a/src/equicordplugins/commandPalette/ui.tsx +++ b/src/equicordplugins/commandPalette/ui.tsx @@ -6,8 +6,8 @@ import "./style.css"; -import { classNameFactory } from "@utils/css"; import { IS_LINUX, IS_MAC, IS_WINDOWS } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { closeAllModals, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { React, TextInput, useCallback, useEffect, useMemo, useRef, useState } from "@webpack/common"; import type { CSSProperties } from "react"; diff --git a/src/equicordplugins/contentWarning/index.tsx b/src/equicordplugins/contentWarning/index.tsx index ee41523f1..cb2283c19 100644 --- a/src/equicordplugins/contentWarning/index.tsx +++ b/src/equicordplugins/contentWarning/index.tsx @@ -8,11 +8,11 @@ import "./styles.css"; import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Flex } from "@components/Flex"; import { HeadingTertiary } from "@components/Heading"; import { DeleteIcon } from "@components/Icons"; import { EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { useForceUpdater } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; import { Button, TextInput, useState } from "@webpack/common"; diff --git a/src/equicordplugins/customSounds/SoundOverrideComponent.tsx b/src/equicordplugins/customSounds/SoundOverrideComponent.tsx index ee4c095d6..8820fd6ba 100644 --- a/src/equicordplugins/customSounds/SoundOverrideComponent.tsx +++ b/src/equicordplugins/customSounds/SoundOverrideComponent.tsx @@ -5,10 +5,10 @@ */ import { AudioPlayerInterface, playAudio } from "@api/AudioPlayer"; -import { classNameFactory } from "@utils/css"; import { Card } from "@components/Card"; import { FormSwitch } from "@components/FormSwitch"; import { Heading } from "@components/Heading"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { useForceUpdater } from "@utils/react"; import { makeRange } from "@utils/types"; diff --git a/src/equicordplugins/customSounds/index.tsx b/src/equicordplugins/customSounds/index.tsx index 0c6fbd470..7af13bd02 100644 --- a/src/equicordplugins/customSounds/index.tsx +++ b/src/equicordplugins/customSounds/index.tsx @@ -9,9 +9,9 @@ import "./styles.css"; import { AudioProcessor, PreprocessAudioData } from "@api/AudioPlayer"; import { get as getFromDataStore } from "@api/DataStore"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Heading } from "@components/Heading"; import { Devs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType, StartAt } from "@utils/types"; import { Button, React, showToast, TextInput } from "@webpack/common"; diff --git a/src/equicordplugins/customUserColors/SetColorModal.tsx b/src/equicordplugins/customUserColors/SetColorModal.tsx index 092425732..eae219390 100644 --- a/src/equicordplugins/customUserColors/SetColorModal.tsx +++ b/src/equicordplugins/customUserColors/SetColorModal.tsx @@ -5,8 +5,8 @@ */ import { set } from "@api/DataStore"; -import { classNameFactory } from "@utils/css"; import { HeadingPrimary, HeadingSecondary } from "@components/Heading"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; import { Button, ColorPicker, useState } from "@webpack/common"; diff --git a/src/equicordplugins/elementHighlighter.dev/index.tsx b/src/equicordplugins/elementHighlighter.dev/index.tsx index b1eb7a04b..ad31dabe4 100644 --- a/src/equicordplugins/elementHighlighter.dev/index.tsx +++ b/src/equicordplugins/elementHighlighter.dev/index.tsx @@ -7,9 +7,9 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { EquicordDevs, IS_MAC } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { copyWithToast } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; diff --git a/src/equicordplugins/exitSounds/index.tsx b/src/equicordplugins/exitSounds/index.tsx index 378012509..6e96327ff 100644 --- a/src/equicordplugins/exitSounds/index.tsx +++ b/src/equicordplugins/exitSounds/index.tsx @@ -9,9 +9,9 @@ import "./styles.css"; import { playAudio } from "@api/AudioPlayer"; import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; import { ChannelActions, Constants, GuildStore, IconUtils, MediaEngineStore, Menu, RestAPI, SearchableSelect, SelectedChannelStore, TextInput, Toasts } from "@webpack/common"; diff --git a/src/equicordplugins/ghosted/index.tsx b/src/equicordplugins/ghosted/index.tsx index 0318ee9d5..27963df1e 100644 --- a/src/equicordplugins/ghosted/index.tsx +++ b/src/equicordplugins/ghosted/index.tsx @@ -8,9 +8,9 @@ import "./styles.css"; import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList"; import { definePluginSettings, migratePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs, EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { closeModal, openModal } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { Channel } from "@vencord/discord-types"; diff --git a/src/equicordplugins/githubRepos/index.tsx b/src/equicordplugins/githubRepos/index.tsx index f1386cf4d..408bfe975 100644 --- a/src/equicordplugins/githubRepos/index.tsx +++ b/src/equicordplugins/githubRepos/index.tsx @@ -7,10 +7,10 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import ErrorBoundary from "@components/ErrorBoundary"; import { EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { User } from "@vencord/discord-types"; import { findByCodeLazy } from "@webpack"; diff --git a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx index eaac41711..2a7444c6e 100644 --- a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx +++ b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx @@ -6,8 +6,8 @@ import "./style.css"; -import { classNameFactory } from "@utils/css"; import { HiddenServersStore } from "@equicordplugins/hideServers/HiddenServersStore"; +import { classNameFactory } from "@utils/css"; import { Button, GuildStore, useStateFromStores } from "@webpack/common"; import { openHiddenServersModal } from "./HiddenServersMenu"; diff --git a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx index 2af6015aa..6f94bbe3f 100644 --- a/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx +++ b/src/equicordplugins/hideServers/components/HiddenServersMenu.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Heading } from "@components/Heading"; import { HiddenServersStore } from "@equicordplugins/hideServers/HiddenServersStore"; +import { classNameFactory } from "@utils/css"; import { getGuildAcronym } from "@utils/discord"; import { classes } from "@utils/misc"; import { diff --git a/src/equicordplugins/iRememberYou/components/ui.tsx b/src/equicordplugins/iRememberYou/components/ui.tsx index c0dacde26..f75147cd8 100644 --- a/src/equicordplugins/iRememberYou/components/ui.tsx +++ b/src/equicordplugins/iRememberYou/components/ui.tsx @@ -4,12 +4,12 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Card } from "@components/Card"; import { HeadingPrimary } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; import { SettingsTab, wrapTab } from "@components/settings"; +import { classNameFactory } from "@utils/css"; import { openUserProfile } from "@utils/discord"; import { Avatar, Clickable, React, TextInput, Tooltip, } from "@webpack/common"; diff --git a/src/equicordplugins/ignoreCalls/index.tsx b/src/equicordplugins/ignoreCalls/index.tsx index d407e9032..16dad93d9 100644 --- a/src/equicordplugins/ignoreCalls/index.tsx +++ b/src/equicordplugins/ignoreCalls/index.tsx @@ -8,10 +8,10 @@ import "./styles.css"; import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { ErrorBoundary } from "@components/index"; import { Devs, EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { Channel } from "@vencord/discord-types"; import { findComponentByCodeLazy } from "@webpack"; diff --git a/src/equicordplugins/inRole/RoleMembersModal.tsx b/src/equicordplugins/inRole/RoleMembersModal.tsx index 79a5adcf2..6483b105b 100644 --- a/src/equicordplugins/inRole/RoleMembersModal.tsx +++ b/src/equicordplugins/inRole/RoleMembersModal.tsx @@ -4,9 +4,9 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Paragraph } from "@components/Paragraph"; +import { classNameFactory } from "@utils/css"; import { ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { GuildMember } from "@vencord/discord-types"; import { Parser } from "@webpack/common"; diff --git a/src/equicordplugins/keyboardNavigation/components/CommandPalette.tsx b/src/equicordplugins/keyboardNavigation/components/CommandPalette.tsx index 1d61636c5..d860aeace 100644 --- a/src/equicordplugins/keyboardNavigation/components/CommandPalette.tsx +++ b/src/equicordplugins/keyboardNavigation/components/CommandPalette.tsx @@ -6,8 +6,8 @@ import "./style.css"; -import { classNameFactory } from "@utils/css"; import { actions } from "@equicordplugins/keyboardNavigation/commands"; +import { classNameFactory } from "@utils/css"; import { Logger } from "@utils/Logger"; import { closeAllModals, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { React, TextInput, useEffect, useState } from "@webpack/common"; diff --git a/src/equicordplugins/keyboardNavigation/components/MultipleChoice.tsx b/src/equicordplugins/keyboardNavigation/components/MultipleChoice.tsx index c311ff510..e2ecc64c2 100644 --- a/src/equicordplugins/keyboardNavigation/components/MultipleChoice.tsx +++ b/src/equicordplugins/keyboardNavigation/components/MultipleChoice.tsx @@ -6,8 +6,8 @@ import "./style.css"; -import { classNameFactory } from "@utils/css"; import { ButtonAction } from "@equicordplugins/keyboardNavigation/commands"; +import { classNameFactory } from "@utils/css"; import { closeAllModals, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { React, TextInput, useEffect, useState } from "@webpack/common"; diff --git a/src/equicordplugins/keyboardNavigation/index.tsx b/src/equicordplugins/keyboardNavigation/index.tsx index 39c6b7b8c..904d19ce8 100644 --- a/src/equicordplugins/keyboardNavigation/index.tsx +++ b/src/equicordplugins/keyboardNavigation/index.tsx @@ -5,8 +5,8 @@ */ import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Devs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { closeAllModals } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { openUserSettingsPanel, useState } from "@webpack/common"; diff --git a/src/equicordplugins/keywordNotify/index.tsx b/src/equicordplugins/keywordNotify/index.tsx index ed161e44f..52556e4dd 100644 --- a/src/equicordplugins/keywordNotify/index.tsx +++ b/src/equicordplugins/keywordNotify/index.tsx @@ -8,12 +8,12 @@ import "./style.css"; import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Flex } from "@components/Flex"; import { FormSwitch } from "@components/FormSwitch"; import { Heading, HeadingTertiary } from "@components/Heading"; import { DeleteIcon } from "@components/Icons"; import { EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { useForceUpdater } from "@utils/react"; diff --git a/src/equicordplugins/messageLoggerEnhanced/components/FolderSelectInput.tsx b/src/equicordplugins/messageLoggerEnhanced/components/FolderSelectInput.tsx index 11de8ed9e..c0d2ff937 100644 --- a/src/equicordplugins/messageLoggerEnhanced/components/FolderSelectInput.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/components/FolderSelectInput.tsx @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import { classNameFactory } from "@utils/css"; import { Heading } from "@components/Heading"; import { DEFAULT_IMAGE_CACHE_DIR } from "@equicordplugins/messageLoggerEnhanced/utils/constants"; +import { classNameFactory } from "@utils/css"; import { copyWithToast } from "@utils/discord"; import { Button, Toasts } from "@webpack/common"; diff --git a/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx b/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx index 3e73d97a1..174901695 100644 --- a/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx @@ -4,7 +4,6 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Flex } from "@components/Flex"; import { InfoIcon } from "@components/Icons"; @@ -13,6 +12,7 @@ import { settings } from "@equicordplugins/messageLoggerEnhanced/index"; import { LoggedMessage, LoggedMessageJSON } from "@equicordplugins/messageLoggerEnhanced/types"; import { messageJsonToMessageClass } from "@equicordplugins/messageLoggerEnhanced/utils"; import { importLogs } from "@equicordplugins/messageLoggerEnhanced/utils/settingsUtils"; +import { classNameFactory } from "@utils/css"; import { copyWithToast, openUserProfile } from "@utils/discord"; import { closeAllModals, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { LazyComponent } from "@utils/react"; diff --git a/src/equicordplugins/moreStickers/utils.tsx b/src/equicordplugins/moreStickers/utils.tsx index e2e501bfa..0933e1458 100644 --- a/src/equicordplugins/moreStickers/utils.tsx +++ b/src/equicordplugins/moreStickers/utils.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import type { FFmpeg } from "@ffmpeg/ffmpeg"; +import { classNameFactory } from "@utils/css"; import { waitFor } from "@webpack"; import { React } from "@webpack/common"; diff --git a/src/equicordplugins/moreUserTags/index.tsx b/src/equicordplugins/moreUserTags/index.tsx index 2f6a8efc4..4889178e0 100644 --- a/src/equicordplugins/moreUserTags/index.tsx +++ b/src/equicordplugins/moreUserTags/index.tsx @@ -6,8 +6,8 @@ import "./styles.css"; -import { classNameFactory } from "@utils/css"; import { Devs, EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { getCurrentChannel, getIntlMessage } from "@utils/discord"; import definePlugin from "@utils/types"; import { Channel, Message, User } from "@vencord/discord-types"; diff --git a/src/equicordplugins/musicControls/spotify/lyrics/components/util.tsx b/src/equicordplugins/musicControls/spotify/lyrics/components/util.tsx index b390277bf..dd1da812f 100644 --- a/src/equicordplugins/musicControls/spotify/lyrics/components/util.tsx +++ b/src/equicordplugins/musicControls/spotify/lyrics/components/util.tsx @@ -4,11 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { settings } from "@equicordplugins/musicControls/settings"; import { SpotifyLrcStore } from "@equicordplugins/musicControls/spotify/lyrics/providers/store"; import { SyncedLyric } from "@equicordplugins/musicControls/spotify/lyrics/providers/types"; import { SpotifyStore } from "@equicordplugins/musicControls/spotify/SpotifyStore"; +import { classNameFactory } from "@utils/css"; import { findByPropsLazy } from "@webpack"; import { React, useEffect, useState, useStateFromStores } from "@webpack/common"; diff --git a/src/equicordplugins/musicControls/tidal/lyrics/components/util.tsx b/src/equicordplugins/musicControls/tidal/lyrics/components/util.tsx index 04968b02f..8aa0002b2 100644 --- a/src/equicordplugins/musicControls/tidal/lyrics/components/util.tsx +++ b/src/equicordplugins/musicControls/tidal/lyrics/components/util.tsx @@ -4,11 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { settings } from "@equicordplugins/musicControls/settings"; import { TidalLrcStore } from "@equicordplugins/musicControls/tidal/lyrics/providers/store"; import { EnhancedLyric } from "@equicordplugins/musicControls/tidal/lyrics/types"; import { TidalStore } from "@equicordplugins/musicControls/tidal/TidalStore"; +import { classNameFactory } from "@utils/css"; import { findByPropsLazy } from "@webpack"; import { React, useEffect, useState, useStateFromStores } from "@webpack/common"; diff --git a/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx b/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx index 6644d889d..fd8277a02 100644 --- a/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx +++ b/src/equicordplugins/newPluginsManager/NewPluginsModal.tsx @@ -7,7 +7,6 @@ import "./styles.css"; import { Settings, useSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import { Button } from "@components/Button"; import ErrorBoundary from "@components/ErrorBoundary"; @@ -15,6 +14,7 @@ import { Flex } from "@components/Flex"; import { PluginDependencyList } from "@components/settings/tabs/plugins"; import { PluginCard } from "@components/settings/tabs/plugins/PluginCard"; import { ChangeList } from "@utils/ChangeList"; +import { classNameFactory } from "@utils/css"; import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { useForceUpdater } from "@utils/react"; import { findComponentByCodeLazy } from "@webpack"; diff --git a/src/equicordplugins/questify/utils/misc.tsx b/src/equicordplugins/questify/utils/misc.tsx index 539de8d94..5bdb694e8 100644 --- a/src/equicordplugins/questify/utils/misc.tsx +++ b/src/equicordplugins/questify/utils/misc.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { questIsIgnored, settings } from "@equicordplugins/questify/settings"; +import { classNameFactory } from "@utils/css"; import { Logger } from "@utils/Logger"; import { findByPropsLazy } from "@webpack"; import { FluxDispatcher, RestAPI, UserStore } from "@webpack/common"; diff --git a/src/equicordplugins/recentDMSwitcher/index.tsx b/src/equicordplugins/recentDMSwitcher/index.tsx index a063f77f5..ac561bd56 100644 --- a/src/equicordplugins/recentDMSwitcher/index.tsx +++ b/src/equicordplugins/recentDMSwitcher/index.tsx @@ -8,8 +8,8 @@ import "./styles.css"; import * as DataStore from "@api/DataStore"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { EquicordDevs, IS_MAC } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { closeModal, openModal } from "@utils/modal"; import definePlugin, { makeRange, OptionType } from "@utils/types"; import { Button, ChannelRouter, ChannelStore, IconUtils, React, RelationshipStore, SelectedChannelStore, Toasts, UserStore } from "@webpack/common"; diff --git a/src/equicordplugins/repeatMessages/index.tsx b/src/equicordplugins/repeatMessages/index.tsx index a13596e90..6ca6bc494 100644 --- a/src/equicordplugins/repeatMessages/index.tsx +++ b/src/equicordplugins/repeatMessages/index.tsx @@ -6,8 +6,8 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { migratePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Devs, EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { sendMessage } from "@utils/discord"; import { useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; diff --git a/src/equicordplugins/scheduledMessages/components/ScheduleTimeModal.tsx b/src/equicordplugins/scheduledMessages/components/ScheduleTimeModal.tsx index 466452116..95f4348af 100644 --- a/src/equicordplugins/scheduledMessages/components/ScheduleTimeModal.tsx +++ b/src/equicordplugins/scheduledMessages/components/ScheduleTimeModal.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import ErrorBoundary from "@components/ErrorBoundary"; import { Heading } from "@components/Heading"; +import { classNameFactory } from "@utils/css"; import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; import { findByPropsLazy } from "@webpack"; import { ChannelStore, DraftType, showToast, TextInput, Toasts, UploadManager, useState } from "@webpack/common"; diff --git a/src/equicordplugins/scheduledMessages/components/ViewScheduledModal.tsx b/src/equicordplugins/scheduledMessages/components/ViewScheduledModal.tsx index b83a83e00..1c41e9dbe 100644 --- a/src/equicordplugins/scheduledMessages/components/ViewScheduledModal.tsx +++ b/src/equicordplugins/scheduledMessages/components/ViewScheduledModal.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import ErrorBoundary from "@components/ErrorBoundary"; import { Heading } from "@components/Heading"; +import { classNameFactory } from "@utils/css"; import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; import { ChannelStore, showToast, Toasts, useState } from "@webpack/common"; diff --git a/src/equicordplugins/themeLibrary/utils/settings.tsx b/src/equicordplugins/themeLibrary/utils/settings.tsx index 6ca48a45d..f7992a469 100644 --- a/src/equicordplugins/themeLibrary/utils/settings.tsx +++ b/src/equicordplugins/themeLibrary/utils/settings.tsx @@ -6,11 +6,11 @@ import * as DataStore from "@api/DataStore"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { HeadingSecondary } from "@components/Heading"; import { OpenExternalIcon } from "@components/Icons"; import { Paragraph } from "@components/Paragraph"; import { copyToClipboard } from "@utils/clipboard"; +import { classNameFactory } from "@utils/css"; import { OptionType } from "@utils/types"; import { Button, Toasts } from "@webpack/common"; diff --git a/src/equicordplugins/timezones/TimezoneModal.tsx b/src/equicordplugins/timezones/TimezoneModal.tsx index b9dde2bf0..8d599fd1f 100644 --- a/src/equicordplugins/timezones/TimezoneModal.tsx +++ b/src/equicordplugins/timezones/TimezoneModal.tsx @@ -5,8 +5,8 @@ */ import * as DataStore from "@api/DataStore"; -import { classNameFactory } from "@utils/css"; import { HeadingPrimary, HeadingSecondary } from "@components/Heading"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; import { Button, SearchableSelect, useEffect, useMemo, useState } from "@webpack/common"; diff --git a/src/equicordplugins/urlHighlighter/index.tsx b/src/equicordplugins/urlHighlighter/index.tsx index 41065042f..e717ef883 100644 --- a/src/equicordplugins/urlHighlighter/index.tsx +++ b/src/equicordplugins/urlHighlighter/index.tsx @@ -7,11 +7,11 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import ErrorBoundary from "@components/ErrorBoundary"; import { Heading } from "@components/Heading"; import { EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; import { ColorPicker, TextInput } from "@webpack/common"; diff --git a/src/equicordplugins/userpfp/index.tsx b/src/equicordplugins/userpfp/index.tsx index 61683c5b8..fc7ed6de5 100644 --- a/src/equicordplugins/userpfp/index.tsx +++ b/src/equicordplugins/userpfp/index.tsx @@ -7,11 +7,11 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { Flex } from "@components/Flex"; import { Heart } from "@components/Heart"; import { Devs, EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { openInviteModal } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { User } from "@vencord/discord-types"; diff --git a/src/equicordplugins/voiceChannelLog.dev/index.tsx b/src/equicordplugins/voiceChannelLog.dev/index.tsx index 3c6d25961..e5ed8fd12 100644 --- a/src/equicordplugins/voiceChannelLog.dev/index.tsx +++ b/src/equicordplugins/voiceChannelLog.dev/index.tsx @@ -6,8 +6,8 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Devs, EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { Message, User } from "@vencord/discord-types"; import { findByCodeLazy } from "@webpack"; diff --git a/src/equicordplugins/whosWatching/index.tsx b/src/equicordplugins/whosWatching/index.tsx index a49730b38..87a48cad4 100644 --- a/src/equicordplugins/whosWatching/index.tsx +++ b/src/equicordplugins/whosWatching/index.tsx @@ -5,12 +5,12 @@ */ import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Heading, HeadingSecondary } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; import { EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { getIntlMessage, openUserProfile } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; diff --git a/src/plugins/serverListIndicators/index.tsx b/src/plugins/serverListIndicators/index.tsx index f9982fd40..3f04013ce 100644 --- a/src/plugins/serverListIndicators/index.tsx +++ b/src/plugins/serverListIndicators/index.tsx @@ -20,10 +20,10 @@ import "./styles.css"; import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { BaseText } from "@components/BaseText"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs, EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; import { findStoreLazy } from "@webpack"; import { GuildStore, PresenceStore, RelationshipStore, Tooltip, useStateFromStores } from "@webpack/common"; diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index 3ea93d484..72e551c8b 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -19,9 +19,9 @@ import "./styles.css"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Button } from "@components/Button"; import { Devs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import definePlugin, { OptionType } from "@utils/types"; const cl = classNameFactory("vc-usrbg-"); diff --git a/src/plugins/voiceMessages/index.tsx b/src/plugins/voiceMessages/index.tsx index 5ce278fb6..8bfecee20 100644 --- a/src/plugins/voiceMessages/index.tsx +++ b/src/plugins/voiceMessages/index.tsx @@ -20,7 +20,6 @@ import "./styles.css"; import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@utils/css"; import { Card } from "@components/Card"; import { Heading } from "@components/Heading"; import { Microphone } from "@components/Icons"; @@ -28,6 +27,7 @@ import { Link } from "@components/Link"; import { Paragraph } from "@components/Paragraph"; import { lastState as silentMessageEnabled } from "@plugins/silentMessageToggle"; import { Devs, EquicordDevs } from "@utils/constants"; +import { classNameFactory } from "@utils/css"; import { Margins } from "@utils/margins"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; import { useAwaiter } from "@utils/react"; From ba2d8566569535427e8971e07e2e7c1bff3d1b4b Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 19 Dec 2025 21:40:07 -0500 Subject: [PATCH 034/108] Equibop: Move IS_VESKTOP Checks --- src/api/Styles.ts | 2 +- src/main/ipcMain.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/Styles.ts b/src/api/Styles.ts index 967cef640..ea3324879 100644 --- a/src/api/Styles.ts +++ b/src/api/Styles.ts @@ -64,7 +64,7 @@ export function initStyles() { }); } - if (IS_VESKTOP && VesktopNative.app.getRendererCss) { + if (IS_VESKTOP && VesktopNative.app.getRendererCss || IS_EQUIBOP && VesktopNative.app.getRendererCss) { VesktopNative.app.getRendererCss().then(css => vesktopCssNode!.textContent = css); VesktopNative.app.onRendererCssUpdate(newCss => { vesktopCssNode!.textContent = newCss; diff --git a/src/main/ipcMain.ts b/src/main/ipcMain.ts index 0718883e9..977641ed1 100644 --- a/src/main/ipcMain.ts +++ b/src/main/ipcMain.ts @@ -32,7 +32,7 @@ import { registerCspIpcHandlers } from "./csp/manager"; 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"); +const RENDERER_CSS_PATH = join(__dirname, IS_EQUIBOP ? "equibop/renderer.css" : "desktop/renderer.css"); mkdirSync(THEMES_DIR, { recursive: true }); From 2d51b7cc40e3a92804a4d254a47a9ebc354f779e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 20 Dec 2025 20:49:08 +0100 Subject: [PATCH 035/108] FixImageQuality: fix embed images --- src/plugins/fixImagesQuality/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/fixImagesQuality/index.tsx b/src/plugins/fixImagesQuality/index.tsx index 29aaabad4..848cedb0d 100644 --- a/src/plugins/fixImagesQuality/index.tsx +++ b/src/plugins/fixImagesQuality/index.tsx @@ -68,6 +68,8 @@ export default definePlugin({ if (!contentType?.startsWith("image/") || src.startsWith("data:")) return; const url = new URL(src); + if (!url.pathname.startsWith("/attachments/")) return; + url.searchParams.set("animated", String(!freeze)); if (!settings.store.originalImagesInChat && mediaLayoutType === "MOSAIC") { From 7eb5602623f83f0547c9fa821b7e033a2b45216e Mon Sep 17 00:00:00 2001 From: prism Date: Sat, 20 Dec 2025 21:09:14 +0100 Subject: [PATCH 036/108] UI Tweaks and Code Fixes (#584) * fix: correct renderer css path * CloudTab: add sync rules and minor UI tweaks * CloudTab: more ui tweak * CloudTab: fix cloud icons --- .../settings/tabs/sync/CloudTab.tsx | 173 +++++++++++------- src/main/ipcMain.ts | 2 +- 2 files changed, 111 insertions(+), 64 deletions(-) diff --git a/src/components/settings/tabs/sync/CloudTab.tsx b/src/components/settings/tabs/sync/CloudTab.tsx index ad6aec597..3f2f5a0ab 100644 --- a/src/components/settings/tabs/sync/CloudTab.tsx +++ b/src/components/settings/tabs/sync/CloudTab.tsx @@ -26,16 +26,27 @@ import { Divider } from "@components/Divider"; import { Flex } from "@components/Flex"; import { FormSwitch } from "@components/FormSwitch"; import { Heading } from "@components/Heading"; +import { CloudDownloadIcon, CloudUploadIcon } from "@components/Icons"; import { Link } from "@components/Link"; import { Paragraph } from "@components/Paragraph"; -import { QuickAction, QuickActionCard } from "@components/settings/QuickAction"; import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab"; +import { localStorage } from "@utils/localStorage"; import { Margins } from "@utils/margins"; +import { useForceUpdater } from "@utils/react"; import { findComponentByCodeLazy } from "@webpack"; -import { Alerts, useState } from "@webpack/common"; +import { Alerts, SearchableSelect, Select, useState } from "@webpack/common"; -const UploadIcon = findComponentByCodeLazy("M12.7 3.3a1 1 0 0 0-1.4 0l-5 5a1 1 0 0 0 1.4 1.4L11 6.42V20"); -const DownloadIcon = findComponentByCodeLazy("M12.7 20.7a1 1 0 0 1-1.4 0l-5-5a1 1 0 1 1 1.4-1.4l3.3 3.29V4"); +const ICON_STYLE: React.CSSProperties = { width: 20, height: 20, borderRadius: 4, verticalAlign: "middle" }; + +function EquicordIcon() { + return Equicord; +} + +function VencordIcon() { + return Vencord; +} + +const RefreshIcon = findComponentByCodeLazy("M4 12a8 8 0 0 1 14.93-4H15"); const TrashIcon = findComponentByCodeLazy("2.81h8.36a3"); const SkullIcon = findComponentByCodeLazy("m13.47 1 .07.04c.45.06"); @@ -48,9 +59,22 @@ function validateUrl(url: string) { } } +const cloudBackendOptions = [ + { label: "Equicord Cloud", value: "https://cloud.equicord.org/" }, + { label: "Vencord Cloud", value: "https://api.vencord.dev/" } +]; + +const syncDirectionOptions = [ + { label: "Two-way sync (changes go both directions)", value: "both" }, + { label: "This device is the source (upload only)", value: "push" }, + { label: "The cloud is the source (download only)", value: "pull" }, + { label: "Do not sync automatically (manual sync via buttons below only)", value: "manual" } +]; + function CloudTab() { const settings = useSettings(["cloud.authenticated", "cloud.url", "cloud.settingsSync"]); const [inputKey, setInputKey] = useState(0); + const forceUpdate = useForceUpdater(); const { cloud } = settings; const isAuthenticated = cloud.authenticated; @@ -73,7 +97,7 @@ function CloudTab() { Equicord's cloud integration allows you to sync your settings across multiple devices and Discord installations. Your data is securely stored and can be easily restored at any time. - + We use our own Equicloud backend with enhanced features. View our privacy policy to see what we store and how we use your data. Equicloud is BSD 3.0 licensed, so you can self-host if preferred. @@ -95,27 +119,33 @@ function CloudTab() { Cloud Backend - + Choose which cloud backend to use for storing your settings. You can switch between Equicord's and Vencord's cloud services, or use a self-hosted instance. - - Current: {cloud.url} - - { - cloud.url = v; - cloud.authenticated = false; - await deauthorizeCloud(); - }} - validate={validateUrl} + o.value === cloud.url)} + onChange={v => changeUrl(v)} + className={Margins.bottom16} + closeOnSelect={true} + renderOptionPrefix={o => o?.value?.includes("equicord") ? : } /> - + +
    + { + cloud.url = v; + cloud.authenticated = false; + await deauthorizeCloud(); + }} + validate={validateUrl} + /> +
    - -
    @@ -157,25 +176,49 @@ function CloudTab() { hideBorder /> - {isAuthenticated && ( - - putCloudSettings(true)} - disabled={!syncEnabled} - /> - getCloudSettings(true, true)} - disabled={!syncEnabled} - /> - - )} + + + Sync Rules for This Device + + This setting controls how settings move between this device and the cloud. You can let changes flow both ways, or choose one place to be the main source of truth. + + +