mirror of
https://github.com/Vendicated/Vencord.git
synced 2024-09-20 06:30:35 +00:00
add plugin FixHardcodedColors
This commit is contained in:
parent
67dfaa95ba
commit
6ae30fa2fa
2 changed files with 115 additions and 0 deletions
22
src/plugins/fixHardcodedColors/README.md
Normal file
22
src/plugins/fixHardcodedColors/README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
Discord often hardcodes colors despite having css variables for all it's colors.
|
||||
|
||||
For example, `--primary-160` is `#ebedef`.
|
||||
|
||||
But in the code, they have hardcoded the color hex instead of using the variable
|
||||
```css
|
||||
.defaultLightModeCustomGradient_e77fa3 {
|
||||
background: linear-gradient(rgba(0,0,0,0) 20%, #ebedef 100%);
|
||||
}
|
||||
```
|
||||
|
||||
This causes issues for theme devs who want to make stuff by directly modifying color variables as they need to manually fix all these problems.
|
||||
|
||||
This is very prevalent when using ClientTheme and looking at "channels and roles"
|
||||
![Discord_tn6oWjipFv](https://github.com/Vendicated/Vencord/assets/37855219/e74e41af-b277-4b28-83be-f87807bad16d)
|
||||
|
||||
This plugin addresses this issue by generating css to make the problematic code use color variables instead, for example:
|
||||
```css
|
||||
.defaultLightModeCustomGradient_e77fa3 {
|
||||
background: linear-gradient(rgba(0,0,0,0) 20%, var(--primary-160) 100%);
|
||||
}
|
||||
```
|
93
src/plugins/fixHardcodedColors/index.ts
Normal file
93
src/plugins/fixHardcodedColors/index.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { StartAt } from "@utils/types";
|
||||
import { createStyleSheet, getStyles, newStyleListener } from "plugins/clientTheme/cssUtil";
|
||||
|
||||
export default definePlugin({
|
||||
name: "Fix hardcoded colors",
|
||||
description: "replace hardcoded colors with color variables",
|
||||
authors: [Devs.F53],
|
||||
startAt: StartAt.DOMContentLoaded,
|
||||
|
||||
async start() {
|
||||
const styles = await getStyles();
|
||||
const colorVariables = getColorVariables(styles);
|
||||
|
||||
const fixSheet = createStyleSheet("hardcodedColorFixes", generateFixes(colorVariables, styles));
|
||||
newStyleListener(styles => fixSheet.innerText += generateFixes(colorVariables, styles));
|
||||
},
|
||||
stop() {
|
||||
document.getElementById("hardcodedColorFixes")?.remove();
|
||||
}
|
||||
});
|
||||
|
||||
function generateFixes(colorVariables: ColorVariable[], styles: string) {
|
||||
const stylesToFix = getStylesWithColors(styles);
|
||||
let out = "";
|
||||
for (const style of stylesToFix)
|
||||
out += generateFix(colorVariables, style);
|
||||
return out;
|
||||
}
|
||||
|
||||
function generateFix(colorVariables: ColorVariable[], problematicStyle: string) {
|
||||
const selector = /^[^{]*/.exec(problematicStyle)?.[0];
|
||||
const rules = Array.from(problematicStyle.matchAll(/(?:{|;)([a-z-]+):([^;}]+)/g), match => ({ property: match[1], value: match[2] }));
|
||||
if (!selector) return "";
|
||||
|
||||
const fixes: string[] = [];
|
||||
for (const rule of rules) {
|
||||
let fixedValue = rule.value;
|
||||
for (const match of Array.from(rule.value.matchAll(/#[a-f\d]{6}|rgb\(\d+,\d+\d+\)/g))) {
|
||||
const rgb = toRGB(match[0]);
|
||||
for (const color of colorVariables) {
|
||||
const distance = rgb.reduce((totalDistance, b, i) => totalDistance + Math.abs(b - color.rgb[i]), 0);
|
||||
if (distance > 5) continue;
|
||||
|
||||
fixedValue = fixedValue.replaceAll(match[0], ` var(${color.variable}) `);
|
||||
break; // already found variable to replace it, don't keep looking
|
||||
}
|
||||
}
|
||||
fixedValue.replaceAll(" ", "");
|
||||
if (fixedValue !== rule.value)
|
||||
fixes.push(`${rule.property}:${fixedValue}`);
|
||||
}
|
||||
if (fixes.length === 0) return "";
|
||||
|
||||
return `${selector}{${fixes.join(";")}}`;
|
||||
}
|
||||
|
||||
const cssWithColorRegex = /(?:^|})[^{}]+?{[^}]*?(?:#[a-f\d]{6}|rgb\(\d+,\d+,\d+\))[^}]*?}/g;
|
||||
// gets array of styles that hardcode color
|
||||
function getStylesWithColors(styles: string) {
|
||||
return Array.from(styles.matchAll(cssWithColorRegex), match => {
|
||||
if (match[0][0] === "}") return match[0].slice(1);
|
||||
return match[0];
|
||||
});
|
||||
}
|
||||
|
||||
const variableRegex = /(--[a-z-\d]*?)-hsl:(\d+).*?(\d+\.?\d*)%.*?(\d+\.?\d*)%/g;
|
||||
interface ColorVariable { variable: string, rgb: [number, number, number]; }
|
||||
function getColorVariables(styles: string): ColorVariable[] {
|
||||
return Array.from(styles.matchAll(variableRegex), match => {
|
||||
const variable = match[1];
|
||||
const [h, s, l] = match.slice(2);
|
||||
return { variable, rgb: toRGB(`hsl(${h},${s}%,${l}%)`) };
|
||||
}).filter(color => // ignore solid white/black colors because they are weird
|
||||
color.rgb.some(b => b !== 255) && color.rgb.some(b => b !== 0)
|
||||
).toSorted(a => // prefer --primary colors over anything else
|
||||
a.variable.startsWith("--primary") ? -1 : 0
|
||||
);
|
||||
}
|
||||
|
||||
function toRGB(color: string) {
|
||||
// https://stackoverflow.com/a/74662179/8133370
|
||||
const { style } = new Option();
|
||||
style.color = color; // for some reason this is immediately translated into "rgb(x, x, x)", no matter the input
|
||||
// turn into array [r: number, g: number, b: number]
|
||||
return Array.from(style.color.matchAll(/\b\d+\b/g)).flatMap(Number) as [number, number, number];
|
||||
}
|
Loading…
Reference in a new issue