Merge branch 'dev'
This commit is contained in:
commit
aa71c621f6
10 changed files with 611 additions and 124 deletions
|
|
@ -136,13 +136,81 @@ export async function uploadToS3(
|
|||
}
|
||||
}
|
||||
|
||||
export async function uploadToCatbox(
|
||||
_: IpcMainInvokeEvent,
|
||||
fileBuffer: ArrayBuffer,
|
||||
filename: string,
|
||||
userhash?: string
|
||||
): Promise<NativeUploadResult> {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("reqtype", "fileupload");
|
||||
if (userhash) {
|
||||
formData.append("userhash", userhash);
|
||||
}
|
||||
formData.append("fileToUpload", new Blob([fileBuffer]), filename);
|
||||
|
||||
const response = await fetch("https://catbox.moe/user/api.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
return { success: false, error: `Upload failed: ${response.status} ${errorText}` };
|
||||
}
|
||||
|
||||
const text = (await response.text()).trim();
|
||||
if (!text) {
|
||||
return { success: false, error: "No URL returned from upload" };
|
||||
}
|
||||
|
||||
return { success: true, url: text };
|
||||
} catch (e) {
|
||||
return { success: false, error: e instanceof Error ? e.message : "Unknown error" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function uploadToLitterbox(
|
||||
_: IpcMainInvokeEvent,
|
||||
fileBuffer: ArrayBuffer,
|
||||
filename: string,
|
||||
expiry: string
|
||||
): Promise<NativeUploadResult> {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("reqtype", "fileupload");
|
||||
formData.append("time", expiry);
|
||||
formData.append("fileToUpload", new Blob([fileBuffer]), filename);
|
||||
|
||||
const response = await fetch("https://litterbox.catbox.moe/resources/internals/api.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
return { success: false, error: `Upload failed: ${response.status} ${errorText}` };
|
||||
}
|
||||
|
||||
const text = (await response.text()).trim();
|
||||
if (!text) {
|
||||
return { success: false, error: "No URL returned from upload" };
|
||||
}
|
||||
|
||||
return { success: true, url: text };
|
||||
} catch (e) {
|
||||
return { success: false, error: e instanceof Error ? e.message : "Unknown error" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchFile(
|
||||
|
||||
_: IpcMainInvokeEvent,
|
||||
|
||||
url: string
|
||||
|
||||
): Promise<{ success: boolean; data?: ArrayBuffer; contentType?: string; error?: string }> {
|
||||
): Promise<{ success: boolean; data?: ArrayBuffer; contentType?: string; error?: string; }> {
|
||||
|
||||
try {
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,12 @@ export const settings = definePluginSettings({
|
|||
default: "24h",
|
||||
hidden: true
|
||||
},
|
||||
catboxUserhash: {
|
||||
type: OptionType.STRING,
|
||||
description: "Catbox userhash for account binding",
|
||||
default: "",
|
||||
hidden: true
|
||||
},
|
||||
stripQueryParams: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Strip query params from uploaded URLs",
|
||||
|
|
@ -180,6 +186,7 @@ export function SettingsComponent() {
|
|||
const isEzHost = store.serviceType === ServiceType.EZHOST;
|
||||
const isS3 = store.serviceType === ServiceType.S3;
|
||||
const isZipline = store.serviceType === ServiceType.ZIPLINE;
|
||||
const isCatbox = store.serviceType === ServiceType.CATBOX;
|
||||
const isLitterbox = store.serviceType === ServiceType.LITTERBOX;
|
||||
|
||||
return (
|
||||
|
|
@ -310,6 +317,16 @@ export function SettingsComponent() {
|
|||
</>
|
||||
)}
|
||||
|
||||
{isCatbox && (
|
||||
<SettingTextInput
|
||||
name="Catbox Userhash"
|
||||
description="Your Catbox userhash for account binding (leave empty for anonymous uploads)"
|
||||
value={store.catboxUserhash}
|
||||
onChange={v => store.catboxUserhash = v}
|
||||
placeholder="Your Catbox userhash"
|
||||
/>
|
||||
)}
|
||||
|
||||
{isLitterbox && (
|
||||
<SettingsSection name="Litterbox Expiry" description="How long uploads are retained">
|
||||
<Select
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ async function uploadToNest(fileBlob: Blob, filename: string): Promise<string> {
|
|||
throw new Error(`Upload failed: ${response.status} ${errorText}`);
|
||||
}
|
||||
|
||||
const data = await response.json() as { fileURL?: string };
|
||||
const data = await response.json() as { fileURL?: string; };
|
||||
|
||||
if (data.fileURL) {
|
||||
return data.fileURL;
|
||||
|
|
@ -132,7 +132,7 @@ export function isConfigured(): boolean {
|
|||
case ServiceType.NEST:
|
||||
return Boolean(nestToken);
|
||||
case ServiceType.EZHOST:
|
||||
return Boolean((settings.store as { ezHostKey?: string }).ezHostKey);
|
||||
return Boolean((settings.store as { ezHostKey?: string; }).ezHostKey);
|
||||
case ServiceType.S3:
|
||||
return isS3Configured();
|
||||
case ServiceType.CATBOX:
|
||||
|
|
@ -148,7 +148,7 @@ export function isConfigured(): boolean {
|
|||
}
|
||||
|
||||
async function uploadToEzHost(fileBlob: Blob, filename: string): Promise<string> {
|
||||
const { ezHostKey } = (settings.store as { ezHostKey?: string });
|
||||
const { ezHostKey } = (settings.store as { ezHostKey?: string; });
|
||||
|
||||
if (!ezHostKey) throw new Error("E-Z Host API key is required");
|
||||
|
||||
|
|
@ -193,29 +193,27 @@ async function uploadToEzHost(fileBlob: Blob, filename: string): Promise<string>
|
|||
}
|
||||
|
||||
async function uploadToCatbox(fileBlob: Blob, filename: string): Promise<string> {
|
||||
const { catboxUserhash } = settings.store;
|
||||
|
||||
if (Native) {
|
||||
const result = await Native.uploadToCatbox(await fileBlob.arrayBuffer(), filename, catboxUserhash || undefined);
|
||||
if (!result.success || !result.url) throw new Error(result.error || "No URL returned from upload");
|
||||
return result.url;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("reqtype", "fileupload");
|
||||
if (catboxUserhash) formData.append("userhash", catboxUserhash);
|
||||
formData.append("fileToUpload", fileBlob, filename);
|
||||
|
||||
const uploadUrl = Native
|
||||
? "https://catbox.moe/user/api.php"
|
||||
: `${CORS_PROXY}?url=${encodeURIComponent("https://catbox.moe/user/api.php")}`;
|
||||
|
||||
const response = await fetch(uploadUrl, {
|
||||
const response = await fetch(`${CORS_PROXY}?url=${encodeURIComponent("https://catbox.moe/user/api.php")}`, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
throw new Error(`Upload failed: ${response.status} ${text}`);
|
||||
}
|
||||
|
||||
if (!response.ok) throw new Error(`Upload failed: ${response.status} ${await response.text()}`);
|
||||
const text = (await response.text()).trim();
|
||||
if (!text) {
|
||||
throw new Error("No URL returned from upload");
|
||||
}
|
||||
|
||||
if (!text) throw new Error("No URL returned from upload");
|
||||
return text;
|
||||
}
|
||||
|
||||
|
|
@ -239,30 +237,27 @@ async function uploadTo0x0(fileBlob: Blob, filename: string): Promise<string> {
|
|||
}
|
||||
|
||||
async function uploadToLitterbox(fileBlob: Blob, filename: string): Promise<string> {
|
||||
const expiry = settings.store.litterboxExpiry || "24h";
|
||||
|
||||
if (Native) {
|
||||
const result = await Native.uploadToLitterbox(await fileBlob.arrayBuffer(), filename, expiry);
|
||||
if (!result.success || !result.url) throw new Error(result.error || "No URL returned from upload");
|
||||
return result.url;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("reqtype", "fileupload");
|
||||
formData.append("time", settings.store.litterboxExpiry || "24h");
|
||||
formData.append("time", expiry);
|
||||
formData.append("fileToUpload", fileBlob, filename);
|
||||
|
||||
const uploadUrl = Native
|
||||
? "https://litterbox.catbox.moe/resources/internals/api.php"
|
||||
: `${CORS_PROXY}?url=${encodeURIComponent("https://litterbox.catbox.moe/resources/internals/api.php")}`;
|
||||
|
||||
const response = await fetch(uploadUrl, {
|
||||
const response = await fetch(`${CORS_PROXY}?url=${encodeURIComponent("https://litterbox.catbox.moe/resources/internals/api.php")}`, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
throw new Error(`Upload failed: ${response.status} ${text}`);
|
||||
}
|
||||
|
||||
if (!response.ok) throw new Error(`Upload failed: ${response.status} ${await response.text()}`);
|
||||
const text = (await response.text()).trim();
|
||||
if (!text) {
|
||||
throw new Error("No URL returned from upload");
|
||||
}
|
||||
|
||||
if (!text) throw new Error("No URL returned from upload");
|
||||
return text;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ import { classes } from "@utils/misc";
|
|||
import definePlugin from "@utils/types";
|
||||
import { Activity, ApplicationStream, Channel, Message, OnlineStatus, User } from "@vencord/discord-types";
|
||||
import { MessageFlags } from "@vencord/discord-types/enums";
|
||||
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findCssClassesLazy, findExportedComponentLazy, findLazy } from "@webpack";
|
||||
import { ChannelStore, MessageStore, Parser, RelationshipStore, SnowflakeUtils, UserStore, useStateFromStores } from "@webpack/common";
|
||||
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findCssClassesLazy, findExportedComponentLazy } from "@webpack";
|
||||
import { ChannelStore, ExperimentStore, MessageStore, Parser, RelationshipStore, SnowflakeUtils, UserStore, useStateFromStores } from "@webpack/common";
|
||||
|
||||
const cl = classNameFactory("vc-message-peek-");
|
||||
|
||||
|
|
@ -27,7 +27,6 @@ const MessageActions = findByPropsLazy("fetchMessages", "sendMessage");
|
|||
|
||||
const hasRelevantActivity: (props: ActivityCheckProps) => boolean = findByCodeLazy(".OFFLINE||", ".INVISIBLE)return!1");
|
||||
const ActivityText: React.ComponentType<ActivityTextProps> = findComponentByCodeLazy("hasQuest:", "hideEmoji:");
|
||||
const FavoritesServerExperiment = findLazy(m => m?.definition?.id === "2021-09_favorites_server");
|
||||
|
||||
const ONE_HOUR_MS = 60 * 60 * 1000;
|
||||
|
||||
|
|
@ -216,7 +215,7 @@ function Timestamp({ channel }: { channel: Channel; }) {
|
|||
if (!lastMessage) return null;
|
||||
|
||||
const timestamp = SnowflakeUtils.extractTimestamp(lastMessage.id);
|
||||
const className = FavoritesServerExperiment.getCurrentConfig().favoritesEnabled ? cl("timestamp-favorites") : cl("timestamp");
|
||||
const className = ExperimentStore.getUserExperimentBucket("2021-09_favorites_server") >= 1 ? cl("timestamp-favorites") : cl("timestamp");
|
||||
return <span className={className}>{formatRelativeTime(timestamp)}</span>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1294,7 +1294,7 @@ export default definePlugin({
|
|||
find: ",{progressTextAnimation:",
|
||||
replacement: {
|
||||
match: /(?<=children:\i}=)(\i)/,
|
||||
replace: "Object.assign({},$1,$self.getQuestPanelPercentComplete($1))"
|
||||
replace: "Object.assign({},$1,$1.quest?$self.getQuestPanelPercentComplete($1):{})"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1302,7 +1302,7 @@ export default definePlugin({
|
|||
// useful information for Quests being auto-completed.
|
||||
find: '"progress-title"',
|
||||
replacement: {
|
||||
match: /(?<=return.{0,150}?quest:(\i),percentComplete:\i.{0,280}?"progress-title",children.{0,115}?children:)(\i.{0,50}"progress-subtitle",isTextTransition:!0,children.{0,115}?children:)/,
|
||||
match: /(?<={quest:(\i).{0,250}?return.{0,150}?,percentComplete:\i.{0,280}?"progress-title",children.{0,115}?children:)(\i.{0,50}"progress-subtitle",isTextTransition:!0,children.{0,115}?children:)/,
|
||||
replace: "$self.getQuestPanelTitleText($1)??$2$self.getQuestPanelSubtitleText($1)??"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "useCanFavoriteChannel",
|
||||
replacement: {
|
||||
match: /\i\.isDM\(\)\|\|\i\.isThread\(\)/,
|
||||
match: /(?<=\i\.id\)\),\i=)\i\.isThread\(\)/,
|
||||
replace: "false",
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1084,6 +1084,20 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, ...args:
|
|||
insertSetAliasMenuItem(children, emojiRef);
|
||||
};
|
||||
|
||||
const messageSendListener = (_channelId: string, messageObj: { content: string; }) => {
|
||||
if (!messageObj.content || !Object.keys(aliasMap).length) return;
|
||||
|
||||
messageObj.content = messageObj.content.replace(/:([a-z0-9_]{2,32}):/gi, (match, aliasName) => {
|
||||
const ref = aliasMap[aliasName.toLowerCase()];
|
||||
if (!ref) return match;
|
||||
|
||||
if (ref.kind === "custom" && ref.id) {
|
||||
return `<${ref.animated ? "a" : ""}:${ref.name}:${ref.id}>`;
|
||||
}
|
||||
return getUnicodeSurrogate(ref) ?? resolveUnicodeSurrogateByName(ref.name) ?? match;
|
||||
});
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "FavoriteEmojiFirst",
|
||||
authors: [Devs.Aria, Devs.Ven, EquicordDevs.justjxke],
|
||||
|
|
@ -1097,6 +1111,7 @@ export default definePlugin({
|
|||
"textarea-context": messageContextMenuPatch
|
||||
},
|
||||
isModified: true,
|
||||
onBeforeMessageSend: messageSendListener,
|
||||
patches: [
|
||||
{
|
||||
find: "renderResults({results:",
|
||||
|
|
|
|||
|
|
@ -13,15 +13,19 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Button, ErrorBoundary, Heading, TextButton } from "@components/index";
|
||||
import mentionAvatars from "@plugins/mentionAvatars";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/index";
|
||||
import { classNameFactory, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/index";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { GuildMember, Message, User } from "@vencord/discord-types";
|
||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { ChannelStore, GuildMemberStore, GuildStore, Menu, MessageStore, RelationshipStore, StreamerModeStore, TextInput, useEffect, UserStore, useState } from "@webpack/common";
|
||||
import { ChannelStore, GuildMemberStore, GuildStore, Menu, MessageStore, RelationshipStore, StreamerModeStore, TextInput, useEffect, useState } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
const SMYNC = classNameFactory();
|
||||
const UserStore = findStoreLazy("UserStore");
|
||||
const wrapEmojis = findByCodeLazy("lastIndex;return");
|
||||
const adjustColor = findByCodeLazy("light1", "dark1", "toonStroke");
|
||||
const AccessibilityStore = findStoreLazy("AccessibilityStore");
|
||||
|
||||
const colorPattern = /^#(?:[\da-f]{3}){1,2}$|^#(?:[\da-f]{4}){1,2}$|(rgb|hsl)a?\((\s*-?\d+%?\s*,){2}(\s*-?\d+%?\s*)\)|(rgb|hsl)a?\((\s*-?\d+%?\s*,){3}\s*(0|(0?\.\d+)|1)\)$/iu;
|
||||
const roleColorPattern = /^role((?:\+|-)\d{0,4})?$/iu;
|
||||
const symbolPattern = /^[\p{S}\p{P}]{1,3}$/iu;
|
||||
|
|
@ -76,22 +80,14 @@ function validColor(color: string) {
|
|||
return isValid;
|
||||
}
|
||||
|
||||
function resolveColor(user: User | GuildMember, savedColor: string, fallbackColor: string, canUseGradient: boolean): any {
|
||||
const fallbackReturn = {
|
||||
normal: {
|
||||
original: {
|
||||
color: fallbackColor,
|
||||
"text-decoration-color": fallbackColor
|
||||
},
|
||||
adjusted: {
|
||||
color: fallbackColor,
|
||||
"text-decoration-color": fallbackColor
|
||||
}
|
||||
},
|
||||
gradient: null
|
||||
};
|
||||
|
||||
if (!savedColor.trim()) { return fallbackReturn; }
|
||||
function resolveColor(
|
||||
user: User | GuildMember,
|
||||
savedColor: string,
|
||||
canUseGradient: boolean,
|
||||
inGuild: boolean,
|
||||
forceWhite: boolean
|
||||
): Record<string, any> | null {
|
||||
if (!savedColor.trim()) { return null; }
|
||||
|
||||
let gradient: any = null;
|
||||
let primaryColor: any = savedColor;
|
||||
|
|
@ -103,18 +99,24 @@ function resolveColor(user: User | GuildMember, savedColor: string, fallbackColo
|
|||
|
||||
if (savedColor.toLowerCase().includes("role")) {
|
||||
const percentage = roleColorPattern.exec(savedColor)?.[1] || "";
|
||||
if (percentage && isNaN(parseInt(percentage))) return fallbackReturn;
|
||||
if (percentage && isNaN(parseInt(percentage))) return null;
|
||||
|
||||
const colorStrings = (user as any)?.colorStrings || {};
|
||||
primaryColor = colorStrings.primaryColor || null;
|
||||
secondaryColor = colorStrings.secondaryColor || null;
|
||||
tertiaryColor = colorStrings.tertiaryColor || null;
|
||||
const displayStyles = !inGuild ? (user as any)?.displayNameStyles || {} : {};
|
||||
|
||||
primaryColor = forceWhite ? "#ffffff" : colorStrings.primaryColor || displayStyles?.colors?.[0] || "#ffffff";
|
||||
if (typeof primaryColor === "number") primaryColor = `#${primaryColor.toString(16).padStart(6, "0")}`;
|
||||
|
||||
secondaryColor = forceWhite ? null : colorStrings.secondaryColor || displayStyles?.colors?.[1] || null;
|
||||
if (typeof secondaryColor === "number") secondaryColor = `#${secondaryColor.toString(16).padStart(6, "0")}`;
|
||||
|
||||
tertiaryColor = forceWhite ? null : colorStrings.tertiaryColor || displayStyles?.colors?.[2] || null;
|
||||
if (typeof tertiaryColor === "number") tertiaryColor = `#${tertiaryColor.toString(16).padStart(6, "0")}`;
|
||||
|
||||
primaryAdjusted = primaryColor;
|
||||
secondaryAdjusted = secondaryColor;
|
||||
tertiaryAdjusted = tertiaryColor;
|
||||
|
||||
if (!primaryColor) { return fallbackReturn; }
|
||||
|
||||
if (primaryColor && percentage) {
|
||||
primaryAdjusted = adjustHex(primaryColor, parseInt(percentage));
|
||||
}
|
||||
|
|
@ -128,7 +130,7 @@ function resolveColor(user: User | GuildMember, savedColor: string, fallbackColo
|
|||
}
|
||||
}
|
||||
|
||||
gradient = !canUseGradient || !secondaryColor
|
||||
gradient = !canUseGradient || !secondaryColor || forceWhite
|
||||
? null
|
||||
: tertiaryColor
|
||||
? "linear-gradient(to right,var(--custom-gradient-color-1),var(--custom-gradient-color-2),var(--custom-gradient-color-3),var(--custom-gradient-color-1))"
|
||||
|
|
@ -160,7 +162,7 @@ function resolveColor(user: User | GuildMember, savedColor: string, fallbackColo
|
|||
"--custom-gradient-color-2": secondaryColor || primaryColor,
|
||||
"--custom-gradient-color-3": tertiaryColor || primaryColor,
|
||||
"background-image": gradient,
|
||||
"animation": "show-me-your-name-animation 1.5s linear infinite"
|
||||
"animation": "smyn-animation 1.5s linear infinite"
|
||||
},
|
||||
static: {
|
||||
original: {
|
||||
|
|
@ -227,7 +229,14 @@ function validTemplate(value: string) {
|
|||
}
|
||||
}
|
||||
|
||||
function getProcessedNames(author: any, truncateAllNamesWithStreamerMode: boolean, discriminators: boolean): {
|
||||
function getProcessedNames(
|
||||
author: any,
|
||||
truncateAllNamesWithStreamerMode: boolean,
|
||||
discriminators: boolean,
|
||||
inGuild: boolean,
|
||||
friendNameOnlyInDirectMessages: boolean,
|
||||
customNameOnlyInDirectMessages: boolean
|
||||
): {
|
||||
username: string | null;
|
||||
display: string | null;
|
||||
nick: string | null;
|
||||
|
|
@ -263,13 +272,13 @@ function getProcessedNames(author: any, truncateAllNamesWithStreamerMode: boolea
|
|||
? author.nick[0] + "..."
|
||||
: author.nick as string;
|
||||
|
||||
const friendName: string | null = author ? RelationshipStore.getNickname(author.id) || null : null;
|
||||
const friendName: string | null = (author && !(inGuild && friendNameOnlyInDirectMessages)) ? RelationshipStore.getNickname(author.id) || null : null;
|
||||
const friend: string | null = !friendName ? null
|
||||
: StreamerModeStore.enabled && (truncateAllNamesWithStreamerMode || friendName.toLowerCase() === author.username.toLowerCase())
|
||||
? friendName[0] + "..."
|
||||
: friendName as string;
|
||||
|
||||
const customName: string | null = !author?.id ? null : customNicknames[author.id] || null;
|
||||
const customName: string | null = (author && !(inGuild && customNameOnlyInDirectMessages)) ? customNicknames[author.id] || null : null;
|
||||
const custom: string | null = !customName ? null
|
||||
: StreamerModeStore.enabled && (truncateAllNamesWithStreamerMode || customName.toLowerCase() === author.username.toLowerCase())
|
||||
? customName[0] + "..."
|
||||
|
|
@ -311,7 +320,7 @@ function getMemberListProfilesReactionsVoiceName(
|
|||
const member = guildId && user ? GuildMemberStore.getMember(guildId, user.id) : null;
|
||||
const author = user && member ? { ...user, ...member } : user || member || null;
|
||||
const shouldHookless = type === "reactionsTooltip" || type === "profilesTooltip";
|
||||
return renderUsername(author, null, null, type, "", shouldHookless);
|
||||
return renderUsername(author, null, null, type, "", shouldHookless, !!guildId);
|
||||
}
|
||||
|
||||
function getMemberListProfilesReactionsVoiceNameText(props: memberListProfileReactionProps): string | null {
|
||||
|
|
@ -332,7 +341,7 @@ function getMessageName(props: messageProps): [string | null, JSX.Element | null
|
|||
const member = isWebhook ? null : target && channel ? GuildMemberStore.getMember(channel.guild_id, target.id) : null;
|
||||
const author = user && member ? { ...user, ...member } : user || member || null;
|
||||
const mentionSymbol = hideDefaultAtSign && (!isRepliedMessage || replies) ? "" : withMentionPrefix ? "@" : "";
|
||||
return renderUsername(author, channel?.id || null, message?.id || null, isRepliedMessage ? "replies" : "messages", mentionSymbol);
|
||||
return renderUsername(author, channel?.id || null, message?.id || null, isRepliedMessage ? "replies" : "messages", mentionSymbol, false, !!channel?.guild_id);
|
||||
}
|
||||
|
||||
function getMessageNameElement(props: messageProps): JSX.Element | null {
|
||||
|
|
@ -343,7 +352,7 @@ function getMessageNameText(props: messageProps): string | null {
|
|||
return getMessageName(props)[0];
|
||||
}
|
||||
|
||||
export function getMentionNameElement(props: mentionProps): JSX.Element | null {
|
||||
function getMentionNameElement(props: mentionProps): JSX.Element | null {
|
||||
const { hideDefaultAtSign, mentions } = settings.use(["hideDefaultAtSign", "mentions"]);
|
||||
const { channelId, userId, props: nestedProps } = props;
|
||||
const channel = channelId ? ChannelStore.getChannel(channelId) || null : null;
|
||||
|
|
@ -351,7 +360,43 @@ export function getMentionNameElement(props: mentionProps): JSX.Element | null {
|
|||
const member = channel ? GuildMemberStore.getMember(channel.guild_id, userId) : null;
|
||||
const author = user && member ? { ...user, ...member } : user || member || null;
|
||||
const mentionSymbol = hideDefaultAtSign && mentions ? "" : "@";
|
||||
return renderUsername(author, channelId || null, nestedProps?.messageId || null, "mentions", mentionSymbol)[1];
|
||||
return renderUsername(author, channelId || null, nestedProps?.messageId || null, "mentions", mentionSymbol, false, !!channel?.guild_id)[1];
|
||||
}
|
||||
|
||||
function getEffectType(effectId: number | null | undefined): string | null {
|
||||
switch (effectId) {
|
||||
case 1: return "solid";
|
||||
// Delegate gradient effect handling to the guild
|
||||
// gradient handler. This adds animation to DM
|
||||
// gradients which are usually static.
|
||||
// case 2: return "gradient";
|
||||
case 3: return "neon";
|
||||
case 4: return "toon";
|
||||
case 5: return "pop";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
function computeEffectCSSVars(styles: any): Record<string, string> {
|
||||
if (!styles?.colors?.length) return {};
|
||||
|
||||
const toHex = (n: number) => `#${(n >>> 0).toString(16).padStart(6, "0")}`;
|
||||
const primary = toHex(styles.colors[0]);
|
||||
const secondary = styles.colors.length > 1 ? toHex(styles.colors[1]) : primary;
|
||||
const adjusted = adjustColor(primary);
|
||||
|
||||
return {
|
||||
"--smyn-effect-main-color": adjusted.main,
|
||||
"--smyn-effect-gradient-start": primary,
|
||||
"--smyn-effect-gradient-end": secondary,
|
||||
"--smyn-effect-light-1": adjusted.light1,
|
||||
"--smyn-effect-light-2": adjusted.light2,
|
||||
"--smyn-effect-dark-1": adjusted.dark1,
|
||||
"--smyn-effect-dark-2": adjusted.dark2,
|
||||
"--smyn-effect-neon-stroke": adjusted.neonStroke,
|
||||
"--smyn-effect-neon-flicker": `hsl(from ${adjusted.main} h calc(min(1, s) * ((s * 1.1) + 10)) 85)`,
|
||||
"--smyn-effect-toon-stroke": adjusted.toonStroke,
|
||||
};
|
||||
}
|
||||
|
||||
function renderUsername(
|
||||
|
|
@ -360,7 +405,8 @@ function renderUsername(
|
|||
messageId: string | null,
|
||||
type: "messages" | "replies" | "mentions" | "membersList" | "profilesPopout" | "profilesTooltip" | "reactionsTooltip" | "reactionsPopout" | "voiceChannel",
|
||||
mentionSymbol: string,
|
||||
hookless = false
|
||||
hookless: boolean,
|
||||
inGuild: boolean
|
||||
): [string | null, JSX.Element | null, string | null] {
|
||||
const isMessage = type === "messages";
|
||||
const isReply = type === "replies";
|
||||
|
|
@ -372,19 +418,44 @@ function renderUsername(
|
|||
const isReaction = isReactionsTooltip || isReactionsPopout;
|
||||
const isVoice = type === "voiceChannel";
|
||||
|
||||
const config = hookless ? settings.store : settings.use(["messages", "replies", "mentions", "memberList", "profilePopout", "reactions", "discriminators", "hideDefaultAtSign", "truncateAllNamesWithStreamerMode", "removeDuplicates", "ignoreGradients", "ignoreFonts", "animateGradients", "includedNames", "customNameColor", "friendNameColor", "nicknameColor", "displayNameColor", "usernameColor", "nameSeparator", "triggerNameRerender"]);
|
||||
const { messages, replies, mentions, memberList, profilePopout, reactions, discriminators, hideDefaultAtSign, truncateAllNamesWithStreamerMode, removeDuplicates, ignoreGradients, ignoreFonts, animateGradients, includedNames, customNameColor, friendNameColor, nicknameColor, displayNameColor, usernameColor, nameSeparator, triggerNameRerender } = config;
|
||||
const config = hookless ? settings.store : settings.use(["messages", "replies", "mentions", "memberList", "profilePopout", "reactions", "friendNameOnlyInDirectMessages", "customNameOnlyInDirectMessages", "discriminators", "hideDefaultAtSign", "truncateAllNamesWithStreamerMode", "removeDuplicates", "ignoreGradients", "ignoreFonts", "animateGradients", "includedNames", "customNameColor", "friendNameColor", "nicknameColor", "displayNameColor", "usernameColor", "nameSeparator", "triggerNameRerender"]);
|
||||
const { messages, replies, mentions, memberList, profilePopout, reactions, friendNameOnlyInDirectMessages, customNameOnlyInDirectMessages, discriminators, truncateAllNamesWithStreamerMode, removeDuplicates, ignoreGradients, ignoreFonts, animateGradients, includedNames, customNameColor, friendNameColor, nicknameColor, displayNameColor, usernameColor, nameSeparator, triggerNameRerender } = config;
|
||||
|
||||
const channel = channelId ? ChannelStore.getChannel(channelId) || null : null;
|
||||
const message = channelId && messageId ? MessageStore.getMessage(channelId, messageId) : null;
|
||||
const groupId = (message as any)?.showMeYourNameGroupId || null;
|
||||
|
||||
const isHovering = (isMessage || isMention)
|
||||
? ((messageId && hoveringMessageMap.has(messageId)) || (groupId && hoveringMessageMap.has(groupId)))
|
||||
: isReply
|
||||
? (messageId && hoveringRepliesMap.has(messageId)) || (groupId && hoveringRepliesMap.has(groupId))
|
||||
: isReactionsPopout
|
||||
? hoveringReactionPopoutSet.has((author as User).id)
|
||||
: false;
|
||||
|
||||
const authorDisplayNameStyles = (!inGuild && (author as any)?.displayNameStyles) || null;
|
||||
const effectType = authorDisplayNameStyles ? getEffectType(authorDisplayNameStyles.effectId) : null;
|
||||
const effectCSSVars = authorDisplayNameStyles ? computeEffectCSSVars(authorDisplayNameStyles) : {};
|
||||
const hasEffect = !!effectType;
|
||||
const needsEffectDataAttr = effectType === "neon" || effectType === "toon" || effectType === "pop";
|
||||
const shouldShowEffect = hasEffect && isHovering;
|
||||
const shouldAnimateEffect = shouldShowEffect && !AccessibilityStore.useReducedMotion;
|
||||
const shouldUseDMDefault = !inGuild && !isHovering;
|
||||
|
||||
const canUseGradient = ((author as GuildMember)?.guildId ? (GuildStore.getGuild((author as GuildMember).guildId) ?? {}).premiumFeatures?.features.includes("ENHANCED_ROLE_COLORS") : !inGuild);
|
||||
const useTopRoleStyle = isMention || isReactionsPopout || channel?.isDM() || channel?.isGroupDM();
|
||||
const topRoleStyle = author ? resolveColor(author, "Role", canUseGradient, inGuild, shouldUseDMDefault) : null;
|
||||
const hasGradient = !!topRoleStyle?.gradient && Object.keys(topRoleStyle.gradient).length > 0;
|
||||
|
||||
const canUseGradient = ((author as GuildMember)?.guildId ? (GuildStore.getGuild((author as GuildMember).guildId) ?? {}).premiumFeatures?.features.includes("ENHANCED_ROLE_COLORS") : false);
|
||||
const textMutedValue = getComputedStyle(document.documentElement)?.getPropertyValue("--text-muted")?.trim() || "#72767d";
|
||||
const options = splitTemplate(includedNames);
|
||||
const resolvedUsernameColor = author ? resolveColor(author, usernameColor.trim(), "", canUseGradient) : null;
|
||||
const resolvedDisplayNameColor = author ? resolveColor(author, displayNameColor.trim(), "", canUseGradient) : null;
|
||||
const resolvedNicknameColor = author ? resolveColor(author, nicknameColor.trim(), "", canUseGradient) : null;
|
||||
const resolvedFriendNameColor = author ? resolveColor(author, friendNameColor.trim(), "", canUseGradient) : null;
|
||||
const resolvedCustomNameColor = author ? resolveColor(author, customNameColor.trim(), "", canUseGradient) : null;
|
||||
const resolvedUsernameColor = author ? resolveColor(author, usernameColor.trim(), canUseGradient, inGuild, shouldUseDMDefault) : null;
|
||||
const resolvedDisplayNameColor = author ? resolveColor(author, displayNameColor.trim(), canUseGradient, inGuild, shouldUseDMDefault) : null;
|
||||
const resolvedNicknameColor = author ? resolveColor(author, nicknameColor.trim(), canUseGradient, inGuild, shouldUseDMDefault) : null;
|
||||
const resolvedFriendNameColor = author ? resolveColor(author, friendNameColor.trim(), canUseGradient, inGuild, shouldUseDMDefault) : null;
|
||||
const resolvedCustomNameColor = author ? resolveColor(author, customNameColor.trim(), canUseGradient, inGuild, shouldUseDMDefault) : null;
|
||||
const affixColor = { color: textMutedValue, "-webkit-text-fill-color": textMutedValue, isolation: "isolate", "white-space": "pre", "font-family": "var(--font-primary)", "letter-spacing": "normal" };
|
||||
const { username, display, nick, friend, custom } = getProcessedNames(author, truncateAllNamesWithStreamerMode, discriminators);
|
||||
const { username, display, nick, friend, custom } = getProcessedNames(author, truncateAllNamesWithStreamerMode, discriminators, inGuild, friendNameOnlyInDirectMessages, customNameOnlyInDirectMessages);
|
||||
|
||||
const names: Record<string, [string | null, object | null]> = {
|
||||
user: [username, resolvedUsernameColor],
|
||||
|
|
@ -416,7 +487,7 @@ function renderUsername(
|
|||
}
|
||||
}
|
||||
|
||||
if (!chosenName || !chosenStyle) {
|
||||
if (!chosenName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +497,7 @@ function renderUsername(
|
|||
wrapped: chosenName,
|
||||
type: chosenType,
|
||||
suffix: suffix || "",
|
||||
style: chosenStyle as React.CSSProperties
|
||||
style: chosenStyle
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -513,18 +584,6 @@ function renderUsername(
|
|||
fourth = remainingNames.shift();
|
||||
fifth = remainingNames.shift();
|
||||
|
||||
const useTopRoleStyle = isMention || isReactionsPopout;
|
||||
const topRoleStyle = resolveColor(author, "Role", "", canUseGradient);
|
||||
const hasGradient = !!topRoleStyle?.gradient && Object.keys(topRoleStyle.gradient).length > 0;
|
||||
const message = channelId && messageId ? MessageStore.getMessage(channelId, messageId) : null;
|
||||
const groupId = (message as any)?.showMeYourNameGroupId || null;
|
||||
|
||||
const isHovering = (isMessage || isReply || isMention)
|
||||
? ((messageId && hoveringMessageMap.has(messageId)) || (groupId && hoveringMessageMap.has(groupId)))
|
||||
: isReactionsPopout
|
||||
? hoveringReactionPopoutSet.has((author as User).id)
|
||||
: false;
|
||||
|
||||
const shouldGradientGlow = isHovering && hasGradient;
|
||||
const shouldAnimateGradients = shouldGradientGlow && !AccessibilityStore.useReducedMotion;
|
||||
const shouldAnimateSecondaryNames = animateGradients && !ignoreGradients;
|
||||
|
|
@ -560,21 +619,34 @@ function renderUsername(
|
|||
...(isReactionsPopout
|
||||
? { display: "flex", flexWrap: "wrap", lineHeight: "1.1em", fontSize: "0.9em" }
|
||||
: {}),
|
||||
...(hasEffect ? effectCSSVars : {})
|
||||
} as React.CSSProperties;
|
||||
|
||||
const nameElement = (
|
||||
<span style={topLevelStyle} className="show-me-your-name-container">
|
||||
<span
|
||||
style={{
|
||||
...topLevelStyle,
|
||||
...(topRoleStyle?.normal.original || {})
|
||||
}}
|
||||
className="show-me-your-name-container"
|
||||
>
|
||||
{mentionSymbol && <span>{mentionSymbol}</span>}
|
||||
{(
|
||||
<span
|
||||
className={(shouldGradientGlow ? (gradientClasses + " " + firstGroupClasses) : firstGroupClasses)}
|
||||
className={SMYNC(firstGroupClasses, { [gradientClasses]: shouldGradientGlow })}
|
||||
data-text={shouldGradientGlow ? firstDataText : undefined}
|
||||
style={(shouldGradientGlow && useTopRoleStyle && topRoleStyle ? topRoleStyle.gradient.animated : undefined) as React.CSSProperties}
|
||||
>
|
||||
<span
|
||||
className={firstNameClasses}
|
||||
style={
|
||||
topRoleStyle ?
|
||||
className={SMYNC(firstNameClasses, {
|
||||
"smyn-effect-container": shouldShowEffect,
|
||||
[`smyn-effect-${effectType}`]: shouldShowEffect,
|
||||
"smyn-effect-animated": shouldAnimateEffect
|
||||
})}
|
||||
data-username-with-effects={needsEffectDataAttr && shouldShowEffect ? first.name : undefined}
|
||||
style={shouldShowEffect
|
||||
? undefined
|
||||
: topRoleStyle ?
|
||||
shouldAnimateGradients && topRoleStyle.gradient
|
||||
? topRoleStyle.gradient.animated
|
||||
: topRoleStyle.gradient
|
||||
|
|
@ -593,7 +665,7 @@ function renderUsername(
|
|||
].map(({ name, dataText, groupClass, nameClass, position }) => name && (
|
||||
<span
|
||||
key={position}
|
||||
className={(shouldGradientGlow && shouldAnimateSecondaryNames ? (gradientClasses + " " + groupClass) : groupClass)}
|
||||
className={SMYNC(groupClass, { [gradientClasses]: shouldGradientGlow && shouldAnimateSecondaryNames })}
|
||||
data-text={shouldGradientGlow && dataText ? dataText : undefined}
|
||||
style={(shouldGradientGlow && shouldAnimateSecondaryNames ? name.style.gradient.animated : undefined) as React.CSSProperties}
|
||||
>
|
||||
|
|
@ -601,17 +673,24 @@ function renderUsername(
|
|||
<span>{nameSeparator}</span>
|
||||
{name.prefix}</span>
|
||||
<span
|
||||
// On non-primary names, allow disabling the gradients completely, or just their animation & glow.
|
||||
className={nameClass}
|
||||
// On non-primary names, allow disabling the effects completely, or just their animation & glow.
|
||||
className={SMYNC(nameClass, {
|
||||
"smyn-effect-container": shouldShowEffect && !ignoreGradients,
|
||||
[`smyn-effect-${effectType}`]: shouldShowEffect && !ignoreGradients,
|
||||
"smyn-effect-animated": shouldAnimateEffect && shouldAnimateSecondaryNames
|
||||
})}
|
||||
data-username-with-effects={needsEffectDataAttr && shouldShowEffect && !ignoreGradients ? name.name : undefined}
|
||||
style={{
|
||||
...(ignoreFonts ? { "font-family": "var(--font-primary)", "letter-spacing": "normal" } : {}),
|
||||
...(ignoreGradients
|
||||
? name.style.normal.adjusted
|
||||
: shouldAnimateGradients && shouldAnimateSecondaryNames && name.style.gradient
|
||||
? name.style.gradient.animated
|
||||
: name.style.gradient
|
||||
? name.style.gradient.static.original
|
||||
: name.style.normal.adjusted)
|
||||
...(name.style
|
||||
? ignoreGradients
|
||||
? name.style.normal.adjusted
|
||||
: shouldAnimateGradients && shouldAnimateSecondaryNames && name.style.gradient
|
||||
? name.style.gradient.animated
|
||||
: name.style.gradient
|
||||
? name.style.gradient.static.original
|
||||
: name.style.normal.adjusted
|
||||
: {})
|
||||
}}>
|
||||
{name.wrapped}</span>
|
||||
<span style={affixColor as React.CSSProperties} className={suffixClasses}>
|
||||
|
|
@ -625,10 +704,12 @@ function renderUsername(
|
|||
}
|
||||
|
||||
const hoveringMessageMap = new Map<string, number>();
|
||||
const hoveringRepliesMap = new Map<string, number>();
|
||||
const hoveringReactionPopoutSet = new Set<string>();
|
||||
|
||||
function handleHoveringMessage(message: any, isHovering: boolean) {
|
||||
const messageId = message?.id;
|
||||
const repliedId = message?.messageReference?.message_id;
|
||||
const groupId = message?.showMeYourNameGroupId ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -637,9 +718,11 @@ function handleHoveringMessage(message: any, isHovering: boolean) {
|
|||
if (isHovering) {
|
||||
addHoveringMessage(messageId);
|
||||
addHoveringMessage(groupId);
|
||||
addHoveringReply(repliedId);
|
||||
} else {
|
||||
removeHoveringMessage(messageId);
|
||||
removeHoveringMessage(groupId);
|
||||
removeHoveringReply(repliedId);
|
||||
}
|
||||
}, [messageId, groupId, isHovering]);
|
||||
}
|
||||
|
|
@ -668,6 +751,30 @@ function removeHoveringMessage(id: string) {
|
|||
}
|
||||
}
|
||||
|
||||
function addHoveringReply(id: string) {
|
||||
if (!id) return;
|
||||
|
||||
const currentCount = hoveringRepliesMap.get(id) || 0;
|
||||
hoveringRepliesMap.set(id, currentCount + 1);
|
||||
|
||||
if (currentCount === 0) {
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
}
|
||||
}
|
||||
|
||||
function removeHoveringReply(id: string) {
|
||||
if (!id) return;
|
||||
|
||||
const currentCount = hoveringRepliesMap.get(id) || 0;
|
||||
|
||||
if (currentCount <= 1) {
|
||||
hoveringRepliesMap.delete(id);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
} else {
|
||||
hoveringRepliesMap.set(id, currentCount - 1);
|
||||
}
|
||||
}
|
||||
|
||||
function addHoveringReactionPopout(id: string) {
|
||||
hoveringReactionPopoutSet.add(id);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
|
|
@ -789,7 +896,7 @@ const settings = definePluginSettings({
|
|||
memberList: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Display the first available name listed in your custom name format in the member list.",
|
||||
description: "Display the first available name listed in your custom name format in the member list and DMs list.",
|
||||
},
|
||||
profilePopout: {
|
||||
type: OptionType.BOOLEAN,
|
||||
|
|
@ -834,18 +941,28 @@ const settings = definePluginSettings({
|
|||
ignoreGradients: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "For the non-primary names, if the role has a gradient, ignore it in favor of the color set below.",
|
||||
description: "For the non-primary names, if the role has a gradient or nitro effect, ignore it in favor of the color set below.",
|
||||
},
|
||||
animateGradients: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
description: "For the non-primary names, if the role has a gradient, animate it. This is disabled by \"Ignore Gradients\" and reduced motion.",
|
||||
description: "For the non-primary names, if the role has a gradient or nitro effect, animate it. This is disabled by \"Ignore Gradients\" and reduced motion.",
|
||||
},
|
||||
nameSeparator: {
|
||||
type: OptionType.STRING,
|
||||
description: "The separator to use between names. The default is a single space.",
|
||||
default: " ",
|
||||
},
|
||||
friendNameOnlyInDirectMessages: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
description: "Only display friend names when in DMs, and not in servers.",
|
||||
},
|
||||
customNameOnlyInDirectMessages: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
description: "Only display custom names when in DMs, and not in servers.",
|
||||
},
|
||||
includedNames: {
|
||||
type: OptionType.STRING,
|
||||
description: "The order to display usernames, display names, nicknames, friend names, and custom names. Use the following placeholders: {user}, {display}, {nick}, {friend}, {custom}. You can provide multiple name options to use as fallbacks if one is unavailable by separating them with commas as such: {custom, friend, nick}. You can have up to three prefixes and three suffixes per name.",
|
||||
|
|
@ -898,6 +1015,8 @@ export default definePlugin({
|
|||
isModified: true,
|
||||
settings,
|
||||
|
||||
UserStore,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: '="SYSTEM_TAG"',
|
||||
|
|
@ -923,6 +1042,14 @@ export default definePlugin({
|
|||
replace: "$self.getMemberListProfilesReactionsVoiceNameText({...arguments[0],type:\"membersList\"})??"
|
||||
},
|
||||
},
|
||||
{
|
||||
// Replace name in solo DM title bar and tooltip.
|
||||
find: "channel.isSystemDM(),",
|
||||
replacement: {
|
||||
match: /(?<=}\);)(return.{0,500}?{text:)(\i,position:"bottom",children:.{0,40}?children:)(\i\?\?\i.\i.getName\(\i\))/,
|
||||
replace: "const smynName=arguments[0].channel.recipients.length===1?$self.getMemberListProfilesReactionsVoiceNameText({user:$self.UserStore.getUser(arguments[0].channel.recipients[0]),type:\"profilesPopout\"})??null:null;$1smynName??$2smynName??$3"
|
||||
},
|
||||
},
|
||||
{
|
||||
// Track hovering on messages to animate gradients.
|
||||
// Attach the group ID to their messages to allow animating gradients within a group.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
@keyframes show-me-your-name-animation {
|
||||
/* stylelint-disable rule-empty-line-before */
|
||||
/* stylelint-disable property-no-vendor-prefix */
|
||||
|
||||
@keyframes smyn-animation {
|
||||
0% {
|
||||
background-position: 0;
|
||||
}
|
||||
|
|
@ -8,12 +11,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.show-me-your-name-gradient {
|
||||
.smyn-gradient {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.show-me-your-name-gradient::after {
|
||||
.smyn-gradient::after {
|
||||
background-clip: text;
|
||||
content: attr(data-text) / "";
|
||||
inset: 0;
|
||||
|
|
@ -26,25 +29,286 @@
|
|||
z-index: -1;
|
||||
}
|
||||
|
||||
.show-me-your-name-gradient-unset-bg::after {
|
||||
.smyn-gradient-unset-bg::after {
|
||||
background: unset;
|
||||
}
|
||||
|
||||
.show-me-your-name-gradient-inherit-bg::after {
|
||||
.smyn-gradient-inherit-bg::after {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.show-me-your-name-gradient-hovered::after {
|
||||
.smyn-gradient-hovered::after {
|
||||
opacity: .7;
|
||||
transition: opacity .1s ease-in-out
|
||||
transition: opacity .1s ease-in-out;
|
||||
}
|
||||
|
||||
.show-me-your-name-modal-footer-container button {
|
||||
.smyn-modal-footer-container button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.show-me-your-name-reset-button {
|
||||
.smyn-reset-button {
|
||||
margin-top: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.smyn-container:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.smyn-effect-container {
|
||||
position: relative;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.smyn-effect-solid {
|
||||
color: var(--smyn-effect-main-color);
|
||||
}
|
||||
|
||||
.smyn-effect-gradient {
|
||||
background: linear-gradient(to bottom right, var(--smyn-effect-gradient-start) 10%, var(--smyn-effect-gradient-end) 90%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
background-size: 100% auto;
|
||||
-webkit-text-fill-color: transparent;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.smyn-effect-neon {
|
||||
paint-order: stroke fill;
|
||||
-webkit-text-stroke-width: calc(1px + .04em);
|
||||
-webkit-text-stroke-color: var(--smyn-effect-neon-stroke);
|
||||
-webkit-text-fill-color: currentcolor;
|
||||
color: var(--white);
|
||||
margin-inline-start: calc(-1px - .04em);
|
||||
margin-bottom: calc(-1px - .04em);
|
||||
padding-bottom: calc(1px + .04em);
|
||||
padding-inline-start: calc(1px + .04em);
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.smyn-effect-neon::after {
|
||||
content: attr(data-username-with-effects);
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(to bottom left, var(--smyn-effect-light-2) 0, var(--smyn-effect-light-2) 6%, var(--smyn-effect-main-color) 20%, var(--smyn-effect-light-1) 50%, var(--smyn-effect-light-2) 56%, var(--smyn-effect-main-color) 70%, var(--smyn-effect-light-1) 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
background-position: 100% 0;
|
||||
background-size: 200% 200%;
|
||||
-webkit-text-fill-color: transparent;
|
||||
color: var(--smyn-effect-main-color);
|
||||
filter: blur(calc(1px + .12em));
|
||||
opacity: 0;
|
||||
transition: opacity .15s ease-in-out;
|
||||
-webkit-text-stroke-width: calc(1px + .04em);
|
||||
-webkit-text-stroke-color: transparent;
|
||||
perspective: 1px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.smyn-effect-animated.smyn-effect-neon {
|
||||
animation: smyn-neon-flicker 4s cubic-bezier(.24, .31, .36, .93) infinite;
|
||||
animation-direction: normal;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.smyn-effect-animated.smyn-effect-neon::after {
|
||||
opacity: .8;
|
||||
animation: smyn-neon-glow-flicker 1666ms linear infinite;
|
||||
animation-direction: normal;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.smyn-effect-toon {
|
||||
--smyn-toon-stroke-width: calc(1.6px + 0.04em);
|
||||
--smyn-toon-margin: calc(var(--smyn-toon-stroke-width)*-1);
|
||||
|
||||
paint-order: stroke fill;
|
||||
position: relative;
|
||||
-webkit-text-stroke-width: var(--smyn-toon-stroke-width);
|
||||
-webkit-text-stroke-color: var(--smyn-effect-toon-stroke);
|
||||
color: var(--smyn-effect-toon-stroke);
|
||||
margin-inline: var(--smyn-toon-margin);
|
||||
margin-bottom: var(--smyn-toon-margin);
|
||||
padding-inline: var(--smyn-toon-stroke-width);
|
||||
padding-bottom: var(--smyn-toon-stroke-width);
|
||||
transition: color 266ms cubic-bezier(.43, .21, .27, .78);
|
||||
}
|
||||
|
||||
.smyn-effect-toon::before {
|
||||
background: linear-gradient(180deg, var(--white) 0, var(--smyn-effect-light-2) 8%, var(--smyn-effect-light-1) 15%, var(--smyn-effect-main-color) 25%, var(--smyn-effect-light-2) 45%, var(--smyn-effect-main-color) 55%, var(--white) 75%, var(--smyn-effect-light-2) 83%, var(--smyn-effect-light-1) 90%, var(--smyn-effect-main-color) 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
background-size: 100% 400%;
|
||||
content: attr(data-username-with-effects);
|
||||
inset: 0;
|
||||
padding-inline: var(--smyn-toon-stroke-width);
|
||||
padding-bottom: var(--smyn-toon-margin);
|
||||
position: absolute;
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-text-stroke-width: 0;
|
||||
-webkit-text-stroke-color: transparent;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: opacity 266ms cubic-bezier(.43, .21, .27, .78);
|
||||
}
|
||||
|
||||
.smyn-effect-pop {
|
||||
--smyn-pop-stroke-width: calc(1.2px + 0.04em);
|
||||
--smyn-pop-bottom-translate: 0.08em;
|
||||
|
||||
display: inline-block;
|
||||
color: var(--white);
|
||||
paint-order: stroke fill;
|
||||
position: relative;
|
||||
-webkit-text-stroke-color: var(--smyn-effect-dark-2);
|
||||
-webkit-text-stroke-width: var(--smyn-pop-stroke-width);
|
||||
margin-inline-start: calc(var(--smyn-pop-stroke-width)*-1);
|
||||
margin-bottom: calc(var(--smyn-pop-stroke-width)*-1 - var(--smyn-pop-bottom-translate));
|
||||
padding-bottom: calc(var(--smyn-pop-stroke-width) + var(--smyn-pop-bottom-translate));
|
||||
padding-inline-start: var(--smyn-pop-stroke-width);
|
||||
}
|
||||
|
||||
.smyn-effect-pop::before {
|
||||
bottom: calc(var(--smyn-pop-bottom-translate)*-1 - var(--smyn-pop-stroke-width));
|
||||
color: var(--smyn-effect-main-color);
|
||||
content: attr(data-username-with-effects);
|
||||
padding-inline-start: var(--smyn-pop-stroke-width);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: calc(100% - var(--smyn-pop-stroke-width));
|
||||
z-index: -1;
|
||||
-webkit-text-stroke-width: var(--smyn-pop-stroke-width);
|
||||
-webkit-text-stroke-color: transparent;
|
||||
background: linear-gradient(to bottom left, var(--smyn-effect-light-1) 0, var(--smyn-effect-light-1) 6%, var(--smyn-effect-main-color) 20%, var(--smyn-effect-main-color) 50%, var(--smyn-effect-light-1) 56%, var(--smyn-effect-main-color) 70%, var(--smyn-effect-main-color) 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
background-position: 100% 0;
|
||||
background-size: 200% 200%;
|
||||
transform: translate3d(0, var(--smyn-pop-bottom-translate), 0);
|
||||
-webkit-text-fill-color: transparent;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.smyn-effect-pop:hover::before {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--smyn-effect-main-color);
|
||||
text-underline-offset: calc(var(--smyn-pop-bottom-translate));
|
||||
}
|
||||
|
||||
.smyn-effect-animated.smyn-effect-toon::before {
|
||||
animation: smyn-toon-animation 4s cubic-bezier(.44, .29, .48, 1) infinite;
|
||||
animation-direction: normal;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.smyn-effect-animated.smyn-effect-pop {
|
||||
animation: smyn-pop-main 4s cubic-bezier(.44, .29, .48, 1) infinite;
|
||||
animation-direction: normal;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.smyn-effect-animated.smyn-effect-pop::before {
|
||||
animation: smyn-pop-shadow 4s cubic-bezier(.44, .29, .48, 1) infinite;
|
||||
animation-direction: normal;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes smyn-neon-flicker {
|
||||
|
||||
0%,
|
||||
15%,
|
||||
18%,
|
||||
20%,
|
||||
23%,
|
||||
25%,
|
||||
50% {
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
16%,
|
||||
22%,
|
||||
28% {
|
||||
color: var(--smyn-effect-neon-flicker);
|
||||
}
|
||||
|
||||
51%,
|
||||
100% {
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes smyn-neon-glow-flicker {
|
||||
0% {
|
||||
background-position: 100% 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes smyn-toon-animation {
|
||||
|
||||
0%,
|
||||
5% {
|
||||
background-position: 50% 0;
|
||||
}
|
||||
|
||||
55%,
|
||||
100% {
|
||||
background-position: 50% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes smyn-pop-main {
|
||||
0% {
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
18% {
|
||||
perspective: 1px;
|
||||
transform: translate3d(0, -.05em, 0);
|
||||
}
|
||||
|
||||
35% {
|
||||
perspective: 1px;
|
||||
transform: translate3d(0, .08em, 0);
|
||||
}
|
||||
|
||||
50%,
|
||||
100% {
|
||||
perspective: 1px;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes smyn-pop-shadow {
|
||||
0% {
|
||||
background-position: 100% 0;
|
||||
perspective: 1px;
|
||||
transform: translate3d(0, .08em, 0);
|
||||
}
|
||||
|
||||
18% {
|
||||
perspective: 1px;
|
||||
transform: translate3d(0, .13em, 0);
|
||||
}
|
||||
|
||||
35% {
|
||||
perspective: 1px;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
50%,
|
||||
100% {
|
||||
background-position: 0 100%;
|
||||
perspective: 1px;
|
||||
transform: translate3d(0, .08em, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -83,6 +83,7 @@ export let ApplicationCommandIndexStore: t.ApplicationCommandIndexStore;
|
|||
export let EditMessageStore: t.EditMessageStore;
|
||||
export let QuestStore: t.QuestStore;
|
||||
export let PendingReplyStore: GenericStore;
|
||||
export let ExperimentStore: GenericStore;
|
||||
|
||||
/**
|
||||
* @see jsdoc of {@link t.useStateFromStores}
|
||||
|
|
@ -137,6 +138,7 @@ waitForStore("PopoutWindowStore", m => PopoutWindowStore = m);
|
|||
waitForStore("ApplicationCommandIndexStore", m => ApplicationCommandIndexStore = m);
|
||||
waitForStore("EditMessageStore", m => EditMessageStore = m);
|
||||
waitForStore("PendingReplyStore", m => PendingReplyStore = m);
|
||||
waitForStore("ExperimentStore", m => ExperimentStore = m);
|
||||
waitForStore("QuestStore", m => QuestStore = m);
|
||||
waitForStore("ThemeStore", m => {
|
||||
ThemeStore = m;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue