From c88da3a6aba9a44cdebb66be18f4456450faa70c Mon Sep 17 00:00:00 2001 From: Etorix <92535668+EtorixDev@users.noreply.github.com> Date: Fri, 13 Feb 2026 09:01:35 -0800 Subject: [PATCH] SMYN: Support for nitro effects in DMs & limiting friend/custom names to DMs. (#764) * Stuff. * I hate it here. * Clarify setting. * Fix Questify patch. --- src/equicordplugins/questify/index.tsx | 4 +- src/plugins/showMeYourName/index.tsx | 277 +++++++++++++++++------- src/plugins/showMeYourName/style.css | 282 ++++++++++++++++++++++++- 3 files changed, 477 insertions(+), 86 deletions(-) diff --git a/src/equicordplugins/questify/index.tsx b/src/equicordplugins/questify/index.tsx index cb14afbf1..645f56598 100644 --- a/src/equicordplugins/questify/index.tsx +++ b/src/equicordplugins/questify/index.tsx @@ -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)??" } }, diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index 7687b7fba..83502998c 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -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 | 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 { + 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 = { 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 = ( - + {mentionSymbol && {mentionSymbol}} {( name && ( @@ -601,17 +673,24 @@ function renderUsername( {nameSeparator} {name.prefix} {name.wrapped} @@ -625,10 +704,12 @@ function renderUsername( } const hoveringMessageMap = new Map(); +const hoveringRepliesMap = new Map(); const hoveringReactionPopoutSet = new Set(); 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. diff --git a/src/plugins/showMeYourName/style.css b/src/plugins/showMeYourName/style.css index 45cfb18a0..1903eef63 100644 --- a/src/plugins/showMeYourName/style.css +++ b/src/plugins/showMeYourName/style.css @@ -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); + } } \ No newline at end of file