new plugin CharacterCounter

This commit is contained in:
Vendicated 2026-03-28 03:25:44 +01:00
parent 0218f2b190
commit 5a9c3786bd
No known key found for this signature in database
GPG key ID: D66986BAF75ECF18
3 changed files with 96 additions and 0 deletions

View file

@ -0,0 +1,7 @@
# Character Counter
Adds a character counter to the message input box
![](https://github.com/user-attachments/assets/57f8c01e-a122-46d2-afda-679edb7a996b)
![](https://github.com/user-attachments/assets/640bcca4-77a3-4407-8aa0-15c490dd9256)

View file

@ -0,0 +1,74 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./style.css";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import definePlugin, { OptionType } from "@utils/types";
import { UserStore } from "@webpack/common";
const cl = classNameFactory("vc-charCounter-");
const settings = definePluginSettings({
colorEffects: {
type: OptionType.BOOLEAN,
description: "Enable yellow/red colouring as you get closer to the character limit",
default: true,
}
});
function getCounterColor(percentage: number) {
if (!settings.store.colorEffects) return "var(--primary-330)";
if (percentage < 50) return "var(--text-muted)";
if (percentage < 75) return "var(--yellow-330)";
if (percentage < 90) return "var(--orange-330)";
return "var(--red-360)";
}
export default definePlugin({
name: "CharacterCounter",
description: "Adds a character counter to the chat input",
authors: [Devs.thororen],
settings,
patches: [
{
find: ".CREATE_FORUM_POST||",
replacement: [
{
match: /(?<=textValue:(\i),editorHeight:\i,channelId:\i\.id\}\)),\i/,
replace: ",$self.renderCharCounter({text:$1})"
}
]
},
{
find: "#{intl::PREMIUM_MESSAGE_LENGTH_UPSELL_TOOLTIP}",
replacement: {
match: /return \i\?\i\(\):\i\(\)(?<=#{intl::PREMIUM_MESSAGE_LENGTH_UPSELL_TOOLTIP_WITHOUT_LINK}.{0,200}?)/,
replace: "return null"
}
}
],
renderCharCounter: ErrorBoundary.wrap(({ text }: { text: string; }) => {
if (!text.length) return null;
const premiumType = UserStore.getCurrentUser().premiumType ?? 0;
const charMax = premiumType === 2 ? 4000 : 2000;
const color = getCounterColor((text.length / charMax) * 100);
return (
<div className={cl("counter")} style={{ color }}>
<span className={cl("count")}>{text.length}</span>
/
<span className={cl("max")}>{charMax}</span>
</div>
);
}, { noop: true })
});

View file

@ -0,0 +1,15 @@
.vc-charCounter-counter {
font-size: 12px;
color: var(--text-muted);
position: absolute;
bottom: 4px;
right: 11px;
pointer-events: none;
z-index: 2;
background: transparent;
transition: color 0.3s ease;
}
.vc-charCounter-count {
transition: color 0.3s ease;
}