Fix Vencord context menu options missing

Also merges MenuItemDemanglerAPI into ContextMenuAPI because these patch the same module anyway and depend on each other
This commit is contained in:
Vendicated 2026-05-08 16:00:46 +02:00
parent 2fbe267499
commit 3109ff102e
No known key found for this signature in database
GPG key ID: D66986BAF75ECF18
2 changed files with 66 additions and 80 deletions

View file

@ -17,7 +17,23 @@
*/
import { Devs } from "@utils/constants";
import { canonicalizeMatch } from "@utils/patches";
import definePlugin from "@utils/types";
import { Menu } from "@webpack/common";
// duplicate values have multiple branches with different types. Just include all to be safe
const nameMap = {
radio: "MenuRadioItem",
separator: "MenuSeparator",
checkbox: "MenuCheckboxItem",
groupstart: "MenuGroup",
control: "MenuControlItem",
compositecontrol: "MenuControlItem",
item: "MenuItem",
customitem: "MenuItem",
};
export default definePlugin({
name: "ContextMenuAPI",
@ -26,13 +42,6 @@ export default definePlugin({
required: true,
patches: [
{
find: "♫ (つ。◕‿‿◕。)つ ♪",
replacement: {
match: /(?=let{navId:)(?<=function \i\((\i)\).+?)/,
replace: "$1=Vencord.Api.ContextMenu._usePatchContextMenu($1);"
}
},
{
find: "navId:",
all: true,
@ -50,6 +59,54 @@ export default definePlugin({
}
}
]
}
]
},
{
find: "Menu API only allows Items",
replacement: [
// Patch the central context menu handler
{
match: /(?=let{navId:)(?<=function \i\((\i)\).+?)/,
replace: "$1=Vencord.Api.ContextMenu._usePatchContextMenu($1);"
},
// Demangle Discord's Menu Item module
{
match: /(?<=(\(\i\.type===(\i\.\i)\).{0,50}?navigable:.+Menu API).+?)}$/s,
replace: (_, m) => {
const registerCalls = [] as string[];
const typeCheckRe = canonicalizeMatch(/\(\i\.type===(\i\.\i)\)/g); // if (t.type === m.MenuItem)
const pushTypeRe = /type:"(\w+)"/g; // push({type:"item"})
let typeMatch: RegExpExecArray | null;
while ((typeMatch = typeCheckRe.exec(m)) !== null) {
const component = typeMatch[1];
// Set the starting index of the second regex to that of the first to start
// matching from after the if
pushTypeRe.lastIndex = typeCheckRe.lastIndex;
// extract the first type: "..."
const type = pushTypeRe.exec(m)?.[1];
if (type && type in nameMap) {
const name = nameMap[type];
registerCalls.push(`$self.registerMenuItem("${name}",${component})`);
}
}
if (registerCalls.length < 6) {
console.warn("[MenuItemDemanglerAPI] Expected to remap 6 items, only remapped", registerCalls.length);
}
return `${registerCalls.join(";")};}`;
},
}
],
},
],
registerMenuItem(name: string, component: any) {
Object.defineProperty(component, "name", { value: name });
Menu[name] = component;
}
});

View file

@ -1,71 +0,0 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Devs } from "@utils/constants";
import { canonicalizeMatch } from "@utils/patches";
import definePlugin from "@utils/types";
import { Menu } from "@webpack/common";
// duplicate values have multiple branches with different types. Just include all to be safe
const nameMap = {
radio: "MenuRadioItem",
separator: "MenuSeparator",
checkbox: "MenuCheckboxItem",
groupstart: "MenuGroup",
control: "MenuControlItem",
compositecontrol: "MenuControlItem",
item: "MenuItem",
customitem: "MenuItem",
};
export default definePlugin({
name: "MenuItemDemanglerAPI",
description: "Demangles Discord's Menu Item module",
authors: [Devs.Ven],
required: true,
patches: [
{
find: "Menu API only allows Items",
replacement: {
match: /(?<=(\(\i\.type===(\i\.\i)\).{0,50}?navigable:.+Menu API).+?)}$/s,
replace: (_, m) => {
const registerCalls = [] as string[];
const typeCheckRe = canonicalizeMatch(/\(\i\.type===(\i\.\i)\)/g); // if (t.type === m.MenuItem)
const pushTypeRe = /type:"(\w+)"/g; // push({type:"item"})
let typeMatch: RegExpExecArray | null;
while ((typeMatch = typeCheckRe.exec(m)) !== null) {
const component = typeMatch[1];
// Set the starting index of the second regex to that of the first to start
// matching from after the if
pushTypeRe.lastIndex = typeCheckRe.lastIndex;
// extract the first type: "..."
const type = pushTypeRe.exec(m)?.[1];
if (type && type in nameMap) {
const name = nameMap[type];
registerCalls.push(`$self.registerMenuItem("${name}",${component})`);
}
}
if (registerCalls.length < 6) {
console.warn("[MenuItemDemanglerAPI] Expected to remap 6 items, only remapped", registerCalls.length);
}
return `${registerCalls.join(";")};}`;
},
},
},
],
registerMenuItem(name: string, component: any) {
Object.defineProperty(component, "name", { value: name });
Menu[name] = component;
}
});