fix various broken things (#4141)

Co-authored-by: Vendicated <vendicated@riseup.net>
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
sadan4 2026-04-23 23:49:49 -04:00 • committed by GitHub
parent b8dc0ae720
commit 7dca07569d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 217 additions and 138 deletions

View file

@ -55,6 +55,9 @@ export function isPluginEnabled(p: string) {
}
export function addPatch(newPatch: Omit<Patch, "plugin">, pluginName: string, pluginPath = `Vencord.Plugins.plugins[${JSON.stringify(pluginName)}]`) {
// TODO: this causes crashes
if (pluginName === "Vesktop" && newPatch.find === ".STREAMING_AUTO_STREAMER_MODE,") return;
const patch = newPatch as Patch;
patch.plugin = pluginName;

View file

@ -54,7 +54,10 @@ export async function loadLazyChunks() {
async function searchAndLoadLazyChunks(factoryCode: string) {
// Workaround to avoid loading the CSS debugging chunk which turns the app pink
const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&"));
// const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&"));
// Disabled for now since this causes lots of chunks concatenated into the same module get marked as invalid, and thus not loaded.
const hasCssDebuggingLoad = foundCssDebuggingLoad = false;
const lazyChunks = factoryCode.matchAll(hasCssDebuggingLoad ? CompleteLazyChunkRegex : PartialLazyChunkRegex);
const validChunkGroups = new Set<[chunkIds: PropertyKey[], entryPoint: PropertyKey]>();

View file

@ -37,6 +37,13 @@ async function runReporter() {
await loadLazyChunksDone;
// Manually require all modules to make sure all lazily required modules are patched
for (const moduleId of Object.keys(Webpack.wreq.m)) {
try {
Webpack.wreq(moduleId);
} catch { }
}
if (IS_REPORTER && IS_WEB && !IS_VESKTOP) {
console.log("[REPORTER_META]", {
buildNumber: getBuildNumber(),
@ -51,7 +58,7 @@ async function runReporter() {
}
for (const [plugin, moduleId, match, totalTime] of patchTimings) {
if (totalTime > 5) {
if (totalTime > 10) {
new Logger("WebpackPatcher").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`);
}
}

View file

@ -0,0 +1,37 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2026 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { setColorPicker, setCreateScroller } from "@webpack/common";
export default definePlugin({
name: "ConcatenatedComponentExtractor",
description: "Extract components that have been concatenated by the bundler",
authors: [Devs.sadan],
required: true,
patches: [
{
find: "#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}),focusProps:",
replacement: {
match: /(?=function (\i)\(\i\)\{let\{onChange:\i,onClose:\i,[^}]+?showEyeDropper:)/,
replace: "$self.setColorPicker($1);"
}
},
{
find: /="ltr",orientation:\i="vertical"[^}]+?customTheme:/,
replacement: {
match: /(?=function (\i)\(\i,\i,\i\)\{.{0,20}?return \i\.forwardRef\(function\(\i,\i\)\{let\{[^}]+?="ltr",orientation:)/,
replace: "$self.setCreateScroller($1);"
}
}
],
setCreateScroller,
setColorPicker,
});

View file

@ -197,7 +197,7 @@ export default definePlugin({
{
// Modify the expanded prop to use the boolean if the above patch fails, or check if the folder is expanded from the list if it succeeds
// Also export the list of expanded folders to the child folder component if the patch above succeeds, else export undefined
match: /(?<=folderNode:(\i),expanded:)\i(?=,)/,
match: /(?<=\(0,\i\.jsxs?\)\(\i,\{folderNode:(\i),expanded:)\i(?=,)/,
replace: (isExpandedOrExpandedIds, folderNote) => ""
+ `typeof ${isExpandedOrExpandedIds}==="boolean"?${isExpandedOrExpandedIds}:${isExpandedOrExpandedIds}.has(${folderNote}.id),`
+ `betterFoldersExpandedIds:${isExpandedOrExpandedIds} instanceof Set?${isExpandedOrExpandedIds}:void 0`

View file

@ -155,7 +155,7 @@ export default definePlugin({
predicate: () => settings.store.organizeMenu,
replacement: [
{
match: /children:\[(\i),(?<=\1=.{0,30}\.openUserSettings.+?)/,
match: /children:\[(\i),(?<=\1=(?:function|.{0,30}\.openUserSettings).+?)/, // TODO .{0,30}\.openUserSettings is stable compat
replace: "children:[$self.transformSettingsEntries($1),",
},
]

View file

@ -50,8 +50,8 @@ export default definePlugin({
{
find: "#{intl::PREMIUM_MESSAGE_LENGTH_UPSELL_TOOLTIP}",
replacement: {
match: /return \i\?\i\(\):\i\(\)(?<=#{intl::PREMIUM_MESSAGE_LENGTH_UPSELL_TOOLTIP_WITHOUT_LINK}.{0,200}?)/,
replace: "return null"
match: /(?<=\.PREMIUM_UPSELL\);)(?=.{0,50}\.PREMIUM_UPSELL_VIEWED)/,
replace: "return null;"
}
}
],

View file

@ -1,78 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { Devs, IS_MAC } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
export default definePlugin({
name: "CtrlEnterSend",
authors: [Devs.UlyssesZhan],
description: "Use Ctrl+Enter to send messages (customizable)",
tags: ["Shortcuts", "Chat"],
settings: definePluginSettings({
submitRule: {
description: "The way to send a message",
type: OptionType.SELECT,
options: [
{
label: "Ctrl+Enter (Enter or Shift+Enter for new line) (cmd+enter on macOS)",
value: "ctrl+enter"
},
{
label: "Shift+Enter (Enter for new line)",
value: "shift+enter"
},
{
label: "Enter (Shift+Enter for new line; Discord default)",
value: "enter"
}
],
default: "ctrl+enter"
},
sendMessageInTheMiddleOfACodeBlock: {
description: "Whether to send a message in the middle of a code block",
type: OptionType.BOOLEAN,
default: true,
}
}),
patches: [
// Only one of the two patches will be at effect; Discord often updates to switch between them.
// See: https://discord.com/channels/1015060230222131221/1032770730703716362/1261398512017477673
{
find: ".selectPreviousCommandOption(",
replacement: {
match: /(?<=(\i)\.key!==\i\.\i.ENTER\|\|).{0,100}(\(0,\i\.\i\)\(\i\)).{0,100}(?=\|\|\(\i\.preventDefault)/,
replace: "!$self.shouldSubmit($1,$2)"
}
},
{
find: "!this.hasOpenCodeBlock()",
replacement: {
match: /!(\i).shiftKey&&!(this.hasOpenCodeBlock\(\))&&\(.{0,100}?\)/,
replace: "$self.shouldSubmit($1, $2)"
}
}
],
shouldSubmit(event: KeyboardEvent, codeblock: boolean): boolean {
let result = false;
switch (this.settings.store.submitRule) {
case "shift+enter":
result = event.shiftKey;
break;
case "ctrl+enter":
result = IS_MAC ? event.metaKey : event.ctrlKey;
break;
case "enter":
result = !event.shiftKey && !event.ctrlKey;
break;
}
if (!this.settings.store.sendMessageInTheMiddleOfACodeBlock) {
result &&= !codeblock;
}
return result;
}
});

View file

@ -16,7 +16,7 @@ import { useAuthorizationStore } from "./lib/stores/AuthorizationStore";
import { useCurrentUserDecorationsStore } from "./lib/stores/CurrentUserDecorationsStore";
import { useUserDecorAvatarDecoration, useUsersDecorationsStore } from "./lib/stores/UsersDecorationsStore";
import { settings } from "./settings";
import { setDecorationGridDecoration, setDecorationGridItem } from "./ui/components";
import { setAvatarDecorationModalPreview, setDecorationGridDecoration, setDecorationGridItem } from "./ui/components";
import DecorSection from "./ui/components/DecorSection";
export interface AvatarDecoration {
@ -60,7 +60,7 @@ export default definePlugin({
},
// Remove NEW label from decor avatar decorations
{
match: /(?<=\.\i\.PURCHASE)(?=,)(?<=avatarDecoration:(\i).+?)/,
match: /(?<=\i\.PURCHASE)(?=,)(?<=avatarDecoration:(\i).+?)/,
replace: "||$1.skuId===$self.SKU_ID"
}
]
@ -111,10 +111,16 @@ export default definePlugin({
// Patch avatar decoration preview to display Decor avatar decorations as if they are purchased
{
find: "#{intl::PREMIUM_UPSELL_PROFILE_AVATAR_DECO_INLINE_UPSELL_DESCRIPTION}",
replacement: {
match: /(#{intl::PREMIUM_UPSELL_PROFILE_AVATAR_DECO_INLINE_UPSELL_DESCRIPTION}.+?return null!=(\i)&&\()(null==\i)/,
replace: (_, rest, avatarDecoration, hasPurchase) => `${rest}(${avatarDecoration}.skuId!==$self.SKU_ID&&${avatarDecoration}.skuId!==$self.RAW_SKU_ID&&${hasPurchase})`
}
replacement: [
{
match: /(#{intl::PREMIUM_UPSELL_PROFILE_AVATAR_DECO_INLINE_UPSELL_DESCRIPTION}.+?return null!=(\i)&&\()(null==\i)/,
replace: (_, rest, avatarDecoration, hasPurchase) => `${rest}(${avatarDecoration}.skuId!==$self.SKU_ID&&${avatarDecoration}.skuId!==$self.RAW_SKU_ID&&${hasPurchase})`
},
{
match: /(?<==)\i=>{let{user:\i,guildId:\i,avatarDecoration:/,
replace: "$self.AvatarDecorationModalPreview=$&"
}
]
}
],
settings,
@ -138,6 +144,10 @@ export default definePlugin({
setDecorationGridDecoration(e);
},
set AvatarDecorationModalPreview(e: any) {
setAvatarDecorationModalPreview(e);
},
SKU_ID,
RAW_SKU_ID,

View file

@ -5,7 +5,6 @@
*/
import { AvatarDecoration } from "@plugins/decor";
import { findComponentByCodeLazy } from "@webpack";
import type { ComponentType, HTMLProps, PropsWithChildren } from "react";
type DecorationGridItemComponent = ComponentType<PropsWithChildren<HTMLProps<HTMLDivElement>> & {
@ -16,7 +15,8 @@ type DecorationGridItemComponent = ComponentType<PropsWithChildren<HTMLProps<HTM
export let DecorationGridItem: DecorationGridItemComponent;
export const setDecorationGridItem = v => DecorationGridItem = v;
export const AvatarDecorationModalPreview = findComponentByCodeLazy("#{intl::PREMIUM_UPSELL_PROFILE_AVATAR_DECO_INLINE_UPSELL_DESCRIPTION}");
export let AvatarDecorationModalPreview: ComponentType<any> = () => null;
export const setAvatarDecorationModalPreview = v => AvatarDecorationModalPreview = v;
type DecorationGridDecorationComponent = React.ComponentType<HTMLProps<HTMLDivElement> & {
avatarDecoration: AvatarDecoration;

View file

@ -24,13 +24,13 @@ import { Paragraph } from "@components/Paragraph";
import { Devs, IS_MAC } from "@utils/constants";
import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findLazy } from "@webpack";
import { findByPropsLazy } from "@webpack";
import { Forms, React } from "@webpack/common";
import hideBugReport from "./hideBugReport.css?managed";
const KbdStyles = findByPropsLazy("key", "combo");
const BugReporterExperiment = findLazy(m => m?.definition?.name === "2026-01-bug-reporter");
let BugReporterExperiment: any;
const modKey = IS_MAC ? "cmd" : "ctrl";
const altKey = IS_MAC ? "opt" : "alt";
@ -127,9 +127,20 @@ export default definePlugin({
match: /}getServerAssignment\((\i),\i,\i\){/,
replace: "$&if($1==null)return;"
}
},
{
find: "2026-01-bug-reporter",
replacement: {
match: /(?<==)(?=\(0,\i\(.+?\)\.\i\)\({name:"2026-01-bug-reporter")/,
replace: "$self.BugReporterExperiment="
}
}
],
set BugReporterExperiment(value: any) {
BugReporterExperiment = value;
},
start: () => !BugReporterExperiment.getConfig().hasBugReporterAccess && enableStyle(hideBugReport),
stop: () => disableStyle(hideBugReport),

View file

@ -27,7 +27,7 @@ export default definePlugin({
patches: [
// DM User Sidebar
{
find: ".SIDEBAR}),nicknameIcons",
find: "#{intl::PREMIUM_GIFTING_BUTTON}),action:",
replacement: {
match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id)}\)}\)/,
replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:true})"

View file

@ -20,7 +20,6 @@ import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { Channel, Message } from "@vencord/discord-types";
import { findLazy } from "@webpack";
import { ContextMenuApi, FluxDispatcher, Menu, MessageActions } from "@webpack/common";
enum GreetMode {
@ -42,7 +41,7 @@ const settings = definePluginSettings({
unholyMultiGreetEnabled?: boolean;
}>();
const WELCOME_STICKERS = findLazy(m => Array.isArray(m) && m[0]?.name === "Wave");
let WELCOME_STICKERS: any;
function greet(channel: Channel, message: Message, stickers: string[]) {
const options = MessageActions.getSendMessageOptionsForReply({
@ -165,9 +164,20 @@ export default definePlugin({
match: /className:\i\.\i,(?=.{0,40}?"sticker")(?<={channel:\i,message:\i}=(\i).+?)/,
replace: "$&onContextMenu:(vcEvent)=>$self.pickSticker(vcEvent, $1),"
}
},
{
find: '"Wumpus waves hello"',
replacement: {
match: /(?<==)(?=\[{id:"749054660769218631",format_type:\d,description:"Wumpus waves hello")/,
replace: "$self.WELCOME_STICKERS="
}
}
],
set WELCOME_STICKERS(value: any) {
WELCOME_STICKERS = value;
},
pickSticker(
event: React.UIEvent,
props: {

View file

@ -171,7 +171,7 @@ export default definePlugin({
replace: `id:"${ELEMENT_ID}",$&`
},
{
match: /(?<=null!=(\i)\?.{0,20})\i\.\i,{children:\1/,
match: /(?<=null!=(\i)\?.{0,20})\i(?:\.\i)?,{children:\1/, // TODO: (?:\.\i)? is stable compat
replace: "'div',{onClick:e=>e.stopPropagation(),children:$1"
}
]

View file

@ -96,7 +96,7 @@ export default definePlugin({
{
find: ".REQUEST_GUILD_MEMBERS,",
replacement: {
match: /\.send\(\i\.\i\.REQUEST_GUILD_MEMBERS,{/,
match: /\.send\(\i\.REQUEST_GUILD_MEMBERS,{/,
replace: "$&nonce:arguments[1].nonce,"
}
},

View file

@ -40,7 +40,7 @@ import {
Text,
UserStore
} from "@webpack/common";
import { JSX } from "react";
import { ComponentType, JSX } from "react";
const messageCache = new Map<string, {
message?: Message;
@ -48,8 +48,8 @@ const messageCache = new Map<string, {
}>();
const Embed = findComponentLazy(m => m.prototype?.renderSuppressButton);
const AutoModEmbed = findComponentByCodeLazy("withFooter", "childrenMessageContent:");
const ChannelMessage = findComponentByCodeLazy("childrenExecutedCommand:", ".hideAccessories");
let AutoModEmbed: ComponentType<any> = () => null;
const SearchResultClasses = findCssClassesLazy("message", "searchResult");
const EmbedClasses = findCssClassesLazy("embedAuthorIcon", "embedAuthor", "embedAuthor", "embedMargin");
@ -373,6 +373,20 @@ export default definePlugin({
settings,
patches: [
{
find: "!1,withFooter:",
replacement: {
match: /(?=function (\i)\(\i\){let{message:\i,channel:\i,[^}]+?withFooter:)/,
replace: "$self.AutoModEmbed=$1;"
}
}
],
set AutoModEmbed(value: any) {
AutoModEmbed = value;
},
start() {
addMessageAccessory("MessageLinkEmbeds", props => {
if (!messageLinkRegex.test(props.message.content))

View file

@ -25,9 +25,9 @@ import { isNonNullish } from "@utils/guards";
import { Logger } from "@utils/Logger";
import definePlugin from "@utils/types";
import { Channel, User } from "@vencord/discord-types";
import { findByPropsLazy, findComponentByCodeLazy, findCssClassesLazy } from "@webpack";
import { findByPropsLazy, findCssClassesLazy } from "@webpack";
import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, Text, useMemo, UserStore } from "@webpack/common";
import { JSX } from "react";
import { ComponentType, JSX } from "react";
const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel");
const UserUtils = findByPropsLazy("getGlobalName");
@ -35,7 +35,8 @@ const UserUtils = findByPropsLazy("getGlobalName");
const ProfileListClasses = findCssClassesLazy("empty", "textContainer", "connectionIcon");
const TabBarClasses = findCssClassesLazy("tabPanelScroller", "tabBarPanel");
const MutualsListClasses = findCssClassesLazy("row", "icon", "name", "details");
const ExpandableList = findComponentByCodeLazy('action:"PRESS_SECTION"', '"section"');
let ExpandableList: ComponentType<any> = () => null;
function getGroupDMName(channel: Channel) {
return channel.name ||
@ -140,11 +141,19 @@ export default definePlugin({
match: /\.openUserProfileModal.+?\)}\)}\)(?<=,(\i)&&(\i)&&(\(0,\i\.jsxs?\)\(\i\.\i,{className:(\i)\.\i}\)).{0,50}?"MUTUAL_FRIENDS".+?)/,
replace: (m, hasMutualGuilds, hasMutualFriends, Divider, classes) => "" +
`${m},$self.renderDMPageList({user:arguments[0].user,hasDivider:${hasMutualGuilds}||${hasMutualFriends},Divider:${Divider},listStyle:${classes}.list})`
},
{
match: /(?=function (\i)\(\i\){let{section:\i,header:\i[^}]+?onExpand:)/,
replace: "$self.ExpandableList=$1;"
}
]
}
],
set ExpandableList(value: any) {
ExpandableList = value;
},
getMutualGroupDms(userId: string) {
try {
return getMutualGroupDms(userId);

View file

@ -93,7 +93,8 @@ export default definePlugin({
},
{
// User popout
find: /\.POPOUT,onClose:\i}\),nicknameIcons:.+?\.isProvisional/,
// Same find as ShowConnections
find: "#{intl::XcTHmQ::raw}",
replacement: {
match: /user:(\i),widgets:.{0,100}?\}\),/,
replace: "$&$self.renderProfileComponent({user:$1}),"

View file

@ -9,11 +9,10 @@ import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { hasGuildFeature } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy } from "@webpack";
import { findByPropsLazy } from "@webpack";
import { ChannelStore, GuildStore } from "@webpack/common";
const SummaryStore = findByPropsLazy("allSummaries", "findSummary");
const createSummaryFromServer = findByCodeLazy(".people)),startId:", ".type}");
const settings = definePluginSettings({
summaryExpiryThresholdDays: {
@ -39,7 +38,7 @@ interface Summary {
unsafe: boolean;
}
interface ChannelSummaries {
interface ChannelSummary {
type: string;
channel_id: string;
guild_id: string;
@ -48,6 +47,21 @@ interface ChannelSummaries {
// custom property
time?: number;
}
// TODO: these types are wrong and evil and incorrect
function createChannelSummaryFromServer(s: Summary, channelId: string): ChannelSummary {
return {
id: s.id,
topic: s.topic,
summShort: s.summ_short,
people: Array.from(new Set(s.people)),
startId: s.start_id,
endId: s.end_id,
count: s.count,
channelId,
source: s.source,
type: s.type as any,
} as any as ChannelSummary;
}
export default definePlugin({
name: "Summaries",
@ -71,9 +85,13 @@ export default definePlugin({
}
}
],
flux: {
CONVERSATION_SUMMARY_UPDATE(data) {
const incomingSummaries: ChannelSummaries[] = data.summaries.map((summary: any) => ({ ...createSummaryFromServer(summary), time: Date.now() }));
const incomingSummaries: ChannelSummary[] = data.summaries.map((summary: any) => ({
...createChannelSummaryFromServer(summary, undefined!),
time: Date.now()
}));
// idk if this is good for performance but it doesnt seem to be a problem in my experience
DataStore.update("summaries-data", summaries => {

View file

@ -161,7 +161,8 @@ export default definePlugin({
patches: [
{
find: /\.POPOUT,onClose:\i}\),nicknameIcons:.+?\.isProvisional/,
// Same find as ReviewDB
find: "#{intl::XcTHmQ::raw}",
replacement: {
match: /userId:\i\.id,guild:\i\}\)(?=])/,
replace: "$&,$self.profilePopoutComponent(arguments[0])"

View file

@ -27,6 +27,7 @@ import { formatDuration } from "@utils/text";
import type { Channel, RoleOrUserPermission } from "@vencord/discord-types";
import { findByPropsLazy, findComponentByCodeLazy, findCssClassesLazy } from "@webpack";
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
import { ComponentType } from "react";
import { cl, settings } from "..";
@ -65,9 +66,11 @@ const enum ChannelFlags {
}
const ChatScrollClasses = findCssClassesLazy("auto", "managedReactiveScroller", "customTheme");
const ChannelBeginHeader = findComponentByCodeLazy("#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}");
const TagComponent = findComponentByCodeLazy("#{intl::FORUM_TAG_A11Y_FILTER_BY_TAG}");
let ChannelBeginHeader: ComponentType<any> = () => null;
export const setChannelBeginHeader = v => ChannelBeginHeader = v;
const EmojiParser = findByPropsLazy("convertSurrogateToName");
const EmojiUtils = findByPropsLazy("getURL", "getEmojiColors");

View file

@ -28,7 +28,7 @@ import type { Channel, Role } from "@vencord/discord-types";
import { findCssClassesLazy } from "@webpack";
import { ChannelStore, PermissionsBits, PermissionStore, Tooltip } from "@webpack/common";
import HiddenChannelLockScreen from "./components/HiddenChannelLockScreen";
import HiddenChannelLockScreen, { setChannelBeginHeader } from "./components/HiddenChannelLockScreen";
export const cl = classNameFactory("vc-shc-");
@ -476,9 +476,19 @@ export default definePlugin({
match: /(getVoiceStateForUser.{0,150}?)&&\i\.\i\.canWithPartialContext.{0,20}VIEW_CHANNEL.+?}\)(?=\?)/,
replace: "$1"
}
},
{
find: "#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}",
replacement: {
match: /(?=function (\i)\(\i\){let{channel:.{0,200}?getSortedRoles\()/,
replace: "$self.ChannelBeginHeader=$1;"
}
}
],
set ChannelBeginHeader(value: any) {
setChannelBeginHeader(value);
},
swapViewChannelWithConnectPermission(mergedPermissions: bigint, channel: Channel) {
if (!PermissionStore.can(PermissionsBits.CONNECT, channel)) {

View file

@ -31,11 +31,12 @@ export default definePlugin({
tags: ["Voice"],
authors: [Devs.Ven, Devs.D3SOX],
patches: [
...[
".handleVoiceStatusClick", // voice channels
".handleClickChat" // stage channels
].map(find => ({
find,
// Stage Channels & Voice Channels
// the find is for stage channels, but it also handles voice
// channels because they're both in the same concatenated module
// the find for voice channels was `.handleVoiceStatusClick`
{
find: ".handleClickChat",
// hack: these are not React onClick, it is a custom prop handled by Discord
// thus, replacing this with onDoubleClick won't work, and you also cannot check
// e.detail since instead of the event they pass the channel.
@ -46,10 +47,10 @@ export default definePlugin({
replace: "onClick:()=>{$self.schedule(()=>{this.handleClick()},this)",
},
]
})),
},
{
// channel mentions
find: 'className:"channelMention",children',
find: 'className:"channelMention",children:[null!=',
replacement: {
match: /onClick:(\i)(?=,.{0,30}className:"channelMention".+?(\i)\.inContent)/,
replace: (_, onClick, props) => ""

View file

@ -17,21 +17,15 @@
*/
import { useTimer } from "@utils/react";
import { findComponentByCodeLazy } from "@webpack";
import { cl } from ".";
interface VoiceMessageProps {
src: string;
waveform: string;
}
const VoiceMessage = findComponentByCodeLazy<VoiceMessageProps>("waveform:", "onVolumeChange");
import { cl, VoiceMessage } from ".";
export type VoicePreviewOptions = {
src?: string;
waveform: string;
recording?: boolean;
};
export const VoicePreview = ({
src,
waveform,

View file

@ -49,6 +49,12 @@ export type VoiceRecorder = ComponentType<{
onRecordingChange?(recording: boolean): void;
}>;
export interface VoiceMessageProps {
src: string;
waveform: string;
}
export let VoiceMessage: ComponentType<VoiceMessageProps> = () => null;
const VoiceRecorder = IS_DISCORD_DESKTOP ? VoiceRecorderDesktop : VoiceRecorderWeb;
const ctxMenuPatch: NavContextMenuPatchCallback = (children, props) => {
@ -74,6 +80,21 @@ export default definePlugin({
tags: ["Voice"],
authors: [Devs.Ven, Devs.Vap, Devs.Nickyux],
settings,
patches: [
{
find: "#{intl::3XohGn::raw}",
replacement: {
match: /(?<=\i=)(?=\i\.memo\(.{0,50}?=1,onVolumeChange:[^}]+?waveform:[^}]+?playbackCacheKey:)/,
replace: "$self.VoiceMessage=",
}
}
],
set VoiceMessage(value) {
VoiceMessage = value;
},
contextMenus: {
"channel-attach": ctxMenuPatch
}

View file

@ -57,7 +57,7 @@ export const Tooltip = waitForComponent<t.Tooltip>("Tooltip", m => m.prototype?.
export const TooltipContainer = TooltipContainerComponent as never;
export const TextInput = waitForComponent<t.TextInput>("TextInput", filters.componentByCode("#{intl::MAXIMUM_LENGTH_ERROR}", '"input"'));
export const TextArea = waitForComponent<t.TextArea>("TextArea", filters.componentByCode("this.getPaddingRight()},id:"));
export const TextArea = waitForComponent<t.TextArea>("TextArea", filters.componentByCode('"data-mana-component":"text-area"'));
export const Select = waitForComponent<t.Select>("Select", filters.componentByCode('selectionMode:"single",onSelectionChange:', "isSelected:"));
export const SearchableSelect = waitForComponent<t.SearchableSelect>("SearchableSelect", filters.componentByCode('?"multiple":"single",required:'));
export const Slider = waitForComponent<t.Slider>("Slider", filters.componentByCode("markDash", "this.renderMark("));
@ -69,12 +69,21 @@ export const Paginator = waitForComponent<t.Paginator>("Paginator", filters.comp
export const Clickable = waitForComponent<t.Clickable>("Clickable", filters.componentByCode("this.context?this.renderNonInteractive():"));
export const Avatar = waitForComponent<t.Avatar>("Avatar", filters.componentByCode(".size-1.375*"));
export const ColorPicker = waitForComponent<t.ColorPicker>("ColorPicker", filters.componentByCode("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", "showEyeDropper"));
export let ColorPicker: t.ColorPicker = () => null;
export function setColorPicker(component: t.ColorPicker) {
ColorPicker = component;
}
export const UserSummaryItem = waitForComponent("UserSummaryItem", filters.componentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"));
export let createScroller: (scrollbarClassName: string, fadeClassName: string, customThemeClassName: string) => t.ScrollerThin;
export let createScroller: ((scrollbarClassName: string, fadeClassName: string, customThemeClassName: string) => t.ScrollerThin) | undefined;
export function setCreateScroller(cs: NonNullable<typeof createScroller>) {
createScroller = cs;
}
export let createListScroller: (scrollBarClassName: string, fadeClassName: string, someOtherClassIdkMan: string, resizeObserverClass: typeof ResizeObserver) => t.ListScrollerThin;
waitFor(filters.byCode("getScrollerNode:", "resizeObserver:", "sectionHeight:"), m => createListScroller = m);
const listScrollerClassnames = ["thin", "auto", "fade"] as const;
export const scrollerClasses = findCssClassesLazy("thin", "auto", "fade", "customTheme", "none");
@ -88,12 +97,9 @@ export const listScrollerClasses = proxyLazyWebpack(() => {
return mapMangledCssClasses(mod, listScrollerClassnames);
});
waitFor(filters.byCode('="ltr",orientation:', "customTheme:", "forwardRef"), m => createScroller = m);
waitFor(filters.byCode("getScrollerNode:", "resizeObserver:", "sectionHeight:"), m => createListScroller = m);
export const ScrollerNone = LazyComponent(() => createScroller(scrollerClasses.none, scrollerClasses.fade, scrollerClasses.customTheme));
export const ScrollerThin = LazyComponent(() => createScroller(scrollerClasses.thin, scrollerClasses.fade, scrollerClasses.customTheme));
export const ScrollerAuto = LazyComponent(() => createScroller(scrollerClasses.auto, scrollerClasses.fade, scrollerClasses.customTheme));
export const ScrollerNone = LazyComponent(() => createScroller?.(scrollerClasses.none, scrollerClasses.fade, scrollerClasses.customTheme)!);
export const ScrollerThin = LazyComponent(() => createScroller?.(scrollerClasses.thin, scrollerClasses.fade, scrollerClasses.customTheme)!);
export const ScrollerAuto = LazyComponent(() => createScroller?.(scrollerClasses.auto, scrollerClasses.fade, scrollerClasses.customTheme)!);
export const ListScrollerThin = LazyComponent(() => createListScroller(listScrollerClasses.thin, listScrollerClasses.fade, "", ResizeObserver));
export const ListScrollerAuto = LazyComponent(() => createListScroller(listScrollerClasses.auto, listScrollerClasses.fade, "", ResizeObserver));

View file

@ -306,7 +306,6 @@ function proxyFactoryAndUpdateExisting(moduleFactories: AnyWebpackRequire["m"],
*/
function updateExistingFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, newProxiedFactory: AnyModuleFactory, ignoreExistingInTarget: boolean) {
let existingFactory: AnyModuleFactory | undefined;
let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined;
for (const wreq of allWebpackInstances) {
const instanceModuleFactories = wreq.m[SYM_ORIGINAL_MODULE_FACTORIES] ?? wreq.m;
@ -316,7 +315,6 @@ function updateExistingFactory(moduleFactories: AnyWebpackRequire["m"], moduleId
if (Object.hasOwn(wreq.m, moduleId)) {
existingFactory = wreq.m[moduleId]; // Must use wreq.m here instead of instanceModuleFactories to use our proxy in case the module factories are already proxied.
moduleFactoriesWithFactory = instanceModuleFactories;
break;
}
}