add tone indicators plugin (#378)
* add tone indicators plugin * im tired of this man * oh my fucking god its EquicordDevs not Devs * dont use json * Update index.tsx --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> Co-authored-by: justjxke <Jake0300@protonmai.com>
This commit is contained in:
parent
7167aead16
commit
8d6bff6c45
4 changed files with 242 additions and 0 deletions
34
src/equicordplugins/toneIndicators/ToneIndicator.tsx
Normal file
34
src/equicordplugins/toneIndicators/ToneIndicator.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Tooltip } from "@webpack/common";
|
||||
import type { ReactElement } from "react";
|
||||
|
||||
export interface ToneIndicatorProps {
|
||||
indicator: string;
|
||||
desc: string;
|
||||
}
|
||||
|
||||
export default function ToneIndicator({
|
||||
indicator,
|
||||
desc,
|
||||
}: ToneIndicatorProps): ReactElement {
|
||||
return (
|
||||
<Tooltip text={desc}>
|
||||
{tooltipProps => (
|
||||
<span
|
||||
{...tooltipProps}
|
||||
style={{
|
||||
color: "var(--text-normal)",
|
||||
userSelect: "text",
|
||||
}}
|
||||
>
|
||||
/{indicator}
|
||||
</span>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
142
src/equicordplugins/toneIndicators/index.tsx
Normal file
142
src/equicordplugins/toneIndicators/index.tsx
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { React } from "@webpack/common";
|
||||
import { type ReactNode } from "react";
|
||||
|
||||
import indicatorsDefault from "./indicators";
|
||||
import ToneIndicator from "./ToneIndicator";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
customIndicators: {
|
||||
type: OptionType.STRING,
|
||||
description: "Custom tone indicators (format: jk=Joking; srs=Serious)",
|
||||
default: "",
|
||||
},
|
||||
maxIndicators: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Maximum number of tone indicators to show per message",
|
||||
default: 5,
|
||||
},
|
||||
});
|
||||
|
||||
function getCustomIndicators(): Record<string, string> {
|
||||
const raw = settings.store.customIndicators || "";
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
raw.split("; ").forEach(entry => {
|
||||
const [key, ...rest] = entry.split("=");
|
||||
if (key && rest.length > 0) {
|
||||
result[key.trim().toLowerCase()] = rest.join("=").trim();
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getIndicator(text: string): string | null {
|
||||
text = text.toLowerCase();
|
||||
const customIndicators = getCustomIndicators();
|
||||
|
||||
return (
|
||||
customIndicators[text] ||
|
||||
customIndicators[`_${text}`] ||
|
||||
indicatorsDefault.get(text) ||
|
||||
indicatorsDefault.get(`_${text}`) ||
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
function splitTextWithIndicators(text: string): ReactNode[] {
|
||||
const nodes: ReactNode[] = [];
|
||||
let lastIndex = 0;
|
||||
let count = 0;
|
||||
const regex = /\/([a-z]{1,20})(?=\s|$|[^\w])/gi;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = regex.exec(text)) && count < settings.store.maxIndicators) {
|
||||
const indicator = match[1];
|
||||
const desc = getIndicator(indicator);
|
||||
if (!desc) continue;
|
||||
|
||||
if (match.index > lastIndex) {
|
||||
nodes.push(text.slice(lastIndex, match.index));
|
||||
}
|
||||
|
||||
nodes.push(
|
||||
<ToneIndicator
|
||||
key={`ti-${match.index}`}
|
||||
indicator={indicator}
|
||||
desc={desc}
|
||||
/>,
|
||||
);
|
||||
|
||||
lastIndex = regex.lastIndex;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
function patchChildrenTree(children: any): any {
|
||||
const transform = (node: any): any => {
|
||||
if (node == null) return node;
|
||||
|
||||
if (typeof node === "string") {
|
||||
if (!/\/[a-z]+/i.test(node)) return node;
|
||||
const parts = splitTextWithIndicators(node);
|
||||
return parts.length === 1 ? parts[0] : parts;
|
||||
}
|
||||
|
||||
if (node?.props?.children != null) {
|
||||
const c = node.props.children;
|
||||
if (Array.isArray(c)) {
|
||||
node.props.children = c.map(transform).flat();
|
||||
} else {
|
||||
node.props.children = transform(c);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
if (Array.isArray(children)) return children.map(transform).flat();
|
||||
return transform(children);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "ToneIndicators",
|
||||
description: "Show tooltips for tone indicators like /srs, /gen, etc. in sent messages.",
|
||||
authors: [EquicordDevs.justjxke],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
|
||||
replacement: [
|
||||
{
|
||||
match: /(?=return\{hasSpoilerEmbeds:\i,content:(\i)\})/,
|
||||
replace: (_: any, content: string) =>
|
||||
`${content}=$self.patchToneIndicators(${content});`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
patchToneIndicators(content: any): any {
|
||||
try {
|
||||
return patchChildrenTree(content);
|
||||
} catch {
|
||||
return content;
|
||||
}
|
||||
},
|
||||
});
|
||||
62
src/equicordplugins/toneIndicators/indicators.ts
Normal file
62
src/equicordplugins/toneIndicators/indicators.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
const defaultIndicators = new Map(
|
||||
Object.entries({
|
||||
j: "Joking",
|
||||
hj: "Half Joking",
|
||||
s: "Sarcastic",
|
||||
_sarc: "Sarcastic", // Alias for s
|
||||
gen: "Genuine",
|
||||
_g: "Genuine", // Alias for gen,
|
||||
genq: "Genuine Question",
|
||||
_gq: "Genuine Question", // Alias for genq
|
||||
srs: "Serious",
|
||||
nsrs: "Non-Serious",
|
||||
pos: "Positive Connotation",
|
||||
_pc: "Positive Connotation", // Alias for pos
|
||||
neu: "Neutral Connotation",
|
||||
neg: "Negative Connotation",
|
||||
_nc: "Negative Connotation", // Alias for neg
|
||||
p: "Platonic",
|
||||
r: "Romantic",
|
||||
a: "Alterous",
|
||||
c: "Copypasta",
|
||||
l: "Lyrics",
|
||||
_ly: "Lyrics", // Alias for l
|
||||
lh: "Light-Hearted",
|
||||
nm: "Not Mad",
|
||||
lu: "A Little Upset",
|
||||
nbh: "Not Directed At Anybody Here",
|
||||
nay: "Not Directed At You",
|
||||
ay: "Directed At You",
|
||||
nsb: "Not Subtweeting",
|
||||
x: "Sexual Intent",
|
||||
_sx: "Sexual Intent", // Alias for x
|
||||
nx: "Non-Sexual Intent",
|
||||
_nsx: "Non-Sexual Intent", // Alias for nx
|
||||
rh: "Rhetorical Question",
|
||||
_rt: "Rhetorical Question", // Alias for rh
|
||||
t: "Teasing",
|
||||
ij: "Inside Joke",
|
||||
m: "Metaphorically",
|
||||
li: "Literally",
|
||||
hyp: "Hyperbole",
|
||||
ex: "Exaggeration",
|
||||
f: "Fake",
|
||||
q: "Quote",
|
||||
th: "Threat",
|
||||
cb: "Clickbait",
|
||||
ref: "Reference",
|
||||
nf: "Not Forced",
|
||||
nbr: "Not Being Rude",
|
||||
ot: "Off-Topic",
|
||||
nj: "Not Joking",
|
||||
naj: "Not A Joke",
|
||||
}),
|
||||
);
|
||||
|
||||
export default defaultIndicators;
|
||||
|
|
@ -1145,6 +1145,10 @@ export const EquicordDevs = Object.freeze({
|
|||
name: "Mocha",
|
||||
id: 808802000224518264n
|
||||
},
|
||||
justjxke: {
|
||||
name: "justjxke",
|
||||
id: 852558183087472640n
|
||||
}
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
// iife so #__PURE__ works correctly
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue