87 lines
2.6 kB
1
import { InternalItem, Menu, MenuElement } from "@moonlight-mod/types/coreExtensions/contextMenu";
2
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
3
import parser from "@moonlight-mod/wp/contextMenu_evilMenu";
4
5
// NOTE: We originally had item as a function that returned this, but it didn't
6
// quite know how to work out the type and thought it was a JSX element (it
7
// *technically* was). This has less type safety, but a @ts-expect-error has
8
// zero, so it's better than nothing.
9
type ReturnType = MenuElement | MenuElement[];
10
11
type Patch = {
12
navId: string;
13
item: React.FC<any>;
14
anchor: string | RegExp;
15
before: boolean;
16
};
17
18
function addItem<T = any>(navId: string, item: React.FC<T>, anchor: string | RegExp, before = false) {
19
if (anchor instanceof RegExp && anchor.flags.includes("g"))
20
throw new Error("anchor regular expression should not be global");
21
patches.push({ navId, item, anchor, before });
22
}
23
24
const patches: Patch[] = [];
25
function _patchMenu(props: React.ComponentProps<Menu>, items: InternalItem[]) {
26
const matches = patches.filter((p) => p.navId === props.navId);
27
if (!matches.length) return items;
28
29
for (const patch of matches) {
30
const idx = items.findIndex((i) =>
31
typeof patch.anchor === "string" ? i.key === patch.anchor : patch.anchor.test(i.key!)
32
);
33
if (idx === -1) continue;
34
items.splice(idx + 1 - +patch.before, 0, ...parser(patch.item(menuProps) as ReturnType));
35
}
36
37
return items;
38
}
39
40
let menuProps: any;
41
function _saveProps(self: any, el: any) {
42
menuProps = el.props;
43
44
const original = self.props.closeContextMenu;
45
self.props.closeContextMenu = function (...args: any[]) {
46
menuProps = undefined;
47
return original?.apply(this, args);
48
};
49
50
return el;
51
}
52
53
module.exports = {
54
patches,
55
addItem,
56
_patchMenu,
57
_saveProps
58
};
59
60
// Unmangle Menu elements
61
const code =
62
spacepack.require.m[
63
spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id
64
].toString();
65
66
let MangledMenu;
67
68
const typeRegex = /if\(.\.type===(.)\.(.+?)\).+?type:"(.+?)"/g;
69
const typeMap: Record<string, string | undefined> = {
70
checkbox: "MenuCheckboxItem",
71
control: "MenuControlItem",
72
groupstart: "MenuGroup",
73
customitem: "MenuItem",
74
radio: "MenuRadioItem",
75
separator: "MenuSeparator"
76
};
77
78
for (const [, modIdent, mangled, type] of code.matchAll(typeRegex)) {
79
if (!MangledMenu) {
80
const modId = code.match(new RegExp(`${modIdent}=.\\((\\d+?)\\)`))![1];
81
MangledMenu = spacepack.require(modId);
82
}
83
84
const prop = typeMap[type];
85
if (!prop) continue;
86
module.exports[prop] = MangledMenu[mangled];
87
}
88