Plugin
RoleColorEverywhere
Adds the top role color anywhere possible
1
import { definePluginSettings } from "@api/Settings";2
import ErrorBoundary from "@components/ErrorBoundary";3
import { Devs } from "@utils/constants";4
import { Logger } from "@utils/Logger";5
import definePlugin, { makeRange, OptionType } from "@utils/types";6
import { findByCodeLazy } from "@webpack";7
import { ChannelStore, GuildMemberStore, GuildRoleStore, GuildStore } from "@webpack/common";8
9
const useMessageAuthor = findByCodeLazy(039;"Result cannot be null because the message is not null"039;);10
11
const settings = definePluginSettings({12
chatMentions: {13
type: OptionType.BOOLEAN,14
default: true,15
description: "Show role colors in chat mentions (including in the message box)",16
restartNeeded: true17
},18
memberList: {19
type: OptionType.BOOLEAN,20
default: true,21
description: "Show role colors in member list role headers",22
restartNeeded: true23
},24
voiceUsers: {25
type: OptionType.BOOLEAN,26
default: true,27
description: "Show role colors in the voice chat user list",28
restartNeeded: true29
},30
reactorsList: {31
type: OptionType.BOOLEAN,32
default: true,33
description: "Show role colors in the reactors list",34
restartNeeded: true35
},36
pollResults: {37
type: OptionType.BOOLEAN,38
default: true,39
description: "Show role colors in the poll results",40
restartNeeded: true41
},42
colorChatMessages: {43
type: OptionType.BOOLEAN,44
default: false,45
description: "Color chat messages based on the author039;s role color",46
restartNeeded: true,47
},48
messageSaturation: {49
type: OptionType.SLIDER,50
description: "Intensity of message coloring.",51
markers: makeRange(0, 100, 10),52
default: 3053
}54
});55
56
export default definePlugin({57
name: "RoleColorEverywhere",58
authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN, Devs.Kyuuhachi, Devs.jamesbt365],59
description: "Adds the top role color anywhere possible",60
tags: ["Roles", "Appearance"],61
settings,62
63
patches: [64
// Chat Mentions65
{66
find: ".USER_MENTION)",67
replacement: [68
{69
match: /(?<=user:(\i),guildId:([^,]+?),.{0,100}?children:\i=>\i)\((\i)\)/,70
replace: "({...$3,color:$self.getColorInt($1?.id,$2)})",71
}72
],73
predicate: () => settings.store.chatMentions74
},75
// Slate76
{77
// Same find as FullUserInChatbox78
find: 039;"text":"locked"039;,79
replacement: [80
{81
match: /let\{id:(\i),guildId:\i,channelId:(\i)[^}]*\}.*?\.\i,{(?=children)/,82
replace: "$&color:$self.getColorInt($1,$2),"83
}84
],85
predicate: () => settings.store.chatMentions86
},87
// Member List Role Headers88
{89
find: 039;tutorialId:"whos-online039;,90
replacement: [91
{92
match: /(#{intl::CHANNEL_MEMBERS_A11Y_LABEL}.+}\):null,).{0,100}?(?:—|\\u2014) ",\i\]\}\)\]/,93
replace: "$1$self.RoleGroupColor(arguments[0])]"94
},95
],96
predicate: () => settings.store.memberList97
},98
{99
find: "#{intl::THREAD_BROWSER_PRIVATE}",100
replacement: [101
{102
match: /children:\[\i," (?:—|\\u2014) ",\i\]/,103
replace: "children:[$self.RoleGroupColor(arguments[0])]"104
},105
],106
predicate: () => settings.store.memberList107
},108
// Voice Users109
{110
find: "#{intl::GUEST_NAME_SUFFIX})]",111
replacement: [112
{113
match: /#{intl::GUEST_NAME_SUFFIX}.{0,50}?""\](?<=guildId:(\i),.+?user:(\i).+?)/,114
replace: "$&,style:$self.getColorStyle($2.id,$1),"115
}116
],117
predicate: () => settings.store.voiceUsers118
},119
// Reaction List120
{121
find: "MessageReactions.render:",122
replacement: {123
match: /tag:"strong",variant:"text-md\/medium"(?<=onContextMenu:.{0,15}\((\i),(\i),\i\).+?)/,124
replace: "$&,style:$self.getColorStyle($2?.id,$1?.channel?.id)"125
},126
predicate: () => settings.store.reactorsList,127
},128
// Poll Results129
{130
find: ",reactionVoteCounts",131
replacement: {132
match: /\.SIZE_32.+?variant:"text-md\/normal",className:\i\.\i,(?="aria-label":)/,133
replace: "$&style:$self.getColorStyle(arguments[0]?.user?.id,arguments[0]?.channel?.id),"134
},135
predicate: () => settings.store.pollResults136
},137
// Messages138
{139
find: ".SEND_FAILED,",140
replacement: {141
match: /(?<=\]:(\i)\.isUnsupported.{0,50}?,)(?=children:\[)/,142
replace: "style:$self.useMessageColorsStyle($1),"143
},144
predicate: () => settings.store.colorChatMessages145
}146
],147
148
getColorString(userId: string, channelOrGuildId: string) {149
try {150
const guildId = ChannelStore.getChannel(channelOrGuildId)?.guild_id ?? GuildStore.getGuild(channelOrGuildId)?.id;151
if (guildId == null) return null;152
153
return GuildMemberStore.getMember(guildId, userId)?.colorString ?? null;154
} catch (e) {155
new Logger("RoleColorEverywhere").error("Failed to get color string", e);156
}157
158
return null;159
},160
161
getColorInt(userId: string, channelOrGuildId: string) {162
const colorString = this.getColorString(userId, channelOrGuildId);163
return colorString && parseInt(colorString.slice(1), 16);164
},165
166
getColorStyle(userId: string, channelOrGuildId: string) {167
const colorString = this.getColorString(userId, channelOrGuildId);168
169
return colorString && {170
color: colorString171
};172
},173
174
useMessageColorsStyle(message: any) {175
try {176
const { messageSaturation } = settings.use(["messageSaturation"]);177
const author = useMessageAuthor(message);178
179
// Do not apply role color if the send fails, otherwise it becomes indistinguishable180
if (message.state === "SEND_FAILED") return;181
182
if (author.colorString != null && messageSaturation !== 0) {183
const value = `color-mix(in oklab, ${author.colorString} ${messageSaturation}%, var({DEFAULT}))`;184
185
return {186
color: value.replace("{DEFAULT}", "--text-default"),187
"--text-strong": value.replace("{DEFAULT}", "--text-strong"),188
"--text-muted": value.replace("{DEFAULT}", "--text-muted")189
};190
}191
} catch (e) {192
new Logger("RoleColorEverywhere").error("Failed to get message color", e);193
}194
195
return null;196
},197
198
RoleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => {199
const role = GuildRoleStore.getRole(guildId, id);200
201
return (202
<span style={{203
color: role?.colorString,204
fontWeight: "unset",205
letterSpacing: ".05em"206
}}>207
{title ?? label} — {count}208
</span>209
);210
}, { noop: true })211
});212