From 65dd4056ab2844bd9c32f8fd07036635584e4840 Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 1 May 2026 09:30:40 -0400 Subject: [PATCH] ChatBarButtonsAPI: Button Wrapper --- src/api/ChatButtons.tsx | 19 +++++++++++++ src/api/PluginManager.ts | 9 ++++-- src/equicordplugins/collapsibleUi/index.tsx | 12 ++------ src/equicordplugins/commandPalette/index.tsx | 13 ++------- src/equicordplugins/hideChatButtons/index.tsx | 28 ++++++++----------- src/plugins/_api/chatButtons.ts | 14 +++++++--- src/utils/types.ts | 7 +++-- 7 files changed, 55 insertions(+), 47 deletions(-) diff --git a/src/api/ChatButtons.tsx b/src/api/ChatButtons.tsx index 4288c6f8b..a04725386 100644 --- a/src/api/ChatButtons.tsx +++ b/src/api/ChatButtons.tsx @@ -190,3 +190,22 @@ addContextMenuPatch("textarea-context", (children, args) => { ); }); + +// Wrapper stuff is down here to avoid conflicts with above +export type ChatBarButtonWrapper = (buttons: ReactNode) => ReactNode; + +/** + * Registry for plugins that need to wrap the entire chat bar button container + */ +export const ChatBarButtonWrappers = new Map(); + +export const addChatBarButtonWrapper = (id: string, wrapper: ChatBarButtonWrapper) => ChatBarButtonWrappers.set(id, wrapper); +export const removeChatBarButtonWrapper = (id: string) => ChatBarButtonWrappers.delete(id); + +export function _wrapButtons(buttons: ReactNode) { + let wrapped = buttons; + for (const wrapper of ChatBarButtonWrappers.values()) { + wrapped = wrapper(wrapped); + } + return wrapped; +} diff --git a/src/api/PluginManager.ts b/src/api/PluginManager.ts index e473cf0bb..3f968ca5b 100644 --- a/src/api/PluginManager.ts +++ b/src/api/PluginManager.ts @@ -17,7 +17,7 @@ */ import { addProfileBadge, removeProfileBadge } from "@api/Badges"; -import { addChatBarButton, removeChatBarButton } from "@api/ChatButtons"; +import { addChatBarButton, addChatBarButtonWrapper, removeChatBarButton, removeChatBarButtonWrapper } from "@api/ChatButtons"; import { registerCommand, unregisterCommand } from "@api/Commands"; import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu"; import { addMemberListDecorator, removeMemberListDecorator } from "@api/MemberListDecorators"; @@ -201,7 +201,7 @@ export const startPlugin = traceFunction("startPlugin", function startPlugin(p: onBeforeMessageEdit, onBeforeMessageSend, onMessageClick, chatBarButton, renderMemberListDecorator, renderMessageAccessory, renderMessageDecoration, messagePopoverButton, // Custom - renderNicknameIcon, headerBarButton, audioProcessor, userAreaButton, renderProfileCollection + renderNicknameIcon, headerBarButton, audioProcessor, userAreaButton, renderProfileCollection, chatBarButtonWrapper } = p; if (p.start) { @@ -269,6 +269,7 @@ export const startPlugin = traceFunction("startPlugin", function startPlugin(p: if (audioProcessor) addAudioProcessor(name, audioProcessor); if (userAreaButton) addUserAreaButton(name, userAreaButton.render, userAreaButton.priority); if (renderProfileCollection) addProfileCollection(name, renderProfileCollection); + if (chatBarButtonWrapper) addChatBarButtonWrapper(name, chatBarButtonWrapper); return true; }, p => `startPlugin ${p.name}`); @@ -279,7 +280,7 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu onBeforeMessageEdit, onBeforeMessageSend, onMessageClick, chatBarButton, renderMemberListDecorator, renderMessageAccessory, renderMessageDecoration, messagePopoverButton, // Custom - renderNicknameIcon, headerBarButton, audioProcessor, userAreaButton, renderProfileCollection + renderNicknameIcon, headerBarButton, audioProcessor, userAreaButton, renderProfileCollection, chatBarButtonWrapper } = p; if (p.stop) { @@ -345,6 +346,7 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu if (audioProcessor) removeAudioProcessor(name); if (userAreaButton) removeUserAreaButton(name); if (renderProfileCollection) removeProfileCollection(name); + if (chatBarButtonWrapper) removeChatBarButtonWrapper(name); return true; }, p => `stopPlugin ${p.name}`); @@ -400,6 +402,7 @@ export const initPluginManager = onlyOnce(function init() { if (p.audioProcessor) neededApiPlugins.add("AudioPlayerAPI"); if (p.userAreaButton) neededApiPlugins.add("UserAreaAPI"); if (p.renderProfileCollection) neededApiPlugins.add("ProfileCollectionsAPI"); + if (p.chatBarButtonWrapper) neededApiPlugins.add("ChatInputButtonAPI"); for (const key of pluginKeysToBind) { p[key] &&= p[key].bind(p) as any; diff --git a/src/equicordplugins/collapsibleUi/index.tsx b/src/equicordplugins/collapsibleUi/index.tsx index f5793fdd4..7893bcb3e 100644 --- a/src/equicordplugins/collapsibleUi/index.tsx +++ b/src/equicordplugins/collapsibleUi/index.tsx @@ -109,7 +109,7 @@ export default definePlugin({ name: "CollapsibleUI", description: "Native collapsible channel, member, chat button, and user area surfaces.", tags: ["Appearance", "Customisation", "Chat", "Servers"], - dependencies: ["HeaderBarAPI"], + dependencies: ["HeaderBarAPI", "ChatInputButtonAPI"], authors: [EquicordDevs.benjii], searchTerms: ["ui", "sidebar", "layout"], managedStyle, @@ -130,16 +130,8 @@ export default definePlugin({ replace: "className:$self.userAreaControlsClass($1)," } }, - { - find: '"sticker")', - replacement: { - match: /(?<="div",\{className:\i\.\i,children:)(\i)\}/, - replace: "$self.wrapChatButtons($1)}" - } - } ], - - wrapChatButtons(buttons: ReactNode[]) { + chatBarButtonWrapper: (buttons: ReactNode) => { if (!Array.isArray(buttons) || buttons.length === 0) return buttons; return ; }, diff --git a/src/equicordplugins/commandPalette/index.tsx b/src/equicordplugins/commandPalette/index.tsx index 41158ece3..df917d822 100644 --- a/src/equicordplugins/commandPalette/index.tsx +++ b/src/equicordplugins/commandPalette/index.tsx @@ -203,17 +203,8 @@ export default definePlugin({ description: "Quickly run actions through a searchable command palette", tags: ["Appearance", "Customisation", "Commands", "Shortcuts"], authors: [EquicordDevs.justjxke], + dependencies: ["ChatInputButtonAPI"], settings, - patches: [ - { - find: '"sticker")', - replacement: { - match: /(?<="div",\{className:\i\.\i,children:)(\i)\}/, - replace: "$self.wrapChatBarChildren($2)}" - } - } - ], - start() { registerBuiltInCommands(); window.addEventListener("keydown", this.handleKeydown); @@ -238,5 +229,5 @@ export default definePlugin({ }); }, - wrapChatBarChildren, + chatBarButtonWrapper: wrapChatBarChildren, }); diff --git a/src/equicordplugins/hideChatButtons/index.tsx b/src/equicordplugins/hideChatButtons/index.tsx index 60fa35c62..62a578cad 100644 --- a/src/equicordplugins/hideChatButtons/index.tsx +++ b/src/equicordplugins/hideChatButtons/index.tsx @@ -5,7 +5,7 @@ */ import { ChatBarButton } from "@api/ChatButtons"; -import { definePluginSettings } from "@api/Settings"; +import { definePluginSettings, migratePluginSetting } from "@api/Settings"; import { EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType, StartAt } from "@utils/types"; import { useEffect, useState } from "@webpack/common"; @@ -14,12 +14,12 @@ import type { MouseEventHandler, ReactNode } from "react"; let hidechatbuttonsopen: boolean | undefined; const settings = definePluginSettings({ - Color: { + color: { type: OptionType.BOOLEAN, description: "Color it red on open", // something extra default: false, }, - Open: { + open: { type: OptionType.BOOLEAN, description: "opened by default", default: false, @@ -35,7 +35,7 @@ function HideToggleButton(props: { open: boolean | undefined, onClick: MouseEven tooltip={props.open ? "Close" : "Open"} > { return ; }, start() { - hidechatbuttonsopen = settings.store.Open; + hidechatbuttonsopen = settings.store.open; }, stop() { hidechatbuttonsopen = undefined; - } + }, }); diff --git a/src/plugins/_api/chatButtons.ts b/src/plugins/_api/chatButtons.ts index c004bdc19..3dc5ecc98 100644 --- a/src/plugins/_api/chatButtons.ts +++ b/src/plugins/_api/chatButtons.ts @@ -15,10 +15,16 @@ export default definePlugin({ patches: [ { find: '"sticker")', - replacement: { - match: /0===(\i)\.length(?=.{0,25}?\(0,\i\.jsxs?\)\(.{0,75}?children:\1)/, - replace: "(Vencord.Api.ChatButtons._injectButtons($1,arguments[0]),$&)" - } + replacement: [ + { + match: /0===(\i)\.length(?=.{0,25}?\(0,\i\.jsxs?\)\(.{0,75}?children:\1)/, + replace: "(Vencord.Api.ChatButtons._injectButtons($1,arguments[0]),$&)" + }, + { + match: /(?<="submit"\)\).{0,100}"div",\{className:\i\.\i,children:)(\i)\}/, + replace: "Vencord.Api.ChatButtons._wrapButtons($1)}" + } + ] } ] }); diff --git a/src/utils/types.ts b/src/utils/types.ts index c33114a5c..cb6d27963 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -18,7 +18,7 @@ import { AudioProcessor } from "@api/AudioPlayer"; import type { ProfileBadge } from "@api/Badges"; -import type { ChatBarButtonData } from "@api/ChatButtons"; +import type { ChatBarButtonData, ChatBarButtonWrapper } from "@api/ChatButtons"; import type { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { HeaderBarButtonData } from "@api/HeaderBar"; import type { MemberListDecoratorFactory } from "@api/MemberListDecorators"; @@ -231,12 +231,15 @@ export interface PluginDef { renderMemberListDecorator?: MemberListDecoratorFactory; - // Custom + /* + * Custom apis added by Equicord and were placed here for quicker identification rather then mixing them in + */ renderNicknameIcon?: NicknameIconFactory; headerBarButton?: HeaderBarButtonData; audioProcessor?: AudioProcessor; userAreaButton?: UserAreaButtonData; renderProfileCollection?: ProfileCollectionFactory; + chatBarButtonWrapper?: ChatBarButtonWrapper; /** * A Vencord plugin that is modified for extra features in Equicord