Krystal😠
Some checks failed
Release / Build Equicord (push) Has been cancelled
Sync to Codeberg / Sync Codeberg and Github (push) Has been cancelled
Test / Test (push) Has been cancelled

This commit is contained in:
thororen1234 2025-02-19 18:02:52 -05:00
parent f00748918c
commit 334ce7c113
No known key found for this signature in database
5 changed files with 123 additions and 64 deletions

View file

@ -122,7 +122,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- SekaiStickers by MaiKokain - SekaiStickers by MaiKokain
- ServerSearch by camila314 - ServerSearch by camila314
- ShowBadgesInChat by Inbestigator & KrystalSkull - ShowBadgesInChat by Inbestigator & KrystalSkull
- Signature by KrystalSkull - Signature by Ven, Rini, ImBanana, KrystalSkull
- SidebarChat by Joona - SidebarChat by Joona
- StatsfmRPC by Crxaw & vmohammad - StatsfmRPC by Crxaw & vmohammad
- Slap by Korbo - Slap by Korbo

View file

@ -13,6 +13,12 @@ export const enum RenderType {
BACKGROUND, BACKGROUND,
} }
export const enum BlockDisplayType {
LEFT,
RIGHT,
BOTH
}
export const settings = definePluginSettings({ export const settings = definePluginSettings({
renderType: { renderType: {
type: OptionType.SELECT, type: OptionType.SELECT,
@ -32,6 +38,33 @@ export const settings = definePluginSettings({
value: RenderType.BACKGROUND value: RenderType.BACKGROUND
}, },
] ]
},
enableShortHexCodes: {
type: OptionType.BOOLEAN,
description: "Enable 3 char hex-code like #39f",
default: true,
// Regex are created on the start, so without restart nothing would change
restartNeeded: true
},
blockView: {
type: OptionType.SELECT,
disabled: () => settings.store.renderType !== RenderType.BLOCK,
description: "Where to display colored block",
options: [
{
label: "Right side",
value: BlockDisplayType.RIGHT,
default: true
},
{
label: "Left side",
value: BlockDisplayType.LEFT
},
{
label: "Both sides",
value: BlockDisplayType.BOTH
}
]
} }
}); });
@ -59,5 +92,4 @@ export const regex = [
{ reg: /rgb\(\v\c\v\c\v\)/g, type: ColorType.RGB }, { reg: /rgb\(\v\c\v\c\v\)/g, type: ColorType.RGB },
{ reg: /rgba\(\v\c\v\c\v(\c|\/?)\s*\f\)/g, type: ColorType.RGBA }, { reg: /rgba\(\v\c\v\c\v(\c|\/?)\s*\f\)/g, type: ColorType.RGBA },
{ reg: /hsl\(\v°?\c\s*?\d+%?\s*?\c\s*?\d+%?\s*?\)/g, type: ColorType.HSL }, { reg: /hsl\(\v°?\c\s*?\d+%?\s*?\c\s*?\d+%?\s*?\)/g, type: ColorType.HSL },
{ reg: /#(?:[0-9a-fA-F]{3}){1,2}/g, type: ColorType.HEX }
].map(v => { v.reg = replaceRegexp(v.reg.source); return v; }); ].map(v => { v.reg = replaceRegexp(v.reg.source); return v; });

View file

