more things

This commit is contained in:
sadan 2024-11-26 00:49:19 -05:00
parent fc30e05414
commit 7f7456f347
No known key found for this signature in database
2 changed files with 144 additions and 14 deletions

View file

@ -37,7 +37,21 @@ interface OKLAB {
} }
const RGB_REGEX = /rgb\((?:(\d+(?:\.\d+)?),? ?)(?:(\d+(?:\.\d+)?),? ?)(?:(\d+(?:\.\d+)?),? ?)\)/; const RGB_REGEX = /rgb\((?:(\d+(?:\.\d+)?),? ?)(?:(\d+(?:\.\d+)?),? ?)(?:(\d+(?:\.\d+)?),? ?)\)/;
/**
* 1: colorspace
*
* 2: color 1 val
*
* 3?: color 1 percentage
*
* 4: color 2
*
* 5?: color 2 percentage
*/
const COLOR_MIX_REGEX = /color-mix\( ?in ([^,]+), ?([^,]+?) ?(\d+)?%? ?, (.+?) ?(\d+)?%? ?\)$/;
const color_mix_cleanup = (str: string) => str.replaceAll(/ +/g, " ").replaceAll("\n", "").replaceAll(/calc\(1 ?\* ? ([^)]+) \)/g, "$1");
const HSL_REGEX = /hsl\((\d+(?:\.\d+)?)(?<hueunits>turn|deg)?(?:, ?|,? )(\d+(?:\.\d+)?)%?(?:, ?|,? )(\d+(?:\.\d+)?)%?(?: ?\)$| ?\/ ?(\d?(?:\.\d+)?)\)$)/;
const hsl_cleanup = (str: string) => str.replaceAll(/calc\(1 ?\* ?([^)]+?)\)/g, "$1");
export class Color { export class Color {
private sRGB: sRGB; private sRGB: sRGB;
private get lRGB(): lRGB { private get lRGB(): lRGB {
@ -135,12 +149,40 @@ export class Color {
} }
} }
public static parse(color: string): Color { /**
{ *
* @param withBG **If color has transparnecy, this needs to be provided**
* @returns
*/
public static parse(color: string, withBG: string = ""): Color {
/* hex: */{
const c = color.replaceAll("#", ""); const c = color.replaceAll("#", "");
if (c.length === 3 || c.length === 6) if (c.length === 3 || c.length === 6)
return new Color(Color.hexToRGB(color)); return new Color(Color.hexToRGB(color));
} }
hsl: {
if (!color.startsWith("hsl(")) break hsl;
color = hsl_cleanup(color);
const parsed = color.match(HSL_REGEX);
if (!parsed) throw new Error("failed to parse HSL(): " + color);
// eslint-disable-next-line prefer-const
let [, hue, units, sat, lig, alpha]: any = parsed;
hue = parseFloat(hue);
hue = units === "turn" ? hue * 360 : hue;
sat = parseFloat(sat);
lig = parseFloat(lig);
sat /= 100;
lig /= 100;
if (Number.isNaN(hue + sat + lig))
throw new Error("invalid hsl value. got: " + color);
const toRet = new Color({
type: "hsl",
h: hue,
s: sat,
l: lig,
});
return alpha ? toRet.withOpacity(Color.parse(withBG), alpha) : toRet;
}
rgb: { rgb: {
const c = color.match(RGB_REGEX); const c = color.match(RGB_REGEX);
if (!c) break rgb; if (!c) break rgb;
@ -156,6 +198,44 @@ export class Color {
b: b / 255 b: b / 255
}); });
} }
colormix: {
if (!color.startsWith("color-mix(")) break colormix;
color = color_mix_cleanup(color);
const parsed = color.match(COLOR_MIX_REGEX);
if (!parsed?.[3]) throw new Error("Error parsing color-mix: " + color);
const color1 = parsed[2],
colorSpace = parsed[1];
let color2: string, p1: string | undefined, p2: string | undefined;
switch (parsed.length) {
case 4: {
const [, , , c2] = parsed;
color2 = c2;
break;
}
case 5: {
const [, , , c1P, c2] = parsed;
color2 = c2;
p1 = c1P;
break;
}
case 6: {
const [, , , c1P, c2, c2P] = parsed;
color2 = c2;
p1 = c1P;
p2 = c2P;
break;
}
default: {
throw new Error("Error parsing color-mix" + color);
}
}
if (p1 && p2 && +p1 + +p2 !== 100) {
throw new Error("percents do not add up to 100. percents that add up to less than 100 are not supported at this time");
}
const parsedColor1 = Color.parse(color1, withBG);
const parsedColor2 = Color.parse(color2, withBG);
return parsedColor1.mix(colorSpace, parseFloat(p1 || "50") / 100, parsedColor2);
}
throw new Error("Color not recognized. got: " + color); throw new Error("Color not recognized. got: " + color);
} }
@ -163,7 +243,7 @@ export class Color {
return (fg.lumin + 0.05) / (bg.lumin + 0.05); return (fg.lumin + 0.05) / (bg.lumin + 0.05);
} }
public mix(colorspace: "oklab", thisPercent: number, other: Color, otherPercent = 1 - thisPercent): Color { public mix(colorspace: "oklab" | (string & {}), thisPercent: number, other: Color, otherPercent = 1 - thisPercent): Color {
switch (colorspace) { switch (colorspace) {
case "oklab": { case "oklab": {
const okl1 = this.OKLAB; const okl1 = this.OKLAB;
@ -193,6 +273,17 @@ export class Color {
}); });
} }
private withOpacity(bg: Color, alpha: number): Color {
const r = (this.sRGB.r * alpha) + (bg.sRGB.r * (1 - alpha));
const g = (this.sRGB.g * alpha) + (bg.sRGB.g * (1 - alpha));
const b = (this.sRGB.b * alpha) + (bg.sRGB.b * (1 - alpha));
return new Color({
type: "srgb",
r,
g,
b
});
}
private static OKLABtolRGB({ l, a, b }: OKLAB): lRGB { private static OKLABtolRGB({ l, a, b }: OKLAB): lRGB {
const l1 = Math.pow(l + 0.3963377774 * a + 0.2158037573 * b, 3); const l1 = Math.pow(l + 0.3963377774 * a + 0.2158037573 * b, 3);
const m1 = Math.pow(l - 0.1055613458 * a - 0.0638541728 * b, 3); const m1 = Math.pow(l - 0.1055613458 * a - 0.0638541728 * b, 3);
@ -291,3 +382,6 @@ export class Contrast {
} }
} }
export const getBackgroundColor = (c: CSSStyleDeclaration) => {
return c.getPropertyValue("--bg-overlay-chat") || c.getPropertyValue("--background-primary") || (() => { throw new Error("no background color found"); })();
};

