SMYN: New styles + broader capability. (#1298)
* New Styles. * More locations, DM non-hover styling, etc. * Corrections. * More Options. * Fixes. * Fixes.
This commit is contained in:
parent
4bcb1dca36
commit
ca0bd2c129
2 changed files with 454 additions and 406 deletions
|
|
@ -9,7 +9,7 @@ import "./style.css";
|
|||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { DataStore } from "@api/index";
|
||||
import { isPluginEnabled } from "@api/PluginManager";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { definePluginSettings, migratePluginSetting } from "@api/Settings";
|
||||
import { TextButton } from "@components/Button";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Heading } from "@components/Heading";
|
||||
|
|
@ -19,13 +19,36 @@ import { Devs, EquicordDevs } from "@utils/constants";
|
|||
import { classNameFactory } from "@utils/index";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { GuildMember, Message, RenderModalProps, User } from "@vencord/discord-types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { AccessibilityStore, ChannelStore, GuildMemberStore, GuildStore, Menu, MessageStore, Modal, openModal, RelationshipStore, StreamerModeStore, TextInput, useEffect, UserStore, useState } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
const SMYNC = classNameFactory();
|
||||
const wrapEmojis = findByCodeLazy("lastIndex;return");
|
||||
const adjustColor = findByCodeLazy("light1", '.get("hsl.s"))');
|
||||
|
||||
interface DisplayNameStyles {
|
||||
colors: number[];
|
||||
effectId: number;
|
||||
fontId: number;
|
||||
}
|
||||
|
||||
interface UserNameWithEffectsProps {
|
||||
userName: string;
|
||||
displayNameStyles: DisplayNameStyles;
|
||||
effectDisplayType: number;
|
||||
textClassName?: string;
|
||||
loop?: boolean;
|
||||
shouldWrap?: boolean;
|
||||
}
|
||||
|
||||
const UserNameWithEffects = findComponentByCodeLazy<UserNameWithEffectsProps>(
|
||||
"UserNameWithEffects",
|
||||
"--custom-display-name-styles-prism-cycle"
|
||||
);
|
||||
|
||||
const DisplayNameEffectDisplayTypes: Record<"PLAIN" | "STATIC" | "ANIMATED", number> = findByPropsLazy("PLAIN", "STATIC", "ANIMATED");
|
||||
const DisplayNameEffects: Record<"GRADIENT" | "GLOW" | "POP" | "GUMMY", number> = findByPropsLazy("SOLID", "GRADIENT", "NEON", "TOON", "POP", "GLOW", "PRISM", "GUMMY");
|
||||
const DisplayNameFonts: Record<"DEFAULT", number> = findByPropsLazy("DEFAULT", "CHERRY_BOMB", "CHICLE", "MUSEO_MODERNO");
|
||||
|
||||
const roleColorPattern = /^role((?:\+|-)\d{0,4})?$/iu;
|
||||
const symbolPattern = /^[\p{S}\p{P}]{1,3}$/iu;
|
||||
|
|
@ -106,12 +129,12 @@ function validColor(color: string) {
|
|||
|
||||
function resolveColor(
|
||||
colorStrings: colorStringsType,
|
||||
displayNameStyles: { effectId: number; colors: number[]; } | null | undefined,
|
||||
displayNameStyles: DisplayNameStyles | null | undefined,
|
||||
savedColor: string,
|
||||
canUseGradient: boolean,
|
||||
inGuild: boolean,
|
||||
ircColorsEnabled: boolean,
|
||||
isHovering: boolean,
|
||||
shouldShowEffects: boolean,
|
||||
): Record<string, any> | null {
|
||||
const defaultColor = getComputedStyle(document.documentElement).getPropertyValue("--text-strong").trim() || null;
|
||||
|
||||
|
|
@ -119,7 +142,11 @@ function resolveColor(
|
|||
|
||||
savedColor = savedColor.trim() || defaultColor;
|
||||
const isRoleColor = savedColor.toLowerCase().includes("role");
|
||||
const forceDefault = !inGuild && !ircColorsEnabled && (isRoleColor ? !isHovering : false);
|
||||
const forceDefault = !inGuild
|
||||
&& !ircColorsEnabled
|
||||
&& !displayNameStyles
|
||||
&& isRoleColor
|
||||
&& !shouldShowEffects;
|
||||
|
||||
let gradient: any = null;
|
||||
let primaryColor: any = null;
|
||||
|
|
@ -327,6 +354,13 @@ interface memberListProfileReactionProps {
|
|||
type: "typingIndicator" | "membersList" | "profilesPopout" | "profilesTooltip" | "reactionsTooltip" | "reactionsPopout" | "voiceChannel";
|
||||
guildId?: string;
|
||||
tags?: any;
|
||||
isHovered?: boolean;
|
||||
}
|
||||
|
||||
interface activeNowNameProps {
|
||||
user: User | null | undefined;
|
||||
guildId?: string;
|
||||
isHovered: boolean;
|
||||
}
|
||||
|
||||
type colorStringsType = { primaryColor: string | null, secondaryColor: string | null, tertiaryColor: string | null; } | null | undefined;
|
||||
|
|
@ -341,7 +375,7 @@ function getTypingMemberListProfilesReactionsVoiceName(
|
|||
const member = guildId && user ? GuildMemberStore.getMember(guildId, user.id) : null;
|
||||
const author = user && member ? { ...user, ...member } : user || member || null;
|
||||
const shouldHookless = ["typingIndicator", "reactionsTooltip", "profilesTooltip"].includes(type);
|
||||
return renderUsername(author, null, null, type, "", shouldHookless, !!guildId);
|
||||
return renderUsername(author, null, null, type, "", shouldHookless, !!guildId, undefined, undefined, props.isHovered);
|
||||
}
|
||||
|
||||
function getTypingMemberListProfilesReactionsVoiceNameText(props: memberListProfileReactionProps): string | null {
|
||||
|
|
@ -352,6 +386,26 @@ function getTypingMemberListProfilesReactionsVoiceNameElement(props: memberListP
|
|||
return getTypingMemberListProfilesReactionsVoiceName(props)[1];
|
||||
}
|
||||
|
||||
function getActiveNowNameElement({ user, guildId, isHovered }: activeNowNameProps): JSX.Element | string | null {
|
||||
const name = getTypingMemberListProfilesReactionsVoiceNameText({ user, guildId, type: "membersList" });
|
||||
|
||||
if (!name) return null;
|
||||
|
||||
const displayNameStyles: DisplayNameStyles | null = (
|
||||
AccessibilityStore.displayNameStylesEnabled
|
||||
&& (user as any)?.displayNameStyles
|
||||
) || null;
|
||||
|
||||
return displayNameStyles
|
||||
? <DisplayNameEffectName
|
||||
name={name}
|
||||
styles={displayNameStyles}
|
||||
animate={shouldAnimateNameEffects(isHovered, settings.store.styleActiveNow)}
|
||||
showStaticEffect={settings.store.styleActiveNow}
|
||||
/>
|
||||
: name;
|
||||
}
|
||||
|
||||
function getMessageName(props: messageProps): [string | null, JSX.Element | null, string | null] {
|
||||
const { hideDefaultAtSign, replies } = settings.use(["hideDefaultAtSign", "replies"]);
|
||||
const { message, userOverride, isRepliedMessage, withMentionPrefix } = props;
|
||||
|
|
@ -397,40 +451,96 @@ function getMentionNameElement(props: mentionProps): JSX.Element | null {
|
|||
return renderUsername(author, channelId || null, nestedProps?.messageId || null, "mentions", mentionSymbol, false, !!channel?.guild_id, colorString, colorStrings)[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 getDisplayNameStyles(styles: DisplayNameStyles, ignoreFont: boolean): DisplayNameStyles {
|
||||
return ignoreFont
|
||||
? { ...styles, fontId: DisplayNameFonts.DEFAULT }
|
||||
: styles;
|
||||
}
|
||||
|
||||
function computeEffectCSSVars(styles: any): Record<string, string> {
|
||||
if (!styles?.colors?.length) return {};
|
||||
function getDisplayNameEffectDisplayType(isHovered: boolean, showStaticEffect = true): number {
|
||||
if (shouldAnimateNameEffects(isHovered, showStaticEffect)) return DisplayNameEffectDisplayTypes.ANIMATED;
|
||||
|
||||
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 showStaticEffect
|
||||
? DisplayNameEffectDisplayTypes.STATIC
|
||||
: DisplayNameEffectDisplayTypes.PLAIN;
|
||||
}
|
||||
|
||||
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 shouldAnimateNameEffects(isHovered: boolean, isEffectVisible = true): boolean {
|
||||
return isHovered || (isEffectVisible && settings.store.alwaysAnimateEffects);
|
||||
}
|
||||
|
||||
function getDisplayNameEffectClassName(
|
||||
styles: DisplayNameStyles | null | undefined,
|
||||
effectDisplayType: number,
|
||||
): string {
|
||||
const useGradientAnimationOverride = needsGradientAnimationOverride(styles)
|
||||
&& effectDisplayType === DisplayNameEffectDisplayTypes.ANIMATED
|
||||
&& !AccessibilityStore.useReducedMotion;
|
||||
|
||||
return [
|
||||
"smyn-native-effect",
|
||||
useGradientAnimationOverride && "smyn-native-gradient-animated",
|
||||
styles?.effectId === DisplayNameEffects.GRADIENT && settings.store.gradientGlow && "smyn-native-gradient-glow",
|
||||
useGradientAnimationOverride && styles?.effectId === DisplayNameEffects.GRADIENT && settings.store.gradientGlow && "smyn-native-gradient-glow-active",
|
||||
styles?.effectId === DisplayNameEffects.POP && "smyn-native-pop",
|
||||
styles?.effectId === DisplayNameEffects.GUMMY && "smyn-native-gummy",
|
||||
styles?.effectId === DisplayNameEffects.GUMMY
|
||||
&& effectDisplayType === DisplayNameEffectDisplayTypes.ANIMATED
|
||||
&& !AccessibilityStore.useReducedMotion
|
||||
&& "smyn-native-gummy-animated",
|
||||
].filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
function DisplayNameEffectName({ name, styles, animate, ignoreFont = false, showStaticEffect = true, useMessageLayout = false }: {
|
||||
name: string;
|
||||
styles: DisplayNameStyles;
|
||||
animate: boolean;
|
||||
ignoreFont?: boolean;
|
||||
showStaticEffect?: boolean;
|
||||
useMessageLayout?: boolean;
|
||||
}) {
|
||||
const effectDisplayType = animate
|
||||
? DisplayNameEffectDisplayTypes.ANIMATED
|
||||
: showStaticEffect
|
||||
? DisplayNameEffectDisplayTypes.STATIC
|
||||
: DisplayNameEffectDisplayTypes.PLAIN;
|
||||
|
||||
return (
|
||||
<UserNameWithEffects
|
||||
userName={name}
|
||||
displayNameStyles={getDisplayNameStyles(styles, ignoreFont)}
|
||||
effectDisplayType={effectDisplayType}
|
||||
textClassName={SMYNC(getDisplayNameEffectClassName(styles, effectDisplayType), {
|
||||
"smyn-native-message-effect": useMessageLayout,
|
||||
})}
|
||||
loop
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function needsGradientAnimationOverride(styles: DisplayNameStyles | null | undefined): boolean {
|
||||
return styles?.effectId === DisplayNameEffects.GRADIENT
|
||||
|| styles?.effectId === DisplayNameEffects.GLOW;
|
||||
}
|
||||
|
||||
function getSecondaryNameStyle(
|
||||
style: Record<string, any> | null,
|
||||
useNativeEffect: boolean,
|
||||
ignoreEffects: boolean,
|
||||
animateGradient: boolean,
|
||||
showGradientGlow: boolean,
|
||||
): Record<string, any> {
|
||||
if (!style) return {};
|
||||
|
||||
if (useNativeEffect) {
|
||||
if (!showGradientGlow || !style.gradient) return {};
|
||||
return animateGradient ? style.gradient.animated : style.gradient.static.original;
|
||||
}
|
||||
|
||||
if (ignoreEffects) return style.normal.adjusted;
|
||||
if (animateGradient && style.gradient) return style.gradient.animated;
|
||||
|
||||
return style.gradient?.static.original ?? style.normal.adjusted;
|
||||
}
|
||||
|
||||
function renderUsername(
|
||||
|
|
@ -442,7 +552,8 @@ function renderUsername(
|
|||
hookless: boolean,
|
||||
inGuild: boolean,
|
||||
colorString?: string,
|
||||
colorStrings?: { primaryColor: string | null, secondaryColor: string | null, tertiaryColor: string | null; } | null
|
||||
colorStrings?: { primaryColor: string | null, secondaryColor: string | null, tertiaryColor: string | null; } | null,
|
||||
isHoveredOverride?: boolean,
|
||||
): [string | null, JSX.Element | null, string | null] {
|
||||
const isMessage = type === "messages";
|
||||
const isReply = type === "replies";
|
||||
|
|
@ -455,20 +566,23 @@ function renderUsername(
|
|||
const isReaction = isReactionsTooltip || isReactionsPopout;
|
||||
const isVoice = type === "voiceChannel";
|
||||
|
||||
const config = hookless ? settings.store : settings.use(["messages", "replies", "mentions", "typingIndicator", "memberList", "profilePopout", "reactions", "friendNameOnlyInDirectMessages", "customNameOnlyInDirectMessages", "discriminators", "hideDefaultAtSign", "truncateAllNamesWithStreamerMode", "removeDuplicates", "ignoreGradients", "ignoreFonts", "animateGradients", "includedNames", "customNameColor", "friendNameColor", "nicknameColor", "displayNameColor", "usernameColor", "nameSeparator", "triggerNameRerender"]);
|
||||
const { messages, replies, mentions, typingIndicator, memberList, profilePopout, reactions, friendNameOnlyInDirectMessages, customNameOnlyInDirectMessages, discriminators, truncateAllNamesWithStreamerMode, removeDuplicates, ignoreGradients, ignoreFonts, animateGradients, includedNames, customNameColor, friendNameColor, nicknameColor, displayNameColor, usernameColor, nameSeparator, triggerNameRerender } = config;
|
||||
const config = hookless ? settings.store : settings.use(["messages", "replies", "mentions", "typingIndicator", "memberList", "styleDirectMessagesList", "styleDirectMessagesMessages", "styleFriendsList", "styleActiveNow", "profilePopout", "reactions", "friendNameOnlyInDirectMessages", "customNameOnlyInDirectMessages", "discriminators", "hideDefaultAtSign", "truncateAllNamesWithStreamerMode", "removeDuplicates", "ignoreEffects", "ignoreFonts", "animateEffects", "alwaysAnimateEffects", "gradientGlow", "includedNames", "customNameColor", "friendNameColor", "nicknameColor", "displayNameColor", "usernameColor", "nameSeparator", "triggerNameRerender"]);
|
||||
const { messages, replies, mentions, typingIndicator, memberList, styleDirectMessagesMessages, profilePopout, reactions, friendNameOnlyInDirectMessages, customNameOnlyInDirectMessages, discriminators, truncateAllNamesWithStreamerMode, removeDuplicates, ignoreEffects, ignoreFonts, animateEffects, 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 showStaticDisplayNameEffect = !isMessage
|
||||
|| !(channel?.isDM() || channel?.isGroupDM())
|
||||
|| styleDirectMessagesMessages;
|
||||
|
||||
const isHovering = (isMessage || isMention)
|
||||
? ((messageId && hoveringMessageMap.has(messageId)) || (groupId && hoveringMessageMap.has(groupId)))
|
||||
const isHovered = isHoveredOverride ?? ((isMessage || isMention)
|
||||
? Boolean((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;
|
||||
? Boolean((messageId && hoveringRepliesMap.has(messageId)) || (groupId && hoveringRepliesMap.has(groupId)))
|
||||
: false);
|
||||
const shouldShowHoverEffects = shouldAnimateNameEffects(isHovered, showStaticDisplayNameEffect);
|
||||
const shouldShowDisplayNameStyle = showStaticDisplayNameEffect || shouldShowHoverEffects;
|
||||
|
||||
if (colorString && !colorStrings) {
|
||||
colorStrings = {
|
||||
|
|
@ -481,26 +595,27 @@ function renderUsername(
|
|||
const ircColorsEnabled = isPluginEnabled(ircColors.name);
|
||||
|
||||
const authorColorStrings = colorStrings || (author as any)?.colorStrings || null;
|
||||
const authorDisplayNameStyles = (!inGuild && !ircColorsEnabled && (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 authorDisplayNameStyles: DisplayNameStyles | null = (
|
||||
!inGuild
|
||||
&& !ircColorsEnabled
|
||||
&& AccessibilityStore.displayNameStylesEnabled
|
||||
&& (author as any)?.displayNameStyles
|
||||
) || null;
|
||||
const shouldShowDisplayNameEffect = !!authorDisplayNameStyles;
|
||||
const usesGradientAnimationOverride = needsGradientAnimationOverride(authorDisplayNameStyles);
|
||||
|
||||
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(authorColorStrings, authorDisplayNameStyles, "Role", canUseGradient, inGuild, ircColorsEnabled, isHovering) : null;
|
||||
const topRoleStyle = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, "Role", canUseGradient, inGuild, ircColorsEnabled, shouldShowHoverEffects) : null;
|
||||
const hasGradient = !!topRoleStyle?.gradient && Object.keys(topRoleStyle.gradient).length > 0;
|
||||
|
||||
const textMutedValue = getComputedStyle(document.documentElement)?.getPropertyValue("--text-muted")?.trim() || "#72767d";
|
||||
const options = splitTemplate(includedNames);
|
||||
const resolvedUsernameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, usernameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, isHovering) : null;
|
||||
const resolvedDisplayNameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, displayNameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, isHovering) : null;
|
||||
const resolvedNicknameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, nicknameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, isHovering) : null;
|
||||
const resolvedFriendNameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, friendNameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, isHovering) : null;
|
||||
const resolvedCustomNameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, customNameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, isHovering) : null;
|
||||
const resolvedUsernameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, usernameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, shouldShowHoverEffects) : null;
|
||||
const resolvedDisplayNameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, displayNameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, shouldShowHoverEffects) : null;
|
||||
const resolvedNicknameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, nicknameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, shouldShowHoverEffects) : null;
|
||||
const resolvedFriendNameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, friendNameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, shouldShowHoverEffects) : null;
|
||||
const resolvedCustomNameColor = author ? resolveColor(authorColorStrings, authorDisplayNameStyles, customNameColor.trim(), canUseGradient, inGuild, ircColorsEnabled, shouldShowHoverEffects) : 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, inGuild, friendNameOnlyInDirectMessages, customNameOnlyInDirectMessages);
|
||||
|
||||
|
|
@ -634,19 +749,22 @@ function renderUsername(
|
|||
fourth = remainingNames.shift();
|
||||
fifth = remainingNames.shift();
|
||||
|
||||
const shouldGradientGlow = isHovering && hasGradient;
|
||||
const shouldAnimateGradients = shouldGradientGlow && !AccessibilityStore.useReducedMotion;
|
||||
const shouldAnimateSecondaryNames = animateGradients && !ignoreGradients;
|
||||
const shouldShowGradientGlow = shouldShowHoverEffects
|
||||
&& hasGradient
|
||||
&& (!authorDisplayNameStyles || (usesGradientAnimationOverride && authorDisplayNameStyles.effectId !== DisplayNameEffects.GRADIENT));
|
||||
const shouldAnimatePrimaryGradient = shouldShowGradientGlow && !AccessibilityStore.useReducedMotion;
|
||||
const shouldAnimateSecondaryEffects = shouldShowHoverEffects && animateEffects && !ignoreEffects;
|
||||
const shouldShowSecondaryDisplayNameEffect = shouldShowDisplayNameEffect && !ignoreEffects;
|
||||
|
||||
const firstDataText = mentionSymbol + first.name;
|
||||
const secondDataText = second && shouldAnimateSecondaryNames ? second.name : "";
|
||||
const thirdDataText = third && shouldAnimateSecondaryNames ? third.name : "";
|
||||
const fourthDataText = fourth && shouldAnimateSecondaryNames ? fourth.name : "";
|
||||
const fifthDataText = fifth && shouldAnimateSecondaryNames ? fifth.name : "";
|
||||
const secondDataText = second && shouldAnimateSecondaryEffects ? second.name : "";
|
||||
const thirdDataText = third && shouldAnimateSecondaryEffects ? third.name : "";
|
||||
const fourthDataText = fourth && shouldAnimateSecondaryEffects ? fourth.name : "";
|
||||
const fifthDataText = fifth && shouldAnimateSecondaryEffects ? fifth.name : "";
|
||||
const allDataText = [firstDataText, secondDataText, thirdDataText, fourthDataText, fifthDataText].filter(Boolean).join(nameSeparator).trim();
|
||||
|
||||
// Only mentions and reactions popouts should patch in the gradient glow or else a double glow will appear on messages.
|
||||
const hoveringClass = (isHovering ? " smyn-gradient-hovered" : "");
|
||||
const hoveringClass = (shouldShowHoverEffects ? " smyn-gradient-hovered" : "");
|
||||
const gradientClasses = useTopRoleStyle
|
||||
? "smyn-gradient smyn-gradient-inherit-bg" + hoveringClass
|
||||
: "smyn-gradient smyn-gradient-unset-bg" + hoveringClass;
|
||||
|
|
@ -671,43 +789,51 @@ function renderUsername(
|
|||
...(isReactionsPopout
|
||||
? { display: "flex", flexWrap: "wrap", lineHeight: "1.1em", fontSize: "0.9em" }
|
||||
: {}),
|
||||
...(hasEffect ? effectCSSVars : {}),
|
||||
"--smyn-gradient-duration": `${animationDuration}s`
|
||||
"--smyn-gradient-duration": `${animationDuration}s`,
|
||||
"--smyn-underline-color": topRoleStyle?.normal.original?.["text-decoration-color"],
|
||||
} as React.CSSProperties;
|
||||
|
||||
const nameElement = (
|
||||
<span
|
||||
style={{
|
||||
...topLevelStyle,
|
||||
...(topRoleStyle?.normal.original || {})
|
||||
...(shouldShowDisplayNameEffect ? {} : topRoleStyle?.normal.original || {})
|
||||
}}
|
||||
className="smyn-container"
|
||||
>
|
||||
{mentionSymbol && <span>{mentionSymbol}</span>}
|
||||
{(
|
||||
<span
|
||||
className={SMYNC(firstGroupClasses, { [gradientClasses]: shouldGradientGlow })}
|
||||
data-text={shouldGradientGlow ? firstDataText : undefined}
|
||||
style={(shouldGradientGlow && useTopRoleStyle && topRoleStyle ? topRoleStyle.gradient.animated : undefined) as React.CSSProperties}
|
||||
className={SMYNC(firstGroupClasses, { [gradientClasses]: shouldShowGradientGlow })}
|
||||
data-text={shouldShowGradientGlow ? firstDataText : undefined}
|
||||
style={(shouldShowGradientGlow && useTopRoleStyle && topRoleStyle?.gradient
|
||||
? shouldAnimatePrimaryGradient
|
||||
? topRoleStyle.gradient.animated
|
||||
: topRoleStyle.gradient.static.original
|
||||
: undefined) as React.CSSProperties}
|
||||
>
|
||||
<span
|
||||
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
|
||||
className={SMYNC(firstNameClasses)}
|
||||
style={shouldShowDisplayNameEffect
|
||||
? undefined
|
||||
: topRoleStyle ?
|
||||
shouldAnimateGradients && topRoleStyle.gradient
|
||||
shouldAnimatePrimaryGradient && topRoleStyle.gradient
|
||||
? topRoleStyle.gradient.animated
|
||||
: topRoleStyle.gradient
|
||||
? topRoleStyle.gradient.static.original
|
||||
: topRoleStyle.normal.original
|
||||
: undefined
|
||||
}>
|
||||
{first.wrapped}</span>
|
||||
{shouldShowDisplayNameEffect && authorDisplayNameStyles
|
||||
? <DisplayNameEffectName
|
||||
name={first.name}
|
||||
styles={authorDisplayNameStyles}
|
||||
animate={shouldShowHoverEffects}
|
||||
showStaticEffect={showStaticDisplayNameEffect}
|
||||
useMessageLayout={isMessage}
|
||||
/>
|
||||
: first.wrapped}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
{[
|
||||
|
|
@ -726,26 +852,30 @@ function renderUsername(
|
|||
<span
|
||||
// On non-primary names, allow disabling the effects completely, or just their animation & glow.
|
||||
className={SMYNC(nameClass, {
|
||||
[gradientClasses]: shouldGradientGlow && shouldAnimateSecondaryNames,
|
||||
"smyn-effect-container": shouldShowEffect && !ignoreGradients,
|
||||
[`smyn-effect-${effectType}`]: shouldShowEffect && !ignoreGradients,
|
||||
"smyn-effect-animated": shouldAnimateEffect && shouldAnimateSecondaryNames
|
||||
[gradientClasses]: shouldShowGradientGlow && shouldAnimateSecondaryEffects,
|
||||
})}
|
||||
data-text={shouldGradientGlow && dataText ? dataText : undefined}
|
||||
data-username-with-effects={needsEffectDataAttr && shouldShowEffect && !ignoreGradients ? name.name : undefined}
|
||||
data-text={shouldShowGradientGlow && dataText ? dataText : undefined}
|
||||
style={{
|
||||
...(ignoreFonts ? { "font-family": "var(--font-primary)", "letter-spacing": "normal" } : {}),
|
||||
...(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
|
||||
: {})
|
||||
...(ignoreFonts ? { fontFamily: "var(--font-primary)", letterSpacing: "normal" } : {}),
|
||||
...getSecondaryNameStyle(
|
||||
shouldShowDisplayNameStyle ? name.style : null,
|
||||
shouldShowSecondaryDisplayNameEffect,
|
||||
ignoreEffects,
|
||||
shouldAnimatePrimaryGradient && shouldAnimateSecondaryEffects,
|
||||
shouldShowGradientGlow && shouldAnimateSecondaryEffects,
|
||||
)
|
||||
}}>
|
||||
{name.wrapped}</span>
|
||||
{shouldShowSecondaryDisplayNameEffect && authorDisplayNameStyles
|
||||
? <DisplayNameEffectName
|
||||
name={name.name}
|
||||
styles={authorDisplayNameStyles}
|
||||
animate={shouldAnimateSecondaryEffects}
|
||||
ignoreFont={ignoreFonts}
|
||||
showStaticEffect={showStaticDisplayNameEffect}
|
||||
useMessageLayout={isMessage}
|
||||
/>
|
||||
: name.wrapped}
|
||||
</span>
|
||||
<span style={affixColor as React.CSSProperties} className={suffixClasses}>
|
||||
{name.suffix}</span>
|
||||
</span>
|
||||
|
|
@ -758,28 +888,25 @@ 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) {
|
||||
function handleHoveringMessage(message: any, isHovered: boolean) {
|
||||
const messageId = message?.id;
|
||||
const repliedId = message?.messageReference?.message_id;
|
||||
const groupId = message?.showMeYourNameGroupId ?? "";
|
||||
|
||||
const effectiveIsHovering = settings.store.alwaysShowEffects || isHovering;
|
||||
|
||||
useEffect(() => {
|
||||
if (!message) return;
|
||||
if (!message || !isHovered) return;
|
||||
|
||||
if (effectiveIsHovering) {
|
||||
addHoveringMessage(messageId);
|
||||
addHoveringMessage(groupId);
|
||||
addHoveringReply(repliedId);
|
||||
} else {
|
||||
addHoveringMessage(messageId);
|
||||
addHoveringMessage(groupId);
|
||||
addHoveringReply(repliedId);
|
||||
|
||||
return () => {
|
||||
removeHoveringMessage(messageId);
|
||||
removeHoveringMessage(groupId);
|
||||
removeHoveringReply(repliedId);
|
||||
}
|
||||
}, [messageId, groupId, effectiveIsHovering]);
|
||||
};
|
||||
}, [messageId, repliedId, groupId, isHovered]);
|
||||
}
|
||||
|
||||
function addHoveringMessage(id: string) {
|
||||
|
|
@ -789,7 +916,7 @@ function addHoveringMessage(id: string) {
|
|||
hoveringMessageMap.set(id, currentCount + 1);
|
||||
|
||||
if (currentCount === 0) {
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
triggerNameRerender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -800,7 +927,7 @@ function removeHoveringMessage(id: string) {
|
|||
|
||||
if (currentCount <= 1) {
|
||||
hoveringMessageMap.delete(id);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
triggerNameRerender();
|
||||
} else {
|
||||
hoveringMessageMap.set(id, currentCount - 1);
|
||||
}
|
||||
|
|
@ -813,7 +940,7 @@ function addHoveringReply(id: string) {
|
|||
hoveringRepliesMap.set(id, currentCount + 1);
|
||||
|
||||
if (currentCount === 0) {
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
triggerNameRerender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -824,19 +951,17 @@ function removeHoveringReply(id: string) {
|
|||
|
||||
if (currentCount <= 1) {
|
||||
hoveringRepliesMap.delete(id);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
triggerNameRerender();
|
||||
} else {
|
||||
hoveringRepliesMap.set(id, currentCount - 1);
|
||||
}
|
||||
}
|
||||
|
||||
function addHoveringReactionPopout(id: string) {
|
||||
hoveringReactionPopoutSet.add(id);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
function useNameHoverState() {
|
||||
return useState(false);
|
||||
}
|
||||
|
||||
function removeHoveringReactionPopout(id: string) {
|
||||
hoveringReactionPopoutSet.delete(id);
|
||||
function triggerNameRerender() {
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
}
|
||||
|
||||
|
|
@ -862,7 +987,7 @@ function CustomNicknameModal({ modalProps, user }: { modalProps: RenderModalProp
|
|||
}
|
||||
|
||||
await DataStore.set("SMYNCustomNicknames", customNicknames);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
triggerNameRerender();
|
||||
modalProps.onClose();
|
||||
}
|
||||
},
|
||||
|
|
@ -893,7 +1018,7 @@ function CustomNicknameModal({ modalProps, user }: { modalProps: RenderModalProp
|
|||
setValue("");
|
||||
delete customNicknames[user.id];
|
||||
await DataStore.set("SMYNCustomNicknames", customNicknames);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
triggerNameRerender();
|
||||
}}
|
||||
>
|
||||
Reset SMYN Nickname
|
||||
|
|
@ -925,6 +1050,10 @@ const userContextPatch: NavContextMenuPatchCallback = (children, { user }) => {
|
|||
);
|
||||
};
|
||||
|
||||
migratePluginSetting("ShowMeYourName", "ignoreEffects", "ignoreGradients");
|
||||
migratePluginSetting("ShowMeYourName", "animateEffects", "animateGradients");
|
||||
migratePluginSetting("ShowMeYourName", "alwaysAnimateEffects", "alwaysShowEffects");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
messages: {
|
||||
type: OptionType.BOOLEAN,
|
||||
|
|
@ -949,7 +1078,31 @@ const settings = definePluginSettings({
|
|||
memberList: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Display the first available name listed in your custom name format in the members list, DMs list, and friends list.",
|
||||
description: "Display the first available name listed in your custom name format in the members list, DMs list, friends list, and Active Now.",
|
||||
},
|
||||
styleDirectMessagesList: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
displayName: "Style Direct Messages List",
|
||||
description: "Show display name effects on unselected entries without requiring hover. Selected and hovered entries are still styled when disabled.",
|
||||
},
|
||||
styleDirectMessagesMessages: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
displayName: "Style Direct Messages Messages",
|
||||
description: "Show display name effects on message authors without requiring hover. Hovered names are still styled when disabled.",
|
||||
},
|
||||
styleFriendsList: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
displayName: "Style Friends List",
|
||||
description: "Show display name effects without requiring hover. Hovered entries are still styled when disabled.",
|
||||
},
|
||||
styleActiveNow: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
displayName: "Style Active Now",
|
||||
description: "Show display name effects without requiring hover. Hovered entries are still styled when disabled.",
|
||||
},
|
||||
profilePopout: {
|
||||
type: OptionType.BOOLEAN,
|
||||
|
|
@ -991,15 +1144,26 @@ const settings = definePluginSettings({
|
|||
default: false,
|
||||
description: "For the non-primary names, use Discord's default fonts regardless of the user's custom nitro font.",
|
||||
},
|
||||
ignoreGradients: {
|
||||
ignoreEffects: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "For the non-primary names, if the role has a gradient and the color below is set to \"Role+-#\", use the primary color instead of the whole gradient, and if it has a nitro effect, ignore it entirely."
|
||||
description: "For non-primary names, use only the primary role color and omit display name effects.",
|
||||
},
|
||||
animateGradients: {
|
||||
animateEffects: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
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.",
|
||||
description: "Animate role gradients and display name effects on non-primary names. This is disabled by \"Ignore Effects\" and reduced motion.",
|
||||
},
|
||||
alwaysAnimateEffects: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
displayName: "Always Animate Effects",
|
||||
description: "Force display name effects to animate when they would already be visible, even without hover.",
|
||||
},
|
||||
gradientGlow: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Add a glow to animated user gradients the same way server role gradients glow.",
|
||||
},
|
||||
nameSeparator: {
|
||||
type: OptionType.STRING,
|
||||
|
|
@ -1016,11 +1180,6 @@ const settings = definePluginSettings({
|
|||
default: false,
|
||||
description: "Only display custom names when in DMs, and not in servers.",
|
||||
},
|
||||
alwaysShowEffects: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
description: "Always show effects as if you were hovering.",
|
||||
},
|
||||
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.",
|
||||
|
|
@ -1076,6 +1235,7 @@ export default definePlugin({
|
|||
|
||||
patches: [
|
||||
{
|
||||
// Replace message and reply names and expose their hover context.
|
||||
find: '="SYSTEM_TAG"',
|
||||
group: true,
|
||||
replacement: [
|
||||
|
|
@ -1085,7 +1245,7 @@ export default definePlugin({
|
|||
replace: "$3$self.getMessageNameElement({...arguments[0],colorString:$1,colorStrings:$2})??($4),\"data-text\":$self.getMessageNameText(arguments[0])??($5)"
|
||||
},
|
||||
{
|
||||
// Pass the message object to the should-animate checker.
|
||||
// Expose the message object to SMYN's message-hover patch.
|
||||
match: /(\(\{)(shouldSubscribe)/,
|
||||
replace: "$1message:arguments[0].message,$2"
|
||||
}
|
||||
|
|
@ -1100,29 +1260,94 @@ export default definePlugin({
|
|||
},
|
||||
},
|
||||
{
|
||||
// Replace names in DMs list.
|
||||
// Replace names and preserve display name styles in the DMs list.
|
||||
find: "ImpressionNames.DM_LIST_RIGHT_CLICK_MENU_SHOWN",
|
||||
replacement: {
|
||||
match: /(?<=getMentionCount\(\i.id\)>0\),\i=)/,
|
||||
replace: "$self.getTypingMemberListProfilesReactionsVoiceNameText({...arguments[0],type:\"membersList\"})??"
|
||||
},
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
// Replace the displayed DM name.
|
||||
match: /(?<=getMentionCount\(\i.id\)>0\),\i=)/,
|
||||
replace: "$self.getTypingMemberListProfilesReactionsVoiceNameText({...arguments[0],type:\"membersList\"})??"
|
||||
},
|
||||
{
|
||||
// Keep fonts at rest and gate persistent effects behind the location setting.
|
||||
match: /(?<=userName:\i,)displayNameStyles:(\i)\?\.displayNameStyles,effectDisplayType:(\i\|\|\i\|\|\i)\?\i\.\i\.ANIMATED:\i\.\i\.PLAIN,loop:\i(?=,boldFontOpacity:)/,
|
||||
replace: "displayNameStyles:$1?.displayNameStyles,effectDisplayType:$self.getDisplayNameEffectDisplayType($2,$self.settings.store.styleDirectMessagesList),textClassName:$self.getDisplayNameEffectClassName($1?.displayNameStyles,$self.getDisplayNameEffectDisplayType($2,$self.settings.store.styleDirectMessagesList)),loop:$self.shouldAnimateNameEffects($2,$self.settings.store.styleDirectMessagesList)"
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
// Replace names in the friends list.
|
||||
find: "hasUniqueUsername()}),usernameClass",
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
// Pass the row hover state to Discord's native display-name component.
|
||||
match: /(?<=\(0,\i\.jsx\)\(\i\.\i,\{user:\i,nick:\i,)(?=botClass:)/,
|
||||
replace: "displayNameStylesType:$self.getDisplayNameEffectDisplayType(arguments[0].hovered,$self.settings.store.styleFriendsList),"
|
||||
},
|
||||
{
|
||||
// Replace the displayed friend name.
|
||||
match: /(?<=nick:)(\i)/,
|
||||
replace: "$self.getTypingMemberListProfilesReactionsVoiceNameText({user:arguments[0].user,type:\"membersList\"})??$1"
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
// Set Discord's existing friends-row hover state on pointer entry.
|
||||
find: "handleMouseEnter=()=>{let{isFocused:",
|
||||
replacement: {
|
||||
match: /(?<=nick:)(\i)/,
|
||||
replace: "$self.getTypingMemberListProfilesReactionsVoiceNameText({user:arguments[0].user,type:\"membersList\"})??$1"
|
||||
match: /(?<=handleMouseEnter=\(\)=>\{let\{isFocused:\i,isActive:\i,onOtherHover:\i}=this.props,\{isContextMenuActive:\i}=this.state;this.setState\(\{hovered:)\i(?=}\),)/,
|
||||
replace: "!0"
|
||||
},
|
||||
},
|
||||
{
|
||||
// Don't block name style in friends list just
|
||||
// because the name is the same as the username.
|
||||
// Preserve display name styles when SMYN replaces the friends-list name.
|
||||
find: 'location:"DiscordTag"})',
|
||||
replacement: {
|
||||
match: /(?<=,forceUsername:(\i),.*?displayNameStyles:)\i!==\i\?(\i.displayNameStyles):null/,
|
||||
replace: "!$1?$2:null"
|
||||
},
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
// Keep the user's display name styles when SMYN supplies the name.
|
||||
match: /(?<=showStreamerModeTooltip:\i&&\i\.\i\.isNameConcealed\(e\),displayNameStyles:)\i!==\i\?(\i\.displayNameStyles):null/,
|
||||
replace: "!arguments[0].forceUsername?$1:null"
|
||||
},
|
||||
{
|
||||
// Render native display name effects statically before hover.
|
||||
match: /(?<=displayNameStylesType:\i=\i\.\i\.)PLAIN(?=,\.\.\.\i}=e)/,
|
||||
replace: "STATIC"
|
||||
},
|
||||
{
|
||||
// Attach SMYN's effect-specific classes.
|
||||
match: /(?<=userName:\i,displayNameStyles:(\i),effectDisplayType:(\i))(?=}\):\i)/,
|
||||
replace: ",textClassName:$self.getDisplayNameEffectClassName($1,$2)"
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
find: '("NowPlayingHeader")',
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
// Replace the card title and apply its propagated hover state.
|
||||
match: /(?<=\.Header,\{priorityUser:(\i),guildId:(\i)\?\.id,title:)(\i)(?=,subtitle:.{0,200}?displayNameFont:1===\i\.length)/,
|
||||
replace: "$self.getActiveNowNameElement({user:$1.user,guildId:$2?.id,isHovered:arguments[0].smynHovered})??$3"
|
||||
},
|
||||
{
|
||||
// Add local hover state to each Active Now card.
|
||||
match: /(hasStaffQuestActivityPanelOverride:\i}=\i,\i=(\i)\.useRef\(null\),\[\i,\i\]=\2\.useState\(null\))/,
|
||||
replace: "$1,[smynHovered,setSmynHovered]=$2.useState(!1)"
|
||||
},
|
||||
{
|
||||
// Pass the card hover state to its title renderer.
|
||||
match: /(?<=\(0,\i\.jsx\)\(\i,\{party:\i,onUserContextMenu:\i,quest:\i)(?=}\))/,
|
||||
replace: ",smynHovered"
|
||||
},
|
||||
{
|
||||
// Update local hover state while preserving Discord's handlers.
|
||||
match: /(?<=\{\.\.\.(\i),ref:\i,)onMouseEnter:(\i)(?=,"aria-haspopup":"menu",className:)/,
|
||||
replace: "onMouseEnter:smynEvent=>{$2(smynEvent);setSmynHovered(!0)},onMouseLeave:smynEvent=>{$1.onMouseLeave?.(smynEvent);setSmynHovered(!1)}"
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
// Replace name in solo DM title bar and tooltip.
|
||||
|
|
@ -1133,8 +1358,8 @@ export default definePlugin({
|
|||
},
|
||||
},
|
||||
{
|
||||
// Track hovering on messages to animate gradients.
|
||||
// Attach the group ID to their messages to allow animating gradients within a group.
|
||||
// Track message hover to animate display name effects.
|
||||
// Attach the group ID so every name in a grouped message animates together.
|
||||
find: "CUSTOM_GIFT?\"\":",
|
||||
replacement: [
|
||||
{
|
||||
|
|
@ -1146,6 +1371,7 @@ export default definePlugin({
|
|||
{
|
||||
// Replace names in mentions.
|
||||
find: ".USER_MENTION)",
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
match: /(?=function \i\(\i\){return\(0)/,
|
||||
|
|
@ -1208,18 +1434,24 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
// Replace reaction-popout names and track hover per reactor row.
|
||||
find: ".MESSAGE,userId:",
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
// Track hovering over reaction popouts.
|
||||
// Add local hover state to each reactor row.
|
||||
match: /(function \i\(\i\)\{let\{emoji:\i,user:\i,message:\i,channel:\i,guildId:\i,reactionType:\i,onRemoveReactor:\i\}=\i,)/,
|
||||
replace: "$1[smynHovered,setSmynHovered]=$self.useNameHoverState(),"
|
||||
},
|
||||
{
|
||||
// Update hover state from the full reactor row.
|
||||
match: /(?<=\(0,\i.\i\)\(\i.\i,{className:\i.\i,)(?=(?:align:\i\.\i\.\i\.CENTER|onContextMenu:\i=>))/g,
|
||||
replace: "onMouseEnter:()=>{$self.addHoveringReactionPopout(arguments[0].user.id)},onMouseLeave:()=>{$self.removeHoveringReactionPopout(arguments[0].user.id)},"
|
||||
replace: "onMouseEnter:()=>setSmynHovered(!0),onMouseLeave:()=>setSmynHovered(!1),"
|
||||
},
|
||||
{
|
||||
// Replace names in reaction popouts.
|
||||
match: /(?<=Child,{className:\i.\i,children:)/g,
|
||||
replace: "($self.getTypingMemberListProfilesReactionsVoiceNameElement({user:arguments[0].user,guildId:arguments[0].guildId,type:\"reactionsPopout\"}))??"
|
||||
replace: "($self.getTypingMemberListProfilesReactionsVoiceNameElement({user:arguments[0].user,guildId:arguments[0].guildId,type:\"reactionsPopout\",isHovered:smynHovered}))??"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -1260,36 +1492,25 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
flux: {
|
||||
RELATIONSHIP_UPDATE(data) {
|
||||
// Allows rerendering when changing friend names.
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
},
|
||||
|
||||
RUNNING_STREAMER_TOOLS_CHANGE(data) {
|
||||
// Allows rerendering when toggling streamer mode.
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
},
|
||||
|
||||
ACCESSIBILITY_SYSTEM_PREFERS_REDUCED_MOTION_CHANGED(data) {
|
||||
// Allows rerendering when toggling reduced motion.
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
},
|
||||
|
||||
ACCESSIBILITY_SET_PREFERS_REDUCED_MOTION(data) {
|
||||
// Allows rerendering when toggling reduced motion.
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
}
|
||||
RELATIONSHIP_UPDATE: triggerNameRerender,
|
||||
RUNNING_STREAMER_TOOLS_CHANGE: triggerNameRerender,
|
||||
ACCESSIBILITY_SET_DISPLAY_NAME_STYLES_ENABLED: triggerNameRerender,
|
||||
ACCESSIBILITY_SYSTEM_PREFERS_REDUCED_MOTION_CHANGED: triggerNameRerender,
|
||||
ACCESSIBILITY_SET_PREFERS_REDUCED_MOTION: triggerNameRerender,
|
||||
},
|
||||
|
||||
addHoveringMessage,
|
||||
removeHoveringMessage,
|
||||
handleHoveringMessage,
|
||||
addHoveringReactionPopout,
|
||||
removeHoveringReactionPopout,
|
||||
useNameHoverState,
|
||||
getMessageName,
|
||||
getMessageNameText,
|
||||
getMessageNameElement,
|
||||
getMentionNameElement,
|
||||
getActiveNowNameElement,
|
||||
getDisplayNameEffectClassName,
|
||||
getDisplayNameEffectDisplayType,
|
||||
shouldAnimateNameEffects,
|
||||
getTypingMemberListProfilesReactionsVoiceNameText,
|
||||
getTypingMemberListProfilesReactionsVoiceNameElement
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,263 +54,90 @@
|
|||
font-weight: 500;
|
||||
}
|
||||
|
||||
.smyn-container:hover {
|
||||
text-decoration: underline;
|
||||
.smyn-native-gradient-animated > [data-username-with-effects] {
|
||||
animation: smyn-animation var(--smyn-gradient-duration, 1.5s) linear infinite;
|
||||
background-image: linear-gradient(to right, var(--custom-display-name-styles-gradient-stops), var(--custom-display-name-styles-main-color) 100%) !important;
|
||||
background-size: 100px auto !important;
|
||||
}
|
||||
|
||||
.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%);
|
||||
.smyn-native-gradient-glow > [data-username-with-effects]::after {
|
||||
background: inherit;
|
||||
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;
|
||||
content: attr(data-username-with-effects) / "";
|
||||
filter: blur(4px);
|
||||
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;
|
||||
position: absolute;
|
||||
text-indent: 0;
|
||||
transition: opacity .1s ease-in-out;
|
||||
-webkit-text-fill-color: transparent;
|
||||
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-native-gradient-glow-active > [data-username-with-effects],
|
||||
:where(span, div):has(> .smyn-native-gradient-glow-active),
|
||||
:where(span, div):has(> * > .smyn-native-gradient-glow-active),
|
||||
:where(span, div):has(> * > * > .smyn-native-gradient-glow-active) {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.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-native-gradient-glow-active > [data-username-with-effects]::after {
|
||||
opacity: .7;
|
||||
transition: opacity .1s ease-in-out;
|
||||
}
|
||||
|
||||
.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-native-effect.smyn-native-message-effect {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.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-native-gummy-animated > [data-username-with-effects] {
|
||||
--smyn-gummy-inline-headroom: .12em;
|
||||
|
||||
margin-inline: calc(var(--smyn-gummy-inline-headroom) * -1);
|
||||
padding-inline: var(--smyn-gummy-inline-headroom);
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.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-container:hover {
|
||||
text-decoration-color: var(--smyn-underline-color, currentcolor);
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: initial;
|
||||
text-decoration-thickness: initial;
|
||||
}
|
||||
|
||||
.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;
|
||||
.smyn-container:hover .smyn-native-effect > [data-username-with-effects] {
|
||||
text-decoration-color: var(--smyn-underline-color, currentcolor);
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: initial;
|
||||
text-decoration-thickness: initial;
|
||||
}
|
||||
|
||||
.smyn-container:hover .smyn-native-pop > [data-username-with-effects],
|
||||
.smyn-container:hover .smyn-native-gummy > [data-username-with-effects] {
|
||||
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-container:hover .smyn-native-pop > [data-username-with-effects]::before {
|
||||
text-decoration-color: var(--smyn-underline-color, currentcolor);
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: initial;
|
||||
text-decoration-thickness: initial;
|
||||
text-underline-offset: .08em;
|
||||
}
|
||||
|
||||
.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-container:hover .smyn-native-gummy > [data-username-with-effects] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.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-container:hover .smyn-native-gummy > [data-username-with-effects]::before {
|
||||
color: transparent;
|
||||
content: attr(data-username-with-effects) / "";
|
||||
inset: var(--custom-gummy-squish-headroom, 0) 0 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
text-decoration-color: var(--smyn-underline-color, currentcolor);
|
||||
text-decoration-line: underline;
|
||||
text-decoration-style: initial;
|
||||
text-decoration-thickness: initial;
|
||||
white-space: var(--custom-display-name-styles-wrap);
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue