From 2370e902753117a1a2cb18bbc36262eabf4e10ba Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Thu, 16 Oct 2025 17:27:52 -0500 Subject: [PATCH] gradient role colors --- src/webpage/jsontypes.ts | 5 ++ src/webpage/member.ts | 3 +- src/webpage/role.ts | 107 ++++++++++++++++++++++++++++++++++++--- src/webpage/settings.ts | 2 +- src/webpage/style.css | 38 +++++++++++--- src/webpage/user.ts | 5 +- translations/en.json | 10 +++- 7 files changed, 152 insertions(+), 18 deletions(-) diff --git a/src/webpage/jsontypes.ts b/src/webpage/jsontypes.ts index 65d1985..4d68c3b 100644 --- a/src/webpage/jsontypes.ts +++ b/src/webpage/jsontypes.ts @@ -491,6 +491,11 @@ type rolesjson = { icon: string; unicode_emoji: string; flags: number; + colors?: { + primary_color: number; + secondary_color?: number | null; + tertiary_color?: number | null; + }; }; type dirrectjson = { id: string; diff --git a/src/webpage/member.ts b/src/webpage/member.ts index f8c8c50..ed53185 100644 --- a/src/webpage/member.ts +++ b/src/webpage/member.ts @@ -493,7 +493,8 @@ class Member extends SnowFlake { } */ - html.style.color = `var(--role-${this.getTopColor()})`; + const id = this.getTopColor(); + if (id) html.style.setProperty("--userbg", `var(--role-${id})`); } //this.profileclick(html); diff --git a/src/webpage/role.ts b/src/webpage/role.ts index a647eb8..675b970 100644 --- a/src/webpage/role.ts +++ b/src/webpage/role.ts @@ -4,7 +4,7 @@ import {Guild} from "./guild.js"; import {SnowFlake} from "./snowflake.js"; import {rolesjson} from "./jsontypes.js"; import {Search} from "./search.js"; -import {OptionsElement, Buttons, Dialog} from "./settings.js"; +import {OptionsElement, Buttons, Dialog, ColorInput} from "./settings.js"; import {Contextmenu} from "./contextmenu.js"; import {Channel} from "./channel.js"; import {I18n} from "./i18n.js"; @@ -21,6 +21,11 @@ class Role extends SnowFlake { unicode_emoji!: string; position!: number; headers: Guild["headers"]; + colors?: { + primary_color: number; + secondary_color?: number | null; + tertiary_color?: number | null; + }; constructor(json: rolesjson, owner: Guild) { super(json.id); this.headers = owner.headers; @@ -31,7 +36,7 @@ class Role extends SnowFlake { } (this as any)[thing] = (json as any)[thing]; } - document.body.style.setProperty(`--role-${this.id}`, this.getColor()); + this.roleLoad(); this.permissions = new Permissions(json.permissions); this.owner = owner; } @@ -51,6 +56,27 @@ class Role extends SnowFlake { return span; } } + getColorStyle(short = false) { + const [len1, len2, len3] = short + ? (["", "", ""] as const) + : (["30px", "60px", "90px"] as const); + if (this.colors) { + const prim = this.getColor(); + if (this.colors.secondary_color) { + const second = Role.numberToColor(this.colors.secondary_color); + if (this.colors.tertiary_color) { + const third = Role.numberToColor(this.colors.tertiary_color); + return `repeating-linear-gradient(90deg, ${prim}, ${second} ${len1}, ${third} ${len2}, ${prim} ${len3})`; + } + return `repeating-linear-gradient(90deg, ${prim} , ${second} ${len1}, ${prim} ${len2})`; + } + } + return `linear-gradient(90deg, ${this.getColor()})`; + } + roleLoad() { + const style = this.getColorStyle(); + document.documentElement.style.setProperty(`--role-${this.id}`, style); + } newJson(json: rolesjson) { for (const thing of Object.keys(json)) { if (thing === "id" || thing === "permissions") { @@ -58,7 +84,7 @@ class Role extends SnowFlake { } (this as any)[thing] = (json as any)[thing]; } - document.body.style.setProperty(`--role-${this.id}`, this.getColor()); + this.roleLoad(); this.permissions.allow = BigInt(json.permissions); } compare(str: string) { @@ -80,11 +106,14 @@ class Role extends SnowFlake { get localuser(): Localuser { return this.guild.localuser; } + static numberToColor(numb: number) { + return `#${numb.toString(16).padStart(6, "0")}`; + } getColor(): string | null { if (this.color === 0) { return null; } - return `#${this.color.toString(16).padStart(6, "0")}`; + return Role.numberToColor(this.color); } canManage() { if (this.guild.member.hasPermission("MANAGE_ROLES")) { @@ -250,8 +279,9 @@ class RoleList extends Buttons { } makeguildmenus(option: Options) { option.addButtonInput("", I18n.role.displaySettings(), () => { - const role = this.guild.roleids.get(this.curid as string); - if (!role) return; + const r = this.guild.roleids.get(this.curid as string); + if (!r) return; + const role = r; const form = option.addSubForm( I18n.role.displaySettings(), (e) => { @@ -277,10 +307,70 @@ class RoleList extends Buttons { form.addCheckboxInput(I18n.role.mentionable(), "mentionable", { initState: role.mentionable, }); + let count: number; + if (role.colors) { + count = role.colors.secondary_color ? (role.colors.tertiary_color ? 3 : 2) : 1; + } else { + count = 1; + } + + form.options + .addSelect( + I18n.role.colors.name(), + () => {}, + (["one", "two", "three"] as const).map((_) => I18n.role.colors[_]()), + { + defaultIndex: count - 1, + }, + ) + .watchForChange((_) => { + count = _ + 1; + colorOptGen(); + }); + const color = "#" + role.color.toString(16).padStart(6, "0"); const colorI = form.addColorInput(I18n.role.color(), "color", { initColor: color, }); + + const opt = form.addOptions(""); + + const c2 = + role.colors?.secondary_color !== undefined && role.colors?.secondary_color !== null + ? Role.numberToColor(role.colors.secondary_color) + : color; + const c3 = + role.colors?.tertiary_color !== undefined && role.colors?.tertiary_color !== null + ? Role.numberToColor(role.colors.tertiary_color) + : color; + + const colors = [color, c2, c3]; + + let colorInputs: ColorInput[] = [colorI]; + function colorOptGen() { + colorInputs = [colorInputs[0]]; + opt.removeAll(); + + if (count >= 2) { + colorInputs[1] = opt.addColorInput(I18n.role.colors.secondColor(), () => {}, { + initColor: colors[1], + }); + colorInputs[1].watchForChange((_) => { + colors[1] = _; + }); + } + if (count === 3) { + colorInputs[2] = opt.addColorInput(I18n.role.colors.thirdColor(), () => {}, { + initColor: colors[2], + }); + + colorInputs[2].watchForChange((_) => { + colors[2] = _; + }); + } + } + colorOptGen(); + form.addEmojiInput(I18n.role.roleEmoji(), "unicode_emoji", this.guild.localuser, { initEmoji: role.unicode_emoji ? new Emoji( @@ -304,6 +394,11 @@ class RoleList extends Buttons { form.addPreprocessor((obj: any) => { obj.color = Number("0x" + colorI.colorContent.substring(1)); obj.permissions = this.permission.allow.toString(); + obj.colors = { + primary_color: obj.color, + secondary_color: colorInputs[1] ? Number("0x" + colors[1].substring(1)) : undefined, + tertiary_color: colorInputs[2] ? Number("0x" + colors[2].substring(1)) : undefined, + }; console.log(obj.color); }); diff --git a/src/webpage/settings.ts b/src/webpage/settings.ts index 9ab058b..990fa89 100644 --- a/src/webpage/settings.ts +++ b/src/webpage/settings.ts @@ -364,7 +364,7 @@ class ButtonInput implements OptionsElement { submit() {} } -class ColorInput implements OptionsElement { +export class ColorInput implements OptionsElement { readonly label: string; readonly owner: Options; readonly onSubmit: (str: string) => void; diff --git a/src/webpage/style.css b/src/webpage/style.css index d5b9296..1552080 100644 --- a/src/webpage/style.css +++ b/src/webpage/style.css @@ -1584,6 +1584,10 @@ span.instanceStatus { font-size: 0.9rem; margin-left: 4px; font-weight: 600; + color: transparent; + background-clip: text, border-box; + position: relative; + background-image: var(--userbg, linear-gradient(var(--primary-text))); } } .liststyle .statusDiv { @@ -2088,6 +2092,10 @@ span.instanceStatus { font-weight: bold; word-break: break-all; cursor: pointer; + color: transparent; + background-clip: text, border-box; + position: relative; + background-image: var(--userbg, linear-gradient(var(--primary-text))); } .roleIcon { display: inline-block; @@ -2108,20 +2116,28 @@ span.instanceStatus { .pinText:hover { text-decoration: underline; } -.username:hover { +.username:hover:after { text-decoration: underline; + position: absolute; + content: ""; + width: 100%; + height: 1px; + background-image: inherit; + left: 0; + bottom: 1px; } .bot { padding: 2px 5px; margin-left: 4px; vertical-align: 1px; - background: color-mix(in srgb, var(--accent-color) 75%, transparent); + background: color-mix(in srgb, var(--accent-color) 75%, transparent) !important; font-size: 0.75em !important; font-weight: bold; - color: var(--primary-text-prominent); + color: var(--primary-text-prominent) !important; border-radius: 4px; flex-grow: 0; width: fit-content; + background-clip: border-box !important; } .membererror { display: inline-block; @@ -2264,15 +2280,23 @@ span .quote:last-of-type .quoteline { background: color-mix(in srgb, var(--mention-highlight) 10%, var(--primary-hover)); } .mentionMD { - background: color-mix(in srgb, var(--mention), transparent); + color: transparent; + background-clip: text; padding: 1px 2px; border-radius: 4px; cursor: pointer; + background-image: var(--userbg, linear-gradient(var(--primary-text))); + position: relative; } -.mentionMD::after { - /* content: "​"; */ +.mentionMD::before { + background: color-mix(in srgb, var(--mention), transparent); + content: ""; + position: absolute; + width: 100%; + height: 100%; + left: 0px; } -.mentionMD:hover { +.mentionMD:hover::before { background: var(--mention); } .reactiondiv { diff --git a/src/webpage/user.ts b/src/webpage/user.ts index b592649..da7d447 100644 --- a/src/webpage/user.ts +++ b/src/webpage/user.ts @@ -912,7 +912,8 @@ class User extends SnowFlake { roleDiv.classList.add("rolediv"); const color = document.createElement("div"); roleDiv.append(color); - color.style.setProperty("--role-color", `#${role.color.toString(16).padStart(6, "0")}`); + + color.style.setProperty("--role-color", role.getColorStyle(true)); color.classList.add("colorrolediv"); const span = document.createElement("span"); roleDiv.append(span); @@ -1158,7 +1159,7 @@ class User extends SnowFlake { roleDiv.classList.add("rolediv"); const color = document.createElement("div"); roleDiv.append(color); - color.style.setProperty("--role-color", `#${role.color.toString(16).padStart(6, "0")}`); + color.style.setProperty("--role-color", role.getColorStyle(true)); color.classList.add("colorrolediv"); const span = document.createElement("span"); roleDiv.append(span); diff --git a/translations/en.json b/translations/en.json index 31070c2..e77beb6 100644 --- a/translations/en.json +++ b/translations/en.json @@ -404,7 +404,15 @@ "confirmDelete": "Are you sure you want to delete $1?", "roleEmoji": "Role emoji:", "roleFileIcon": "Role Icon:", - "deleting":"Deleting role..." + "deleting":"Deleting role...", + "colors":{ + "name":"Color count:", + "one":"One", + "two":"Two", + "three":"Three", + "secondColor":"Second Color:", + "thirdColor":"Third Color:" + } }, "folder":{ "name":"Folder name:",