View file

@ -24,9 +24,10 @@ import { Logger } from "@utils/Logger";
import { useForceUpdater } from "@utils/react"; import { useForceUpdater } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy } from "@webpack"; import { findByCodeLazy } from "@webpack";
import { ChannelStore, GuildMemberStore, GuildStore, useEffect, useMemo, useState } from "@webpack/common"; import { ChannelStore, GuildMemberStore, GuildStore, useEffect, useMemo, useRef, useState } from "@webpack/common";
import Message from "discord-types/general/Message";
import { Color, Contrast } from "./color"; import { Color, Contrast, getBackgroundColor } from "./color";
const useMessageAuthor = findByCodeLazy('"Result cannot be null because the message is not null"'); const useMessageAuthor = findByCodeLazy('"Result cannot be null because the message is not null"');
@ -193,7 +194,7 @@ export default definePlugin({
}, },
predicate: () => settings.store.colorChatMessages predicate: () => settings.store.colorChatMessages
}, },
// HORROR // HORROR: ref for message content to get background colors
{ {
find: "#{intl::MESSAGE_EDITED}", find: "#{intl::MESSAGE_EDITED}",
replacement: { replacement: {
@ -207,9 +208,45 @@ export default definePlugin({
match: /(?<=ref:)\i/, match: /(?<=ref:)\i/,
replace: "vc_ref" replace: "vc_ref"
} }
},
// cap contrast of usernames
{
find: "style:\"username\"",
replacement: {
match: /(?<=message:(\i).*)(?<=color:(\i).*)(?<=void 0,)/,
replace: "...$self.capContrast($2, $1),"
}
} }
], ],
capContrast(color: string, { mentioned }: Message) {
const ref = useRef<{ ref: Element; }>(null);
const contrast = useGetContrastValue();
const [dep, update] = useForceUpdater(true);
return useMemo(() => {
try {
if (color == null || contrast === 1) return {};
if (!ref.current?.ref) {
console.log("updating");
setTimeout(update, 0);
return { ref };
}
const computed = getComputedStyle(ref.current.ref);
const standardBG = getBackgroundColor(computed);
const bgColor = mentioned ? computed.getPropertyValue("--background-mentioned") : standardBG;
if (!bgColor) throw new Error("Background color not found");
return {
ref,
style: {
color: new Contrast(Color.parse(bgColor, standardBG)).calculateMinContrastColor(Color.parse(color), contrast)
}
};
} catch (error) {
console.error(error);
}
}, [ref?.current?.ref, contrast, color, dep, mentioned]);
},
getColorString(userId: string, channelOrGuildId: string) { getColorString(userId: string, channelOrGuildId: string) {
try { try {
const guildId = ChannelStore.getChannel(channelOrGuildId)?.guild_id ?? GuildStore.getGuild(channelOrGuildId)?.id; const guildId = ChannelStore.getChannel(channelOrGuildId)?.guild_id ?? GuildStore.getGuild(channelOrGuildId)?.id;
@ -254,17 +291,16 @@ export default definePlugin({
}; };
} }
// why // why
if (!ref.current) setTimeout(update, 0); if (!ref.current) {
setTimeout(update, 0);
return {};
}
const computed = window.getComputedStyle(ref.current); const computed = window.getComputedStyle(ref.current);
const textNormal = computed.getPropertyValue("--text-normal"), const textNormal = computed.getPropertyValue("--text-normal"),
headerPrimary = computed.getPropertyValue("--header-primary"), headerPrimary = computed.getPropertyValue("--header-primary"),
textMuted = computed.getPropertyValue("--text-muted"); textMuted = computed.getPropertyValue("--text-muted");
const bgOverlayChat = computed.getPropertyValue("--bg-overlay-chat"),
backgroundPrimary = computed.getPropertyValue("--background-primary"); const bg = new Contrast(Color.parse(getBackgroundColor(computed)));
if (!(bgOverlayChat || backgroundPrimary)) {
throw new Error("No background color found");
}
const bg = new Contrast(Color.parse(bgOverlayChat || backgroundPrimary));
const rc = Color.parse(author.colorString); const rc = Color.parse(author.colorString);
const mkColor = c => bg.calculateMinContrastColor(rc.mix("oklab", messageSaturation / 100, Color.parse(c)), contrast); const mkColor = c => bg.calculateMinContrastColor(rc.mix("oklab", messageSaturation / 100, Color.parse(c)), contrast);
return { return {