ChatBarButtonsAPI: Button Wrapper

This commit is contained in:
thororen1234 2026-05-01 09:30:40 -04:00
parent b4d231558b
commit 65dd4056ab
No known key found for this signature in database
7 changed files with 55 additions and 47 deletions

View file

@ -190,3 +190,22 @@ addContextMenuPatch("textarea-context", (children, args) => {
</Menu.MenuItem>
);
});
// 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<string, ChatBarButtonWrapper>();
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;
}

View file

@ -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;

View file

@ -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 <ChatButtonsRow buttons={buttons} />;
},

View file

@ -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,
});

View file

@ -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"}
>
<svg
fill={settings.store.Color && props.open ? "#c32a32" : "currentColor"}
fill={settings.store.color && props.open ? "#c32a32" : "currentColor"}
fillRule="evenodd"
width="20"
height="20"
@ -78,29 +78,23 @@ function ButtonsInnerComponent({ buttons }: { buttons: ReactNode; }) {
);
}
migratePluginSetting("HideChatButtons", "open", "Open");
migratePluginSetting("HideChatButtons", "color", "Color");
export default definePlugin({
name: "HideChatButtons",
description: "Able to hide the chat buttons",
tags: ["Chat", "Utility"],
settings: settings,
authors: [EquicordDevs.iamme],
patches: [
{
find: '"sticker")',
replacement: {
match: /(?<="div",\{className:\i\.\i,children:)(\i)\}/,
replace: "$self.buttonsInner($1)}"
}
}
],
dependencies: ["ChatInputButtonAPI"],
settings: settings,
startAt: StartAt.Init,
buttonsInner(buttons: ReactNode) {
chatBarButtonWrapper: (buttons: ReactNode) => {
return <ButtonsInnerComponent buttons={buttons} />;
},
start() {
hidechatbuttonsopen = settings.store.Open;
hidechatbuttonsopen = settings.store.open;
},
stop() {
hidechatbuttonsopen = undefined;
}
},
});

View file

@ -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)}"
}
]
}
]
});

View file

@ -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