@ -6,20 +6,14 @@
import "./styles.css"; import "./styles.css";
import ErrorBoundary from "@components/ErrorBoundary";
import { EquicordDevs } from "@utils/constants"; import { EquicordDevs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin, { StartAt } from "@utils/types";
import { React } from "@webpack/common"; import { React } from "@webpack/common";
import type { ReactElement } from "react";
import { import { BlockDisplayType, ColorType, regex, RenderType, replaceRegexp, settings } from "./constants";
ColorType,
regex,
RenderType,
replaceRegexp,
settings,
} from "./constants";
const source = regex.map(r => r.reg.source).join("|");
const matchAllRegExp = new RegExp(`^(${source})`, "i");
interface ParsedColorInfo { interface ParsedColorInfo {
type: "color"; type: "color";
@ -28,10 +22,7 @@ interface ParsedColorInfo {
text: string; text: string;
} }
const requiredFirstCharacters = ["r", "h", "#"].flatMap(v => [ const requiredFirstCharacters = ["r", "h", "#"].flatMap(v => [v, v.toUpperCase()]);
v,
v.toUpperCase(),
]);
export default definePlugin({ export default definePlugin({
authors: [EquicordDevs.Hen], authors: [EquicordDevs.Hen],
@ -46,8 +37,8 @@ export default definePlugin({
group: true, group: true,
replacement: { replacement: {
match: /roleMention:\{order:(\i\.\i\.order)/, match: /roleMention:\{order:(\i\.\i\.order)/,
replace: "color:$self.getColor($1),$&", replace: "color:$self.getColor($1),$&"
}, }
}, },
// Changes text md rule regex, so it stops right before hsl( | rgb( // Changes text md rule regex, so it stops right before hsl( | rgb(
// Without it discord will try to pass a string without those to color rule // Without it discord will try to pass a string without those to color rule
@ -55,11 +46,19 @@ export default definePlugin({
find: ".defaultRules.text,match:", find: ".defaultRules.text,match:",
group: true, group: true,
replacement: { replacement: {
// $) don't match with a " after the ) // $)/)
match: /\$\)(?!")/, match: /\$\)\//,
// hsl(|rgb(|$& // hsl(|rgb(|$&
replace: requiredFirstCharacters.join("|") + "|$&", replace: requiredFirstCharacters.join("|") + "|$&"
}, }
},
// Fix the issue with [#123](https://example.com) rendered as plain text
{
find: "parseInlineCodeChildContent:",
replacement: {
match: /parseInlineCodeChildContent:/,
replace: "isInsideOfLink:true,$&"
}
}, },
// Discord just requires it to be here // Discord just requires it to be here
// Or it explodes (bad) // Or it explodes (bad)
@ -68,11 +67,23 @@ export default definePlugin({
group: true, group: true,
replacement: { replacement: {
match: /roleMention:{type:/, match: /roleMention:{type:/,
replace: 'color:{type:"inlineObject"},$&', replace: "color:{type:\"inlineObject\"},$&",
}, }
}, },
], ],
start() {
const amount = settings.store.enableShortHexCodes ? "{1,2}" : "{2}";
regex.push({
reg: new RegExp("#(?:[0-9a-fA-F]{3})" + amount, "g"),
type: ColorType.HEX
});
},
// Needed to load all regex before patching
startAt: StartAt.Init,
getColor(order: number) { getColor(order: number) {
const source = regex.map(r => r.reg.source).join("|");
const matchAllRegExp = new RegExp(`^(${source})`, "i");
return { return {
order, order,
// Don't even try to match if the message chunk doesn't start with... // Don't even try to match if the message chunk doesn't start with...
@ -82,20 +93,16 @@ export default definePlugin({
match(content: string) { match(content: string) {
return matchAllRegExp.exec(content); return matchAllRegExp.exec(content);
}, },
parse( parse(matchedContent: RegExpExecArray, _, parseProps: Record<string, any>):
matchedContent: RegExpExecArray, ParsedColorInfo | ({ type: "text", content: string; }) {
_,
parseProps: Record<string, any>,
): ParsedColorInfo | { type: "text"; content: string; } {
// This check makes sure that it doesn't try to parse color // This check makes sure that it doesn't try to parse color
// When typing/editing message // When typing/editing message
// //
// Discord doesn't know how to deal with color and crashes // Discord doesn't know how to deal with color and crashes
if (!parseProps.messageId) if (!parseProps.messageId || parseProps.isInsideOfLink) return {
return { type: "text",
type: "text", content: matchedContent[0]
content: matchedContent[0], };
};
const content = matchedContent[0]; const content = matchedContent[0];
try { try {
@ -105,44 +112,59 @@ export default definePlugin({
type: "color", type: "color",
colorType: type, colorType: type,
color: parseColor(content, type), color: parseColor(content, type),
text: content, text: content
}; };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return { return {
type: "text", type: "text",
content: matchedContent[0], content: matchedContent[0]
}; };
} }
}, },
// react(args: ReturnType<typeof this.parse>) react: ErrorBoundary.wrap(({ text, colorType, color }: ParsedColorInfo) => {
react({ text, colorType, color }: ParsedColorInfo) {
if (settings.store.renderType === RenderType.FOREGROUND) { if (settings.store.renderType === RenderType.FOREGROUND) {
return <span style={{ color: color }}>{text}</span>; return <span style={{ color: color }}>{text}</span>;
} }
const styles = { const styles = {
"--color": color, "--color": color
} as React.CSSProperties; } as React.CSSProperties;
if (settings.store.renderType === RenderType.BACKGROUND) { if (settings.store.renderType === RenderType.BACKGROUND) {
const isDark = isColorDark(color, colorType); const isDark = isColorDark(color, colorType);
const className = `vc-color-bg ${!isDark ? "vc-color-bg-invert" : ""}`; const className = `vc-color-bg ${!isDark ? "vc-color-bg-invert" : ""}`;
return ( return <span className={className} style={styles}>{text}</span>;
<span className={className} style={styles}>
{text}
</span>
);
} }
return ( // Only block display left
<> const margin = "2px";
{text}
<span className="vc-color-block" style={styles}></span> switch (settings.store.blockView) {
</> case BlockDisplayType.LEFT:
); styles.marginRight = margin;
}, return <><span className="vc-color-block" style={styles} />{text}</>;
case BlockDisplayType.RIGHT:
styles.marginLeft = margin;
return <>{text}<span className="vc-color-block" style={styles} /></>;
case BlockDisplayType.BOTH:
styles.marginLeft = margin;
styles.marginRight = margin;
return <>
<span className="vc-color-block" style={styles} />
{text}
<span className="vc-color-block" style={styles} />
</>;
}
}, {
fallback: data => {
const child = data.children as ReactElement<any>;
return <>{child.props?.text}</>;
}
})
}; };
}, }
}); });
// https://en.wikipedia.org/wiki/Relative_luminance // https://en.wikipedia.org/wiki/Relative_luminance
@ -150,25 +172,32 @@ const calcRGBLightness = (r: number, g: number, b: number) => {
return 0.2126 * r + 0.7152 * g + 0.0722 * b; return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}; };
const isColorDark = (color: string, type: ColorType): boolean => { const isColorDark = (color: string, type: ColorType): boolean => {
const border = 115;
switch (type) { switch (type) {
case ColorType.RGBA: case ColorType.RGBA:
case ColorType.RGB: { case ColorType.RGB: {
const match = color.match(/\d+/g)!; const match = color.match(/\d+/g)!;
const lightness = calcRGBLightness(+match[0], +match[1], +match[2]); const lightness = calcRGBLightness(+match[0], +match[1], +match[2]);
return lightness < 140; return lightness < border;
} }
case ColorType.HEX: { case ColorType.HEX: {
var rgb = parseInt(color.substring(1), 16); color = color.substring(1);
if (color.length === 3) {
color = color.split("").flatMap(v => [v, v]).join("");
}
const rgb = parseInt(color, 16);
const r = (rgb >> 16) & 0xff; const r = (rgb >> 16) & 0xff;
const g = (rgb >> 8) & 0xff; const g = (rgb >> 8) & 0xff;
const b = (rgb >> 0) & 0xff; const b = (rgb >> 0) & 0xff;
const lightness = calcRGBLightness(r, g, b); const lightness = calcRGBLightness(r, g, b);
return lightness < 140; return lightness < border;
} }
case ColorType.HSL: { case ColorType.HSL: {
const match = color.match(/\d+/g)!; const match = color.match(/\d+/g)!;
const lightness = +match[2]; const lightness = +match[2];
return lightness < 50; return lightness < (border / 255 * 100);
} }
} }
}; };
@ -184,10 +213,7 @@ const getColorType = (color: string): ColorType => {
}; };
function parseColor(str: string, type: ColorType): string { function parseColor(str: string, type: ColorType): string {
str = str str = str.toLowerCase().trim().replaceAll(/(\s|,)+/g, " ");
.toLowerCase()
.trim()
.replaceAll(/(\s|,)+/g, " ");
switch (type) { switch (type) {
case ColorType.RGB: case ColorType.RGB:
return str; return str;

View file

@ -3,10 +3,11 @@
background: var(--color); background: var(--color);
height: 1rem; height: 1rem;
vertical-align: middle; vertical-align: middle;
margin-bottom: 0.2rem;
border-radius: 4px; border-radius: 4px;
display: inline-block; display: inline-block;
user-select: none; user-select: none;
margin-left: 2px; box-sizing: border-box;
} }
.vc-color-bg { .vc-color-bg {
@ -16,5 +17,5 @@
/* Light color in dark theme */ /* Light color in dark theme */
.theme-dark .vc-color-bg.vc-color-bg-invert, .theme-dark .vc-color-bg.vc-color-bg-invert,
.theme-light .vc-color-bg:not(.vc-color-bg-invert) { .theme-light .vc-color-bg:not(.vc-color-bg-invert) {
color: var(--background-tertiary); color: var(--background-secondary);
} }

View file

@ -9,7 +9,7 @@ import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption,
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { addMessagePreSendListener, removeMessagePreSendListener } from "@api/MessageEvents"; import { addMessagePreSendListener, removeMessagePreSendListener } from "@api/MessageEvents";
import { definePluginSettings, migratePluginSettings } from "@api/Settings"; import { definePluginSettings, migratePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants"; import { Devs, EquicordDevs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { Menu, React } from "@webpack/common"; import { Menu, React } from "@webpack/common";
@ -98,7 +98,7 @@ migratePluginSettings("Signature", "SentVia");
export default definePlugin({ export default definePlugin({
name: "Signature", name: "Signature",
description: "Automated fingerprint/end text", description: "Automated fingerprint/end text",
authors: [EquicordDevs.KrystalSkull], authors: [Devs.Ven, Devs.Rini, Devs.ImBanana, EquicordDevs.KrystalSkull],
dependencies: ["MessageEventsAPI", "ChatInputButtonAPI"], dependencies: ["MessageEventsAPI", "ChatInputButtonAPI"],
start: () => { start: () => {