/* * Vencord, a Discord client mod * Copyright (c) 2024 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ import { definePluginSettings, Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType, StartAt } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; import { Button, Clipboard, Forms, TextInput, Toasts, useState } from "@webpack/common"; import { darkenColorHex, generateRandomColorHex, saturateColorHex } from "./generateTheme"; import { themes } from "./themeDefinitions"; export interface ThemePreset { bgcol: string; accentcol: string; textcol: string; brand: string; name: string; } let setPreset; function LoadPreset(preset?: ThemePreset) { if (setPreset === settings.store.ColorPreset) { return; } const theme: ThemePreset = preset == null ? themes[settings.store.ColorPreset] : preset; setPreset = settings.store.ColorPreset; settings.store.Primary = theme.bgcol; settings.store.Accent = theme.accentcol; settings.store.Text = theme.textcol; settings.store.Brand = theme.brand; injectCSS(); } function mute(hex, amount) { hex = hex.replace(/^#/, ""); const bigint = parseInt(hex, 16); let r = (bigint >> 16) & 255; let g = (bigint >> 8) & 255; let b = bigint & 255; r = Math.max(r - amount, 0); g = Math.max(g - amount, 0); b = Math.max(b - amount, 0); return "#" + ((r << 16) + (g << 8) + b).toString(16).padStart(6, "0"); } function copyPreset(name: string) { const template = ` { bgcol: "${settings.store.Primary}", accentcol: "${settings.store.Accent}", textcol: "${settings.store.Text}", brand: "${settings.store.Brand}", name: "${name}" } `; if (Clipboard.SUPPORTS_COPY) { Clipboard.copy(template); } } function CopyPresetComponent() { const [inputtedName, setInputtedName] = useState(""); return ( <> {"Preset name"} ); } const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); export function generateAndApplyProceduralTheme() { const randomBackgroundColor = generateRandomColorHex(); const accentColor = darkenColorHex(randomBackgroundColor); const textColor = "ddd0d0"; const brandColor = saturateColorHex(randomBackgroundColor); settings.store.Primary = randomBackgroundColor; settings.store.Accent = accentColor; settings.store.Text = textColor; settings.store.Brand = brandColor; injectCSS(); } const settings = definePluginSettings({ serverListAnim: { type: OptionType.BOOLEAN, description: "Toggles if the server list hides when not hovered", default: false, onChange: () => injectCSS() }, memberListAnim: { type: OptionType.BOOLEAN, description: "Toggles if the member list hides when not hovered", default: true, onChange: () => injectCSS() }, privacyBlur: { type: OptionType.BOOLEAN, description: "Blurs potentially sensitive information when not tabbed in", default: false, onChange: () => injectCSS() }, tooltips: { type: OptionType.BOOLEAN, description: "If tooltips are displayed in the client", default: false, onChange: () => injectCSS() }, customFont: { type: OptionType.STRING, description: "The google fonts @import for a custom font (blank to disable)", default: "@import url('https://fonts.googleapis.com/css2?family=Poppins&wght@500&display=swap');", onChange: injectCSS }, animationSpeed: { type: OptionType.STRING, description: "The speed of animations", default: "0.2", onChange: injectCSS }, ColorPreset: { type: OptionType.SELECT, description: "Some pre-made color presets (more soon hopefully)", options: themes.map(theme => ({ label: theme.name, value: themes.indexOf(theme), default: themes.indexOf(theme) === 0 })), onChange: () => { LoadPreset(); } }, Primary: { type: OptionType.COMPONENT, description: "", default: "000000", component: () => }, Accent: { type: OptionType.COMPONENT, description: "", default: "313338", component: () => }, Text: { type: OptionType.COMPONENT, description: "", default: "ffffff", component: () => }, Brand: { type: OptionType.COMPONENT, description: "", default: "ffffff", component: () => }, pastelStatuses: { type: OptionType.BOOLEAN, description: "Changes the status colors to be more pastel (fits with the catppuccin presets)", default: true, onChange: () => injectCSS() }, DevTools: { type: OptionType.COMPONENT, description: "meow", default: "", component: () => }, ExportTheme: { type: OptionType.COMPONENT, description: "", default: "", component: () => } }); export function ColorPick({ propertyname }: { propertyname: string; }) { return (
{propertyname} { const hexColor = color.toString(16).padStart(6, "0"); settings.store[propertyname] = hexColor; injectCSS(); } } showEyeDropper={false} />
); } function copyCSS() { if (Clipboard.SUPPORTS_COPY) { Clipboard.copy(getCSS(parseFontContent())); } } function parseFontContent() { const fontRegex = /family=([^&;,:]+)/; const customFontString: string = Settings.plugins.Glide.customFont; if (customFontString == null) { return; } const fontNameMatch: RegExpExecArray | null = fontRegex.exec(customFontString); const fontName = fontNameMatch ? fontNameMatch[1].replace(/[^a-zA-Z0-9]+/g, " ") : ""; return fontName; } function injectCSS() { const fontName = parseFontContent(); const theCSS = getCSS(fontName); var elementToRemove = document.getElementById("GlideStyleInjection"); if (elementToRemove) { elementToRemove.remove(); } const styleElement = document.createElement("style"); styleElement.id = "GlideStyleInjection"; styleElement.textContent = theCSS; document.documentElement.appendChild(styleElement); } function getCSS(fontName) { return ` /* IMPORTS */ /* Fonts */ @import url('https://fonts.googleapis.com/css2?family=Nunito&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=swap'); ${Settings.plugins.Glide.customFont} /*Settings things*/ /*Server list animation*/ ${Settings.plugins.Glide.serverListAnim ? ` .guilds_a4d4d9 { width: 10px; transition: width var(--animspeed) ease 0.1s, opacity var(--animspeed) ease 0.1s; opacity: 0; } .guilds_a4d4d9:hover { width: 65px; opacity: 100; } ` : ""} /*Member list anim toggle*/ ${Settings.plugins.Glide.memberListAnim ? ` .container_cbd271 { width: 60px; opacity: 0.2; transition: width var(--animspeed) ease 0.1s, opacity var(--animspeed) ease 0.1s; } .container_cbd271:hover { width: 250px; opacity: 1; } ` : ""} /*Privacy blur*/ ${Settings.plugins.Glide.privacyBlur ? ` .header_ec86aa, .container_ee69e0, .title_a7d72e, .layout_f9647d, [aria-label="Members"] { filter: blur(0); transition: filter 0.2s ease-in-out; } body:not(:hover) .header_ec86aa, body:not(:hover) .container_ee69e0, body:not(:hover) .title_a7d72e, body:not(:hover) [aria-label="Members"], body:not(:hover) .layout_f9647d { filter: blur(5px); } ` : ""} /*Tooltips*/ [class*="tooltip"] { ${!Settings.plugins.Glide.tooltips ? "display: none !important;" : ""} } /*Root configs*/ :root { --animspeed: ${Settings.plugins.Glide.animationSpeed + "s"}; --font-primary: ${(fontName.length > 0 ? fontName : "Nunito")}; --accent: #${Settings.plugins.Glide.Accent}; --bgcol: #${Settings.plugins.Glide.Primary}; --text: #${Settings.plugins.Glide.Text}; --brand: #${Settings.plugins.Glide.Brand}; --mutedtext: ${mute(Settings.plugins.Glide.Text, 20)}; --mutedbrand: ${mute(Settings.plugins.Glide.Brand, 10)}; --mutedaccent: ${mute(Settings.plugins.Glide.Accent, 10)}; } :root { /*VARIABLES*/ /*editable variables. Feel free to mess around with these to your hearts content, i recommend not editing the logic variables unless you have an understanding of css*/ --glowcol: rgba(0, 0, 0, 0); --mentioncol: rgb(0, 0, 0); --mentionhighlightcol: rgb(0, 0, 0); --linkcol: rgb(95, 231, 255); --highlightcol: rgb(95, 231, 255); /*COLOR ASSIGNING (most of these probably effect more than whats commented)*/ /*accent based*/ /*buttons*/ --button-secondary-background: var(--accent); /*also buttons*/ --brand-experiment: var(--brand); --brand-experiment-560: var(--brand); --brand-500: var(--brand); /*message bar*/ --channeltextarea-background: var(--accent); /*selected dm background*/ --background-modifier-selected: var(--accent); /*emoji autofill*/ --primary-630: var(--accent); /*plugin grid square and nitro shop*/ --background-secondary-alt: var(--accent); /*modal background, self explanatory*/ --modal-background: var(--accent); /*color of the background of mention text*/ --mention-background: var(--accent); --input-background: var(--accent); /*the side profile thingy*/ --profile-body-background-color: var(--accent); /*the weird hover thing idk*/ --background-modifier-hover: var(--mutedaccent) !important; /*background based*/ /*primary color, self explanatory*/ --background-primary: var(--bgcol); /*dm list*/ --background-secondary: var(--bgcol); /*outer frame and search background*/ --background-tertiary: var(--bgcol); /*friends header bar*/ --bg-overlay-2: var(--bgcol); /*user panel*/ --bg-overlay-1: var(--bgcol); /*call thingy*/ --bg-overlay-app-frame: var(--bgcol); /*shop*/ --background-mentioned-hover: var(--bgcol) !important; --background-mentioned: var(--bgcol) !important; /*other*/ /*mention side line color color*/ --info-warning-foreground: var(--mentionhighlightcol); /*text color of mention text*/ --mention-foreground: white; /*Link color*/ --text-link: var(--linkcol); --header-primary: var(--text); --header-secondary: var(--text); --font-display: var(--text); --text-normal: var(--text); --text-muted: var(--mutedtext); --channels-default: var(--mutedtext); --interactive-normal: var(--text) !important; --white-500: var(--text); } /*EXTRA COLORS*/ [class*="tooltipPrimary__"] { background-color: var(--mutedaccent) !important; } [class*="tooltipPointer_"] { border-top-color: var(--mutedaccent) !important; } /*sorry, forgot to document what these are when i was adding them*/ .inspector_c3120f, .scroller_dcade6, .unicodeShortcut_dfa278 { background-color: var(--bgcol); } .inner_effbe2 { background-color: var(--accent); } /*recolor embeds*/ [class^="embedWrap"] { border-color: var(--accent) !important; background: var(--accent); } /*emoji menu recolor*/ .contentWrapper_af5dbb, .header_a3bc57 { background-color: var(--bgcol); } /*vc background recolor*/ .root_dd069c { background-color: var(--bgcol); } /*Fix the forum page*/ /*Set the bg color*/ .container_a6d69a { background-color: var(--bgcol); } /*Recolor the posts to the accent*/ .container_d331f1 { background-color: var(--accent); } /*Recolor the background of stickers in the sticker picker that dont take up the full 1:1 ratio*/ [id^="sticker-picker-grid"] { background-color: var(--bgcol); } /* profile sidebar*/ [class="none_eed6a8 scrollerBase_eed6a8"] { background-color: var(--bgcol) !important; } /*Recolor the emoji, gif, and sticker picker selected button*/ .navButtonActive_af5dbb, .stickerCategoryGenericSelected_a7a485, .categoryItemDefaultCategorySelected_dfa278 { background-color: var(--accent) !important; } /*side profile bar*/ [class="none_c49869 scrollerBase_c49869"] { background-color: var(--bgcol) !important; } .userPanelOverlayBackground_a2b6ae, .badgeList_ab525a { background-color: var(--accent) !important; border-radius: 15px !important; } /*uhhhhhhhhhhhhhhh*/ .headerText_c47fa9 { color: var(--text) !important; } /*message bar placeholder*/ .placeholder_a552a6 { color: var(--mutedtext) !important } ${settings.store.pastelStatuses ? ` /*Pastel statuses*/ rect[fill='#23a55a'], svg[fill='#23a55a'] { fill: #80c968 !important; } rect[fill='#f0b232'], svg[fill='#f0b232'] { fill: #e7ca45 !important; } rect[fill='#f23f43'], svg[fill='#f23f43'] { fill: #e0526c !important; } rect[fill='#80848e'], svg[fill='#80848e'] { fill: #696e88 !important; } rect[fill='#593695'], svg[fill='#593695'] { fill: #ac7de6 important; } ` : ""} .name_d8bfb3 { color: var(--text) !important; } .unread_d8bfb3 { background-color: var(--text) !important; } /*ROUNDING (rounding)*/ /*round message bar, some buttons, dm list button, new messages notif bar, channel buttons, emoji menu search bar, context menus, account connections(in that order)*/ .scrollableContainer_d0696b, .button_dd4f85, .interactive_f5eb4b, .newMessagesBar_cf58b5, .link_d8bfb3, .searchBar_c6ee36, .menu_d90b3d, .connectedAccountContainer_f3eb60 { border-radius: 25px; } /*round emojis seperately (and spotify activity icons)*/ [data-type="emoji"], [class*="Spotify"] { border-radius: 5px; } /*round gifs and stickers (and maybe images idk lmao), and embeds*/ [class^="imageWr"], [data-type="sticker"], [class^="embed"] { border-radius: 20px; } .item_d90b3d { border-radius: 15px; } /*slightly move messages right when hovered*/ .cozyMessage_d5deea { left: 0px; transition-duration: 0.2s; } .cozyMessage_d5deea:hover { left: 3px; } /*CONTENT (Typically changing values or hiding elements)*/ /*remove status text in user thing*/ .panelSubtextContainer_b2ca13 { display: none !important; } /*Hide most of the ugly useless scrollbars*/ ::-webkit-scrollbar { display:none; } /*Hide user profile button, the dm favourite, dm close, support, gift buttons, the now playing column, and the channel + favourite icons*/ [aria-label="Hide User Profile"], .favoriteIcon_c91bad, .closeButton_c91bad, [href="https://support.discord.com"], .nowPlayingColumn_c2739c, button[aria-label="Send a gift"], .icon_d8bfb3, .iconContainer_d8bfb3 { display :none; } /*yeet the shitty nitro and family link tabs that no one likes*/ .channel_c91bad[aria-posinset="2"], .familyCenterLinkButton_f0963d { display: none; } /*Remove the buttons at the bottom of the user pop out (seriously, who wanted this?)*/ .addFriendSection__413d3 { display: none; } /*No more useless spotify activity header*/ .headerContainer_d5089b { display: none; } /*hide sidebar connections*/ .profilePanelConnections_b433b4 { display: none; } /*pad the message bar right slightly. Not sure what caused the buttons to flow out of it, might be something in the theme :shrug:*/ .inner_d0696b { padding-right: 10px; } /*Yeet hypesquad badges (who cares)*/ [aria-label*="HypeSquad"] { display: none !important; } /*Hide icon on file uploading status*/ .icon_a4623d { display: none; } /*hide the play button while a soundmoji is playing*/ .playing_bf9443 [viewBox="0 0 24 24"] { display:none; } /*hide the public servers button on member list*/ [aria-label="Explore Discoverable Servers"] { display: none; } /*fix context menu being not symmetrical*/ .scroller_d90b3d { padding: 6px 8px !important; } /*Hide the icon that displays what platform the user is listening with on spotify status*/ .platformIcon_d5089b { display: none !important; } /*hide the album name on spotify statuses (who cares)*/ [class="state_d5089b ellipsis_d5089b textRow_d5089b"] { display: none; } /*space the connections a bit better*/ .userInfoSection_a24910 { margin-bottom: 0px; padding-bottom: 0px; } /*Space channels*/ .containerDefault_f6f816 { padding-top: 5px; } /*round banners in profile popout*/ .banner_c3e427:not(.panelBanner_c3e427) { border-radius: 20px; } /*round the user popout*/ .userPopoutOuter_c69a7b { border-radius: 25px; } /*round the inner profile popout*/ [class="userPopoutInner_c69a7b userProfileInner_c69a7b userProfileInnerThemedWithBanner_c69a7b"]::before { border-radius: 20px; } .footer_be6801 { display: none !important; } /*STYLING (Modification of content to fit the theme)*/ /*Round and scale down the users banner*/ .panelBanner_c3e427 { border-radius: 20px; transform: scale(0.95); } /*add a soft glow to message bar contents, user panel, dms, channel names (in that order)*/ .inner_d0696b .layout_f9647d, .name_d8bfb3 { filter: drop-shadow(0px 0px 3px var(--glowcol)); } [type="button"] { transition: all 0.1s ease-in-out; } [type="button"]:hover { filter: drop-shadow(0px 0px 3px var(--glowcol)); } /*Change the font*/ :root { --font-code: "Fira Code"; } /*Round all status symbols. basically does what that one plugin does but easier (disabled because of a bug) .pointerEvents_c51b4e { mask: url(#svg-mask-status-online); } */ /*pfp uploader crosshair*/ .overlayAvatar_ba5b9e { background-image: url(https://raw.githubusercontent.com/Equicord/Equicord/main/src/equicordplugins/glide/crosshair.png); background-repeat: no-repeat; background-position-x: 50%; background-position-y: 50%; border-width: 2px; } /*change highlighted text color*/ ::selection { color: inherit; background-color: transparent; text-shadow: 0px 0px 2px var(--highlightcol); } /*hide the line between connections and note*/ [class="connectedAccounts_f3eb60 userInfoSection_a24910"] { border-top: transparent !important; } .container_cebd1c:not(.checked_cebd1c) { background-color: var(--mutedbrand) !important; } .checked_cebd1c { background-color: var(--brand) !important; } `; } export default definePlugin({ name: "Glide", description: "A sleek, rounded theme for discord.", authors: [Devs.Samwich], settings, start() { injectCSS(); }, stop() { const injectedStyle = document.getElementById("GlideStyleInjection"); if (injectedStyle) { injectedStyle.remove(); } }, startAt: StartAt.DOMContentLoaded, // preview thing, kinda low effort but eh settingsAboutComponent: () => });