-57
.eslintrc.json
-57
.eslintrc.json
···
1
-
{
2
-
"extends": [
3
-
"eslint:recommended",
4
-
"plugin:@typescript-eslint/recommended",
5
-
"plugin:prettier/recommended",
6
-
"plugin:react/recommended"
7
-
],
8
-
"plugins": ["@typescript-eslint", "prettier", "react"],
9
-
"parser": "@typescript-eslint/parser",
10
-
"env": {
11
-
"browser": true,
12
-
"node": true
13
-
},
14
-
"parserOptions": {
15
-
"ecmaFeatures": {
16
-
"jsx": true
17
-
},
18
-
"ecmaVersion": "latest",
19
-
"sourceType": "module"
20
-
},
21
-
"rules": {
22
-
"indent": "off",
23
-
"eqeqeq": [
24
-
"error",
25
-
"always",
26
-
{
27
-
"null": "ignore"
28
-
}
29
-
],
30
-
"quotes": [
31
-
"error",
32
-
"double",
33
-
{ "avoidEscape": true, "allowTemplateLiterals": true }
34
-
],
35
-
"@typescript-eslint/no-unused-vars": [
36
-
"error",
37
-
{ "args": "none", "varsIgnorePattern": "^_" }
38
-
],
39
-
// Mostly so we don't forget to leave these in when committing
40
-
"no-console": "error",
41
-
"no-debugger": "error",
42
-
43
-
// Quite honestly we're interacting with so much unknown within Discord that
44
-
// this being enabled is a hinderance
45
-
"@typescript-eslint/no-explicit-any": "off",
46
-
47
-
"@typescript-eslint/no-var-requires": "off",
48
-
49
-
// https://canary.discord.com/channels/1154257010532032512/1154275441788583996/1181760413231230976
50
-
"no-unused-labels": "off"
51
-
},
52
-
"settings": {
53
-
"react": {
54
-
"version": "18.2"
55
-
}
56
-
}
57
-
}
+41
.github/workflows/browser.yml
+41
.github/workflows/browser.yml
···
1
+
name: Browser extension builds
2
+
3
+
on:
4
+
push:
5
+
branches:
6
+
- develop
7
+
8
+
jobs:
9
+
browser:
10
+
name: Browser extension builds
11
+
runs-on: ubuntu-latest
12
+
steps:
13
+
- uses: actions/checkout@v4
14
+
- uses: pnpm/action-setup@v4
15
+
- uses: actions/setup-node@v4
16
+
with:
17
+
node-version: 22
18
+
cache: pnpm
19
+
20
+
- name: Install dependencies
21
+
run: pnpm install --frozen-lockfile
22
+
- name: Build moonlight
23
+
env:
24
+
NODE_ENV: production
25
+
run: pnpm run build
26
+
27
+
- name: Build MV3
28
+
run: pnpm run browser
29
+
- name: Build MV2
30
+
run: pnpm run browser-mv2
31
+
32
+
- name: Upload MV3
33
+
uses: actions/upload-artifact@v4
34
+
with:
35
+
name: browser
36
+
path: ./dist/browser
37
+
- name: Upload MV2
38
+
uses: actions/upload-artifact@v4
39
+
with:
40
+
name: browser-mv2
41
+
path: ./dist/browser-mv2
+4
-8
.github/workflows/lint.yml
+4
-8
.github/workflows/lint.yml
···
9
9
name: Lint commits
10
10
runs-on: ubuntu-latest
11
11
steps:
12
-
- uses: actions/checkout@v3
13
-
14
-
- uses: pnpm/action-setup@v2
15
-
with:
16
-
version: 8
17
-
run_install: false
18
-
- uses: actions/setup-node@v3
12
+
- uses: actions/checkout@v4
13
+
- uses: pnpm/action-setup@v4
14
+
- uses: actions/setup-node@v4
19
15
with:
20
-
node-version: 18
16
+
node-version: 22
21
17
cache: pnpm
22
18
23
19
- name: Install dependencies
+9
-11
.github/workflows/nightly.yml
+9
-11
.github/workflows/nightly.yml
···
15
15
name: Nightly builds on GitHub Pages
16
16
runs-on: ubuntu-latest
17
17
steps:
18
-
- uses: actions/checkout@v3
19
-
20
-
- uses: pnpm/action-setup@v2
21
-
with:
22
-
version: 8
23
-
run_install: false
24
-
- uses: actions/setup-node@v3
18
+
- uses: actions/checkout@v4
19
+
- uses: pnpm/action-setup@v4
20
+
- uses: actions/setup-node@v4
25
21
with:
26
-
node-version: 18
22
+
node-version: 22
27
23
cache: pnpm
28
24
29
25
- name: Install dependencies
···
31
27
- name: Build moonlight
32
28
env:
33
29
NODE_ENV: production
30
+
MOONLIGHT_BRANCH: nightly
31
+
MOONLIGHT_VERSION: ${{ github.sha }}
34
32
run: pnpm run build
35
33
36
34
- name: Write ref/commit to file
···
45
43
echo "$(date +%s)" >> ./dist/ref
46
44
47
45
- name: Setup GitHub Pages
48
-
uses: actions/configure-pages@v3
46
+
uses: actions/configure-pages@v5
49
47
- name: Upload artifact
50
-
uses: actions/upload-pages-artifact@v1
48
+
uses: actions/upload-pages-artifact@v3
51
49
with:
52
50
path: ./dist
53
51
- name: Deploy to GitHub Pages
54
-
uses: actions/deploy-pages@v2
52
+
uses: actions/deploy-pages@v4
+16
.github/workflows/nix.yml
+16
.github/workflows/nix.yml
···
1
+
name: Check Nix flake
2
+
on: [push, pull_request]
3
+
4
+
permissions:
5
+
checks: write
6
+
7
+
jobs:
8
+
nix:
9
+
name: Check Nix flake
10
+
runs-on: ubuntu-latest
11
+
steps:
12
+
- uses: actions/checkout@v4
13
+
- uses: DeterminateSystems/nix-installer-action@main
14
+
15
+
- name: Build default flake output
16
+
run: nix build
+6
-8
.github/workflows/release.yml
+6
-8
.github/workflows/release.yml
···
13
13
name: Release builds to GitHub Releases
14
14
runs-on: ubuntu-latest
15
15
steps:
16
-
- uses: actions/checkout@v3
17
-
18
-
- uses: pnpm/action-setup@v2
19
-
with:
20
-
version: 8
21
-
run_install: false
22
-
- uses: actions/setup-node@v3
16
+
- uses: actions/checkout@v4
17
+
- uses: pnpm/action-setup@v4
18
+
- uses: actions/setup-node@v4
23
19
with:
24
-
node-version: 18
20
+
node-version: 22
25
21
cache: pnpm
26
22
27
23
- name: Install dependencies
···
29
25
- name: Build moonlight
30
26
env:
31
27
NODE_ENV: production
28
+
MOONLIGHT_BRANCH: stable
29
+
MOONLIGHT_VERSION: ${{ github.ref_name }}
32
30
run: pnpm run build
33
31
- name: Create archive
34
32
run: |
+32
.github/workflows/types.yml
+32
.github/workflows/types.yml
···
1
+
name: Publish types on npm
2
+
on: workflow_dispatch
3
+
4
+
permissions:
5
+
contents: read
6
+
pages: write
7
+
id-token: write
8
+
9
+
jobs:
10
+
types:
11
+
name: Publish types on npm
12
+
runs-on: ubuntu-latest
13
+
steps:
14
+
- uses: actions/checkout@v4
15
+
- uses: pnpm/action-setup@v4
16
+
- uses: actions/setup-node@v4
17
+
with:
18
+
node-version: 22
19
+
cache: pnpm
20
+
registry-url: https://registry.npmjs.org
21
+
22
+
- name: Install dependencies
23
+
run: pnpm install --frozen-lockfile
24
+
- name: Build moonlight
25
+
env:
26
+
NODE_ENV: production
27
+
run: pnpm run build
28
+
29
+
- name: Publish types
30
+
run: pnpm publish --filter=./packages/types --access public --no-git-checks
31
+
env:
32
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+8
.gitignore
+8
.gitignore
+4
-4
.prettierrc
+4
-4
.prettierrc
-14
.vscode/tasks.json
-14
.vscode/tasks.json
+4
-4
CHANGELOG.md
+4
-4
CHANGELOG.md
+125
-621
LICENSE
+125
-621
LICENSE
···
1
-
GNU AFFERO GENERAL PUBLIC LICENSE
2
-
Version 3, 19 November 2007
1
+
GNU LESSER GENERAL PUBLIC LICENSE
2
+
Version 3, 29 June 2007
3
3
4
4
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
5
Everyone is permitted to copy and distribute verbatim copies
6
6
of this license document, but changing it is not allowed.
7
7
8
-
Preamble
9
8
10
-
The GNU Affero General Public License is a free, copyleft license for
11
-
software and other kinds of works, specifically designed to ensure
12
-
cooperation with the community in the case of network server software.
13
-
14
-
The licenses for most software and other practical works are designed
15
-
to take away your freedom to share and change the works. By contrast,
16
-
our General Public Licenses are intended to guarantee your freedom to
17
-
share and change all versions of a program--to make sure it remains free
18
-
software for all its users.
19
-
20
-
When we speak of free software, we are referring to freedom, not
21
-
price. Our General Public Licenses are designed to make sure that you
22
-
have the freedom to distribute copies of free software (and charge for
23
-
them if you wish), that you receive source code or can get it if you
24
-
want it, that you can change the software or use pieces of it in new
25
-
free programs, and that you know you can do these things.
26
-
27
-
Developers that use our General Public Licenses protect your rights
28
-
with two steps: (1) assert copyright on the software, and (2) offer
29
-
you this License which gives you legal permission to copy, distribute
30
-
and/or modify the software.
31
-
32
-
A secondary benefit of defending all users' freedom is that
33
-
improvements made in alternate versions of the program, if they
34
-
receive widespread use, become available for other developers to
35
-
incorporate. Many developers of free software are heartened and
36
-
encouraged by the resulting cooperation. However, in the case of
37
-
software used on network servers, this result may fail to come about.
38
-
The GNU General Public License permits making a modified version and
39
-
letting the public access it on a server without ever releasing its
40
-
source code to the public.
41
-
42
-
The GNU Affero General Public License is designed specifically to
43
-
ensure that, in such cases, the modified source code becomes available
44
-
to the community. It requires the operator of a network server to
45
-
provide the source code of the modified version running there to the
46
-
users of that server. Therefore, public use of a modified version, on
47
-
a publicly accessible server, gives the public access to the source
48
-
code of the modified version.
49
-
50
-
An older license, called the Affero General Public License and
51
-
published by Affero, was designed to accomplish similar goals. This is
52
-
a different license, not a version of the Affero GPL, but Affero has
53
-
released a new version of the Affero GPL which permits relicensing under
54
-
this license.
55
-
56
-
The precise terms and conditions for copying, distribution and
57
-
modification follow.
58
-
59
-
TERMS AND CONDITIONS
60
-
61
-
0. Definitions.
62
-
63
-
"This License" refers to version 3 of the GNU Affero General Public License.
64
-
65
-
"Copyright" also means copyright-like laws that apply to other kinds of
66
-
works, such as semiconductor masks.
67
-
68
-
"The Program" refers to any copyrightable work licensed under this
69
-
License. Each licensee is addressed as "you". "Licensees" and
70
-
"recipients" may be individuals or organizations.
71
-
72
-
To "modify" a work means to copy from or adapt all or part of the work
73
-
in a fashion requiring copyright permission, other than the making of an
74
-
exact copy. The resulting work is called a "modified version" of the
75
-
earlier work or a work "based on" the earlier work.
76
-
77
-
A "covered work" means either the unmodified Program or a work based
78
-
on the Program.
79
-
80
-
To "propagate" a work means to do anything with it that, without
81
-
permission, would make you directly or secondarily liable for
82
-
infringement under applicable copyright law, except executing it on a
83
-
computer or modifying a private copy. Propagation includes copying,
84
-
distribution (with or without modification), making available to the
85
-
public, and in some countries other activities as well.
86
-
87
-
To "convey" a work means any kind of propagation that enables other
88
-
parties to make or receive copies. Mere interaction with a user through
89
-
a computer network, with no transfer of a copy, is not conveying.
90
-
91
-
An interactive user interface displays "Appropriate Legal Notices"
92
-
to the extent that it includes a convenient and prominently visible
93
-
feature that (1) displays an appropriate copyright notice, and (2)
94
-
tells the user that there is no warranty for the work (except to the
95
-
extent that warranties are provided), that licensees may convey the
96
-
work under this License, and how to view a copy of this License. If
97
-
the interface presents a list of user commands or options, such as a
98
-
menu, a prominent item in the list meets this criterion.
99
-
100
-
1. Source Code.
101
-
102
-
The "source code" for a work means the preferred form of the work
103
-
for making modifications to it. "Object code" means any non-source
104
-
form of a work.
105
-
106
-
A "Standard Interface" means an interface that either is an official
107
-
standard defined by a recognized standards body, or, in the case of
108
-
interfaces specified for a particular programming language, one that
109
-
is widely used among developers working in that language.
110
-
111
-
The "System Libraries" of an executable work include anything, other
112
-
than the work as a whole, that (a) is included in the normal form of
113
-
packaging a Major Component, but which is not part of that Major
114
-
Component, and (b) serves only to enable use of the work with that
115
-
Major Component, or to implement a Standard Interface for which an
116
-
implementation is available to the public in source code form. A
117
-
"Major Component", in this context, means a major essential component
118
-
(kernel, window system, and so on) of the specific operating system
119
-
(if any) on which the executable work runs, or a compiler used to
120
-
produce the work, or an object code interpreter used to run it.
121
-
122
-
The "Corresponding Source" for a work in object code form means all
123
-
the source code needed to generate, install, and (for an executable
124
-
work) run the object code and to modify the work, including scripts to
125
-
control those activities. However, it does not include the work's
126
-
System Libraries, or general-purpose tools or generally available free
127
-
programs which are used unmodified in performing those activities but
128
-
which are not part of the work. For example, Corresponding Source
129
-
includes interface definition files associated with source files for
130
-
the work, and the source code for shared libraries and dynamically
131
-
linked subprograms that the work is specifically designed to require,
132
-
such as by intimate data communication or control flow between those
133
-
subprograms and other parts of the work.
134
-
135
-
The Corresponding Source need not include anything that users
136
-
can regenerate automatically from other parts of the Corresponding
137
-
Source.
138
-
139
-
The Corresponding Source for a work in source code form is that
140
-
same work.
141
-
142
-
2. Basic Permissions.
143
-
144
-
All rights granted under this License are granted for the term of
145
-
copyright on the Program, and are irrevocable provided the stated
146
-
conditions are met. This License explicitly affirms your unlimited
147
-
permission to run the unmodified Program. The output from running a
148
-
covered work is covered by this License only if the output, given its
149
-
content, constitutes a covered work. This License acknowledges your
150
-
rights of fair use or other equivalent, as provided by copyright law.
151
-
152
-
You may make, run and propagate covered works that you do not
153
-
convey, without conditions so long as your license otherwise remains
154
-
in force. You may convey covered works to others for the sole purpose
155
-
of having them make modifications exclusively for you, or provide you
156
-
with facilities for running those works, provided that you comply with
157
-
the terms of this License in conveying all material for which you do
158
-
not control copyright. Those thus making or running the covered works
159
-
for you must do so exclusively on your behalf, under your direction
160
-
and control, on terms that prohibit them from making any copies of
161
-
your copyrighted material outside their relationship with you.
162
-
163
-
Conveying under any other circumstances is permitted solely under
164
-
the conditions stated below. Sublicensing is not allowed; section 10
165
-
makes it unnecessary.
166
-
167
-
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168
-
169
-
No covered work shall be deemed part of an effective technological
170
-
measure under any applicable law fulfilling obligations under article
171
-
11 of the WIPO copyright treaty adopted on 20 December 1996, or
172
-
similar laws prohibiting or restricting circumvention of such
173
-
measures.
174
-
175
-
When you convey a covered work, you waive any legal power to forbid
176
-
circumvention of technological measures to the extent such circumvention
177
-
is effected by exercising rights under this License with respect to
178
-
the covered work, and you disclaim any intention to limit operation or
179
-
modification of the work as a means of enforcing, against the work's
180
-
users, your or third parties' legal rights to forbid circumvention of
181
-
technological measures.
182
-
183
-
4. Conveying Verbatim Copies.
184
-
185
-
You may convey verbatim copies of the Program's source code as you
186
-
receive it, in any medium, provided that you conspicuously and
187
-
appropriately publish on each copy an appropriate copyright notice;
188
-
keep intact all notices stating that this License and any
189
-
non-permissive terms added in accord with section 7 apply to the code;
190
-
keep intact all notices of the absence of any warranty; and give all
191
-
recipients a copy of this License along with the Program.
192
-
193
-
You may charge any price or no price for each copy that you convey,
194
-
and you may offer support or warranty protection for a fee.
195
-
196
-
5. Conveying Modified Source Versions.
197
-
198
-
You may convey a work based on the Program, or the modifications to
199
-
produce it from the Program, in the form of source code under the
200
-
terms of section 4, provided that you also meet all of these conditions:
201
-
202
-
a) The work must carry prominent notices stating that you modified
203
-
it, and giving a relevant date.
204
-
205
-
b) The work must carry prominent notices stating that it is
206
-
released under this License and any conditions added under section
207
-
7. This requirement modifies the requirement in section 4 to
208
-
"keep intact all notices".
209
-
210
-
c) You must license the entire work, as a whole, under this
211
-
License to anyone who comes into possession of a copy. This
212
-
License will therefore apply, along with any applicable section 7
213
-
additional terms, to the whole of the work, and all its parts,
214
-
regardless of how they are packaged. This License gives no
215
-
permission to license the work in any other way, but it does not
216
-
invalidate such permission if you have separately received it.
217
-
218
-
d) If the work has interactive user interfaces, each must display
219
-
Appropriate Legal Notices; however, if the Program has interactive
220
-
interfaces that do not display Appropriate Legal Notices, your
221
-
work need not make them do so.
222
-
223
-
A compilation of a covered work with other separate and independent
224
-
works, which are not by their nature extensions of the covered work,
225
-
and which are not combined with it such as to form a larger program,
226
-
in or on a volume of a storage or distribution medium, is called an
227
-
"aggregate" if the compilation and its resulting copyright are not
228
-
used to limit the access or legal rights of the compilation's users
229
-
beyond what the individual works permit. Inclusion of a covered work
230
-
in an aggregate does not cause this License to apply to the other
231
-
parts of the aggregate.
232
-
233
-
6. Conveying Non-Source Forms.
234
-
235
-
You may convey a covered work in object code form under the terms
236
-
of sections 4 and 5, provided that you also convey the
237
-
machine-readable Corresponding Source under the terms of this License,
238
-
in one of these ways:
239
-
240
-
a) Convey the object code in, or embodied in, a physical product
241
-
(including a physical distribution medium), accompanied by the
242
-
Corresponding Source fixed on a durable physical medium
243
-
customarily used for software interchange.
244
-
245
-
b) Convey the object code in, or embodied in, a physical product
246
-
(including a physical distribution medium), accompanied by a
247
-
written offer, valid for at least three years and valid for as
248
-
long as you offer spare parts or customer support for that product
249
-
model, to give anyone who possesses the object code either (1) a
250
-
copy of the Corresponding Source for all the software in the
251
-
product that is covered by this License, on a durable physical
252
-
medium customarily used for software interchange, for a price no
253
-
more than your reasonable cost of physically performing this
254
-
conveying of source, or (2) access to copy the
255
-
Corresponding Source from a network server at no charge.
256
-
257
-
c) Convey individual copies of the object code with a copy of the
258
-
written offer to provide the Corresponding Source. This
259
-
alternative is allowed only occasionally and noncommercially, and
260
-
only if you received the object code with such an offer, in accord
261
-
with subsection 6b.
262
-
263
-
d) Convey the object code by offering access from a designated
264
-
place (gratis or for a charge), and offer equivalent access to the
265
-
Corresponding Source in the same way through the same place at no
266
-
further charge. You need not require recipients to copy the
267
-
Corresponding Source along with the object code. If the place to
268
-
copy the object code is a network server, the Corresponding Source
269
-
may be on a different server (operated by you or a third party)
270
-
that supports equivalent copying facilities, provided you maintain
271
-
clear directions next to the object code saying where to find the
272
-
Corresponding Source. Regardless of what server hosts the
273
-
Corresponding Source, you remain obligated to ensure that it is
274
-
available for as long as needed to satisfy these requirements.
275
-
276
-
e) Convey the object code using peer-to-peer transmission, provided
277
-
you inform other peers where the object code and Corresponding
278
-
Source of the work are being offered to the general public at no
279
-
charge under subsection 6d.
280
-
281
-
A separable portion of the object code, whose source code is excluded
282
-
from the Corresponding Source as a System Library, need not be
283
-
included in conveying the object code work.
284
-
285
-
A "User Product" is either (1) a "consumer product", which means any
286
-
tangible personal property which is normally used for personal, family,
287
-
or household purposes, or (2) anything designed or sold for incorporation
288
-
into a dwelling. In determining whether a product is a consumer product,
289
-
doubtful cases shall be resolved in favor of coverage. For a particular
290
-
product received by a particular user, "normally used" refers to a
291
-
typical or common use of that class of product, regardless of the status
292
-
of the particular user or of the way in which the particular user
293
-
actually uses, or expects or is expected to use, the product. A product
294
-
is a consumer product regardless of whether the product has substantial
295
-
commercial, industrial or non-consumer uses, unless such uses represent
296
-
the only significant mode of use of the product.
297
-
298
-
"Installation Information" for a User Product means any methods,
299
-
procedures, authorization keys, or other information required to install
300
-
and execute modified versions of a covered work in that User Product from
301
-
a modified version of its Corresponding Source. The information must
302
-
suffice to ensure that the continued functioning of the modified object
303
-
code is in no case prevented or interfered with solely because
304
-
modification has been made.
305
-
306
-
If you convey an object code work under this section in, or with, or
307
-
specifically for use in, a User Product, and the conveying occurs as
308
-
part of a transaction in which the right of possession and use of the
309
-
User Product is transferred to the recipient in perpetuity or for a
310
-
fixed term (regardless of how the transaction is characterized), the
311
-
Corresponding Source conveyed under this section must be accompanied
312
-
by the Installation Information. But this requirement does not apply
313
-
if neither you nor any third party retains the ability to install
314
-
modified object code on the User Product (for example, the work has
315
-
been installed in ROM).
316
-
317
-
The requirement to provide Installation Information does not include a
318
-
requirement to continue to provide support service, warranty, or updates
319
-
for a work that has been modified or installed by the recipient, or for
320
-
the User Product in which it has been modified or installed. Access to a
321
-
network may be denied when the modification itself materially and
322
-
adversely affects the operation of the network or violates the rules and
323
-
protocols for communication across the network.
324
-
325
-
Corresponding Source conveyed, and Installation Information provided,
326
-
in accord with this section must be in a format that is publicly
327
-
documented (and with an implementation available to the public in
328
-
source code form), and must require no special password or key for
329
-
unpacking, reading or copying.
330
-
331
-
7. Additional Terms.
332
-
333
-
"Additional permissions" are terms that supplement the terms of this
334
-
License by making exceptions from one or more of its conditions.
335
-
Additional permissions that are applicable to the entire Program shall
336
-
be treated as though they were included in this License, to the extent
337
-
that they are valid under applicable law. If additional permissions
338
-
apply only to part of the Program, that part may be used separately
339
-
under those permissions, but the entire Program remains governed by
340
-
this License without regard to the additional permissions.
341
-
342
-
When you convey a copy of a covered work, you may at your option
343
-
remove any additional permissions from that copy, or from any part of
344
-
it. (Additional permissions may be written to require their own
345
-
removal in certain cases when you modify the work.) You may place
346
-
additional permissions on material, added by you to a covered work,
347
-
for which you have or can give appropriate copyright permission.
348
-
349
-
Notwithstanding any other provision of this License, for material you
350
-
add to a covered work, you may (if authorized by the copyright holders of
351
-
that material) supplement the terms of this License with terms:
352
-
353
-
a) Disclaiming warranty or limiting liability differently from the
354
-
terms of sections 15 and 16 of this License; or
355
-
356
-
b) Requiring preservation of specified reasonable legal notices or
357
-
author attributions in that material or in the Appropriate Legal
358
-
Notices displayed by works containing it; or
359
-
360
-
c) Prohibiting misrepresentation of the origin of that material, or
361
-
requiring that modified versions of such material be marked in
362
-
reasonable ways as different from the original version; or
363
-
364
-
d) Limiting the use for publicity purposes of names of licensors or
365
-
authors of the material; or
366
-
367
-
e) Declining to grant rights under trademark law for use of some
368
-
trade names, trademarks, or service marks; or
369
-
370
-
f) Requiring indemnification of licensors and authors of that
371
-
material by anyone who conveys the material (or modified versions of
372
-
it) with contractual assumptions of liability to the recipient, for
373
-
any liability that these contractual assumptions directly impose on
374
-
those licensors and authors.
375
-
376
-
All other non-permissive additional terms are considered "further
377
-
restrictions" within the meaning of section 10. If the Program as you
378
-
received it, or any part of it, contains a notice stating that it is
379
-
governed by this License along with a term that is a further
380
-
restriction, you may remove that term. If a license document contains
381
-
a further restriction but permits relicensing or conveying under this
382
-
License, you may add to a covered work material governed by the terms
383
-
of that license document, provided that the further restriction does
384
-
not survive such relicensing or conveying.
9
+
This version of the GNU Lesser General Public License incorporates
10
+
the terms and conditions of version 3 of the GNU General Public
11
+
License, supplemented by the additional permissions listed below.
385
12
386
-
If you add terms to a covered work in accord with this section, you
387
-
must place, in the relevant source files, a statement of the
388
-
additional terms that apply to those files, or a notice indicating
389
-
where to find the applicable terms.
13
+
0. Additional Definitions.
390
14
391
-
Additional terms, permissive or non-permissive, may be stated in the
392
-
form of a separately written license, or stated as exceptions;
393
-
the above requirements apply either way.
15
+
As used herein, "this License" refers to version 3 of the GNU Lesser
16
+
General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+
General Public License.
394
18
395
-
8. Termination.
19
+
"The Library" refers to a covered work governed by this License,
20
+
other than an Application or a Combined Work as defined below.
396
21
397
-
You may not propagate or modify a covered work except as expressly
398
-
provided under this License. Any attempt otherwise to propagate or
399
-
modify it is void, and will automatically terminate your rights under
400
-
this License (including any patent licenses granted under the third
401
-
paragraph of section 11).
22
+
An "Application" is any work that makes use of an interface provided
23
+
by the Library, but which is not otherwise based on the Library.
24
+
Defining a subclass of a class defined by the Library is deemed a mode
25
+
of using an interface provided by the Library.
402
26
403
-
However, if you cease all violation of this License, then your
404
-
license from a particular copyright holder is reinstated (a)
405
-
provisionally, unless and until the copyright holder explicitly and
406
-
finally terminates your license, and (b) permanently, if the copyright
407
-
holder fails to notify you of the violation by some reasonable means
408
-
prior to 60 days after the cessation.
27
+
A "Combined Work" is a work produced by combining or linking an
28
+
Application with the Library. The particular version of the Library
29
+
with which the Combined Work was made is also called the "Linked
30
+
Version".
409
31
410
-
Moreover, your license from a particular copyright holder is
411
-
reinstated permanently if the copyright holder notifies you of the
412
-
violation by some reasonable means, this is the first time you have
413
-
received notice of violation of this License (for any work) from that
414
-
copyright holder, and you cure the violation prior to 30 days after
415
-
your receipt of the notice.
32
+
The "Minimal Corresponding Source" for a Combined Work means the
33
+
Corresponding Source for the Combined Work, excluding any source code
34
+
for portions of the Combined Work that, considered in isolation, are
35
+
based on the Application, and not on the Linked Version.
416
36
417
-
Termination of your rights under this section does not terminate the
418
-
licenses of parties who have received copies or rights from you under
419
-
this License. If your rights have been terminated and not permanently
420
-
reinstated, you do not qualify to receive new licenses for the same
421
-
material under section 10.
37
+
The "Corresponding Application Code" for a Combined Work means the
38
+
object code and/or source code for the Application, including any data
39
+
and utility programs needed for reproducing the Combined Work from the
40
+
Application, but excluding the System Libraries of the Combined Work.
422
41
423
-
9. Acceptance Not Required for Having Copies.
42
+
1. Exception to Section 3 of the GNU GPL.
424
43
425
-
You are not required to accept this License in order to receive or
426
-
run a copy of the Program. Ancillary propagation of a covered work
427
-
occurring solely as a consequence of using peer-to-peer transmission
428
-
to receive a copy likewise does not require acceptance. However,
429
-
nothing other than this License grants you permission to propagate or
430
-
modify any covered work. These actions infringe copyright if you do
431
-
not accept this License. Therefore, by modifying or propagating a
432
-
covered work, you indicate your acceptance of this License to do so.
44
+
You may convey a covered work under sections 3 and 4 of this License
45
+
without being bound by section 3 of the GNU GPL.
433
46
434
-
10. Automatic Licensing of Downstream Recipients.
47
+
2. Conveying Modified Versions.
435
48
436
-
Each time you convey a covered work, the recipient automatically
437
-
receives a license from the original licensors, to run, modify and
438
-
propagate that work, subject to this License. You are not responsible
439
-
for enforcing compliance by third parties with this License.
49
+
If you modify a copy of the Library, and, in your modifications, a
50
+
facility refers to a function or data to be supplied by an Application
51
+
that uses the facility (other than as an argument passed when the
52
+
facility is invoked), then you may convey a copy of the modified
53
+
version:
440
54
441
-
An "entity transaction" is a transaction transferring control of an
442
-
organization, or substantially all assets of one, or subdividing an
443
-
organization, or merging organizations. If propagation of a covered
444
-
work results from an entity transaction, each party to that
445
-
transaction who receives a copy of the work also receives whatever
446
-
licenses to the work the party's predecessor in interest had or could
447
-
give under the previous paragraph, plus a right to possession of the
448
-
Corresponding Source of the work from the predecessor in interest, if
449
-
the predecessor has it or can get it with reasonable efforts.
55
+
a) under this License, provided that you make a good faith effort to
56
+
ensure that, in the event an Application does not supply the
57
+
function or data, the facility still operates, and performs
58
+
whatever part of its purpose remains meaningful, or
450
59
451
-
You may not impose any further restrictions on the exercise of the
452
-
rights granted or affirmed under this License. For example, you may
453
-
not impose a license fee, royalty, or other charge for exercise of
454
-
rights granted under this License, and you may not initiate litigation
455
-
(including a cross-claim or counterclaim in a lawsuit) alleging that
456
-
any patent claim is infringed by making, using, selling, offering for
457
-
sale, or importing the Program or any portion of it.
60
+
b) under the GNU GPL, with none of the additional permissions of
61
+
this License applicable to that copy.
458
62
459
-
11. Patents.
63
+
3. Object Code Incorporating Material from Library Header Files.
460
64
461
-
A "contributor" is a copyright holder who authorizes use under this
462
-
License of the Program or a work on which the Program is based. The
463
-
work thus licensed is called the contributor's "contributor version".
65
+
The object code form of an Application may incorporate material from
66
+
a header file that is part of the Library. You may convey such object
67
+
code under terms of your choice, provided that, if the incorporated
68
+
material is not limited to numerical parameters, data structure
69
+
layouts and accessors, or small macros, inline functions and templates
70
+
(ten or fewer lines in length), you do both of the following:
464
71
465
-
A contributor's "essential patent claims" are all patent claims
466
-
owned or controlled by the contributor, whether already acquired or
467
-
hereafter acquired, that would be infringed by some manner, permitted
468
-
by this License, of making, using, or selling its contributor version,
469
-
but do not include claims that would be infringed only as a
470
-
consequence of further modification of the contributor version. For
471
-
purposes of this definition, "control" includes the right to grant
472
-
patent sublicenses in a manner consistent with the requirements of
473
-
this License.
72
+
a) Give prominent notice with each copy of the object code that the
73
+
Library is used in it and that the Library and its use are
74
+
covered by this License.
474
75
475
-
Each contributor grants you a non-exclusive, worldwide, royalty-free
476
-
patent license under the contributor's essential patent claims, to
477
-
make, use, sell, offer for sale, import and otherwise run, modify and
478
-
propagate the contents of its contributor version.
76
+
b) Accompany the object code with a copy of the GNU GPL and this license
77
+
document.
479
78
480
-
In the following three paragraphs, a "patent license" is any express
481
-
agreement or commitment, however denominated, not to enforce a patent
482
-
(such as an express permission to practice a patent or covenant not to
483
-
sue for patent infringement). To "grant" such a patent license to a
484
-
party means to make such an agreement or commitment not to enforce a
485
-
patent against the party.
79
+
4. Combined Works.
486
80
487
-
If you convey a covered work, knowingly relying on a patent license,
488
-
and the Corresponding Source of the work is not available for anyone
489
-
to copy, free of charge and under the terms of this License, through a
490
-
publicly available network server or other readily accessible means,
491
-
then you must either (1) cause the Corresponding Source to be so
492
-
available, or (2) arrange to deprive yourself of the benefit of the
493
-
patent license for this particular work, or (3) arrange, in a manner
494
-
consistent with the requirements of this License, to extend the patent
495
-
license to downstream recipients. "Knowingly relying" means you have
496
-
actual knowledge that, but for the patent license, your conveying the
497
-
covered work in a country, or your recipient's use of the covered work
498
-
in a country, would infringe one or more identifiable patents in that
499
-
country that you have reason to believe are valid.
81
+
You may convey a Combined Work under terms of your choice that,
82
+
taken together, effectively do not restrict modification of the
83
+
portions of the Library contained in the Combined Work and reverse
84
+
engineering for debugging such modifications, if you also do each of
85
+
the following:
500
86
501
-
If, pursuant to or in connection with a single transaction or
502
-
arrangement, you convey, or propagate by procuring conveyance of, a
503
-
covered work, and grant a patent license to some of the parties
504
-
receiving the covered work authorizing them to use, propagate, modify
505
-
or convey a specific copy of the covered work, then the patent license
506
-
you grant is automatically extended to all recipients of the covered
507
-
work and works based on it.
87
+
a) Give prominent notice with each copy of the Combined Work that
88
+
the Library is used in it and that the Library and its use are
89
+
covered by this License.
508
90
509
-
A patent license is "discriminatory" if it does not include within
510
-
the scope of its coverage, prohibits the exercise of, or is
511
-
conditioned on the non-exercise of one or more of the rights that are
512
-
specifically granted under this License. You may not convey a covered
513
-
work if you are a party to an arrangement with a third party that is
514
-
in the business of distributing software, under which you make payment
515
-
to the third party based on the extent of your activity of conveying
516
-
the work, and under which the third party grants, to any of the
517
-
parties who would receive the covered work from you, a discriminatory
518
-
patent license (a) in connection with copies of the covered work
519
-
conveyed by you (or copies made from those copies), or (b) primarily
520
-
for and in connection with specific products or compilations that
521
-
contain the covered work, unless you entered into that arrangement,
522
-
or that patent license was granted, prior to 28 March 2007.
91
+
b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+
document.
523
93
524
-
Nothing in this License shall be construed as excluding or limiting
525
-
any implied license or other defenses to infringement that may
526
-
otherwise be available to you under applicable patent law.
527
-
528
-
12. No Surrender of Others' Freedom.
529
-
530
-
If conditions are imposed on you (whether by court order, agreement or
531
-
otherwise) that contradict the conditions of this License, they do not
532
-
excuse you from the conditions of this License. If you cannot convey a
533
-
covered work so as to satisfy simultaneously your obligations under this
534
-
License and any other pertinent obligations, then as a consequence you may
535
-
not convey it at all. For example, if you agree to terms that obligate you
536
-
to collect a royalty for further conveying from those to whom you convey
537
-
the Program, the only way you could satisfy both those terms and this
538
-
License would be to refrain entirely from conveying the Program.
539
-
540
-
13. Remote Network Interaction; Use with the GNU General Public License.
541
-
542
-
Notwithstanding any other provision of this License, if you modify the
543
-
Program, your modified version must prominently offer all users
544
-
interacting with it remotely through a computer network (if your version
545
-
supports such interaction) an opportunity to receive the Corresponding
546
-
Source of your version by providing access to the Corresponding Source
547
-
from a network server at no charge, through some standard or customary
548
-
means of facilitating copying of software. This Corresponding Source
549
-
shall include the Corresponding Source for any work covered by version 3
550
-
of the GNU General Public License that is incorporated pursuant to the
551
-
following paragraph.
552
-
553
-
Notwithstanding any other provision of this License, you have
554
-
permission to link or combine any covered work with a work licensed
555
-
under version 3 of the GNU General Public License into a single
556
-
combined work, and to convey the resulting work. The terms of this
557
-
License will continue to apply to the part which is the covered work,
558
-
but the work with which it is combined will remain governed by version
559
-
3 of the GNU General Public License.
560
-
561
-
14. Revised Versions of this License.
562
-
563
-
The Free Software Foundation may publish revised and/or new versions of
564
-
the GNU Affero General Public License from time to time. Such new versions
565
-
will be similar in spirit to the present version, but may differ in detail to
566
-
address new problems or concerns.
94
+
c) For a Combined Work that displays copyright notices during
95
+
execution, include the copyright notice for the Library among
96
+
these notices, as well as a reference directing the user to the
97
+
copies of the GNU GPL and this license document.
567
98
568
-
Each version is given a distinguishing version number. If the
569
-
Program specifies that a certain numbered version of the GNU Affero General
570
-
Public License "or any later version" applies to it, you have the
571
-
option of following the terms and conditions either of that numbered
572
-
version or of any later version published by the Free Software
573
-
Foundation. If the Program does not specify a version number of the
574
-
GNU Affero General Public License, you may choose any version ever published
575
-
by the Free Software Foundation.
576
-
577
-
If the Program specifies that a proxy can decide which future
578
-
versions of the GNU Affero General Public License can be used, that proxy's
579
-
public statement of acceptance of a version permanently authorizes you
580
-
to choose that version for the Program.
581
-
582
-
Later license versions may give you additional or different
583
-
permissions. However, no additional obligations are imposed on any
584
-
author or copyright holder as a result of your choosing to follow a
585
-
later version.
586
-
587
-
15. Disclaimer of Warranty.
588
-
589
-
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590
-
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591
-
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592
-
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593
-
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594
-
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595
-
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596
-
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597
-
598
-
16. Limitation of Liability.
599
-
600
-
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601
-
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602
-
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603
-
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604
-
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605
-
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606
-
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607
-
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608
-
SUCH DAMAGES.
609
-
610
-
17. Interpretation of Sections 15 and 16.
611
-
612
-
If the disclaimer of warranty and limitation of liability provided
613
-
above cannot be given local legal effect according to their terms,
614
-
reviewing courts shall apply local law that most closely approximates
615
-
an absolute waiver of all civil liability in connection with the
616
-
Program, unless a warranty or assumption of liability accompanies a
617
-
copy of the Program in return for a fee.
99
+
d) Do one of the following:
618
100
619
-
END OF TERMS AND CONDITIONS
101
+
0) Convey the Minimal Corresponding Source under the terms of this
102
+
License, and the Corresponding Application Code in a form
103
+
suitable for, and under terms that permit, the user to
104
+
recombine or relink the Application with a modified version of
105
+
the Linked Version to produce a modified Combined Work, in the
106
+
manner specified by section 6 of the GNU GPL for conveying
107
+
Corresponding Source.
620
108
621
-
How to Apply These Terms to Your New Programs
109
+
1) Use a suitable shared library mechanism for linking with the
110
+
Library. A suitable mechanism is one that (a) uses at run time
111
+
a copy of the Library already present on the user's computer
112
+
system, and (b) will operate properly with a modified version
113
+
of the Library that is interface-compatible with the Linked
114
+
Version.
622
115
623
-
If you develop a new program, and you want it to be of the greatest
624
-
possible use to the public, the best way to achieve this is to make it
625
-
free software which everyone can redistribute and change under these terms.
116
+
e) Provide Installation Information, but only if you would otherwise
117
+
be required to provide such information under section 6 of the
118
+
GNU GPL, and only to the extent that such information is
119
+
necessary to install and execute a modified version of the
120
+
Combined Work produced by recombining or relinking the
121
+
Application with a modified version of the Linked Version. (If
122
+
you use option 4d0, the Installation Information must accompany
123
+
the Minimal Corresponding Source and Corresponding Application
124
+
Code. If you use option 4d1, you must provide the Installation
125
+
Information in the manner specified by section 6 of the GNU GPL
126
+
for conveying Corresponding Source.)
626
127
627
-
To do so, attach the following notices to the program. It is safest
628
-
to attach them to the start of each source file to most effectively
629
-
state the exclusion of warranty; and each file should have at least
630
-
the "copyright" line and a pointer to where the full notice is found.
128
+
5. Combined Libraries.
631
129
632
-
<one line to give the program's name and a brief idea of what it does.>
633
-
Copyright (C) <year> <name of author>
130
+
You may place library facilities that are a work based on the
131
+
Library side by side in a single library together with other library
132
+
facilities that are not Applications and are not covered by this
133
+
License, and convey such a combined library under terms of your
134
+
choice, if you do both of the following:
634
135
635
-
This program is free software: you can redistribute it and/or modify
636
-
it under the terms of the GNU Affero General Public License as published by
637
-
the Free Software Foundation, either version 3 of the License, or
638
-
(at your option) any later version.
136
+
a) Accompany the combined library with a copy of the same work based
137
+
on the Library, uncombined with any other library facilities,
138
+
conveyed under the terms of this License.
639
139
640
-
This program is distributed in the hope that it will be useful,
641
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
642
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643
-
GNU Affero General Public License for more details.
140
+
b) Give prominent notice with the combined library that part of it
141
+
is a work based on the Library, and explaining where to find the
142
+
accompanying uncombined form of the same work.
644
143
645
-
You should have received a copy of the GNU Affero General Public License
646
-
along with this program. If not, see <https://www.gnu.org/licenses/>.
144
+
6. Revised Versions of the GNU Lesser General Public License.
647
145
648
-
Also add information on how to contact you by electronic and paper mail.
146
+
The Free Software Foundation may publish revised and/or new versions
147
+
of the GNU Lesser General Public License from time to time. Such new
148
+
versions will be similar in spirit to the present version, but may
149
+
differ in detail to address new problems or concerns.
649
150
650
-
If your software can interact with users remotely through a computer
651
-
network, you should also make sure that it provides a way for users to
652
-
get its source. For example, if your program is a web application, its
653
-
interface could display a "Source" link that leads users to an archive
654
-
of the code. There are many ways you could offer source, and different
655
-
solutions will be better for different programs; see section 13 for the
656
-
specific requirements.
151
+
Each version is given a distinguishing version number. If the
152
+
Library as you received it specifies that a certain numbered version
153
+
of the GNU Lesser General Public License "or any later version"
154
+
applies to it, you have the option of following the terms and
155
+
conditions either of that published version or of any later version
156
+
published by the Free Software Foundation. If the Library as you
157
+
received it does not specify a version number of the GNU Lesser
158
+
General Public License, you may choose any version of the GNU Lesser
159
+
General Public License ever published by the Free Software Foundation.
657
160
658
-
You should also get your employer (if you work as a programmer) or school,
659
-
if any, to sign a "copyright disclaimer" for the program, if necessary.
660
-
For more information on this, and how to apply and follow the GNU AGPL, see
661
-
<https://www.gnu.org/licenses/>.
161
+
If the Library as you received it specifies that a proxy can decide
162
+
whether future versions of the GNU Lesser General Public License shall
163
+
apply, that proxy's public statement of acceptance of any version is
164
+
permanent authorization for you to choose that version for the
165
+
Library.
+16
-5
README.md
+16
-5
README.md
···
1
1
<h3 align="center">
2
-
<img src="./img/wordmark.png" alt="moonlight" />
2
+
<picture>
3
+
<source media="(prefers-color-scheme: dark)" srcset="./img/wordmark-light.png">
4
+
<source media="(prefers-color-scheme: light)" srcset="./img/wordmark.png">
5
+
<img src="./img/wordmark.png" alt="moonlight" />
6
+
</picture>
3
7
4
-
<a href="https://discord.gg/FdZBTFCP6F">Discord server</a>
8
+
<a href="https://moonlight-mod.github.io/using/install">Install</a>
9
+
\- <a href="https://moonlight-mod.github.io/ext-dev/getting-started">Docs</a>
10
+
\- <a href="https://discord.gg/FdZBTFCP6F">Discord server</a>
5
11
\- <a href="https://github.com/moonlight-mod/moonlight">GitHub</a>
6
-
\- <a href="https://moonlight-mod.github.io/">Docs</a>
7
12
8
13
<hr />
14
+
15
+
<picture>
16
+
<source media="(prefers-color-scheme: dark)" srcset="https://moonlight-mod.github.io/moonbase.png">
17
+
<source media="(prefers-color-scheme: light)" srcset="https://moonlight-mod.github.io/moonbase-light.png">
18
+
<img src="https://moonlight-mod.github.io/moonbase.png" alt="A screenshot of Moonbase, the moonlight UI" />
19
+
</picture>
9
20
</h3>
10
21
11
22
**moonlight** is yet another Discord client mod, focused on providing a decent user and developer experience.
12
23
13
24
moonlight is heavily inspired by hh3 (a private client mod) and the projects before it that it is inspired by, namely EndPwn. All core code is original or used with permission from their respective authors where not copyleft.
14
25
15
-
**_This is an experimental passion project._** moonlight was not created out of malicious intent nor intended to seriously compete with other mods. Anything and everything is subject to change.
26
+
moonlight is a **_passion project_** - things may break from time to time, but we try our best to keep things working in a timely manner.
16
27
17
-
moonlight is licensed under the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html) (`AGPL-3.0-or-later`). See [the documentation](https://moonlight-mod.github.io/) for more information.
28
+
moonlight is licensed under the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.html) (`LGPL-3.0-or-later`). See [the documentation](https://moonlight-mod.github.io/) for more information.
+141
-43
build.mjs
+141
-43
build.mjs
···
13
13
14
14
const prod = process.env.NODE_ENV === "production";
15
15
const watch = process.argv.includes("--watch");
16
+
const browser = process.argv.includes("--browser");
17
+
const mv2 = process.argv.includes("--mv2");
18
+
const clean = process.argv.includes("--clean");
19
+
20
+
const buildBranch = process.env.MOONLIGHT_BRANCH ?? "dev";
21
+
const buildVersion = process.env.MOONLIGHT_VERSION ?? "dev";
16
22
17
23
const external = [
18
24
"electron",
19
25
"fs",
20
26
"path",
21
27
"module",
22
-
"events",
23
-
"original-fs", // wtf asar?
28
+
"discord", // mappings
24
29
25
30
// Silence an esbuild warning
26
31
"./node-preload.js"
···
65
70
name: "build-log",
66
71
setup(build) {
67
72
build.onEnd((result) => {
68
-
console.log(
69
-
`[${timeFormatter.format(new Date())}] [${tag}] build finished`
70
-
);
73
+
console.log(`[${timeFormatter.format(new Date())}] [${tag}] build finished`);
71
74
});
72
75
}
73
76
});
74
77
75
78
async function build(name, entry) {
76
-
const outfile = path.join("./dist", name + ".js");
79
+
let outfile = path.join("./dist", name + ".js");
80
+
const browserDir = mv2 ? "browser-mv2" : "browser";
81
+
if (name === "browser") outfile = path.join("./dist", browserDir, "index.js");
77
82
78
83
const dropLabels = [];
79
-
if (name !== "injector") dropLabels.push("injector");
80
-
if (name !== "node-preload") dropLabels.push("nodePreload");
81
-
if (name !== "web-preload") dropLabels.push("webPreload");
84
+
const labels = {
85
+
injector: ["injector"],
86
+
nodePreload: ["node-preload"],
87
+
webPreload: ["web-preload"],
88
+
browser: ["browser"],
89
+
90
+
webTarget: ["web-preload", "browser"],
91
+
nodeTarget: ["node-preload", "injector"]
92
+
};
93
+
for (const [label, targets] of Object.entries(labels)) {
94
+
if (!targets.includes(name)) {
95
+
dropLabels.push(label);
96
+
}
97
+
}
82
98
83
99
const define = {
84
100
MOONLIGHT_ENV: `"${name}"`,
85
-
MOONLIGHT_PROD: prod.toString()
101
+
MOONLIGHT_PROD: prod.toString(),
102
+
MOONLIGHT_BRANCH: `"${buildBranch}"`,
103
+
MOONLIGHT_VERSION: `"${buildVersion}"`
86
104
};
87
105
88
-
for (const iterName of Object.keys(config)) {
106
+
for (const iterName of ["injector", "node-preload", "web-preload", "browser"]) {
89
107
const snake = iterName.replace(/-/g, "_").toUpperCase();
90
108
define[`MOONLIGHT_${snake}`] = (name === iterName).toString();
91
109
}
···
93
111
const nodeDependencies = ["glob"];
94
112
const ignoredExternal = name === "web-preload" ? nodeDependencies : [];
95
113
114
+
const plugins = [deduplicatedLogging, taggedBuildLog(name)];
115
+
if (name === "browser") {
116
+
plugins.push(
117
+
copyStaticFiles({
118
+
src: mv2 ? "./packages/browser/manifestv2.json" : "./packages/browser/manifest.json",
119
+
dest: `./dist/${browserDir}/manifest.json`
120
+
})
121
+
);
122
+
123
+
if (!mv2) {
124
+
plugins.push(
125
+
copyStaticFiles({
126
+
src: "./packages/browser/modifyResponseHeaders.json",
127
+
dest: `./dist/${browserDir}/modifyResponseHeaders.json`
128
+
})
129
+
);
130
+
plugins.push(
131
+
copyStaticFiles({
132
+
src: "./packages/browser/blockLoading.json",
133
+
dest: `./dist/${browserDir}/blockLoading.json`
134
+
})
135
+
);
136
+
}
137
+
138
+
plugins.push(
139
+
copyStaticFiles({
140
+
src: mv2 ? "./packages/browser/src/background-mv2.js" : "./packages/browser/src/background.js",
141
+
dest: `./dist/${browserDir}/background.js`
142
+
})
143
+
);
144
+
}
145
+
96
146
/** @type {import("esbuild").BuildOptions} */
97
147
const esbuildConfig = {
98
148
entryPoints: [entry],
99
149
outfile,
100
150
101
-
format: "cjs",
102
-
platform: name === "web-preload" ? "browser" : "node",
151
+
format: "iife",
152
+
globalName: "module.exports",
153
+
154
+
platform: ["web-preload", "browser"].includes(name) ? "browser" : "node",
103
155
104
156
treeShaking: true,
105
157
bundle: true,
···
112
164
dropLabels,
113
165
114
166
logLevel: "silent",
115
-
plugins: [deduplicatedLogging, taggedBuildLog(name)]
167
+
plugins,
168
+
169
+
// https://github.com/evanw/esbuild/issues/3944
170
+
footer:
171
+
name === "web-preload"
172
+
? {
173
+
js: `\n//# sourceURL=${name}.js`
174
+
}
175
+
: undefined
116
176
};
117
177
178
+
if (name === "browser") {
179
+
const coreExtensionsJson = {};
180
+
181
+
function readDir(dir) {
182
+
const files = fs.readdirSync(dir);
183
+
for (const file of files) {
184
+
const filePath = dir + "/" + file;
185
+
const normalizedPath = filePath.replace("./dist/core-extensions/", "");
186
+
if (fs.statSync(filePath).isDirectory()) {
187
+
readDir(filePath);
188
+
} else {
189
+
coreExtensionsJson[normalizedPath] = fs.readFileSync(filePath, "utf8");
190
+
}
191
+
}
192
+
}
193
+
194
+
readDir("./dist/core-extensions");
195
+
196
+
esbuildConfig.banner = {
197
+
js: `window._moonlight_coreExtensionsStr = ${JSON.stringify(JSON.stringify(coreExtensionsJson))};`
198
+
};
199
+
}
200
+
118
201
if (watch) {
119
202
const ctx = await esbuild.context(esbuildConfig);
120
203
await ctx.watch();
···
123
206
}
124
207
}
125
208
126
-
async function buildExt(ext, side, copyManifest, fileExt) {
209
+
async function buildExt(ext, side, fileExt) {
127
210
const outdir = path.join("./dist", "core-extensions", ext);
128
211
if (!fs.existsSync(outdir)) {
129
212
fs.mkdirSync(outdir, { recursive: true });
130
213
}
131
214
132
-
const entryPoints = [
133
-
`packages/core-extensions/src/${ext}/${side}.${fileExt}`
134
-
];
215
+
const entryPoints = [`packages/core-extensions/src/${ext}/${side}.${fileExt}`];
135
216
136
217
const wpModulesDir = `packages/core-extensions/src/${ext}/webpackModules`;
137
218
if (fs.existsSync(wpModulesDir) && side === "index") {
138
-
const wpModules = fs.readdirSync(wpModulesDir);
139
-
for (const wpModule of wpModules) {
140
-
entryPoints.push(
141
-
`packages/core-extensions/src/${ext}/webpackModules/${wpModule}`
142
-
);
219
+
const wpModules = fs.opendirSync(wpModulesDir);
220
+
for await (const wpModule of wpModules) {
221
+
if (wpModule.isFile()) {
222
+
entryPoints.push(`packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}`);
223
+
} else {
224
+
for (const fileExt of ["ts", "tsx"]) {
225
+
const path = `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}/index.${fileExt}`;
226
+
if (fs.existsSync(path)) {
227
+
entryPoints.push({
228
+
in: path,
229
+
out: `webpackModules/${wpModule.name}`
230
+
});
231
+
}
232
+
}
233
+
}
143
234
}
144
235
}
145
236
···
156
247
}
157
248
};
158
249
250
+
const styleInput = `packages/core-extensions/src/${ext}/style.css`;
251
+
const styleOutput = `dist/core-extensions/${ext}/style.css`;
252
+
159
253
const esbuildConfig = {
160
254
entryPoints,
161
255
outdir,
162
256
163
-
format: "cjs",
257
+
format: "iife",
258
+
globalName: "module.exports",
164
259
platform: "node",
165
260
166
261
treeShaking: true,
···
174
269
},
175
270
logLevel: "silent",
176
271
plugins: [
177
-
...(copyManifest
272
+
copyStaticFiles({
273
+
src: `./packages/core-extensions/src/${ext}/manifest.json`,
274
+
dest: `./dist/core-extensions/${ext}/manifest.json`
275
+
}),
276
+
...(fs.existsSync(styleInput)
178
277
? [
179
278
copyStaticFiles({
180
-
src: `./packages/core-extensions/src/${ext}/manifest.json`,
181
-
dest: `./dist/core-extensions/${ext}/manifest.json`
279
+
src: styleInput,
280
+
dest: styleOutput
182
281
})
183
282
]
184
283
: []),
···
198
297
199
298
const promises = [];
200
299
201
-
for (const [name, entry] of Object.entries(config)) {
202
-
promises.push(build(name, entry));
203
-
}
204
-
205
-
const coreExtensions = fs.readdirSync("./packages/core-extensions/src");
206
-
for (const ext of coreExtensions) {
207
-
let copiedManifest = false;
300
+
if (clean) {
301
+
fs.rmSync("./dist", { recursive: true, force: true });
302
+
} else if (browser) {
303
+
build("browser", "packages/browser/src/index.ts");
304
+
} else {
305
+
for (const [name, entry] of Object.entries(config)) {
306
+
promises.push(build(name, entry));
307
+
}
208
308
209
-
for (const fileExt of ["ts", "tsx"]) {
210
-
for (const type of ["index", "node", "host"]) {
211
-
if (
212
-
fs.existsSync(
213
-
`./packages/core-extensions/src/${ext}/${type}.${fileExt}`
214
-
)
215
-
) {
216
-
promises.push(buildExt(ext, type, !copiedManifest, fileExt));
217
-
copiedManifest = true;
309
+
const coreExtensions = fs.readdirSync("./packages/core-extensions/src");
310
+
for (const ext of coreExtensions) {
311
+
for (const fileExt of ["ts", "tsx"]) {
312
+
for (const type of ["index", "node", "host"]) {
313
+
if (fs.existsSync(`./packages/core-extensions/src/${ext}/${type}.${fileExt}`)) {
314
+
promises.push(buildExt(ext, type, fileExt));
315
+
}
218
316
}
219
317
}
220
318
}
-1
env.d.ts
-1
env.d.ts
···
1
-
/// <reference types="./packages/types/src/index" />
+25
eslint.config.mjs
+25
eslint.config.mjs
···
1
+
import config from "@moonlight-mod/eslint-config";
2
+
3
+
export default [
4
+
...config,
5
+
{
6
+
rules: {
7
+
// baseUrl being set to ./packages/ makes language server suggest "types/src" instead of "@moonlight-mod/types"
8
+
"no-restricted-imports": [
9
+
"error",
10
+
{
11
+
patterns: [
12
+
{
13
+
group: ["types/*"],
14
+
message: "Use @moonlight-mod/types instead"
15
+
},
16
+
{
17
+
group: ["core/*"],
18
+
message: "Use @moonlight-mod/core instead"
19
+
}
20
+
]
21
+
}
22
+
]
23
+
}
24
+
}
25
+
];
+61
flake.lock
+61
flake.lock
···
1
+
{
2
+
"nodes": {
3
+
"flake-utils": {
4
+
"inputs": {
5
+
"systems": "systems"
6
+
},
7
+
"locked": {
8
+
"lastModified": 1701680307,
9
+
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
10
+
"owner": "numtide",
11
+
"repo": "flake-utils",
12
+
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
13
+
"type": "github"
14
+
},
15
+
"original": {
16
+
"owner": "numtide",
17
+
"repo": "flake-utils",
18
+
"type": "github"
19
+
}
20
+
},
21
+
"nixpkgs": {
22
+
"locked": {
23
+
"lastModified": 1744232761,
24
+
"narHash": "sha256-gbl9hE39nQRpZaLjhWKmEu5ejtQsgI5TWYrIVVJn30U=",
25
+
"owner": "NixOS",
26
+
"repo": "nixpkgs",
27
+
"rev": "f675531bc7e6657c10a18b565cfebd8aa9e24c14",
28
+
"type": "github"
29
+
},
30
+
"original": {
31
+
"owner": "NixOS",
32
+
"ref": "nixos-unstable",
33
+
"repo": "nixpkgs",
34
+
"type": "github"
35
+
}
36
+
},
37
+
"root": {
38
+
"inputs": {
39
+
"flake-utils": "flake-utils",
40
+
"nixpkgs": "nixpkgs"
41
+
}
42
+
},
43
+
"systems": {
44
+
"locked": {
45
+
"lastModified": 1681028828,
46
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
47
+
"owner": "nix-systems",
48
+
"repo": "default",
49
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
50
+
"type": "github"
51
+
},
52
+
"original": {
53
+
"owner": "nix-systems",
54
+
"repo": "default",
55
+
"type": "github"
56
+
}
57
+
}
58
+
},
59
+
"root": "root",
60
+
"version": 7
61
+
}
+31
flake.nix
+31
flake.nix
···
1
+
{
2
+
description = "Yet another Discord mod";
3
+
4
+
inputs = {
5
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6
+
flake-utils.url = "github:numtide/flake-utils";
7
+
};
8
+
9
+
outputs = { self, nixpkgs, flake-utils }:
10
+
let overlay = import ./nix/overlay.nix { };
11
+
in flake-utils.lib.eachDefaultSystem (system:
12
+
let
13
+
pkgs = import nixpkgs {
14
+
inherit system;
15
+
config.allowUnfree = true;
16
+
overlays = [ overlay ];
17
+
};
18
+
in {
19
+
# Don't use these unless you're testing things
20
+
packages.default = pkgs.moonlight-mod;
21
+
packages.moonlight-mod = pkgs.moonlight-mod;
22
+
23
+
packages.discord = pkgs.discord;
24
+
packages.discord-ptb = pkgs.discord-ptb;
25
+
packages.discord-canary = pkgs.discord-canary;
26
+
packages.discord-development = pkgs.discord-development;
27
+
}) // {
28
+
overlays.default = overlay;
29
+
homeModules.default = ./nix/home-manager.nix;
30
+
};
31
+
}
img/wordmark-light.png
img/wordmark-light.png
This is a binary file and will not be displayed.
+57
nix/default.nix
+57
nix/default.nix
···
1
+
{
2
+
lib,
3
+
stdenv,
4
+
nodejs_22,
5
+
pnpm_10,
6
+
}:
7
+
8
+
stdenv.mkDerivation (finalAttrs: {
9
+
pname = "moonlight";
10
+
version = (builtins.fromJSON (builtins.readFile ./../package.json)).version;
11
+
12
+
src = ./..;
13
+
14
+
outputs = [ "out" "firefox" ];
15
+
16
+
nativeBuildInputs = [
17
+
nodejs_22
18
+
pnpm_10.configHook
19
+
];
20
+
21
+
pnpmDeps = pnpm_10.fetchDeps {
22
+
inherit (finalAttrs) pname version src;
23
+
hash = "sha256-I+zRCUqJabpGJRFBGW0NrM9xzyzeCjioF54zlCpynBU=";
24
+
};
25
+
26
+
env = {
27
+
NODE_ENV = "production";
28
+
MOONLIGHT_VERSION = "v${finalAttrs.version}";
29
+
};
30
+
31
+
buildPhase = ''
32
+
runHook preBuild
33
+
34
+
pnpm run build
35
+
pnpm run browser-mv2
36
+
37
+
runHook postBuild
38
+
'';
39
+
40
+
installPhase = ''
41
+
runHook preInstall
42
+
43
+
cp -r dist $out
44
+
45
+
mkdir -p $firefox/share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/
46
+
mv $out/browser-mv2 $firefox/share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/{0fb6d66f-f22d-4555-a87b-34ef4bea5e2a}
47
+
48
+
runHook postInstall
49
+
'';
50
+
51
+
meta = with lib; {
52
+
description = "Yet another Discord mod";
53
+
homepage = "https://moonlight-mod.github.io/";
54
+
license = licenses.lgpl3;
55
+
maintainers = with maintainers; [ notnite ];
56
+
};
57
+
})
+56
nix/home-manager.nix
+56
nix/home-manager.nix
···
1
+
{ config, lib, pkgs, ... }:
2
+
3
+
let cfg = config.programs.moonlight-mod;
4
+
in {
5
+
options.programs.moonlight-mod = {
6
+
enable = lib.mkEnableOption "Yet another Discord mod";
7
+
8
+
configs = let
9
+
# TODO: type this
10
+
type = lib.types.nullOr (lib.types.attrs);
11
+
default = null;
12
+
in {
13
+
stable = lib.mkOption {
14
+
inherit type default;
15
+
description = "Configuration for Discord Stable";
16
+
};
17
+
18
+
ptb = lib.mkOption {
19
+
inherit type default;
20
+
description = "Configuration for Discord PTB";
21
+
};
22
+
23
+
canary = lib.mkOption {
24
+
inherit type default;
25
+
description = "Configuration for Discord Canary";
26
+
};
27
+
28
+
development = lib.mkOption {
29
+
inherit type default;
30
+
description = "Configuration for Discord Development";
31
+
};
32
+
};
33
+
};
34
+
35
+
config = lib.mkIf cfg.enable {
36
+
xdg.configFile."moonlight-mod/stable.json" =
37
+
lib.mkIf (cfg.configs.stable != null) {
38
+
text = builtins.toJSON cfg.configs.stable;
39
+
};
40
+
41
+
xdg.configFile."moonlight-mod/ptb.json" =
42
+
lib.mkIf (cfg.configs.ptb != null) {
43
+
text = builtins.toJSON cfg.configs.ptb;
44
+
};
45
+
46
+
xdg.configFile."moonlight-mod/canary.json" =
47
+
lib.mkIf (cfg.configs.canary != null) {
48
+
text = builtins.toJSON cfg.configs.canary;
49
+
};
50
+
51
+
xdg.configFile."moonlight-mod/development.json" =
52
+
lib.mkIf (cfg.configs.development != null) {
53
+
text = builtins.toJSON cfg.configs.development;
54
+
};
55
+
};
56
+
}
+57
nix/overlay.nix
+57
nix/overlay.nix
···
1
+
{ ... }:
2
+
3
+
let
4
+
nameTable = {
5
+
discord = "Discord";
6
+
discord-ptb = "DiscordPTB";
7
+
discord-canary = "DiscordCanary";
8
+
discord-development = "DiscordDevelopment";
9
+
};
10
+
11
+
darwinNameTable = {
12
+
discord = "Discord";
13
+
discord-ptb = "Discord PTB";
14
+
discord-canary = "Discord Canary";
15
+
discord-development = "Discord Development";
16
+
};
17
+
18
+
mkOverride = prev: moonlight: name:
19
+
let discord = prev.${name};
20
+
in discord.overrideAttrs (old: {
21
+
installPhase = let
22
+
folderName = nameTable.${name};
23
+
darwinFolderName = darwinNameTable.${name};
24
+
25
+
injected = ''
26
+
require("${moonlight}/injector").inject(
27
+
require("path").join(__dirname, "../_app.asar")
28
+
);
29
+
'';
30
+
31
+
packageJson = ''
32
+
{"name":"${name}","main":"./injector.js","private":true}
33
+
'';
34
+
35
+
in old.installPhase + "\n" + ''
36
+
resources="$out/opt/${folderName}/resources"
37
+
if [ ! -d "$resources" ]; then
38
+
resources="$out/Applications/${darwinFolderName}.app/Contents/Resources"
39
+
fi
40
+
41
+
mv "$resources/app.asar" "$resources/_app.asar"
42
+
mkdir -p "$resources/app"
43
+
44
+
cat > "$resources/app/injector.js" <<EOF
45
+
${injected}
46
+
EOF
47
+
48
+
echo '${packageJson}' > "$resources/app/package.json"
49
+
'';
50
+
});
51
+
in final: prev: rec {
52
+
moonlight-mod = final.callPackage ./default.nix { };
53
+
discord = mkOverride prev moonlight-mod "discord";
54
+
discord-ptb = mkOverride prev moonlight-mod "discord-ptb";
55
+
discord-canary = mkOverride prev moonlight-mod "discord-canary";
56
+
discord-development = mkOverride prev moonlight-mod "discord-development";
57
+
}
+26
-15
package.json
+26
-15
package.json
···
1
1
{
2
2
"name": "moonlight",
3
-
"version": "1.0.7",
3
+
"version": "1.3.14",
4
+
"packageManager": "pnpm@10.7.1",
4
5
"description": "Yet another Discord mod",
6
+
"license": "LGPL-3.0-or-later",
5
7
"homepage": "https://moonlight-mod.github.io/",
6
8
"repository": {
7
9
"type": "git",
···
9
11
},
10
12
"bugs": {
11
13
"url": "https://github.com/moonlight-mod/moonlight/issues"
14
+
},
15
+
"engineStrict": true,
16
+
"engines": {
17
+
"node": ">=22",
18
+
"pnpm": ">=10",
19
+
"npm": "pnpm",
20
+
"yarn": "pnpm"
12
21
},
13
22
"scripts": {
14
23
"build": "node build.mjs",
15
24
"dev": "node build.mjs --watch",
25
+
"clean": "node build.mjs --clean",
26
+
"browser": "node build.mjs --browser",
27
+
"browser-mv2": "node build.mjs --browser --mv2",
16
28
"lint": "eslint packages",
17
-
"lint:fix": "eslint packages",
18
-
"lint:report": "eslint --output-file eslint_report.json --format json packages",
29
+
"lint:fix": "pnpm lint --fix",
30
+
"lint:report": "pnpm lint --output-file eslint_report.json --format json",
19
31
"typecheck": "tsc --noEmit",
20
32
"check": "pnpm run lint && pnpm run typecheck",
21
-
"prepare": "husky install"
33
+
"prepare": "husky install",
34
+
"updates": "pnpm taze -r"
22
35
},
23
36
"devDependencies": {
24
-
"@typescript-eslint/eslint-plugin": "^6.13.2",
25
-
"@typescript-eslint/parser": "^6.13.2",
26
-
"esbuild": "^0.19.3",
27
-
"esbuild-copy-static-files": "^0.1.0",
28
-
"eslint": "^8.55.0",
29
-
"eslint-config-prettier": "^9.1.0",
30
-
"eslint-plugin-prettier": "^5.0.1",
31
-
"eslint-plugin-react": "^7.33.2",
32
-
"husky": "^8.0.3",
33
-
"prettier": "^3.1.0",
34
-
"typescript": "^5.3.2"
37
+
"@moonlight-mod/eslint-config": "catalog:dev",
38
+
"@types/node": "catalog:dev",
39
+
"esbuild": "catalog:dev",
40
+
"esbuild-copy-static-files": "catalog:dev",
41
+
"eslint": "catalog:dev",
42
+
"husky": "catalog:dev",
43
+
"prettier": "catalog:dev",
44
+
"taze": "catalog:dev",
45
+
"typescript": "catalog:dev"
35
46
}
36
47
}
+14
packages/browser/blockLoading.json
+14
packages/browser/blockLoading.json
+46
packages/browser/manifest.json
+46
packages/browser/manifest.json
···
1
+
{
2
+
"$schema": "https://json.schemastore.org/chrome-manifest",
3
+
"manifest_version": 3,
4
+
"name": "moonlight",
5
+
"description": "Yet another Discord mod",
6
+
"version": "1.3.14",
7
+
"permissions": ["declarativeNetRequestWithHostAccess", "webRequest", "scripting", "webNavigation"],
8
+
"host_permissions": [
9
+
"https://moonlight-mod.github.io/*",
10
+
"https://api.github.com/*",
11
+
"https://*.discord.com/*",
12
+
"https://*.discordapp.com/*"
13
+
],
14
+
"content_scripts": [
15
+
{
16
+
"js": ["index.js"],
17
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"],
18
+
"run_at": "document_start",
19
+
"world": "MAIN"
20
+
}
21
+
],
22
+
"declarative_net_request": {
23
+
"rule_resources": [
24
+
{
25
+
"id": "modifyResponseHeaders",
26
+
"enabled": true,
27
+
"path": "modifyResponseHeaders.json"
28
+
},
29
+
{
30
+
"id": "blockLoading",
31
+
"enabled": true,
32
+
"path": "blockLoading.json"
33
+
}
34
+
]
35
+
},
36
+
"background": {
37
+
"service_worker": "background.js",
38
+
"type": "module"
39
+
},
40
+
"web_accessible_resources": [
41
+
{
42
+
"resources": ["index.js"],
43
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"]
44
+
}
45
+
]
46
+
}
+33
packages/browser/manifestv2.json
+33
packages/browser/manifestv2.json
···
1
+
{
2
+
"$schema": "https://json.schemastore.org/chrome-manifest",
3
+
"manifest_version": 2,
4
+
"name": "moonlight",
5
+
"description": "Yet another Discord mod",
6
+
"version": "1.3.14",
7
+
"permissions": [
8
+
"webRequest",
9
+
"webRequestBlocking",
10
+
"scripting",
11
+
"webNavigation",
12
+
"https://*.discord.com/*",
13
+
"https://*.discordapp.com/*",
14
+
"https://moonlight-mod.github.io/*",
15
+
"https://api.github.com/*"
16
+
],
17
+
"background": {
18
+
"scripts": ["background.js"]
19
+
},
20
+
"content_scripts": [
21
+
{
22
+
"js": ["index.js"],
23
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"],
24
+
"run_at": "document_start",
25
+
"world": "MAIN"
26
+
}
27
+
],
28
+
"browser_specific_settings": {
29
+
"gecko": {
30
+
"id": "{0fb6d66f-f22d-4555-a87b-34ef4bea5e2a}"
31
+
}
32
+
}
33
+
}
+19
packages/browser/modifyResponseHeaders.json
+19
packages/browser/modifyResponseHeaders.json
···
1
+
[
2
+
{
3
+
"id": 1,
4
+
"priority": 2,
5
+
"action": {
6
+
"type": "modifyHeaders",
7
+
"responseHeaders": [
8
+
{
9
+
"header": "Content-Security-Policy",
10
+
"operation": "remove"
11
+
}
12
+
]
13
+
},
14
+
"condition": {
15
+
"resourceTypes": ["main_frame"],
16
+
"requestDomains": ["discord.com"]
17
+
}
18
+
}
19
+
]
+21
packages/browser/package.json
+21
packages/browser/package.json
···
1
+
{
2
+
"name": "@moonlight-mod/browser",
3
+
"private": true,
4
+
"engines": {
5
+
"node": ">=22",
6
+
"pnpm": ">=10",
7
+
"npm": "pnpm",
8
+
"yarn": "pnpm"
9
+
},
10
+
"dependencies": {
11
+
"@moonlight-mod/core": "workspace:*",
12
+
"@moonlight-mod/types": "workspace:*",
13
+
"@moonlight-mod/web-preload": "workspace:*",
14
+
"@zenfs/core": "catalog:prod",
15
+
"@zenfs/dom": "catalog:prod"
16
+
},
17
+
"engineStrict": true,
18
+
"devDependencies": {
19
+
"@types/chrome": "catalog:dev"
20
+
}
21
+
}
+84
packages/browser/src/background-mv2.js
+84
packages/browser/src/background-mv2.js
···
1
+
/* eslint-disable no-console */
2
+
/* eslint-disable no-undef */
3
+
4
+
const scriptUrls = ["web.", "sentry."];
5
+
let blockedScripts = new Set();
6
+
7
+
chrome.webRequest.onBeforeRequest.addListener(
8
+
async (details) => {
9
+
if (details.tabId === -1) return;
10
+
11
+
const url = new URL(details.url);
12
+
const hasUrl = scriptUrls.some((scriptUrl) => {
13
+
return (
14
+
details.url.includes(scriptUrl) &&
15
+
!url.searchParams.has("inj") &&
16
+
(url.host.endsWith("discord.com") || url.host.endsWith("discordapp.com"))
17
+
);
18
+
});
19
+
if (hasUrl) blockedScripts.add(details.url);
20
+
21
+
if (blockedScripts.size === scriptUrls.length) {
22
+
const blockedScriptsCopy = Array.from(blockedScripts);
23
+
blockedScripts.clear();
24
+
25
+
setTimeout(async () => {
26
+
console.log("Starting moonlight");
27
+
await chrome.scripting.executeScript({
28
+
target: { tabId: details.tabId },
29
+
world: "MAIN",
30
+
args: [blockedScriptsCopy],
31
+
func: async (blockedScripts) => {
32
+
console.log("Initializing moonlight");
33
+
try {
34
+
await window._moonlightBrowserInit();
35
+
} catch (e) {
36
+
console.error(e);
37
+
}
38
+
39
+
console.log("Readding scripts");
40
+
try {
41
+
const scripts = [...document.querySelectorAll("script")].filter(
42
+
(script) => script.src && blockedScripts.some((url) => url.includes(script.src))
43
+
);
44
+
45
+
blockedScripts.reverse();
46
+
for (const url of blockedScripts) {
47
+
if (url.includes("/sentry.")) continue;
48
+
49
+
const script = scripts.find((script) => url.includes(script.src));
50
+
const newScript = document.createElement("script");
51
+
for (const attr of script.attributes) {
52
+
if (attr.name === "src") attr.value += "?inj";
53
+
newScript.setAttribute(attr.name, attr.value);
54
+
}
55
+
script.remove();
56
+
document.documentElement.appendChild(newScript);
57
+
}
58
+
} catch (e) {
59
+
console.error(e);
60
+
}
61
+
}
62
+
});
63
+
}, 0);
64
+
}
65
+
66
+
if (hasUrl) return { cancel: true };
67
+
},
68
+
{
69
+
urls: ["https://*.discord.com/assets/*.js", "https://*.discordapp.com/assets/*.js"]
70
+
},
71
+
["blocking"]
72
+
);
73
+
74
+
chrome.webRequest.onHeadersReceived.addListener(
75
+
(details) => {
76
+
return {
77
+
responseHeaders: details.responseHeaders.filter(
78
+
(header) => header.name.toLowerCase() !== "content-security-policy"
79
+
)
80
+
};
81
+
},
82
+
{ urls: ["https://*.discord.com/*", "https://*.discordapp.com/*"] },
83
+
["blocking", "responseHeaders"]
84
+
);
+111
packages/browser/src/background.js
+111
packages/browser/src/background.js
···
1
+
/* eslint-disable no-console */
2
+
/* eslint-disable no-undef */
3
+
4
+
const scriptUrls = ["web.", "sentry."];
5
+
let blockedScripts = new Set();
6
+
7
+
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
8
+
const url = new URL(details.url);
9
+
if (
10
+
!url.searchParams.has("inj") &&
11
+
(url.hostname.endsWith("discord.com") || url.hostname.endsWith("discordapp.com"))
12
+
) {
13
+
console.log("Enabling block ruleset");
14
+
await chrome.declarativeNetRequest.updateEnabledRulesets({
15
+
enableRulesetIds: ["modifyResponseHeaders", "blockLoading"]
16
+
});
17
+
}
18
+
});
19
+
20
+
chrome.webRequest.onBeforeRequest.addListener(
21
+
async (details) => {
22
+
if (details.tabId === -1) return;
23
+
24
+
const url = new URL(details.url);
25
+
const hasUrl = scriptUrls.some((scriptUrl) => {
26
+
return (
27
+
details.url.includes(scriptUrl) &&
28
+
!url.searchParams.has("inj") &&
29
+
(url.hostname.endsWith("discord.com") || url.hostname.endsWith("discordapp.com"))
30
+
);
31
+
});
32
+
33
+
if (hasUrl) blockedScripts.add(details.url);
34
+
35
+
if (blockedScripts.size === scriptUrls.length) {
36
+
const blockedScriptsCopy = Array.from(blockedScripts);
37
+
blockedScripts.clear();
38
+
39
+
console.log("Running moonlight script");
40
+
try {
41
+
await chrome.scripting.executeScript({
42
+
target: { tabId: details.tabId },
43
+
world: "MAIN",
44
+
files: ["index.js"]
45
+
});
46
+
} catch (e) {
47
+
console.error(e);
48
+
}
49
+
50
+
console.log("Initializing moonlight");
51
+
try {
52
+
await chrome.scripting.executeScript({
53
+
target: { tabId: details.tabId },
54
+
world: "MAIN",
55
+
func: async () => {
56
+
try {
57
+
await window._moonlightBrowserInit();
58
+
} catch (e) {
59
+
console.error(e);
60
+
}
61
+
}
62
+
});
63
+
} catch (e) {
64
+
console.log(e);
65
+
}
66
+
67
+
console.log("Disabling block ruleset");
68
+
try {
69
+
await chrome.declarativeNetRequest.updateEnabledRulesets({
70
+
disableRulesetIds: ["blockLoading"],
71
+
enableRulesetIds: ["modifyResponseHeaders"]
72
+
});
73
+
} catch (e) {
74
+
console.error(e);
75
+
}
76
+
77
+
console.log("Readding scripts");
78
+
try {
79
+
await chrome.scripting.executeScript({
80
+
target: { tabId: details.tabId },
81
+
world: "MAIN",
82
+
args: [blockedScriptsCopy],
83
+
func: async (blockedScripts) => {
84
+
const scripts = [...document.querySelectorAll("script")].filter(
85
+
(script) => script.src && blockedScripts.some((url) => url.includes(script.src))
86
+
);
87
+
88
+
blockedScripts.reverse();
89
+
for (const url of blockedScripts) {
90
+
if (url.includes("/sentry.")) continue;
91
+
92
+
const script = scripts.find((script) => url.includes(script.src));
93
+
const newScript = document.createElement("script");
94
+
for (const attr of script.attributes) {
95
+
if (attr.name === "src") attr.value += "?inj";
96
+
newScript.setAttribute(attr.name, attr.value);
97
+
}
98
+
script.remove();
99
+
document.documentElement.appendChild(newScript);
100
+
}
101
+
}
102
+
});
103
+
} catch (e) {
104
+
console.error(e);
105
+
}
106
+
}
107
+
},
108
+
{
109
+
urls: ["*://*.discord.com/assets/*.js", "*://*.discordapp.com/assets/*.js"]
110
+
}
111
+
);
+161
packages/browser/src/index.ts
+161
packages/browser/src/index.ts
···
1
+
import "@moonlight-mod/web-preload";
2
+
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
3
+
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
4
+
import { getExtensions } from "@moonlight-mod/core/extension";
5
+
import { loadExtensions } from "@moonlight-mod/core/extension/loader";
6
+
import { MoonlightBranch, MoonlightNode } from "@moonlight-mod/types";
7
+
import { getConfig, getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
8
+
import { IndexedDB } from "@zenfs/dom";
9
+
import { configureSingle } from "@zenfs/core";
10
+
import * as fs from "@zenfs/core/promises";
11
+
import { NodeEventPayloads, NodeEventType } from "@moonlight-mod/types/core/event";
12
+
import { createEventEmitter } from "@moonlight-mod/core/util/event";
13
+
14
+
function getParts(path: string) {
15
+
if (path.startsWith("/")) path = path.substring(1);
16
+
return path.split("/");
17
+
}
18
+
19
+
window._moonlightBrowserInit = async () => {
20
+
delete window._moonlightBrowserInit;
21
+
22
+
// Set up a virtual filesystem with IndexedDB
23
+
await configureSingle({
24
+
backend: IndexedDB,
25
+
storeName: "moonlight-fs"
26
+
});
27
+
28
+
window.moonlightNodeSandboxed = {
29
+
fs: {
30
+
async readFile(path) {
31
+
return new Uint8Array(await fs.readFile(path));
32
+
},
33
+
async readFileString(path) {
34
+
const file = await this.readFile(path);
35
+
return new TextDecoder().decode(file);
36
+
},
37
+
async writeFile(path, data) {
38
+
await fs.writeFile(path, data);
39
+
},
40
+
async writeFileString(path, data) {
41
+
const file = new TextEncoder().encode(data);
42
+
await this.writeFile(path, file);
43
+
},
44
+
async unlink(path) {
45
+
await fs.unlink(path);
46
+
},
47
+
48
+
async readdir(path) {
49
+
return await fs.readdir(path);
50
+
},
51
+
async mkdir(path) {
52
+
const parts = getParts(path);
53
+
for (let i = 0; i < parts.length; i++) {
54
+
const path = this.join(...parts.slice(0, i + 1));
55
+
if (!(await this.exists(path))) await fs.mkdir(path);
56
+
}
57
+
},
58
+
59
+
async rmdir(path) {
60
+
const entries = await this.readdir(path);
61
+
62
+
for (const entry of entries) {
63
+
const fullPath = this.join(path, entry);
64
+
const isFile = await this.isFile(fullPath);
65
+
if (isFile) {
66
+
await this.unlink(fullPath);
67
+
} else {
68
+
await this.rmdir(fullPath);
69
+
}
70
+
}
71
+
72
+
await fs.rmdir(path);
73
+
},
74
+
75
+
async exists(path) {
76
+
return await fs.exists(path);
77
+
},
78
+
async isFile(path) {
79
+
return (await fs.stat(path)).isFile();
80
+
},
81
+
async isDir(path) {
82
+
return (await fs.stat(path)).isDirectory();
83
+
},
84
+
85
+
join(...parts) {
86
+
let str = parts.join("/");
87
+
if (!str.startsWith("/")) str = "/" + str;
88
+
return str;
89
+
},
90
+
dirname(path) {
91
+
const parts = getParts(path);
92
+
return "/" + parts.slice(0, parts.length - 1).join("/");
93
+
},
94
+
basename(path) {
95
+
const parts = getParts(path);
96
+
return parts[parts.length - 1];
97
+
}
98
+
},
99
+
// TODO
100
+
addCors(url) {},
101
+
addBlocked(url) {}
102
+
};
103
+
104
+
// Actual loading begins here
105
+
let config = await readConfig();
106
+
initLogger(config);
107
+
108
+
const extensions = await getExtensions();
109
+
const processedExtensions = await loadExtensions(extensions);
110
+
111
+
const moonlightNode: MoonlightNode = {
112
+
get config() {
113
+
return config;
114
+
},
115
+
extensions,
116
+
processedExtensions,
117
+
nativesCache: {},
118
+
isBrowser: true,
119
+
events: createEventEmitter<NodeEventType, NodeEventPayloads>(),
120
+
121
+
version: MOONLIGHT_VERSION,
122
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
123
+
124
+
getConfig(ext) {
125
+
return getConfig(ext, config);
126
+
},
127
+
getConfigOption(ext, name) {
128
+
const manifest = getManifest(extensions, ext);
129
+
return getConfigOption(ext, name, config, manifest?.settings);
130
+
},
131
+
async setConfigOption(ext, name, value) {
132
+
setConfigOption(config, ext, name, value);
133
+
await this.writeConfig(config);
134
+
},
135
+
136
+
getNatives: () => {},
137
+
getLogger: (id: string) => {
138
+
return new Logger(id);
139
+
},
140
+
141
+
getMoonlightDir() {
142
+
return "/";
143
+
},
144
+
getExtensionDir: (ext: string) => {
145
+
return `/extensions/${ext}`;
146
+
},
147
+
148
+
async writeConfig(newConfig) {
149
+
await writeConfig(newConfig);
150
+
config = newConfig;
151
+
this.events.dispatchEvent(NodeEventType.ConfigSaved, newConfig);
152
+
}
153
+
};
154
+
155
+
Object.assign(window, {
156
+
moonlightNode
157
+
});
158
+
159
+
// This is set by web-preload for us
160
+
await window._moonlightWebLoad!();
161
+
};
+7
packages/browser/tsconfig.json
+7
packages/browser/tsconfig.json
+7
packages/core/package.json
+7
packages/core/package.json
+57
packages/core/src/asar.ts
+57
packages/core/src/asar.ts
···
1
+
// https://github.com/electron/asar
2
+
// http://formats.kaitai.io/python_pickle/
3
+
import { BinaryReader } from "./util/binary";
4
+
5
+
/*
6
+
The asar format is kinda bad, especially because it uses multiple pickle
7
+
entries. It spams sizes, expecting us to read small buffers and parse those,
8
+
but we can just take it all through at once without having to create multiple
9
+
BinaryReaders. This implementation might be wrong, though.
10
+
11
+
This either has size/offset or files but I can't get the type to cooperate,
12
+
so pretend this is a union.
13
+
*/
14
+
15
+
type AsarEntry = {
16
+
size: number;
17
+
offset: `${number}`; // who designed this
18
+
19
+
files?: Record<string, AsarEntry>;
20
+
};
21
+
22
+
export default function extractAsar(file: ArrayBuffer) {
23
+
const array = new Uint8Array(file);
24
+
const br = new BinaryReader(array);
25
+
26
+
// two uints, one containing the number '4', to signify that the other uint takes up 4 bytes
27
+
// bravo, electron, bravo
28
+
const _payloadSize = br.readUInt32();
29
+
const _headerSize = br.readInt32();
30
+
31
+
const headerStringStart = br.position;
32
+
const headerStringSize = br.readUInt32(); // How big the block is
33
+
const actualStringSize = br.readUInt32(); // How big the string in that block is
34
+
35
+
const base = headerStringStart + headerStringSize + 4;
36
+
37
+
const string = br.readString(actualStringSize);
38
+
const header: AsarEntry = JSON.parse(string);
39
+
40
+
const ret: Record<string, Uint8Array> = {};
41
+
function addDirectory(dir: AsarEntry, path: string) {
42
+
for (const [name, data] of Object.entries(dir.files!)) {
43
+
const fullName = path + "/" + name;
44
+
if (data.files != null) {
45
+
addDirectory(data, fullName);
46
+
} else {
47
+
br.position = base + parseInt(data.offset);
48
+
const file = br.read(data.size);
49
+
ret[fullName] = file;
50
+
}
51
+
}
52
+
}
53
+
54
+
addDirectory(header, "");
55
+
56
+
return ret;
57
+
}
+31
-31
packages/core/src/config.ts
+31
-31
packages/core/src/config.ts
···
1
1
import { Config } from "@moonlight-mod/types";
2
-
import requireImport from "./util/import";
3
2
import { getConfigPath } from "./util/data";
3
+
import * as constants from "@moonlight-mod/types/constants";
4
+
import Logger from "./util/logger";
5
+
6
+
const logger = new Logger("core/config");
4
7
5
8
const defaultConfig: Config = {
9
+
// If you're updating this, update `builtinExtensions` in constants as well
6
10
extensions: {
7
11
moonbase: true,
8
12
disableSentry: true,
9
13
noTrack: true,
10
14
noHideToken: true
11
15
},
12
-
repositories: ["https://moonlight-mod.github.io/extensions-dist/repo.json"]
16
+
repositories: [constants.mainRepo]
13
17
};
14
18
15
-
export function writeConfig(config: Config) {
16
-
const fs = requireImport("fs");
17
-
const configPath = getConfigPath();
18
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
19
-
}
20
-
21
-
function readConfigNode(): Config {
22
-
const fs = requireImport("fs");
23
-
const configPath = getConfigPath();
24
-
25
-
if (!fs.existsSync(configPath)) {
26
-
writeConfig(defaultConfig);
27
-
return defaultConfig;
19
+
export async function writeConfig(config: Config) {
20
+
try {
21
+
const configPath = await getConfigPath();
22
+
await moonlightNodeSandboxed.fs.writeFileString(configPath, JSON.stringify(config, null, 2));
23
+
} catch (e) {
24
+
logger.error("Failed to write config", e);
28
25
}
29
-
30
-
let config: Config = JSON.parse(fs.readFileSync(configPath, "utf8"));
31
-
32
-
// Assign the default values if they don't exist (newly added)
33
-
config = { ...defaultConfig, ...config };
34
-
writeConfig(config);
35
-
36
-
return config;
37
26
}
38
27
39
-
export function readConfig(): Config {
28
+
export async function readConfig(): Promise<Config> {
40
29
webPreload: {
41
30
return moonlightNode.config;
42
31
}
43
32
44
-
nodePreload: {
45
-
return readConfigNode();
46
-
}
33
+
const configPath = await getConfigPath();
34
+
if (!(await moonlightNodeSandboxed.fs.exists(configPath))) {
35
+
await writeConfig(defaultConfig);
36
+
return defaultConfig;
37
+
} else {
38
+
try {
39
+
let config: Config = JSON.parse(await moonlightNodeSandboxed.fs.readFileString(configPath));
40
+
// Assign the default values if they don't exist (newly added)
41
+
config = { ...defaultConfig, ...config };
42
+
await writeConfig(config);
47
43
48
-
injector: {
49
-
return readConfigNode();
44
+
return config;
45
+
} catch (e) {
46
+
logger.error("Failed to read config, falling back to defaults", e);
47
+
// We don't want to write the default config here - if a user is manually
48
+
// editing their config and messes it up, we'll delete it all instead of
49
+
// letting them fix it
50
+
return defaultConfig;
51
+
}
50
52
}
51
-
52
-
throw new Error("Called readConfig() in an impossible environment");
53
53
}
+17
packages/core/src/cors.ts
+17
packages/core/src/cors.ts
···
1
+
const cors: string[] = [];
2
+
const blocked: string[] = [];
3
+
4
+
export function registerCors(url: string) {
5
+
cors.push(url);
6
+
}
7
+
8
+
export function registerBlocked(url: string) {
9
+
blocked.push(url);
10
+
}
11
+
12
+
export function getDynamicCors() {
13
+
return {
14
+
cors,
15
+
blocked
16
+
};
17
+
}
+111
-70
packages/core/src/extension/loader.ts
+111
-70
packages/core/src/extension/loader.ts
···
2
2
ExtensionWebExports,
3
3
DetectedExtension,
4
4
ProcessedExtensions,
5
-
WebpackModuleFunc
5
+
WebpackModuleFunc,
6
+
constants,
7
+
ExtensionManifest,
8
+
ExtensionEnvironment
6
9
} from "@moonlight-mod/types";
7
10
import { readConfig } from "../config";
8
11
import Logger from "../util/logger";
···
10
13
import calculateDependencies from "../util/dependency";
11
14
import { createEventEmitter } from "../util/event";
12
15
import { registerStyles } from "../styles";
16
+
import { WebEventPayloads, WebEventType } from "@moonlight-mod/types/core/event";
13
17
14
18
const logger = new Logger("core/extension/loader");
15
19
16
-
async function loadExt(ext: DetectedExtension) {
17
-
webPreload: {
18
-
if (ext.scripts.web != null) {
19
-
const source =
20
-
ext.scripts.web + "\n//# sourceURL=file:///" + ext.scripts.webPath;
21
-
const fn = new Function("require", "module", "exports", source);
20
+
function evalIIFE(id: string, source: string): ExtensionWebExports {
21
+
const fn = new Function("require", "module", "exports", source);
22
22
23
-
const module = { id: ext.id, exports: {} };
24
-
fn.apply(window, [
25
-
() => {
26
-
logger.warn("Attempted to require() from web");
27
-
},
28
-
module,
29
-
module.exports
30
-
]);
23
+
const module = { id, exports: {} };
24
+
fn.apply(window, [
25
+
() => {
26
+
logger.warn("Attempted to require() from web");
27
+
},
28
+
module,
29
+
module.exports
30
+
]);
31
+
32
+
return module.exports;
33
+
}
34
+
35
+
async function evalEsm(source: string): Promise<ExtensionWebExports> {
36
+
// Data URLs (`data:`) don't seem to work under the CSP, but object URLs do
37
+
const url = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
38
+
39
+
const module = await import(url);
40
+
41
+
URL.revokeObjectURL(url);
42
+
43
+
return module;
44
+
}
45
+
46
+
async function loadExtWeb(ext: DetectedExtension) {
47
+
if (ext.scripts.web != null) {
48
+
const source = ext.scripts.web + `\n//# sourceURL=${ext.id}/web.js`;
49
+
50
+
let exports: ExtensionWebExports;
31
51
32
-
const exports: ExtensionWebExports = module.exports;
33
-
if (exports.patches != null) {
34
-
let idx = 0;
35
-
for (const patch of exports.patches) {
36
-
if (Array.isArray(patch.replace)) {
37
-
for (const replacement of patch.replace) {
38
-
const newPatch = Object.assign({}, patch, {
39
-
replace: replacement
40
-
});
52
+
try {
53
+
exports = evalIIFE(ext.id, source);
54
+
} catch {
55
+
logger.trace(`Failed to load IIFE for extension ${ext.id}, trying ESM loading`);
56
+
exports = await evalEsm(source);
57
+
}
41
58
42
-
registerPatch({ ...newPatch, ext: ext.id, id: idx });
43
-
idx++;
44
-
}
45
-
} else {
46
-
registerPatch({ ...patch, ext: ext.id, id: idx });
47
-
idx++;
48
-
}
59
+
if (exports.patches != null) {
60
+
let idx = 0;
61
+
for (const patch of exports.patches) {
62
+
if (Array.isArray(patch.replace)) {
63
+
registerPatch({ ...patch, ext: ext.id, id: idx });
64
+
} else {
65
+
registerPatch({ ...patch, replace: [patch.replace], ext: ext.id, id: idx });
49
66
}
67
+
idx++;
50
68
}
69
+
}
51
70
52
-
if (exports.webpackModules != null) {
53
-
for (const [name, wp] of Object.entries(exports.webpackModules)) {
54
-
if (wp.run == null && ext.scripts.webpackModules?.[name] != null) {
55
-
const func = new Function(
56
-
"module",
57
-
"exports",
58
-
"require",
59
-
ext.scripts.webpackModules[name]!
60
-
) as WebpackModuleFunc;
61
-
registerWebpackModule({
62
-
...wp,
63
-
ext: ext.id,
64
-
id: name,
65
-
run: func
66
-
});
67
-
} else {
68
-
registerWebpackModule({ ...wp, ext: ext.id, id: name });
69
-
}
71
+
if (exports.webpackModules != null) {
72
+
for (const [name, wp] of Object.entries(exports.webpackModules)) {
73
+
if (wp.run == null && ext.scripts.webpackModules?.[name] != null) {
74
+
const source = ext.scripts.webpackModules[name]! + `\n//# sourceURL=${ext.id}/webpackModules/${name}.js`;
75
+
const func = new Function("module", "exports", "require", source) as WebpackModuleFunc;
76
+
registerWebpackModule({
77
+
...wp,
78
+
ext: ext.id,
79
+
id: name,
80
+
run: func
81
+
});
82
+
} else {
83
+
registerWebpackModule({ ...wp, ext: ext.id, id: name });
70
84
}
71
85
}
86
+
}
72
87
73
-
if (exports.styles != null) {
74
-
registerStyles(
75
-
exports.styles.map((style, i) => `/* ${ext.id}#${i} */ ${style}`)
76
-
);
77
-
}
88
+
if (exports.styles != null) {
89
+
registerStyles(exports.styles.map((style, i) => `/* ${ext.id}#${i} */ ${style}`));
90
+
}
91
+
if (ext.scripts.style != null) {
92
+
registerStyles([`/* ${ext.id}#style.css */ ${ext.scripts.style}`]);
93
+
}
94
+
}
95
+
}
96
+
97
+
async function loadExt(ext: DetectedExtension) {
98
+
webTarget: {
99
+
try {
100
+
await loadExtWeb(ext);
101
+
} catch (e) {
102
+
logger.error(`Failed to load extension "${ext.id}"`, e);
78
103
}
79
104
}
80
105
···
100
125
}
101
126
}
102
127
128
+
export enum ExtensionCompat {
129
+
Compatible,
130
+
InvalidApiLevel,
131
+
InvalidEnvironment
132
+
}
133
+
134
+
export function checkExtensionCompat(manifest: ExtensionManifest): ExtensionCompat {
135
+
let environment;
136
+
webTarget: {
137
+
environment = ExtensionEnvironment.Web;
138
+
}
139
+
nodeTarget: {
140
+
environment = ExtensionEnvironment.Desktop;
141
+
}
142
+
143
+
if (manifest.apiLevel !== constants.apiLevel) return ExtensionCompat.InvalidApiLevel;
144
+
if ((manifest.environment ?? "both") !== "both" && manifest.environment !== environment)
145
+
return ExtensionCompat.InvalidEnvironment;
146
+
return ExtensionCompat.Compatible;
147
+
}
148
+
103
149
/*
104
150
This function resolves extensions and loads them, split into a few stages:
105
151
···
114
160
extensions fires an event on completion, which allows us to await the loading
115
161
of another extension, resolving dependencies & load order effectively.
116
162
*/
117
-
export async function loadExtensions(
118
-
exts: DetectedExtension[]
119
-
): Promise<ProcessedExtensions> {
120
-
const config = readConfig();
163
+
export async function loadExtensions(exts: DetectedExtension[]): Promise<ProcessedExtensions> {
164
+
exts = exts.filter((ext) => checkExtensionCompat(ext.manifest) === ExtensionCompat.Compatible);
165
+
166
+
const config = await readConfig();
121
167
const items = exts
122
168
.map((ext) => {
123
169
return {
···
155
201
};
156
202
}
157
203
158
-
export async function loadProcessedExtensions({
159
-
extensions,
160
-
dependencyGraph
161
-
}: ProcessedExtensions) {
162
-
const eventEmitter = createEventEmitter();
204
+
export async function loadProcessedExtensions({ extensions, dependencyGraph }: ProcessedExtensions) {
205
+
const eventEmitter = createEventEmitter<WebEventType, WebEventPayloads>();
163
206
const finished: Set<string> = new Set();
164
207
165
208
logger.trace(
···
181
224
}
182
225
183
226
function done() {
184
-
eventEmitter.removeEventListener("ext-ready", cb);
227
+
eventEmitter.removeEventListener(WebEventType.ExtensionLoad, cb);
185
228
r();
186
229
}
187
230
188
-
eventEmitter.addEventListener("ext-ready", cb);
231
+
eventEmitter.addEventListener(WebEventType.ExtensionLoad, cb);
189
232
if (finished.has(dep)) done();
190
233
})
191
234
);
192
235
193
236
if (waitPromises.length > 0) {
194
-
logger.debug(
195
-
`Waiting on ${waitPromises.length} dependencies for "${ext.id}"`
196
-
);
237
+
logger.debug(`Waiting on ${waitPromises.length} dependencies for "${ext.id}"`);
197
238
await Promise.all(waitPromises);
198
239
}
199
240
···
201
242
await loadExt(ext);
202
243
203
244
finished.add(ext.id);
204
-
eventEmitter.dispatchEvent("ext-ready", ext.id);
245
+
eventEmitter.dispatchEvent(WebEventType.ExtensionLoad, ext.id);
205
246
logger.debug(`Loaded "${ext.id}"`);
206
247
}
207
248
208
-
webPreload: {
249
+
webTarget: {
209
250
for (const ext of extensions) {
210
251
moonlight.enabledExtensions.add(ext.id);
211
252
}
+131
-86
packages/core/src/extension.ts
+131
-86
packages/core/src/extension.ts
···
1
-
import {
2
-
ExtensionManifest,
3
-
DetectedExtension,
4
-
ExtensionLoadSource,
5
-
constants
6
-
} from "@moonlight-mod/types";
1
+
import { ExtensionManifest, DetectedExtension, ExtensionLoadSource, constants } from "@moonlight-mod/types";
7
2
import { readConfig } from "./config";
8
-
import requireImport from "./util/import";
9
3
import { getCoreExtensionsPath, getExtensionsPath } from "./util/data";
4
+
import Logger from "./util/logger";
5
+
6
+
const logger = new Logger("core/extension");
10
7
11
-
function findManifests(dir: string): string[] {
12
-
const fs = requireImport("fs");
13
-
const path = requireImport("path");
8
+
async function findManifests(dir: string): Promise<string[]> {
14
9
const ret = [];
15
10
16
-
for (const file of fs.readdirSync(dir)) {
17
-
if (file === "manifest.json") {
18
-
ret.push(path.join(dir, file));
19
-
}
11
+
if (await moonlightNodeSandboxed.fs.exists(dir)) {
12
+
for (const file of await moonlightNodeSandboxed.fs.readdir(dir)) {
13
+
const path = moonlightNodeSandboxed.fs.join(dir, file);
14
+
if (file === "manifest.json") {
15
+
ret.push(path);
16
+
}
20
17
21
-
if (fs.statSync(path.join(dir, file)).isDirectory()) {
22
-
ret.push(...findManifests(path.join(dir, file)));
18
+
if (!(await moonlightNodeSandboxed.fs.isFile(path))) {
19
+
ret.push(...(await findManifests(path)));
20
+
}
23
21
}
24
22
}
25
23
26
24
return ret;
27
25
}
28
26
29
-
function loadDetectedExtensions(
27
+
async function loadDetectedExtensions(
30
28
dir: string,
31
-
type: ExtensionLoadSource
32
-
): DetectedExtension[] {
33
-
const fs = requireImport("fs");
34
-
const path = requireImport("path");
29
+
type: ExtensionLoadSource,
30
+
seen: Set<string>
31
+
): Promise<DetectedExtension[]> {
35
32
const ret: DetectedExtension[] = [];
36
33
37
-
const manifests = findManifests(dir);
34
+
const manifests = await findManifests(dir);
38
35
for (const manifestPath of manifests) {
39
-
if (!fs.existsSync(manifestPath)) continue;
40
-
const dir = path.dirname(manifestPath);
36
+
try {
37
+
if (!(await moonlightNodeSandboxed.fs.exists(manifestPath))) continue;
38
+
const dir = moonlightNodeSandboxed.fs.dirname(manifestPath);
39
+
40
+
const manifest: ExtensionManifest = JSON.parse(await moonlightNodeSandboxed.fs.readFileString(manifestPath));
41
+
if (seen.has(manifest.id)) {
42
+
logger.warn(`Duplicate extension found, skipping: ${manifest.id}`);
43
+
continue;
44
+
}
45
+
seen.add(manifest.id);
46
+
47
+
const webPath = moonlightNodeSandboxed.fs.join(dir, "index.js");
48
+
const nodePath = moonlightNodeSandboxed.fs.join(dir, "node.js");
49
+
const hostPath = moonlightNodeSandboxed.fs.join(dir, "host.js");
41
50
42
-
const manifest: ExtensionManifest = JSON.parse(
43
-
fs.readFileSync(manifestPath, "utf8")
44
-
);
51
+
// if none exist (empty manifest) don't give a shit
52
+
if (
53
+
!moonlightNodeSandboxed.fs.exists(webPath) &&
54
+
!moonlightNodeSandboxed.fs.exists(nodePath) &&
55
+
!moonlightNodeSandboxed.fs.exists(hostPath)
56
+
) {
57
+
continue;
58
+
}
59
+
60
+
const web = (await moonlightNodeSandboxed.fs.exists(webPath))
61
+
? await moonlightNodeSandboxed.fs.readFileString(webPath)
62
+
: undefined;
45
63
46
-
const webPath = path.join(dir, "index.js");
47
-
const nodePath = path.join(dir, "node.js");
48
-
const hostPath = path.join(dir, "host.js");
64
+
let url: string | undefined = undefined;
65
+
const urlPath = moonlightNodeSandboxed.fs.join(dir, constants.repoUrlFile);
66
+
if (type === ExtensionLoadSource.Normal && (await moonlightNodeSandboxed.fs.exists(urlPath))) {
67
+
url = await moonlightNodeSandboxed.fs.readFileString(urlPath);
68
+
}
49
69
50
-
// if none exist (empty manifest) don't give a shit
51
-
if (
52
-
!fs.existsSync(webPath) &&
53
-
!fs.existsSync(nodePath) &&
54
-
!fs.existsSync(hostPath)
55
-
) {
56
-
continue;
70
+
const wpModules: Record<string, string> = {};
71
+
const wpModulesPath = moonlightNodeSandboxed.fs.join(dir, "webpackModules");
72
+
if (await moonlightNodeSandboxed.fs.exists(wpModulesPath)) {
73
+
const wpModulesFile = await moonlightNodeSandboxed.fs.readdir(wpModulesPath);
74
+
75
+
for (const wpModuleFile of wpModulesFile) {
76
+
if (wpModuleFile.endsWith(".js")) {
77
+
wpModules[wpModuleFile.replace(".js", "")] = await moonlightNodeSandboxed.fs.readFileString(
78
+
moonlightNodeSandboxed.fs.join(wpModulesPath, wpModuleFile)
79
+
);
80
+
}
81
+
}
82
+
}
83
+
84
+
const stylePath = moonlightNodeSandboxed.fs.join(dir, "style.css");
85
+
86
+
ret.push({
87
+
id: manifest.id,
88
+
manifest,
89
+
source: {
90
+
type,
91
+
url
92
+
},
93
+
scripts: {
94
+
web,
95
+
webPath: web != null ? webPath : undefined,
96
+
webpackModules: wpModules,
97
+
nodePath: (await moonlightNodeSandboxed.fs.exists(nodePath)) ? nodePath : undefined,
98
+
hostPath: (await moonlightNodeSandboxed.fs.exists(hostPath)) ? hostPath : undefined,
99
+
style: (await moonlightNodeSandboxed.fs.exists(stylePath))
100
+
? await moonlightNodeSandboxed.fs.readFileString(stylePath)
101
+
: undefined
102
+
}
103
+
});
104
+
} catch (err) {
105
+
logger.error(`Failed to load extension from "${manifestPath}":`, err);
57
106
}
107
+
}
58
108
59
-
const web = fs.existsSync(webPath)
60
-
? fs.readFileSync(webPath, "utf8")
61
-
: undefined;
109
+
return ret;
110
+
}
62
111
63
-
let url: string | undefined = undefined;
64
-
const urlPath = path.join(dir, constants.repoUrlFile);
65
-
if (type === ExtensionLoadSource.Normal && fs.existsSync(urlPath)) {
66
-
url = fs.readFileSync(urlPath, "utf8");
67
-
}
112
+
async function getExtensionsNative(): Promise<DetectedExtension[]> {
113
+
const config = await readConfig();
114
+
const res = [];
115
+
const seen = new Set<string>();
68
116
69
-
const wpModules: Record<string, string> = {};
70
-
const wpModulesPath = path.join(dir, "webpackModules");
71
-
if (fs.existsSync(wpModulesPath)) {
72
-
const wpModulesFile = fs.readdirSync(wpModulesPath);
117
+
res.push(...(await loadDetectedExtensions(getCoreExtensionsPath(), ExtensionLoadSource.Core, seen)));
118
+
119
+
for (const devSearchPath of config.devSearchPaths ?? []) {
120
+
res.push(...(await loadDetectedExtensions(devSearchPath, ExtensionLoadSource.Developer, seen)));
121
+
}
122
+
123
+
res.push(...(await loadDetectedExtensions(await getExtensionsPath(), ExtensionLoadSource.Normal, seen)));
124
+
125
+
return res;
126
+
}
127
+
128
+
async function getExtensionsBrowser(): Promise<DetectedExtension[]> {
129
+
const ret: DetectedExtension[] = [];
130
+
const seen = new Set<string>();
73
131
74
-
for (const wpModuleFile of wpModulesFile) {
75
-
if (wpModuleFile.endsWith(".js")) {
76
-
wpModules[wpModuleFile.replace(".js", "")] = fs.readFileSync(
77
-
path.join(wpModulesPath, wpModuleFile),
78
-
"utf8"
79
-
);
80
-
}
132
+
const coreExtensionsFs: Record<string, string> = JSON.parse(_moonlight_coreExtensionsStr);
133
+
const coreExtensions = Array.from(new Set(Object.keys(coreExtensionsFs).map((x) => x.split("/")[0])));
134
+
135
+
for (const ext of coreExtensions) {
136
+
if (!coreExtensionsFs[`${ext}/index.js`]) continue;
137
+
const manifest = JSON.parse(coreExtensionsFs[`${ext}/manifest.json`]);
138
+
const web = coreExtensionsFs[`${ext}/index.js`];
139
+
140
+
const wpModules: Record<string, string> = {};
141
+
const wpModulesPath = `${ext}/webpackModules`;
142
+
for (const wpModuleFile of Object.keys(coreExtensionsFs)) {
143
+
if (wpModuleFile.startsWith(wpModulesPath)) {
144
+
wpModules[wpModuleFile.replace(wpModulesPath + "/", "").replace(".js", "")] = coreExtensionsFs[wpModuleFile];
81
145
}
82
146
}
83
147
···
85
149
id: manifest.id,
86
150
manifest,
87
151
source: {
88
-
type,
89
-
url
152
+
type: ExtensionLoadSource.Core
90
153
},
91
154
scripts: {
92
155
web,
93
-
webPath: web != null ? webPath : undefined,
94
156
webpackModules: wpModules,
95
-
nodePath: fs.existsSync(nodePath) ? nodePath : undefined,
96
-
hostPath: fs.existsSync(hostPath) ? hostPath : undefined
157
+
style: coreExtensionsFs[`${ext}/style.css`]
97
158
}
98
159
});
160
+
seen.add(manifest.id);
99
161
}
100
162
101
-
return ret;
102
-
}
103
-
104
-
function getExtensionsNative(): DetectedExtension[] {
105
-
const config = readConfig();
106
-
const res = [];
107
-
108
-
res.push(
109
-
...loadDetectedExtensions(getCoreExtensionsPath(), ExtensionLoadSource.Core)
110
-
);
111
-
112
-
res.push(
113
-
...loadDetectedExtensions(getExtensionsPath(), ExtensionLoadSource.Normal)
114
-
);
115
-
116
-
for (const devSearchPath of config.devSearchPaths ?? []) {
117
-
res.push(
118
-
...loadDetectedExtensions(devSearchPath, ExtensionLoadSource.Developer)
119
-
);
163
+
if (await moonlightNodeSandboxed.fs.exists("/extensions")) {
164
+
ret.push(...(await loadDetectedExtensions("/extensions", ExtensionLoadSource.Normal, seen)));
120
165
}
121
166
122
-
return res;
167
+
return ret;
123
168
}
124
169
125
-
export function getExtensions(): DetectedExtension[] {
170
+
export async function getExtensions(): Promise<DetectedExtension[]> {
126
171
webPreload: {
127
172
return moonlightNode.extensions;
128
173
}
129
174
130
-
nodePreload: {
131
-
return getExtensionsNative();
175
+
browser: {
176
+
return await getExtensionsBrowser();
132
177
}
133
178
134
-
injector: {
135
-
return getExtensionsNative();
179
+
nodeTarget: {
180
+
return await getExtensionsNative();
136
181
}
137
182
138
183
throw new Error("Called getExtensions() outside of node-preload/web-preload");
+56
packages/core/src/fs.ts
+56
packages/core/src/fs.ts
···
1
+
import type { MoonlightFS } from "@moonlight-mod/types";
2
+
import requireImport from "./util/import";
3
+
4
+
export default function createFS(): MoonlightFS {
5
+
const fs = requireImport("fs");
6
+
const path = requireImport("path");
7
+
8
+
return {
9
+
async readFile(path) {
10
+
const file = fs.readFileSync(path);
11
+
return new Uint8Array(file);
12
+
},
13
+
async readFileString(path) {
14
+
return fs.readFileSync(path, "utf8");
15
+
},
16
+
async writeFile(path, data) {
17
+
fs.writeFileSync(path, Buffer.from(data));
18
+
},
19
+
async writeFileString(path, data) {
20
+
fs.writeFileSync(path, data, "utf8");
21
+
},
22
+
async unlink(path) {
23
+
fs.unlinkSync(path);
24
+
},
25
+
26
+
async readdir(path) {
27
+
return fs.readdirSync(path);
28
+
},
29
+
async mkdir(path) {
30
+
fs.mkdirSync(path, { recursive: true });
31
+
},
32
+
async rmdir(path) {
33
+
fs.rmSync(path, { recursive: true });
34
+
},
35
+
36
+
async exists(path) {
37
+
return fs.existsSync(path);
38
+
},
39
+
async isFile(path) {
40
+
return fs.statSync(path).isFile();
41
+
},
42
+
async isDir(path) {
43
+
return fs.statSync(path).isDirectory();
44
+
},
45
+
46
+
join(...parts) {
47
+
return path.join(...parts);
48
+
},
49
+
dirname(dir) {
50
+
return path.dirname(dir);
51
+
},
52
+
basename(dir) {
53
+
return path.basename(dir);
54
+
}
55
+
};
56
+
}
+266
-120
packages/core/src/patch.ts
+266
-120
packages/core/src/patch.ts
···
6
6
IdentifiedWebpackModule,
7
7
WebpackJsonp,
8
8
WebpackJsonpEntry,
9
-
WebpackModuleFunc
9
+
WebpackModuleFunc,
10
+
WebpackRequireType
10
11
} from "@moonlight-mod/types";
11
12
import Logger from "./util/logger";
12
13
import calculateDependencies, { Dependency } from "./util/dependency";
13
-
import WebpackRequire from "@moonlight-mod/types/discord/require";
14
+
import { WebEventType } from "@moonlight-mod/types/core/event";
15
+
import { processFind, processReplace, testFind } from "./util/patch";
14
16
15
17
const logger = new Logger("core/patch");
16
18
17
19
// Can't be Set because we need splice
18
20
const patches: IdentifiedPatch[] = [];
19
21
let webpackModules: Set<IdentifiedWebpackModule> = new Set();
22
+
let webpackRequire: WebpackRequireType | null = null;
23
+
24
+
const moduleLoadSubscriptions: Map<string, ((moduleId: string) => void)[]> = new Map();
20
25
21
26
export function registerPatch(patch: IdentifiedPatch) {
27
+
patch.find = processFind(patch.find);
28
+
processReplace(patch.replace);
29
+
22
30
patches.push(patch);
31
+
moonlight.unpatched.add(patch);
23
32
}
24
33
25
34
export function registerWebpackModule(wp: IdentifiedWebpackModule) {
26
35
webpackModules.add(wp);
36
+
if (wp.dependencies?.length) {
37
+
moonlight.pendingModules.add(wp);
38
+
}
39
+
}
40
+
41
+
export function onModuleLoad(module: string | string[], callback: (moduleId: string) => void): void {
42
+
let moduleIds = module;
43
+
44
+
if (typeof module === "string") {
45
+
moduleIds = [module];
46
+
}
47
+
48
+
for (const moduleId of moduleIds) {
49
+
if (moduleLoadSubscriptions.has(moduleId)) {
50
+
moduleLoadSubscriptions.get(moduleId)?.push(callback);
51
+
} else {
52
+
moduleLoadSubscriptions.set(moduleId, [callback]);
53
+
}
54
+
}
27
55
}
28
56
29
57
/*
···
38
66
const moduleCache: Record<string, string> = {};
39
67
const patched: Record<string, Array<string>> = {};
40
68
69
+
function createSourceURL(id: string) {
70
+
const remapped = Object.entries(moonlight.moonmap.modules).find((m) => m[1] === id)?.[0];
71
+
72
+
if (remapped) {
73
+
return `// Webpack Module: ${id}\n//# sourceURL=${remapped}`;
74
+
}
75
+
76
+
return `//# sourceURL=Webpack-Module/${id.slice(0, 3)}/${id}`;
77
+
}
78
+
79
+
function patchModule(id: string, patchId: string, replaced: string, entry: WebpackJsonpEntry[1]) {
80
+
// Store what extensions patched what modules for easier debugging
81
+
patched[id] = patched[id] ?? [];
82
+
patched[id].push(patchId);
83
+
84
+
// Webpack module arguments are minified, so we replace them with consistent names
85
+
// We have to wrap it so things don't break, though
86
+
const patchedStr = patched[id].sort().join(", ");
87
+
88
+
const wrapped =
89
+
`(${replaced}).apply(this, arguments)\n` + `// Patched by moonlight: ${patchedStr}\n` + createSourceURL(id);
90
+
91
+
try {
92
+
const func = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
93
+
entry[id] = func;
94
+
entry[id].__moonlight = true;
95
+
return true;
96
+
} catch (e) {
97
+
logger.warn("Error constructing function for patch", patchId, e);
98
+
patched[id].pop();
99
+
return false;
100
+
}
101
+
}
102
+
41
103
function patchModules(entry: WebpackJsonpEntry[1]) {
104
+
// Populate the module cache
42
105
for (const [id, func] of Object.entries(entry)) {
43
-
let moduleString = Object.prototype.hasOwnProperty.call(moduleCache, id)
44
-
? moduleCache[id]
45
-
: func.toString().replace(/\n/g, "");
106
+
if (!Object.hasOwn(moduleCache, id) && func.__moonlight !== true) {
107
+
moduleCache[id] = func.toString().replace(/\n/g, "");
108
+
moonlight.moonmap.parseScript(id, moduleCache[id]);
109
+
}
110
+
}
46
111
47
-
for (const patch of patches) {
112
+
for (const [id, func] of Object.entries(entry)) {
113
+
if (func.__moonlight === true) continue;
114
+
115
+
// Clone the module string so finds don't get messed up by other extensions
116
+
const origModuleString = moduleCache[id];
117
+
let moduleString = origModuleString;
118
+
const patchedStr = [];
119
+
const mappedName = Object.entries(moonlight.moonmap.modules).find((m) => m[1] === id)?.[0];
120
+
let modified = false;
121
+
let swappedModule = false;
122
+
123
+
const exts = new Set<string>();
124
+
125
+
for (let i = 0; i < patches.length; i++) {
126
+
const patch = patches[i];
48
127
if (patch.prerequisite != null && !patch.prerequisite()) {
128
+
moonlight.unpatched.delete(patch);
49
129
continue;
50
130
}
51
131
···
54
134
patch.find.lastIndex = 0;
55
135
}
56
136
57
-
// indexOf is faster than includes by 0.25% lmao
58
-
const match =
59
-
typeof patch.find === "string"
60
-
? moduleString.indexOf(patch.find) !== -1
61
-
: patch.find.test(moduleString);
137
+
const match = testFind(origModuleString, patch.find) || patch.find === mappedName;
62
138
63
139
// Global regexes apply to all modules
64
-
const shouldRemove =
65
-
typeof patch.find === "string" ? true : !patch.find.global;
140
+
const shouldRemove = typeof patch.find === "string" ? true : !patch.find.global;
66
141
142
+
let replaced = moduleString;
143
+
let hardFailed = false;
67
144
if (match) {
68
-
moonlight.unpatched.delete(patch);
145
+
// We ensured normal PatchReplace objects get turned into arrays on register
146
+
const replaces = patch.replace as PatchReplace[];
69
147
70
-
// We ensured all arrays get turned into normal PatchReplace objects on register
71
-
const replace = patch.replace as PatchReplace;
148
+
let isPatched = true;
149
+
for (let i = 0; i < replaces.length; i++) {
150
+
const replace = replaces[i];
151
+
let patchId = `${patch.ext}#${patch.id}`;
152
+
if (replaces.length > 1) patchId += `#${i}`;
153
+
patchedStr.push(patchId);
72
154
73
-
if (
74
-
replace.type === undefined ||
75
-
replace.type === PatchReplaceType.Normal
76
-
) {
77
-
// Add support for \i to match rspack's minified names
78
-
if (typeof replace.match !== "string") {
79
-
replace.match = new RegExp(
80
-
replace.match.source.replace(/\\i/g, "[A-Za-z_$][\\w$]*"),
81
-
replace.match.flags
82
-
);
83
-
}
84
-
// tsc fails to detect the overloads for this, so I'll just do this
85
-
// Verbose, but it works
86
-
let replaced;
87
-
if (typeof replace.replacement === "string") {
88
-
replaced = moduleString.replace(replace.match, replace.replacement);
89
-
} else {
90
-
replaced = moduleString.replace(replace.match, replace.replacement);
91
-
}
155
+
if (replace.type === undefined || replace.type === PatchReplaceType.Normal) {
156
+
// tsc fails to detect the overloads for this, so I'll just do this
157
+
// Verbose, but it works
158
+
if (typeof replace.replacement === "string") {
159
+
replaced = replaced.replace(replace.match, replace.replacement);
160
+
} else {
161
+
replaced = replaced.replace(replace.match, replace.replacement);
162
+
}
92
163
93
-
if (replaced === moduleString) {
94
-
logger.warn("Patch replacement failed", id, patch);
95
-
continue;
164
+
if (replaced === moduleString) {
165
+
logger.warn("Patch replacement failed", id, patchId, patch);
166
+
isPatched = false;
167
+
if (patch.hardFail) {
168
+
hardFailed = true;
169
+
break;
170
+
} else {
171
+
continue;
172
+
}
173
+
}
174
+
} else if (replace.type === PatchReplaceType.Module) {
175
+
// Directly replace the module with a new one
176
+
const newModule = replace.replacement(replaced);
177
+
entry[id] = newModule;
178
+
entry[id].__moonlight = true;
179
+
replaced = newModule.toString().replace(/\n/g, "");
180
+
swappedModule = true;
96
181
}
182
+
}
97
183
98
-
// Store what extensions patched what modules for easier debugging
99
-
patched[id] = patched[id] || [];
100
-
patched[id].push(`${patch.ext}#${patch.id}`);
184
+
if (!hardFailed) {
185
+
moduleString = replaced;
186
+
modified = true;
187
+
exts.add(patch.ext);
188
+
}
101
189
102
-
// Webpack module arguments are minified, so we replace them with consistent names
103
-
// We have to wrap it so things don't break, though
104
-
const patchedStr = patched[id].sort().join(", ");
190
+
if (isPatched) moonlight.unpatched.delete(patch);
191
+
if (shouldRemove) patches.splice(i--, 1);
192
+
}
193
+
}
105
194
106
-
const wrapped =
107
-
`(${replaced}).apply(this, arguments)\n` +
108
-
`// Patched by moonlight: ${patchedStr}\n` +
109
-
`//# sourceURL=Webpack-Module-${id}`;
195
+
if (modified) {
196
+
let shouldCache = true;
197
+
if (!swappedModule) shouldCache = patchModule(id, patchedStr.join(", "), moduleString, entry);
198
+
if (shouldCache) moduleCache[id] = moduleString;
199
+
moonlight.patched.set(id, exts);
200
+
}
110
201
111
-
try {
112
-
const func = new Function(
113
-
"module",
114
-
"exports",
115
-
"require",
116
-
wrapped
117
-
) as WebpackModuleFunc;
118
-
entry[id] = func;
119
-
entry[id].__moonlight = true;
120
-
moduleString = replaced;
121
-
} catch (e) {
122
-
logger.warn("Error constructing function for patch", e);
202
+
try {
203
+
const parsed = moonlight.lunast.parseScript(id, moduleString);
204
+
if (parsed != null) {
205
+
for (const [parsedId, parsedScript] of Object.entries(parsed)) {
206
+
if (patchModule(parsedId, "lunast", parsedScript, entry)) {
207
+
moduleCache[parsedId] = parsedScript;
123
208
}
124
-
} else if (replace.type === PatchReplaceType.Module) {
125
-
// Directly replace the module with a new one
126
-
const newModule = replace.replacement(moduleString);
127
-
entry[id] = newModule;
128
-
entry[id].__moonlight = true;
129
-
moduleString =
130
-
newModule.toString().replace(/\n/g, "") +
131
-
`//# sourceURL=Webpack-Module-${id}`;
132
-
}
133
-
134
-
if (shouldRemove) {
135
-
patches.splice(
136
-
patches.findIndex((p) => p.ext === patch.ext && p.id === patch.id),
137
-
1
138
-
);
139
209
}
140
210
}
211
+
} catch (e) {
212
+
logger.error("Failed to parse script for LunAST", id, e);
141
213
}
142
214
143
215
if (moonlightNode.config.patchAll === true) {
144
-
if (
145
-
(typeof id !== "string" || !id.includes("_")) &&
146
-
!entry[id].__moonlight
147
-
) {
148
-
const wrapped =
149
-
`(${moduleString}).apply(this, arguments)\n` +
150
-
`//# sourceURL=Webpack-Module-${id}`;
151
-
entry[id] = new Function(
152
-
"module",
153
-
"exports",
154
-
"require",
155
-
wrapped
156
-
) as WebpackModuleFunc;
216
+
if ((typeof id !== "string" || !id.includes("_")) && !entry[id].__moonlight) {
217
+
const wrapped = `(${moduleCache[id]}).apply(this, arguments)\n` + createSourceURL(id);
218
+
entry[id] = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
157
219
entry[id].__moonlight = true;
158
220
}
159
221
}
160
222
223
+
// Dispatch module load event subscription
224
+
if (moduleLoadSubscriptions.has(id)) {
225
+
const loadCallbacks = moduleLoadSubscriptions.get(id)!;
226
+
for (const callback of loadCallbacks) {
227
+
try {
228
+
callback(id);
229
+
} catch (e) {
230
+
logger.error("Error in module load subscription: " + e);
231
+
}
232
+
}
233
+
moduleLoadSubscriptions.delete(id);
234
+
}
235
+
161
236
moduleCache[id] = moduleString;
162
237
}
163
238
}
···
169
244
*/
170
245
let chunkId = Number.MAX_SAFE_INTEGER;
171
246
247
+
function depToString(x: ExplicitExtensionDependency) {
248
+
return x.ext != null ? `${x.ext}_${x.id}` : x.id;
249
+
}
250
+
172
251
function handleModuleDependencies() {
173
252
const modules = Array.from(webpackModules.values());
174
253
175
-
const dependencies: Dependency<string, IdentifiedWebpackModule>[] =
176
-
modules.map((wp) => {
177
-
return {
178
-
id: `${wp.ext}_${wp.id}`,
179
-
data: wp
180
-
};
181
-
});
254
+
const dependencies: Dependency<string, IdentifiedWebpackModule>[] = modules.map((wp) => {
255
+
return {
256
+
id: depToString(wp),
257
+
data: wp
258
+
};
259
+
});
182
260
183
261
const [sorted, _] = calculateDependencies(dependencies, {
184
262
fetchDep: (id) => {
185
-
return modules.find((x) => id === `${x.ext}_${x.id}`) ?? null;
263
+
return modules.find((x) => id === depToString(x)) ?? null;
186
264
},
187
265
188
266
getDeps: (item) => {
189
267
const deps = item.data?.dependencies ?? [];
190
268
return (
191
269
deps.filter(
192
-
(dep) => !(dep instanceof RegExp || typeof dep === "string")
270
+
(dep) => !(dep instanceof RegExp || typeof dep === "string") && dep.ext != null
193
271
) as ExplicitExtensionDependency[]
194
-
).map((x) => `${x.ext}_${x.id}`);
272
+
).map(depToString);
195
273
}
196
274
});
197
275
···
206
284
207
285
for (const [_modId, mod] of Object.entries(entry)) {
208
286
const modStr = mod.toString();
209
-
const wpModules = Array.from(webpackModules.values());
210
-
for (const wpModule of wpModules) {
211
-
const id = wpModule.ext + "_" + wpModule.id;
287
+
for (const wpModule of webpackModules) {
288
+
const id = depToString(wpModule);
212
289
if (wpModule.dependencies) {
213
290
const deps = new Set(wpModule.dependencies);
214
291
215
292
// FIXME: This dependency resolution might fail if the things we want
216
293
// got injected earlier. If weird dependencies fail, this is likely why.
217
294
if (deps.size) {
218
-
for (const dep of deps.values()) {
295
+
for (const dep of deps) {
219
296
if (typeof dep === "string") {
220
297
if (modStr.includes(dep)) deps.delete(dep);
221
298
} else if (dep instanceof RegExp) {
222
299
if (dep.test(modStr)) deps.delete(dep);
223
300
} else if (
224
-
injectedWpModules.find(
225
-
(x) => x.ext === dep.ext && x.id === dep.id
226
-
)
301
+
dep.ext != null
302
+
? injectedWpModules.find((x) => x.ext === dep.ext && x.id === dep.id)
303
+
: injectedWpModules.find((x) => x.id === dep.id)
227
304
) {
228
305
deps.delete(dep);
229
306
}
230
307
}
231
308
309
+
wpModule.dependencies = Array.from(deps);
232
310
if (deps.size !== 0) {
233
-
// Update the deps that have passed
234
-
webpackModules.delete(wpModule);
235
-
wpModule.dependencies = Array.from(deps);
236
-
webpackModules.add(wpModule);
237
311
continue;
238
312
}
239
-
240
-
wpModule.dependencies = Array.from(deps);
241
313
}
242
314
}
243
315
244
316
webpackModules.delete(wpModule);
317
+
moonlight.pendingModules.delete(wpModule);
245
318
injectedWpModules.push(wpModule);
246
319
247
320
inject = true;
248
321
249
-
if (wpModule.run) modules[id] = wpModule.run;
250
-
if (wpModule.entrypoint) entrypoints.push(id);
322
+
if (wpModule.run) {
323
+
modules[id] = wpModule.run;
324
+
wpModule.run.__moonlight = true;
325
+
// @ts-expect-error hacks
326
+
wpModule.run.call = function (self, module, exports, require) {
327
+
try {
328
+
wpModule.run!.apply(self, [module, exports, require]);
329
+
} catch (err) {
330
+
logger.error(`Failed to run module "${id}":`, err);
331
+
}
332
+
};
333
+
if (wpModule.entrypoint) entrypoints.push(id);
334
+
}
251
335
}
252
336
if (!webpackModules.size) break;
253
337
}
254
338
339
+
for (const [name, func] of Object.entries(moonlight.moonmap.getWebpackModules("window.moonlight.moonmap"))) {
340
+
// @ts-expect-error probably should fix the type on this idk
341
+
func.__moonlight = true;
342
+
injectedWpModules.push({ id: name, run: func });
343
+
modules[name] = func;
344
+
inject = true;
345
+
}
346
+
347
+
if (webpackRequire != null) {
348
+
for (const id of moonlight.moonmap.getLazyModules()) {
349
+
webpackRequire.e(id);
350
+
}
351
+
}
352
+
255
353
if (inject) {
256
354
logger.debug("Injecting modules:", modules, entrypoints);
257
355
window.webpackChunkdiscord_app.push([
258
356
[--chunkId],
259
357
modules,
260
-
(require: typeof WebpackRequire) => entrypoints.map(require)
358
+
(require: WebpackRequireType) =>
359
+
entrypoints.map((id) => {
360
+
try {
361
+
if (require.m[id] == null) {
362
+
logger.error(`Failing to load entrypoint module "${id}" because it's not found in Webpack.`);
363
+
} else {
364
+
require(id);
365
+
}
366
+
} catch (err) {
367
+
logger.error(`Failed to load entrypoint module "${id}":`, err);
368
+
}
369
+
})
261
370
]);
262
371
}
263
372
}
···
268
377
}
269
378
}
270
379
380
+
function moduleSourceGetter(id: string) {
381
+
return moduleCache[id] ?? null;
382
+
}
383
+
271
384
/*
272
385
Webpack modules are bundled into an array of arrays that hold each function.
273
386
Since we run code before Discord, we can create our own Webpack array and
···
279
392
export async function installWebpackPatcher() {
280
393
await handleModuleDependencies();
281
394
395
+
moonlight.lunast.setModuleSourceGetter(moduleSourceGetter);
396
+
moonlight.moonmap.setModuleSourceGetter(moduleSourceGetter);
397
+
398
+
const wpRequireFetcher: WebpackModuleFunc = (module, exports, require) => {
399
+
webpackRequire = require;
400
+
};
401
+
wpRequireFetcher.__moonlight = true;
402
+
webpackModules.add({
403
+
id: "moonlight",
404
+
entrypoint: true,
405
+
run: wpRequireFetcher
406
+
});
407
+
282
408
let realWebpackJsonp: WebpackJsonp | null = null;
283
409
Object.defineProperty(window, "webpackChunkdiscord_app", {
284
410
set: (jsonp: WebpackJsonp) => {
···
290
416
const realPush = jsonp.push;
291
417
if (jsonp.push.__moonlight !== true) {
292
418
jsonp.push = (items) => {
419
+
moonlight.events.dispatchEvent(WebEventType.ChunkLoad, {
420
+
chunkId: items[0],
421
+
modules: items[1],
422
+
require: items[2]
423
+
});
424
+
293
425
patchModules(items[1]);
294
426
295
427
try {
···
327
459
}
328
460
});
329
461
330
-
registerWebpackModule({
331
-
ext: "moonlight",
332
-
id: "fix_rspack_init_modules",
333
-
entrypoint: true,
334
-
run: function (module, exports, require) {
335
-
patchModules(require.m);
462
+
Object.defineProperty(Function.prototype, "m", {
463
+
configurable: true,
464
+
set(modules: any) {
465
+
const { stack } = new Error();
466
+
if (stack!.includes("/assets/") && !Array.isArray(modules)) {
467
+
moonlight.events.dispatchEvent(WebEventType.ChunkLoad, {
468
+
modules: modules
469
+
});
470
+
patchModules(modules);
471
+
472
+
if (!window.webpackChunkdiscord_app) window.webpackChunkdiscord_app = [];
473
+
injectModules(modules);
474
+
}
475
+
476
+
Object.defineProperty(this, "m", {
477
+
value: modules,
478
+
configurable: true,
479
+
enumerable: true,
480
+
writable: true
481
+
});
336
482
}
337
483
});
338
484
}
+49
packages/core/src/persist.ts
+49
packages/core/src/persist.ts
···
1
+
import { join, dirname } from "node:path";
2
+
import { mkdirSync, renameSync, existsSync, copyFileSync, readdirSync } from "node:fs";
3
+
import Logger from "./util/logger";
4
+
5
+
const logger = new Logger("core/persist");
6
+
7
+
export default function persist(asarPath: string) {
8
+
try {
9
+
if (process.platform === "win32") {
10
+
persistWin32(asarPath);
11
+
}
12
+
} catch (e) {
13
+
logger.error(`Failed to persist moonlight: ${e}`);
14
+
}
15
+
}
16
+
17
+
function persistWin32(asarPath: string) {
18
+
const updaterModule = require(join(asarPath, "common", "updater"));
19
+
const updater = updaterModule.Updater;
20
+
21
+
const currentAppDir = join(dirname(asarPath), "app");
22
+
23
+
const realEmit = updater.prototype.emit;
24
+
updater.prototype.emit = function (event: string, ...args: any[]) {
25
+
if (event === "host-updated") {
26
+
const versions = this.queryCurrentVersionsSync();
27
+
28
+
const newRootDir = join(this.rootPath, "app-" + versions.current_host.map((v: number) => v.toString()).join("."));
29
+
logger.info(`Persisting moonlight - new root dir: ${newRootDir}`);
30
+
31
+
const newResources = join(newRootDir, "resources");
32
+
33
+
// app.asar -> _app.asar
34
+
const newAsar = join(newResources, "app.asar");
35
+
const newRenamedAsar = join(newResources, "_app.asar");
36
+
if (!existsSync(newRenamedAsar)) renameSync(newAsar, newRenamedAsar);
37
+
38
+
// copy the already existing app dir so we don't have to figure out the moonlight dir
39
+
const newAppDir = join(newResources, "app");
40
+
if (!existsSync(newAppDir)) mkdirSync(newAppDir);
41
+
42
+
for (const file of readdirSync(currentAppDir)) {
43
+
copyFileSync(join(currentAppDir, file), join(newAppDir, file));
44
+
}
45
+
}
46
+
47
+
return realEmit.call(this, event, ...args);
48
+
};
49
+
}
+63
packages/core/src/util/binary.ts
+63
packages/core/src/util/binary.ts
···
1
+
// https://github.com/NotNite/brc-save-editor/blob/main/src/lib/binary.ts
2
+
export interface BinaryInterface {
3
+
data: Uint8Array;
4
+
view: DataView;
5
+
length: number;
6
+
position: number;
7
+
}
8
+
9
+
export class BinaryReader implements BinaryInterface {
10
+
data: Uint8Array;
11
+
view: DataView;
12
+
length: number;
13
+
position: number;
14
+
15
+
constructor(data: Uint8Array) {
16
+
this.data = data;
17
+
this.view = new DataView(data.buffer);
18
+
19
+
this.length = data.length;
20
+
this.position = 0;
21
+
}
22
+
23
+
readByte() {
24
+
return this._read(this.view.getInt8, 1);
25
+
}
26
+
27
+
readBoolean() {
28
+
return this.readByte() !== 0;
29
+
}
30
+
31
+
readInt32() {
32
+
return this._read(this.view.getInt32, 4);
33
+
}
34
+
35
+
readUInt32() {
36
+
return this._read(this.view.getUint32, 4);
37
+
}
38
+
39
+
readSingle() {
40
+
return this._read(this.view.getFloat32, 4);
41
+
}
42
+
43
+
readInt64() {
44
+
return this._read(this.view.getBigInt64, 8);
45
+
}
46
+
47
+
readString(length: number) {
48
+
const result = this.read(length);
49
+
return new TextDecoder().decode(result);
50
+
}
51
+
52
+
read(length: number) {
53
+
const data = this.data.subarray(this.position, this.position + length);
54
+
this.position += length;
55
+
return data;
56
+
}
57
+
58
+
private _read<T>(func: (position: number, littleEndian?: boolean) => T, length: number): T {
59
+
const result = func.call(this.view, this.position, true);
60
+
this.position += length;
61
+
return result;
62
+
}
63
+
}
packages/core/src/util/clone.ts
packages/core/src/util/clone.ts
This is a binary file and will not be displayed.
+39
packages/core/src/util/config.ts
+39
packages/core/src/util/config.ts
···
1
+
import type { Config, DetectedExtension, ExtensionManifest } from "@moonlight-mod/types";
2
+
3
+
export function getManifest(extensions: DetectedExtension[], ext: string) {
4
+
return extensions.find((x) => x.id === ext)?.manifest;
5
+
}
6
+
7
+
export function getConfig(ext: string, config: Config) {
8
+
const val = config.extensions[ext];
9
+
if (val == null || typeof val === "boolean") return undefined;
10
+
return val.config;
11
+
}
12
+
13
+
export function getConfigOption<T>(
14
+
ext: string,
15
+
key: string,
16
+
config: Config,
17
+
settings?: ExtensionManifest["settings"]
18
+
): T | undefined {
19
+
const defaultValue: T | undefined = structuredClone(settings?.[key]?.default);
20
+
const cfg = getConfig(ext, config);
21
+
if (cfg == null || typeof cfg === "boolean") return defaultValue;
22
+
return cfg?.[key] ?? defaultValue;
23
+
}
24
+
25
+
export function setConfigOption<T>(config: Config, ext: string, key: string, value: T) {
26
+
const oldConfig = config.extensions[ext];
27
+
const newConfig =
28
+
typeof oldConfig === "boolean"
29
+
? {
30
+
enabled: oldConfig,
31
+
config: { [key]: value }
32
+
}
33
+
: {
34
+
...oldConfig,
35
+
config: { ...(oldConfig?.config ?? {}), [key]: value }
36
+
};
37
+
38
+
config.extensions[ext] = newConfig;
39
+
}
+25
-28
packages/core/src/util/data.ts
+25
-28
packages/core/src/util/data.ts
···
1
1
import { constants } from "@moonlight-mod/types";
2
-
import requireImport from "./import";
2
+
3
+
export async function getMoonlightDir() {
4
+
browser: {
5
+
return "/";
6
+
}
3
7
4
-
export function getMoonlightDir(): string {
5
8
const electron = require("electron");
6
-
const fs = requireImport("fs");
7
-
const path = requireImport("path");
8
9
9
10
let appData = "";
10
11
injector: {
···
15
16
appData = electron.ipcRenderer.sendSync(constants.ipcGetAppData);
16
17
}
17
18
18
-
const dir = path.join(appData, "moonlight-mod");
19
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
19
+
const dir = moonlightNodeSandboxed.fs.join(appData, "moonlight-mod");
20
+
if (!(await moonlightNodeSandboxed.fs.exists(dir))) await moonlightNodeSandboxed.fs.mkdir(dir);
20
21
21
22
return dir;
22
23
}
···
26
27
version: string;
27
28
};
28
29
29
-
export function getConfigPath(): string {
30
-
const dir = getMoonlightDir();
31
-
const fs = requireImport("fs");
32
-
const path = requireImport("path");
30
+
export async function getConfigPath() {
31
+
browser: {
32
+
return "/config.json";
33
+
}
34
+
35
+
const dir = await getMoonlightDir();
33
36
34
37
let configPath = "";
35
38
36
-
const buildInfoPath = path.join(process.resourcesPath, "build_info.json");
37
-
if (!fs.existsSync(buildInfoPath)) {
38
-
configPath = path.join(dir, "desktop.json");
39
+
const buildInfoPath = moonlightNodeSandboxed.fs.join(process.resourcesPath, "build_info.json");
40
+
if (!(await moonlightNodeSandboxed.fs.exists(buildInfoPath))) {
41
+
configPath = moonlightNodeSandboxed.fs.join(dir, "desktop.json");
39
42
} else {
40
-
const buildInfo: BuildInfo = JSON.parse(
41
-
fs.readFileSync(buildInfoPath, "utf8")
42
-
);
43
-
configPath = path.join(dir, buildInfo.releaseChannel + ".json");
43
+
const buildInfo: BuildInfo = JSON.parse(await moonlightNodeSandboxed.fs.readFileString(buildInfoPath));
44
+
configPath = moonlightNodeSandboxed.fs.join(dir, buildInfo.releaseChannel + ".json");
44
45
}
45
46
46
47
return configPath;
47
48
}
48
49
49
-
function getPathFromMoonlight(...names: string[]): string {
50
-
const dir = getMoonlightDir();
51
-
const fs = requireImport("fs");
52
-
const path = requireImport("path");
50
+
async function getPathFromMoonlight(...names: string[]) {
51
+
const dir = await getMoonlightDir();
53
52
54
-
const target = path.join(dir, ...names);
55
-
if (!fs.existsSync(target)) fs.mkdirSync(target);
53
+
const target = moonlightNodeSandboxed.fs.join(dir, ...names);
54
+
if (!(await moonlightNodeSandboxed.fs.exists(target))) await moonlightNodeSandboxed.fs.mkdir(target);
56
55
57
56
return target;
58
57
}
59
58
60
-
export function getExtensionsPath(): string {
61
-
return getPathFromMoonlight(constants.extensionsDir);
59
+
export async function getExtensionsPath() {
60
+
return await getPathFromMoonlight(constants.extensionsDir);
62
61
}
63
62
64
63
export function getCoreExtensionsPath(): string {
65
-
const path = requireImport("path");
66
-
const a = path.join(__dirname, constants.coreExtensionsDir);
67
-
return a;
64
+
return moonlightNodeSandboxed.fs.join(__dirname, constants.coreExtensionsDir);
68
65
}
+4
-14
packages/core/src/util/dependency.ts
+4
-14
packages/core/src/util/dependency.ts
···
35
35
const fullDeps: Set<T> = new Set();
36
36
let failed = false;
37
37
38
-
// eslint-disable-next-line no-inner-declarations
39
38
function resolveDeps(id: T, root: boolean) {
40
39
if (id === item.id && !root) {
41
40
logger.warn(`Circular dependency detected: "${item.id}"`);
···
113
112
logger.trace("Enabled stage", itemsOrig);
114
113
const implicitlyEnabled: T[] = [];
115
114
116
-
// eslint-disable-next-line no-inner-declarations
117
115
function validateDeps(dep: Dependency<T, D>) {
118
116
if (getEnabled!(dep)) {
119
117
const deps = dependencyGraphOrig.get(dep.id)!;
···
122
120
validateDeps({ id, data });
123
121
}
124
122
} else {
125
-
const dependsOnMe = Array.from(dependencyGraphOrig.entries()).filter(
126
-
([, v]) => v?.has(dep.id)
127
-
);
123
+
const dependsOnMe = Array.from(dependencyGraphOrig.entries()).filter(([, v]) => v?.has(dep.id));
128
124
129
125
if (dependsOnMe.length > 0) {
130
126
logger.debug("Implicitly enabling dependency", dep.id);
···
134
130
}
135
131
136
132
for (const dep of itemsOrig) validateDeps(dep);
137
-
itemsOrig = itemsOrig.filter(
138
-
(x) => getEnabled(x) || implicitlyEnabled.includes(x.id)
139
-
);
133
+
itemsOrig = itemsOrig.filter((x) => getEnabled(x) || implicitlyEnabled.includes(x.id));
140
134
}
141
135
142
136
if (getIncompatible != null) {
···
176
170
dependencyGraph.set(item.id, new Set(dependencyGraph.get(item.id)));
177
171
}
178
172
179
-
while (
180
-
Array.from(dependencyGraph.values()).filter((x) => x != null).length > 0
181
-
) {
182
-
const noDependents = items.filter(
183
-
(e) => dependencyGraph.get(e.id)?.size === 0
184
-
);
173
+
while (Array.from(dependencyGraph.values()).filter((x) => x != null).length > 0) {
174
+
const noDependents = items.filter((e) => dependencyGraph.get(e.id)?.size === 0);
185
175
186
176
if (noDependents.length === 0) {
187
177
logger.warn("Stuck dependency graph detected", dependencyGraph);
+48
-54
packages/core/src/util/event.ts
+48
-54
packages/core/src/util/event.ts
···
1
-
export type MoonlightEventCallback = (data: string) => void;
1
+
import { MoonlightEventEmitter } from "@moonlight-mod/types/core/event";
2
2
3
-
export interface MoonlightEventEmitter {
4
-
dispatchEvent: (id: string, data: string) => void;
5
-
addEventListener: (id: string, cb: MoonlightEventCallback) => void;
6
-
removeEventListener: (id: string, cb: MoonlightEventCallback) => void;
7
-
}
3
+
export function createEventEmitter<
4
+
EventId extends string = string,
5
+
EventData = Record<EventId, any>
6
+
>(): MoonlightEventEmitter<EventId, EventData> {
7
+
webTarget: {
8
+
const eventEmitter = new EventTarget();
9
+
const listeners = new Map<(data: EventData) => void, (e: Event) => void>();
8
10
9
-
function nodeMethod(): MoonlightEventEmitter {
10
-
const EventEmitter = require("events");
11
-
const eventEmitter = new EventEmitter();
12
-
const listeners = new Map<MoonlightEventCallback, (...args: any[]) => void>();
11
+
return {
12
+
dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => {
13
+
eventEmitter.dispatchEvent(new CustomEvent(id as string, { detail: data }));
14
+
},
13
15
14
-
return {
15
-
dispatchEvent: (id: string, data: string) => {
16
-
eventEmitter.emit(id, data);
17
-
},
16
+
addEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => {
17
+
const untyped = cb as (data: EventData) => void;
18
+
if (listeners.has(untyped)) return;
18
19
19
-
addEventListener: (id: string, cb: (data: string) => void) => {
20
-
if (listeners.has(cb)) return;
20
+
function listener(e: Event) {
21
+
const event = e as CustomEvent<string>;
22
+
cb(event.detail as EventData[Id]);
23
+
}
21
24
22
-
function listener(data: string) {
23
-
cb(data);
24
-
}
25
+
listeners.set(untyped, listener);
26
+
eventEmitter.addEventListener(id as string, listener);
27
+
},
25
28
26
-
listeners.set(cb, listener);
27
-
eventEmitter.on(id, listener);
28
-
},
29
-
30
-
removeEventListener: (id: string, cb: (data: string) => void) => {
31
-
const listener = listeners.get(cb);
32
-
if (listener == null) return;
33
-
listeners.delete(cb);
34
-
eventEmitter.off(id, listener);
35
-
}
36
-
};
37
-
}
29
+
removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => {
30
+
const untyped = cb as (data: EventData) => void;
31
+
const listener = listeners.get(untyped);
32
+
if (listener == null) return;
33
+
listeners.delete(untyped);
34
+
eventEmitter.removeEventListener(id as string, listener);
35
+
}
36
+
};
37
+
}
38
38
39
-
export function createEventEmitter(): MoonlightEventEmitter {
40
-
webPreload: {
41
-
const eventEmitter = new EventTarget();
42
-
const listeners = new Map<MoonlightEventCallback, (e: Event) => void>();
39
+
nodeTarget: {
40
+
const EventEmitter = require("events");
41
+
const eventEmitter = new EventEmitter();
42
+
const listeners = new Map<(data: EventData) => void, (e: Event) => void>();
43
43
44
44
return {
45
-
dispatchEvent: (id: string, data: string) => {
46
-
eventEmitter.dispatchEvent(new CustomEvent(id, { detail: data }));
45
+
dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => {
46
+
eventEmitter.emit(id as string, data);
47
47
},
48
48
49
-
addEventListener: (id: string, cb: (data: string) => void) => {
50
-
if (listeners.has(cb)) return;
49
+
addEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => {
50
+
const untyped = cb as (data: EventData) => void;
51
+
if (listeners.has(untyped)) return;
51
52
52
53
function listener(e: Event) {
53
54
const event = e as CustomEvent<string>;
54
-
cb(event.detail);
55
+
cb(event as EventData[Id]);
55
56
}
56
57
57
-
listeners.set(cb, listener);
58
-
eventEmitter.addEventListener(id, listener);
58
+
listeners.set(untyped, listener);
59
+
eventEmitter.on(id as string, listener);
59
60
},
60
61
61
-
removeEventListener: (id: string, cb: (data: string) => void) => {
62
-
const listener = listeners.get(cb);
62
+
removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => {
63
+
const untyped = cb as (data: EventData) => void;
64
+
const listener = listeners.get(untyped);
63
65
if (listener == null) return;
64
-
listeners.delete(cb);
65
-
eventEmitter.removeEventListener(id, listener);
66
+
listeners.delete(untyped);
67
+
eventEmitter.off(id as string, listener);
66
68
}
67
69
};
68
-
}
69
-
70
-
nodePreload: {
71
-
return nodeMethod();
72
-
}
73
-
74
-
injector: {
75
-
return nodeMethod();
76
70
}
77
71
78
72
throw new Error("Called createEventEmitter() in an impossible environment");
+3
-5
packages/core/src/util/import.ts
+3
-5
packages/core/src/util/import.ts
···
9
9
cemented if import is passed a string literal.
10
10
*/
11
11
12
-
const canRequire = ["path", "fs"] as const;
13
-
type CanRequire = (typeof canRequire)[number];
12
+
const _canRequire = ["path", "fs"] as const;
13
+
type CanRequire = (typeof _canRequire)[number];
14
14
15
15
type ImportTypes = {
16
16
path: typeof import("path");
17
17
fs: typeof import("fs");
18
18
};
19
19
20
-
export default function requireImport<T extends CanRequire>(
21
-
type: T
22
-
): Awaited<ImportTypes[T]> {
20
+
export default function requireImport<T extends CanRequire>(type: T): Awaited<ImportTypes[T]> {
23
21
return require(type);
24
22
}
+12
-16
packages/core/src/util/logger.ts
+12
-16
packages/core/src/util/logger.ts
···
1
1
/* eslint-disable no-console */
2
2
import { LogLevel } from "@moonlight-mod/types/logger";
3
-
import { readConfig } from "../config";
3
+
import { Config } from "@moonlight-mod/types";
4
4
5
5
const colors = {
6
6
[LogLevel.SILLY]: "#EDD3E9",
···
11
11
[LogLevel.ERROR]: "#FF0000"
12
12
};
13
13
14
-
const config = readConfig();
15
14
let maxLevel = LogLevel.INFO;
16
-
if (config.loggerLevel != null) {
17
-
const enumValue =
18
-
LogLevel[config.loggerLevel.toUpperCase() as keyof typeof LogLevel];
19
-
if (enumValue != null) {
20
-
maxLevel = enumValue;
21
-
}
22
-
}
23
15
24
16
export default class Logger {
25
17
private name: string;
···
57
49
const logLevel = LogLevel[level].toUpperCase();
58
50
if (maxLevel > level) return;
59
51
60
-
if (MOONLIGHT_WEB_PRELOAD) {
61
-
args = [
62
-
`%c[${logLevel}]`,
63
-
`background-color: ${colors[level]}; color: #FFFFFF;`,
64
-
`[${this.name}]`,
65
-
...obj
66
-
];
52
+
if (MOONLIGHT_WEB_PRELOAD || MOONLIGHT_BROWSER) {
53
+
args = [`%c[${logLevel}]`, `background-color: ${colors[level]}; color: #FFFFFF;`, `[${this.name}]`, ...obj];
67
54
} else {
68
55
args = [`[${logLevel}]`, `[${this.name}]`, ...obj];
69
56
}
···
92
79
}
93
80
}
94
81
}
82
+
83
+
export function initLogger(config: Config) {
84
+
if (config.loggerLevel != null) {
85
+
const enumValue = LogLevel[config.loggerLevel.toUpperCase() as keyof typeof LogLevel];
86
+
if (enumValue != null) {
87
+
maxLevel = enumValue;
88
+
}
89
+
}
90
+
}
+30
packages/core/src/util/patch.ts
+30
packages/core/src/util/patch.ts
···
1
+
import { PatchReplace, PatchReplaceType } from "@moonlight-mod/types";
2
+
3
+
type SingleFind = string | RegExp;
4
+
type Find = SingleFind | SingleFind[];
5
+
6
+
export function processFind<T extends Find>(find: T): T {
7
+
if (Array.isArray(find)) {
8
+
return find.map(processFind) as T;
9
+
} else if (find instanceof RegExp) {
10
+
// Add support for \i to match rspack's minified names
11
+
return new RegExp(find.source.replace(/\\i/g, "[A-Za-z_$][\\w$]*"), find.flags) as T;
12
+
} else {
13
+
return find;
14
+
}
15
+
}
16
+
17
+
export function processReplace(replace: PatchReplace | PatchReplace[]) {
18
+
if (Array.isArray(replace)) {
19
+
replace.forEach(processReplace);
20
+
} else {
21
+
if (replace.type === undefined || replace.type === PatchReplaceType.Normal) {
22
+
replace.match = processFind(replace.match);
23
+
}
24
+
}
25
+
}
26
+
27
+
export function testFind(src: string, find: SingleFind) {
28
+
// indexOf is faster than includes by 0.25% lmao
29
+
return typeof find === "string" ? src.indexOf(find) !== -1 : find.test(src);
30
+
}
+4
-1
packages/core/tsconfig.json
+4
-1
packages/core/tsconfig.json
+11
-2
packages/core-extensions/package.json
+11
-2
packages/core-extensions/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/core-extensions",
3
3
"private": true,
4
+
"engineStrict": true,
5
+
"engines": {
6
+
"node": ">=22",
7
+
"pnpm": ">=10",
8
+
"npm": "pnpm",
9
+
"yarn": "pnpm"
10
+
},
4
11
"dependencies": {
5
-
"@electron/asar": "^3.2.5",
6
-
"@moonlight-mod/types": "workspace:*"
12
+
"@moonlight-mod/core": "workspace:*",
13
+
"@moonlight-mod/types": "workspace:*",
14
+
"microdiff": "catalog:prod",
15
+
"nanotar": "catalog:prod"
7
16
}
8
17
}
+19
packages/core-extensions/src/appPanels/index.ts
+19
packages/core-extensions/src/appPanels/index.ts
···
1
+
import type { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
{
5
+
find: 'setProperty("--custom-app-panels-height"',
6
+
replace: [
7
+
{
8
+
match: /\(0,.\.jsx\)\((.\..),{section:/,
9
+
replacement: (prev, el) => `...require("appPanels_appPanels").default.getPanels(${el}),${prev}`
10
+
}
11
+
]
12
+
}
13
+
];
14
+
15
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
16
+
appPanels: {
17
+
dependencies: [{ id: "react" }]
18
+
}
19
+
};
+11
packages/core-extensions/src/appPanels/manifest.json
+11
packages/core-extensions/src/appPanels/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "appPanels",
4
+
"apiLevel": 2,
5
+
"meta": {
6
+
"name": "App Panels",
7
+
"tagline": "An API for adding panels around the user/voice controls",
8
+
"authors": ["NotNite"],
9
+
"tags": ["library"]
10
+
}
11
+
}
+23
packages/core-extensions/src/appPanels/webpackModules/appPanels.ts
+23
packages/core-extensions/src/appPanels/webpackModules/appPanels.ts
···
1
+
import type { AppPanels as AppPanelsType } from "@moonlight-mod/types/coreExtensions/appPanels";
2
+
import React from "@moonlight-mod/wp/react";
3
+
4
+
const panels: Record<string, React.FC<any>> = {};
5
+
6
+
export const AppPanels: AppPanelsType = {
7
+
addPanel(section, element) {
8
+
panels[section] = element;
9
+
},
10
+
getPanels(panel) {
11
+
return Object.entries(panels).map(([section, element]) =>
12
+
React.createElement(
13
+
panel,
14
+
{
15
+
section
16
+
},
17
+
React.createElement(element)
18
+
)
19
+
);
20
+
}
21
+
};
22
+
23
+
export default AppPanels;
+85
packages/core-extensions/src/commands/index.ts
+85
packages/core-extensions/src/commands/index.ts
···
1
+
import { Patch, ExtensionWebpackModule } from "@moonlight-mod/types";
2
+
import { APPLICATION_ID } from "@moonlight-mod/types/coreExtensions/commands";
3
+
4
+
export const patches: Patch[] = [
5
+
{
6
+
find: ".fI5MTU)", // COMMAND_SECTION_BUILT_IN_NAME
7
+
replace: [
8
+
// inject commands
9
+
{
10
+
match: /return (\i)\.filter/,
11
+
replacement: (orig, commands) =>
12
+
`return [...${commands},...require("commands_commands").default._getCommands()].filter`
13
+
},
14
+
15
+
// section
16
+
{
17
+
match: /(?<=\i={)(?=\[\i\.\i\.BUILT_IN]:{id:\i\.\i\.BUILT_IN,type:(\i.\i\.BUILT_IN))/,
18
+
replacement: (_, type) =>
19
+
`"${APPLICATION_ID}":{id:"${APPLICATION_ID}",type:${type},get name(){return "moonlight"}},`
20
+
}
21
+
]
22
+
},
23
+
24
+
// index our section
25
+
{
26
+
find: '"ApplicationCommandIndexStore"',
27
+
replace: {
28
+
match: /(?<=let \i=(\i)\((\i\.\i)\[\i\.\i\.BUILT_IN\],(\i),!0,!0,(\i)\);)null!=(\i)&&(\i)\.push\(\i\)/,
29
+
replacement: (_, createSection, sections, deny, props, section, commands) =>
30
+
`null!=${section}&&(${section}.data=${section}.data.filter(c=>c.applicationId=="-1"));
31
+
null!=${section}&&${commands}.push(${section});
32
+
const moonlightCommands=${createSection}(${sections}["${APPLICATION_ID}"],${deny},!0,!0,${props});
33
+
null!=moonlightCommands&&(moonlightCommands.data=moonlightCommands.data.filter(c=>c.applicationId=="${APPLICATION_ID}"));
34
+
null!=moonlightCommands&&${commands}.push(moonlightCommands)`
35
+
}
36
+
},
37
+
38
+
// grab legacy commands (needed for adding actions that act like sed/plus reacting)
39
+
{
40
+
find: "={tts:{action:",
41
+
replace: {
42
+
match: /Object\.setPrototypeOf\((\i),null\)/,
43
+
replacement: (_, legacyCommands) => `require("commands_commands")._getLegacyCommands(${legacyCommands})`
44
+
}
45
+
},
46
+
47
+
// add icon
48
+
{
49
+
find: ",hasSpaceTerminator:",
50
+
replace: {
51
+
match: /(\i)\.type===/,
52
+
replacement: (orig, section) => `${section}.id!=="${APPLICATION_ID}"&&${orig}`
53
+
}
54
+
},
55
+
{
56
+
find: ".icon,bot:null==",
57
+
replace: {
58
+
match: /(\.useMemo\(\(\)=>{(var \i;)?)((return |if\()(\i)\.type)/,
59
+
replacement: (_, before, beforeVar, after, afterIf, section) => `${before}
60
+
if (${section}.id==="${APPLICATION_ID}") return "https://moonlight-mod.github.io/favicon.png";
61
+
${after}`
62
+
}
63
+
},
64
+
// fix icon sizing because they expect built in to be 24 and others to be 32
65
+
{
66
+
find: ".builtInSeparator}):null]",
67
+
replace: {
68
+
match: /(\i)\.type===\i\.\i\.BUILT_IN/,
69
+
replacement: (orig, section) => `${section}.id!=="${APPLICATION_ID}"&&${orig}`
70
+
}
71
+
},
72
+
73
+
// tell it this app id is authorized
74
+
{
75
+
find: /let{customInstallUrl:\i,installParams:\i,integrationTypesConfig:\i}/,
76
+
replace: {
77
+
match: /\|\|(\i)===\i\.\i\.BUILT_IN/,
78
+
replacement: (orig, id) => `${orig}||${id}==="${APPLICATION_ID}"`
79
+
}
80
+
}
81
+
];
82
+
83
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
84
+
commands: {}
85
+
};
+11
packages/core-extensions/src/commands/manifest.json
+11
packages/core-extensions/src/commands/manifest.json
+71
packages/core-extensions/src/commands/webpackModules/commands.ts
+71
packages/core-extensions/src/commands/webpackModules/commands.ts
···
1
+
import {
2
+
APPLICATION_ID,
3
+
Commands,
4
+
LegacyCommand,
5
+
RegisteredCommand
6
+
} from "@moonlight-mod/types/coreExtensions/commands";
7
+
8
+
type LegacyCommands = Record<string, LegacyCommand>;
9
+
let legacyCommands: LegacyCommands | undefined;
10
+
let queuedLegacyCommands: Record<string, LegacyCommand> | null = {};
11
+
12
+
const registeredCommands: RegisteredCommand[] = [];
13
+
14
+
export function _getLegacyCommands(commands: LegacyCommands) {
15
+
legacyCommands = commands;
16
+
if (queuedLegacyCommands != null) {
17
+
for (const [key, value] of Object.entries(queuedLegacyCommands)) {
18
+
legacyCommands[key] = value;
19
+
}
20
+
queuedLegacyCommands = null;
21
+
}
22
+
}
23
+
24
+
export const commands: Commands = {
25
+
registerCommand(command) {
26
+
const registered: RegisteredCommand = {
27
+
...command,
28
+
untranslatedName: command.id,
29
+
displayName: command.id,
30
+
applicationId: APPLICATION_ID,
31
+
untranslatedDescription: command.description,
32
+
displayDescription: command.description,
33
+
options: command.options?.map((o) => ({
34
+
...o,
35
+
displayName: o.name,
36
+
displayDescription: o.description
37
+
}))
38
+
};
39
+
registeredCommands.push(registered);
40
+
},
41
+
42
+
registerLegacyCommand(id, command) {
43
+
if (command.match) {
44
+
if (command.match instanceof RegExp) {
45
+
command.match = this.anyScopeRegex(command.match);
46
+
} else if (command.match.regex && typeof command.match !== "function") {
47
+
command.match = this.anyScopeRegex(command.match.regex);
48
+
}
49
+
}
50
+
51
+
if (!legacyCommands) {
52
+
queuedLegacyCommands![id] = command;
53
+
} else {
54
+
legacyCommands[id] = command;
55
+
}
56
+
},
57
+
58
+
anyScopeRegex(regex) {
59
+
const out = function (str: string) {
60
+
return regex.exec(str);
61
+
};
62
+
out.regex = regex;
63
+
return out;
64
+
},
65
+
66
+
_getCommands() {
67
+
return [...registeredCommands];
68
+
}
69
+
};
70
+
71
+
export default commands;
+6
-32
packages/core-extensions/src/common/index.ts
+6
-32
packages/core-extensions/src/common/index.ts
···
1
1
import { ExtensionWebExports } from "@moonlight-mod/types";
2
2
3
3
export const webpackModules: ExtensionWebExports["webpackModules"] = {
4
-
components: {
5
-
dependencies: [
6
-
{ ext: "spacepack", id: "spacepack" },
7
-
"MasonryList:",
8
-
".flexGutterSmall,"
9
-
]
10
-
},
11
-
12
-
flux: {
13
-
dependencies: [
14
-
{ ext: "spacepack", id: "spacepack" },
15
-
"useStateFromStores:function"
16
-
]
17
-
},
18
-
19
-
fluxDispatcher: {
20
-
dependencies: [
21
-
{ ext: "spacepack", id: "spacepack" },
22
-
"isDispatching",
23
-
"dispatch"
24
-
]
4
+
stores: {
5
+
dependencies: [{ id: "discord/packages/flux" }]
25
6
},
26
-
27
-
react: {
28
-
dependencies: [
29
-
{ ext: "spacepack", id: "spacepack" },
30
-
"__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
31
-
/\.?version(?:=|:)/,
32
-
/\.?createElement(?:=|:)/
33
-
]
7
+
ErrorBoundary: {
8
+
dependencies: [{ id: "react" }]
34
9
},
35
-
36
-
stores: {
37
-
dependencies: [{ ext: "common", id: "flux" }]
10
+
icons: {
11
+
dependencies: [{ id: "react" }, { id: "discord/components/common/index" }]
38
12
}
39
13
};
+3
-1
packages/core-extensions/src/common/manifest.json
+3
-1
packages/core-extensions/src/common/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "common",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Common",
5
-
"tagline": "A *lot* of common clientmodding utilities from the Discord client",
7
+
"tagline": "Common client modding utilities for the Discord client",
6
8
"authors": ["Cynosphere", "NotNite"],
7
9
"tags": ["library"]
8
10
},
+27
packages/core-extensions/src/common/style.css
+27
packages/core-extensions/src/common/style.css
···
1
+
.moonlight-error-boundary {
2
+
margin: 0 0 15px;
3
+
padding: 10px;
4
+
border-radius: 5px;
5
+
font-size: 1rem;
6
+
font-weight: 300;
7
+
line-height: 22px;
8
+
color: var(--text-normal, white);
9
+
background: hsl(var(--red-400-hsl) / 0.1);
10
+
border: 2px solid hsl(var(--red-400-hsl) / 0.5);
11
+
12
+
.theme-light & {
13
+
color: var(--text-normal, black) !important;
14
+
}
15
+
16
+
& > h3 {
17
+
margin-bottom: 0.25rem;
18
+
}
19
+
20
+
& > .hljs {
21
+
background: var(--background-secondary);
22
+
border: 1px solid var(--background-tertiary);
23
+
white-space: pre-wrap;
24
+
font-family: var(--font-code);
25
+
user-select: text;
26
+
}
27
+
}
+47
packages/core-extensions/src/common/webpackModules/ErrorBoundary.tsx
+47
packages/core-extensions/src/common/webpackModules/ErrorBoundary.tsx
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import { ErrorBoundaryProps, ErrorBoundaryState } from "@moonlight-mod/types/coreExtensions/common";
3
+
4
+
const logger = moonlight.getLogger("ErrorBoundary");
5
+
6
+
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
7
+
constructor(props: ErrorBoundaryProps) {
8
+
super(props);
9
+
this.state = {
10
+
errored: false,
11
+
error: undefined,
12
+
componentStack: undefined
13
+
};
14
+
}
15
+
16
+
static getDerivedStateFromError(error: Error) {
17
+
return {
18
+
errored: true,
19
+
error
20
+
};
21
+
}
22
+
23
+
componentDidCatch(error: Error, { componentStack }: { componentStack: string }) {
24
+
logger.error(`${error}\n\nComponent stack:\n${componentStack}`);
25
+
this.setState({ error, componentStack });
26
+
}
27
+
28
+
render() {
29
+
const { noop, fallback: FallbackComponent, children, message } = this.props;
30
+
const { errored, error, componentStack } = this.state;
31
+
32
+
if (FallbackComponent) return <FallbackComponent children={children} {...this.state} />;
33
+
34
+
if (errored) {
35
+
return noop ? null : (
36
+
<div className={`moonlight-error-boundary`}>
37
+
<h3>{message ?? "An error occurred rendering this component:"}</h3>
38
+
<code className="hljs">{`${error}\n\nComponent stack:\n${componentStack}`}</code>
39
+
</div>
40
+
);
41
+
}
42
+
43
+
return children;
44
+
}
45
+
}
46
+
47
+
export default ErrorBoundary;
-26
packages/core-extensions/src/common/webpackModules/components.ts
-26
packages/core-extensions/src/common/webpackModules/components.ts
···
1
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
2
-
3
-
const Components = spacepack.findByCode("MasonryList:function")[0].exports;
4
-
const MarkdownParser = spacepack.findByCode(
5
-
"parseAutoModerationSystemMessage:"
6
-
)[0].exports.default;
7
-
const LegacyText = spacepack.findByCode(".selectable", ".colorStandard")[0]
8
-
.exports.default;
9
-
const Flex = spacepack.findByCode(".flex" + "GutterSmall,")[0].exports.default;
10
-
const CardClasses = spacepack.findByCode("card", "cardHeader", "inModal")[0]
11
-
.exports;
12
-
const ControlClasses = spacepack.findByCode(
13
-
"title",
14
-
"titleDefault",
15
-
"dividerDefault"
16
-
)[0].exports;
17
-
18
-
// We use CJS export here because merging the exports from Components is annoying as shit
19
-
module.exports = {
20
-
...Components,
21
-
MarkdownParser,
22
-
LegacyText,
23
-
Flex,
24
-
CardClasses,
25
-
ControlClasses
26
-
};
-5
packages/core-extensions/src/common/webpackModules/flux.ts
-5
packages/core-extensions/src/common/webpackModules/flux.ts
-6
packages/core-extensions/src/common/webpackModules/fluxDispatcher.ts
-6
packages/core-extensions/src/common/webpackModules/fluxDispatcher.ts
+31
packages/core-extensions/src/common/webpackModules/icons.ts
+31
packages/core-extensions/src/common/webpackModules/icons.ts
···
1
+
import { Icons, IconSize } from "@moonlight-mod/types/coreExtensions/common";
2
+
import { tokens } from "@moonlight-mod/wp/discord/components/common/index";
3
+
4
+
// This is defined in a Webpack module but we copy it here to be less breakage-prone
5
+
const sizes: Partial<Record<IconSize, number>> = {
6
+
xxs: 12,
7
+
xs: 16,
8
+
sm: 18,
9
+
md: 24,
10
+
lg: 32,
11
+
refresh_sm: 20
12
+
};
13
+
14
+
export const icons: Icons = {
15
+
parseProps(props) {
16
+
// NOTE: var() fallback is non-standard behavior, just for safety reasons
17
+
const color = props?.color ?? tokens?.colors?.["INTERACTIVE_NORMAL"] ?? "var(--interactive-normal)";
18
+
19
+
const size = sizes[props?.size ?? "md"];
20
+
21
+
return {
22
+
// note: this default size is also non-standard behavior, just for safety
23
+
width: size ?? props?.width ?? sizes.md!,
24
+
height: size ?? props?.width ?? sizes.md!,
25
+
26
+
fill: typeof color === "string" ? color : color.css,
27
+
className: props?.colorClass ?? ""
28
+
};
29
+
}
30
+
};
31
+
export default icons;
-7
packages/core-extensions/src/common/webpackModules/react.ts
-7
packages/core-extensions/src/common/webpackModules/react.ts
+2
-2
packages/core-extensions/src/common/webpackModules/stores.ts
+2
-2
packages/core-extensions/src/common/webpackModules/stores.ts
···
1
-
import Flux from "@moonlight-mod/wp/common_flux";
1
+
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
2
2
3
3
module.exports = new Proxy(
4
4
{},
5
5
{
6
6
get: function (target, key, receiver) {
7
-
const allStores = Flux.Store.getAll();
7
+
const allStores = Store.getAll();
8
8
9
9
let targetStore;
10
10
for (const store of allStores) {
+84
packages/core-extensions/src/componentEditor/index.ts
+84
packages/core-extensions/src/componentEditor/index.ts
···
1
+
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
// dm list
5
+
{
6
+
find: ".interactiveSystemDM]:",
7
+
replace: [
8
+
{
9
+
match: /decorators:(\i\.isSystemDM\(\)\?\(0,\i\.jsx\)\(.+?verified:!0}\):null)/,
10
+
replacement: (_, decorators) =>
11
+
`decorators:require("componentEditor_dmList").default._patchDecorators([${decorators}],arguments[0])`
12
+
},
13
+
{
14
+
match: /(?<=selected:\i,)children:\[/,
15
+
replacement: 'children:require("componentEditor_dmList").default._patchItems(['
16
+
},
17
+
{
18
+
match: /(?<=(onMouseDown|nameplate):\i}\))]/,
19
+
replacement: "],arguments[0])"
20
+
}
21
+
],
22
+
hardFail: true
23
+
},
24
+
25
+
// member list
26
+
{
27
+
find: ".lostPermission",
28
+
replace: [
29
+
{
30
+
match:
31
+
/(?<=\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[\(0,\i\.jsx\)\(\i,{user:\i}\),.+?onClickPremiumGuildIcon:\i}\)])/,
32
+
replacement: (_, decorators) =>
33
+
`children:require("componentEditor_memberList").default._patchDecorators(${decorators},arguments[0])`
34
+
},
35
+
{
36
+
match: /name:null==\i\?\(0,\i\.jsx\)\("span"/,
37
+
replacement: (orig: string) =>
38
+
`children:require("componentEditor_memberList").default._patchItems([],arguments[0]),${orig}`
39
+
}
40
+
]
41
+
},
42
+
43
+
// messages
44
+
{
45
+
find: '},"new-member")),',
46
+
replace: [
47
+
{
48
+
match: /(?<=\.BADGES](=|:))(\i)(;|})/,
49
+
replacement: (_, leading, badges, trailing) =>
50
+
`require("componentEditor_messages").default._patchUsernameBadges(${badges},arguments[0])${trailing}`
51
+
},
52
+
{
53
+
match: /(?<=className:\i,)badges:(\i)/,
54
+
replacement: (_, badges) =>
55
+
`badges:require("componentEditor_messages").default._patchBadges(${badges},arguments[0])`
56
+
},
57
+
{
58
+
match: /(?<=username:\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[.+?])}\),usernameSpanId:/,
59
+
replacement: (_, elements) =>
60
+
`children:require("componentEditor_messages").default._patchUsername(${elements},arguments[0])}),usernameSpanId:`
61
+
}
62
+
]
63
+
},
64
+
{
65
+
find: '.provider&&"Discord"===',
66
+
replace: {
67
+
match: /(?<=\.container\),)children:(\[.+?this\.renderSuppressConfirmModal\(\),.+?\])}\)/,
68
+
replacement: (_, elements) =>
69
+
`children:require("componentEditor_messages").default._patchAccessories(${elements},this.props)})`
70
+
}
71
+
}
72
+
];
73
+
74
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
75
+
dmList: {
76
+
dependencies: [{ id: "react" }]
77
+
},
78
+
memberList: {
79
+
dependencies: [{ id: "react" }]
80
+
},
81
+
messages: {
82
+
dependencies: [{ id: "react" }]
83
+
}
84
+
};
+11
packages/core-extensions/src/componentEditor/manifest.json
+11
packages/core-extensions/src/componentEditor/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "componentEditor",
4
+
"apiLevel": 2,
5
+
"meta": {
6
+
"name": "Component Editor",
7
+
"tagline": "A library to add to commonly patched components",
8
+
"authors": ["Cynosphere"],
9
+
"tags": ["library"]
10
+
}
11
+
}
+61
packages/core-extensions/src/componentEditor/webpackModules/dmList.tsx
+61
packages/core-extensions/src/componentEditor/webpackModules/dmList.tsx
···
1
+
import {
2
+
DMList,
3
+
DMListItem,
4
+
DMListDecorator,
5
+
DMListAnchorIndicies,
6
+
DMListDecoratorAnchorIndicies
7
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
8
+
import React from "@moonlight-mod/wp/react";
9
+
10
+
const items: Record<string, DMListItem> = {};
11
+
const decorators: Record<string, DMListDecorator> = {};
12
+
13
+
function addEntries(
14
+
elements: React.ReactNode[],
15
+
entries: Record<string, DMListItem | DMListDecorator>,
16
+
indicies: Partial<Record<keyof typeof DMListAnchorIndicies | keyof typeof DMListDecoratorAnchorIndicies, number>>,
17
+
props: any
18
+
) {
19
+
const originalElements = [...elements];
20
+
for (const [id, entry] of Object.entries(entries)) {
21
+
const component = <entry.component {...props} key={id} />;
22
+
23
+
if (entry.anchor === undefined) {
24
+
if (entry.before) {
25
+
elements.splice(0, 0, component);
26
+
} else {
27
+
elements.push(component);
28
+
}
29
+
} else {
30
+
const index = elements.indexOf(originalElements[indicies[entry.anchor]!]);
31
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
32
+
}
33
+
}
34
+
}
35
+
36
+
export const dmList: DMList = {
37
+
addItem(id, component, anchor, before = false) {
38
+
items[id] = {
39
+
component,
40
+
anchor,
41
+
before
42
+
};
43
+
},
44
+
addDecorator(id, component, anchor, before = false) {
45
+
decorators[id] = {
46
+
component,
47
+
anchor,
48
+
before
49
+
};
50
+
},
51
+
_patchItems(elements, props) {
52
+
addEntries(elements, items, DMListAnchorIndicies, props);
53
+
return elements;
54
+
},
55
+
_patchDecorators(elements, props) {
56
+
addEntries(elements, decorators, DMListDecoratorAnchorIndicies, props);
57
+
return elements;
58
+
}
59
+
};
60
+
61
+
export default dmList;
+50
packages/core-extensions/src/componentEditor/webpackModules/memberList.tsx
+50
packages/core-extensions/src/componentEditor/webpackModules/memberList.tsx
···
1
+
import {
2
+
MemberList,
3
+
MemberListDecorator,
4
+
MemberListDecoratorAnchorIndicies
5
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
6
+
import React from "@moonlight-mod/wp/react";
7
+
8
+
const items: Record<string, React.FC<any>> = {};
9
+
const decorators: Record<string, MemberListDecorator> = {};
10
+
11
+
export const memberList: MemberList = {
12
+
addItem(id, component) {
13
+
items[id] = component;
14
+
},
15
+
addDecorator(id, component, anchor, before = false) {
16
+
decorators[id] = {
17
+
component,
18
+
anchor,
19
+
before
20
+
};
21
+
},
22
+
_patchItems(elements, props) {
23
+
for (const [id, Component] of Object.entries(items)) {
24
+
elements.push(<Component {...props} key={id} />);
25
+
}
26
+
27
+
return elements;
28
+
},
29
+
_patchDecorators(elements, props) {
30
+
const originalElements = [...elements];
31
+
for (const [id, entry] of Object.entries(decorators)) {
32
+
const component = <entry.component {...props} key={id} />;
33
+
34
+
if (entry.anchor === undefined) {
35
+
if (entry.before) {
36
+
elements.splice(0, 0, component);
37
+
} else {
38
+
elements.push(component);
39
+
}
40
+
} else {
41
+
const index = elements.indexOf(originalElements[MemberListDecoratorAnchorIndicies[entry.anchor]!]);
42
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
43
+
}
44
+
}
45
+
46
+
return elements;
47
+
}
48
+
};
49
+
50
+
export default memberList;
+97
packages/core-extensions/src/componentEditor/webpackModules/messages.tsx
+97
packages/core-extensions/src/componentEditor/webpackModules/messages.tsx
···
1
+
import {
2
+
MessageBadge,
3
+
MessageBadgeIndicies,
4
+
Messages,
5
+
MessageUsername,
6
+
MessageUsernameBadge,
7
+
MessageUsernameBadgeIndicies,
8
+
MessageUsernameIndicies
9
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
10
+
import React from "@moonlight-mod/wp/react";
11
+
12
+
const username: Record<string, MessageUsername> = {};
13
+
const usernameBadges: Record<string, MessageUsernameBadge> = {};
14
+
const badges: Record<string, MessageBadge> = {};
15
+
const accessories: Record<string, React.FC<any>> = {};
16
+
17
+
function addEntries(
18
+
elements: React.ReactNode[],
19
+
entries: Record<string, MessageUsername | MessageUsernameBadge | MessageBadge>,
20
+
indicies: Partial<
21
+
Record<
22
+
| keyof typeof MessageUsernameIndicies
23
+
| keyof typeof MessageUsernameBadgeIndicies
24
+
| keyof typeof MessageBadgeIndicies,
25
+
number
26
+
>
27
+
>,
28
+
props: any
29
+
) {
30
+
const originalElements = [...elements];
31
+
for (const [id, entry] of Object.entries(entries)) {
32
+
const component = <entry.component {...props} key={id} />;
33
+
34
+
if (entry.anchor === undefined) {
35
+
if (entry.before) {
36
+
elements.splice(0, 0, component);
37
+
} else {
38
+
elements.push(component);
39
+
}
40
+
} else {
41
+
const index = elements.indexOf(originalElements[indicies[entry.anchor]!]);
42
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
43
+
}
44
+
}
45
+
}
46
+
47
+
function addComponents(elements: React.ReactNode[], components: Record<string, React.FC<any>>, props: any) {
48
+
for (const [id, Component] of Object.entries(components)) {
49
+
const component = <Component {...props} key={id} />;
50
+
elements.push(component);
51
+
}
52
+
}
53
+
54
+
export const messages: Messages = {
55
+
addToUsername(id, component, anchor, before = false) {
56
+
username[id] = {
57
+
component,
58
+
anchor,
59
+
before
60
+
};
61
+
},
62
+
addUsernameBadge(id, component, anchor, before = false) {
63
+
usernameBadges[id] = {
64
+
component,
65
+
anchor,
66
+
before
67
+
};
68
+
},
69
+
addBadge(id, component, anchor, before = false) {
70
+
badges[id] = {
71
+
component,
72
+
anchor,
73
+
before
74
+
};
75
+
},
76
+
addAccessory(id, component) {
77
+
accessories[id] = component;
78
+
},
79
+
_patchUsername(elements, props) {
80
+
addEntries(elements, username, MessageUsernameIndicies, props);
81
+
return elements;
82
+
},
83
+
_patchUsernameBadges(elements, props) {
84
+
addEntries(elements, usernameBadges, MessageUsernameBadgeIndicies, props);
85
+
return elements;
86
+
},
87
+
_patchBadges(elements, props) {
88
+
addEntries(elements, badges, MessageBadgeIndicies, props);
89
+
return elements;
90
+
},
91
+
_patchAccessories(elements, props) {
92
+
addComponents(elements, accessories, props);
93
+
return elements;
94
+
}
95
+
};
96
+
97
+
export default messages;
+31
packages/core-extensions/src/contextMenu/index.tsx
+31
packages/core-extensions/src/contextMenu/index.tsx
···
1
+
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
{
5
+
find: "Menu API only allows Items and groups of Items as children.",
6
+
replace: [
7
+
{
8
+
match: /(?<=let{navId[^}]+?}=(.),.=).+?(?=,)/,
9
+
replacement: (items, props) => `require("contextMenu_contextMenu")._patchMenu(${props},${items})`
10
+
}
11
+
]
12
+
},
13
+
{
14
+
find: ".getContextMenu(",
15
+
replace: [
16
+
{
17
+
match: /(?<=let\{[^}]+?\}=.;return ).\({[^}]+?}\)/,
18
+
replacement: (render) => `require("contextMenu_contextMenu")._saveProps(this,${render})`
19
+
}
20
+
]
21
+
}
22
+
];
23
+
24
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
25
+
contextMenu: {
26
+
dependencies: [{ ext: "spacepack", id: "spacepack" }, "Menu API only allows Items and groups of Items as children."]
27
+
},
28
+
evilMenu: {
29
+
dependencies: [{ ext: "spacepack", id: "spacepack" }, "Menu API only allows Items and groups of Items as children."]
30
+
}
31
+
};
+11
packages/core-extensions/src/contextMenu/manifest.json
+11
packages/core-extensions/src/contextMenu/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "contextMenu",
4
+
"apiLevel": 2,
5
+
"meta": {
6
+
"name": "Context Menu",
7
+
"tagline": "A library for patching and creating context menus",
8
+
"authors": ["redstonekasi"],
9
+
"tags": ["library"]
10
+
}
11
+
}
+88
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
+88
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
···
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
+
// spacepack.require.m[moonlight.moonmap.modules["discord/modules/menus/web/Menu"]].toString();
62
+
const code =
63
+
spacepack.require.m[
64
+
spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id
65
+
].toString();
66
+
67
+
let MangledMenu;
68
+
69
+
const typeRegex = /if\(.\.type===(.)\.(.+?)\).+?type:"(.+?)"/g;
70
+
const typeMap: Record<string, string | undefined> = {
71
+
checkbox: "MenuCheckboxItem",
72
+
control: "MenuControlItem",
73
+
groupstart: "MenuGroup",
74
+
customitem: "MenuItem",
75
+
radio: "MenuRadioItem",
76
+
separator: "MenuSeparator"
77
+
};
78
+
79
+
for (const [, modIdent, mangled, type] of code.matchAll(typeRegex)) {
80
+
if (!MangledMenu) {
81
+
const modId = code.match(new RegExp(`${modIdent}=.\\((\\d+?)\\)`))![1];
82
+
MangledMenu = spacepack.require(modId);
83
+
}
84
+
85
+
const prop = typeMap[type];
86
+
if (!prop) continue;
87
+
module.exports[prop] = MangledMenu[mangled];
88
+
}
+18
packages/core-extensions/src/contextMenu/webpackModules/evilMenu.ts
+18
packages/core-extensions/src/contextMenu/webpackModules/evilMenu.ts
···
1
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
2
+
3
+
// spacepack.require.m[moonlight.moonmap.modules["discord/modules/menus/web/Menu"]].toString();
4
+
let code =
5
+
spacepack.require.m[
6
+
spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id
7
+
].toString();
8
+
9
+
const parserSym = code.match(/(?<=_patchMenu\(.,).+?(?=\()/)![0];
10
+
11
+
code = code.replace(/{(.):\(\)=>./, (orig, e) => `{${e}:()=>${parserSym}`);
12
+
const mod = new Function("module", "exports", "require", `(${code}).apply(this, arguments)`);
13
+
14
+
const exp: any = {};
15
+
mod({}, exp, require);
16
+
17
+
const parser = spacepack.findFunctionByStrings(exp, "Menu API only allows Items and groups of Items as children.")!;
18
+
module.exports = parser;
+19
packages/core-extensions/src/devToolsExtensions/host.ts
+19
packages/core-extensions/src/devToolsExtensions/host.ts
···
1
+
import { app, session } from "electron";
2
+
import { resolve } from "node:path";
3
+
import Logger from "@moonlight-mod/core/util/logger";
4
+
5
+
const logger = new Logger("DevTools Extensions");
6
+
7
+
app.whenReady().then(async () => {
8
+
const paths = moonlightHost.getConfigOption<string[]>("devToolsExtensions", "paths") ?? [];
9
+
10
+
for (const path of paths) {
11
+
const resolved = resolve(path);
12
+
13
+
try {
14
+
await session.defaultSession.loadExtension(resolved);
15
+
} catch (err) {
16
+
logger.error(`Failed to load an extension in "${resolved}":`, err);
17
+
}
18
+
}
19
+
});
+22
packages/core-extensions/src/devToolsExtensions/manifest.json
+22
packages/core-extensions/src/devToolsExtensions/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "devToolsExtensions",
4
+
"meta": {
5
+
"name": "DevTools Extensions",
6
+
"tagline": "Loads Chrome extensions into Electron DevTools",
7
+
"authors": [
8
+
"Cynosphere"
9
+
],
10
+
"tags": [
11
+
"development"
12
+
]
13
+
},
14
+
"settings": {
15
+
"paths": {
16
+
"advice": "restart",
17
+
"displayName": "Extension Paths",
18
+
"type": "list"
19
+
}
20
+
},
21
+
"apiLevel": 2
22
+
}
+4
-23
packages/core-extensions/src/disableSentry/host.ts
+4
-23
packages/core-extensions/src/disableSentry/host.ts
···
1
1
import { join } from "node:path";
2
2
import { Module } from "node:module";
3
-
import { BrowserWindow } from "electron";
4
3
5
4
const logger = moonlightHost.getLogger("disableSentry");
6
5
7
6
if (moonlightHost.asarPath !== "moonlightDesktop") {
8
7
try {
9
-
const hostSentryPath = require.resolve(
10
-
join(moonlightHost.asarPath, "node_modules", "@sentry", "electron")
11
-
);
12
-
require.cache[hostSentryPath] = new Module(
13
-
hostSentryPath,
14
-
require.cache[require.resolve(moonlightHost.asarPath)]
15
-
);
8
+
const hostSentryPath = require.resolve(join(moonlightHost.asarPath, "node_modules", "@sentry", "electron"));
9
+
require.cache[hostSentryPath] = new Module(hostSentryPath, require.cache[require.resolve(moonlightHost.asarPath)]);
16
10
require.cache[hostSentryPath]!.exports = {
17
11
init: () => {},
18
12
captureException: () => {},
19
13
setTag: () => {},
20
-
setUser: () => {}
14
+
setUser: () => {},
15
+
captureMessage: () => {}
21
16
};
22
17
logger.debug("Stubbed Sentry host side!");
23
18
} catch (err) {
24
19
logger.error("Failed to stub Sentry host side:", err);
25
20
}
26
21
}
27
-
28
-
moonlightHost.events.on("window-created", (window: BrowserWindow) => {
29
-
window.webContents.session.webRequest.onBeforeRequest(
30
-
{
31
-
urls: [
32
-
"https://*.sentry.io/*",
33
-
"https://*.discord.com/error-reporting-proxy/*"
34
-
]
35
-
},
36
-
function (details, callback) {
37
-
callback({ cancel: true });
38
-
}
39
-
);
40
-
});
+8
-18
packages/core-extensions/src/disableSentry/index.ts
+8
-18
packages/core-extensions/src/disableSentry/index.ts
···
3
3
4
4
export const patches: Patch[] = [
5
5
{
6
-
find: "DSN:function",
6
+
find: "profiledRootComponent:",
7
7
replace: {
8
8
type: PatchReplaceType.Normal,
9
-
match: /default:function\(\){return .}/,
10
-
replacement:
11
-
'default:function(){return require("disableSentry_stub").proxy()}'
12
-
}
13
-
},
14
-
{
15
-
find: "window.DiscordSentry.addBreadcrumb",
16
-
replace: {
17
-
type: PatchReplaceType.Normal,
18
-
match: /default:function\(\){return .}/,
19
-
replacement:
20
-
'default:function(){return (...args)=>{moonlight.getLogger("disableSentry").debug("Sentry calling addBreadcrumb passthrough:", ...args);}}'
9
+
match: /Z:\(\)=>\i/,
10
+
replacement: 'Z:()=>require("disableSentry_stub").proxy()'
21
11
}
22
12
},
23
13
{
24
-
find: "initSentry:function",
14
+
find: "this._sentryUtils=",
25
15
replace: {
26
16
type: PatchReplaceType.Normal,
27
-
match: /initSentry:function\(\){return .}/,
28
-
replacement: "default:function(){return ()=>{}}"
17
+
match: /(?<=this._sentryUtils=)./,
18
+
replacement: "undefined"
29
19
}
30
20
},
31
21
{
32
22
find: "window.DiscordErrors=",
33
23
replace: {
34
24
type: PatchReplaceType.Normal,
35
-
match: /uses_client_mods:\(0,.\.usesClientMods\)\(\)/,
36
-
replacement: "uses_client_mods:false"
25
+
match: /(?<=uses_client_mods:)./,
26
+
replacement: "false"
37
27
}
38
28
}
39
29
];
+12
-1
packages/core-extensions/src/disableSentry/manifest.json
+12
-1
packages/core-extensions/src/disableSentry/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "disableSentry",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Disable Sentry",
5
7
"tagline": "Turns off Discord's error reporting systems",
6
8
"authors": ["Cynosphere", "NotNite"],
7
9
"tags": ["privacy"]
8
-
}
10
+
},
11
+
"blocked": [
12
+
"https://*.sentry.io/*",
13
+
"https://*.discord.com/error-reporting-proxy/*",
14
+
"https://discord.com/assets/sentry.*.js",
15
+
"https://*.discord.com/assets/sentry.*.js",
16
+
"https://*.discordapp.com/error-reporting-proxy/*",
17
+
"https://discordapp.com/assets/sentry.*.js",
18
+
"https://*.discordapp.com/assets/sentry.*.js"
19
+
]
9
20
}
+13
-19
packages/core-extensions/src/disableSentry/node.ts
+13
-19
packages/core-extensions/src/disableSentry/node.ts
···
5
5
6
6
const logger = moonlightNode.getLogger("disableSentry");
7
7
8
-
if (!ipcRenderer.sendSync(constants.ipcGetIsMoonlightDesktop)) {
9
-
const preloadPath = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
10
-
try {
11
-
const sentryPath = require.resolve(
12
-
resolve(preloadPath, "..", "node_modules", "@sentry", "electron")
13
-
);
14
-
require.cache[sentryPath] = new Module(
15
-
sentryPath,
16
-
require.cache[require.resolve(preloadPath)]
17
-
);
18
-
require.cache[sentryPath]!.exports = {
19
-
init: () => {},
20
-
setTag: () => {},
21
-
setUser: () => {}
22
-
};
23
-
logger.debug("Stubbed Sentry node side!");
24
-
} catch (err) {
25
-
logger.error("Failed to stub Sentry:", err);
26
-
}
8
+
const preloadPath = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
9
+
try {
10
+
const sentryPath = require.resolve(resolve(preloadPath, "..", "node_modules", "@sentry", "electron"));
11
+
require.cache[sentryPath] = new Module(sentryPath, require.cache[require.resolve(preloadPath)]);
12
+
require.cache[sentryPath]!.exports = {
13
+
init: () => {},
14
+
setTag: () => {},
15
+
setUser: () => {},
16
+
captureMessage: () => {}
17
+
};
18
+
logger.debug("Stubbed Sentry node side!");
19
+
} catch (err) {
20
+
logger.error("Failed to stub Sentry:", err);
27
21
}
+1
-2
packages/core-extensions/src/disableSentry/webpackModules/stub.ts
+1
-2
packages/core-extensions/src/disableSentry/webpackModules/stub.ts
···
23
23
throw Error("crash");
24
24
};
25
25
} else if (keys.includes(prop.toString())) {
26
-
return (...args: any[]) =>
27
-
logger.debug(`Sentry calling "${prop.toString()}":`, ...args);
26
+
return (...args: any[]) => logger.debug(`Sentry calling "${prop.toString()}":`, ...args);
28
27
} else {
29
28
return undefined;
30
29
}
+47
-3
packages/core-extensions/src/experiments/index.ts
+47
-3
packages/core-extensions/src/experiments/index.ts
···
2
2
3
3
export const patches: Patch[] = [
4
4
{
5
-
find: "isStaffEnv:",
5
+
find: "isStaffPersonal:",
6
+
replace: {
7
+
match: /&&null!=this\.personalConnectionId/,
8
+
replacement: "||!0"
9
+
}
10
+
},
11
+
{
12
+
find: '"scientist:triggered"', // Scientist? Triggered.
13
+
replace: {
14
+
match: ".personal_connection_id",
15
+
replacement: ".personal_connection_id || true"
16
+
}
17
+
},
18
+
19
+
// Enable staff help menu
20
+
{
21
+
find: ".HEADER_BAR)",
6
22
replace: {
7
-
match: /.\.isStaff\(\)/,
8
-
replacement: "!0"
23
+
match: /&&\((\i)\?\(0,/,
24
+
replacement: (_, isStaff) =>
25
+
`&&(((moonlight.getConfigOption("experiments","devtools")??false)?true:${isStaff})?(0,`
26
+
}
27
+
},
28
+
// staff help menu - visual refresh
29
+
{
30
+
find: '("AppTitleBar")',
31
+
replace: {
32
+
match: /{hasBugReporterAccess:(\i)}=\i\.\i\.useExperiment\({location:"HeaderBar"},{autoTrackExposure:!1}\);/,
33
+
replacement: (orig, isStaff) =>
34
+
`${orig}if(moonlight.getConfigOption("experiments","devtools")??false)${isStaff}=true;`
35
+
}
36
+
},
37
+
{
38
+
find: 'navId:"staff-help-popout",',
39
+
replace: {
40
+
match: /isDiscordDeveloper:(\i)}\),/,
41
+
replacement: (_, isStaff) =>
42
+
`isDiscordDeveloper:(moonlight.getConfigOption("experiments","devtools")??false)||${isStaff}}),`
43
+
}
44
+
},
45
+
46
+
// Enable further staff-locked options
47
+
{
48
+
find: "shouldShowLurkerModeUpsellPopout:",
49
+
replace: {
50
+
match: /\.useReducedMotion,isStaff:(\i)(,|})/,
51
+
replacement: (_, isStaff, trail) =>
52
+
`.useReducedMotion,isStaff:(moonlight.getConfigOption("experiments","staffSettings")??false)?true:${isStaff}${trail}`
9
53
}
10
54
}
11
55
];
+17
-1
packages/core-extensions/src/experiments/manifest.json
+17
-1
packages/core-extensions/src/experiments/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "experiments",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Experiments",
5
7
"tagline": "Allows you to configure Discord's internal A/B testing features",
6
-
"authors": ["NotNite"],
8
+
"authors": ["NotNite", "Cynosphere"],
7
9
"tags": ["dangerZone"]
10
+
},
11
+
"settings": {
12
+
"devtools": {
13
+
"advice": "reload",
14
+
"displayName": "Enable staff help menu (DevTools)",
15
+
"type": "boolean",
16
+
"default": false
17
+
},
18
+
"staffSettings": {
19
+
"advice": "reload",
20
+
"displayName": "Allow access to other staff settings elsewhere",
21
+
"type": "boolean",
22
+
"default": false
23
+
}
8
24
}
9
25
}
+7
-19
packages/core-extensions/src/markdown/index.ts
+7
-19
packages/core-extensions/src/markdown/index.ts
···
6
6
replace: [
7
7
{
8
8
match: /={newline:(.+?)},(.{1,2})=\(0,/,
9
-
replacement: (_, rules, RULES) =>
10
-
`=require("markdown_markdown")._addRules({newline:${rules}}),${RULES}=(0,`
9
+
replacement: (_, rules, RULES) => `=require("markdown_markdown")._addRules({newline:${rules}}),${RULES}=(0,`
11
10
},
12
11
{
13
-
match: /(?<=var (.{1,2})={RULES:.+?})/,
14
-
replacement: (_, rulesets) =>
15
-
`;require("markdown_markdown")._applyRulesetBlacklist(${rulesets});`
12
+
match: /(?<=;(.{1,2}\.Z)={RULES:.+?})/,
13
+
replacement: (_, rulesets) => `;require("markdown_markdown")._applyRulesetBlacklist(${rulesets});`
16
14
}
17
15
]
18
16
},
···
25
23
`__slateRules,${rulesDef}=__slateRules=require("markdown_markdown")._addSlateRules({link:{${rules}}),${syntaxBefore}=new Set`
26
24
},
27
25
{
28
-
match:
29
-
/(originalMatch:.}=(.);)(.+?)case"emoticon":(return .+?;)(.+?)case"link":{(.+?)}default:/,
30
-
replacement: (
31
-
_,
32
-
start,
33
-
rule,
34
-
body,
35
-
plaintextReturn,
36
-
otherRules,
37
-
inlineStyleBody
38
-
) =>
39
-
`${start}if(${rule}.type.startsWith("__moonlight_")){if(__slateRules[${rule}.type].type=="inlineStyle"){${inlineStyleBody}}else{${plaintextReturn}}}${body}case"emoticon":${plaintextReturn}${otherRules}case"link":{${inlineStyleBody}}default:`
26
+
match: /(originalMatch:.}=(.);)(.+?)case"emoticon":(return .+?;)(.+?)case"subtext":{(.+?)}default:/,
27
+
replacement: (_, start, rule, body, plaintextReturn, otherRules, inlineStyleBody) =>
28
+
`${start}if(${rule}.type.startsWith("__moonlight_")){if(__slateRules[${rule}.type].type=="inlineStyle"){${inlineStyleBody}}else{${plaintextReturn}}}${body}case"emoticon":${plaintextReturn}${otherRules}case"subtext":{${inlineStyleBody}}default:`
40
29
}
41
30
]
42
31
},
···
44
33
find: '"Slate: Unknown decoration attribute: "',
45
34
replace: {
46
35
match: /=({strong:.+?});/,
47
-
replacement: (_, rules) =>
48
-
`=require("markdown_markdown")._addSlateDecorators(${rules});`
36
+
replacement: (_, rules) => `=require("markdown_markdown")._addSlateDecorators(${rules});`
49
37
}
50
38
}
51
39
];
+2
packages/core-extensions/src/markdown/manifest.json
+2
packages/core-extensions/src/markdown/manifest.json
+4
-17
packages/core-extensions/src/markdown/webpackModules/markdown.ts
+4
-17
packages/core-extensions/src/markdown/webpackModules/markdown.ts
···
1
-
/* eslint-disable no-console */
2
-
import {
3
-
MarkdownRule,
4
-
Ruleset,
5
-
SlateRule
6
-
} from "@moonlight-mod/types/coreExtensions/markdown";
1
+
import { MarkdownRule, Ruleset, SlateRule } from "@moonlight-mod/types/coreExtensions/markdown";
7
2
8
-
export const rules: Record<
9
-
string,
10
-
(rules: Record<string, MarkdownRule>) => MarkdownRule
11
-
> = {};
12
-
export const slateRules: Record<
13
-
string,
14
-
(rules: Record<string, SlateRule>) => SlateRule
15
-
> = {};
3
+
export const rules: Record<string, (rules: Record<string, MarkdownRule>) => MarkdownRule> = {};
4
+
export const slateRules: Record<string, (rules: Record<string, SlateRule>) => SlateRule> = {};
16
5
export const slateDecorators: Record<string, string> = {};
17
6
export const ruleBlacklists: Record<Ruleset, Record<string, boolean>> = {
18
7
RULES: {},
···
67
56
return originalRules;
68
57
}
69
58
70
-
export function _applyRulesetBlacklist(
71
-
rulesets: Record<Ruleset, Record<string, MarkdownRule>>
72
-
) {
59
+
export function _applyRulesetBlacklist(rulesets: Record<Ruleset, Record<string, MarkdownRule>>) {
73
60
for (const ruleset of Object.keys(rulesets) as Ruleset[]) {
74
61
if (ruleset === "RULES") continue;
75
62
+108
packages/core-extensions/src/moonbase/host.ts
+108
packages/core-extensions/src/moonbase/host.ts
···
1
+
import * as electron from "electron";
2
+
import * as fs from "node:fs/promises";
3
+
import * as path from "node:path";
4
+
import getNatives from "./native";
5
+
import { MoonlightBranch } from "@moonlight-mod/types";
6
+
7
+
const natives = getNatives();
8
+
9
+
const confirm = (action: string) =>
10
+
electron.dialog
11
+
.showMessageBox({
12
+
title: "Are you sure?",
13
+
message: `Are you sure? This will ${action} and restart Discord.`,
14
+
type: "warning",
15
+
buttons: ["OK", "Cancel"]
16
+
})
17
+
.then((r) => r.response === 0);
18
+
19
+
async function updateAndRestart() {
20
+
if (!(await confirm("update moonlight"))) return;
21
+
const newVersion = await natives.checkForMoonlightUpdate();
22
+
23
+
if (newVersion === null) {
24
+
electron.dialog.showMessageBox({ message: "You are already on the latest version of moonlight." });
25
+
return;
26
+
}
27
+
28
+
try {
29
+
await natives.updateMoonlight();
30
+
await electron.dialog.showMessageBox({ message: "Update successful, restarting Discord." });
31
+
electron.app.relaunch();
32
+
electron.app.exit(0);
33
+
} catch {
34
+
await electron.dialog.showMessageBox({
35
+
message: "Failed to update moonlight. Please use the installer instead.",
36
+
type: "error"
37
+
});
38
+
}
39
+
}
40
+
41
+
async function resetConfig() {
42
+
if (!(await confirm("reset your configuration"))) return;
43
+
44
+
const config = await moonlightHost.getConfigPath();
45
+
const dir = path.dirname(config);
46
+
const branch = path.basename(config, ".json");
47
+
await fs.rename(config, path.join(dir, `${branch}-backup-${Math.floor(Date.now() / 1000)}.json`));
48
+
49
+
await electron.dialog.showMessageBox({ message: "Configuration reset, restarting Discord." });
50
+
electron.app.relaunch();
51
+
electron.app.exit(0);
52
+
}
53
+
54
+
async function changeBranch(branch: MoonlightBranch) {
55
+
if (moonlightHost.branch === branch) return;
56
+
if (!(await confirm("switch branches"))) return;
57
+
try {
58
+
await natives.updateMoonlight(branch);
59
+
await electron.dialog.showMessageBox({ message: "Branch switch successful, restarting Discord." });
60
+
electron.app.relaunch();
61
+
electron.app.exit(0);
62
+
} catch (e) {
63
+
await electron.dialog.showMessageBox({ message: "Failed to switch branches:\n" + e, type: "error" });
64
+
}
65
+
}
66
+
67
+
function showAbout() {
68
+
electron.dialog.showMessageBox({
69
+
title: "About moonlight",
70
+
message: `moonlight ${moonlightHost.branch} ${moonlightHost.version}`
71
+
});
72
+
}
73
+
74
+
electron.app.whenReady().then(() => {
75
+
const original = electron.Menu.buildFromTemplate;
76
+
electron.Menu.buildFromTemplate = function (entries) {
77
+
const i = entries.findIndex((e) => e.label === "Check for Updates...");
78
+
if (i === -1) return original.call(this, entries);
79
+
80
+
if (!entries.find((e) => e.label === "moonlight")) {
81
+
const options: Electron.MenuItemConstructorOptions[] = [
82
+
{ label: "Update and restart", click: updateAndRestart },
83
+
{ label: "Reset config", click: resetConfig }
84
+
];
85
+
86
+
if (moonlightHost.branch !== MoonlightBranch.DEV) {
87
+
options.push({
88
+
label: "Switch branch",
89
+
submenu: [MoonlightBranch.STABLE, MoonlightBranch.NIGHTLY].map((branch) => ({
90
+
label: branch,
91
+
type: "radio",
92
+
checked: moonlightHost.branch === branch,
93
+
click: () => changeBranch(branch)
94
+
}))
95
+
});
96
+
}
97
+
98
+
options.push({ label: "About", click: showAbout });
99
+
100
+
entries.splice(i + 1, 0, {
101
+
label: "moonlight",
102
+
submenu: options
103
+
});
104
+
}
105
+
106
+
return original.call(this, entries);
107
+
};
108
+
});
+83
-90
packages/core-extensions/src/moonbase/index.tsx
+83
-90
packages/core-extensions/src/moonbase/index.tsx
···
1
-
import { ExtensionWebExports, WebpackRequireType } from "@moonlight-mod/types";
2
-
import extensionsPage from "./ui/extensions";
3
-
import configPage from "./ui/config";
1
+
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
4
2
5
-
import { CircleXIconSVG, DownloadIconSVG, TrashIconSVG } from "./types";
6
-
import ui from "./ui";
7
-
8
-
export const pageModules: (require: WebpackRequireType) => Record<
9
-
string,
3
+
export const patches: Patch[] = [
10
4
{
11
-
name: string;
12
-
element: React.FunctionComponent;
5
+
find: "window.DiscordErrors=",
6
+
replace: [
7
+
// replace reporting line with update status
8
+
{
9
+
// CvQlAA mapped to ERRORS_ACTION_TO_TAKE
10
+
// FIXME: Better patch find?
11
+
match: /,(\(0,(\i)\.jsx\))\("p",{children:\i\.\i\.string\(\i\.\i\.CvQlAA\)}\)/,
12
+
replacement: (_, createElement, ReactJSX) =>
13
+
`,${createElement}(require("moonbase_crashScreen")?.UpdateText??${ReactJSX}.Fragment,{state:this.state,setState:this.setState.bind(this)})`
14
+
},
15
+
16
+
// wrap actions field to display error details
17
+
{
18
+
match: /(?<=return(\(0,(\i)\.jsx\))\(.+?,)action:(\i),className:/,
19
+
replacement: (_, createElement, ReactJSX, action) =>
20
+
`action:require("moonbase_crashScreen")?.wrapAction?${createElement}(require("moonbase_crashScreen").wrapAction,{action:${action},state:this.state}):${action},className:`
21
+
},
22
+
23
+
// add update button
24
+
// +hivLS -> ERRORS_RELOAD
25
+
{
26
+
match: /(?<=\["\+hivLS"\]\)}\),(\(0,(\i)\.jsx\))\(\i,{}\))/,
27
+
replacement: (_, createElement, ReactJSX) =>
28
+
`,${createElement}(require("moonbase_crashScreen")?.UpdateButton??${ReactJSX}.Fragment,{state:this.state,setState:this.setState.bind(this)})`
29
+
}
30
+
]
13
31
}
14
-
> = (require) => ({
15
-
extensions: {
16
-
name: "Extensions",
17
-
element: extensionsPage(require)
32
+
];
33
+
34
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
35
+
stores: {
36
+
dependencies: [{ id: "discord/packages/flux" }, { id: "discord/Dispatcher" }]
18
37
},
19
-
config: {
20
-
name: "Config",
21
-
element: configPage(require)
22
-
}
23
-
});
24
38
25
-
export const webpackModules: ExtensionWebExports["webpackModules"] = {
26
-
stores: {
39
+
ui: {
27
40
dependencies: [
28
-
{ ext: "common", id: "flux" },
29
-
{ ext: "common", id: "fluxDispatcher" }
41
+
{ ext: "spacepack", id: "spacepack" },
42
+
{ id: "react" },
43
+
{ id: "discord/components/common/index" },
44
+
{ ext: "moonbase", id: "stores" },
45
+
{ ext: "moonbase", id: "ThemeDarkIcon" },
46
+
{ id: "discord/modules/guild_settings/web/AppCard.css" },
47
+
{ ext: "contextMenu", id: "contextMenu" },
48
+
{ id: "discord/modules/modals/Modals" },
49
+
"Masks.PANEL_BUTTON",
50
+
'"Missing channel in Channel.openChannelContextMenu"',
51
+
".forumOrHome]:"
30
52
]
31
53
},
32
54
33
-
moonbase: {
55
+
ThemeDarkIcon: {
56
+
dependencies: [{ ext: "common", id: "icons" }, { id: "react" }]
57
+
},
58
+
59
+
settings: {
34
60
dependencies: [
35
61
{ ext: "spacepack", id: "spacepack" },
36
62
{ ext: "settings", id: "settings" },
37
-
{ ext: "common", id: "react" },
38
-
{ ext: "common", id: "components" },
63
+
{ id: "react" },
64
+
{ ext: "moonbase", id: "ui" },
65
+
{ ext: "contextMenu", id: "contextMenu" },
66
+
':"USER_SETTINGS_MODAL_SET_SECTION"'
67
+
],
68
+
entrypoint: true
69
+
},
70
+
71
+
updates: {
72
+
dependencies: [
73
+
{ id: "react" },
39
74
{ ext: "moonbase", id: "stores" },
40
-
DownloadIconSVG,
41
-
TrashIconSVG,
42
-
CircleXIconSVG,
43
-
"Masks.PANEL_BUTTON",
44
-
"removeButtonContainer:",
45
-
'"Missing channel in Channel.openChannelContextMenu"',
46
-
".default.HEADER_BAR"
75
+
{ ext: "moonbase", id: "ThemeDarkIcon" },
76
+
{ ext: "notices", id: "notices" },
77
+
{
78
+
ext: "spacepack",
79
+
id: "spacepack"
80
+
},
81
+
{ id: "discord/Constants" },
82
+
{ id: "discord/components/common/index" }
47
83
],
48
-
entrypoint: true,
49
-
run: (module, exports, require) => {
50
-
const settings = require("settings_settings").Settings;
51
-
const React = require("common_react");
52
-
const spacepack = require("spacepack_spacepack").spacepack;
53
-
const { MoonbaseSettingsStore } =
54
-
require("moonbase_stores") as typeof import("./webpackModules/stores");
55
-
56
-
const addSection = (name: string, element: React.FunctionComponent) => {
57
-
settings.addSection(name, name, element, null, -2, {
58
-
stores: [MoonbaseSettingsStore],
59
-
element: () => {
60
-
// Require it here because lazy loading SUX
61
-
const SettingsNotice =
62
-
spacepack.findByCode("onSaveButtonColor")[0].exports.default;
63
-
return (
64
-
<SettingsNotice
65
-
submitting={MoonbaseSettingsStore.submitting}
66
-
onReset={() => {
67
-
MoonbaseSettingsStore.reset();
68
-
}}
69
-
onSave={() => {
70
-
MoonbaseSettingsStore.writeConfig();
71
-
}}
72
-
/>
73
-
);
74
-
}
75
-
});
76
-
};
84
+
entrypoint: true
85
+
},
77
86
78
-
if (moonlight.getConfigOption<boolean>("moonbase", "sections")) {
79
-
const pages = pageModules(require);
80
-
81
-
const { Text } = require("common_components");
82
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
87
+
moonbase: {
88
+
dependencies: [{ ext: "moonbase", id: "stores" }]
89
+
},
83
90
84
-
settings.addHeader("Moonbase", -2);
85
-
for (const page of Object.values(pages)) {
86
-
addSection(page.name, () => (
87
-
<>
88
-
<Text
89
-
className={Margins.marginBottom20}
90
-
variant="heading-lg/semibold"
91
-
tag="h2"
92
-
>
93
-
Extensions
94
-
</Text>
95
-
<page.element />
96
-
</>
97
-
));
98
-
}
99
-
} else {
100
-
addSection("Moonbase", ui(require));
101
-
}
102
-
}
91
+
crashScreen: {
92
+
dependencies: [
93
+
{ ext: "spacepack", id: "spacepack" },
94
+
{ id: "react" },
95
+
{ ext: "moonbase", id: "stores" },
96
+
{ id: "discord/packages/flux" },
97
+
{ id: "discord/components/common/index" },
98
+
/tabBar:"tabBar_[a-z0-9]+",tabBarItem:"tabBarItem_[a-z0-9]+"/
99
+
]
103
100
}
104
101
};
105
-
106
-
export const styles = [
107
-
".moonbase-settings > :first-child { margin-top: 0px; }"
108
-
];
+35
-5
packages/core-extensions/src/moonbase/manifest.json
+35
-5
packages/core-extensions/src/moonbase/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "moonbase",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Moonbase",
5
7
"tagline": "The official settings UI for moonlight",
6
-
"authors": ["Cynosphere", "NotNite"]
8
+
"authors": ["Cynosphere", "NotNite", "redstonekasi"]
7
9
},
8
-
"dependencies": ["spacepack", "settings", "common"],
10
+
"dependencies": ["spacepack", "settings", "common", "notices", "contextMenu"],
9
11
"settings": {
10
12
"sections": {
13
+
"advice": "reload",
11
14
"displayName": "Split into sections",
12
15
"description": "Show the Moonbase tabs as separate sections",
13
-
"type": "boolean"
16
+
"type": "boolean",
17
+
"default": false
18
+
},
19
+
"oldLocation": {
20
+
"advice": "reload",
21
+
"displayName": "Put Moonbase back at the bottom",
22
+
"type": "boolean",
23
+
"default": false
14
24
},
15
25
"saveFilter": {
26
+
"advice": "none",
16
27
"displayName": "Persist filter",
17
28
"description": "Save extension filter in config",
18
-
"type": "boolean"
29
+
"type": "boolean",
30
+
"default": false
31
+
},
32
+
"updateChecking": {
33
+
"advice": "none",
34
+
"displayName": "Automatic update checking",
35
+
"description": "Checks for updates to moonlight",
36
+
"type": "boolean",
37
+
"default": true
38
+
},
39
+
"updateBanner": {
40
+
"advice": "none",
41
+
"displayName": "Show update banner",
42
+
"description": "Shows a banner for moonlight and extension updates",
43
+
"type": "boolean",
44
+
"default": true
19
45
}
20
-
}
46
+
},
47
+
"cors": [
48
+
"https://github.com/moonlight-mod/moonlight/releases/download/",
49
+
"https://objects.githubusercontent.com/github-production-release-asset-"
50
+
]
21
51
}
+178
packages/core-extensions/src/moonbase/native.ts
+178
packages/core-extensions/src/moonbase/native.ts
···
1
+
import { MoonlightBranch } from "@moonlight-mod/types";
2
+
import type { MoonbaseNatives, RepositoryManifest } from "./types";
3
+
import extractAsar from "@moonlight-mod/core/asar";
4
+
import { distDir, repoUrlFile, installedVersionFile } from "@moonlight-mod/types/constants";
5
+
import { parseTarGzip } from "nanotar";
6
+
7
+
const moonlightGlobal = globalThis.moonlightHost ?? globalThis.moonlightNode;
8
+
9
+
const githubRepo = "moonlight-mod/moonlight";
10
+
const githubApiUrl = `https://api.github.com/repos/${githubRepo}/releases/latest`;
11
+
const artifactName = "dist.tar.gz";
12
+
13
+
const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref";
14
+
const nightlyZipUrl = "https://moonlight-mod.github.io/moonlight/dist.tar.gz";
15
+
16
+
export const userAgent = `moonlight/${moonlightGlobal.version} (https://github.com/moonlight-mod/moonlight)`;
17
+
18
+
// User-Agent header causes trouble on Firefox
19
+
const isBrowser = globalThis.moonlightNode != null && globalThis.moonlightNode.isBrowser;
20
+
const sharedHeaders: Record<string, string> = {};
21
+
if (!isBrowser) sharedHeaders["User-Agent"] = userAgent;
22
+
23
+
async function getStableRelease(): Promise<{
24
+
name: string;
25
+
assets: {
26
+
name: string;
27
+
browser_download_url: string;
28
+
}[];
29
+
}> {
30
+
const req = await fetch(githubApiUrl, {
31
+
cache: "no-store",
32
+
headers: sharedHeaders
33
+
});
34
+
return await req.json();
35
+
}
36
+
37
+
export default function getNatives(): MoonbaseNatives {
38
+
const logger = moonlightGlobal.getLogger("moonbase/natives");
39
+
40
+
return {
41
+
async checkForMoonlightUpdate() {
42
+
try {
43
+
if (moonlightGlobal.branch === MoonlightBranch.STABLE) {
44
+
const json = await getStableRelease();
45
+
return json.name !== moonlightGlobal.version ? json.name : null;
46
+
} else if (moonlightGlobal.branch === MoonlightBranch.NIGHTLY) {
47
+
const req = await fetch(nightlyRefUrl, {
48
+
cache: "no-store",
49
+
headers: sharedHeaders
50
+
});
51
+
const ref = (await req.text()).split("\n")[0];
52
+
return ref !== moonlightGlobal.version ? ref : null;
53
+
}
54
+
55
+
return null;
56
+
} catch (e) {
57
+
logger.error("Error checking for moonlight update", e);
58
+
return null;
59
+
}
60
+
},
61
+
62
+
async updateMoonlight(overrideBranch?: MoonlightBranch) {
63
+
const branch = overrideBranch ?? moonlightGlobal.branch;
64
+
65
+
// Note: this won't do anything on browser, we should probably disable it
66
+
// entirely when running in browser.
67
+
async function downloadStable(): Promise<[ArrayBuffer, string]> {
68
+
const json = await getStableRelease();
69
+
const asset = json.assets.find((a) => a.name === artifactName);
70
+
if (!asset) throw new Error("Artifact not found");
71
+
72
+
logger.debug(`Downloading ${asset.browser_download_url}`);
73
+
const req = await fetch(asset.browser_download_url, {
74
+
cache: "no-store",
75
+
headers: sharedHeaders
76
+
});
77
+
78
+
return [await req.arrayBuffer(), json.name];
79
+
}
80
+
81
+
async function downloadNightly(): Promise<[ArrayBuffer, string]> {
82
+
logger.debug(`Downloading ${nightlyZipUrl}`);
83
+
const zipReq = await fetch(nightlyZipUrl, {
84
+
cache: "no-store",
85
+
headers: sharedHeaders
86
+
});
87
+
88
+
const refReq = await fetch(nightlyRefUrl, {
89
+
cache: "no-store",
90
+
headers: sharedHeaders
91
+
});
92
+
const ref = (await refReq.text()).split("\n")[0];
93
+
94
+
return [await zipReq.arrayBuffer(), ref];
95
+
}
96
+
97
+
const [tar, ref] =
98
+
branch === MoonlightBranch.STABLE
99
+
? await downloadStable()
100
+
: branch === MoonlightBranch.NIGHTLY
101
+
? await downloadNightly()
102
+
: [null, null];
103
+
104
+
if (!tar || !ref) return;
105
+
106
+
const dist = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), distDir);
107
+
if (await moonlightNodeSandboxed.fs.exists(dist)) await moonlightNodeSandboxed.fs.rmdir(dist);
108
+
await moonlightNodeSandboxed.fs.mkdir(dist);
109
+
110
+
logger.debug("Extracting update");
111
+
const files = await parseTarGzip(tar);
112
+
for (const file of files) {
113
+
if (!file.data) continue;
114
+
// @ts-expect-error What do you mean their own types are wrong
115
+
if (file.type !== "file") continue;
116
+
117
+
const fullFile = moonlightNodeSandboxed.fs.join(dist, file.name);
118
+
const fullDir = moonlightNodeSandboxed.fs.dirname(fullFile);
119
+
if (!(await moonlightNodeSandboxed.fs.exists(fullDir))) await moonlightNodeSandboxed.fs.mkdir(fullDir);
120
+
await moonlightNodeSandboxed.fs.writeFile(fullFile, file.data);
121
+
}
122
+
123
+
logger.debug("Writing version file:", ref);
124
+
const versionFile = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), installedVersionFile);
125
+
await moonlightNodeSandboxed.fs.writeFileString(versionFile, ref.trim());
126
+
127
+
logger.debug("Update extracted");
128
+
},
129
+
130
+
async fetchRepositories(repos) {
131
+
const ret: Record<string, RepositoryManifest[]> = {};
132
+
133
+
for (const repo of repos) {
134
+
try {
135
+
const req = await fetch(repo, {
136
+
cache: "no-store",
137
+
headers: sharedHeaders
138
+
});
139
+
const json = await req.json();
140
+
ret[repo] = json;
141
+
} catch (e) {
142
+
logger.error(`Error fetching repository ${repo}`, e);
143
+
}
144
+
}
145
+
146
+
return ret;
147
+
},
148
+
149
+
async installExtension(manifest, url, repo) {
150
+
const req = await fetch(url, {
151
+
cache: "no-store",
152
+
headers: sharedHeaders
153
+
});
154
+
155
+
const dir = moonlightGlobal.getExtensionDir(manifest.id);
156
+
// remake it in case of updates
157
+
if (await moonlightNodeSandboxed.fs.exists(dir)) await moonlightNodeSandboxed.fs.rmdir(dir);
158
+
await moonlightNodeSandboxed.fs.mkdir(dir);
159
+
160
+
const buffer = await req.arrayBuffer();
161
+
const files = extractAsar(buffer);
162
+
for (const [file, buf] of Object.entries(files)) {
163
+
const fullFile = moonlightNodeSandboxed.fs.join(dir, file);
164
+
const fullDir = moonlightNodeSandboxed.fs.dirname(fullFile);
165
+
166
+
if (!(await moonlightNodeSandboxed.fs.exists(fullDir))) await moonlightNodeSandboxed.fs.mkdir(fullDir);
167
+
await moonlightNodeSandboxed.fs.writeFile(moonlightNodeSandboxed.fs.join(dir, file), buf);
168
+
}
169
+
170
+
await moonlightNodeSandboxed.fs.writeFileString(moonlightNodeSandboxed.fs.join(dir, repoUrlFile), repo);
171
+
},
172
+
173
+
async deleteExtension(id) {
174
+
const dir = moonlightGlobal.getExtensionDir(id);
175
+
await moonlightNodeSandboxed.fs.rmdir(dir);
176
+
}
177
+
};
178
+
}
+2
-69
packages/core-extensions/src/moonbase/node.ts
+2
-69
packages/core-extensions/src/moonbase/node.ts
···
1
-
import { MoonbaseNatives, RepositoryManifest } from "./types";
2
-
import asar from "@electron/asar";
3
-
import fs from "fs";
4
-
import path from "path";
5
-
import os from "os";
6
-
import { repoUrlFile } from "types/src/constants";
7
-
8
-
const logger = moonlightNode.getLogger("moonbase");
9
-
10
-
async function fetchRepositories(repos: string[]) {
11
-
const ret: Record<string, RepositoryManifest[]> = {};
12
-
13
-
for (const repo of repos) {
14
-
try {
15
-
const req = await fetch(repo);
16
-
const json = await req.json();
17
-
ret[repo] = json;
18
-
} catch (e) {
19
-
logger.error(`Error fetching repository ${repo}`, e);
20
-
}
21
-
}
22
-
23
-
return ret;
24
-
}
25
-
26
-
async function installExtension(
27
-
manifest: RepositoryManifest,
28
-
url: string,
29
-
repo: string
30
-
) {
31
-
const req = await fetch(url);
32
-
33
-
const dir = moonlightNode.getExtensionDir(manifest.id);
34
-
// remake it in case of updates
35
-
if (fs.existsSync(dir)) fs.rmdirSync(dir, { recursive: true });
36
-
fs.mkdirSync(dir, { recursive: true });
37
-
38
-
// for some reason i just can't .writeFileSync() a file that ends in .asar???
39
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "moonlight-"));
40
-
const tempFile = path.join(tempDir, "extension");
41
-
const buffer = await req.arrayBuffer();
42
-
fs.writeFileSync(tempFile, Buffer.from(buffer));
43
-
44
-
asar.extractAll(tempFile, dir);
45
-
fs.writeFileSync(path.join(dir, repoUrlFile), repo);
46
-
}
47
-
48
-
async function deleteExtension(id: string) {
49
-
const dir = moonlightNode.getExtensionDir(id);
50
-
fs.rmdirSync(dir, { recursive: true });
51
-
}
52
-
53
-
function getExtensionConfig(id: string, key: string): any {
54
-
const config = moonlightNode.config.extensions[id];
55
-
if (typeof config === "object") {
56
-
return config.config?.[key];
57
-
}
58
-
59
-
return undefined;
60
-
}
61
-
62
-
const exports: MoonbaseNatives = {
63
-
fetchRepositories,
64
-
installExtension,
65
-
deleteExtension,
66
-
getExtensionConfig
67
-
};
68
-
69
-
module.exports = exports;
1
+
import getNatives from "./native";
2
+
module.exports = getNatives();
+269
packages/core-extensions/src/moonbase/style.css
+269
packages/core-extensions/src/moonbase/style.css
···
1
+
:root {
2
+
--moonbase-bg: #222034;
3
+
--moonbase-fg: #fffba6;
4
+
}
5
+
6
+
.moonbase-settings > :first-child {
7
+
margin-top: 0px;
8
+
}
9
+
10
+
.moonbase-retry-button {
11
+
padding: 8px;
12
+
margin-right: 8px;
13
+
}
14
+
15
+
textarea.moonbase-resizeable {
16
+
resize: vertical;
17
+
}
18
+
19
+
.moonbase-link-buttons {
20
+
border-bottom: 2px solid var(--background-modifier-accent);
21
+
margin-bottom: -2px;
22
+
margin-left: 0 !important;
23
+
padding-right: 20px;
24
+
gap: 1rem;
25
+
}
26
+
27
+
.moonbase-speen {
28
+
animation: moonbase-speen-animation 0.25s linear infinite;
29
+
}
30
+
31
+
@keyframes moonbase-speen-animation {
32
+
from {
33
+
transform: rotate(0deg);
34
+
}
35
+
to {
36
+
transform: rotate(360deg);
37
+
}
38
+
}
39
+
40
+
/* Update notice at the top of the client */
41
+
.moonbase-updates-notice {
42
+
background-color: var(--moonbase-bg);
43
+
color: var(--moonbase-fg);
44
+
--custom-notice-text: var(--moonbase-fg);
45
+
line-height: unset;
46
+
height: 36px;
47
+
}
48
+
49
+
.moonbase-updates-notice button {
50
+
color: var(--moonbase-fg);
51
+
border-color: var(--moonbase-fg);
52
+
}
53
+
54
+
.moonbase-updates-notice_text-wrapper {
55
+
display: inline-flex;
56
+
align-items: center;
57
+
line-height: 36px;
58
+
gap: 2px;
59
+
}
60
+
61
+
/* Help messages in Moonbase UI */
62
+
.moonbase-help-message {
63
+
display: flex;
64
+
flex-direction: row;
65
+
justify-content: space-between;
66
+
}
67
+
68
+
.moonbase-help-message-sticky {
69
+
position: sticky;
70
+
top: 24px;
71
+
z-index: 10;
72
+
background-color: var(--background-primary);
73
+
}
74
+
75
+
.moonbase-extension-update-section {
76
+
margin-top: 15px;
77
+
}
78
+
79
+
.moonbase-update-section {
80
+
background-color: var(--moonbase-bg);
81
+
--info-help-foreground: var(--moonbase-fg);
82
+
border: none !important;
83
+
color: var(--moonbase-fg);
84
+
}
85
+
86
+
.moonbase-update-section button {
87
+
--info-help-foreground: var(--moonbase-fg);
88
+
color: var(--moonbase-fg);
89
+
background-color: transparent;
90
+
border-color: var(--moonbase-fg);
91
+
}
92
+
93
+
.moonbase-help-message-buttons {
94
+
display: flex;
95
+
flex-direction: row;
96
+
gap: 8px;
97
+
align-items: center;
98
+
}
99
+
100
+
.moonbase-update-divider {
101
+
margin: 32px 0;
102
+
}
103
+
104
+
.moonlight-card-info-header {
105
+
margin-bottom: 0.25rem;
106
+
}
107
+
108
+
.moonlight-card-badge {
109
+
border-radius: 0.1875rem;
110
+
padding: 0 0.275rem;
111
+
margin-right: 0.4em;
112
+
background-color: var(--badge-color, var(--bg-mod-strong));
113
+
}
114
+
115
+
/* Crash screen */
116
+
.moonbase-crash-wrapper > [class^="buttons_"] {
117
+
gap: 1rem;
118
+
}
119
+
120
+
.moonbase-crash-wrapper {
121
+
display: flex;
122
+
flex-direction: column;
123
+
align-items: center;
124
+
gap: 1rem;
125
+
height: 50%;
126
+
width: 50vw;
127
+
max-height: 50%;
128
+
max-width: 50vw;
129
+
}
130
+
131
+
.moonbase-crash-tabs {
132
+
width: 100%;
133
+
}
134
+
135
+
.moonbase-crash-details-wrapper {
136
+
overflow-y: scroll;
137
+
color: var(--text-normal);
138
+
background: var(--background-secondary);
139
+
border: 1px solid var(--background-tertiary);
140
+
border-radius: 4px;
141
+
padding: 0.5em;
142
+
143
+
&::-webkit-scrollbar {
144
+
width: 8px;
145
+
height: 8px;
146
+
}
147
+
148
+
&::-webkit-scrollbar-thumb {
149
+
background-clip: padding-box;
150
+
border: 2px solid transparent;
151
+
border-radius: 4px;
152
+
background-color: var(--scrollbar-thin-thumb);
153
+
min-height: 40px;
154
+
}
155
+
156
+
&::-webkit-scrollbar-track {
157
+
border: 2px solid var(--scrollbar-thin-track);
158
+
background-color: var(--scrollbar-thin-track);
159
+
border-color: var(--scrollbar-thin-track);
160
+
}
161
+
}
162
+
163
+
.moonbase-crash-details {
164
+
box-sizing: border-box;
165
+
padding: 0;
166
+
font-family: var(--font-code);
167
+
font-size: 0.75rem;
168
+
line-height: 1rem;
169
+
margin: 6px;
170
+
white-space: pre-wrap;
171
+
background-clip: border-box;
172
+
173
+
& > code {
174
+
font-size: 0.875rem;
175
+
line-height: 1.125rem;
176
+
text-indent: 0;
177
+
white-space: pre-wrap;
178
+
text-size-adjust: none;
179
+
display: block;
180
+
user-select: text;
181
+
}
182
+
}
183
+
184
+
.moonbase-crash-extensions {
185
+
overflow-y: scroll;
186
+
display: grid;
187
+
grid-auto-columns: 25vw;
188
+
gap: 8px;
189
+
190
+
&::-webkit-scrollbar {
191
+
width: 8px;
192
+
height: 8px;
193
+
}
194
+
195
+
&::-webkit-scrollbar-thumb {
196
+
background-clip: padding-box;
197
+
border: 2px solid transparent;
198
+
border-radius: 4px;
199
+
background-color: var(--scrollbar-thin-thumb);
200
+
min-height: 40px;
201
+
}
202
+
203
+
&::-webkit-scrollbar-track {
204
+
border: 2px solid var(--scrollbar-thin-track);
205
+
background-color: var(--scrollbar-thin-track);
206
+
border-color: var(--scrollbar-thin-track);
207
+
}
208
+
}
209
+
210
+
.moonbase-crash-extensionCard {
211
+
color: var(--text-normal);
212
+
background: var(--background-secondary);
213
+
border: 1px solid var(--background-tertiary);
214
+
border-radius: 4px;
215
+
padding: 0.5em;
216
+
display: flex;
217
+
}
218
+
219
+
.moonbase-crash-extensionCard-meta {
220
+
display: flex;
221
+
flex-direction: column;
222
+
flex-grow: 1;
223
+
}
224
+
225
+
.moonbase-crash-extensionCard-title {
226
+
color: var(--text-normal);
227
+
font-family: var(--font-primary);
228
+
font-size: 16px;
229
+
line-height: 1.25;
230
+
font-weight: 600;
231
+
}
232
+
233
+
.moonbase-crash-extensionCard-version {
234
+
color: var(--text-muted);
235
+
font-family: var(--font-primary);
236
+
font-size: 14px;
237
+
line-height: 1.286;
238
+
font-weight: 400;
239
+
}
240
+
241
+
/* About page */
242
+
.moonbase-wordmark {
243
+
width: 100%;
244
+
}
245
+
246
+
.moonbase-devs {
247
+
width: 100%;
248
+
display: flex;
249
+
justify-content: center;
250
+
gap: 0rem 0.5rem;
251
+
padding-top: 0.5rem;
252
+
}
253
+
254
+
.moonbase-dev {
255
+
height: 4rem;
256
+
}
257
+
258
+
.moonbase-dev-avatar {
259
+
width: 2rem;
260
+
border-radius: 50%;
261
+
}
262
+
263
+
.moonbase-gap {
264
+
gap: 0.5rem;
265
+
}
266
+
267
+
.moonbase-about-page {
268
+
gap: 1rem;
269
+
}
+27
-25
packages/core-extensions/src/moonbase/types.ts
+27
-25
packages/core-extensions/src/moonbase/types.ts
···
1
-
import { DetectedExtension, ExtensionManifest } from "types/src";
2
-
3
-
export const DownloadIconSVG =
4
-
"M12 2a1 1 0 0 1 1 1v10.59l3.3-3.3a1 1 0 1 1 1.4 1.42l-5 5a1 1 0 0 1-1.4 0l-5-5a1 1 0 1 1 1.4-1.42l3.3 3.3V3a1 1 0 0 1 1-1ZM3 20a1 1 0 1 0 0 2h18a1 1 0 1 0 0-2H3Z";
5
-
export const TrashIconSVG =
6
-
"M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z";
7
-
export const CircleXIconSVG =
8
-
"M7.02799 0.333252C3.346 0.333252 0.361328 3.31792 0.361328 6.99992C0.361328 10.6819 3.346 13.6666 7.02799 13.6666C10.71 13.6666 13.6947 10.6819 13.6947 6.99992C13.6947 3.31792 10.7093 0.333252 7.02799 0.333252ZM10.166 9.19525L9.22333 10.1379L7.02799 7.94325L4.83266 10.1379L3.89 9.19525L6.08466 6.99992L3.88933 4.80459L4.832 3.86259L7.02733 6.05792L9.22266 3.86259L10.1653 4.80459L7.97066 6.99992L10.166 9.19525Z";
9
-
export const DangerIconSVG =
10
-
"M12 23a11 11 0 1 0 0-22 11 11 0 0 0 0 22Zm1.44-15.94L13.06 14a1.06 1.06 0 0 1-2.12 0l-.38-6.94a1 1 0 0 1 1-1.06h.88a1 1 0 0 1 1 1.06Zm-.19 10.69a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Z";
11
-
export const ChevronSmallDownIconSVG =
12
-
"M16.59 8.59003L12 13.17L7.41 8.59003L6 10L12 16L18 10L16.59 8.59003Z";
13
-
export const ChevronSmallUpIconSVG =
14
-
"M7.41 16.0001L12 11.4201L16.59 16.0001L18 14.5901L12 8.59006L6 14.5901L7.41 16.0001Z";
15
-
export const ArrowsUpDownIconSVG =
16
-
"M3.81962 11.3333L3.81962 1.33325L5.52983 1.33325L5.52985 11.3333L7.46703 9.36658L8.66663 10.5916L4.67068 14.6666L0.666626 10.5916L1.86622 9.34158L3.81962 11.3333Z";
1
+
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
2
+
import { DetectedExtension, ExtensionManifest, MoonlightBranch } from "@moonlight-mod/types";
17
3
18
4
export type MoonbaseNatives = {
19
-
fetchRepositories(
20
-
repos: string[]
21
-
): Promise<Record<string, RepositoryManifest[]>>;
22
-
installExtension(
23
-
manifest: RepositoryManifest,
24
-
url: string,
25
-
repo: string
26
-
): Promise<void>;
5
+
checkForMoonlightUpdate(): Promise<string | null>;
6
+
updateMoonlight(overrideBranch?: MoonlightBranch): Promise<void>;
7
+
8
+
fetchRepositories(repos: string[]): Promise<Record<string, RepositoryManifest[]>>;
9
+
installExtension(manifest: RepositoryManifest, url: string, repo: string): Promise<void>;
27
10
deleteExtension(id: string): Promise<void>;
28
-
getExtensionConfig(id: string, key: string): any;
29
11
};
30
12
31
13
export type RepositoryManifest = ExtensionManifest & {
···
40
22
41
23
export type MoonbaseExtension = {
42
24
id: string;
25
+
uniqueId: number;
43
26
manifest: ExtensionManifest | RepositoryManifest;
44
27
source: DetectedExtension["source"];
45
28
state: ExtensionState;
29
+
compat: ExtensionCompat;
30
+
hasUpdate: boolean;
31
+
changelog?: string;
32
+
settingsOverride?: ExtensionManifest["settings"];
46
33
};
34
+
35
+
export enum UpdateState {
36
+
Ready,
37
+
Working,
38
+
Installed,
39
+
Failed
40
+
}
41
+
42
+
// Ordered in terms of priority
43
+
export enum RestartAdvice {
44
+
NotNeeded, // No action is needed
45
+
ReloadSuggested, // A reload might be needed
46
+
ReloadNeeded, // A reload is needed
47
+
RestartNeeded // A restart is needed
48
+
}
-157
packages/core-extensions/src/moonbase/ui/config/index.tsx
-157
packages/core-extensions/src/moonbase/ui/config/index.tsx
···
1
-
import { LogLevel, WebpackRequireType } from "@moonlight-mod/types";
2
-
import { CircleXIconSVG } from "../../types";
3
-
4
-
const logLevels = Object.values(LogLevel).filter(
5
-
(v) => typeof v === "string"
6
-
) as string[];
7
-
8
-
export default (require: WebpackRequireType) => {
9
-
const React = require("common_react");
10
-
const spacepack = require("spacepack_spacepack").spacepack;
11
-
const CommonComponents = require("common_components");
12
-
const {
13
-
FormDivider,
14
-
FormItem,
15
-
FormText,
16
-
FormSwitch,
17
-
TextInput,
18
-
Flex,
19
-
Button,
20
-
SingleSelect
21
-
} = CommonComponents;
22
-
23
-
const { MoonbaseSettingsStore } =
24
-
require("moonbase_stores") as typeof import("../../webpackModules/stores");
25
-
26
-
const FormClasses = spacepack.findByCode("dividerDefault:")[0].exports;
27
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
28
-
29
-
const RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0]
30
-
.exports;
31
-
const CircleXIcon = spacepack.findByCode(CircleXIconSVG)[0].exports.default;
32
-
function RemoveEntryButton({ onClick }: { onClick: () => void }) {
33
-
const { Tooltip, Clickable } = CommonComponents;
34
-
return (
35
-
<div className={RemoveButtonClasses.removeButtonContainer}>
36
-
<Tooltip text="Remove entry" position="top">
37
-
{(props: any) => (
38
-
<Clickable
39
-
{...props}
40
-
className={RemoveButtonClasses.removeButton}
41
-
onClick={onClick}
42
-
>
43
-
<CircleXIcon width={24} height={24} />
44
-
</Clickable>
45
-
)}
46
-
</Tooltip>
47
-
</div>
48
-
);
49
-
}
50
-
51
-
function ArrayFormItem({
52
-
config
53
-
}: {
54
-
config: "repositories" | "devSearchPaths";
55
-
}) {
56
-
const items = MoonbaseSettingsStore.getConfigOption(config) ?? [];
57
-
return (
58
-
<Flex
59
-
style={{
60
-
gap: "20px"
61
-
}}
62
-
direction={Flex.Direction.VERTICAL}
63
-
>
64
-
{items.map((val, i) => (
65
-
<div
66
-
key={i}
67
-
style={{
68
-
display: "grid",
69
-
height: "32px",
70
-
gap: "8px",
71
-
gridTemplateColumns: "1fr 32px",
72
-
alignItems: "center"
73
-
}}
74
-
>
75
-
<TextInput
76
-
size={TextInput.Sizes.DEFAULT}
77
-
value={val}
78
-
onChange={(newVal: string) => {
79
-
items[i] = newVal;
80
-
MoonbaseSettingsStore.setConfigOption(config, items);
81
-
}}
82
-
/>
83
-
<RemoveEntryButton
84
-
onClick={() => {
85
-
items.splice(i, 1);
86
-
MoonbaseSettingsStore.setConfigOption(config, items);
87
-
}}
88
-
/>
89
-
</div>
90
-
))}
91
-
92
-
<Button
93
-
look={Button.Looks.FILLED}
94
-
color={Button.Colors.GREEN}
95
-
size={Button.Sizes.SMALL}
96
-
style={{
97
-
marginTop: "10px"
98
-
}}
99
-
onClick={() => {
100
-
items.push("");
101
-
MoonbaseSettingsStore.setConfigOption(config, items);
102
-
}}
103
-
>
104
-
Add new entry
105
-
</Button>
106
-
</Flex>
107
-
);
108
-
}
109
-
110
-
return function ConfigPage() {
111
-
return (
112
-
<>
113
-
<FormItem title="Repositories">
114
-
<FormText className={Margins.marginBottom4}>
115
-
A list of remote repositories to display extensions from
116
-
</FormText>
117
-
<ArrayFormItem config="repositories" />
118
-
</FormItem>
119
-
<FormDivider className={FormClasses.dividerDefault} />
120
-
<FormItem
121
-
title="Extension search paths"
122
-
className={Margins.marginTop20}
123
-
>
124
-
<FormText className={Margins.marginBottom4}>
125
-
A list of local directories to search for built extensions
126
-
</FormText>
127
-
<ArrayFormItem config="devSearchPaths" />
128
-
</FormItem>
129
-
<FormDivider className={FormClasses.dividerDefault} />
130
-
<FormSwitch
131
-
className={Margins.marginTop20}
132
-
value={MoonbaseSettingsStore.getConfigOption("patchAll")}
133
-
onChange={(value: boolean) => {
134
-
MoonbaseSettingsStore.setConfigOption("patchAll", value);
135
-
}}
136
-
note="Wraps every webpack module in a function, separating them in DevTools"
137
-
>
138
-
Patch all
139
-
</FormSwitch>
140
-
<FormItem title="Log level">
141
-
<SingleSelect
142
-
autofocus={false}
143
-
clearable={false}
144
-
value={MoonbaseSettingsStore.getConfigOption("loggerLevel")}
145
-
options={logLevels.map((o) => ({
146
-
value: o.toLowerCase(),
147
-
label: o[0] + o.slice(1).toLowerCase()
148
-
}))}
149
-
onChange={(v) =>
150
-
MoonbaseSettingsStore.setConfigOption("loggerLevel", v)
151
-
}
152
-
/>
153
-
</FormItem>
154
-
</>
155
-
);
156
-
};
157
-
};
-223
packages/core-extensions/src/moonbase/ui/extensions/card.tsx
-223
packages/core-extensions/src/moonbase/ui/extensions/card.tsx
···
1
-
import WebpackRequire from "@moonlight-mod/types/discord/require";
2
-
import {
3
-
DangerIconSVG,
4
-
DownloadIconSVG,
5
-
ExtensionState,
6
-
TrashIconSVG
7
-
} from "../../types";
8
-
import { ExtensionLoadSource } from "@moonlight-mod/types";
9
-
import info from "./info";
10
-
import settings from "./settings";
11
-
12
-
export enum ExtensionPage {
13
-
Info,
14
-
Description,
15
-
Settings
16
-
}
17
-
18
-
export default (require: typeof WebpackRequire) => {
19
-
const React = require("common_react");
20
-
const spacepack = require("spacepack_spacepack").spacepack;
21
-
const CommonComponents = require("common_components");
22
-
const Flux = require("common_flux");
23
-
24
-
const { ExtensionInfo } = info(require);
25
-
const Settings = settings(require);
26
-
const { MoonbaseSettingsStore } =
27
-
require("moonbase_stores") as typeof import("../../webpackModules/stores");
28
-
29
-
const UserProfileClasses = spacepack.findByCode(
30
-
"tabBarContainer",
31
-
"topSection"
32
-
)[0].exports;
33
-
34
-
const DownloadIcon =
35
-
spacepack.findByCode(DownloadIconSVG)[0].exports.DownloadIcon;
36
-
const TrashIcon = spacepack.findByCode(TrashIconSVG)[0].exports.default;
37
-
const DangerIcon =
38
-
spacepack.findByCode(DangerIconSVG)[0].exports.CircleExclamationPointIcon;
39
-
40
-
const PanelButton =
41
-
spacepack.findByCode("Masks.PANEL_BUTTON")[0].exports.default;
42
-
43
-
return function ExtensionCard({ id }: { id: string }) {
44
-
const [tab, setTab] = React.useState(ExtensionPage.Info);
45
-
const [restartNeeded, setRestartNeeded] = React.useState(false);
46
-
47
-
const { ext, enabled, busy, update } = Flux.useStateFromStores(
48
-
[MoonbaseSettingsStore],
49
-
() => {
50
-
return {
51
-
ext: MoonbaseSettingsStore.getExtension(id),
52
-
enabled: MoonbaseSettingsStore.getExtensionEnabled(id),
53
-
busy: MoonbaseSettingsStore.busy,
54
-
update: MoonbaseSettingsStore.getExtensionUpdate(id)
55
-
};
56
-
}
57
-
);
58
-
59
-
// Why it work like that :sob:
60
-
if (ext == null) return <></>;
61
-
62
-
const {
63
-
Card,
64
-
CardClasses,
65
-
Flex,
66
-
Text,
67
-
MarkdownParser,
68
-
Switch,
69
-
TabBar,
70
-
Button
71
-
} = CommonComponents;
72
-
73
-
const tagline = ext.manifest?.meta?.tagline;
74
-
const settings = ext.manifest?.settings;
75
-
const description = ext.manifest?.meta?.description;
76
-
77
-
return (
78
-
<Card editable={true} className={CardClasses.card}>
79
-
<div className={CardClasses.cardHeader}>
80
-
<Flex direction={Flex.Direction.VERTICAL}>
81
-
<Flex direction={Flex.Direction.HORIZONTAL}>
82
-
<Text variant="text-md/semibold">
83
-
{ext.manifest?.meta?.name ?? ext.id}
84
-
</Text>
85
-
</Flex>
86
-
87
-
{tagline != null && (
88
-
<Text variant="text-sm/normal">
89
-
{MarkdownParser.parse(tagline)}
90
-
</Text>
91
-
)}
92
-
</Flex>
93
-
94
-
<Flex
95
-
direction={Flex.Direction.HORIZONTAL}
96
-
align={Flex.Align.END}
97
-
justify={Flex.Justify.END}
98
-
>
99
-
{ext.state === ExtensionState.NotDownloaded ? (
100
-
<Button
101
-
color={Button.Colors.BRAND}
102
-
submitting={busy}
103
-
onClick={() => {
104
-
MoonbaseSettingsStore.installExtension(id);
105
-
}}
106
-
>
107
-
Install
108
-
</Button>
109
-
) : (
110
-
<div
111
-
// too lazy to learn how <Flex /> works lmao
112
-
style={{
113
-
display: "flex",
114
-
alignItems: "center",
115
-
gap: "1rem"
116
-
}}
117
-
>
118
-
{ext.source.type === ExtensionLoadSource.Normal && (
119
-
<PanelButton
120
-
icon={TrashIcon}
121
-
tooltipText="Delete"
122
-
onClick={() => {
123
-
MoonbaseSettingsStore.deleteExtension(id);
124
-
}}
125
-
/>
126
-
)}
127
-
128
-
{update !== null && (
129
-
<PanelButton
130
-
icon={DownloadIcon}
131
-
tooltipText="Update"
132
-
onClick={() => {
133
-
MoonbaseSettingsStore.installExtension(id);
134
-
}}
135
-
/>
136
-
)}
137
-
138
-
{restartNeeded && (
139
-
<PanelButton
140
-
icon={() => (
141
-
<DangerIcon
142
-
color={CommonComponents.tokens.colors.STATUS_DANGER}
143
-
/>
144
-
)}
145
-
onClick={() => window.location.reload()}
146
-
tooltipText="You will need to reload/restart your client for this extension to work properly."
147
-
/>
148
-
)}
149
-
150
-
<Switch
151
-
checked={enabled}
152
-
onChange={() => {
153
-
setRestartNeeded(true);
154
-
MoonbaseSettingsStore.setExtensionEnabled(id, !enabled);
155
-
}}
156
-
/>
157
-
</div>
158
-
)}
159
-
</Flex>
160
-
</div>
161
-
162
-
<div className={UserProfileClasses.body}>
163
-
{(description != null || settings != null) && (
164
-
<div
165
-
className={UserProfileClasses.tabBarContainer}
166
-
style={{
167
-
padding: "0 10px"
168
-
}}
169
-
>
170
-
<TabBar
171
-
selectedItem={tab}
172
-
type="top"
173
-
onItemSelect={setTab}
174
-
className={UserProfileClasses.tabBar}
175
-
>
176
-
<TabBar.Item
177
-
className={UserProfileClasses.tabBarItem}
178
-
id={ExtensionPage.Info}
179
-
>
180
-
Info
181
-
</TabBar.Item>
182
-
183
-
{description != null && (
184
-
<TabBar.Item
185
-
className={UserProfileClasses.tabBarItem}
186
-
id={ExtensionPage.Description}
187
-
>
188
-
Description
189
-
</TabBar.Item>
190
-
)}
191
-
192
-
{settings != null && (
193
-
<TabBar.Item
194
-
className={UserProfileClasses.tabBarItem}
195
-
id={ExtensionPage.Settings}
196
-
>
197
-
Settings
198
-
</TabBar.Item>
199
-
)}
200
-
</TabBar>
201
-
</div>
202
-
)}
203
-
204
-
<Flex
205
-
justify={Flex.Justify.START}
206
-
wrap={Flex.Wrap.WRAP}
207
-
style={{
208
-
padding: "16px 16px"
209
-
}}
210
-
>
211
-
{tab === ExtensionPage.Info && <ExtensionInfo ext={ext} />}
212
-
{tab === ExtensionPage.Description && (
213
-
<Text variant="text-md/normal">
214
-
{MarkdownParser.parse(description ?? "*No description*")}
215
-
</Text>
216
-
)}
217
-
{tab === ExtensionPage.Settings && <Settings ext={ext} />}
218
-
</Flex>
219
-
</div>
220
-
</Card>
221
-
);
222
-
};
223
-
};
-373
packages/core-extensions/src/moonbase/ui/extensions/filterBar.tsx
-373
packages/core-extensions/src/moonbase/ui/extensions/filterBar.tsx
···
1
-
import { WebpackRequireType } from "@moonlight-mod/types";
2
-
import { tagNames } from "./info";
3
-
import {
4
-
ArrowsUpDownIconSVG,
5
-
ChevronSmallDownIconSVG,
6
-
ChevronSmallUpIconSVG
7
-
} from "../../types";
8
-
9
-
export enum Filter {
10
-
Core = 1 << 0,
11
-
Normal = 1 << 1,
12
-
Developer = 1 << 2,
13
-
Enabled = 1 << 3,
14
-
Disabled = 1 << 4,
15
-
Installed = 1 << 5,
16
-
Repository = 1 << 6
17
-
}
18
-
export const defaultFilter = ~(~0 << 7);
19
-
20
-
export default async (require: WebpackRequireType) => {
21
-
const spacepack = require("spacepack_spacepack").spacepack;
22
-
const React = require("common_react");
23
-
const Flux = require("common_flux");
24
-
const { WindowStore } = require("common_stores");
25
-
26
-
const {
27
-
Button,
28
-
Text,
29
-
Heading,
30
-
Popout,
31
-
Dialog
32
-
} = require("common_components");
33
-
34
-
const channelModule =
35
-
require.m[
36
-
spacepack.findByCode(
37
-
'"Missing channel in Channel.openChannelContextMenu"'
38
-
)[0].id
39
-
].toString();
40
-
const moduleId = channelModule.match(/webpackId:"(.+?)"/)![1];
41
-
await require.el(moduleId);
42
-
43
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
44
-
const SortMenuClasses = spacepack.findByCode("container:", "clearText:")[0]
45
-
.exports;
46
-
const FilterDialogClasses = spacepack.findByCode(
47
-
"countContainer:",
48
-
"tagContainer:"
49
-
)[0].exports;
50
-
const FilterBarClasses = spacepack.findByCode("tagsButtonWithCount:")[0]
51
-
.exports;
52
-
53
-
const TagItem = spacepack.findByCode("IncreasedActivityForumTagPill:")[0]
54
-
.exports.default;
55
-
56
-
const ChevronSmallDownIcon = spacepack.findByCode(ChevronSmallDownIconSVG)[0]
57
-
.exports.default;
58
-
const ChevronSmallUpIcon = spacepack.findByCode(ChevronSmallUpIconSVG)[0]
59
-
.exports.default;
60
-
const ArrowsUpDownIcon =
61
-
spacepack.findByCode(ArrowsUpDownIconSVG)[0].exports.default;
62
-
63
-
function toggleTag(
64
-
selectedTags: Set<string>,
65
-
setSelectedTags: (tags: Set<string>) => void,
66
-
tag: string
67
-
) {
68
-
const newState = new Set(selectedTags);
69
-
if (newState.has(tag)) newState.delete(tag);
70
-
else newState.add(tag);
71
-
setSelectedTags(newState);
72
-
}
73
-
74
-
function FilterButtonPopout({
75
-
filter,
76
-
setFilter,
77
-
closePopout
78
-
}: {
79
-
filter: Filter;
80
-
setFilter: (filter: Filter) => void;
81
-
closePopout: () => void;
82
-
}) {
83
-
const {
84
-
Menu,
85
-
MenuItem,
86
-
MenuGroup,
87
-
MenuCheckboxItem
88
-
} = require("common_components");
89
-
90
-
const toggleFilter = (set: Filter) =>
91
-
setFilter(filter & set ? filter & ~set : filter | set);
92
-
93
-
return (
94
-
<div className={SortMenuClasses.container}>
95
-
<Menu navId="sort-filter" hideScrollbar={true} onClose={closePopout}>
96
-
<MenuGroup label="Type">
97
-
<MenuCheckboxItem
98
-
id="t-core"
99
-
label="Core"
100
-
checked={filter & Filter.Core}
101
-
action={() => toggleFilter(Filter.Core)}
102
-
/>
103
-
<MenuCheckboxItem
104
-
id="t-normal"
105
-
label="Normal"
106
-
checked={filter & Filter.Normal}
107
-
action={() => toggleFilter(Filter.Normal)}
108
-
/>
109
-
<MenuCheckboxItem
110
-
id="t-developer"
111
-
label="Developer"
112
-
checked={filter & Filter.Developer}
113
-
action={() => toggleFilter(Filter.Developer)}
114
-
/>
115
-
</MenuGroup>
116
-
<MenuGroup label="State">
117
-
<MenuCheckboxItem
118
-
id="s-enabled"
119
-
label="Enabled"
120
-
checked={filter & Filter.Enabled}
121
-
action={() => toggleFilter(Filter.Enabled)}
122
-
/>
123
-
<MenuCheckboxItem
124
-
id="s-disabled"
125
-
label="Disabled"
126
-
checked={filter & Filter.Disabled}
127
-
action={() => toggleFilter(Filter.Disabled)}
128
-
/>
129
-
</MenuGroup>
130
-
<MenuGroup label="Location">
131
-
<MenuCheckboxItem
132
-
id="l-installed"
133
-
label="Installed"
134
-
checked={filter & Filter.Installed}
135
-
action={() => toggleFilter(Filter.Installed)}
136
-
/>
137
-
<MenuCheckboxItem
138
-
id="l-repository"
139
-
label="Repository"
140
-
checked={filter & Filter.Repository}
141
-
action={() => toggleFilter(Filter.Repository)}
142
-
/>
143
-
</MenuGroup>
144
-
<MenuGroup>
145
-
<MenuItem
146
-
id="reset-all"
147
-
className={SortMenuClasses.clearText}
148
-
label={
149
-
<Text variant="text-sm/medium" color="none">
150
-
Reset to default
151
-
</Text>
152
-
}
153
-
action={() => {
154
-
setFilter(defaultFilter);
155
-
closePopout();
156
-
}}
157
-
/>
158
-
</MenuGroup>
159
-
</Menu>
160
-
</div>
161
-
);
162
-
}
163
-
164
-
function TagButtonPopout({
165
-
selectedTags,
166
-
setSelectedTags,
167
-
setPopoutRef,
168
-
closePopout
169
-
}: any) {
170
-
return (
171
-
<Dialog ref={setPopoutRef} className={FilterDialogClasses.container}>
172
-
<div className={FilterDialogClasses.header}>
173
-
<div className={FilterDialogClasses.headerLeft}>
174
-
<Heading
175
-
color="interactive-normal"
176
-
variant="text-xs/bold"
177
-
className={FilterDialogClasses.headerText}
178
-
>
179
-
Select tags
180
-
</Heading>
181
-
<div className={FilterDialogClasses.countContainer}>
182
-
<Text
183
-
className={FilterDialogClasses.countText}
184
-
color="none"
185
-
variant="text-xs/medium"
186
-
>
187
-
{selectedTags.size}
188
-
</Text>
189
-
</div>
190
-
</div>
191
-
</div>
192
-
<div className={FilterDialogClasses.tagContainer}>
193
-
{Object.keys(tagNames).map((tag) => (
194
-
<TagItem
195
-
key={tag}
196
-
className={FilterDialogClasses.tag}
197
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
198
-
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
199
-
selected={selectedTags.has(tag)}
200
-
/>
201
-
))}
202
-
</div>
203
-
<div className={FilterDialogClasses.separator} />
204
-
<Button
205
-
look={Button.Looks.LINK}
206
-
size={Button.Sizes.MIN}
207
-
color={Button.Colors.CUSTOM}
208
-
className={FilterDialogClasses.clear}
209
-
onClick={() => {
210
-
setSelectedTags(new Set());
211
-
closePopout();
212
-
}}
213
-
>
214
-
<Text variant="text-sm/medium" color="text-link">
215
-
Clear all
216
-
</Text>
217
-
</Button>
218
-
</Dialog>
219
-
);
220
-
}
221
-
222
-
return function FilterBar({
223
-
filter,
224
-
setFilter,
225
-
selectedTags,
226
-
setSelectedTags
227
-
}: {
228
-
filter: Filter;
229
-
setFilter: (filter: Filter) => void;
230
-
selectedTags: Set<string>;
231
-
setSelectedTags: (tags: Set<string>) => void;
232
-
}) {
233
-
const windowSize = Flux.useStateFromStores([WindowStore], () =>
234
-
WindowStore.windowSize()
235
-
);
236
-
237
-
const tagsContainer = React.useRef<HTMLDivElement>(null);
238
-
const tagListInner = React.useRef<HTMLDivElement>(null);
239
-
const [tagsButtonOffset, setTagsButtonOffset] = React.useState(0);
240
-
React.useLayoutEffect(() => {
241
-
if (tagsContainer.current === null || tagListInner.current === null)
242
-
return;
243
-
const { left: containerX, top: containerY } =
244
-
tagsContainer.current.getBoundingClientRect();
245
-
let offset = 0;
246
-
for (const child of tagListInner.current.children) {
247
-
const {
248
-
right: childX,
249
-
top: childY,
250
-
height
251
-
} = child.getBoundingClientRect();
252
-
if (childY - containerY > height) break;
253
-
const newOffset = childX - containerX;
254
-
if (newOffset > offset) {
255
-
offset = newOffset;
256
-
}
257
-
}
258
-
setTagsButtonOffset(offset);
259
-
}, [windowSize]);
260
-
261
-
return (
262
-
<div
263
-
ref={tagsContainer}
264
-
style={{
265
-
paddingTop: "12px"
266
-
}}
267
-
className={`${FilterBarClasses.tagsContainer} ${Margins.marginBottom8}`}
268
-
>
269
-
<Popout
270
-
renderPopout={({ closePopout }: any) => (
271
-
<FilterButtonPopout
272
-
filter={filter}
273
-
setFilter={setFilter}
274
-
closePopout={closePopout}
275
-
/>
276
-
)}
277
-
position="bottom"
278
-
align="left"
279
-
>
280
-
{(props: any, { isShown }: { isShown: boolean }) => (
281
-
<Button
282
-
{...props}
283
-
size={Button.Sizes.MIN}
284
-
color={Button.Colors.CUSTOM}
285
-
className={FilterBarClasses.sortDropdown}
286
-
innerClassName={FilterBarClasses.sortDropdownInner}
287
-
>
288
-
<ArrowsUpDownIcon />
289
-
<Text
290
-
className={FilterBarClasses.sortDropdownText}
291
-
variant="text-sm/medium"
292
-
color="interactive-normal"
293
-
>
294
-
Sort & filter
295
-
</Text>
296
-
{isShown ? (
297
-
<ChevronSmallUpIcon size={20} />
298
-
) : (
299
-
<ChevronSmallDownIcon size={20} />
300
-
)}
301
-
</Button>
302
-
)}
303
-
</Popout>
304
-
<div className={FilterBarClasses.divider} />
305
-
<div className={FilterBarClasses.tagList}>
306
-
<div ref={tagListInner} className={FilterBarClasses.tagListInner}>
307
-
{Object.keys(tagNames).map((tag) => (
308
-
<TagItem
309
-
key={tag}
310
-
className={FilterBarClasses.tag}
311
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
312
-
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
313
-
selected={selectedTags.has(tag)}
314
-
/>
315
-
))}
316
-
</div>
317
-
</div>
318
-
<Popout
319
-
renderPopout={({ setPopoutRef, closePopout }: any) => (
320
-
<TagButtonPopout
321
-
selectedTags={selectedTags}
322
-
setSelectedTags={setSelectedTags}
323
-
setPopoutRef={setPopoutRef}
324
-
closePopout={closePopout}
325
-
/>
326
-
)}
327
-
position="bottom"
328
-
align="right"
329
-
>
330
-
{(props: any, { isShown }: { isShown: boolean }) => (
331
-
<Button
332
-
{...props}
333
-
size={Button.Sizes.MIN}
334
-
color={Button.Colors.CUSTOM}
335
-
style={{
336
-
left: tagsButtonOffset
337
-
}}
338
-
// TODO: Use Discord's class name utility
339
-
className={`${FilterBarClasses.tagsButton} ${
340
-
selectedTags.size > 0
341
-
? FilterBarClasses.tagsButtonWithCount
342
-
: ""
343
-
}`}
344
-
innerClassName={FilterBarClasses.tagsButtonInner}
345
-
>
346
-
{selectedTags.size > 0 ? (
347
-
<div
348
-
style={{ boxSizing: "content-box" }}
349
-
className={FilterBarClasses.countContainer}
350
-
>
351
-
<Text
352
-
className={FilterBarClasses.countText}
353
-
color="none"
354
-
variant="text-xs/medium"
355
-
>
356
-
{selectedTags.size}
357
-
</Text>
358
-
</div>
359
-
) : (
360
-
<>All</>
361
-
)}
362
-
{isShown ? (
363
-
<ChevronSmallUpIcon size={20} />
364
-
) : (
365
-
<ChevronSmallDownIcon size={20} />
366
-
)}
367
-
</Button>
368
-
)}
369
-
</Popout>
370
-
</div>
371
-
);
372
-
};
373
-
};
-118
packages/core-extensions/src/moonbase/ui/extensions/index.tsx
-118
packages/core-extensions/src/moonbase/ui/extensions/index.tsx
···
1
-
import {
2
-
ExtensionLoadSource,
3
-
ExtensionTag,
4
-
WebpackRequireType
5
-
} from "@moonlight-mod/types";
6
-
import { ExtensionState } from "../../types";
7
-
import filterBar, { Filter, defaultFilter } from "./filterBar";
8
-
import card from "./card";
9
-
10
-
export default (require: WebpackRequireType) => {
11
-
const React = require("common_react");
12
-
const spacepack = require("spacepack_spacepack").spacepack;
13
-
const Flux = require("common_flux");
14
-
15
-
const { MoonbaseSettingsStore } =
16
-
require("moonbase_stores") as typeof import("../../webpackModules/stores");
17
-
18
-
const ExtensionCard = card(require);
19
-
const FilterBar = React.lazy(() =>
20
-
filterBar(require).then((c) => ({ default: c }))
21
-
);
22
-
23
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
24
-
const SearchBar = spacepack.findByCode("Messages.SEARCH", "hideSearchIcon")[0]
25
-
.exports.default;
26
-
27
-
return function ExtensionsPage() {
28
-
const { extensions, savedFilter } = Flux.useStateFromStoresObject(
29
-
[MoonbaseSettingsStore],
30
-
() => {
31
-
return {
32
-
extensions: MoonbaseSettingsStore.extensions,
33
-
savedFilter: MoonbaseSettingsStore.getExtensionConfig(
34
-
"moonbase",
35
-
"filter"
36
-
)
37
-
};
38
-
}
39
-
);
40
-
41
-
const [query, setQuery] = React.useState("");
42
-
43
-
let filter: Filter, setFilter: (filter: Filter) => void;
44
-
if (moonlight.getConfigOption<boolean>("moonbase", "saveFilter")) {
45
-
filter = savedFilter ?? defaultFilter;
46
-
setFilter = (filter) =>
47
-
MoonbaseSettingsStore.setExtensionConfig("moonbase", "filter", filter);
48
-
} else {
49
-
const state = React.useState(defaultFilter);
50
-
filter = state[0];
51
-
setFilter = state[1];
52
-
}
53
-
const [selectedTags, setSelectedTags] = React.useState(new Set<string>());
54
-
const sorted = Object.values(extensions).sort((a, b) => {
55
-
const aName = a.manifest.meta?.name ?? a.id;
56
-
const bName = b.manifest.meta?.name ?? b.id;
57
-
return aName.localeCompare(bName);
58
-
});
59
-
60
-
const filtered = sorted.filter(
61
-
(ext) =>
62
-
(ext.manifest.meta?.name?.toLowerCase().includes(query) ||
63
-
ext.manifest.meta?.tagline?.toLowerCase().includes(query) ||
64
-
ext.manifest.meta?.description?.toLowerCase().includes(query)) &&
65
-
[...selectedTags.values()].every(
66
-
(tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag)
67
-
) &&
68
-
// This seems very bad, sorry
69
-
!(
70
-
(!(filter & Filter.Core) &&
71
-
ext.source.type === ExtensionLoadSource.Core) ||
72
-
(!(filter & Filter.Normal) &&
73
-
ext.source.type === ExtensionLoadSource.Normal) ||
74
-
(!(filter & Filter.Developer) &&
75
-
ext.source.type === ExtensionLoadSource.Developer) ||
76
-
(!(filter & Filter.Enabled) &&
77
-
MoonbaseSettingsStore.getExtensionEnabled(ext.id)) ||
78
-
(!(filter & Filter.Disabled) &&
79
-
!MoonbaseSettingsStore.getExtensionEnabled(ext.id)) ||
80
-
(!(filter & Filter.Installed) &&
81
-
ext.state !== ExtensionState.NotDownloaded) ||
82
-
(!(filter & Filter.Repository) &&
83
-
ext.state === ExtensionState.NotDownloaded)
84
-
)
85
-
);
86
-
87
-
return (
88
-
<>
89
-
<SearchBar
90
-
size={SearchBar.Sizes.MEDIUM}
91
-
query={query}
92
-
onChange={(v: string) => setQuery(v.toLowerCase())}
93
-
onClear={() => setQuery("")}
94
-
autoFocus={true}
95
-
autoComplete="off"
96
-
inputProps={{
97
-
autoCapitalize: "none",
98
-
autoCorrect: "off",
99
-
spellCheck: "false"
100
-
}}
101
-
/>
102
-
<React.Suspense
103
-
fallback={<div className={Margins.marginBottom20}></div>}
104
-
>
105
-
<FilterBar
106
-
filter={filter}
107
-
setFilter={setFilter}
108
-
selectedTags={selectedTags}
109
-
setSelectedTags={setSelectedTags}
110
-
/>
111
-
</React.Suspense>
112
-
{filtered.map((ext) => (
113
-
<ExtensionCard id={ext.id} key={ext.id} />
114
-
))}
115
-
</>
116
-
);
117
-
};
118
-
};
-209
packages/core-extensions/src/moonbase/ui/extensions/info.tsx
-209
packages/core-extensions/src/moonbase/ui/extensions/info.tsx
···
1
-
import WebpackRequire from "@moonlight-mod/types/discord/require";
2
-
import { ExtensionTag } from "@moonlight-mod/types";
3
-
import { MoonbaseExtension } from "../../types";
4
-
5
-
type Dependency = {
6
-
id: string;
7
-
type: DependencyType;
8
-
};
9
-
10
-
enum DependencyType {
11
-
Dependency = "dependency",
12
-
Optional = "optional",
13
-
Incompatible = "incompatible"
14
-
}
15
-
16
-
export const tagNames: Record<ExtensionTag, string> = {
17
-
[ExtensionTag.Accessibility]: "Accessibility",
18
-
[ExtensionTag.Appearance]: "Appearance",
19
-
[ExtensionTag.Chat]: "Chat",
20
-
[ExtensionTag.Commands]: "Commands",
21
-
[ExtensionTag.ContextMenu]: "Context Menu",
22
-
[ExtensionTag.DangerZone]: "Danger Zone",
23
-
[ExtensionTag.Development]: "Development",
24
-
[ExtensionTag.Fixes]: "Fixes",
25
-
[ExtensionTag.Fun]: "Fun",
26
-
[ExtensionTag.Markdown]: "Markdown",
27
-
[ExtensionTag.Voice]: "Voice",
28
-
[ExtensionTag.Privacy]: "Privacy",
29
-
[ExtensionTag.Profiles]: "Profiles",
30
-
[ExtensionTag.QualityOfLife]: "Quality of Life",
31
-
[ExtensionTag.Library]: "Library"
32
-
};
33
-
34
-
export default (require: typeof WebpackRequire) => {
35
-
const React = require("common_react");
36
-
const spacepack = require("spacepack_spacepack").spacepack;
37
-
38
-
const CommonComponents = require("common_components");
39
-
const UserInfoClasses = spacepack.findByCode(
40
-
"infoScroller",
41
-
"userInfoSection",
42
-
"userInfoSectionHeader"
43
-
)[0].exports;
44
-
45
-
const { MoonbaseSettingsStore } =
46
-
require("moonbase_stores") as typeof import("../../webpackModules/stores");
47
-
48
-
function InfoSection({
49
-
title,
50
-
children
51
-
}: {
52
-
title: string;
53
-
children: React.ReactNode;
54
-
}) {
55
-
return (
56
-
<div
57
-
style={{
58
-
marginRight: "1em"
59
-
}}
60
-
>
61
-
<CommonComponents.Text
62
-
variant="eyebrow"
63
-
className={UserInfoClasses.userInfoSectionHeader}
64
-
>
65
-
{title}
66
-
</CommonComponents.Text>
67
-
68
-
<CommonComponents.Text variant="text-sm/normal">
69
-
{children}
70
-
</CommonComponents.Text>
71
-
</div>
72
-
);
73
-
}
74
-
75
-
function Badge({
76
-
color,
77
-
children
78
-
}: {
79
-
color: string;
80
-
children: React.ReactNode;
81
-
}) {
82
-
return (
83
-
<span
84
-
style={{
85
-
borderRadius: ".1875rem",
86
-
padding: "0 0.275rem",
87
-
marginRight: "0.4em",
88
-
backgroundColor: color,
89
-
color: "#fff"
90
-
}}
91
-
>
92
-
{children}
93
-
</span>
94
-
);
95
-
}
96
-
97
-
function ExtensionInfo({ ext }: { ext: MoonbaseExtension }) {
98
-
const authors = ext.manifest?.meta?.authors;
99
-
const tags = ext.manifest?.meta?.tags;
100
-
const version = ext.manifest?.version;
101
-
102
-
const dependencies: Dependency[] = [];
103
-
if (ext.manifest.dependencies != null) {
104
-
dependencies.push(
105
-
...ext.manifest.dependencies.map((dep) => ({
106
-
id: dep,
107
-
type: DependencyType.Dependency
108
-
}))
109
-
);
110
-
}
111
-
112
-
if (ext.manifest.suggested != null) {
113
-
dependencies.push(
114
-
...ext.manifest.suggested.map((dep) => ({
115
-
id: dep,
116
-
type: DependencyType.Optional
117
-
}))
118
-
);
119
-
}
120
-
121
-
if (ext.manifest.incompatible != null) {
122
-
dependencies.push(
123
-
...ext.manifest.incompatible.map((dep) => ({
124
-
id: dep,
125
-
type: DependencyType.Incompatible
126
-
}))
127
-
);
128
-
}
129
-
130
-
return (
131
-
<>
132
-
{authors != null && (
133
-
<InfoSection title="Authors">
134
-
{authors.map((author, i) => {
135
-
const comma = i !== authors.length - 1 ? ", " : "";
136
-
if (typeof author === "string") {
137
-
return (
138
-
<span key={i}>
139
-
{author}
140
-
{comma}
141
-
</span>
142
-
);
143
-
} else {
144
-
// TODO: resolve IDs
145
-
return (
146
-
<span key={i}>
147
-
{author.name}
148
-
{comma}
149
-
</span>
150
-
);
151
-
}
152
-
})}
153
-
</InfoSection>
154
-
)}
155
-
156
-
{tags != null && (
157
-
<InfoSection title="Tags">
158
-
{tags.map((tag, i) => {
159
-
const name = tagNames[tag];
160
-
161
-
return (
162
-
<Badge
163
-
key={i}
164
-
color={
165
-
tag === ExtensionTag.DangerZone
166
-
? "var(--red-400)"
167
-
: "var(--brand-500)"
168
-
}
169
-
>
170
-
{name}
171
-
</Badge>
172
-
);
173
-
})}
174
-
</InfoSection>
175
-
)}
176
-
177
-
{dependencies.length > 0 && (
178
-
<InfoSection title="Dependencies">
179
-
{dependencies.map((dep) => {
180
-
const colors = {
181
-
[DependencyType.Dependency]: "var(--brand-500)",
182
-
[DependencyType.Optional]: "var(--orange-400)",
183
-
[DependencyType.Incompatible]: "var(--red-400)"
184
-
};
185
-
const color = colors[dep.type];
186
-
const name = MoonbaseSettingsStore.getExtensionName(dep.id);
187
-
return (
188
-
<Badge color={color} key={dep.id}>
189
-
{name}
190
-
</Badge>
191
-
);
192
-
})}
193
-
</InfoSection>
194
-
)}
195
-
196
-
{version != null && (
197
-
<InfoSection title="Version">
198
-
<span>{version}</span>
199
-
</InfoSection>
200
-
)}
201
-
</>
202
-
);
203
-
}
204
-
205
-
return {
206
-
InfoSection,
207
-
ExtensionInfo
208
-
};
209
-
};
-396
packages/core-extensions/src/moonbase/ui/extensions/settings.tsx
-396
packages/core-extensions/src/moonbase/ui/extensions/settings.tsx
···
1
-
import {
2
-
ExtensionSettingType,
3
-
ExtensionSettingsManifest,
4
-
MultiSelectSettingType,
5
-
NumberSettingType,
6
-
SelectOption,
7
-
SelectSettingType
8
-
} from "@moonlight-mod/types/config";
9
-
import WebpackRequire from "@moonlight-mod/types/discord/require";
10
-
import { CircleXIconSVG, ExtensionState, MoonbaseExtension } from "../../types";
11
-
12
-
type SettingsProps = {
13
-
ext: MoonbaseExtension;
14
-
name: string;
15
-
setting: ExtensionSettingsManifest;
16
-
disabled: boolean;
17
-
};
18
-
19
-
type SettingsComponent = React.ComponentType<SettingsProps>;
20
-
21
-
export default (require: typeof WebpackRequire) => {
22
-
const React = require("common_react");
23
-
const CommonComponents = require("common_components");
24
-
const Flux = require("common_flux");
25
-
const spacepack = require("spacepack_spacepack").spacepack;
26
-
27
-
const { MoonbaseSettingsStore } =
28
-
require("moonbase_stores") as typeof import("../../webpackModules/stores");
29
-
30
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
31
-
32
-
function useConfigEntry<T>(id: string, name: string) {
33
-
return Flux.useStateFromStores(
34
-
[MoonbaseSettingsStore],
35
-
() => {
36
-
return {
37
-
value: MoonbaseSettingsStore.getExtensionConfig<T>(id, name),
38
-
displayName: MoonbaseSettingsStore.getExtensionConfigName(id, name),
39
-
description: MoonbaseSettingsStore.getExtensionConfigDescription(
40
-
id,
41
-
name
42
-
)
43
-
};
44
-
},
45
-
[id, name]
46
-
);
47
-
}
48
-
49
-
function Boolean({ ext, name, setting, disabled }: SettingsProps) {
50
-
const { FormSwitch } = CommonComponents;
51
-
const { value, displayName, description } = useConfigEntry<boolean>(
52
-
ext.id,
53
-
name
54
-
);
55
-
56
-
return (
57
-
<FormSwitch
58
-
value={value ?? false}
59
-
hideBorder={true}
60
-
disabled={disabled}
61
-
onChange={(value: boolean) => {
62
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
63
-
}}
64
-
note={description}
65
-
className={`${Margins.marginReset} ${Margins.marginTop20}`}
66
-
>
67
-
{displayName}
68
-
</FormSwitch>
69
-
);
70
-
}
71
-
72
-
function Number({ ext, name, setting, disabled }: SettingsProps) {
73
-
const { FormItem, FormText, Slider } = CommonComponents;
74
-
const { value, displayName, description } = useConfigEntry<number>(
75
-
ext.id,
76
-
name
77
-
);
78
-
79
-
const castedSetting = setting as NumberSettingType;
80
-
const min = castedSetting.min ?? 0;
81
-
const max = castedSetting.max ?? 100;
82
-
83
-
return (
84
-
<FormItem className={Margins.marginTop20} title={displayName}>
85
-
{description && <FormText>{description}</FormText>}
86
-
<Slider
87
-
initialValue={value ?? 0}
88
-
disabled={disabled}
89
-
minValue={castedSetting.min ?? 0}
90
-
maxValue={castedSetting.max ?? 100}
91
-
onValueChange={(value: number) => {
92
-
const rounded = Math.max(min, Math.min(max, Math.round(value)));
93
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, rounded);
94
-
}}
95
-
/>
96
-
</FormItem>
97
-
);
98
-
}
99
-
100
-
function String({ ext, name, setting, disabled }: SettingsProps) {
101
-
const { FormItem, FormText, TextInput } = CommonComponents;
102
-
const { value, displayName, description } = useConfigEntry<string>(
103
-
ext.id,
104
-
name
105
-
);
106
-
107
-
return (
108
-
<FormItem className={Margins.marginTop20} title={displayName}>
109
-
{description && (
110
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
111
-
)}
112
-
<TextInput
113
-
value={value ?? ""}
114
-
onChange={(value: string) => {
115
-
if (disabled) return;
116
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
117
-
}}
118
-
/>
119
-
</FormItem>
120
-
);
121
-
}
122
-
123
-
function Select({ ext, name, setting, disabled }: SettingsProps) {
124
-
const { FormItem, FormText, SingleSelect } = CommonComponents;
125
-
const { value, displayName, description } = useConfigEntry<string>(
126
-
ext.id,
127
-
name
128
-
);
129
-
130
-
const castedSetting = setting as SelectSettingType;
131
-
const options = castedSetting.options;
132
-
133
-
return (
134
-
<FormItem className={Margins.marginTop20} title={displayName}>
135
-
{description && (
136
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
137
-
)}
138
-
<SingleSelect
139
-
autofocus={false}
140
-
clearable={false}
141
-
value={value ?? ""}
142
-
options={options.map((o: SelectOption) =>
143
-
typeof o === "string" ? { value: o, label: o } : o
144
-
)}
145
-
onChange={(value: string) => {
146
-
if (disabled) return;
147
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
148
-
}}
149
-
/>
150
-
</FormItem>
151
-
);
152
-
}
153
-
154
-
function MultiSelect({ ext, name, setting, disabled }: SettingsProps) {
155
-
const { FormItem, FormText, Select, useVariableSelect, multiSelect } =
156
-
CommonComponents;
157
-
const { value, displayName, description } = useConfigEntry<
158
-
string | string[]
159
-
>(ext.id, name);
160
-
161
-
const castedSetting = setting as MultiSelectSettingType;
162
-
const options = castedSetting.options;
163
-
164
-
return (
165
-
<FormItem className={Margins.marginTop20} title={displayName}>
166
-
{description && (
167
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
168
-
)}
169
-
<Select
170
-
autofocus={false}
171
-
clearable={false}
172
-
closeOnSelect={false}
173
-
options={options.map((o: SelectOption) =>
174
-
typeof o === "string" ? { value: o, label: o } : o
175
-
)}
176
-
{...useVariableSelect({
177
-
onSelectInteraction: multiSelect,
178
-
value: new Set(Array.isArray(value) ? value : [value]),
179
-
onChange: (value: string) => {
180
-
if (disabled) return;
181
-
MoonbaseSettingsStore.setExtensionConfig(
182
-
ext.id,
183
-
name,
184
-
Array.from(value)
185
-
);
186
-
}
187
-
})}
188
-
/>
189
-
</FormItem>
190
-
);
191
-
}
192
-
193
-
const RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0]
194
-
.exports;
195
-
const CircleXIcon = spacepack.findByCode(CircleXIconSVG)[0].exports.default;
196
-
function RemoveEntryButton({
197
-
onClick,
198
-
disabled
199
-
}: {
200
-
onClick: () => void;
201
-
disabled: boolean;
202
-
}) {
203
-
const { Tooltip, Clickable } = CommonComponents;
204
-
return (
205
-
<div className={RemoveButtonClasses.removeButtonContainer}>
206
-
<Tooltip text="Remove entry" position="top">
207
-
{(props: any) => (
208
-
<Clickable
209
-
{...props}
210
-
className={RemoveButtonClasses.removeButton}
211
-
onClick={onClick}
212
-
>
213
-
<CircleXIcon width={16} height={16} />
214
-
</Clickable>
215
-
)}
216
-
</Tooltip>
217
-
</div>
218
-
);
219
-
}
220
-
221
-
function List({ ext, name, setting, disabled }: SettingsProps) {
222
-
const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents;
223
-
const { value, displayName, description } = useConfigEntry<string[]>(
224
-
ext.id,
225
-
name
226
-
);
227
-
228
-
const entries = value ?? [];
229
-
const updateConfig = () =>
230
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, entries);
231
-
232
-
return (
233
-
<FormItem className={Margins.marginTop20} title={displayName}>
234
-
{description && (
235
-
<FormText className={Margins.marginBottom4}>{description}</FormText>
236
-
)}
237
-
<Flex direction={Flex.Direction.VERTICAL}>
238
-
{entries.map((val, i) => (
239
-
// FIXME: stylesheets
240
-
<div
241
-
key={i}
242
-
style={{
243
-
display: "grid",
244
-
height: "32px",
245
-
gap: "8px",
246
-
gridTemplateColumns: "1fr 32px",
247
-
alignItems: "center"
248
-
}}
249
-
>
250
-
<TextInput
251
-
size={TextInput.Sizes.MINI}
252
-
value={val}
253
-
disabled={disabled}
254
-
onChange={(newVal: string) => {
255
-
entries[i] = newVal;
256
-
updateConfig();
257
-
}}
258
-
/>
259
-
<RemoveEntryButton
260
-
disabled={disabled}
261
-
onClick={() => {
262
-
entries.splice(i, 1);
263
-
updateConfig();
264
-
}}
265
-
/>
266
-
</div>
267
-
))}
268
-
269
-
<Button
270
-
look={Button.Looks.FILLED}
271
-
color={Button.Colors.GREEN}
272
-
size={Button.Sizes.SMALL}
273
-
disabled={disabled}
274
-
className={Margins.marginTop8}
275
-
onClick={() => {
276
-
entries.push("");
277
-
updateConfig();
278
-
}}
279
-
>
280
-
Add new entry
281
-
</Button>
282
-
</Flex>
283
-
</FormItem>
284
-
);
285
-
}
286
-
287
-
function Dictionary({ ext, name, setting, disabled }: SettingsProps) {
288
-
const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents;
289
-
const { value, displayName, description } = useConfigEntry<
290
-
Record<string, string>
291
-
>(ext.id, name);
292
-
293
-
const entries = Object.entries(value ?? {});
294
-
const updateConfig = () =>
295
-
MoonbaseSettingsStore.setExtensionConfig(
296
-
ext.id,
297
-
name,
298
-
Object.fromEntries(entries)
299
-
);
300
-
301
-
return (
302
-
<FormItem className={Margins.marginTop20} title={displayName}>
303
-
{description && (
304
-
<FormText className={Margins.marginBottom4}>{description}</FormText>
305
-
)}
306
-
<Flex direction={Flex.Direction.VERTICAL}>
307
-
{entries.map(([key, val], i) => (
308
-
// FIXME: stylesheets
309
-
<div
310
-
key={i}
311
-
style={{
312
-
display: "grid",
313
-
height: "32px",
314
-
gap: "8px",
315
-
gridTemplateColumns: "1fr 1fr 32px",
316
-
alignItems: "center"
317
-
}}
318
-
>
319
-
<TextInput
320
-
size={TextInput.Sizes.MINI}
321
-
value={key}
322
-
disabled={disabled}
323
-
onChange={(newKey: string) => {
324
-
entries[i][0] = newKey;
325
-
updateConfig();
326
-
}}
327
-
/>
328
-
<TextInput
329
-
size={TextInput.Sizes.MINI}
330
-
value={val}
331
-
disabled={disabled}
332
-
onChange={(newValue: string) => {
333
-
entries[i][1] = newValue;
334
-
updateConfig();
335
-
}}
336
-
/>
337
-
<RemoveEntryButton
338
-
disabled={disabled}
339
-
onClick={() => {
340
-
entries.splice(i, 1);
341
-
updateConfig();
342
-
}}
343
-
/>
344
-
</div>
345
-
))}
346
-
347
-
<Button
348
-
look={Button.Looks.FILLED}
349
-
color={Button.Colors.GREEN}
350
-
size={Button.Sizes.SMALL}
351
-
className={Margins.marginTop8}
352
-
disabled={disabled}
353
-
onClick={() => {
354
-
entries.push([`entry-${entries.length}`, ""]);
355
-
updateConfig();
356
-
}}
357
-
>
358
-
Add new entry
359
-
</Button>
360
-
</Flex>
361
-
</FormItem>
362
-
);
363
-
}
364
-
365
-
function Setting({ ext, name, setting, disabled }: SettingsProps) {
366
-
const elements: Partial<Record<ExtensionSettingType, SettingsComponent>> = {
367
-
[ExtensionSettingType.Boolean]: Boolean,
368
-
[ExtensionSettingType.Number]: Number,
369
-
[ExtensionSettingType.String]: String,
370
-
[ExtensionSettingType.Select]: Select,
371
-
[ExtensionSettingType.MultiSelect]: MultiSelect,
372
-
[ExtensionSettingType.List]: List,
373
-
[ExtensionSettingType.Dictionary]: Dictionary
374
-
};
375
-
const element = elements[setting.type];
376
-
if (element == null) return <></>;
377
-
return React.createElement(element, { ext, name, setting, disabled });
378
-
}
379
-
380
-
return function Settings({ ext }: { ext: MoonbaseExtension }) {
381
-
const { Flex } = CommonComponents;
382
-
return (
383
-
<Flex className="moonbase-settings" direction={Flex.Direction.VERTICAL}>
384
-
{Object.entries(ext.manifest.settings!).map(([name, setting]) => (
385
-
<Setting
386
-
ext={ext}
387
-
key={name}
388
-
name={name}
389
-
setting={setting}
390
-
disabled={ext.state === ExtensionState.NotDownloaded}
391
-
/>
392
-
))}
393
-
</Flex>
394
-
);
395
-
};
396
-
};
-54
packages/core-extensions/src/moonbase/ui/index.tsx
-54
packages/core-extensions/src/moonbase/ui/index.tsx
···
1
-
import { WebpackRequireType } from "@moonlight-mod/types";
2
-
import { pageModules } from "..";
3
-
4
-
export default (require: WebpackRequireType) => {
5
-
const React = require("common_react");
6
-
const spacepack = require("spacepack_spacepack").spacepack;
7
-
8
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
9
-
10
-
const { Divider } = spacepack.findByCode(".default.HEADER_BAR")[0].exports
11
-
.default;
12
-
const TitleBarClasses = spacepack.findByCode("iconWrapper:", "children:")[0]
13
-
.exports;
14
-
const TabBarClasses = spacepack.findByCode("nowPlayingColumn:")[0].exports;
15
-
16
-
const pages = pageModules(require);
17
-
18
-
return function Moonbase() {
19
-
const { Text, TabBar } = require("common_components");
20
-
21
-
const [selectedTab, setSelectedTab] = React.useState(Object.keys(pages)[0]);
22
-
23
-
return (
24
-
<>
25
-
<div
26
-
className={`${TitleBarClasses.children} ${Margins.marginBottom20}`}
27
-
>
28
-
<Text
29
-
className={TitleBarClasses.titleWrapper}
30
-
variant="heading-lg/semibold"
31
-
tag="h2"
32
-
>
33
-
Moonbase
34
-
</Text>
35
-
<Divider />
36
-
<TabBar
37
-
selectedItem={selectedTab}
38
-
onItemSelect={setSelectedTab}
39
-
type="top-pill"
40
-
className={TabBarClasses.tabBar}
41
-
>
42
-
{Object.entries(pages).map(([id, page]) => (
43
-
<TabBar.Item key={id} id={id} className={TabBarClasses.item}>
44
-
{page.name}
45
-
</TabBar.Item>
46
-
))}
47
-
</TabBar>
48
-
</div>
49
-
50
-
{React.createElement(pages[selectedTab].element)}
51
-
</>
52
-
);
53
-
};
54
-
};
+36
packages/core-extensions/src/moonbase/webpackModules/ThemeDarkIcon.tsx
+36
packages/core-extensions/src/moonbase/webpackModules/ThemeDarkIcon.tsx
···
1
+
// RIP to ThemeDarkIcon ????-2025
2
+
// <Cynthia> Failed to remap "ThemeDarkIcon" in "discord/components/common/index"
3
+
// <NotNite> bro are you fucking kidding me
4
+
// <NotNite> that's literally the icon we use for the update banner
5
+
6
+
import React from "@moonlight-mod/wp/react";
7
+
import icons from "@moonlight-mod/wp/common_icons";
8
+
import type { IconProps } from "@moonlight-mod/types/coreExtensions/common";
9
+
10
+
export default function ThemeDarkIcon(props?: IconProps) {
11
+
const parsed = icons.parseProps(props);
12
+
13
+
return (
14
+
<svg
15
+
aria-hidden="true"
16
+
role="img"
17
+
xmlns="http://www.w3.org/2000/svg"
18
+
width={parsed.width}
19
+
height={parsed.height}
20
+
fill="none"
21
+
viewBox="0 0 24 24"
22
+
>
23
+
<path
24
+
fill={parsed.fill}
25
+
className={parsed.className}
26
+
d="M20.52 18.96c.32-.4-.01-.96-.52-.96A11 11 0 0 1 9.77 2.94c.31-.78-.3-1.68-1.1-1.43a11 11 0 1 0 11.85 17.45Z"
27
+
/>
28
+
29
+
<path
30
+
fill={parsed.fill}
31
+
className={parsed.className}
32
+
d="m17.73 9.27-.76-2.02a.5.5 0 0 0-.94 0l-.76 2.02-2.02.76a.5.5 0 0 0 0 .94l2.02.76.76 2.02a.5.5 0 0 0 .94 0l.76-2.02 2.02-.76a.5.5 0 0 0 0-.94l-2.02-.76ZM19.73 2.62l.45 1.2 1.2.45c.21.08.21.38 0 .46l-1.2.45-.45 1.2a.25.25 0 0 1-.46 0l-.45-1.2-1.2-.45a.25.25 0 0 1 0-.46l1.2-.45.45-1.2a.25.25 0 0 1 .46 0Z"
33
+
/>
34
+
</svg>
35
+
);
36
+
}
+263
packages/core-extensions/src/moonbase/webpackModules/crashScreen.tsx
+263
packages/core-extensions/src/moonbase/webpackModules/crashScreen.tsx
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import { Button, TabBar } from "@moonlight-mod/wp/discord/components/common/index";
3
+
import { useStateFromStores, useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
4
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
5
+
import { RepositoryManifest, UpdateState } from "../types";
6
+
import { ConfigExtension, DetectedExtension } from "@moonlight-mod/types";
7
+
import DiscoveryClasses from "@moonlight-mod/wp/discord/modules/discovery/web/Discovery.css";
8
+
9
+
const MODULE_REGEX = /Webpack-Module\/(\d+)\/(\d+)/g;
10
+
11
+
const logger = moonlight.getLogger("moonbase/crashScreen");
12
+
13
+
type ErrorState = {
14
+
error: Error;
15
+
info: {
16
+
componentStack: string;
17
+
};
18
+
__moonlight_update?: UpdateState;
19
+
};
20
+
21
+
type WrapperProps = {
22
+
action: React.ReactNode;
23
+
state: ErrorState;
24
+
};
25
+
26
+
type UpdateCardProps = {
27
+
id: number;
28
+
ext: {
29
+
version: string;
30
+
download: string;
31
+
updateManifest: RepositoryManifest;
32
+
};
33
+
};
34
+
35
+
const updateStrings: Record<UpdateState, string> = {
36
+
[UpdateState.Ready]: "A new version of moonlight is available.",
37
+
[UpdateState.Working]: "Updating moonlight...",
38
+
[UpdateState.Installed]: "Updated moonlight. Click Reload to apply changes.",
39
+
[UpdateState.Failed]: "Failed to update moonlight. Please use the installer."
40
+
};
41
+
const buttonStrings: Record<UpdateState, string> = {
42
+
[UpdateState.Ready]: "Update moonlight",
43
+
[UpdateState.Working]: "Updating moonlight...",
44
+
[UpdateState.Installed]: "",
45
+
[UpdateState.Failed]: "Update failed"
46
+
};
47
+
const extensionButtonStrings: Record<UpdateState, string> = {
48
+
[UpdateState.Ready]: "Update",
49
+
[UpdateState.Working]: "Updating...",
50
+
[UpdateState.Installed]: "Updated",
51
+
[UpdateState.Failed]: "Update failed"
52
+
};
53
+
54
+
function ExtensionUpdateCard({ id, ext }: UpdateCardProps) {
55
+
const [state, setState] = React.useState(UpdateState.Ready);
56
+
const installed = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.getExtension(id), [id]);
57
+
58
+
return (
59
+
<div className="moonbase-crash-extensionCard">
60
+
<div className="moonbase-crash-extensionCard-meta">
61
+
<div className="moonbase-crash-extensionCard-title">
62
+
{ext.updateManifest.meta?.name ?? ext.updateManifest.id}
63
+
</div>
64
+
<div className="moonbase-crash-extensionCard-version">{`v${installed?.manifest?.version ?? "???"} -> v${
65
+
ext.version
66
+
}`}</div>
67
+
</div>
68
+
<div className="moonbase-crash-extensionCard-button">
69
+
<Button
70
+
color={Button.Colors.GREEN}
71
+
disabled={state !== UpdateState.Ready}
72
+
onClick={() => {
73
+
setState(UpdateState.Working);
74
+
MoonbaseSettingsStore.installExtension(id)
75
+
.then(() => setState(UpdateState.Installed))
76
+
.catch(() => setState(UpdateState.Failed));
77
+
}}
78
+
>
79
+
{extensionButtonStrings[state]}
80
+
</Button>
81
+
</div>
82
+
</div>
83
+
);
84
+
}
85
+
86
+
function ExtensionDisableCard({ ext }: { ext: DetectedExtension }) {
87
+
async function disableWithDependents() {
88
+
const disable = new Set<string>();
89
+
disable.add(ext.id);
90
+
for (const [id, dependencies] of moonlightNode.processedExtensions.dependencyGraph) {
91
+
if (dependencies?.has(ext.id)) disable.add(id);
92
+
}
93
+
94
+
const config = structuredClone(moonlightNode.config);
95
+
for (const id in config.extensions) {
96
+
if (!disable.has(id)) continue;
97
+
if (typeof config.extensions[id] === "boolean") config.extensions[id] = false;
98
+
else (config.extensions[id] as ConfigExtension).enabled = false;
99
+
}
100
+
101
+
let msg = `Are you sure you want to disable "${ext.manifest.meta?.name ?? ext.id}"`;
102
+
if (disable.size > 1) {
103
+
msg += ` and its ${disable.size - 1} dependent${disable.size - 1 === 1 ? "" : "s"}`;
104
+
}
105
+
msg += "?";
106
+
107
+
if (confirm(msg)) {
108
+
await moonlightNode.writeConfig(config);
109
+
window.location.reload();
110
+
}
111
+
}
112
+
113
+
return (
114
+
<div className="moonbase-crash-extensionCard">
115
+
<div className="moonbase-crash-extensionCard-meta">
116
+
<div className="moonbase-crash-extensionCard-title">{ext.manifest.meta?.name ?? ext.id}</div>
117
+
<div className="moonbase-crash-extensionCard-version">{`v${ext.manifest.version ?? "???"}`}</div>
118
+
</div>
119
+
<div className="moonbase-crash-extensionCard-button">
120
+
<Button color={Button.Colors.RED} onClick={disableWithDependents}>
121
+
Disable
122
+
</Button>
123
+
</div>
124
+
</div>
125
+
);
126
+
}
127
+
128
+
export function wrapAction({ action, state }: WrapperProps) {
129
+
const [tab, setTab] = React.useState("crash");
130
+
131
+
const { updates, updateCount } = useStateFromStoresObject([MoonbaseSettingsStore], () => {
132
+
const { updates } = MoonbaseSettingsStore;
133
+
return {
134
+
updates: Object.entries(updates),
135
+
updateCount: Object.keys(updates).length
136
+
};
137
+
});
138
+
139
+
const causes = React.useMemo(() => {
140
+
const causes = new Set<string>();
141
+
if (state.error.stack) {
142
+
for (const [, , id] of state.error.stack.matchAll(MODULE_REGEX))
143
+
for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
144
+
}
145
+
for (const [, , id] of state.info.componentStack.matchAll(MODULE_REGEX))
146
+
for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
147
+
148
+
for (const [path, id] of Object.entries(moonlight.moonmap.modules)) {
149
+
const MAPPING_REGEX = new RegExp(
150
+
// @ts-expect-error Only Firefox has RegExp.escape
151
+
`(${RegExp.escape ? RegExp.escape(path) : path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`,
152
+
"g"
153
+
);
154
+
155
+
if (state.error.stack) {
156
+
for (const match of state.error.stack.matchAll(MAPPING_REGEX))
157
+
if (match) for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
158
+
}
159
+
for (const match of state.info.componentStack.matchAll(MAPPING_REGEX))
160
+
if (match) for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
161
+
}
162
+
163
+
return [...causes];
164
+
}, []);
165
+
166
+
return (
167
+
<div className="moonbase-crash-wrapper">
168
+
{action}
169
+
<TabBar
170
+
className={`${DiscoveryClasses.tabBar} moonbase-crash-tabs`}
171
+
type="top"
172
+
selectedItem={tab}
173
+
onItemSelect={(v) => setTab(v)}
174
+
>
175
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id="crash">
176
+
Crash details
177
+
</TabBar.Item>
178
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id="extensions" disabled={updateCount === 0}>
179
+
{`Extension updates (${updateCount})`}
180
+
</TabBar.Item>
181
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id="causes" disabled={causes.length === 0}>
182
+
{`Possible causes (${causes.length})`}
183
+
</TabBar.Item>
184
+
</TabBar>
185
+
{tab === "crash" ? (
186
+
<div className="moonbase-crash-details-wrapper">
187
+
<pre className="moonbase-crash-details">
188
+
<code>
189
+
{state.error.stack}
190
+
{"\n\nComponent stack:"}
191
+
{state.info.componentStack}
192
+
</code>
193
+
</pre>
194
+
</div>
195
+
) : null}
196
+
{tab === "extensions" ? (
197
+
<div className="moonbase-crash-extensions">
198
+
{updates.map(([id, ext]) => (
199
+
<ExtensionUpdateCard id={Number(id)} ext={ext} />
200
+
))}
201
+
</div>
202
+
) : null}
203
+
{tab === "causes" ? (
204
+
<div className="moonbase-crash-extensions">
205
+
{causes
206
+
.map((ext) => moonlightNode.extensions.find((e) => e.id === ext)!)
207
+
.map((ext) => (
208
+
<ExtensionDisableCard ext={ext} />
209
+
))}
210
+
</div>
211
+
) : null}
212
+
</div>
213
+
);
214
+
}
215
+
216
+
export function UpdateText({ state, setState }: { state: ErrorState; setState: (state: ErrorState) => void }) {
217
+
if (!state.__moonlight_update) {
218
+
setState({
219
+
...state,
220
+
__moonlight_update: UpdateState.Ready
221
+
});
222
+
}
223
+
const newVersion = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.newVersion);
224
+
225
+
return newVersion == null ? null : (
226
+
<p>{state.__moonlight_update !== undefined ? updateStrings[state.__moonlight_update] : ""}</p>
227
+
);
228
+
}
229
+
230
+
export function UpdateButton({ state, setState }: { state: ErrorState; setState: (state: ErrorState) => void }) {
231
+
const newVersion = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.newVersion);
232
+
return newVersion == null ||
233
+
state.__moonlight_update === UpdateState.Installed ||
234
+
state.__moonlight_update === undefined ? null : (
235
+
<Button
236
+
size={Button.Sizes.LARGE}
237
+
disabled={state.__moonlight_update !== UpdateState.Ready}
238
+
onClick={() => {
239
+
setState({
240
+
...state,
241
+
__moonlight_update: UpdateState.Working
242
+
});
243
+
244
+
MoonbaseSettingsStore.updateMoonlight()
245
+
.then(() => {
246
+
setState({
247
+
...state,
248
+
__moonlight_update: UpdateState.Installed
249
+
});
250
+
})
251
+
.catch((e) => {
252
+
logger.error(e);
253
+
setState({
254
+
...state,
255
+
__moonlight_update: UpdateState.Failed
256
+
});
257
+
});
258
+
}}
259
+
>
260
+
{state.__moonlight_update !== undefined ? buttonStrings[state.__moonlight_update] : ""}
261
+
</Button>
262
+
);
263
+
}
+10
packages/core-extensions/src/moonbase/webpackModules/moonbase.ts
+10
packages/core-extensions/src/moonbase/webpackModules/moonbase.ts
···
1
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
2
+
import type { Moonbase } from "@moonlight-mod/types/coreExtensions/moonbase";
3
+
4
+
export const moonbase: Moonbase = {
5
+
registerConfigComponent(ext, option, component) {
6
+
MoonbaseSettingsStore.registerConfigComponent(ext, option, component);
7
+
}
8
+
};
9
+
10
+
export default moonbase;
+100
packages/core-extensions/src/moonbase/webpackModules/settings.tsx
+100
packages/core-extensions/src/moonbase/webpackModules/settings.tsx
···
1
+
import settings from "@moonlight-mod/wp/settings_settings";
2
+
import React from "@moonlight-mod/wp/react";
3
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
4
+
import { Moonbase, pages, RestartAdviceMessage, Update } from "@moonlight-mod/wp/moonbase_ui";
5
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
6
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
7
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
8
+
import { Text, Breadcrumbs } from "@moonlight-mod/wp/discord/components/common/index";
9
+
import { MenuItem } from "@moonlight-mod/wp/contextMenu_contextMenu";
10
+
11
+
const notice = {
12
+
stores: [MoonbaseSettingsStore],
13
+
element: () => {
14
+
// Require it here because lazy loading SUX
15
+
const SettingsNotice = spacepack.require("discord/components/common/SettingsNotice").default;
16
+
return (
17
+
<SettingsNotice
18
+
submitting={MoonbaseSettingsStore.submitting}
19
+
onReset={() => {
20
+
MoonbaseSettingsStore.reset();
21
+
}}
22
+
onSave={async () => {
23
+
await MoonbaseSettingsStore.writeConfig();
24
+
}}
25
+
/>
26
+
);
27
+
}
28
+
};
29
+
30
+
const oldLocation = MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "oldLocation", false);
31
+
const position = oldLocation ? -2 : -9999;
32
+
33
+
function addSection(id: string, name: string, element: React.FunctionComponent) {
34
+
settings.addSection(`moonbase-${id}`, name, element, null, position, notice);
35
+
}
36
+
37
+
// FIXME: move to component types
38
+
type Breadcrumb = {
39
+
id: string;
40
+
label: string;
41
+
};
42
+
43
+
function renderBreadcrumb(crumb: Breadcrumb, last: boolean) {
44
+
return (
45
+
<Text variant="heading-lg/semibold" tag="h2" color={last ? "header-primary" : "header-secondary"}>
46
+
{crumb.label}
47
+
</Text>
48
+
);
49
+
}
50
+
51
+
if (!oldLocation) {
52
+
settings.addDivider(position);
53
+
}
54
+
55
+
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
56
+
if (oldLocation) settings.addHeader("Moonbase", position);
57
+
58
+
const _pages = oldLocation ? pages : pages.reverse();
59
+
for (const page of _pages) {
60
+
addSection(page.id, page.name, () => {
61
+
const breadcrumbs = [
62
+
{ id: "moonbase", label: "Moonbase" },
63
+
{ id: page.id, label: page.name }
64
+
];
65
+
return (
66
+
<>
67
+
<Breadcrumbs
68
+
className={Margins.marginBottom20}
69
+
renderCustomBreadcrumb={renderBreadcrumb}
70
+
breadcrumbs={breadcrumbs}
71
+
activeId={page.id}
72
+
>
73
+
{page.name}
74
+
</Breadcrumbs>
75
+
76
+
<RestartAdviceMessage />
77
+
<Update />
78
+
79
+
<page.element />
80
+
</>
81
+
);
82
+
});
83
+
}
84
+
85
+
if (!oldLocation) settings.addHeader("Moonbase", position);
86
+
} else {
87
+
settings.addSection("moonbase", "Moonbase", Moonbase, null, position, notice);
88
+
89
+
settings.addSectionMenuItems(
90
+
"moonbase",
91
+
...pages.map((page, i) => (
92
+
<MenuItem
93
+
key={page.id}
94
+
id={`moonbase-${page.id}`}
95
+
label={page.name}
96
+
action={() => UserSettingsModalActionCreators.open("moonbase", i.toString())}
97
+
/>
98
+
))
99
+
);
100
+
}
+405
-122
packages/core-extensions/src/moonbase/webpackModules/stores.ts
+405
-122
packages/core-extensions/src/moonbase/webpackModules/stores.ts
···
1
-
import { Config, ExtensionLoadSource } from "@moonlight-mod/types";
1
+
import { Config, ExtensionEnvironment, ExtensionLoadSource, ExtensionSettingsAdvice } from "@moonlight-mod/types";
2
2
import {
3
3
ExtensionState,
4
4
MoonbaseExtension,
5
5
MoonbaseNatives,
6
-
RepositoryManifest
6
+
RepositoryManifest,
7
+
RestartAdvice,
8
+
UpdateState
7
9
} from "../types";
8
-
import Flux from "@moonlight-mod/wp/common_flux";
9
-
import Dispatcher from "@moonlight-mod/wp/common_fluxDispatcher";
10
+
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
11
+
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
12
+
import getNatives from "../native";
13
+
import { mainRepo } from "@moonlight-mod/types/constants";
14
+
import { checkExtensionCompat, ExtensionCompat } from "@moonlight-mod/core/extension/loader";
15
+
import { CustomComponent } from "@moonlight-mod/types/coreExtensions/moonbase";
16
+
import { NodeEventType } from "@moonlight-mod/types/core/event";
17
+
import { getConfigOption, setConfigOption } from "@moonlight-mod/core/util/config";
18
+
import diff from "microdiff";
10
19
11
-
const natives: MoonbaseNatives = moonlight.getNatives("moonbase");
12
20
const logger = moonlight.getLogger("moonbase");
13
21
14
-
class MoonbaseSettingsStore extends Flux.Store<any> {
15
-
private origConfig: Config;
22
+
let natives: MoonbaseNatives = moonlight.getNatives("moonbase");
23
+
if (moonlightNode.isBrowser) natives = getNatives();
24
+
25
+
class MoonbaseSettingsStore extends Store<any> {
26
+
private initialConfig: Config;
27
+
private savedConfig: Config;
16
28
private config: Config;
29
+
private extensionIndex: number;
30
+
private configComponents: Record<string, Record<string, CustomComponent>> = {};
17
31
18
32
modified: boolean;
19
33
submitting: boolean;
20
34
installing: boolean;
21
35
22
-
extensions: { [id: string]: MoonbaseExtension };
23
-
updates: { [id: string]: { version: string; download: string } };
36
+
#updateState = UpdateState.Ready;
37
+
get updateState() {
38
+
return this.#updateState;
39
+
}
40
+
newVersion: string | null;
41
+
shouldShowNotice: boolean;
42
+
43
+
restartAdvice = RestartAdvice.NotNeeded;
44
+
45
+
extensions: { [id: number]: MoonbaseExtension };
46
+
updates: {
47
+
[id: number]: {
48
+
version: string;
49
+
download: string;
50
+
updateManifest: RepositoryManifest;
51
+
};
52
+
};
24
53
25
54
constructor() {
26
55
super(Dispatcher);
27
56
28
-
this.origConfig = moonlightNode.config;
29
-
this.config = this.clone(this.origConfig);
57
+
this.initialConfig = moonlightNode.config;
58
+
this.savedConfig = moonlightNode.config;
59
+
this.config = this.clone(this.savedConfig);
60
+
this.extensionIndex = 0;
30
61
31
62
this.modified = false;
32
63
this.submitting = false;
33
64
this.installing = false;
34
65
66
+
this.newVersion = null;
67
+
this.shouldShowNotice = false;
68
+
35
69
this.extensions = {};
36
70
this.updates = {};
37
71
for (const ext of moonlightNode.extensions) {
38
-
const existingExtension = this.extensions[ext.id];
39
-
if (existingExtension != null) continue;
40
-
41
-
this.extensions[ext.id] = {
72
+
const uniqueId = this.extensionIndex++;
73
+
this.extensions[uniqueId] = {
42
74
...ext,
43
-
state: moonlight.enabledExtensions.has(ext.id)
44
-
? ExtensionState.Enabled
45
-
: ExtensionState.Disabled
75
+
uniqueId,
76
+
state: moonlight.enabledExtensions.has(ext.id) ? ExtensionState.Enabled : ExtensionState.Disabled,
77
+
compat: checkExtensionCompat(ext.manifest),
78
+
hasUpdate: false
46
79
};
47
80
}
48
81
49
-
natives.fetchRepositories(this.config.repositories).then((ret) => {
50
-
for (const [repo, exts] of Object.entries(ret)) {
51
-
try {
52
-
for (const ext of exts) {
53
-
try {
54
-
const existingExtension = this.extensions[ext.id];
55
-
if (existingExtension !== undefined) {
56
-
if (this.hasUpdate(repo, ext, existingExtension)) {
57
-
this.updates[ext.id] = {
58
-
version: ext.version!,
59
-
download: ext.download
60
-
};
61
-
}
82
+
// This is async but we're calling it without
83
+
this.checkUpdates();
62
84
63
-
this.extensions[ext.id].manifest = ext;
64
-
this.extensions[ext.id].source = {
65
-
type: ExtensionLoadSource.Normal,
66
-
url: repo
67
-
};
85
+
// Update our state if another extension edited the config programatically
86
+
moonlightNode.events.addEventListener(NodeEventType.ConfigSaved, (config) => {
87
+
if (!this.submitting) {
88
+
this.config = this.clone(config);
89
+
// NOTE: This is also async but we're calling it without
90
+
this.processConfigChanged();
91
+
}
92
+
});
93
+
}
68
94
69
-
continue;
70
-
}
95
+
async checkUpdates() {
96
+
await Promise.all([this.checkExtensionUpdates(), this.checkMoonlightUpdates()]);
97
+
this.shouldShowNotice = this.newVersion != null || Object.keys(this.updates).length > 0;
98
+
this.emitChange();
99
+
}
100
+
101
+
private async checkExtensionUpdates() {
102
+
const repositories = await natives!.fetchRepositories(this.savedConfig.repositories);
103
+
104
+
// Reset update state
105
+
for (const id in this.extensions) {
106
+
const ext = this.extensions[id];
107
+
ext.hasUpdate = false;
108
+
ext.changelog = undefined;
109
+
}
110
+
this.updates = {};
71
111
72
-
this.extensions[ext.id] = {
73
-
id: ext.id,
74
-
manifest: ext,
75
-
source: { type: ExtensionLoadSource.Normal, url: repo },
76
-
state: ExtensionState.NotDownloaded
77
-
};
78
-
} catch (e) {
79
-
logger.error(`Error processing extension ${ext.id}`, e);
80
-
}
112
+
for (const [repo, exts] of Object.entries(repositories)) {
113
+
for (const ext of exts) {
114
+
const uniqueId = this.extensionIndex++;
115
+
const extensionData = {
116
+
id: ext.id,
117
+
uniqueId,
118
+
manifest: ext,
119
+
source: { type: ExtensionLoadSource.Normal, url: repo },
120
+
state: ExtensionState.NotDownloaded,
121
+
compat: ExtensionCompat.Compatible,
122
+
hasUpdate: false
123
+
};
124
+
125
+
// Don't present incompatible updates
126
+
if (checkExtensionCompat(ext) !== ExtensionCompat.Compatible) continue;
127
+
128
+
const existing = this.getExisting(extensionData);
129
+
if (existing != null) {
130
+
// Make sure the download URL is properly updated
131
+
existing.manifest = {
132
+
...existing.manifest,
133
+
download: ext.download
134
+
};
135
+
136
+
if (this.hasUpdate(extensionData)) {
137
+
this.updates[existing.uniqueId] = {
138
+
version: ext.version!,
139
+
download: ext.download,
140
+
updateManifest: ext
141
+
};
142
+
existing.hasUpdate = true;
143
+
existing.changelog = ext.meta?.changelog;
81
144
}
82
-
} catch (e) {
83
-
logger.error(`Error processing repository ${repo}`, e);
145
+
} else {
146
+
this.extensions[uniqueId] = extensionData;
84
147
}
85
148
}
149
+
}
150
+
}
86
151
87
-
this.emitChange();
88
-
});
152
+
private async checkMoonlightUpdates() {
153
+
this.newVersion = this.getExtensionConfigRaw("moonbase", "updateChecking", true)
154
+
? await natives!.checkForMoonlightUpdate()
155
+
: null;
89
156
}
90
157
91
-
// this logic sucks so bad lol
92
-
private hasUpdate(
93
-
repo: string,
94
-
repoExt: RepositoryManifest,
95
-
existing: MoonbaseExtension
96
-
) {
97
-
return (
98
-
existing.source.type === ExtensionLoadSource.Normal &&
99
-
existing.source.url != null &&
100
-
existing.source.url === repo &&
101
-
repoExt.version != null &&
102
-
existing.manifest.version !== repoExt.version
103
-
);
158
+
private getExisting(ext: MoonbaseExtension) {
159
+
return Object.values(this.extensions).find((e) => e.id === ext.id && e.source.url === ext.source.url);
160
+
}
161
+
162
+
private hasUpdate(ext: MoonbaseExtension) {
163
+
const existing = Object.values(this.extensions).find((e) => e.id === ext.id && e.source.url === ext.source.url);
164
+
if (existing == null) return false;
165
+
166
+
return existing.manifest.version !== ext.manifest.version && existing.state !== ExtensionState.NotDownloaded;
104
167
}
105
168
106
169
// Jank
107
170
private isModified() {
108
-
const orig = JSON.stringify(this.origConfig);
171
+
const orig = JSON.stringify(this.savedConfig);
109
172
const curr = JSON.stringify(this.config);
110
173
return orig !== curr;
111
174
}
···
114
177
return this.submitting || this.installing;
115
178
}
116
179
180
+
// Required for the settings store contract
117
181
showNotice() {
118
182
return this.modified;
119
183
}
120
184
121
-
getExtension(id: string) {
122
-
return this.extensions[id];
185
+
getExtension(uniqueId: number) {
186
+
return this.extensions[uniqueId];
123
187
}
124
188
125
-
getExtensionName(id: string) {
126
-
return Object.prototype.hasOwnProperty.call(this.extensions, id)
127
-
? this.extensions[id].manifest.meta?.name ?? id
128
-
: id;
189
+
getExtensionUniqueId(id: string) {
190
+
return Object.values(this.extensions).find((ext) => ext.id === id)?.uniqueId;
191
+
}
192
+
193
+
getExtensionConflicting(uniqueId: number) {
194
+
const ext = this.getExtension(uniqueId);
195
+
if (ext.state !== ExtensionState.NotDownloaded) return false;
196
+
return Object.values(this.extensions).some(
197
+
(e) => e.id === ext.id && e.uniqueId !== uniqueId && e.state !== ExtensionState.NotDownloaded
198
+
);
199
+
}
200
+
201
+
getExtensionName(uniqueId: number) {
202
+
const ext = this.getExtension(uniqueId);
203
+
return ext.manifest.meta?.name ?? ext.id;
129
204
}
130
205
131
-
getExtensionUpdate(id: string) {
132
-
return Object.prototype.hasOwnProperty.call(this.updates, id)
133
-
? this.updates[id]
134
-
: null;
206
+
getExtensionUpdate(uniqueId: number) {
207
+
return this.updates[uniqueId]?.version;
135
208
}
136
209
137
-
getExtensionEnabled(id: string) {
138
-
const val = this.config.extensions[id];
210
+
getExtensionEnabled(uniqueId: number) {
211
+
const ext = this.getExtension(uniqueId);
212
+
if (ext.state === ExtensionState.NotDownloaded) return false;
213
+
const val = this.config.extensions[ext.id];
139
214
if (val == null) return false;
140
215
return typeof val === "boolean" ? val : val.enabled;
141
216
}
142
217
143
-
getExtensionConfig<T>(id: string, key: string): T | undefined {
144
-
const defaultValue = this.extensions[id].manifest.settings?.[key]?.default;
145
-
const clonedDefaultValue = this.clone(defaultValue);
218
+
getExtensionConfig<T>(uniqueId: number, key: string): T | undefined {
219
+
const ext = this.getExtension(uniqueId);
220
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
221
+
return getConfigOption(ext.id, key, this.config, settings);
222
+
}
223
+
224
+
getExtensionConfigRaw<T>(id: string, key: string, defaultValue: T | undefined): T | undefined {
146
225
const cfg = this.config.extensions[id];
147
-
148
-
if (cfg == null || typeof cfg === "boolean") return clonedDefaultValue;
149
-
return cfg.config?.[key] ?? clonedDefaultValue;
226
+
if (cfg == null || typeof cfg === "boolean") return defaultValue;
227
+
return cfg.config?.[key] ?? defaultValue;
150
228
}
151
229
152
-
getExtensionConfigName(id: string, key: string) {
153
-
return this.extensions[id].manifest.settings?.[key]?.displayName ?? key;
230
+
getExtensionConfigName(uniqueId: number, key: string) {
231
+
const ext = this.getExtension(uniqueId);
232
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
233
+
return settings?.[key]?.displayName ?? key;
154
234
}
155
235
156
-
getExtensionConfigDescription(id: string, key: string) {
157
-
return this.extensions[id].manifest.settings?.[key]?.description;
236
+
getExtensionConfigDescription(uniqueId: number, key: string) {
237
+
const ext = this.getExtension(uniqueId);
238
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
239
+
return settings?.[key]?.description;
158
240
}
159
241
160
242
setExtensionConfig(id: string, key: string, value: any) {
161
-
const oldConfig = this.config.extensions[id];
162
-
const newConfig =
163
-
typeof oldConfig === "boolean"
164
-
? {
165
-
enabled: oldConfig,
166
-
config: { [key]: value }
167
-
}
168
-
: {
169
-
...oldConfig,
170
-
config: { ...(oldConfig?.config ?? {}), [key]: value }
171
-
};
172
-
173
-
this.config.extensions[id] = newConfig;
243
+
setConfigOption(this.config, id, key, value);
174
244
this.modified = this.isModified();
175
245
this.emitChange();
176
246
}
177
247
178
-
setExtensionEnabled(id: string, enabled: boolean) {
179
-
let val = this.config.extensions[id];
248
+
setExtensionEnabled(uniqueId: number, enabled: boolean) {
249
+
const ext = this.getExtension(uniqueId);
250
+
let val = this.config.extensions[ext.id];
180
251
181
252
if (val == null) {
182
-
this.config.extensions[id] = { enabled };
253
+
this.config.extensions[ext.id] = enabled;
183
254
this.modified = this.isModified();
184
255
this.emitChange();
185
256
return;
···
191
262
val.enabled = enabled;
192
263
}
193
264
194
-
this.config.extensions[id] = val;
265
+
this.config.extensions[ext.id] = val;
195
266
this.modified = this.isModified();
196
267
this.emitChange();
197
268
}
198
269
199
-
async installExtension(id: string) {
200
-
const ext = this.getExtension(id);
270
+
dismissAllExtensionUpdates() {
271
+
for (const id in this.extensions) {
272
+
this.extensions[id].hasUpdate = false;
273
+
}
274
+
this.emitChange();
275
+
}
276
+
277
+
async updateAllExtensions() {
278
+
for (const id of Object.keys(this.updates)) {
279
+
try {
280
+
await this.installExtension(parseInt(id));
281
+
} catch (e) {
282
+
logger.error("Error bulk updating extension", id, e);
283
+
}
284
+
}
285
+
}
286
+
287
+
async installExtension(uniqueId: number) {
288
+
const ext = this.getExtension(uniqueId);
201
289
if (!("download" in ext.manifest)) {
202
290
throw new Error("Extension has no download URL");
203
291
}
204
292
205
293
this.installing = true;
206
294
try {
207
-
const url = this.updates[id]?.download ?? ext.manifest.download;
208
-
await natives.installExtension(ext.manifest, url, ext.source.url!);
295
+
const update = this.updates[uniqueId];
296
+
const url = update?.download ?? ext.manifest.download;
297
+
await natives!.installExtension(ext.manifest, url, ext.source.url!);
209
298
if (ext.state === ExtensionState.NotDownloaded) {
210
-
this.extensions[id].state = ExtensionState.Disabled;
299
+
this.extensions[uniqueId].state = ExtensionState.Disabled;
300
+
}
301
+
302
+
if (update != null) {
303
+
const existing = this.extensions[uniqueId];
304
+
existing.settingsOverride = update.updateManifest.settings;
305
+
existing.compat = checkExtensionCompat(update.updateManifest);
306
+
existing.manifest = update.updateManifest;
307
+
existing.changelog = update.updateManifest.meta?.changelog;
211
308
}
212
309
213
-
delete this.updates[id];
310
+
delete this.updates[uniqueId];
214
311
} catch (e) {
215
312
logger.error("Error installing extension:", e);
216
313
}
217
314
218
315
this.installing = false;
316
+
this.restartAdvice = this.#computeRestartAdvice();
219
317
this.emitChange();
220
318
}
221
319
222
-
async deleteExtension(id: string) {
223
-
const ext = this.getExtension(id);
320
+
private getRank(ext: MoonbaseExtension) {
321
+
if (ext.source.type === ExtensionLoadSource.Developer) return 3;
322
+
if (ext.source.type === ExtensionLoadSource.Core) return 2;
323
+
if (ext.source.url === mainRepo) return 1;
324
+
return 0;
325
+
}
326
+
327
+
async getDependencies(uniqueId: number) {
328
+
const ext = this.getExtension(uniqueId);
329
+
330
+
const missingDeps = [];
331
+
for (const dep of ext.manifest.dependencies ?? []) {
332
+
const anyInstalled = Object.values(this.extensions).some(
333
+
(e) => e.id === dep && e.state !== ExtensionState.NotDownloaded
334
+
);
335
+
if (!anyInstalled) missingDeps.push(dep);
336
+
}
337
+
338
+
if (missingDeps.length === 0) return null;
339
+
340
+
const deps: Record<string, MoonbaseExtension[]> = {};
341
+
for (const dep of missingDeps) {
342
+
const candidates = Object.values(this.extensions).filter((e) => e.id === dep);
343
+
344
+
deps[dep] = candidates.sort((a, b) => {
345
+
const aRank = this.getRank(a);
346
+
const bRank = this.getRank(b);
347
+
if (aRank === bRank) {
348
+
const repoIndex = this.savedConfig.repositories.indexOf(a.source.url!);
349
+
const otherRepoIndex = this.savedConfig.repositories.indexOf(b.source.url!);
350
+
return repoIndex - otherRepoIndex;
351
+
} else {
352
+
return bRank - aRank;
353
+
}
354
+
});
355
+
}
356
+
357
+
return deps;
358
+
}
359
+
360
+
async deleteExtension(uniqueId: number) {
361
+
const ext = this.getExtension(uniqueId);
224
362
if (ext == null) return;
225
363
226
364
this.installing = true;
227
365
try {
228
-
await natives.deleteExtension(ext.id);
229
-
this.extensions[id].state = ExtensionState.NotDownloaded;
366
+
await natives!.deleteExtension(ext.id);
367
+
this.extensions[uniqueId].state = ExtensionState.NotDownloaded;
230
368
} catch (e) {
231
369
logger.error("Error deleting extension:", e);
232
370
}
233
371
234
372
this.installing = false;
373
+
this.restartAdvice = this.#computeRestartAdvice();
374
+
this.emitChange();
375
+
}
376
+
377
+
async updateMoonlight() {
378
+
this.#updateState = UpdateState.Working;
379
+
this.emitChange();
380
+
381
+
await natives
382
+
.updateMoonlight()
383
+
.then(() => (this.#updateState = UpdateState.Installed))
384
+
.catch((e) => {
385
+
logger.error(e);
386
+
this.#updateState = UpdateState.Failed;
387
+
});
388
+
235
389
this.emitChange();
236
390
}
237
391
···
245
399
this.emitChange();
246
400
}
247
401
248
-
writeConfig() {
249
-
this.submitting = true;
402
+
tryGetExtensionName(id: string) {
403
+
const uniqueId = this.getExtensionUniqueId(id);
404
+
return (uniqueId != null ? this.getExtensionName(uniqueId) : null) ?? id;
405
+
}
406
+
407
+
registerConfigComponent(ext: string, name: string, component: CustomComponent) {
408
+
if (!(ext in this.configComponents)) this.configComponents[ext] = {};
409
+
this.configComponents[ext][name] = component;
410
+
}
411
+
412
+
getExtensionConfigComponent(ext: string, name: string) {
413
+
return this.configComponents[ext]?.[name];
414
+
}
415
+
416
+
#computeRestartAdvice() {
417
+
// If moonlight update needs a restart, always hide advice.
418
+
if (this.#updateState === UpdateState.Installed) return RestartAdvice.NotNeeded;
419
+
420
+
const i = this.initialConfig; // Initial config, from startup
421
+
const n = this.config; // New config about to be saved
422
+
423
+
let returnedAdvice = RestartAdvice.NotNeeded;
424
+
const updateAdvice = (r: RestartAdvice) => (returnedAdvice < r ? (returnedAdvice = r) : returnedAdvice);
425
+
426
+
// Top-level keys, repositories is not needed here because Moonbase handles it.
427
+
if (i.patchAll !== n.patchAll) updateAdvice(RestartAdvice.ReloadNeeded);
428
+
if (i.loggerLevel !== n.loggerLevel) updateAdvice(RestartAdvice.ReloadNeeded);
429
+
if (diff(i.devSearchPaths ?? [], n.devSearchPaths ?? [], { cyclesFix: false }).length !== 0)
430
+
return updateAdvice(RestartAdvice.RestartNeeded);
431
+
432
+
// Extension specific logic
433
+
for (const id in n.extensions) {
434
+
// Installed extension (might not be detected yet)
435
+
const ext = Object.values(this.extensions).find((e) => e.id === id && e.state !== ExtensionState.NotDownloaded);
436
+
// Installed and detected extension
437
+
const detected = moonlightNode.extensions.find((e) => e.id === id);
438
+
439
+
// If it's not installed at all, we don't care
440
+
if (!ext) continue;
441
+
442
+
const initState = i.extensions[id];
443
+
const newState = n.extensions[id];
444
+
445
+
const newEnabled = typeof newState === "boolean" ? newState : newState.enabled;
446
+
// If it's enabled but not detected yet, restart.
447
+
if (newEnabled && !detected) {
448
+
return updateAdvice(RestartAdvice.RestartNeeded);
449
+
}
450
+
451
+
// Toggling extensions specifically wants to rely on the initial state,
452
+
// that's what was considered when loading extensions.
453
+
const initEnabled = initState && (typeof initState === "boolean" ? initState : initState.enabled);
454
+
if (initEnabled !== newEnabled || detected?.manifest.version !== ext.manifest.version) {
455
+
// If we have the extension locally, we confidently know if it has host/preload scripts.
456
+
// If not, we have to respect the environment specified in the manifest.
457
+
// If that is the default, we can't know what's needed.
458
+
459
+
if (detected?.scripts.hostPath || detected?.scripts.nodePath) {
460
+
return updateAdvice(RestartAdvice.RestartNeeded);
461
+
}
462
+
463
+
switch (ext.manifest.environment) {
464
+
case ExtensionEnvironment.Both:
465
+
case ExtensionEnvironment.Web:
466
+
updateAdvice(RestartAdvice.ReloadNeeded);
467
+
continue;
468
+
case ExtensionEnvironment.Desktop:
469
+
return updateAdvice(RestartAdvice.RestartNeeded);
470
+
default:
471
+
updateAdvice(RestartAdvice.ReloadNeeded);
472
+
continue;
473
+
}
474
+
}
475
+
476
+
const initConfig = typeof initState === "boolean" ? {} : { ...initState?.config };
477
+
const newConfig = typeof newState === "boolean" ? {} : { ...newState?.config };
478
+
479
+
const def = ext.manifest.settings;
480
+
if (!def) continue;
481
+
482
+
for (const key in def) {
483
+
const defaultValue = def[key].default;
484
+
485
+
initConfig[key] ??= defaultValue;
486
+
newConfig[key] ??= defaultValue;
487
+
}
488
+
489
+
const changedKeys = diff(initConfig, newConfig, { cyclesFix: false }).map((c) => c.path[0]);
490
+
for (const key in def) {
491
+
if (!changedKeys.includes(key)) continue;
492
+
493
+
const advice = def[key].advice;
494
+
switch (advice) {
495
+
case ExtensionSettingsAdvice.None:
496
+
updateAdvice(RestartAdvice.NotNeeded);
497
+
continue;
498
+
case ExtensionSettingsAdvice.Reload:
499
+
updateAdvice(RestartAdvice.ReloadNeeded);
500
+
continue;
501
+
case ExtensionSettingsAdvice.Restart:
502
+
updateAdvice(RestartAdvice.RestartNeeded);
503
+
continue;
504
+
default:
505
+
updateAdvice(RestartAdvice.ReloadSuggested);
506
+
}
507
+
}
508
+
}
250
509
510
+
return returnedAdvice;
511
+
}
512
+
513
+
async writeConfig() {
251
514
try {
252
-
moonlightNode.writeConfig(this.config);
253
-
this.origConfig = this.clone(this.config);
254
-
} catch (e) {
255
-
logger.error("Error writing config", e);
515
+
this.submitting = true;
516
+
this.emitChange();
517
+
518
+
await moonlightNode.writeConfig(this.config);
519
+
await this.processConfigChanged();
520
+
} finally {
521
+
this.submitting = false;
522
+
this.emitChange();
256
523
}
524
+
}
257
525
258
-
this.submitting = false;
526
+
private async processConfigChanged() {
527
+
this.savedConfig = this.clone(this.config);
528
+
this.restartAdvice = this.#computeRestartAdvice();
259
529
this.modified = false;
530
+
531
+
const modifiedRepos = diff(this.savedConfig.repositories, this.config.repositories);
532
+
if (modifiedRepos.length !== 0) await this.checkUpdates();
533
+
260
534
this.emitChange();
261
535
}
262
536
263
537
reset() {
264
538
this.submitting = false;
265
539
this.modified = false;
266
-
this.config = this.clone(this.origConfig);
540
+
this.config = this.clone(this.savedConfig);
267
541
this.emitChange();
542
+
}
543
+
544
+
restartDiscord() {
545
+
if (moonlightNode.isBrowser) {
546
+
window.location.reload();
547
+
} else {
548
+
// @ts-expect-error TODO: DiscordNative
549
+
window.DiscordNative.app.relaunch();
550
+
}
268
551
}
269
552
270
553
// Required because electron likes to make it immutable sometimes.
+47
packages/core-extensions/src/moonbase/webpackModules/ui/HelpMessage.tsx
+47
packages/core-extensions/src/moonbase/webpackModules/ui/HelpMessage.tsx
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
3
+
import { Text } from "@moonlight-mod/wp/discord/components/common/index";
4
+
import HelpMessageClasses from "@moonlight-mod/wp/discord/components/common/HelpMessage.css";
5
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
6
+
7
+
// reimpl of HelpMessage but with a custom icon
8
+
export default function HelpMessage({
9
+
className,
10
+
text,
11
+
icon,
12
+
children,
13
+
type = "info"
14
+
}: {
15
+
className?: string;
16
+
text: string;
17
+
icon: React.ComponentType<any>;
18
+
type?: "warning" | "positive" | "error" | "info";
19
+
children?: React.ReactNode;
20
+
}) {
21
+
return (
22
+
<div
23
+
className={`${Margins.marginBottom20} ${HelpMessageClasses[type]} ${HelpMessageClasses.container} moonbase-help-message ${className}`}
24
+
>
25
+
<Flex direction={Flex.Direction.HORIZONTAL}>
26
+
<div
27
+
className={HelpMessageClasses.iconDiv}
28
+
style={{
29
+
alignItems: "center"
30
+
}}
31
+
>
32
+
{React.createElement(icon, {
33
+
size: "sm",
34
+
color: "currentColor",
35
+
className: HelpMessageClasses.icon
36
+
})}
37
+
</div>
38
+
39
+
<Text variant="text-sm/medium" color="currentColor" className={HelpMessageClasses.text}>
40
+
{text}
41
+
</Text>
42
+
43
+
{children}
44
+
</Flex>
45
+
</div>
46
+
);
47
+
}
+43
packages/core-extensions/src/moonbase/webpackModules/ui/RestartAdvice.tsx
+43
packages/core-extensions/src/moonbase/webpackModules/ui/RestartAdvice.tsx
···
1
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
2
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
3
+
import { Button, CircleWarningIcon } from "@moonlight-mod/wp/discord/components/common/index";
4
+
import React from "@moonlight-mod/wp/react";
5
+
import { RestartAdvice } from "../../types";
6
+
import HelpMessage from "./HelpMessage";
7
+
8
+
const strings: Record<RestartAdvice, string> = {
9
+
[RestartAdvice.NotNeeded]: "how did you even",
10
+
[RestartAdvice.ReloadSuggested]: "A reload might be needed to apply some of the changed options.",
11
+
[RestartAdvice.ReloadNeeded]: "A reload is needed to apply some of the changed options.",
12
+
[RestartAdvice.RestartNeeded]: "A restart is needed to apply some of the changed options."
13
+
};
14
+
15
+
const buttonStrings: Record<RestartAdvice, string> = {
16
+
[RestartAdvice.NotNeeded]: "huh?",
17
+
[RestartAdvice.ReloadSuggested]: "Reload",
18
+
[RestartAdvice.ReloadNeeded]: "Reload",
19
+
[RestartAdvice.RestartNeeded]: "Restart"
20
+
};
21
+
22
+
const actions: Record<RestartAdvice, () => void> = {
23
+
[RestartAdvice.NotNeeded]: () => {},
24
+
[RestartAdvice.ReloadSuggested]: () => window.location.reload(),
25
+
[RestartAdvice.ReloadNeeded]: () => window.location.reload(),
26
+
[RestartAdvice.RestartNeeded]: () => MoonbaseSettingsStore.restartDiscord()
27
+
};
28
+
29
+
export default function RestartAdviceMessage() {
30
+
const restartAdvice = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.restartAdvice);
31
+
32
+
if (restartAdvice === RestartAdvice.NotNeeded) return null;
33
+
34
+
return (
35
+
<div className="moonbase-help-message-sticky">
36
+
<HelpMessage text={strings[restartAdvice]} icon={CircleWarningIcon} type="warning">
37
+
<Button color={Button.Colors.YELLOW} size={Button.Sizes.TINY} onClick={actions[restartAdvice]}>
38
+
{buttonStrings[restartAdvice]}
39
+
</Button>
40
+
</HelpMessage>
41
+
</div>
42
+
);
43
+
}
+110
packages/core-extensions/src/moonbase/webpackModules/ui/about.tsx
+110
packages/core-extensions/src/moonbase/webpackModules/ui/about.tsx
···
1
+
import {
2
+
Text,
3
+
useThemeContext,
4
+
Button,
5
+
AngleBracketsIcon,
6
+
BookCheckIcon,
7
+
ClydeIcon
8
+
} from "@moonlight-mod/wp/discord/components/common/index";
9
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
10
+
import React from "@moonlight-mod/wp/react";
11
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
12
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
13
+
14
+
const wordmark = "https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/heads/main/img/wordmark.png";
15
+
const wordmarkLight =
16
+
"https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/heads/main/img/wordmark-light.png";
17
+
18
+
function parse(str: string) {
19
+
return MarkupUtils.parse(str, true, {
20
+
allowHeading: true,
21
+
allowLinks: true,
22
+
allowList: true
23
+
});
24
+
}
25
+
26
+
function Dev({ name, picture, link }: { name: string; picture: string; link: string }) {
27
+
return (
28
+
<Button onClick={() => window.open(link)} color={Button.Colors.PRIMARY} className="moonbase-dev">
29
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
30
+
<img src={picture} alt={name} className="moonbase-dev-avatar" />
31
+
32
+
<Text variant="text-md/semibold">{name}</Text>
33
+
</Flex>
34
+
</Button>
35
+
);
36
+
}
37
+
38
+
function IconButton({
39
+
text,
40
+
link,
41
+
icon,
42
+
openInClient
43
+
}: {
44
+
text: string;
45
+
link: string;
46
+
icon: React.FC<any>;
47
+
openInClient?: boolean;
48
+
}) {
49
+
return (
50
+
<Button
51
+
onClick={() => {
52
+
if (openInClient) {
53
+
try {
54
+
const { handleClick } = spacepack.require("discord/utils/MaskedLinkUtils");
55
+
handleClick({ href: link });
56
+
} catch {
57
+
window.open(link);
58
+
}
59
+
} else {
60
+
// Will open externally in the user's browser
61
+
window.open(link);
62
+
}
63
+
}}
64
+
>
65
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
66
+
{React.createElement(icon, {
67
+
size: "sm",
68
+
color: "currentColor"
69
+
})}
70
+
{text}
71
+
</Flex>
72
+
</Button>
73
+
);
74
+
}
75
+
76
+
export default function AboutPage() {
77
+
const darkTheme = useThemeContext()?.theme !== "light";
78
+
79
+
return (
80
+
<Flex direction={Flex.Direction.VERTICAL} align={Flex.Align.CENTER} className="moonbase-about-page">
81
+
<img src={darkTheme ? wordmarkLight : wordmark} alt="moonlight wordmark" className="moonbase-wordmark" />
82
+
83
+
<Text variant="heading-lg/medium">created by:</Text>
84
+
<div className="moonbase-devs">
85
+
<Dev name="Cynosphere" picture="https://github.com/Cynosphere.png" link="https://github.com/Cynosphere" />
86
+
<Dev name="NotNite" picture="https://github.com/NotNite.png" link="https://github.com/NotNite" />
87
+
<Dev name="adryd" picture="https://github.com/adryd325.png" link="https://github.com/adryd325" />
88
+
<Dev name="redstonekasi" picture="https://github.com/redstonekasi.png" link="https://github.com/redstonekasi" />
89
+
</div>
90
+
91
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
92
+
<IconButton text="View source" icon={AngleBracketsIcon} link="https://github.com/moonlight-mod/moonlight" />
93
+
<IconButton text="Open the docs" icon={BookCheckIcon} link="https://moonlight-mod.github.io/" />
94
+
<IconButton text="Join the server" icon={ClydeIcon} link="https://discord.gg/FdZBTFCP6F" openInClient={true} />
95
+
</Flex>
96
+
97
+
<Flex direction={Flex.Direction.VERTICAL} align={Flex.Align.START}>
98
+
<Text variant="text-sm/normal">
99
+
{parse(`moonlight \`${window.moonlight.version}\` on \`${window.moonlight.branch}\``)}
100
+
</Text>
101
+
102
+
<Text variant="text-sm/normal">
103
+
{parse(
104
+
"moonlight is licensed under the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.html) (`LGPL-3.0-or-later`)."
105
+
)}
106
+
</Text>
107
+
</Flex>
108
+
</Flex>
109
+
);
110
+
}
+157
packages/core-extensions/src/moonbase/webpackModules/ui/config/index.tsx
+157
packages/core-extensions/src/moonbase/webpackModules/ui/config/index.tsx
···
1
+
import { LogLevel } from "@moonlight-mod/types";
2
+
3
+
const logLevels = Object.values(LogLevel).filter((v) => typeof v === "string") as string[];
4
+
5
+
import React from "@moonlight-mod/wp/react";
6
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
7
+
import {
8
+
FormDivider,
9
+
FormItem,
10
+
FormText,
11
+
FormSwitch,
12
+
TextInput,
13
+
Button,
14
+
SingleSelect,
15
+
Tooltip,
16
+
Clickable
17
+
} from "@moonlight-mod/wp/discord/components/common/index";
18
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
19
+
import { CircleXIcon } from "@moonlight-mod/wp/discord/components/common/index";
20
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
21
+
import FormSwitchClasses from "@moonlight-mod/wp/discord/components/common/FormSwitch.css";
22
+
23
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
24
+
25
+
let GuildSettingsRoleEditClasses: any;
26
+
spacepack
27
+
.lazyLoad(
28
+
"renderArtisanalHack",
29
+
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"GuildSettings"/,
30
+
/webpackId:(\d+),name:"GuildSettings"/
31
+
)
32
+
.then(
33
+
() =>
34
+
(GuildSettingsRoleEditClasses = spacepack.require(
35
+
"discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"
36
+
))
37
+
);
38
+
39
+
function RemoveEntryButton({ onClick }: { onClick: () => void }) {
40
+
return (
41
+
<div className={GuildSettingsRoleEditClasses.removeButtonContainer}>
42
+
<Tooltip text="Remove entry" position="top">
43
+
{(props: any) => (
44
+
<Clickable {...props} className={GuildSettingsRoleEditClasses.removeButton} onClick={onClick}>
45
+
<CircleXIcon width={24} height={24} />
46
+
</Clickable>
47
+
)}
48
+
</Tooltip>
49
+
</div>
50
+
);
51
+
}
52
+
53
+
function ArrayFormItem({ config }: { config: "repositories" | "devSearchPaths" }) {
54
+
const items = MoonbaseSettingsStore.getConfigOption(config) ?? [];
55
+
return (
56
+
<Flex
57
+
style={{
58
+
gap: "20px"
59
+
}}
60
+
direction={Flex.Direction.VERTICAL}
61
+
>
62
+
{items.map((val, i) => (
63
+
<div
64
+
key={i}
65
+
style={{
66
+
display: "grid",
67
+
height: "32px",
68
+
gap: "8px",
69
+
gridTemplateColumns: "1fr 32px",
70
+
alignItems: "center"
71
+
}}
72
+
>
73
+
<TextInput
74
+
size={TextInput.Sizes.DEFAULT}
75
+
value={val}
76
+
onChange={(newVal: string) => {
77
+
items[i] = newVal;
78
+
MoonbaseSettingsStore.setConfigOption(config, items);
79
+
}}
80
+
/>
81
+
<RemoveEntryButton
82
+
onClick={() => {
83
+
items.splice(i, 1);
84
+
MoonbaseSettingsStore.setConfigOption(config, items);
85
+
}}
86
+
/>
87
+
</div>
88
+
))}
89
+
90
+
<Button
91
+
look={Button.Looks.FILLED}
92
+
color={Button.Colors.GREEN}
93
+
size={Button.Sizes.SMALL}
94
+
style={{
95
+
marginTop: "10px"
96
+
}}
97
+
onClick={() => {
98
+
items.push("");
99
+
MoonbaseSettingsStore.setConfigOption(config, items);
100
+
}}
101
+
>
102
+
Add new entry
103
+
</Button>
104
+
</Flex>
105
+
);
106
+
}
107
+
108
+
export default function ConfigPage() {
109
+
return (
110
+
<>
111
+
<FormSwitch
112
+
className={Margins.marginTop20}
113
+
value={MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "updateChecking", true) ?? true}
114
+
onChange={(value: boolean) => {
115
+
MoonbaseSettingsStore.setExtensionConfig("moonbase", "updateChecking", value);
116
+
}}
117
+
note="Checks for updates to moonlight"
118
+
>
119
+
Automatic update checking
120
+
</FormSwitch>
121
+
<FormItem title="Repositories">
122
+
<FormText className={Margins.marginBottom4}>A list of remote repositories to display extensions from</FormText>
123
+
<ArrayFormItem config="repositories" />
124
+
</FormItem>
125
+
<FormDivider className={FormSwitchClasses.dividerDefault} />
126
+
<FormItem title="Extension search paths" className={Margins.marginTop20}>
127
+
<FormText className={Margins.marginBottom4}>
128
+
A list of local directories to search for built extensions
129
+
</FormText>
130
+
<ArrayFormItem config="devSearchPaths" />
131
+
</FormItem>
132
+
<FormDivider className={FormSwitchClasses.dividerDefault} />
133
+
<FormSwitch
134
+
className={Margins.marginTop20}
135
+
value={MoonbaseSettingsStore.getConfigOption("patchAll") ?? false}
136
+
onChange={(value: boolean) => {
137
+
MoonbaseSettingsStore.setConfigOption("patchAll", value);
138
+
}}
139
+
note="Wraps every webpack module in a function, separating them in DevTools"
140
+
>
141
+
Patch all
142
+
</FormSwitch>
143
+
<FormItem title="Log level">
144
+
<SingleSelect
145
+
autofocus={false}
146
+
clearable={false}
147
+
value={MoonbaseSettingsStore.getConfigOption("loggerLevel")}
148
+
options={logLevels.map((o) => ({
149
+
value: o.toLowerCase(),
150
+
label: o[0] + o.slice(1).toLowerCase()
151
+
}))}
152
+
onChange={(v) => MoonbaseSettingsStore.setConfigOption("loggerLevel", v)}
153
+
/>
154
+
</FormItem>
155
+
</>
156
+
);
157
+
}
+335
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
+335
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
···
1
+
import { ExtensionState } from "../../../types";
2
+
import { constants, ExtensionLoadSource, ExtensionTag } from "@moonlight-mod/types";
3
+
4
+
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
5
+
import {
6
+
ScienceIcon,
7
+
DownloadIcon,
8
+
TrashIcon,
9
+
AngleBracketsIcon,
10
+
Tooltip,
11
+
Card,
12
+
Text,
13
+
FormSwitch,
14
+
TabBar,
15
+
Button,
16
+
ChannelListIcon,
17
+
HeartIcon,
18
+
WindowTopOutlineIcon,
19
+
WarningIcon
20
+
} from "@moonlight-mod/wp/discord/components/common/index";
21
+
import React from "@moonlight-mod/wp/react";
22
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
23
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
24
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
25
+
import AppCardClasses from "@moonlight-mod/wp/discord/modules/guild_settings/web/AppCard.css";
26
+
import PanelButton from "@moonlight-mod/wp/discord/components/common/PanelButton";
27
+
import DiscoveryClasses from "@moonlight-mod/wp/discord/modules/discovery/web/Discovery.css";
28
+
import MarkupClasses from "@moonlight-mod/wp/discord/modules/messages/web/Markup.css";
29
+
import BuildOverrideClasses from "@moonlight-mod/wp/discord/modules/build_overrides/web/BuildOverride.css";
30
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
31
+
import ErrorBoundary from "@moonlight-mod/wp/common_ErrorBoundary";
32
+
import ExtensionInfo from "./info";
33
+
import Settings from "./settings";
34
+
import { doGenericExtensionPopup, doMissingExtensionPopup } from "./popup";
35
+
36
+
export enum ExtensionPage {
37
+
Info,
38
+
Description,
39
+
Changelog,
40
+
Settings
41
+
}
42
+
43
+
const COMPAT_TEXT_MAP: Record<ExtensionCompat, string> = {
44
+
[ExtensionCompat.Compatible]: "huh?",
45
+
[ExtensionCompat.InvalidApiLevel]: "Incompatible API level",
46
+
[ExtensionCompat.InvalidEnvironment]: "Incompatible platform"
47
+
};
48
+
const CONFLICTING_TEXT = "This extension is already installed from another source.";
49
+
50
+
function PanelLinkButton({ icon, tooltip, link }: { icon: React.ReactNode; tooltip: string; link: string }) {
51
+
return (
52
+
<PanelButton
53
+
icon={icon}
54
+
tooltipText={tooltip}
55
+
onClick={() => {
56
+
window.open(link);
57
+
}}
58
+
/>
59
+
);
60
+
}
61
+
62
+
export default function ExtensionCard({ uniqueId, selectTag }: { uniqueId: number; selectTag: (tag: string) => void }) {
63
+
const { ext, enabled, busy, update, conflicting } = useStateFromStores([MoonbaseSettingsStore], () => {
64
+
return {
65
+
ext: MoonbaseSettingsStore.getExtension(uniqueId),
66
+
enabled: MoonbaseSettingsStore.getExtensionEnabled(uniqueId),
67
+
busy: MoonbaseSettingsStore.busy,
68
+
update: MoonbaseSettingsStore.getExtensionUpdate(uniqueId),
69
+
conflicting: MoonbaseSettingsStore.getExtensionConflicting(uniqueId)
70
+
};
71
+
});
72
+
73
+
const [tab, setTab] = React.useState(
74
+
update != null && ext?.changelog != null ? ExtensionPage.Changelog : ExtensionPage.Info
75
+
);
76
+
77
+
const tagline = ext.manifest?.meta?.tagline;
78
+
const settings = ext.settingsOverride ?? ext.manifest?.settings;
79
+
const description = ext.manifest?.meta?.description;
80
+
const changelog = ext.changelog;
81
+
const linkButtons = [
82
+
ext?.manifest?.meta?.source && (
83
+
<PanelLinkButton icon={<AngleBracketsIcon />} tooltip="View source" link={ext.manifest.meta.source} />
84
+
),
85
+
ext?.source?.url && <PanelLinkButton icon={<ChannelListIcon />} tooltip="View repository" link={ext.source.url} />,
86
+
ext?.manifest?.meta?.donate && (
87
+
<PanelLinkButton icon={<HeartIcon />} tooltip="Donate" link={ext.manifest.meta.donate} />
88
+
)
89
+
].filter((x) => x != null);
90
+
91
+
const enabledDependants = useStateFromStores([MoonbaseSettingsStore], () =>
92
+
Object.keys(MoonbaseSettingsStore.extensions)
93
+
.filter((uniqueId) => {
94
+
const potentialDependant = MoonbaseSettingsStore.getExtension(parseInt(uniqueId));
95
+
96
+
return (
97
+
potentialDependant.manifest.dependencies?.includes(ext?.id) &&
98
+
MoonbaseSettingsStore.getExtensionEnabled(parseInt(uniqueId))
99
+
);
100
+
})
101
+
.map((a) => MoonbaseSettingsStore.getExtension(parseInt(a)))
102
+
);
103
+
const implicitlyEnabled = enabledDependants.length > 0;
104
+
105
+
const hasDuplicateEntry = useStateFromStores([MoonbaseSettingsStore], () =>
106
+
Object.entries(MoonbaseSettingsStore.extensions).some(
107
+
([otherUniqueId, otherExt]) =>
108
+
otherExt != null && otherExt?.id === ext?.id && parseInt(otherUniqueId) !== uniqueId
109
+
)
110
+
);
111
+
112
+
return ext == null ? (
113
+
<></>
114
+
) : (
115
+
<Card editable={true} className={AppCardClasses.card}>
116
+
<div className={AppCardClasses.cardHeader}>
117
+
<Flex direction={Flex.Direction.VERTICAL}>
118
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER}>
119
+
<Text variant="text-md/semibold">{ext.manifest?.meta?.name ?? ext.id}</Text>
120
+
{ext.source.type === ExtensionLoadSource.Developer && (
121
+
<Tooltip text="This is a local extension" position="top">
122
+
{(props: any) => <ScienceIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
123
+
</Tooltip>
124
+
)}
125
+
126
+
{hasDuplicateEntry && ext?.source?.url && (
127
+
<Tooltip text={`This extension is from the following repository: ${ext.source.url}`} position="top">
128
+
{(props: any) => <WindowTopOutlineIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
129
+
</Tooltip>
130
+
)}
131
+
132
+
{ext.manifest?.meta?.deprecated && (
133
+
<Tooltip text="This extension is deprecated" position="top">
134
+
{(props: any) => <WarningIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
135
+
</Tooltip>
136
+
)}
137
+
</Flex>
138
+
139
+
{tagline != null && <Text variant="text-sm/normal">{MarkupUtils.parse(tagline)}</Text>}
140
+
</Flex>
141
+
142
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.END} justify={Flex.Justify.END}>
143
+
<div
144
+
// too lazy to learn how <Flex /> works lmao
145
+
style={{
146
+
display: "flex",
147
+
alignItems: "center",
148
+
gap: "1rem"
149
+
}}
150
+
>
151
+
{ext.state === ExtensionState.NotDownloaded ? (
152
+
<Tooltip
153
+
text={conflicting ? CONFLICTING_TEXT : COMPAT_TEXT_MAP[ext.compat]}
154
+
shouldShow={conflicting || ext.compat !== ExtensionCompat.Compatible}
155
+
>
156
+
{(props: any) => (
157
+
<Button
158
+
{...props}
159
+
color={Button.Colors.BRAND}
160
+
submitting={busy}
161
+
disabled={ext.compat !== ExtensionCompat.Compatible || conflicting}
162
+
onClick={async () => {
163
+
await MoonbaseSettingsStore.installExtension(uniqueId);
164
+
const deps = await MoonbaseSettingsStore.getDependencies(uniqueId);
165
+
if (deps != null) {
166
+
await doMissingExtensionPopup(deps);
167
+
}
168
+
169
+
// Don't auto enable dangerous extensions
170
+
if (!ext.manifest?.meta?.tags?.includes(ExtensionTag.DangerZone)) {
171
+
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, true);
172
+
}
173
+
}}
174
+
>
175
+
Install
176
+
</Button>
177
+
)}
178
+
</Tooltip>
179
+
) : (
180
+
<>
181
+
{ext.source.type === ExtensionLoadSource.Normal && (
182
+
<PanelButton
183
+
icon={TrashIcon}
184
+
tooltipText="Delete"
185
+
onClick={() => {
186
+
MoonbaseSettingsStore.deleteExtension(uniqueId);
187
+
}}
188
+
/>
189
+
)}
190
+
191
+
{update != null && (
192
+
<PanelButton
193
+
icon={DownloadIcon}
194
+
tooltipText="Update"
195
+
onClick={() => {
196
+
MoonbaseSettingsStore.installExtension(uniqueId);
197
+
}}
198
+
/>
199
+
)}
200
+
201
+
<FormSwitch
202
+
value={ext.compat === ExtensionCompat.Compatible && (enabled || implicitlyEnabled)}
203
+
disabled={implicitlyEnabled || ext.compat !== ExtensionCompat.Compatible}
204
+
hideBorder={true}
205
+
style={{ marginBottom: "0px" }}
206
+
// @ts-expect-error fix type later
207
+
tooltipNote={
208
+
ext.compat !== ExtensionCompat.Compatible ? (
209
+
COMPAT_TEXT_MAP[ext.compat]
210
+
) : implicitlyEnabled ? (
211
+
<div style={{ display: "flex", flexDirection: "column" }}>
212
+
<div>{`This extension is a dependency of the following enabled extension${
213
+
enabledDependants.length > 1 ? "s" : ""
214
+
}:`}</div>
215
+
{enabledDependants.map((dep) => (
216
+
<div>{"โข " + (dep.manifest.meta?.name ?? dep.id)}</div>
217
+
))}
218
+
</div>
219
+
) : undefined
220
+
}
221
+
onChange={() => {
222
+
const toggle = () => {
223
+
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, !enabled);
224
+
};
225
+
226
+
if (enabled && constants.builtinExtensions.includes(ext.id)) {
227
+
doGenericExtensionPopup(
228
+
"Built in extension",
229
+
"This extension is enabled by default. Disabling it might have consequences. Are you sure you want to disable it?",
230
+
uniqueId,
231
+
toggle
232
+
);
233
+
} else if (!enabled && ext.manifest?.meta?.tags?.includes(ExtensionTag.DangerZone)) {
234
+
doGenericExtensionPopup(
235
+
"Dangerous extension",
236
+
"This extension is marked as dangerous. Enabling it might have consequences. Are you sure you want to enable it?",
237
+
uniqueId,
238
+
toggle
239
+
);
240
+
} else {
241
+
toggle();
242
+
}
243
+
}}
244
+
/>
245
+
</>
246
+
)}
247
+
</div>
248
+
</Flex>
249
+
</div>
250
+
251
+
<div>
252
+
{(description != null || changelog != null || settings != null || linkButtons.length > 0) && (
253
+
<Flex>
254
+
<TabBar
255
+
selectedItem={tab}
256
+
type="top"
257
+
onItemSelect={setTab}
258
+
className={DiscoveryClasses.tabBar}
259
+
style={{
260
+
padding: "0 20px"
261
+
}}
262
+
>
263
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Info}>
264
+
Info
265
+
</TabBar.Item>
266
+
267
+
{description != null && (
268
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Description}>
269
+
Description
270
+
</TabBar.Item>
271
+
)}
272
+
273
+
{changelog != null && (
274
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Changelog}>
275
+
Changelog
276
+
</TabBar.Item>
277
+
)}
278
+
279
+
{settings != null && (
280
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Settings}>
281
+
Settings
282
+
</TabBar.Item>
283
+
)}
284
+
</TabBar>
285
+
286
+
<Flex
287
+
align={Flex.Align.CENTER}
288
+
justify={Flex.Justify.END}
289
+
direction={Flex.Direction.HORIZONTAL}
290
+
grow={1}
291
+
className="moonbase-link-buttons"
292
+
>
293
+
{linkButtons.length > 0 && linkButtons}
294
+
</Flex>
295
+
</Flex>
296
+
)}
297
+
298
+
<Flex
299
+
justify={Flex.Justify.START}
300
+
wrap={Flex.Wrap.WRAP}
301
+
style={{
302
+
padding: "16px 16px",
303
+
// This looks wonky in the settings tab
304
+
rowGap: tab === ExtensionPage.Info ? "16px" : undefined
305
+
}}
306
+
>
307
+
{tab === ExtensionPage.Info && <ExtensionInfo ext={ext} selectTag={selectTag} />}
308
+
{tab === ExtensionPage.Description && (
309
+
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ width: "100%" }}>
310
+
{MarkupUtils.parse(description ?? "*No description*", true, {
311
+
allowHeading: true,
312
+
allowLinks: true,
313
+
allowList: true
314
+
})}
315
+
</Text>
316
+
)}
317
+
{tab === ExtensionPage.Changelog && (
318
+
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ width: "100%" }}>
319
+
{MarkupUtils.parse(changelog ?? "*No changelog*", true, {
320
+
allowHeading: true,
321
+
allowLinks: true,
322
+
allowList: true
323
+
})}
324
+
</Text>
325
+
)}
326
+
{tab === ExtensionPage.Settings && (
327
+
<ErrorBoundary>
328
+
<Settings ext={ext} />
329
+
</ErrorBoundary>
330
+
)}
331
+
</Flex>
332
+
</div>
333
+
</Card>
334
+
);
335
+
}
+356
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/filterBar.tsx
+356
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/filterBar.tsx
···
1
+
import { tagNames } from "./info";
2
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
3
+
import * as React from "@moonlight-mod/wp/react";
4
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
5
+
import { WindowStore } from "@moonlight-mod/wp/common_stores";
6
+
import {
7
+
Button,
8
+
Text,
9
+
Heading,
10
+
Popout,
11
+
Dialog,
12
+
Menu,
13
+
ChevronSmallDownIcon,
14
+
ChevronSmallUpIcon,
15
+
ArrowsUpDownIcon,
16
+
RetryIcon,
17
+
Tooltip
18
+
} from "@moonlight-mod/wp/discord/components/common/index";
19
+
import { MenuGroup, MenuCheckboxItem, MenuItem } from "@moonlight-mod/wp/contextMenu_contextMenu";
20
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
21
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
22
+
import TagItem from "@moonlight-mod/wp/discord/modules/forums/web/Tag";
23
+
24
+
export enum Filter {
25
+
Core = 1 << 0,
26
+
Normal = 1 << 1,
27
+
Developer = 1 << 2,
28
+
Enabled = 1 << 3,
29
+
Disabled = 1 << 4,
30
+
Installed = 1 << 5,
31
+
Repository = 1 << 6,
32
+
Incompatible = 1 << 7,
33
+
Deprecated = 1 << 8
34
+
}
35
+
export const defaultFilter = 127 as Filter;
36
+
37
+
let HeaderClasses: any;
38
+
let ForumsClasses: any;
39
+
let SortMenuClasses: any;
40
+
spacepack
41
+
.lazyLoad('"Missing channel in Channel.openChannelContextMenu"', /e\("(\d+)"\)/g, /webpackId:(\d+?),/)
42
+
.then(() => {
43
+
ForumsClasses = spacepack.require("discord/modules/forums/web/Forums.css");
44
+
HeaderClasses = spacepack.require("discord/modules/forums/web/Header.css");
45
+
SortMenuClasses = spacepack.require("discord/modules/forums/web/SortMenu.css");
46
+
});
47
+
48
+
function toggleTag(selectedTags: Set<string>, setSelectedTags: (tags: Set<string>) => void, tag: string) {
49
+
const newState = new Set(selectedTags);
50
+
if (newState.has(tag)) newState.delete(tag);
51
+
else newState.add(tag);
52
+
setSelectedTags(newState);
53
+
}
54
+
55
+
function FilterButtonPopout({
56
+
filter,
57
+
setFilter,
58
+
closePopout
59
+
}: {
60
+
filter: Filter;
61
+
setFilter: (filter: Filter) => void;
62
+
closePopout: () => void;
63
+
}) {
64
+
const toggleFilter = (set: Filter) => setFilter(filter & set ? filter & ~set : filter | set);
65
+
66
+
return (
67
+
<div className={SortMenuClasses.container}>
68
+
<Menu navId="sort-filter" hideScroller={true} onClose={closePopout}>
69
+
<MenuGroup label="Type">
70
+
<MenuCheckboxItem
71
+
id="t-core"
72
+
label="Core"
73
+
checked={(filter & Filter.Core) === Filter.Core}
74
+
action={() => toggleFilter(Filter.Core)}
75
+
/>
76
+
<MenuCheckboxItem
77
+
id="t-normal"
78
+
label="Normal"
79
+
checked={(filter & Filter.Normal) === Filter.Normal}
80
+
action={() => toggleFilter(Filter.Normal)}
81
+
/>
82
+
<MenuCheckboxItem
83
+
id="t-developer"
84
+
label="Developer"
85
+
checked={(filter & Filter.Developer) === Filter.Developer}
86
+
action={() => toggleFilter(Filter.Developer)}
87
+
/>
88
+
</MenuGroup>
89
+
<MenuGroup label="State">
90
+
<MenuCheckboxItem
91
+
id="s-enabled"
92
+
label="Enabled"
93
+
checked={(filter & Filter.Enabled) === Filter.Enabled}
94
+
action={() => toggleFilter(Filter.Enabled)}
95
+
/>
96
+
<MenuCheckboxItem
97
+
id="s-disabled"
98
+
label="Disabled"
99
+
checked={(filter & Filter.Disabled) === Filter.Disabled}
100
+
action={() => toggleFilter(Filter.Disabled)}
101
+
/>
102
+
</MenuGroup>
103
+
<MenuGroup label="Location">
104
+
<MenuCheckboxItem
105
+
id="l-installed"
106
+
label="Installed"
107
+
checked={(filter & Filter.Installed) === Filter.Installed}
108
+
action={() => toggleFilter(Filter.Installed)}
109
+
/>
110
+
<MenuCheckboxItem
111
+
id="l-repository"
112
+
label="Repository"
113
+
checked={(filter & Filter.Repository) === Filter.Repository}
114
+
action={() => toggleFilter(Filter.Repository)}
115
+
/>
116
+
</MenuGroup>
117
+
<MenuGroup>
118
+
<MenuCheckboxItem
119
+
id="l-incompatible"
120
+
label="Show incompatible"
121
+
checked={(filter & Filter.Incompatible) === Filter.Incompatible}
122
+
action={() => toggleFilter(Filter.Incompatible)}
123
+
/>
124
+
<MenuCheckboxItem
125
+
id="l-deprecated"
126
+
label="Show deprecated"
127
+
checked={(filter & Filter.Deprecated) === Filter.Deprecated}
128
+
action={() => toggleFilter(Filter.Deprecated)}
129
+
/>
130
+
<MenuItem
131
+
id="reset-all"
132
+
className={SortMenuClasses.clearText}
133
+
label="Reset to default"
134
+
action={() => {
135
+
setFilter(defaultFilter);
136
+
closePopout();
137
+
}}
138
+
/>
139
+
</MenuGroup>
140
+
</Menu>
141
+
</div>
142
+
);
143
+
}
144
+
145
+
function TagButtonPopout({ selectedTags, setSelectedTags, setPopoutRef, closePopout }: any) {
146
+
return (
147
+
<Dialog ref={setPopoutRef} className={HeaderClasses.container}>
148
+
<div className={HeaderClasses.header}>
149
+
<div className={HeaderClasses.headerLeft}>
150
+
<Heading color="interactive-normal" variant="text-xs/bold" className={HeaderClasses.headerText}>
151
+
Select tags
152
+
</Heading>
153
+
<div className={HeaderClasses.countContainer}>
154
+
<Text className={HeaderClasses.countText} color="none" variant="text-xs/medium">
155
+
{selectedTags.size}
156
+
</Text>
157
+
</div>
158
+
</div>
159
+
</div>
160
+
<div className={HeaderClasses.tagContainer}>
161
+
{Object.keys(tagNames).map((tag) => (
162
+
<TagItem
163
+
key={tag}
164
+
className={HeaderClasses.tag}
165
+
tag={{ name: tagNames[tag as keyof typeof tagNames], id: tagNames[tag as keyof typeof tagNames] }}
166
+
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
167
+
selected={selectedTags.has(tag)}
168
+
/>
169
+
))}
170
+
</div>
171
+
<div className={HeaderClasses.separator} />
172
+
<Button
173
+
look={Button.Looks.LINK}
174
+
size={Button.Sizes.MIN}
175
+
color={Button.Colors.CUSTOM}
176
+
className={HeaderClasses.clear}
177
+
onClick={() => {
178
+
setSelectedTags(new Set());
179
+
closePopout();
180
+
}}
181
+
>
182
+
<Text variant="text-sm/medium" color="text-link">
183
+
Clear all
184
+
</Text>
185
+
</Button>
186
+
</Dialog>
187
+
);
188
+
}
189
+
190
+
export default function FilterBar({
191
+
filter,
192
+
setFilter,
193
+
selectedTags,
194
+
setSelectedTags
195
+
}: {
196
+
filter: Filter;
197
+
setFilter: (filter: Filter) => void;
198
+
selectedTags: Set<string>;
199
+
setSelectedTags: (tags: Set<string>) => void;
200
+
}) {
201
+
const windowSize = useStateFromStores([WindowStore], () => WindowStore.windowSize());
202
+
203
+
const tagsContainer = React.useRef<HTMLDivElement>(null);
204
+
const tagListInner = React.useRef<HTMLDivElement>(null);
205
+
const [tagsButtonOffset, setTagsButtonOffset] = React.useState(0);
206
+
const [checkingUpdates, setCheckingUpdates] = React.useState(false);
207
+
208
+
React.useLayoutEffect(() => {
209
+
if (tagsContainer.current === null || tagListInner.current === null) return;
210
+
const { left: containerX, top: containerY } = tagsContainer.current.getBoundingClientRect();
211
+
let offset = 0;
212
+
for (const child of tagListInner.current.children) {
213
+
const { right: childX, top: childY, height } = child.getBoundingClientRect();
214
+
if (childY - containerY > height) break;
215
+
const newOffset = childX - containerX;
216
+
if (newOffset > offset) {
217
+
offset = newOffset;
218
+
}
219
+
}
220
+
setTagsButtonOffset(offset);
221
+
}, [windowSize, tagsContainer.current, tagListInner.current, tagListInner.current?.getBoundingClientRect()?.width]);
222
+
223
+
return (
224
+
<div
225
+
ref={tagsContainer}
226
+
style={{
227
+
paddingTop: "12px"
228
+
}}
229
+
className={`${ForumsClasses.tagsContainer} ${Margins.marginBottom8}`}
230
+
>
231
+
<Tooltip text="Refresh updates" position="top">
232
+
{(props: any) => (
233
+
<Button
234
+
{...props}
235
+
size={Button.Sizes.MIN}
236
+
color={Button.Colors.CUSTOM}
237
+
className={`${ForumsClasses.sortDropdown} moonbase-retry-button`}
238
+
innerClassName={ForumsClasses.sortDropdownInner}
239
+
onClick={() => {
240
+
(async () => {
241
+
try {
242
+
setCheckingUpdates(true);
243
+
await MoonbaseSettingsStore.checkUpdates();
244
+
} finally {
245
+
// artificial delay because the spin is fun
246
+
await new Promise((r) => setTimeout(r, 500));
247
+
setCheckingUpdates(false);
248
+
}
249
+
})();
250
+
}}
251
+
>
252
+
<RetryIcon size={"custom"} width={16} className={checkingUpdates ? "moonbase-speen" : ""} />
253
+
</Button>
254
+
)}
255
+
</Tooltip>
256
+
<Popout
257
+
renderPopout={({ closePopout }: any) => (
258
+
<FilterButtonPopout filter={filter} setFilter={setFilter} closePopout={closePopout} />
259
+
)}
260
+
position="bottom"
261
+
align="left"
262
+
>
263
+
{(props: any, { isShown }: { isShown: boolean }) => (
264
+
<Button
265
+
{...props}
266
+
size={Button.Sizes.MIN}
267
+
color={Button.Colors.CUSTOM}
268
+
className={ForumsClasses.sortDropdown}
269
+
innerClassName={ForumsClasses.sortDropdownInner}
270
+
>
271
+
<ArrowsUpDownIcon size="xs" />
272
+
<Text className={ForumsClasses.sortDropdownText} variant="text-sm/medium" color="interactive-normal">
273
+
Sort & filter
274
+
</Text>
275
+
{isShown ? (
276
+
<ChevronSmallUpIcon size={"custom"} width={20} />
277
+
) : (
278
+
<ChevronSmallDownIcon size={"custom"} width={20} />
279
+
)}
280
+
</Button>
281
+
)}
282
+
</Popout>
283
+
<div className={ForumsClasses.divider} />
284
+
<div className={ForumsClasses.tagList}>
285
+
<div ref={tagListInner} className={ForumsClasses.tagListInner}>
286
+
{Object.keys(tagNames).map((tag) => (
287
+
<TagItem
288
+
key={tag}
289
+
className={ForumsClasses.tag}
290
+
tag={{ name: tagNames[tag as keyof typeof tagNames], id: tag }}
291
+
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
292
+
selected={selectedTags.has(tag)}
293
+
/>
294
+
))}
295
+
</div>
296
+
</div>
297
+
<Popout
298
+
renderPopout={({ setPopoutRef, closePopout }: any) => (
299
+
<TagButtonPopout
300
+
selectedTags={selectedTags}
301
+
setSelectedTags={setSelectedTags}
302
+
setPopoutRef={setPopoutRef}
303
+
closePopout={closePopout}
304
+
/>
305
+
)}
306
+
position="bottom"
307
+
align="right"
308
+
>
309
+
{(props: any, { isShown }: { isShown: boolean }) => (
310
+
<Button
311
+
{...props}
312
+
size={Button.Sizes.MIN}
313
+
color={Button.Colors.CUSTOM}
314
+
style={{
315
+
left: tagsButtonOffset
316
+
}}
317
+
// TODO: Use Discord's class name utility
318
+
className={`${ForumsClasses.tagsButton} ${selectedTags.size > 0 ? ForumsClasses.tagsButtonWithCount : ""}`}
319
+
innerClassName={ForumsClasses.tagsButtonInner}
320
+
>
321
+
{selectedTags.size > 0 ? (
322
+
<div style={{ boxSizing: "content-box" }} className={ForumsClasses.countContainer}>
323
+
<Text className={ForumsClasses.countText} color="none" variant="text-xs/medium">
324
+
{selectedTags.size}
325
+
</Text>
326
+
</div>
327
+
) : (
328
+
<>All</>
329
+
)}
330
+
{isShown ? (
331
+
<ChevronSmallUpIcon size={"custom"} width={20} />
332
+
) : (
333
+
<ChevronSmallDownIcon size={"custom"} width={20} />
334
+
)}
335
+
</Button>
336
+
)}
337
+
</Popout>
338
+
<Button
339
+
size={Button.Sizes.MIN}
340
+
color={Button.Colors.CUSTOM}
341
+
className={`${ForumsClasses.tagsButton} ${ForumsClasses.tagsButtonPlaceholder}`}
342
+
innerClassName={ForumsClasses.tagsButtonInner}
343
+
>
344
+
{selectedTags.size > 0 ? (
345
+
<div style={{ boxSizing: "content-box" }} className={ForumsClasses.countContainer}>
346
+
<Text className={ForumsClasses.countText} color="none" variant="text-xs/medium">
347
+
{selectedTags.size}
348
+
</Text>
349
+
</div>
350
+
) : null}
351
+
352
+
<ChevronSmallUpIcon size={"custom"} width={20} />
353
+
</Button>
354
+
</div>
355
+
);
356
+
}
+162
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
+162
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
···
1
+
import { ExtensionLoadSource, ExtensionTag } from "@moonlight-mod/types";
2
+
import { ExtensionState } from "../../../types";
3
+
import FilterBar, { Filter, defaultFilter } from "./filterBar";
4
+
import ExtensionCard from "./card";
5
+
6
+
import React from "@moonlight-mod/wp/react";
7
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
8
+
import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
9
+
import {
10
+
FormDivider,
11
+
CircleInformationIcon,
12
+
XSmallIcon,
13
+
Button
14
+
} from "@moonlight-mod/wp/discord/components/common/index";
15
+
import PanelButton from "@moonlight-mod/wp/discord/components/common/PanelButton";
16
+
17
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
18
+
import ErrorBoundary from "@moonlight-mod/wp/common_ErrorBoundary";
19
+
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
20
+
import HelpMessage from "../HelpMessage";
21
+
22
+
const SearchBar = spacepack.require("discord/uikit/search/SearchBar").default;
23
+
24
+
const validTags: string[] = Object.values(ExtensionTag);
25
+
26
+
export default function ExtensionsPage() {
27
+
const { extensions, savedFilter } = useStateFromStoresObject([MoonbaseSettingsStore], () => {
28
+
return {
29
+
extensions: MoonbaseSettingsStore.extensions,
30
+
savedFilter: MoonbaseSettingsStore.getExtensionConfigRaw<number>("moonbase", "filter", defaultFilter)
31
+
};
32
+
});
33
+
34
+
const [query, setQuery] = React.useState("");
35
+
const [hitUpdateAll, setHitUpdateAll] = React.useState(false);
36
+
37
+
const filterState = React.useState(defaultFilter);
38
+
39
+
let filter: Filter, setFilter: (filter: Filter) => void;
40
+
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "saveFilter", false)) {
41
+
filter = savedFilter ?? defaultFilter;
42
+
setFilter = (filter) => MoonbaseSettingsStore.setExtensionConfig("moonbase", "filter", filter);
43
+
} else {
44
+
filter = filterState[0];
45
+
setFilter = filterState[1];
46
+
}
47
+
48
+
const [selectedTags, setSelectedTags] = React.useState(new Set<string>());
49
+
const selectTag = React.useCallback(
50
+
(tag: string) => {
51
+
const newState = new Set(selectedTags);
52
+
if (validTags.includes(tag)) newState.add(tag);
53
+
setSelectedTags(newState);
54
+
},
55
+
[selectedTags]
56
+
);
57
+
58
+
const sorted = Object.values(extensions).sort((a, b) => {
59
+
const aName = a.manifest.meta?.name ?? a.id;
60
+
const bName = b.manifest.meta?.name ?? b.id;
61
+
return aName.localeCompare(bName);
62
+
});
63
+
64
+
const filtered = sorted.filter(
65
+
(ext) =>
66
+
(query === "" ||
67
+
ext.manifest.id?.toLowerCase().includes(query) ||
68
+
ext.manifest.meta?.name?.toLowerCase().includes(query) ||
69
+
ext.manifest.meta?.tagline?.toLowerCase().includes(query) ||
70
+
(ext.manifest?.settings != null &&
71
+
Object.entries(ext.manifest.settings).some(([key, setting]) =>
72
+
(setting.displayName ?? key).toLowerCase().includes(query)
73
+
)) ||
74
+
(ext.manifest?.meta?.authors != null &&
75
+
ext.manifest.meta.authors.some((author) =>
76
+
(typeof author === "string" ? author : author.name).toLowerCase().includes(query)
77
+
)) ||
78
+
ext.manifest.meta?.description?.toLowerCase().includes(query)) &&
79
+
[...selectedTags.values()].every((tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag)) &&
80
+
// This seems very bad, sorry
81
+
!(
82
+
(!(filter & Filter.Core) && ext.source.type === ExtensionLoadSource.Core) ||
83
+
(!(filter & Filter.Normal) && ext.source.type === ExtensionLoadSource.Normal) ||
84
+
(!(filter & Filter.Developer) && ext.source.type === ExtensionLoadSource.Developer) ||
85
+
(!(filter & Filter.Enabled) && MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) ||
86
+
(!(filter & Filter.Disabled) && !MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) ||
87
+
(!(filter & Filter.Installed) && ext.state !== ExtensionState.NotDownloaded) ||
88
+
(!(filter & Filter.Repository) && ext.state === ExtensionState.NotDownloaded)
89
+
) &&
90
+
(filter & Filter.Incompatible ||
91
+
ext.compat === ExtensionCompat.Compatible ||
92
+
(ext.compat === ExtensionCompat.InvalidApiLevel && ext.hasUpdate)) &&
93
+
(filter & Filter.Deprecated ||
94
+
ext.manifest?.meta?.deprecated !== true ||
95
+
ext.state !== ExtensionState.NotDownloaded)
96
+
);
97
+
98
+
// Prioritize extensions with updates
99
+
const filteredWithUpdates = filtered.filter((ext) => ext!.hasUpdate);
100
+
const filteredWithoutUpdates = filtered.filter((ext) => !ext!.hasUpdate);
101
+
102
+
return (
103
+
<>
104
+
<SearchBar
105
+
size={SearchBar.Sizes.MEDIUM}
106
+
query={query}
107
+
onChange={(v: string) => setQuery(v.toLowerCase())}
108
+
onClear={() => setQuery("")}
109
+
autoFocus={true}
110
+
autoComplete="off"
111
+
inputProps={{
112
+
autoCapitalize: "none",
113
+
autoCorrect: "off",
114
+
spellCheck: "false"
115
+
}}
116
+
/>
117
+
<FilterBar filter={filter} setFilter={setFilter} selectedTags={selectedTags} setSelectedTags={setSelectedTags} />
118
+
119
+
{filteredWithUpdates.length > 0 && (
120
+
<HelpMessage
121
+
icon={CircleInformationIcon}
122
+
text="Extension updates are available"
123
+
className="moonbase-extension-update-section"
124
+
>
125
+
<div className="moonbase-help-message-buttons">
126
+
<Button
127
+
color={Button.Colors.BRAND}
128
+
size={Button.Sizes.TINY}
129
+
disabled={hitUpdateAll}
130
+
onClick={() => {
131
+
setHitUpdateAll(true);
132
+
MoonbaseSettingsStore.updateAllExtensions();
133
+
}}
134
+
>
135
+
Update all
136
+
</Button>
137
+
<PanelButton
138
+
icon={XSmallIcon}
139
+
onClick={() => {
140
+
MoonbaseSettingsStore.dismissAllExtensionUpdates();
141
+
}}
142
+
/>
143
+
</div>
144
+
</HelpMessage>
145
+
)}
146
+
147
+
{filteredWithUpdates.map((ext) => (
148
+
<ErrorBoundary>
149
+
<ExtensionCard uniqueId={ext.uniqueId} key={ext.uniqueId} selectTag={selectTag} />
150
+
</ErrorBoundary>
151
+
))}
152
+
{filteredWithUpdates.length > 0 && filteredWithoutUpdates.length > 0 && (
153
+
<FormDivider className="moonbase-update-divider" />
154
+
)}
155
+
{filteredWithoutUpdates.map((ext) => (
156
+
<ErrorBoundary>
157
+
<ExtensionCard uniqueId={ext.uniqueId} key={ext.uniqueId} selectTag={selectTag} />
158
+
</ErrorBoundary>
159
+
))}
160
+
</>
161
+
);
162
+
}
+205
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/info.tsx
+205
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/info.tsx
···
1
+
import { ExtensionTag } from "@moonlight-mod/types";
2
+
import { MoonbaseExtension } from "../../../types";
3
+
4
+
import React from "@moonlight-mod/wp/react";
5
+
import { Text } from "@moonlight-mod/wp/discord/components/common/index";
6
+
7
+
type Dependency = {
8
+
id: string;
9
+
type: DependencyType;
10
+
};
11
+
12
+
enum DependencyType {
13
+
Dependency = "dependency",
14
+
Optional = "optional",
15
+
Incompatible = "incompatible"
16
+
}
17
+
18
+
export const tagNames: Record<ExtensionTag, string> = {
19
+
[ExtensionTag.Accessibility]: "Accessibility",
20
+
[ExtensionTag.Appearance]: "Appearance",
21
+
[ExtensionTag.Chat]: "Chat",
22
+
[ExtensionTag.Commands]: "Commands",
23
+
[ExtensionTag.ContextMenu]: "Context Menu",
24
+
[ExtensionTag.DangerZone]: "Danger Zone",
25
+
[ExtensionTag.Development]: "Development",
26
+
[ExtensionTag.Fixes]: "Fixes",
27
+
[ExtensionTag.Fun]: "Fun",
28
+
[ExtensionTag.Markdown]: "Markdown",
29
+
[ExtensionTag.Voice]: "Voice",
30
+
[ExtensionTag.Privacy]: "Privacy",
31
+
[ExtensionTag.Profiles]: "Profiles",
32
+
[ExtensionTag.QualityOfLife]: "Quality of Life",
33
+
[ExtensionTag.Library]: "Library"
34
+
};
35
+
36
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
37
+
38
+
function InfoSection({ title, children }: { title: string; children: React.ReactNode }) {
39
+
return (
40
+
<div
41
+
style={{
42
+
marginRight: "1em"
43
+
}}
44
+
>
45
+
<Text variant="eyebrow" className="moonlight-card-info-header">
46
+
{title}
47
+
</Text>
48
+
49
+
<Text variant="text-sm/normal">{children}</Text>
50
+
</div>
51
+
);
52
+
}
53
+
54
+
function Badge({
55
+
color,
56
+
children,
57
+
style = {},
58
+
onClick
59
+
}: {
60
+
color: string;
61
+
children: React.ReactNode;
62
+
style?: React.CSSProperties;
63
+
onClick?: () => void;
64
+
}) {
65
+
if (onClick) style.cursor ??= "pointer";
66
+
return (
67
+
<span
68
+
className="moonlight-card-badge"
69
+
style={
70
+
{
71
+
"--badge-color": color,
72
+
...style
73
+
} as React.CSSProperties
74
+
}
75
+
onClick={onClick}
76
+
>
77
+
{children}
78
+
</span>
79
+
);
80
+
}
81
+
82
+
export default function ExtensionInfo({
83
+
ext,
84
+
selectTag
85
+
}: {
86
+
ext: MoonbaseExtension;
87
+
selectTag: (tag: string) => void;
88
+
}) {
89
+
const authors = ext.manifest?.meta?.authors;
90
+
const tags = ext.manifest?.meta?.tags;
91
+
const version = ext.manifest?.version;
92
+
93
+
const dependencies: Dependency[] = [];
94
+
const incompatible: Dependency[] = [];
95
+
96
+
if (ext.manifest.dependencies != null) {
97
+
dependencies.push(
98
+
...ext.manifest.dependencies.map((dep) => ({
99
+
id: dep,
100
+
type: DependencyType.Dependency
101
+
}))
102
+
);
103
+
}
104
+
105
+
if (ext.manifest.suggested != null) {
106
+
dependencies.push(
107
+
...ext.manifest.suggested.map((dep) => ({
108
+
id: dep,
109
+
type: DependencyType.Optional
110
+
}))
111
+
);
112
+
}
113
+
114
+
if (ext.manifest.incompatible != null) {
115
+
incompatible.push(
116
+
...ext.manifest.incompatible.map((dep) => ({
117
+
id: dep,
118
+
type: DependencyType.Incompatible
119
+
}))
120
+
);
121
+
}
122
+
123
+
return (
124
+
<>
125
+
{authors != null && (
126
+
<InfoSection title="Authors">
127
+
{authors.map((author, i) => {
128
+
const comma = i !== authors.length - 1 ? ", " : "";
129
+
if (typeof author === "string") {
130
+
return (
131
+
<span key={i}>
132
+
{author}
133
+
{comma}
134
+
</span>
135
+
);
136
+
} else {
137
+
// TODO: resolve IDs
138
+
return (
139
+
<span key={i}>
140
+
{author.name}
141
+
{comma}
142
+
</span>
143
+
);
144
+
}
145
+
})}
146
+
</InfoSection>
147
+
)}
148
+
149
+
{tags != null && (
150
+
<InfoSection title="Tags">
151
+
{tags.map((tag, i) => {
152
+
const name = tagNames[tag];
153
+
let color = "var(--bg-mod-strong)";
154
+
let style;
155
+
if (tag === ExtensionTag.DangerZone) {
156
+
color = "var(--red-460)";
157
+
style = { color: "var(--primary-230)" };
158
+
}
159
+
160
+
return (
161
+
<Badge key={i} color={color} style={style} onClick={() => selectTag(tag)}>
162
+
{name}
163
+
</Badge>
164
+
);
165
+
})}
166
+
</InfoSection>
167
+
)}
168
+
169
+
{dependencies.length > 0 && (
170
+
<InfoSection title="Dependencies">
171
+
{dependencies.map((dep) => {
172
+
const name = MoonbaseSettingsStore.tryGetExtensionName(dep.id);
173
+
174
+
// TODO: figure out a decent way to distinguish suggested
175
+
return (
176
+
<Badge color="var(--bg-mod-strong)" key={dep.id}>
177
+
{name}
178
+
</Badge>
179
+
);
180
+
})}
181
+
</InfoSection>
182
+
)}
183
+
184
+
{incompatible.length > 0 && (
185
+
<InfoSection title="Incompatible">
186
+
{incompatible.map((dep) => {
187
+
const name = MoonbaseSettingsStore.tryGetExtensionName(dep.id);
188
+
189
+
return (
190
+
<Badge color="var(--bg-mod-strong)" key={dep.id}>
191
+
{name}
192
+
</Badge>
193
+
);
194
+
})}
195
+
</InfoSection>
196
+
)}
197
+
198
+
{version != null && (
199
+
<InfoSection title="Version">
200
+
<span>{version}</span>
201
+
</InfoSection>
202
+
)}
203
+
</>
204
+
);
205
+
}
+211
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/popup.tsx
+211
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/popup.tsx
···
1
+
// TODO: clean up the styling here
2
+
import React from "@moonlight-mod/wp/react";
3
+
import { MoonbaseExtension } from "core-extensions/src/moonbase/types";
4
+
import { openModalLazy, useModalsStore, closeModal } from "@moonlight-mod/wp/discord/modules/modals/Modals";
5
+
import { SingleSelect, Text } from "@moonlight-mod/wp/discord/components/common/index";
6
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
7
+
import { ExtensionLoadSource } from "@moonlight-mod/types";
8
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
9
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
10
+
11
+
let ConfirmModal: typeof import("@moonlight-mod/wp/discord/components/modals/ConfirmModal").default;
12
+
13
+
function close() {
14
+
const ModalStore = useModalsStore.getState();
15
+
closeModal(ModalStore.default[0].key);
16
+
}
17
+
18
+
// do this to avoid a hard dependency
19
+
function lazyLoad() {
20
+
if (!ConfirmModal) {
21
+
ConfirmModal = spacepack.require("discord/components/modals/ConfirmModal").default;
22
+
}
23
+
}
24
+
25
+
const presentableLoadSources: Record<ExtensionLoadSource, string> = {
26
+
[ExtensionLoadSource.Developer]: "Local extension", // should never show up
27
+
[ExtensionLoadSource.Core]: "Core extension",
28
+
[ExtensionLoadSource.Normal]: "Extension repository"
29
+
};
30
+
31
+
function ExtensionSelect({
32
+
id,
33
+
candidates,
34
+
option,
35
+
setOption
36
+
}: {
37
+
id: string;
38
+
candidates: MoonbaseExtension[];
39
+
option: string | undefined;
40
+
setOption: (pick: string | undefined) => void;
41
+
}) {
42
+
return (
43
+
<SingleSelect
44
+
key={id}
45
+
autofocus={false}
46
+
value={option}
47
+
options={candidates.map((candidate) => {
48
+
return {
49
+
value: candidate.uniqueId.toString(),
50
+
label:
51
+
candidate.source.url ?? presentableLoadSources[candidate.source.type] ?? candidate.manifest.version ?? ""
52
+
};
53
+
})}
54
+
onChange={(value: string) => {
55
+
setOption(value);
56
+
}}
57
+
placeholder="Missing extension"
58
+
/>
59
+
);
60
+
}
61
+
62
+
function MissingExtensionPopup({
63
+
deps,
64
+
transitionState
65
+
}: {
66
+
deps: Record<string, MoonbaseExtension[]>;
67
+
transitionState: number | null;
68
+
}) {
69
+
lazyLoad();
70
+
const amountNotAvailable = Object.values(deps).filter((candidates) => candidates.length === 0).length;
71
+
72
+
const [options, setOptions] = React.useState<Record<string, string | undefined>>(
73
+
Object.fromEntries(
74
+
Object.entries(deps).map(([id, candidates]) => [
75
+
id,
76
+
candidates.length > 0 ? candidates[0].uniqueId.toString() : undefined
77
+
])
78
+
)
79
+
);
80
+
81
+
return (
82
+
<ConfirmModal
83
+
body={
84
+
<Flex
85
+
style={{
86
+
gap: "20px"
87
+
}}
88
+
direction={Flex.Direction.VERTICAL}
89
+
>
90
+
<Text variant="text-md/normal">
91
+
This extension depends on other extensions which are not downloaded. Choose which extensions to download.
92
+
</Text>
93
+
94
+
{amountNotAvailable > 0 && (
95
+
<Text variant="text-md/normal">
96
+
{amountNotAvailable} extension
97
+
{amountNotAvailable > 1 ? "s" : ""} could not be found, and must be installed manually.
98
+
</Text>
99
+
)}
100
+
101
+
<div
102
+
style={{
103
+
display: "grid",
104
+
gridTemplateColumns: "1fr 2fr",
105
+
gap: "10px"
106
+
}}
107
+
>
108
+
{Object.entries(deps).map(([id, candidates], i) => (
109
+
<>
110
+
<Text
111
+
variant="text-md/normal"
112
+
style={{
113
+
alignSelf: "center",
114
+
wordBreak: "break-word"
115
+
}}
116
+
>
117
+
{MoonbaseSettingsStore.tryGetExtensionName(id)}
118
+
</Text>
119
+
120
+
<ExtensionSelect
121
+
id={id}
122
+
candidates={candidates}
123
+
option={options[id]}
124
+
setOption={(pick) =>
125
+
setOptions((prev) => ({
126
+
...prev,
127
+
[id]: pick
128
+
}))
129
+
}
130
+
/>
131
+
</>
132
+
))}
133
+
</div>
134
+
</Flex>
135
+
}
136
+
cancelText="Cancel"
137
+
confirmText="Install"
138
+
onCancel={close}
139
+
onConfirm={() => {
140
+
close();
141
+
142
+
for (const pick of Object.values(options)) {
143
+
if (pick != null) {
144
+
MoonbaseSettingsStore.installExtension(parseInt(pick));
145
+
}
146
+
}
147
+
}}
148
+
title="Extension dependencies"
149
+
transitionState={transitionState}
150
+
/>
151
+
);
152
+
}
153
+
154
+
export async function doMissingExtensionPopup(deps: Record<string, MoonbaseExtension[]>) {
155
+
await openModalLazy(async () => {
156
+
return ({ transitionState }: { transitionState: number | null }) => {
157
+
return <MissingExtensionPopup transitionState={transitionState} deps={deps} />;
158
+
};
159
+
});
160
+
}
161
+
162
+
function GenericExtensionPopup({
163
+
title,
164
+
content,
165
+
transitionState,
166
+
uniqueId,
167
+
cb
168
+
}: {
169
+
title: string;
170
+
content: string;
171
+
transitionState: number | null;
172
+
uniqueId: number;
173
+
cb: () => void;
174
+
}) {
175
+
lazyLoad();
176
+
177
+
return (
178
+
<ConfirmModal
179
+
title={title}
180
+
body={
181
+
<Flex>
182
+
<Text variant="text-md/normal">{content}</Text>
183
+
</Flex>
184
+
}
185
+
confirmText="Yes"
186
+
cancelText="No"
187
+
onCancel={close}
188
+
onConfirm={() => {
189
+
close();
190
+
cb();
191
+
}}
192
+
transitionState={transitionState}
193
+
/>
194
+
);
195
+
}
196
+
197
+
export async function doGenericExtensionPopup(title: string, content: string, uniqueId: number, cb: () => void) {
198
+
await openModalLazy(async () => {
199
+
return ({ transitionState }: { transitionState: number | null }) => {
200
+
return (
201
+
<GenericExtensionPopup
202
+
title={title}
203
+
content={content}
204
+
transitionState={transitionState}
205
+
uniqueId={uniqueId}
206
+
cb={cb}
207
+
/>
208
+
);
209
+
};
210
+
});
211
+
}
+418
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
+418
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
···
1
+
import {
2
+
ExtensionSettingType,
3
+
ExtensionSettingsManifest,
4
+
MultiSelectSettingType,
5
+
NumberSettingType,
6
+
SelectOption,
7
+
SelectSettingType
8
+
} from "@moonlight-mod/types/config";
9
+
10
+
import { ExtensionState, MoonbaseExtension } from "../../../types";
11
+
12
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
13
+
import React from "@moonlight-mod/wp/react";
14
+
import {
15
+
FormSwitch,
16
+
FormItem,
17
+
FormText,
18
+
TextInput,
19
+
Slider,
20
+
TextArea,
21
+
Tooltip,
22
+
Clickable,
23
+
CircleXIcon,
24
+
Text,
25
+
SingleSelect,
26
+
Button,
27
+
useVariableSelect,
28
+
multiSelect,
29
+
Select as DiscordSelect,
30
+
NumberInputStepper
31
+
} from "@moonlight-mod/wp/discord/components/common/index";
32
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
33
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
34
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
35
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
36
+
import ErrorBoundary from "@moonlight-mod/wp/common_ErrorBoundary";
37
+
38
+
let GuildSettingsRoleEditClasses: any;
39
+
spacepack
40
+
.lazyLoad(
41
+
"renderArtisanalHack",
42
+
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"GuildSettings"/,
43
+
/webpackId:(\d+),name:"GuildSettings"/
44
+
)
45
+
.then(
46
+
() =>
47
+
(GuildSettingsRoleEditClasses = spacepack.require(
48
+
"discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"
49
+
))
50
+
);
51
+
52
+
type SettingsProps = {
53
+
ext: MoonbaseExtension;
54
+
name: string;
55
+
setting: ExtensionSettingsManifest;
56
+
disabled: boolean;
57
+
};
58
+
type SettingsComponent = React.ComponentType<SettingsProps>;
59
+
60
+
const Margins = spacepack.require("discord/styles/shared/Margins.css");
61
+
62
+
function markdownify(str: string) {
63
+
return MarkupUtils.parse(str, true, {
64
+
hideSimpleEmbedContent: true,
65
+
allowLinks: true
66
+
});
67
+
}
68
+
69
+
function useConfigEntry<T>(uniqueId: number, name: string) {
70
+
return useStateFromStores(
71
+
[MoonbaseSettingsStore],
72
+
() => {
73
+
return {
74
+
value: MoonbaseSettingsStore.getExtensionConfig<T>(uniqueId, name),
75
+
displayName: MoonbaseSettingsStore.getExtensionConfigName(uniqueId, name),
76
+
description: MoonbaseSettingsStore.getExtensionConfigDescription(uniqueId, name)
77
+
};
78
+
},
79
+
[uniqueId, name]
80
+
);
81
+
}
82
+
83
+
function Boolean({ ext, name, setting, disabled }: SettingsProps) {
84
+
const { value, displayName, description } = useConfigEntry<boolean>(ext.uniqueId, name);
85
+
86
+
return (
87
+
<FormSwitch
88
+
value={value ?? false}
89
+
hideBorder={true}
90
+
disabled={disabled}
91
+
onChange={(value: boolean) => {
92
+
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
93
+
}}
94
+
note={description != null ? markdownify(description) : undefined}
95
+
className={`${Margins.marginReset} ${Margins.marginTop20}`}
96
+
>
97
+
{displayName}
98
+
</FormSwitch>
99
+
);
100
+
}
101
+
102
+
function Number({ ext, name, setting, disabled }: SettingsProps) {
103
+
const { value, displayName, description } = useConfigEntry<number>(ext.uniqueId, name);
104
+
105
+
const castedSetting = setting as NumberSettingType;
106
+
const min = castedSetting.min;
107
+
const max = castedSetting.max;
108
+
109
+
const onChange = (value: number) => {
110
+
const rounded = min == null || max == null ? Math.round(value) : Math.max(min, Math.min(max, Math.round(value)));
111
+
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, rounded);
112
+
};
113
+
114
+
return (
115
+
<FormItem className={Margins.marginTop20} title={displayName}>
116
+
{min == null || max == null ? (
117
+
<Flex justify={Flex.Justify.BETWEEN} direction={Flex.Direction.HORIZONTAL}>
118
+
{description && <FormText>{markdownify(description)}</FormText>}
119
+
<NumberInputStepper value={value ?? 0} onChange={onChange} />
120
+
</Flex>
121
+
) : (
122
+
<>
123
+
{description && <FormText>{markdownify(description)}</FormText>}
124
+
<Slider
125
+
initialValue={value ?? 0}
126
+
disabled={disabled}
127
+
minValue={min}
128
+
maxValue={max}
129
+
onValueChange={onChange}
130
+
onValueRender={(value: number) => `${Math.round(value)}`}
131
+
/>
132
+
</>
133
+
)}
134
+
</FormItem>
135
+
);
136
+
}
137
+
138
+
function String({ ext, name, setting, disabled }: SettingsProps) {
139
+
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
140
+
141
+
return (
142
+
<FormItem className={Margins.marginTop20} title={displayName}>
143
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
144
+
<TextInput
145
+
value={value ?? ""}
146
+
disabled={disabled}
147
+
onChange={(value: string) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)}
148
+
/>
149
+
</FormItem>
150
+
);
151
+
}
152
+
153
+
function MultilineString({ ext, name, setting, disabled }: SettingsProps) {
154
+
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
155
+
156
+
return (
157
+
<FormItem className={Margins.marginTop20} title={displayName}>
158
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
159
+
<TextArea
160
+
rows={5}
161
+
value={value ?? ""}
162
+
disabled={disabled}
163
+
className={"moonbase-resizeable"}
164
+
onChange={(value: string) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)}
165
+
/>
166
+
</FormItem>
167
+
);
168
+
}
169
+
170
+
function Select({ ext, name, setting, disabled }: SettingsProps) {
171
+
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
172
+
173
+
const castedSetting = setting as SelectSettingType;
174
+
const options = castedSetting.options;
175
+
176
+
return (
177
+
<FormItem className={Margins.marginTop20} title={displayName}>
178
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
179
+
<SingleSelect
180
+
autofocus={false}
181
+
clearable={false}
182
+
value={value ?? ""}
183
+
options={options.map((o: SelectOption) => (typeof o === "string" ? { value: o, label: o } : o))}
184
+
onChange={(value: string) => {
185
+
if (disabled) return;
186
+
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
187
+
}}
188
+
/>
189
+
</FormItem>
190
+
);
191
+
}
192
+
193
+
function MultiSelect({ ext, name, setting, disabled }: SettingsProps) {
194
+
const { value, displayName, description } = useConfigEntry<string | string[]>(ext.uniqueId, name);
195
+
196
+
const castedSetting = setting as MultiSelectSettingType;
197
+
const options = castedSetting.options;
198
+
199
+
return (
200
+
<FormItem className={Margins.marginTop20} title={displayName}>
201
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
202
+
<DiscordSelect
203
+
autofocus={false}
204
+
clearable={false}
205
+
closeOnSelect={false}
206
+
options={options.map((o: SelectOption) => (typeof o === "string" ? { value: o, label: o } : o))}
207
+
{...useVariableSelect({
208
+
onSelectInteraction: multiSelect,
209
+
value: value == null ? new Set() : new Set(Array.isArray(value) ? value : [value]),
210
+
onChange: (value: string) => {
211
+
if (disabled) return;
212
+
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, Array.from(value));
213
+
}
214
+
})}
215
+
/>
216
+
</FormItem>
217
+
);
218
+
}
219
+
220
+
function RemoveEntryButton({ onClick, disabled }: { onClick: () => void; disabled: boolean }) {
221
+
return (
222
+
<div className={GuildSettingsRoleEditClasses.removeButtonContainer}>
223
+
<Tooltip text="Remove entry" position="top">
224
+
{(props: any) => (
225
+
<Clickable {...props} className={GuildSettingsRoleEditClasses.removeButton} onClick={onClick}>
226
+
<CircleXIcon width={16} height={16} />
227
+
</Clickable>
228
+
)}
229
+
</Tooltip>
230
+
</div>
231
+
);
232
+
}
233
+
234
+
function List({ ext, name, setting, disabled }: SettingsProps) {
235
+
const { value, displayName, description } = useConfigEntry<string[]>(ext.uniqueId, name);
236
+
237
+
const entries = value ?? [];
238
+
const updateConfig = () => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, entries);
239
+
240
+
return (
241
+
<FormItem className={Margins.marginTop20} title={displayName}>
242
+
{description && <FormText className={Margins.marginBottom4}>{markdownify(description)}</FormText>}
243
+
<Flex direction={Flex.Direction.VERTICAL}>
244
+
{entries.map((val, i) => (
245
+
// FIXME: stylesheets
246
+
<div
247
+
key={i}
248
+
style={{
249
+
display: "grid",
250
+
height: "32px",
251
+
gap: "8px",
252
+
gridTemplateColumns: "1fr 32px",
253
+
alignItems: "center"
254
+
}}
255
+
>
256
+
<TextInput
257
+
size={TextInput.Sizes.MINI}
258
+
value={val}
259
+
disabled={disabled}
260
+
onChange={(newVal: string) => {
261
+
entries[i] = newVal;
262
+
updateConfig();
263
+
}}
264
+
/>
265
+
<RemoveEntryButton
266
+
disabled={disabled}
267
+
onClick={() => {
268
+
entries.splice(i, 1);
269
+
updateConfig();
270
+
}}
271
+
/>
272
+
</div>
273
+
))}
274
+
275
+
<Button
276
+
look={Button.Looks.FILLED}
277
+
color={Button.Colors.GREEN}
278
+
size={Button.Sizes.SMALL}
279
+
disabled={disabled}
280
+
className={Margins.marginTop8}
281
+
onClick={() => {
282
+
entries.push("");
283
+
updateConfig();
284
+
}}
285
+
>
286
+
Add new entry
287
+
</Button>
288
+
</Flex>
289
+
</FormItem>
290
+
);
291
+
}
292
+
293
+
function Dictionary({ ext, name, setting, disabled }: SettingsProps) {
294
+
const { value, displayName, description } = useConfigEntry<Record<string, string>>(ext.uniqueId, name);
295
+
296
+
const entries = Object.entries(value ?? {});
297
+
const updateConfig = () => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, Object.fromEntries(entries));
298
+
299
+
return (
300
+
<FormItem className={Margins.marginTop20} title={displayName}>
301
+
{description && <FormText className={Margins.marginBottom4}>{markdownify(description)}</FormText>}
302
+
<Flex direction={Flex.Direction.VERTICAL}>
303
+
{entries.map(([key, val], i) => (
304
+
// FIXME: stylesheets
305
+
<div
306
+
key={i}
307
+
style={{
308
+
display: "grid",
309
+
height: "32px",
310
+
gap: "8px",
311
+
gridTemplateColumns: "1fr 1fr 32px",
312
+
alignItems: "center"
313
+
}}
314
+
>
315
+
<TextInput
316
+
size={TextInput.Sizes.MINI}
317
+
value={key}
318
+
disabled={disabled}
319
+
onChange={(newKey: string) => {
320
+
entries[i][0] = newKey;
321
+
updateConfig();
322
+
}}
323
+
/>
324
+
<TextInput
325
+
size={TextInput.Sizes.MINI}
326
+
value={val}
327
+
disabled={disabled}
328
+
onChange={(newValue: string) => {
329
+
entries[i][1] = newValue;
330
+
updateConfig();
331
+
}}
332
+
/>
333
+
<RemoveEntryButton
334
+
disabled={disabled}
335
+
onClick={() => {
336
+
entries.splice(i, 1);
337
+
updateConfig();
338
+
}}
339
+
/>
340
+
</div>
341
+
))}
342
+
343
+
<Button
344
+
look={Button.Looks.FILLED}
345
+
color={Button.Colors.GREEN}
346
+
size={Button.Sizes.SMALL}
347
+
className={Margins.marginTop8}
348
+
disabled={disabled}
349
+
onClick={() => {
350
+
entries.push([`entry-${entries.length}`, ""]);
351
+
updateConfig();
352
+
}}
353
+
>
354
+
Add new entry
355
+
</Button>
356
+
</Flex>
357
+
</FormItem>
358
+
);
359
+
}
360
+
361
+
function Custom({ ext, name, setting, disabled }: SettingsProps) {
362
+
const { value, displayName } = useConfigEntry<any>(ext.uniqueId, name);
363
+
364
+
const { component: Component } = useStateFromStores(
365
+
[MoonbaseSettingsStore],
366
+
() => {
367
+
return {
368
+
component: MoonbaseSettingsStore.getExtensionConfigComponent(ext.id, name)
369
+
};
370
+
},
371
+
[ext.uniqueId, name]
372
+
);
373
+
374
+
if (Component == null) {
375
+
return (
376
+
<Text variant="text-md/normal">{`Custom setting "${displayName}" is missing a component. Perhaps the extension is not installed?`}</Text>
377
+
);
378
+
}
379
+
380
+
return (
381
+
<ErrorBoundary>
382
+
<Component value={value} setValue={(value) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)} />
383
+
</ErrorBoundary>
384
+
);
385
+
}
386
+
387
+
function Setting({ ext, name, setting, disabled }: SettingsProps) {
388
+
const elements: Partial<Record<ExtensionSettingType, SettingsComponent>> = {
389
+
[ExtensionSettingType.Boolean]: Boolean,
390
+
[ExtensionSettingType.Number]: Number,
391
+
[ExtensionSettingType.String]: String,
392
+
[ExtensionSettingType.MultilineString]: MultilineString,
393
+
[ExtensionSettingType.Select]: Select,
394
+
[ExtensionSettingType.MultiSelect]: MultiSelect,
395
+
[ExtensionSettingType.List]: List,
396
+
[ExtensionSettingType.Dictionary]: Dictionary,
397
+
[ExtensionSettingType.Custom]: Custom
398
+
};
399
+
const element = elements[setting.type];
400
+
if (element == null) return <></>;
401
+
return React.createElement(element, { ext, name, setting, disabled });
402
+
}
403
+
404
+
export default function Settings({ ext }: { ext: MoonbaseExtension }) {
405
+
return (
406
+
<Flex className="moonbase-settings" direction={Flex.Direction.VERTICAL}>
407
+
{Object.entries(ext.settingsOverride ?? ext.manifest.settings!).map(([name, setting]) => (
408
+
<Setting
409
+
ext={ext}
410
+
key={name}
411
+
name={name}
412
+
setting={setting}
413
+
disabled={ext.state === ExtensionState.NotDownloaded}
414
+
/>
415
+
))}
416
+
</Flex>
417
+
);
418
+
}
+85
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
+85
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import { Text, TabBar } from "@moonlight-mod/wp/discord/components/common/index";
3
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
4
+
import { UserSettingsModalStore } from "@moonlight-mod/wp/common_stores";
5
+
6
+
import ExtensionsPage from "./extensions";
7
+
import ConfigPage from "./config";
8
+
import AboutPage from "./about";
9
+
import Update from "./update";
10
+
import RestartAdviceMessage from "./RestartAdvice";
11
+
import { Divider } from "@moonlight-mod/wp/discord/components/common/BaseHeaderBar";
12
+
import HeaderBarClasses from "@moonlight-mod/wp/discord/components/common/HeaderBar.css";
13
+
import PeoplePageClasses from "@moonlight-mod/wp/discord/modules/people/web/PeoplePage.css";
14
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
15
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
16
+
17
+
export const pages: {
18
+
id: string;
19
+
name: string;
20
+
element: React.FunctionComponent;
21
+
}[] = [
22
+
{
23
+
id: "extensions",
24
+
name: "Extensions",
25
+
element: ExtensionsPage
26
+
},
27
+
{
28
+
id: "config",
29
+
name: "Config",
30
+
element: ConfigPage
31
+
},
32
+
{
33
+
id: "about",
34
+
name: "About",
35
+
element: AboutPage
36
+
}
37
+
];
38
+
39
+
export function Moonbase(props: { initialTab?: number } = {}) {
40
+
const subsection = useStateFromStores([UserSettingsModalStore], () => UserSettingsModalStore.getSubsection() ?? 0);
41
+
const setSubsection = React.useCallback(
42
+
(to: string) => {
43
+
if (subsection !== to) UserSettingsModalActionCreators.setSection("moonbase", to);
44
+
},
45
+
[subsection]
46
+
);
47
+
48
+
React.useEffect(
49
+
() => () => {
50
+
// Normally there's an onSettingsClose prop you can set but we don't expose it and I don't care enough to add support for it right now
51
+
UserSettingsModalActionCreators.clearSubsection("moonbase");
52
+
},
53
+
[]
54
+
);
55
+
56
+
return (
57
+
<>
58
+
<div className={`${HeaderBarClasses.children} ${Margins.marginBottom20}`}>
59
+
<Text className={HeaderBarClasses.titleWrapper} variant="heading-lg/semibold" tag="h2">
60
+
Moonbase
61
+
</Text>
62
+
<Divider />
63
+
<TabBar
64
+
selectedItem={subsection}
65
+
onItemSelect={setSubsection}
66
+
type="top-pill"
67
+
className={PeoplePageClasses.tabBar}
68
+
>
69
+
{pages.map((page, i) => (
70
+
<TabBar.Item key={page.id} id={i} className={PeoplePageClasses.item}>
71
+
{page.name}
72
+
</TabBar.Item>
73
+
))}
74
+
</TabBar>
75
+
</div>
76
+
77
+
<RestartAdviceMessage />
78
+
<Update />
79
+
80
+
{React.createElement(pages[subsection].element)}
81
+
</>
82
+
);
83
+
}
84
+
85
+
export { RestartAdviceMessage, Update };
+124
packages/core-extensions/src/moonbase/webpackModules/ui/update.tsx
+124
packages/core-extensions/src/moonbase/webpackModules/ui/update.tsx
···
1
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
2
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
3
+
import React from "@moonlight-mod/wp/react";
4
+
import { UpdateState } from "../../types";
5
+
import HelpMessage from "./HelpMessage";
6
+
import { MoonlightBranch } from "@moonlight-mod/types";
7
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
8
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
9
+
import {
10
+
Button,
11
+
Text,
12
+
ModalRoot,
13
+
ModalSize,
14
+
ModalContent,
15
+
ModalHeader,
16
+
Heading,
17
+
ModalCloseButton,
18
+
openModal
19
+
} from "@moonlight-mod/wp/discord/components/common/index";
20
+
import MarkupClasses from "@moonlight-mod/wp/discord/modules/messages/web/Markup.css";
21
+
import ThemeDarkIcon from "@moonlight-mod/wp/moonbase_ThemeDarkIcon";
22
+
23
+
const strings: Record<UpdateState, string> = {
24
+
[UpdateState.Ready]: "A new version of moonlight is available.",
25
+
[UpdateState.Working]: "Updating moonlight...",
26
+
[UpdateState.Installed]: "Updated. Restart Discord to apply changes.",
27
+
[UpdateState.Failed]: "Failed to update moonlight. Please use the installer instead."
28
+
};
29
+
30
+
function MoonlightChangelog({
31
+
changelog,
32
+
version,
33
+
transitionState,
34
+
onClose
35
+
}: {
36
+
changelog: string;
37
+
version: string;
38
+
transitionState: number | null;
39
+
onClose: () => void;
40
+
}) {
41
+
return (
42
+
<ModalRoot transitionState={transitionState} size={ModalSize.DYNAMIC}>
43
+
<ModalHeader>
44
+
<Flex.Child grow={1} shrink={1}>
45
+
<Heading variant="heading-lg/semibold">moonlight</Heading>
46
+
<Text variant="text-xs/normal">{version}</Text>
47
+
</Flex.Child>
48
+
49
+
<Flex.Child grow={0}>
50
+
<ModalCloseButton onClick={onClose} />
51
+
</Flex.Child>
52
+
</ModalHeader>
53
+
54
+
<ModalContent>
55
+
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ padding: "1rem" }}>
56
+
{MarkupUtils.parse(changelog, true, {
57
+
allowHeading: true,
58
+
allowList: true,
59
+
allowLinks: true
60
+
})}
61
+
</Text>
62
+
</ModalContent>
63
+
</ModalRoot>
64
+
);
65
+
}
66
+
67
+
export default function Update() {
68
+
const [newVersion, state] = useStateFromStores([MoonbaseSettingsStore], () => [
69
+
MoonbaseSettingsStore.newVersion,
70
+
MoonbaseSettingsStore.updateState
71
+
]);
72
+
73
+
if (newVersion == null) return null;
74
+
75
+
return (
76
+
<HelpMessage text={strings[state]} className="moonbase-update-section" icon={ThemeDarkIcon}>
77
+
<div className="moonbase-help-message-buttons">
78
+
{moonlight.branch === MoonlightBranch.STABLE && (
79
+
<Button
80
+
look={Button.Looks.OUTLINED}
81
+
color={Button.Colors.CUSTOM}
82
+
size={Button.Sizes.TINY}
83
+
onClick={() => {
84
+
fetch(`https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/tags/${newVersion}/CHANGELOG.md`)
85
+
.then((r) => r.text())
86
+
.then((changelog) =>
87
+
openModal((modalProps) => {
88
+
return <MoonlightChangelog {...modalProps} changelog={changelog} version={newVersion} />;
89
+
})
90
+
);
91
+
}}
92
+
>
93
+
View changelog
94
+
</Button>
95
+
)}
96
+
97
+
{state === UpdateState.Installed && (
98
+
<Button
99
+
look={Button.Looks.OUTLINED}
100
+
color={Button.Colors.CUSTOM}
101
+
size={Button.Sizes.TINY}
102
+
onClick={() => {
103
+
MoonbaseSettingsStore.restartDiscord();
104
+
}}
105
+
>
106
+
Restart Discord
107
+
</Button>
108
+
)}
109
+
110
+
<Button
111
+
look={Button.Looks.OUTLINED}
112
+
color={Button.Colors.CUSTOM}
113
+
size={Button.Sizes.TINY}
114
+
disabled={state !== UpdateState.Ready}
115
+
onClick={() => {
116
+
MoonbaseSettingsStore.updateMoonlight();
117
+
}}
118
+
>
119
+
Update
120
+
</Button>
121
+
</div>
122
+
</HelpMessage>
123
+
);
124
+
}
+74
packages/core-extensions/src/moonbase/webpackModules/updates.tsx
+74
packages/core-extensions/src/moonbase/webpackModules/updates.tsx
···
1
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
2
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
3
+
import Notices from "@moonlight-mod/wp/notices_notices";
4
+
import { MoonlightBranch } from "@moonlight-mod/types";
5
+
import React from "@moonlight-mod/wp/react";
6
+
import ThemeDarkIcon from "@moonlight-mod/wp/moonbase_ThemeDarkIcon";
7
+
8
+
function plural(str: string, num: number) {
9
+
return `${str}${num > 1 ? "s" : ""}`;
10
+
}
11
+
12
+
function listener() {
13
+
if (
14
+
MoonbaseSettingsStore.shouldShowNotice &&
15
+
MoonbaseSettingsStore.getExtensionConfigRaw("moonbase", "updateBanner", true)
16
+
) {
17
+
MoonbaseSettingsStore.removeChangeListener(listener);
18
+
19
+
const version = MoonbaseSettingsStore.newVersion;
20
+
const extensionUpdateCount = Object.keys(MoonbaseSettingsStore.updates).length;
21
+
const hasExtensionUpdates = extensionUpdateCount > 0;
22
+
23
+
let message;
24
+
25
+
if (version != null) {
26
+
message =
27
+
moonlightNode.branch === MoonlightBranch.NIGHTLY
28
+
? `A new version of moonlight is available`
29
+
: `moonlight ${version} is available`;
30
+
}
31
+
32
+
if (hasExtensionUpdates) {
33
+
let concat = false;
34
+
if (message == null) {
35
+
message = "";
36
+
} else {
37
+
concat = true;
38
+
message += ", and ";
39
+
}
40
+
message += `${extensionUpdateCount} ${concat ? "" : "moonlight "}${plural(
41
+
"extension",
42
+
extensionUpdateCount
43
+
)} can be updated`;
44
+
}
45
+
46
+
if (message != null) message += ".";
47
+
48
+
Notices.addNotice({
49
+
element: (
50
+
<div className="moonbase-updates-notice_text-wrapper">
51
+
<ThemeDarkIcon size="sm" color="currentColor" />
52
+
{message}
53
+
</div>
54
+
),
55
+
color: "moonbase-updates-notice",
56
+
buttons: [
57
+
{
58
+
name: "Open Moonbase",
59
+
onClick: () => {
60
+
const { open } = spacepack.require("discord/actions/UserSettingsModalActionCreators").default;
61
+
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
62
+
open("moonbase-extensions");
63
+
} else {
64
+
open("moonbase", "0");
65
+
}
66
+
return true;
67
+
}
68
+
}
69
+
]
70
+
});
71
+
}
72
+
}
73
+
74
+
MoonbaseSettingsStore.addChangeListener(listener);
+12
packages/core-extensions/src/moonbase/wp.d.ts
+12
packages/core-extensions/src/moonbase/wp.d.ts
···
1
+
declare module "@moonlight-mod/wp/moonbase_ui" {
2
+
export * from "core-extensions/src/moonbase/webpackModules/ui";
3
+
}
4
+
5
+
declare module "@moonlight-mod/wp/moonbase_stores" {
6
+
export * from "core-extensions/src/moonbase/webpackModules/stores";
7
+
}
8
+
9
+
declare module "@moonlight-mod/wp/moonbase_ThemeDarkIcon" {
10
+
import ThemeDarkIcon from "core-extensions/src/moonbase/webpackModules/ThemeDarkIcon";
11
+
export = ThemeDarkIcon;
12
+
}
+186
packages/core-extensions/src/nativeFixes/host.ts
+186
packages/core-extensions/src/nativeFixes/host.ts
···
1
+
import { app, nativeTheme } from "electron";
2
+
import * as path from "node:path";
3
+
import * as fs from "node:fs/promises";
4
+
import * as fsSync from "node:fs";
5
+
import { parseTarGzip } from "nanotar";
6
+
7
+
const logger = moonlightHost.getLogger("nativeFixes/host");
8
+
const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(",");
9
+
10
+
moonlightHost.events.on("window-created", function (browserWindow) {
11
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "devtoolsThemeFix") ?? true) {
12
+
browserWindow.webContents.on("devtools-opened", () => {
13
+
if (!nativeTheme.shouldUseDarkColors) return;
14
+
nativeTheme.themeSource = "light";
15
+
setTimeout(() => {
16
+
nativeTheme.themeSource = "dark";
17
+
}, 100);
18
+
});
19
+
}
20
+
});
21
+
22
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "disableRendererBackgrounding") ?? true) {
23
+
// Discord already disables UseEcoQoSForBackgroundProcess and some other
24
+
// related features
25
+
app.commandLine.appendSwitch("disable-renderer-backgrounding");
26
+
app.commandLine.appendSwitch("disable-backgrounding-occluded-windows");
27
+
28
+
// already added on Windows, but not on other operating systems
29
+
app.commandLine.appendSwitch("disable-background-timer-throttling");
30
+
}
31
+
32
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "vulkan") ?? false) {
33
+
enabledFeatures.push("Vulkan", "DefaultANGLEVulkan", "VulkanFromANGLE");
34
+
}
35
+
36
+
if (process.platform === "linux") {
37
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxAutoscroll") ?? false) {
38
+
app.commandLine.appendSwitch("enable-blink-features", "MiddleClickAutoscroll");
39
+
}
40
+
41
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxSpeechDispatcher") ?? true) {
42
+
app.commandLine.appendSwitch("enable-speech-dispatcher");
43
+
}
44
+
45
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxHevcSupport") ?? true) {
46
+
enabledFeatures.push("PlatformHEVCDecoderSupport");
47
+
}
48
+
}
49
+
50
+
// NOTE: Only tested if this appears on Windows, it should appear on all when
51
+
// hardware acceleration is disabled
52
+
const noAccel = app.commandLine.hasSwitch("disable-gpu-compositing");
53
+
if ((moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapi") ?? true) && !noAccel) {
54
+
if (process.platform === "linux") {
55
+
// These will eventually be renamed https://source.chromium.org/chromium/chromium/src/+/5482210941a94d70406b8da962426e4faca7fce4
56
+
enabledFeatures.push("VaapiVideoEncoder", "VaapiVideoDecoder", "VaapiVideoDecodeLinuxGL");
57
+
58
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapiIgnoreDriverChecks") ?? false)
59
+
enabledFeatures.push("VaapiIgnoreDriverChecks");
60
+
}
61
+
}
62
+
63
+
app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].join(","));
64
+
65
+
if (process.platform === "linux" && moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxUpdater")) {
66
+
const exePath = app.getPath("exe");
67
+
const appName = path.basename(exePath);
68
+
const targetDir = path.dirname(exePath);
69
+
const { releaseChannel }: { releaseChannel: string } = JSON.parse(
70
+
fsSync.readFileSync(path.join(targetDir, "resources", "build_info.json"), "utf8")
71
+
);
72
+
73
+
const updaterModule = require(path.join(moonlightHost.asarPath, "app_bootstrap", "hostUpdater.js"));
74
+
const updater = updaterModule.constructor;
75
+
76
+
async function doUpdate(cb: (percent: number) => void) {
77
+
logger.debug("Extracting to", targetDir);
78
+
79
+
const exists = (path: string) =>
80
+
fs
81
+
.stat(path)
82
+
.then(() => true)
83
+
.catch(() => false);
84
+
85
+
const url = `https://discord.com/api/download/${releaseChannel}?platform=linux&format=tar.gz`;
86
+
const resp = await fetch(url, {
87
+
cache: "no-store"
88
+
});
89
+
90
+
const reader = resp.body!.getReader();
91
+
const contentLength = parseInt(resp.headers.get("Content-Length") ?? "0");
92
+
logger.info(`Expecting ${contentLength} bytes for the update`);
93
+
const bytes = new Uint8Array(contentLength);
94
+
let pos = 0;
95
+
let lastPercent = 0;
96
+
97
+
while (true) {
98
+
const { done, value } = await reader.read();
99
+
if (done) {
100
+
break;
101
+
} else {
102
+
bytes.set(value, pos);
103
+
pos += value.length;
104
+
105
+
const newPercent = Math.floor((pos / contentLength) * 100);
106
+
if (lastPercent !== newPercent) {
107
+
lastPercent = newPercent;
108
+
cb(newPercent);
109
+
}
110
+
}
111
+
}
112
+
113
+
const files = await parseTarGzip(bytes);
114
+
115
+
for (const file of files) {
116
+
if (!file.data) continue;
117
+
// @ts-expect-error What do you mean their own types are wrong
118
+
if (file.type !== "file") continue;
119
+
120
+
// Discord update files are inside of a main "Discord(PTB|Canary)" folder
121
+
const filePath = file.name.replace(`${appName}/`, "");
122
+
logger.info("Extracting", filePath);
123
+
124
+
let targetFilePath = path.join(targetDir, filePath);
125
+
if (filePath === "resources/app.asar") {
126
+
// You tried
127
+
targetFilePath = path.join(targetDir, "resources", "_app.asar");
128
+
} else if (filePath === appName || filePath === "chrome_crashpad_handler") {
129
+
// Can't write over the executable? Just move it! 4head
130
+
if (await exists(targetFilePath)) {
131
+
await fs.rename(targetFilePath, targetFilePath + ".bak");
132
+
await fs.unlink(targetFilePath + ".bak");
133
+
}
134
+
}
135
+
const targetFileDir = path.dirname(targetFilePath);
136
+
137
+
if (!(await exists(targetFileDir))) await fs.mkdir(targetFileDir, { recursive: true });
138
+
await fs.writeFile(targetFilePath, file.data);
139
+
140
+
const mode = file.attrs?.mode;
141
+
if (mode != null) {
142
+
// Not sure why this slice is needed
143
+
await fs.chmod(targetFilePath, mode.slice(-3));
144
+
}
145
+
}
146
+
147
+
logger.debug("Done updating");
148
+
}
149
+
150
+
const realEmit = updater.prototype.emit;
151
+
updater.prototype.emit = function (event: string, ...args: any[]) {
152
+
// Arrow functions don't bind `this` :D
153
+
const call = (event: string, ...args: any[]) => realEmit.call(this, event, ...args);
154
+
155
+
if (event === "update-manually") {
156
+
const latestVerStr: string = args[0];
157
+
logger.debug("update-manually called, intercepting", latestVerStr);
158
+
call("update-available");
159
+
160
+
(async () => {
161
+
try {
162
+
await doUpdate((progress) => {
163
+
call("update-progress", progress);
164
+
});
165
+
// Copied from the win32 updater
166
+
this.updateVersion = latestVerStr;
167
+
call(
168
+
"update-downloaded",
169
+
{},
170
+
releaseChannel,
171
+
latestVerStr,
172
+
new Date(),
173
+
this.updateUrl,
174
+
this.quitAndInstall.bind(this)
175
+
);
176
+
} catch (e) {
177
+
logger.error("Error updating", e);
178
+
}
179
+
})();
180
+
181
+
return this;
182
+
} else {
183
+
return realEmit.call(this, event, ...args);
184
+
}
185
+
};
186
+
}
+77
packages/core-extensions/src/nativeFixes/manifest.json
+77
packages/core-extensions/src/nativeFixes/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "nativeFixes",
4
+
"meta": {
5
+
"name": "Native Fixes",
6
+
"tagline": "Various configurable fixes for Discord and Electron",
7
+
"authors": ["Cynosphere", "adryd", "NotNite"],
8
+
"tags": ["fixes"]
9
+
},
10
+
"environment": "desktop",
11
+
"settings": {
12
+
"devtoolsThemeFix": {
13
+
"advice": "restart",
14
+
"displayName": "Devtools Theme Fix",
15
+
"description": "Temporary workaround for devtools defaulting to light theme on Electron 32",
16
+
"type": "boolean",
17
+
"default": true
18
+
},
19
+
"disableRendererBackgrounding": {
20
+
"advice": "restart",
21
+
"displayName": "Disable Renderer Backgrounding",
22
+
"description": "This is enabled by default as a power saving measure, but it breaks screensharing and websocket connections fairly often",
23
+
"type": "boolean",
24
+
"default": true
25
+
},
26
+
"vulkan": {
27
+
"advice": "restart",
28
+
"displayName": "Enable Vulkan renderer",
29
+
"description": "Uses the Vulkan backend for rendering",
30
+
"type": "boolean",
31
+
"default": false
32
+
},
33
+
"linuxAutoscroll": {
34
+
"advice": "restart",
35
+
"displayName": "Enable middle click autoscroll on Linux",
36
+
"description": "Requires manual configuration of your system to disable middle click paste, has no effect on other operating systems",
37
+
"type": "boolean",
38
+
"default": false
39
+
},
40
+
"linuxSpeechDispatcher": {
41
+
"advice": "restart",
42
+
"displayName": "Enable speech-dispatcher for TTS on Linux",
43
+
"description": "Fixes text-to-speech. Has no effect on other operating systems",
44
+
"type": "boolean",
45
+
"default": true
46
+
},
47
+
"vaapi": {
48
+
"advice": "restart",
49
+
"displayName": "Enable VAAPI features on Linux",
50
+
"description": "Provides hardware accelerated video encode and decode. Has no effect on other operating systems",
51
+
"type": "boolean",
52
+
"default": true
53
+
},
54
+
"vaapiIgnoreDriverChecks": {
55
+
"advice": "restart",
56
+
"displayName": "Ignore VAAPI driver checks on Linux",
57
+
"description": "Forces hardware video acceleration on some graphics drivers at the cost of stability. Has no effect on other operating systems",
58
+
"type": "boolean",
59
+
"default": false
60
+
},
61
+
"linuxUpdater": {
62
+
"advice": "restart",
63
+
"displayName": "Linux Updater",
64
+
"description": "Actually implements updating Discord on Linux. Has no effect on other operating systems",
65
+
"type": "boolean",
66
+
"default": false
67
+
},
68
+
"linuxHevcSupport": {
69
+
"advice": "restart",
70
+
"displayName": "HEVC support on Linux",
71
+
"description": "You might also need to enable Vulkan renderer. Has no effect on other operating systems",
72
+
"type": "boolean",
73
+
"default": true
74
+
}
75
+
},
76
+
"apiLevel": 2
77
+
}
+4
-4
packages/core-extensions/src/noHideToken/index.ts
+4
-4
packages/core-extensions/src/noHideToken/index.ts
···
1
-
import { Patch } from "types/src";
1
+
import { Patch } from "@moonlight-mod/types";
2
2
3
3
export const patches: Patch[] = [
4
4
{
5
-
find: "hideToken(){",
5
+
find: "hideToken:()=>",
6
6
replace: {
7
-
match: /hideToken\(\)\{.+?},/,
8
-
replacement: `hideToken(){},`
7
+
match: /hideToken:\(\)=>.+?,/,
8
+
replacement: `hideToken:()=>{},`
9
9
}
10
10
}
11
11
];
+4
-1
packages/core-extensions/src/noHideToken/manifest.json
+4
-1
packages/core-extensions/src/noHideToken/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "noHideToken",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "No Hide Token",
5
-
"tagline": "Disables removal of token from localStorage when opening dev tools",
7
+
"tagline": "Prevents you from being logged-out on hard-crash",
8
+
"description": "Prevents you from being logged-out on hard-crash by disabling removal of token from localStorage when opening dev tools",
6
9
"authors": ["adryd"],
7
10
"tags": ["dangerZone", "development"]
8
11
}
-15
packages/core-extensions/src/noTrack/host.ts
-15
packages/core-extensions/src/noTrack/host.ts
···
1
-
import { BrowserWindow } from "electron";
2
-
3
-
moonlightHost.events.on("window-created", (window: BrowserWindow) => {
4
-
window.webContents.session.webRequest.onBeforeRequest(
5
-
{
6
-
urls: [
7
-
"https://*.discord.com/api/v*/science",
8
-
"https://*.discord.com/api/v*/metrics"
9
-
]
10
-
},
11
-
function (details, callback) {
12
-
callback({ cancel: true });
13
-
}
14
-
);
15
-
});
+3
-3
packages/core-extensions/src/noTrack/index.ts
+3
-3
packages/core-extensions/src/noTrack/index.ts
···
2
2
3
3
export const patches: Patch[] = [
4
4
{
5
-
find: "analyticsTrackingStoreMaker:function",
5
+
find: "analyticsTrackingStoreMaker:()=>",
6
6
replace: {
7
-
match: /analyticsTrackingStoreMaker:function\(\){return .}/,
8
-
replacement: "analyticsTrackingStoreMaker:function(){return ()=>{}}"
7
+
match: /analyticsTrackingStoreMaker:\(\)=>.+?,/,
8
+
replacement: "analyticsTrackingStoreMaker:()=>()=>{},"
9
9
}
10
10
},
11
11
{
+9
-1
packages/core-extensions/src/noTrack/manifest.json
+9
-1
packages/core-extensions/src/noTrack/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "noTrack",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "No Track",
5
7
"tagline": "Disables /api/science and analytics",
6
8
"authors": ["Cynosphere", "NotNite"],
7
9
"tags": ["privacy"]
8
-
}
10
+
},
11
+
"blocked": [
12
+
"https://*.discord.com/api/v*/science",
13
+
"https://*.discord.com/api/v*/metrics",
14
+
"https://*.discordapp.com/api/v*/science",
15
+
"https://*.discordapp.com/api/v*/metrics"
16
+
]
9
17
}
+42
packages/core-extensions/src/notices/index.ts
+42
packages/core-extensions/src/notices/index.ts
···
1
+
import type { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
{
5
+
find: ".GUILD_RAID_NOTIFICATION:",
6
+
replace: {
7
+
match: /(?<=return(\(0,.\.jsx\))\(.+?\);)case .{1,2}\..{1,3}\.GUILD_RAID_NOTIFICATION:/,
8
+
replacement: (orig, createElement) =>
9
+
`case "__moonlight_notice":return${createElement}(require("notices_component").default,{});${orig}`
10
+
}
11
+
},
12
+
{
13
+
find: '"NoticeStore"',
14
+
replace: [
15
+
{
16
+
match: /\[.{1,2}\..{1,3}\.CONNECT_SPOTIFY\]:{/,
17
+
replacement: (orig: string) =>
18
+
`__moonlight_notice:{predicate:()=>require("notices_notices").default.shouldShowNotice()},${orig}`
19
+
},
20
+
{
21
+
match: /=\[(.{1,2}\..{1,3}\.QUARANTINED,)/g,
22
+
replacement: (_, orig) => `=["__moonlight_notice",${orig}`
23
+
}
24
+
]
25
+
}
26
+
];
27
+
28
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
29
+
notices: {
30
+
dependencies: [{ id: "discord/packages/flux" }, { id: "discord/Dispatcher" }]
31
+
},
32
+
33
+
component: {
34
+
dependencies: [
35
+
{ id: "react" },
36
+
{ id: "discord/Dispatcher" },
37
+
{ id: "discord/components/common/index" },
38
+
{ id: "discord/packages/flux" },
39
+
{ ext: "notices", id: "notices" }
40
+
]
41
+
}
42
+
};
+11
packages/core-extensions/src/notices/manifest.json
+11
packages/core-extensions/src/notices/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "notices",
4
+
"apiLevel": 2,
5
+
"meta": {
6
+
"name": "Notices",
7
+
"tagline": "An API for adding notices at the top of the page",
8
+
"authors": ["Cynosphere", "NotNite"],
9
+
"tags": ["library"]
10
+
}
11
+
}
+50
packages/core-extensions/src/notices/webpackModules/component.tsx
+50
packages/core-extensions/src/notices/webpackModules/component.tsx
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
3
+
import { Notice, NoticeCloseButton, PrimaryCTANoticeButton } from "@moonlight-mod/wp/discord/components/common/index";
4
+
import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
5
+
import NoticesStore from "@moonlight-mod/wp/notices_notices";
6
+
import type { Notice as NoticeType } from "@moonlight-mod/types/coreExtensions/notices";
7
+
8
+
function popAndDismiss(notice: NoticeType) {
9
+
NoticesStore.popNotice();
10
+
if (notice?.onDismiss) {
11
+
notice.onDismiss();
12
+
}
13
+
if (!NoticesStore.shouldShowNotice()) {
14
+
Dispatcher.dispatch({
15
+
type: "NOTICE_DISMISS"
16
+
});
17
+
}
18
+
}
19
+
20
+
export default function UpdateNotice() {
21
+
const { notice } = useStateFromStoresObject([NoticesStore], () => ({
22
+
notice: NoticesStore.getCurrentNotice()
23
+
}));
24
+
25
+
if (notice == null) return <></>;
26
+
27
+
return (
28
+
<Notice color={notice.color}>
29
+
{notice.element}
30
+
31
+
{(notice.showClose ?? true) && (
32
+
<NoticeCloseButton onClick={() => popAndDismiss(notice)} noticeType="__moonlight_notice" />
33
+
)}
34
+
35
+
{(notice.buttons ?? []).map((button) => (
36
+
<PrimaryCTANoticeButton
37
+
key={button.name}
38
+
onClick={() => {
39
+
if (button.onClick()) {
40
+
popAndDismiss(notice);
41
+
}
42
+
}}
43
+
noticeType="__moonlight_notice"
44
+
>
45
+
{button.name}
46
+
</PrimaryCTANoticeButton>
47
+
))}
48
+
</Notice>
49
+
);
50
+
}
+55
packages/core-extensions/src/notices/webpackModules/notices.ts
+55
packages/core-extensions/src/notices/webpackModules/notices.ts
···
1
+
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
2
+
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
3
+
import type { Notice, Notices } from "@moonlight-mod/types/coreExtensions/notices";
4
+
5
+
// very lazy way of doing this, FIXME
6
+
let open = false;
7
+
8
+
class NoticesStore extends Store<any> {
9
+
private notices: Notice[] = [];
10
+
11
+
constructor() {
12
+
super(Dispatcher);
13
+
}
14
+
15
+
addNotice(notice: Notice) {
16
+
this.notices.push(notice);
17
+
if (open && this.notices.length !== 0) {
18
+
Dispatcher.dispatch({
19
+
type: "NOTICE_SHOW",
20
+
notice: { type: "__moonlight_notice" }
21
+
});
22
+
}
23
+
this.emitChange();
24
+
}
25
+
26
+
popNotice() {
27
+
this.notices.shift();
28
+
this.emitChange();
29
+
}
30
+
31
+
getCurrentNotice() {
32
+
return this.notices.length > 0 ? this.notices[0] : null;
33
+
}
34
+
35
+
shouldShowNotice() {
36
+
return this.notices.length > 0;
37
+
}
38
+
}
39
+
40
+
const store: Notices = new NoticesStore();
41
+
42
+
function showNotice() {
43
+
open = true;
44
+
if (store.shouldShowNotice()) {
45
+
Dispatcher.dispatch({
46
+
type: "NOTICE_SHOW",
47
+
notice: { type: "__moonlight_notice" }
48
+
});
49
+
}
50
+
}
51
+
52
+
Dispatcher.subscribe("CONNECTION_OPEN", showNotice);
53
+
Dispatcher.subscribe("CONNECTION_OPEN_SUPPLEMENTAL", showNotice);
54
+
55
+
export default store;
+98
-43
packages/core-extensions/src/quietLoggers/index.ts
+98
-43
packages/core-extensions/src/quietLoggers/index.ts
···
1
1
import { Patch } from "@moonlight-mod/types";
2
2
3
3
const notXssDefensesOnly = () =>
4
-
(moonlight.getConfigOption<boolean>("quietLoggers", "xssDefensesOnly") ??
5
-
false) === false;
4
+
(moonlight.getConfigOption<boolean>("quietLoggers", "xssDefensesOnly") ?? false) === false;
5
+
6
+
const silenceDiscordLogger = moonlight.getConfigOption<boolean>("quietLoggers", "silenceDiscordLogger") ?? false;
6
7
7
8
// These patches MUST run before the simple patches, these are to remove loggers
8
9
// that end up causing syntax errors by the normal patch
9
10
const loggerFixes: Patch[] = [
10
11
{
11
-
find: '"./ggsans-800-extrabolditalic.woff2":',
12
+
find: '"./gg-sans/ggsans-800-extrabolditalic.woff2":',
12
13
replace: {
13
-
match: /\.then\(function\(\){var.+?"MODULE_NOT_FOUND",.\}\)/,
14
-
replacement: ".then(()=>(()=>{}))"
14
+
match: /var .=Error.+?;throw .+?,./,
15
+
replacement: ""
15
16
}
16
17
},
17
18
{
18
19
find: '("GatewaySocket")',
19
20
replace: {
20
-
match: /.\.(info|log)(\(.+?\))(;|,)/g,
21
-
replacement: (_, type, body, trail) => `(()=>{})${body}${trail}`
21
+
match: /\i\.(log|info)\(/g,
22
+
replacement: "(()=>{})("
23
+
}
24
+
},
25
+
{
26
+
find: '"_connect called with already existing websocket"',
27
+
replace: {
28
+
match: /\i\.(log|info|verbose)\(/g,
29
+
replacement: "(()=>{})("
22
30
}
23
31
}
24
32
];
···
29
37
// Patches to simply remove a logger call
30
38
const stubPatches = [
31
39
// "sh" is not a valid locale.
40
+
["is not a valid locale", /void \i\.error\(""\.concat\(\i," is not a valid locale\."\)\)/g],
41
+
['"[BUILD INFO] Release Channel: "', /new \i\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?\)\),/],
42
+
['.APP_NATIVE_CRASH,"Storage"', /console\.log\("AppCrashedFatalReport lastCrash:",\i,\i\);/],
43
+
['.APP_NATIVE_CRASH,"Storage"', 'void console.log("AppCrashedFatalReport: getLastCrash not supported.")'],
44
+
['"[NATIVE INFO] ', /new \i\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/],
45
+
['"Spellchecker"', /\i\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
46
+
['throw Error("Messages are still loading.");', /console\.warn\("Unsupported Locale",\i\),/],
47
+
["}_dispatchWithDevtools(", /\i\.totalTime>\i&&\i\.verbose\(.+?\);/],
48
+
['"NativeDispatchUtils"', /null==\i&&\i\.warn\("Tried getting Dispatch instance before instantiated"\),/],
32
49
[
33
-
"is not a valid locale",
34
-
/(.)\.error\(""\.concat\((.)," is not a valid locale\."\)\)/g
50
+
'"Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. Action: "',
51
+
/\i\.has\(\i\.type\)&&\i\.log\(.+?\.type\)\),/
35
52
],
36
-
['.displayName="RunningGameStore"', /.\.info\("games",{.+?}\),/],
53
+
['console.warn("Window state not initialized"', /console\.warn\("Window state not initialized",\i\),/],
54
+
['.name="MaxListenersExceededWarning",', /(?<=\.length),\i\(\i\)/],
37
55
[
38
-
'"[BUILD INFO] Release Channel: "',
39
-
/new\(0,.{1,2}\.default\)\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?"\)\),/
56
+
'"The answer for life the universe and everything is:"',
57
+
/\i\.info\("The answer for life the universe and everything is:",\i\),/
40
58
],
41
59
[
42
-
'.AnalyticEvents.APP_NATIVE_CRASH,"Storage"',
43
-
/console\.log\("AppCrashedFatalReport lastCrash:",.,.\);/
60
+
'"isLibdiscoreBlockedDomainsEnabled called but libdiscore is not loaded"',
61
+
/,\i\.verbose\("isLibdiscoreBlockedDomainsEnabledThisSession: ".concat\(\i\)\)/
44
62
],
45
63
[
46
-
'.AnalyticEvents.APP_NATIVE_CRASH,"Storage"',
47
-
'console.log("AppCrashedFatalReport: getLastCrash not supported.");'
64
+
'"Unable to determine render window for element"',
65
+
/console\.warn\("Unable to determine render window for element",\i\),/
48
66
],
49
67
[
50
-
'"[NATIVE INFO] ',
51
-
/new\(0,.{1,2}\.default\)\(\)\.log\("\[NATIVE INFO] .+?\)\),/
68
+
'"Unable to determine render window for element"',
69
+
/console\.warn\('Unable to find element constructor "'\.concat\(\i,'" in'\),\i\),/
52
70
],
53
-
['"Spellchecker"', /.\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
54
71
[
55
-
'throw new Error("Messages are still loading.");',
56
-
/console\.warn\("Unsupported Locale",.\);/
72
+
'"[PostMessageTransport] Protocol error: event data should be an Array!"',
73
+
/void console\.warn\("\[PostMessageTransport] Protocol error: event data should be an Array!"\)/
57
74
],
58
-
["_dispatchWithDevtools=", /.\.has\(.\.type\)&&.\.log\(.+?\);/],
59
-
["_dispatchWithDevtools=", /.\.totalTime>100&&.\.log\(.+?\);0;/],
60
75
[
61
-
'"NativeDispatchUtils"',
62
-
/null==.&&.\.warn\("Tried getting Dispatch instance before instantiated"\),/
63
-
],
64
-
[
65
-
'Error("Messages are still loading.")',
66
-
/console\.warn\("Unsupported Locale",.\),/
67
-
],
68
-
['("DatabaseManager")', /.\.log\("removing database \(user: ".+?\)\),/],
69
-
[
70
-
'"Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. Action: "',
71
-
/.\.has\(.\.type\)&&.\.log\(.+?\.type\)\),/
76
+
'("ComponentDispatchUtils")',
77
+
/new \i\.Z\("ComponentDispatchUtils"\)\.warn\("ComponentDispatch\.resubscribe: Resubscribe without existing subscription",\i\),/
72
78
]
73
79
];
74
80
81
+
const stripLoggers = [
82
+
'("OverlayRenderStore")',
83
+
'("FetchBlockedDomain")',
84
+
'="UserSettingsProtoLastWriteTimes",',
85
+
'("MessageActionCreators")',
86
+
'("Routing/Utils")',
87
+
'("DatabaseManager")',
88
+
'("KeyboardLayoutMapUtils")',
89
+
'("ChannelMessages")',
90
+
'("MessageQueue")',
91
+
'("RTCLatencyTestManager")',
92
+
'("OverlayStoreV3")',
93
+
'("OverlayBridgeStore")',
94
+
'("AuthenticationStore")',
95
+
'("ConnectionStore")',
96
+
'"Dispatched INITIAL_GUILD "',
97
+
'"handleIdentify called"',
98
+
'("Spotify")'
99
+
];
100
+
75
101
const simplePatches = [
76
102
// Moment.js deprecation warnings
77
-
["suppressDeprecationWarnings=!1", "suppressDeprecationWarnings=!0"],
78
-
79
-
// Zustand related
80
-
[
81
-
/console\.warn\("\[DEPRECATED\] Please use `subscribeWithSelector` middleware"\)/g,
82
-
"/*$&*/"
83
-
]
103
+
["suppressDeprecationWarnings=!1", "suppressDeprecationWarnings=!0"]
84
104
] as { [0]: string | RegExp; [1]: string }[];
85
105
86
106
export const patches: Patch[] = [
87
107
{
88
-
find: ".Messages.XSSDefenses",
108
+
find: ".Messages.SELF_XSS_HEADER",
89
109
replace: {
90
-
match: /\(null!=.{1,2}&&"0\.0\.0"===.{1,2}\.remoteApp\.getVersion\(\)\)/,
110
+
match: /\(null!=\i&&"0\.0\.0"===\i\.remoteApp\.getVersion\(\)\)/,
91
111
replacement: "(true)"
92
112
}
93
113
},
114
+
{
115
+
find: '("ComponentDispatchUtils")',
116
+
replace: {
117
+
match:
118
+
/new \i\.Z\("ComponentDispatchUtils"\)\.warn\("ComponentDispatch\.subscribe: Attempting to add a duplicate listener",\i\)/,
119
+
replacement: "void 0"
120
+
},
121
+
prerequisite: notXssDefensesOnly
122
+
},
123
+
// Highlight.js deprecation warnings
124
+
{
125
+
find: "Deprecated as of",
126
+
replace: {
127
+
match: /console\./g,
128
+
replacement: "false&&console."
129
+
},
130
+
prerequisite: notXssDefensesOnly
131
+
},
132
+
// Discord's logger
133
+
{
134
+
find: "ฮฃ:",
135
+
replace: {
136
+
match: "for",
137
+
replacement: "return;for"
138
+
},
139
+
prerequisite: () => silenceDiscordLogger && notXssDefensesOnly()
140
+
},
94
141
...loggerFixes,
95
142
...stubPatches.map((patch) => ({
96
143
find: patch[0],
···
105
152
replace: {
106
153
match: patch[0],
107
154
replacement: patch[1]
155
+
},
156
+
prerequisite: notXssDefensesOnly
157
+
})),
158
+
...stripLoggers.map((find) => ({
159
+
find,
160
+
replace: {
161
+
match: /(\i|this\.logger)\.(log|warn|error|info|verbose)\(/g,
162
+
replacement: "(()=>{})("
108
163
},
109
164
prerequisite: notXssDefensesOnly
110
165
}))
+10
packages/core-extensions/src/quietLoggers/manifest.json
+10
packages/core-extensions/src/quietLoggers/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "quietLoggers",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Quiet Loggers",
5
7
"tagline": "Quiet errors on startup, and disable unnecesary loggers",
···
8
10
},
9
11
"settings": {
10
12
"xssDefensesOnly": {
13
+
"advice": "reload",
11
14
"displayName": "Only hide self-XSS",
12
15
"description": "Only disable self XSS prevention log",
16
+
"type": "boolean",
17
+
"default": false
18
+
},
19
+
"silenceDiscordLogger": {
20
+
"advice": "reload",
21
+
"displayName": "Silence Discord logger",
22
+
"description": "Hides all messages from Discord's logger (the logs that start with purple text in brackets)",
13
23
"type": "boolean",
14
24
"default": false
15
25
}
+75
packages/core-extensions/src/rocketship/host/permissions.ts
+75
packages/core-extensions/src/rocketship/host/permissions.ts
···
1
+
import type { BrowserWindow } from "electron";
2
+
3
+
type PermissionRequestHandler = (
4
+
webContents: Electron.WebContents,
5
+
permission: string,
6
+
callback: (permissionGranted: boolean) => void,
7
+
details: Electron.PermissionRequestHandlerHandlerDetails
8
+
) => void;
9
+
10
+
type PermissionCheckHandler = (
11
+
webContents: Electron.WebContents | null,
12
+
permission: string,
13
+
requestingOrigin: string,
14
+
details: Electron.PermissionCheckHandlerHandlerDetails
15
+
) => boolean;
16
+
17
+
moonlightHost.events.on("window-created", (window: BrowserWindow, isMainWindow: boolean) => {
18
+
if (!isMainWindow) return;
19
+
const windowSession = window.webContents.session;
20
+
21
+
// setPermissionRequestHandler
22
+
windowSession.setPermissionRequestHandler((webcontents, permission, callback, details) => {
23
+
let cbResult = false;
24
+
function fakeCallback(result: boolean) {
25
+
cbResult = result;
26
+
}
27
+
28
+
if (caughtPermissionRequestHandler) {
29
+
caughtPermissionRequestHandler(webcontents, permission, fakeCallback, details);
30
+
}
31
+
32
+
if (permission === "media" || permission === "display-capture") {
33
+
cbResult = true;
34
+
}
35
+
36
+
callback(cbResult);
37
+
});
38
+
39
+
let caughtPermissionRequestHandler: PermissionRequestHandler | undefined;
40
+
41
+
windowSession.setPermissionRequestHandler = function catchSetPermissionRequestHandler(
42
+
handler: (
43
+
webcontents: Electron.WebContents,
44
+
permission: string,
45
+
callback: (permissionGranted: boolean) => void
46
+
) => void
47
+
) {
48
+
caughtPermissionRequestHandler = handler;
49
+
};
50
+
51
+
// setPermissionCheckHandler
52
+
windowSession.setPermissionCheckHandler((webcontents, permission, requestingOrigin, details) => {
53
+
return false;
54
+
});
55
+
56
+
let caughtPermissionCheckHandler: PermissionCheckHandler | undefined;
57
+
58
+
windowSession.setPermissionCheckHandler((webcontents, permission, requestingOrigin, details) => {
59
+
let result = false;
60
+
61
+
if (caughtPermissionCheckHandler) {
62
+
result = caughtPermissionCheckHandler(webcontents, permission, requestingOrigin, details);
63
+
}
64
+
65
+
if (permission === "media" || permission === "display-capture") {
66
+
result = true;
67
+
}
68
+
69
+
return result;
70
+
});
71
+
72
+
windowSession.setPermissionCheckHandler = function catchSetPermissionCheckHandler(handler: PermissionCheckHandler) {
73
+
caughtPermissionCheckHandler = handler;
74
+
};
75
+
});
+27
packages/core-extensions/src/rocketship/host/types.ts
+27
packages/core-extensions/src/rocketship/host/types.ts
···
1
+
// https://github.com/Vencord/venmic/blob/d737ef33eaae7a73d03ec02673e008cf0243434d/lib/module.d.ts
2
+
type DefaultProps = "node.name" | "application.name";
3
+
4
+
type LiteralUnion<LiteralType, BaseType extends string> = LiteralType | (BaseType & Record<never, never>);
5
+
6
+
type Optional<Type, Key extends keyof Type> = Partial<Pick<Type, Key>> & Omit<Type, Key>;
7
+
8
+
export type Node<T extends string = never> = Record<LiteralUnion<T, string>, string>;
9
+
10
+
export interface LinkData {
11
+
include: Node[];
12
+
exclude: Node[];
13
+
14
+
ignore_devices?: boolean;
15
+
16
+
only_speakers?: boolean;
17
+
only_default_speakers?: boolean;
18
+
19
+
workaround?: Node[];
20
+
}
21
+
22
+
export interface PatchBay {
23
+
unlink(): void;
24
+
25
+
list<T extends string = DefaultProps>(props?: T[]): Node<T>[];
26
+
link(data: Optional<LinkData, "exclude"> | Optional<LinkData, "include">): boolean;
27
+
}
+69
packages/core-extensions/src/rocketship/host/venmic.ts
+69
packages/core-extensions/src/rocketship/host/venmic.ts
···
1
+
import type { BrowserWindow } from "electron";
2
+
import { app, desktopCapturer } from "electron";
3
+
import path from "node:path";
4
+
import { type PatchBay } from "./types";
5
+
6
+
const logger = moonlightHost.getLogger("rocketship");
7
+
8
+
function getPatchbay() {
9
+
try {
10
+
const venmic = require(path.join(path.dirname(moonlightHost.asarPath), "..", "venmic.node")) as {
11
+
PatchBay: new () => PatchBay;
12
+
};
13
+
const patchbay = new venmic.PatchBay();
14
+
return patchbay;
15
+
} catch (error) {
16
+
logger.error("Failed to load venmic.node:", error);
17
+
return null;
18
+
}
19
+
}
20
+
21
+
const patchbay = getPatchbay();
22
+
23
+
// TODO: figure out how to map source to window with venmic
24
+
function linkVenmic() {
25
+
if (patchbay == null) return false;
26
+
27
+
try {
28
+
const pid =
29
+
app
30
+
.getAppMetrics()
31
+
.find((proc) => proc.name === "Audio Service")
32
+
?.pid?.toString() ?? "";
33
+
34
+
logger.info("Audio Service PID:", pid);
35
+
36
+
patchbay.unlink();
37
+
return patchbay.link({
38
+
exclude: [{ "application.process.id": pid }, { "media.class": "Stream/Input/Audio" }],
39
+
ignore_devices: true,
40
+
only_speakers: true,
41
+
only_default_speakers: true
42
+
});
43
+
} catch (error) {
44
+
logger.error("Failed to link venmic:", error);
45
+
return false;
46
+
}
47
+
}
48
+
49
+
moonlightHost.events.on("window-created", (window: BrowserWindow, isMainWindow: boolean) => {
50
+
if (!isMainWindow) return;
51
+
const windowSession = window.webContents.session;
52
+
53
+
// @ts-expect-error these types ancient
54
+
windowSession.setDisplayMediaRequestHandler(
55
+
(request: any, callback: any) => {
56
+
const linked = linkVenmic();
57
+
desktopCapturer.getSources({ types: ["screen", "window"] }).then((sources) => {
58
+
//logger.debug("desktopCapturer.getSources", sources);
59
+
logger.debug("Linked to venmic:", linked);
60
+
61
+
callback({
62
+
video: sources[0],
63
+
audio: "loopback"
64
+
});
65
+
});
66
+
},
67
+
{ useSystemPicker: true }
68
+
);
69
+
});
+2
packages/core-extensions/src/rocketship/host.ts
+2
packages/core-extensions/src/rocketship/host.ts
+124
packages/core-extensions/src/rocketship/index.ts
+124
packages/core-extensions/src/rocketship/index.ts
···
1
+
import { Patch } from "@moonlight-mod/types";
2
+
3
+
const logger = moonlight.getLogger("rocketship");
4
+
const getDisplayMediaOrig = navigator.mediaDevices.getDisplayMedia;
5
+
6
+
async function getVenmicStream() {
7
+
try {
8
+
const devices = await navigator.mediaDevices.enumerateDevices();
9
+
logger.debug("Devices:", devices);
10
+
11
+
// This isn't vencord :(
12
+
const id = devices.find((device) => device.label === "vencord-screen-share")?.deviceId;
13
+
if (!id) return null;
14
+
logger.debug("Got venmic device ID:", id);
15
+
16
+
const stream = await navigator.mediaDevices.getUserMedia({
17
+
audio: {
18
+
deviceId: {
19
+
exact: id
20
+
},
21
+
autoGainControl: false,
22
+
echoCancellation: false,
23
+
noiseSuppression: false
24
+
}
25
+
});
26
+
27
+
return stream.getAudioTracks();
28
+
} catch (error) {
29
+
logger.warn("Failed to get venmic stream:", error);
30
+
return null;
31
+
}
32
+
}
33
+
34
+
navigator.mediaDevices.getDisplayMedia = async function getDisplayMediaRedirect(options) {
35
+
const orig = await getDisplayMediaOrig.call(this, options);
36
+
37
+
const venmic = await getVenmicStream();
38
+
logger.debug("venmic", venmic);
39
+
if (venmic != null) {
40
+
// venmic will be proxying all audio, so we need to remove the original
41
+
// tracks to not cause overlap
42
+
for (const track of orig.getAudioTracks()) {
43
+
orig.removeTrack(track);
44
+
}
45
+
46
+
for (const track of venmic) {
47
+
orig.addTrack(track);
48
+
}
49
+
}
50
+
51
+
return orig;
52
+
};
53
+
54
+
export const patches: Patch[] = [
55
+
// "Ensure discord_voice is happy"
56
+
{
57
+
find: "RustAudioDeviceModule",
58
+
replace: [
59
+
{
60
+
match: /static supported\(\)\{.+?\}/,
61
+
replacement: "static supported(){return true}"
62
+
},
63
+
{
64
+
match: "supported(){return!0}",
65
+
replacement: "supported(){return true}"
66
+
}
67
+
]
68
+
},
69
+
// Remove Native media engine from list of choices
70
+
{
71
+
find: '.CAMERA_BACKGROUND_LIVE="cameraBackgroundLive"',
72
+
replace: {
73
+
match: /.\..{1,2}\.NATIVE,/,
74
+
replacement: ""
75
+
}
76
+
},
77
+
// Stub out browser checks to allow us to use WebRTC voice on Embedded
78
+
{
79
+
find: "Using Unified Plan (",
80
+
replace: {
81
+
match: /return .\..{1,2}\?\((.)\.info/,
82
+
replacement: (_, logger) => `return true?(${logger}.info`
83
+
}
84
+
},
85
+
{
86
+
find: '"UnifiedConnection("',
87
+
replace: {
88
+
match: /this\.videoSupported=.\..{1,2};/,
89
+
replacement: "this.videoSupported=true;"
90
+
}
91
+
},
92
+
{
93
+
find: "OculusBrowser",
94
+
replace: [
95
+
{
96
+
match: /"Firefox"===(.)\(\)\.name/g,
97
+
replacement: (orig, info) => `true||${orig}`
98
+
}
99
+
]
100
+
},
101
+
{
102
+
find: ".getMediaEngine().getDesktopSource",
103
+
replace: {
104
+
match: /.\.isPlatformEmbedded/,
105
+
replacement: "false"
106
+
}
107
+
},
108
+
{
109
+
// Matching MediaEngineStore
110
+
find: '"displayName","MediaEngineStore")',
111
+
replace: [
112
+
// Prevent loading of krisp native module by stubbing out desktop checks
113
+
{
114
+
match: /\(\(0,.\.isWindows\)\(\)\|\|\(0,.\.isLinux\)\(\)\|\|.+?&&!__OVERLAY__/,
115
+
replacement: (orig, macosPlatformCheck) => `false&&!__OVERLAY__`
116
+
},
117
+
// Enable loading of web krisp equivelant by replacing isWeb with true
118
+
{
119
+
match: /\(0,.\.isWeb\)\(\)&&(.{1,2}\.supports\(.{1,2}\..{1,2}.NOISE_CANCELLATION)/,
120
+
replacement: (orig, supportsNoiseCancellation) => `true&&${supportsNoiseCancellation}`
121
+
}
122
+
]
123
+
}
124
+
];
+13
packages/core-extensions/src/rocketship/manifest.json
+13
packages/core-extensions/src/rocketship/manifest.json
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "rocketship",
4
+
"apiLevel": 2,
5
+
"environment": "desktop",
6
+
"meta": {
7
+
"name": "Rocketship",
8
+
"tagline": "Adds new features when using rocketship",
9
+
"description": "**This extension only works on Linux when using rocketship:**\nhttps://github.com/moonlight-mod/rocketship\n\nAdds new features to the Discord Linux client with rocketship, such as a better screensharing experience.",
10
+
"authors": ["NotNite", "Cynosphere", "adryd"],
11
+
"deprecated": true
12
+
}
13
+
}
+6
-8
packages/core-extensions/src/settings/index.ts
+6
-8
packages/core-extensions/src/settings/index.ts
···
3
3
4
4
export const patches: Patch[] = [
5
5
{
6
-
find: ".UserSettingsSections.HOTSPOT_OPTIONS",
6
+
find: '"useGenerateUserSettingsSections"',
7
7
replace: {
8
-
match: /\.CUSTOM,element:(.+?)}\];return (.{1,2})/,
9
-
replacement: (_, lastElement, sections) =>
10
-
`.CUSTOM,element:${lastElement}}];return require("settings_settings").Settings._mutateSections(${sections})`
8
+
match: /(?<=\.push\(.+?\)}\)\)}\),)(.+?)}/,
9
+
replacement: (_, sections: string) => `require("settings_settings").Settings._mutateSections(${sections})}`
11
10
}
12
11
},
13
12
{
14
13
find: 'navId:"user-settings-cog",',
15
14
replace: {
16
-
match: /children:\[(.)\.map\(.+?\),children:.\((.)\)/,
15
+
match: /children:\[(\i)\.map\(.+?\),.*?children:\i\((\i)\)/,
17
16
replacement: (orig, sections, section) =>
18
17
`${orig.replace(
19
-
/Object\.values\(.\.UserSettingsSections\)/,
20
-
(orig) =>
21
-
`[...require("settings_settings").Settings.sectionNames,...${orig}]`
18
+
/Object\.values\(.\..+?\)/,
19
+
(orig) => `[...require("settings_settings").Settings.sectionNames,...${orig}]`
22
20
)}??${sections}.find(x=>x.section==${section})?._moonlight_submenu?.()`
23
21
}
24
22
}
+2
packages/core-extensions/src/settings/manifest.json
+2
packages/core-extensions/src/settings/manifest.json
+18
-12
packages/core-extensions/src/settings/webpackModules/settings.ts
+18
-12
packages/core-extensions/src/settings/webpackModules/settings.ts
···
1
-
import {
2
-
SettingsSection,
3
-
Settings as SettingsType
4
-
} from "@moonlight-mod/types/coreExtensions";
1
+
import { SettingsSection, Settings as SettingsType } from "@moonlight-mod/types/coreExtensions/settings";
2
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
5
3
6
4
export const Settings: SettingsType = {
7
5
ourSections: [],
8
6
sectionNames: [],
7
+
sectionMenuItems: {},
9
8
10
-
addSection: (section, label, element, color = null, pos, notice) => {
9
+
addSection: (section, label, element, color = null, pos, notice, onClick) => {
11
10
const data: SettingsSection = {
12
11
section,
13
12
label,
14
13
color,
15
14
element,
16
15
pos: pos ?? -4,
17
-
notice: notice
16
+
notice: notice,
17
+
onClick: onClick ?? (() => UserSettingsModalActionCreators.open(section))
18
18
};
19
19
20
20
Settings.ourSections.push(data);
21
-
Settings.sectionNames.push(label);
21
+
Settings.sectionNames.push(section);
22
22
return data;
23
23
},
24
+
addSectionMenuItems(section, ...newItems) {
25
+
const data = Settings.ourSections.find((x) => x.section === section);
26
+
if (!data || !("element" in data)) throw new Error(`Could not find section "${section}"`);
27
+
(Settings.sectionMenuItems[section] ??= []).push(...newItems);
28
+
data._moonlight_submenu ??= () => Settings.sectionMenuItems[section];
29
+
},
24
30
25
31
addDivider: (pos = null) => {
26
32
Settings.ourSections.push({
···
39
45
40
46
_mutateSections: (sections) => {
41
47
for (const section of Settings.ourSections) {
42
-
sections.splice(
43
-
section.pos < 0 ? sections.length + section.pos : section.pos,
44
-
0,
45
-
section
46
-
);
48
+
// Discord's `pos` only supports numbers, so lets call the function to get the position.
49
+
if (typeof section.pos === "function") {
50
+
section.pos = section.pos(sections);
51
+
}
52
+
sections.splice(section.pos < 0 ? sections.length + section.pos : section.pos, 0, section);
47
53
}
48
54
49
55
return sections;
+1
-1
packages/core-extensions/src/spacepack/index.ts
+1
-1
packages/core-extensions/src/spacepack/index.ts
+3
packages/core-extensions/src/spacepack/manifest.json
+3
packages/core-extensions/src/spacepack/manifest.json
···
1
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
2
3
"id": "spacepack",
4
+
"apiLevel": 2,
3
5
"meta": {
4
6
"name": "Spacepack",
5
7
"tagline": "Search utilities across all Webpack modules",
···
8
10
},
9
11
"settings": {
10
12
"addToGlobalScope": {
13
+
"advice": "reload",
11
14
"displayName": "Add to global scope",
12
15
"description": "Populates window.spacepack for easier usage in DevTools",
13
16
"type": "boolean",
+100
-62
packages/core-extensions/src/spacepack/webpackModules/spacepack.ts
+100
-62
packages/core-extensions/src/spacepack/webpackModules/spacepack.ts
···
1
-
import {
2
-
WebpackModule,
3
-
WebpackModuleFunc,
4
-
WebpackRequireType
5
-
} from "@moonlight-mod/types";
6
-
import { Spacepack } from "@moonlight-mod/types/coreExtensions";
1
+
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "@moonlight-mod/types";
2
+
import { Spacepack } from "@moonlight-mod/types/coreExtensions/spacepack";
3
+
import { processFind, testFind } from "@moonlight-mod/core/util/patch";
7
4
8
5
const webpackRequire = require as unknown as WebpackRequireType;
9
6
const cache = webpackRequire.c;
···
21
18
module = module.toString();
22
19
}
23
20
21
+
if (module in moonlight.moonmap.modules) {
22
+
module = moonlight.moonmap.modules[module];
23
+
}
24
+
24
25
if (!(module in modules)) {
25
26
return null;
26
27
}
···
36
37
"module",
37
38
"exports",
38
39
"require",
39
-
`(${funcStr}).apply(this, arguments)\n` +
40
-
`//# sourceURL=Webpack-Module-${module}`
40
+
`(${funcStr}).apply(this, arguments)\n` + `//# sourceURL=Webpack-Module/${module.slice(0, 3)}/${module}`
41
41
) as WebpackModuleFunc;
42
42
},
43
43
44
44
findByCode: (...args: (string | RegExp)[]) => {
45
-
return Object.entries(modules)
46
-
.filter(
47
-
([id, mod]) =>
48
-
!args.some(
49
-
(item) =>
50
-
!(item instanceof RegExp
51
-
? item.test(mod.toString())
52
-
: mod.toString().indexOf(item) !== -1)
53
-
)
54
-
)
45
+
const ret = Object.entries(modules)
46
+
.filter(([id, mod]) => !args.some((item) => !testFind(mod.toString(), processFind(item))))
55
47
.map(([id]) => {
56
48
//if (!(id in cache)) require(id);
57
49
//return cache[id];
···
60
52
try {
61
53
exports = require(id);
62
54
} catch (e) {
63
-
logger.error(`Error requiring module "${id}": `, e);
55
+
logger.error(`findByCode: Error requiring module "${id}": `, args, e);
64
56
}
65
57
66
58
return {
···
69
61
};
70
62
})
71
63
.filter((item) => item !== null);
64
+
65
+
if (ret.length === 0) {
66
+
logger.warn("findByCode: Got zero results for", args, new Error().stack!.substring(5));
67
+
}
68
+
69
+
return ret;
72
70
},
73
71
74
72
findByExports: (...args: string[]) => {
···
80
78
!(
81
79
exports !== undefined &&
82
80
exports !== window &&
83
-
(exports?.[item] ||
84
-
exports?.default?.[item] ||
85
-
exports?.Z?.[item] ||
86
-
exports?.ZP?.[item])
81
+
(exports?.[item] || exports?.default?.[item] || exports?.Z?.[item] || exports?.ZP?.[item])
87
82
)
88
83
)
89
84
)
···
95
90
},
96
91
97
92
findObjectFromKey: (exports: Record<string, any>, key: string) => {
93
+
let ret = null;
98
94
let subKey;
99
95
if (key.indexOf(".") > -1) {
100
96
const splitKey = key.split(".");
···
105
101
const obj = exports[exportKey];
106
102
if (obj && obj[key] !== undefined) {
107
103
if (subKey) {
108
-
if (obj[key][subKey]) return obj;
104
+
if (obj[key][subKey]) {
105
+
ret = obj;
106
+
break;
107
+
}
109
108
} else {
110
-
return obj;
109
+
ret = obj;
110
+
break;
111
111
}
112
112
}
113
113
}
114
-
return null;
114
+
115
+
if (ret == null) {
116
+
logger.warn("Failed to find object by key", key, "in", exports, new Error().stack!.substring(5));
117
+
}
118
+
119
+
return ret;
115
120
},
116
121
117
122
findObjectFromValue: (exports: Record<string, any>, value: any) => {
123
+
let ret = null;
118
124
for (const exportKey in exports) {
119
125
const obj = exports[exportKey];
120
126
// eslint-disable-next-line eqeqeq
121
-
if (obj == value) return obj;
127
+
if (obj == value) {
128
+
ret = obj;
129
+
break;
130
+
}
122
131
for (const subKey in obj) {
123
132
// eslint-disable-next-line eqeqeq
124
133
if (obj && obj[subKey] == value) {
125
-
return obj;
134
+
ret = obj;
135
+
break;
126
136
}
127
137
}
128
138
}
129
-
return null;
139
+
140
+
if (ret == null) {
141
+
logger.warn("Failed to find object by value", value, "in", exports, new Error().stack!.substring(5));
142
+
}
143
+
144
+
return ret;
130
145
},
131
146
132
-
findObjectFromKeyValuePair: (
133
-
exports: Record<string, any>,
134
-
key: string,
135
-
value: any
136
-
) => {
147
+
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => {
148
+
let ret = null;
137
149
for (const exportKey in exports) {
138
150
const obj = exports[exportKey];
139
151
// eslint-disable-next-line eqeqeq
140
152
if (obj && obj[key] == value) {
141
-
return obj;
153
+
ret = obj;
154
+
break;
142
155
}
143
156
}
157
+
158
+
if (ret == null) {
159
+
logger.warn(
160
+
"Failed to find object by key value pair",
161
+
key,
162
+
value,
163
+
"in",
164
+
exports,
165
+
new Error().stack!.substring(5)
166
+
);
167
+
}
168
+
144
169
return null;
145
170
},
146
171
147
-
findFunctionByStrings: (
148
-
exports: Record<string, any>,
149
-
...strings: (string | RegExp)[]
150
-
) => {
151
-
return (
172
+
findFunctionByStrings: (exports: Record<string, any>, ...strings: (string | RegExp)[]) => {
173
+
const ret =
152
174
Object.entries(exports).filter(
153
175
([index, func]) =>
154
-
typeof func === "function" &&
155
-
!strings.some(
156
-
(query) =>
157
-
!(query instanceof RegExp
158
-
? func.toString().match(query)
159
-
: func.toString().includes(query))
160
-
)
161
-
)?.[0]?.[1] ?? null
162
-
);
176
+
typeof func === "function" && !strings.some((query) => !testFind(func.toString(), processFind(query)))
177
+
)?.[0]?.[1] ?? null;
178
+
179
+
if (ret == null) {
180
+
logger.warn("Failed to find function by strings", strings, "in", exports, new Error().stack!.substring(5));
181
+
}
182
+
183
+
return ret;
163
184
},
164
185
165
-
lazyLoad: (find: string | RegExp | (string | RegExp)[], match: RegExp) => {
166
-
const module = Array.isArray(find)
167
-
? spacepack.findByCode(...find)
168
-
: spacepack.findByCode(find);
169
-
if (module.length < 1) return Promise.reject("Find failed");
186
+
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => {
187
+
chunk = processFind(chunk);
188
+
module = processFind(module);
170
189
171
-
const findId = module[0].id;
190
+
const mod = Array.isArray(find) ? spacepack.findByCode(...find) : spacepack.findByCode(find);
191
+
if (mod.length < 1) {
192
+
logger.warn("lazyLoad: Module find failed", find, chunk, module, new Error().stack!.substring(5));
193
+
return Promise.reject("Module find failed");
194
+
}
195
+
196
+
const findId = mod[0].id;
172
197
const findCode = webpackRequire.m[findId].toString().replace(/\n/g, "");
173
198
174
-
const matchResult = findCode.match(match);
175
-
if (!matchResult) return Promise.reject("Match failed");
199
+
let chunkIds;
200
+
if (chunk.flags.includes("g")) {
201
+
chunkIds = [...findCode.matchAll(chunk)].map(([, id]) => id);
202
+
} else {
203
+
const match = findCode.match(chunk);
204
+
if (match) chunkIds = [...match[0].matchAll(/"(\d+)"/g)].map(([, id]) => id);
205
+
}
176
206
177
-
const matchId = matchResult[1];
178
-
return webpackRequire.el(matchId).then(() => webpackRequire(matchId));
207
+
if (!chunkIds || chunkIds.length === 0) {
208
+
logger.warn("lazyLoad: Chunk ID match failed", find, chunk, module, new Error().stack!.substring(5));
209
+
return Promise.reject("Chunk ID match failed");
210
+
}
211
+
212
+
const moduleId = findCode.match(module)?.[1];
213
+
if (!moduleId) {
214
+
logger.warn("lazyLoad: Module ID match failed", find, chunk, module, new Error().stack!.substring(5));
215
+
return Promise.reject("Module ID match failed");
216
+
}
217
+
218
+
return Promise.all(chunkIds.map((c) => webpackRequire.e(c))).then(() => webpackRequire(moduleId));
179
219
},
180
220
181
221
filterReal: (modules: WebpackModule[]) => {
···
183
223
}
184
224
};
185
225
186
-
if (
187
-
moonlight.getConfigOption<boolean>("spacepack", "addToGlobalScope") === true
188
-
) {
226
+
if (moonlight.getConfigOption<boolean>("spacepack", "addToGlobalScope") === true) {
189
227
window.spacepack = spacepack;
190
228
}
191
229
+4
-1
packages/core-extensions/tsconfig.json
+4
-1
packages/core-extensions/tsconfig.json
+10
-3
packages/injector/package.json
+10
-3
packages/injector/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/injector",
3
3
"private": true,
4
+
"engines": {
5
+
"node": ">=22",
6
+
"pnpm": ">=10",
7
+
"npm": "pnpm",
8
+
"yarn": "pnpm"
9
+
},
4
10
"dependencies": {
5
-
"@moonlight-mod/types": "workspace:*",
6
-
"@moonlight-mod/core": "workspace:*"
7
-
}
11
+
"@moonlight-mod/core": "workspace:*",
12
+
"@moonlight-mod/types": "workspace:*"
13
+
},
14
+
"engineStrict": true
8
15
}
+173
-113
packages/injector/src/index.ts
+173
-113
packages/injector/src/index.ts
···
5
5
app
6
6
} from "electron";
7
7
import Module from "node:module";
8
-
import { constants } from "@moonlight-mod/types";
9
-
import { readConfig } from "@moonlight-mod/core/config";
8
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
9
+
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
10
10
import { getExtensions } from "@moonlight-mod/core/extension";
11
-
import Logger from "@moonlight-mod/core/util/logger";
12
-
import {
13
-
loadExtensions,
14
-
loadProcessedExtensions
15
-
} from "@moonlight-mod/core/extension/loader";
11
+
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
12
+
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
16
13
import EventEmitter from "node:events";
17
-
import { join, resolve } from "node:path";
14
+
import path from "node:path";
15
+
import persist from "@moonlight-mod/core/persist";
16
+
import createFS from "@moonlight-mod/core/fs";
17
+
import { getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
18
+
import { getConfigPath, getExtensionsPath, getMoonlightDir } from "@moonlight-mod/core/util/data";
18
19
19
20
const logger = new Logger("injector");
20
21
21
22
let oldPreloadPath: string | undefined;
22
23
let corsAllow: string[] = [];
23
-
let isMoonlightDesktop = false;
24
-
let hasOpenAsar = false;
25
-
let openAsarConfigPreload: string | undefined;
24
+
let blockedUrls: RegExp[] = [];
25
+
let injectorConfig: InjectorConfig | undefined;
26
+
27
+
const scriptUrls = ["web.", "sentry."];
28
+
const blockedScripts = new Set<string>();
26
29
27
30
ipcMain.on(constants.ipcGetOldPreloadPath, (e) => {
28
31
e.returnValue = oldPreloadPath;
29
32
});
33
+
30
34
ipcMain.on(constants.ipcGetAppData, (e) => {
31
35
e.returnValue = app.getPath("appData");
32
36
});
33
-
ipcMain.on(constants.ipcGetIsMoonlightDesktop, (e) => {
34
-
e.returnValue = isMoonlightDesktop;
37
+
ipcMain.on(constants.ipcGetInjectorConfig, (e) => {
38
+
e.returnValue = injectorConfig;
35
39
});
36
40
ipcMain.handle(constants.ipcMessageBox, (_, opts) => {
37
41
electron.dialog.showMessageBoxSync(opts);
···
40
44
corsAllow = list;
41
45
});
42
46
43
-
function patchCsp(headers: Record<string, string[]>) {
44
-
const directives = [
45
-
"style-src",
46
-
"connect-src",
47
-
"img-src",
48
-
"font-src",
49
-
"media-src",
50
-
"worker-src",
51
-
"prefetch-src"
52
-
];
53
-
const values = ["*", "blob:", "data:", "'unsafe-inline'", "disclip:"];
47
+
const reEscapeRegExp = /[\\^$.*+?()[\]{}|]/g;
48
+
const reMatchPattern = /^(?<scheme>\*|[a-z][a-z0-9+.-]*):\/\/(?<host>.+?)\/(?<path>.+)?$/;
49
+
50
+
const escapeRegExp = (s: string) => s.replace(reEscapeRegExp, "\\$&");
51
+
ipcMain.handle(constants.ipcSetBlockedList, (_, list: string[]) => {
52
+
// We compile the patterns into a RegExp based on a janky match pattern-like syntax
53
+
const compiled = list
54
+
.map((pattern) => {
55
+
const match = pattern.match(reMatchPattern);
56
+
if (!match?.groups) return;
57
+
58
+
let regex = "";
59
+
if (match.groups.scheme === "*") regex += ".+?";
60
+
else regex += escapeRegExp(match.groups.scheme);
61
+
regex += ":\\/\\/";
62
+
63
+
const parts = match.groups.host.split(".");
64
+
if (parts[0] === "*") {
65
+
parts.shift();
66
+
regex += "(?:.+?\\.)?";
67
+
}
68
+
regex += escapeRegExp(parts.join("."));
69
+
70
+
regex += "\\/" + escapeRegExp(match.groups.path).replace("\\*", ".*?");
71
+
72
+
return new RegExp("^" + regex + "$");
73
+
})
74
+
.filter(Boolean) as RegExp[];
75
+
76
+
blockedUrls = compiled;
77
+
});
78
+
79
+
function patchCsp(headers: Record<string, string[]>, extensionCspOverrides: Record<string, string[]>) {
80
+
const directives = ["script-src", "style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"];
81
+
const values = ["*", "blob:", "data:", "'unsafe-inline'", "'unsafe-eval'", "disclip:"];
54
82
55
83
const csp = "content-security-policy";
56
84
if (headers[csp] == null) return;
···
67
95
68
96
for (const directive of directives) {
69
97
parts[directive] = values;
98
+
}
99
+
100
+
for (const [directive, urls] of Object.entries(extensionCspOverrides)) {
101
+
parts[directive] ??= [];
102
+
parts[directive].push(...urls);
70
103
}
71
104
72
105
const stringified = Object.entries<string[]>(parts)
···
77
110
headers[csp] = [stringified];
78
111
}
79
112
80
-
function removeOpenAsarEventIfPresent(eventHandler: (...args: any[]) => void) {
81
-
const code = eventHandler.toString();
82
-
if (code.indexOf("bw.webContents.on('dom-ready'") > -1) {
83
-
electron.app.off("browser-window-created", eventHandler);
84
-
}
85
-
}
86
-
87
113
class BrowserWindow extends ElectronBrowserWindow {
88
114
constructor(opts: BrowserWindowConstructorOptions) {
89
-
oldPreloadPath = opts.webPreferences!.preload;
115
+
const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;
90
116
91
-
// Only overwrite preload if its the actual main client window
92
-
if (opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1) {
117
+
if (isMainWindow) {
118
+
if (!oldPreloadPath) oldPreloadPath = opts.webPreferences!.preload;
93
119
opts.webPreferences!.preload = require.resolve("./node-preload.js");
94
120
}
95
121
96
122
// Event for modifying window options
97
-
moonlightHost.events.emit("window-options", opts);
123
+
moonlightHost.events.emit("window-options", opts, isMainWindow);
98
124
99
125
super(opts);
100
126
101
127
// Event for when a window is created
102
-
moonlightHost.events.emit("window-created", this);
128
+
moonlightHost.events.emit("window-created", this, isMainWindow);
129
+
130
+
const extensionCspOverrides: Record<string, string[]> = {};
131
+
132
+
{
133
+
const extCsps = moonlightHost.processedExtensions.extensions.map((x) => x.manifest.csp ?? {});
134
+
for (const csp of extCsps) {
135
+
for (const [directive, urls] of Object.entries(csp)) {
136
+
extensionCspOverrides[directive] ??= [];
137
+
extensionCspOverrides[directive].push(...urls);
138
+
}
139
+
}
140
+
}
103
141
104
142
this.webContents.session.webRequest.onHeadersReceived((details, cb) => {
105
143
if (details.responseHeaders != null) {
106
144
// Patch CSP so things can use externally hosted assets
107
145
if (details.resourceType === "mainFrame") {
108
-
patchCsp(details.responseHeaders);
146
+
patchCsp(details.responseHeaders, extensionCspOverrides);
109
147
}
110
148
111
149
// Allow plugins to bypass CORS for specific URLs
112
150
if (corsAllow.some((x) => details.url.startsWith(x))) {
113
-
details.responseHeaders["access-control-allow-origin"] = ["*"];
151
+
if (!details.responseHeaders) details.responseHeaders = {};
152
+
153
+
// Work around HTTP header case sensitivity by reusing the header name if it exists
154
+
// https://github.com/moonlight-mod/moonlight/issues/201
155
+
const fallback = "access-control-allow-origin";
156
+
const key = Object.keys(details.responseHeaders).find((h) => h.toLowerCase() === fallback) ?? fallback;
157
+
details.responseHeaders[key] = ["*"];
114
158
}
159
+
160
+
moonlightHost.events.emit("headers-received", details, isMainWindow);
115
161
116
162
cb({ cancel: false, responseHeaders: details.responseHeaders });
117
163
}
118
164
});
119
165
120
-
if (hasOpenAsar) {
121
-
// Remove DOM injections
122
-
// Settings can still be opened via:
123
-
// `DiscordNative.ipc.send("DISCORD_UPDATED_QUOTES","o")`
124
-
// @ts-expect-error Electron internals
125
-
const events = electron.app._events["browser-window-created"];
126
-
if (Array.isArray(events)) {
127
-
for (const event of events) {
128
-
removeOpenAsarEventIfPresent(event);
166
+
this.webContents.session.webRequest.onBeforeRequest((details, cb) => {
167
+
/*
168
+
In order to get moonlight loading to be truly async, we prevent Discord
169
+
from loading their scripts immediately. We block the requests, keep note
170
+
of their URLs, and then send them off to node-preload when we get all of
171
+
them. node-preload then loads node side, web side, and then recreates
172
+
the script elements to cause them to re-fetch.
173
+
174
+
The browser extension also does this, but in a background script (see
175
+
packages/browser/src/background.js - we should probably get this working
176
+
with esbuild someday).
177
+
*/
178
+
if (details.resourceType === "script" && isMainWindow) {
179
+
const url = new URL(details.url);
180
+
const hasUrl = scriptUrls.some((scriptUrl) => {
181
+
return (
182
+
details.url.includes(scriptUrl) &&
183
+
!url.searchParams.has("inj") &&
184
+
(url.host.endsWith("discord.com") || url.host.endsWith("discordapp.com"))
185
+
);
186
+
});
187
+
if (hasUrl) blockedScripts.add(details.url);
188
+
189
+
if (blockedScripts.size === scriptUrls.length) {
190
+
setTimeout(() => {
191
+
logger.debug("Kicking off node-preload");
192
+
this.webContents.send(constants.ipcNodePreloadKickoff, Array.from(blockedScripts));
193
+
blockedScripts.clear();
194
+
}, 0);
129
195
}
130
-
} else if (events != null) {
131
-
removeOpenAsarEventIfPresent(events);
196
+
197
+
if (hasUrl) return cb({ cancel: true });
132
198
}
133
199
134
-
// Config screen fails to context bridge properly
135
-
// Less than ideal, but better than disabling it everywhere
136
-
if (opts.webPreferences!.preload === openAsarConfigPreload) {
137
-
opts.webPreferences!.sandbox = false;
138
-
}
139
-
}
200
+
// Allow plugins to block some URLs,
201
+
// this is needed because multiple webRequest handlers cannot be registered at once
202
+
cb({ cancel: blockedUrls.some((u) => u.test(details.url)) });
203
+
});
140
204
}
141
205
}
142
206
···
157
221
writable: false
158
222
});
159
223
160
-
export async function inject(asarPath: string) {
161
-
isMoonlightDesktop = asarPath === "moonlightDesktop";
224
+
type InjectorConfig = { disablePersist?: boolean; disableLoad?: boolean };
225
+
export async function inject(asarPath: string, _injectorConfig?: InjectorConfig) {
226
+
injectorConfig = _injectorConfig;
227
+
228
+
global.moonlightNodeSandboxed = {
229
+
fs: createFS(),
230
+
// These aren't supposed to be used from host
231
+
addCors() {},
232
+
addBlocked() {}
233
+
};
234
+
162
235
try {
163
-
const config = readConfig();
164
-
const extensions = getExtensions();
236
+
let config = await readConfig();
237
+
initLogger(config);
238
+
const extensions = await getExtensions();
239
+
const processedExtensions = await loadExtensions(extensions);
240
+
const moonlightDir = await getMoonlightDir();
241
+
const extensionsPath = await getExtensionsPath();
165
242
166
243
// Duplicated in node-preload... oops
167
-
// eslint-disable-next-line no-inner-declarations
168
244
function getConfig(ext: string) {
169
245
const val = config.extensions[ext];
170
246
if (val == null || typeof val === "boolean") return undefined;
171
247
return val.config;
172
248
}
173
-
174
249
global.moonlightHost = {
250
+
get config() {
251
+
return config;
252
+
},
253
+
extensions,
254
+
processedExtensions,
175
255
asarPath,
176
-
config,
177
256
events: new EventEmitter(),
178
-
extensions,
179
-
processedExtensions: {
180
-
extensions: [],
181
-
dependencyGraph: new Map()
182
-
},
257
+
258
+
version: MOONLIGHT_VERSION,
259
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
183
260
184
261
getConfig,
185
-
getConfigOption: <T>(ext: string, name: string) => {
186
-
const config = getConfig(ext);
187
-
if (config == null) return undefined;
188
-
const option = config[name];
189
-
if (option == null) return undefined;
190
-
return option as T;
262
+
getConfigPath,
263
+
getConfigOption(ext, name) {
264
+
const manifest = getManifest(extensions, ext);
265
+
return getConfigOption(ext, name, config, manifest?.settings);
191
266
},
192
-
getLogger: (id: string) => {
267
+
setConfigOption(ext, name, value) {
268
+
setConfigOption(config, ext, name, value);
269
+
this.writeConfig(config);
270
+
},
271
+
async writeConfig(newConfig) {
272
+
await writeConfig(newConfig);
273
+
config = newConfig;
274
+
},
275
+
276
+
getLogger(id) {
193
277
return new Logger(id);
278
+
},
279
+
getMoonlightDir() {
280
+
return moonlightDir;
281
+
},
282
+
getExtensionDir: (ext: string) => {
283
+
return path.join(extensionsPath, ext);
194
284
}
195
285
};
196
286
197
-
// Check if we're running with OpenAsar
198
-
try {
199
-
require.resolve(join(asarPath, "updater", "updater.js"));
200
-
hasOpenAsar = true;
201
-
openAsarConfigPreload = resolve(asarPath, "config", "preload.js");
202
-
// eslint-disable-next-line no-empty
203
-
} catch {}
204
-
205
-
if (hasOpenAsar) {
206
-
// Disable command line switch injection
207
-
// I personally think that the command line switches should be vetted by
208
-
// the user and not just "trust that these are sane defaults that work
209
-
// always". I'm not hating on Ducko or anything, I'm just opinionated.
210
-
// Someone can always make a command line modifier plugin, thats the point
211
-
// of having host modules.
212
-
try {
213
-
const cmdSwitchesPath = require.resolve(
214
-
join(asarPath, "cmdSwitches.js")
215
-
);
216
-
require.cache[cmdSwitchesPath] = new Module(
217
-
cmdSwitchesPath,
218
-
require.cache[require.resolve(asarPath)]
219
-
);
220
-
require.cache[cmdSwitchesPath]!.exports = () => {};
221
-
} catch (error) {
222
-
logger.error("Failed to disable OpenAsar's command line flags:", error);
223
-
}
224
-
}
225
-
226
287
patchElectron();
227
288
228
-
global.moonlightHost.processedExtensions = await loadExtensions(extensions);
229
289
await loadProcessedExtensions(global.moonlightHost.processedExtensions);
230
290
} catch (error) {
231
291
logger.error("Failed to inject:", error);
232
292
}
233
293
234
-
if (isMoonlightDesktop) return;
294
+
if (injectorConfig?.disablePersist !== true) {
295
+
persist(asarPath);
296
+
}
235
297
236
-
// Need to do this instead of require() or it breaks require.main
237
-
// @ts-expect-error Module internals
238
-
Module._load(asarPath, Module, true);
298
+
if (injectorConfig?.disableLoad !== true) {
299
+
// Need to do this instead of require() or it breaks require.main
300
+
// @ts-expect-error Module internals
301
+
Module._load(asarPath, Module, true);
302
+
}
239
303
}
240
304
241
305
function patchElectron() {
···
249
313
configurable: false
250
314
});
251
315
} else {
252
-
Object.defineProperty(
253
-
electronClone,
254
-
property,
255
-
Object.getOwnPropertyDescriptor(electron, property)!
256
-
);
316
+
Object.defineProperty(electronClone, property, Object.getOwnPropertyDescriptor(electron, property)!);
257
317
}
258
318
}
259
319
+8
-1
packages/node-preload/package.json
+8
-1
packages/node-preload/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/node-preload",
3
3
"private": true,
4
+
"engines": {
5
+
"node": ">=22",
6
+
"pnpm": ">=10",
7
+
"npm": "pnpm",
8
+
"yarn": "pnpm"
9
+
},
4
10
"dependencies": {
5
11
"@moonlight-mod/core": "workspace:*",
6
12
"@moonlight-mod/types": "workspace:*"
7
-
}
13
+
},
14
+
"engineStrict": true
8
15
}
+144
-46
packages/node-preload/src/index.ts
+144
-46
packages/node-preload/src/index.ts
···
1
1
import { webFrame, ipcRenderer, contextBridge } from "electron";
2
-
import fs from "fs";
3
-
import path from "path";
2
+
import fs from "node:fs";
3
+
import path from "node:path";
4
4
5
5
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
6
-
import { constants } from "@moonlight-mod/types";
6
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
7
7
import { getExtensions } from "@moonlight-mod/core/extension";
8
-
import { getExtensionsPath } from "@moonlight-mod/core/util/data";
9
-
import Logger from "@moonlight-mod/core/util/logger";
10
-
import {
11
-
loadExtensions,
12
-
loadProcessedExtensions
13
-
} from "@moonlight-mod/core/extension/loader";
8
+
import { getExtensionsPath, getMoonlightDir } from "@moonlight-mod/core/util/data";
9
+
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
10
+
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
11
+
import createFS from "@moonlight-mod/core/fs";
12
+
import { registerCors, registerBlocked, getDynamicCors } from "@moonlight-mod/core/cors";
13
+
import { getConfig, getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
14
+
import { NodeEventPayloads, NodeEventType } from "@moonlight-mod/types/core/event";
15
+
import { createEventEmitter } from "@moonlight-mod/core/util/event";
16
+
17
+
let initialized = false;
18
+
let logger: Logger;
19
+
20
+
function setCors() {
21
+
const data = getDynamicCors();
22
+
ipcRenderer.invoke(constants.ipcSetCorsList, data.cors);
23
+
ipcRenderer.invoke(constants.ipcSetBlockedList, data.blocked);
24
+
}
14
25
15
26
async function injectGlobals() {
16
-
const config = readConfig();
17
-
const extensions = getExtensions();
18
-
const processed = await loadExtensions(extensions);
27
+
global.moonlightNodeSandboxed = {
28
+
fs: createFS(),
29
+
addCors(url) {
30
+
registerCors(url);
31
+
if (initialized) setCors();
32
+
},
33
+
addBlocked(url) {
34
+
registerBlocked(url);
35
+
if (initialized) setCors();
36
+
}
37
+
};
19
38
20
-
function getConfig(ext: string) {
21
-
const val = config.extensions[ext];
22
-
if (val == null || typeof val === "boolean") return undefined;
23
-
return val.config;
24
-
}
39
+
let config = await readConfig();
40
+
initLogger(config);
41
+
logger = new Logger("node-preload");
42
+
43
+
const extensions = await getExtensions();
44
+
const processedExtensions = await loadExtensions(extensions);
45
+
const moonlightDir = await getMoonlightDir();
46
+
const extensionsPath = await getExtensionsPath();
25
47
26
48
global.moonlightNode = {
27
-
config,
28
-
extensions: getExtensions(),
29
-
processedExtensions: processed,
49
+
get config() {
50
+
return config;
51
+
},
52
+
extensions,
53
+
processedExtensions,
30
54
nativesCache: {},
31
-
getConfig,
32
-
getConfigOption: <T>(ext: string, name: string) => {
33
-
const config = getConfig(ext);
34
-
if (config == null) return undefined;
35
-
const option = config[name];
36
-
if (option == null) return undefined;
37
-
return option as T;
55
+
isBrowser: false,
56
+
events: createEventEmitter<NodeEventType, NodeEventPayloads>(),
57
+
58
+
version: MOONLIGHT_VERSION,
59
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
60
+
61
+
getConfig(ext) {
62
+
return getConfig(ext, config);
63
+
},
64
+
getConfigOption(ext, name) {
65
+
const manifest = getManifest(extensions, ext);
66
+
return getConfigOption(ext, name, config, manifest?.settings);
67
+
},
68
+
async setConfigOption(ext, name, value) {
69
+
setConfigOption(config, ext, name, value);
70
+
await this.writeConfig(config);
71
+
},
72
+
async writeConfig(newConfig) {
73
+
await writeConfig(newConfig);
74
+
config = newConfig;
75
+
this.events.dispatchEvent(NodeEventType.ConfigSaved, newConfig);
38
76
},
77
+
39
78
getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
40
79
getLogger: (id: string) => {
41
80
return new Logger(id);
42
81
},
43
-
44
-
getExtensionDir: (ext: string) => {
45
-
const extPath = getExtensionsPath();
46
-
return path.join(extPath, ext);
82
+
getMoonlightDir() {
83
+
return moonlightDir;
47
84
},
48
-
writeConfig
85
+
getExtensionDir: (ext: string) => {
86
+
return path.join(extensionsPath, ext);
87
+
}
49
88
};
50
89
51
-
await loadProcessedExtensions(processed);
90
+
await loadProcessedExtensions(processedExtensions);
52
91
contextBridge.exposeInMainWorld("moonlightNode", moonlightNode);
53
92
54
-
const extCors = moonlightNode.processedExtensions.extensions
55
-
.map((x) => x.manifest.cors ?? [])
56
-
.flat();
93
+
const extCors = moonlightNode.processedExtensions.extensions.flatMap((x) => x.manifest.cors ?? []);
94
+
for (const cors of extCors) {
95
+
registerCors(cors);
96
+
}
57
97
58
98
for (const repo of moonlightNode.config.repositories) {
59
99
const url = new URL(repo);
60
100
url.pathname = "/";
61
-
extCors.push(url.toString());
101
+
registerCors(url.toString());
62
102
}
63
103
64
-
ipcRenderer.invoke(constants.ipcSetCorsList, extCors);
104
+
const extBlocked = moonlightNode.processedExtensions.extensions.flatMap((e) => e.manifest.blocked ?? []);
105
+
for (const blocked of extBlocked) {
106
+
registerBlocked(blocked);
107
+
}
108
+
109
+
setCors();
110
+
111
+
initialized = true;
65
112
}
66
113
67
114
async function loadPreload() {
68
115
const webPreloadPath = path.join(__dirname, "web-preload.js");
69
116
const webPreload = fs.readFileSync(webPreloadPath, "utf8");
70
117
await webFrame.executeJavaScript(webPreload);
118
+
119
+
const func = await webFrame.executeJavaScript("async () => { await window._moonlightWebLoad(); }");
120
+
await func();
71
121
}
72
122
73
-
async function init(oldPreloadPath: string) {
123
+
async function init() {
74
124
try {
75
125
await injectGlobals();
76
126
await loadPreload();
···
81
131
message: message
82
132
});
83
133
}
134
+
}
84
135
85
-
// Let Discord start even if we fail
86
-
if (oldPreloadPath) require(oldPreloadPath);
136
+
const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
137
+
const isOverlay = window.location.href.indexOf("discord_overlay") > -1;
138
+
139
+
if (isOverlay) {
140
+
// The overlay has an inline script tag to call to DiscordNative, so we'll
141
+
// just load it immediately. Somehow moonlight still loads in this env, I
142
+
// have no idea why - so I suspect it's just forwarding render calls or
143
+
// something from the original process
144
+
require(oldPreloadPath);
145
+
} else {
146
+
ipcRenderer.on(constants.ipcNodePreloadKickoff, (_, blockedScripts: string[]) => {
147
+
(async () => {
148
+
try {
149
+
await init();
150
+
logger.debug("Blocked scripts:", blockedScripts);
151
+
152
+
const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
153
+
logger.debug("Old preload path:", oldPreloadPath);
154
+
if (oldPreloadPath) require(oldPreloadPath);
155
+
156
+
// Do this to get global.DiscordNative assigned
157
+
// @ts-expect-error Lying to discord_desktop_core
158
+
process.emit("loaded");
159
+
160
+
function replayScripts() {
161
+
const scripts = [...document.querySelectorAll("script")].filter(
162
+
(script) => script.src && blockedScripts.some((url) => url.includes(script.src))
163
+
);
164
+
165
+
blockedScripts.reverse();
166
+
for (const url of blockedScripts) {
167
+
if (url.includes("/sentry.")) continue;
168
+
169
+
const script = scripts.find((script) => url.includes(script.src))!;
170
+
const newScript = document.createElement("script");
171
+
for (const attr of script.attributes) {
172
+
if (attr.name === "src") attr.value += "?inj";
173
+
newScript.setAttribute(attr.name, attr.value);
174
+
}
175
+
script.remove();
176
+
document.documentElement.appendChild(newScript);
177
+
}
178
+
}
179
+
180
+
if (document.readyState === "complete") {
181
+
replayScripts();
182
+
} else {
183
+
window.addEventListener("load", replayScripts);
184
+
}
185
+
} catch (e) {
186
+
logger.error("Error restoring original scripts:", e);
187
+
}
188
+
})();
189
+
});
87
190
}
88
-
89
-
const oldPreloadPath: string = ipcRenderer.sendSync(
90
-
constants.ipcGetOldPreloadPath
91
-
);
92
-
init(oldPreloadPath);
+4
-1
packages/node-preload/tsconfig.json
+4
-1
packages/node-preload/tsconfig.json
+15
-7
packages/types/package.json
+15
-7
packages/types/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/types",
3
-
"version": "1.1.6",
4
-
"main": "./src/index.ts",
5
-
"types": "./src/index.ts",
3
+
"version": "1.3.17",
6
4
"exports": {
7
5
".": "./src/index.ts",
8
6
"./import": "./src/import.d.ts",
9
7
"./*": "./src/*.ts"
10
8
},
9
+
"main": "./src/index.ts",
10
+
"types": "./src/index.ts",
11
+
"engineStrict": false,
12
+
"engines": {
13
+
"node": ">=22",
14
+
"pnpm": ">=10",
15
+
"npm": "pnpm",
16
+
"yarn": "pnpm"
17
+
},
11
18
"dependencies": {
12
-
"@types/flux": "^3.1.12",
13
-
"@types/node": "^20.6.2",
14
-
"@types/react": "^18.2.22",
15
-
"csstype": "^3.1.2",
19
+
"@moonlight-mod/lunast": "^1.0.1",
20
+
"@moonlight-mod/mappings": "^1.1.25",
21
+
"@moonlight-mod/moonmap": "^1.0.5",
22
+
"@types/react": "^18.3.10",
23
+
"csstype": "^3.1.3",
16
24
"standalone-electron-types": "^1.0.0"
17
25
}
18
26
}
+56
-3
packages/types/src/config.ts
+56
-3
packages/types/src/config.ts
···
6
6
patchAll?: boolean;
7
7
};
8
8
9
-
export type ConfigExtensions =
10
-
| { [key: string]: boolean }
11
-
| { [key: string]: ConfigExtension };
9
+
export type ConfigExtensions = { [key: string]: boolean } | { [key: string]: ConfigExtension };
12
10
13
11
export type ConfigExtension = {
14
12
enabled: boolean;
···
19
17
Boolean = "boolean",
20
18
Number = "number",
21
19
String = "string",
20
+
MultilineString = "multilinestring",
22
21
Select = "select",
23
22
MultiSelect = "multiselect",
24
23
List = "list",
···
34
33
};
35
34
36
35
export type BooleanSettingType = {
36
+
/**
37
+
* Displays as a simple switch.
38
+
*/
37
39
type: ExtensionSettingType.Boolean;
38
40
default?: boolean;
39
41
};
40
42
41
43
export type NumberSettingType = {
44
+
/**
45
+
* Displays as a simple slider.
46
+
*/
42
47
type: ExtensionSettingType.Number;
43
48
default?: number;
44
49
min?: number;
···
46
51
};
47
52
48
53
export type StringSettingType = {
54
+
/**
55
+
* Displays as a single line string input.
56
+
*/
49
57
type: ExtensionSettingType.String;
50
58
default?: string;
51
59
};
52
60
61
+
export type MultilineTextInputSettingType = {
62
+
/**
63
+
* Displays as a multiple line string input.
64
+
*/
65
+
type: ExtensionSettingType.MultilineString;
66
+
default?: string;
67
+
};
68
+
53
69
export type SelectSettingType = {
70
+
/**
71
+
* A dropdown to pick between one of many values.
72
+
*/
54
73
type: ExtensionSettingType.Select;
55
74
options: SelectOption[];
56
75
default?: string;
57
76
};
58
77
59
78
export type MultiSelectSettingType = {
79
+
/**
80
+
* A dropdown to pick multiple values.
81
+
*/
60
82
type: ExtensionSettingType.MultiSelect;
61
83
options: string[];
62
84
default?: string[];
63
85
};
64
86
65
87
export type ListSettingType = {
88
+
/**
89
+
* A list of strings that the user can add or remove from.
90
+
*/
66
91
type: ExtensionSettingType.List;
67
92
default?: string[];
68
93
};
69
94
70
95
export type DictionarySettingType = {
96
+
/**
97
+
* A dictionary (key-value pair) that the user can add or remove from.
98
+
*/
71
99
type: ExtensionSettingType.Dictionary;
72
100
default?: Record<string, string>;
73
101
};
74
102
75
103
export type CustomSettingType = {
104
+
/**
105
+
* A custom component.
106
+
* You can use the registerConfigComponent function in the Moonbase API to register a React component to render here.
107
+
*/
76
108
type: ExtensionSettingType.Custom;
77
109
default?: any;
78
110
};
79
111
112
+
export enum ExtensionSettingsAdvice {
113
+
None = "none",
114
+
Reload = "reload",
115
+
Restart = "restart"
116
+
}
117
+
80
118
export type ExtensionSettingsManifest = {
119
+
/**
120
+
* A human friendly name for the setting.
121
+
*/
81
122
displayName?: string;
123
+
124
+
/**
125
+
* A longer description for the setting.
126
+
* Markdown is not supported.
127
+
*/
82
128
description?: string;
129
+
130
+
/**
131
+
* The "advice" to give upon changing this setting.
132
+
* Can be configured to reload the client, restart the client, or do nothing.
133
+
*/
134
+
advice?: ExtensionSettingsAdvice;
83
135
} & (
84
136
| BooleanSettingType
85
137
| NumberSettingType
86
138
| StringSettingType
139
+
| MultilineTextInputSettingType
87
140
| SelectSettingType
88
141
| MultiSelectSettingType
89
142
| ListSettingType
+11
-1
packages/types/src/constants.ts
+11
-1
packages/types/src/constants.ts
···
2
2
export const distDir = "dist";
3
3
export const coreExtensionsDir = "core-extensions";
4
4
export const repoUrlFile = ".moonlight-repo-url";
5
+
export const installedVersionFile = ".moonlight-installed-version";
5
6
7
+
export const ipcNodePreloadKickoff = "_moonlight_nodePreloadKickoff";
6
8
export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";
9
+
7
10
export const ipcGetAppData = "_moonlight_getAppData";
8
-
export const ipcGetIsMoonlightDesktop = "_moonlight_getIsMoonlightDesktop";
11
+
export const ipcGetInjectorConfig = "_moonlight_getInjectorConfig";
9
12
export const ipcMessageBox = "_moonlight_messageBox";
10
13
export const ipcSetCorsList = "_moonlight_setCorsList";
14
+
export const ipcSetBlockedList = "_moonlight_setBlockedList";
15
+
16
+
export const apiLevel = 2;
17
+
18
+
export const mainRepo = "https://moonlight-mod.github.io/extensions-dist/repo.json";
19
+
// If you're updating this, update `defaultConfig` in core as well
20
+
export const builtinExtensions = ["moonbase", "disableSentry", "noTrack", "noHideToken"];
+30
packages/types/src/core/event.ts
+30
packages/types/src/core/event.ts
···
1
+
import { Config } from "../config";
2
+
import { WebpackModuleFunc, WebpackRequireType } from "../discord";
3
+
4
+
export interface MoonlightEventEmitter<EventId extends string = string, EventData = Record<EventId, any>> {
5
+
dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => void;
6
+
addEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => void;
7
+
removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => void;
8
+
}
9
+
10
+
export enum WebEventType {
11
+
ChunkLoad = "chunkLoad",
12
+
ExtensionLoad = "extensionLoad"
13
+
}
14
+
15
+
export type WebEventPayloads = {
16
+
[WebEventType.ChunkLoad]: {
17
+
chunkId?: number[];
18
+
modules: { [id: string]: WebpackModuleFunc };
19
+
require?: (require: WebpackRequireType) => any;
20
+
};
21
+
[WebEventType.ExtensionLoad]: string;
22
+
};
23
+
24
+
export enum NodeEventType {
25
+
ConfigSaved = "configSaved"
26
+
}
27
+
28
+
export type NodeEventPayloads = {
29
+
[NodeEventType.ConfigSaved]: Config;
30
+
};
+13
packages/types/src/coreExtensions/appPanels.ts
+13
packages/types/src/coreExtensions/appPanels.ts
···
1
+
export type AppPanels = {
2
+
/**
3
+
* Registers a new panel to be displayed around the user/voice controls.
4
+
* @param section A unique name for your section
5
+
* @param element A React component
6
+
*/
7
+
addPanel: (section: string, element: React.FC<any>) => void;
8
+
9
+
/**
10
+
* @private
11
+
*/
12
+
getPanels: (el: React.FC<any>) => React.ReactNode;
13
+
};
+204
packages/types/src/coreExtensions/commands.ts
+204
packages/types/src/coreExtensions/commands.ts
···
1
+
export const APPLICATION_ID = "-3";
2
+
3
+
export enum CommandType {
4
+
CHAT = 1,
5
+
MESSAGE = 3,
6
+
PRIMARY_ENTRY_POINT = 4,
7
+
USER = 2
8
+
}
9
+
10
+
export enum InputType {
11
+
BOT = 3,
12
+
BUILT_IN = 0,
13
+
BUILT_IN_INTEGRATION = 2,
14
+
BUILT_IN_TEXT = 1,
15
+
PLACEHOLDER = 4
16
+
}
17
+
18
+
export enum OptionType {
19
+
SUB_COMMAND = 1,
20
+
SUB_COMMAND_GROUP = 2,
21
+
STRING = 3,
22
+
INTEGER = 4,
23
+
BOOLEAN = 5,
24
+
USER = 6,
25
+
CHANNEL = 7,
26
+
ROLE = 8,
27
+
MENTIONABLE = 9,
28
+
NUMBER = 10,
29
+
ATTACHMENT = 11
30
+
}
31
+
32
+
export enum ChannelType {
33
+
GUILD_TEXT = 0,
34
+
DM = 1,
35
+
GUILD_VOICE = 2,
36
+
GROUP_DM = 3,
37
+
GUILD_CATEGORY = 4,
38
+
GUILD_ANNOUNCEMENT = 5,
39
+
GUILD_STORE = 6,
40
+
ANNOUNCEMENT_THREAD = 10,
41
+
PUBLIC_THREAD = 11,
42
+
PRIVATE_THREAD = 12,
43
+
GUILD_STAGE_VOICE = 13,
44
+
GUILD_DIRECTORY = 14,
45
+
GUILD_FORUM = 15,
46
+
GUILD_MEDIA = 16,
47
+
LOBBY = 17,
48
+
DM_SDK = 18
49
+
}
50
+
51
+
export type RegisteredCommandOption = MoonlightCommandOption & {
52
+
displayName: string;
53
+
displayDescription: string;
54
+
};
55
+
56
+
export type CommandOptionChoice<T> = {
57
+
name: string;
58
+
value: T;
59
+
};
60
+
61
+
type CommandOptionBase<T> = {
62
+
type: T;
63
+
name: string;
64
+
description: string;
65
+
required?: T extends OptionType.SUB_COMMAND
66
+
? never
67
+
: T extends OptionType.SUB_COMMAND_GROUP
68
+
? never
69
+
: boolean | undefined;
70
+
choices?: T extends OptionType.STRING
71
+
? CommandOptionChoice<string>[]
72
+
: T extends OptionType.INTEGER
73
+
? CommandOptionChoice<number>[]
74
+
: T extends OptionType.NUMBER
75
+
? CommandOptionChoice<number>[]
76
+
: never;
77
+
options?: T extends OptionType.SUB_COMMAND
78
+
? MoonlightCommandOption[]
79
+
: T extends OptionType.SUB_COMMAND_GROUP
80
+
? MoonlightCommandOption[]
81
+
: never;
82
+
channelTypes?: T extends OptionType.CHANNEL ? ChannelType[] : never;
83
+
minValue?: T extends OptionType.INTEGER ? number : T extends OptionType.NUMBER ? number : never;
84
+
maxValue?: T extends OptionType.INTEGER ? number : T extends OptionType.NUMBER ? number : never;
85
+
minLength?: T extends OptionType.STRING ? number : never;
86
+
maxLength?: T extends OptionType.STRING ? number : never;
87
+
};
88
+
89
+
// This is bad lol
90
+
export type MoonlightCommandOption =
91
+
| CommandOptionBase<OptionType.SUB_COMMAND>
92
+
| CommandOptionBase<OptionType.SUB_COMMAND_GROUP>
93
+
| CommandOptionBase<OptionType.STRING>
94
+
| CommandOptionBase<OptionType.INTEGER>
95
+
| CommandOptionBase<OptionType.BOOLEAN>
96
+
| CommandOptionBase<OptionType.USER>
97
+
| CommandOptionBase<OptionType.CHANNEL>
98
+
| CommandOptionBase<OptionType.ROLE>
99
+
| CommandOptionBase<OptionType.MENTIONABLE>
100
+
| CommandOptionBase<OptionType.NUMBER>
101
+
| CommandOptionBase<OptionType.ATTACHMENT>;
102
+
103
+
// TODO: types
104
+
export type CommandPredicateState = {
105
+
channel: any;
106
+
guild: any;
107
+
};
108
+
109
+
export type RegisteredCommand = {
110
+
id: string;
111
+
untranslatedName: string;
112
+
displayName: string;
113
+
type: CommandType;
114
+
inputType: InputType;
115
+
applicationId: string; // set to -3!
116
+
untranslatedDescription: string;
117
+
displayDescription: string;
118
+
options?: RegisteredCommandOption[];
119
+
predicate?: (state: CommandPredicateState) => boolean;
120
+
execute: (options: CommandOption[]) => void;
121
+
};
122
+
123
+
export type MoonlightCommand = {
124
+
id: string;
125
+
description: string;
126
+
127
+
/**
128
+
* You likely want CHAT
129
+
*/
130
+
type: CommandType;
131
+
132
+
/**
133
+
* You likely want BUILT_IN (or BUILT_IN_TEXT if usable with replies)
134
+
*/
135
+
inputType: InputType;
136
+
options?: MoonlightCommandOption[];
137
+
predicate?: (state: CommandPredicateState) => boolean;
138
+
execute: (options: CommandOption[]) => void;
139
+
};
140
+
141
+
export type CommandOption = {
142
+
name: string;
143
+
} & ( // TODO: more of these
144
+
| {
145
+
type: Exclude<OptionType, OptionType.STRING>;
146
+
value: any;
147
+
}
148
+
| {
149
+
type: OptionType.STRING;
150
+
value: string;
151
+
}
152
+
| {
153
+
type: OptionType.NUMBER | OptionType.INTEGER;
154
+
value: number;
155
+
}
156
+
| {
157
+
type: OptionType.BOOLEAN;
158
+
value: boolean;
159
+
}
160
+
| {
161
+
type: OptionType.SUB_COMMAND | OptionType.SUB_COMMAND_GROUP;
162
+
options: CommandOption[];
163
+
}
164
+
);
165
+
166
+
export type AnyScopeRegex = RegExp["exec"] & {
167
+
regex: RegExp;
168
+
};
169
+
170
+
export type Commands = {
171
+
/**
172
+
* Register a command in the internal slash command system
173
+
*/
174
+
registerCommand: (command: MoonlightCommand) => void;
175
+
176
+
/**
177
+
* Register a legacy command that works via regex
178
+
*/
179
+
registerLegacyCommand: (id: string, command: LegacyCommand) => void;
180
+
181
+
/**
182
+
* Creates a regular expression that legacy commands can understand
183
+
*/
184
+
anyScopeRegex: (regex: RegExp) => AnyScopeRegex;
185
+
186
+
/**
187
+
* @private
188
+
*/
189
+
_getCommands: () => RegisteredCommand[];
190
+
};
191
+
192
+
export type LegacyContext = {
193
+
channel: any;
194
+
isEdit: boolean;
195
+
};
196
+
197
+
export type LegacyReturn = {
198
+
content: string;
199
+
};
200
+
201
+
export type LegacyCommand = {
202
+
match?: RegExp | { regex: RegExp } | AnyScopeRegex;
203
+
action: (content: string, context: LegacyContext) => LegacyReturn;
204
+
};
+33
packages/types/src/coreExtensions/common.ts
+33
packages/types/src/coreExtensions/common.ts
···
1
+
import type { IconProps, IconSize } from "@moonlight-mod/mappings/discord/components/common/index";
2
+
3
+
export type ErrorBoundaryProps = React.PropsWithChildren<{
4
+
noop?: boolean;
5
+
fallback?: React.FC<any>;
6
+
message?: string;
7
+
}>;
8
+
9
+
export type ErrorBoundaryState = {
10
+
errored: boolean;
11
+
error?: Error;
12
+
componentStack?: string;
13
+
};
14
+
15
+
export type ErrorBoundary = React.ComponentClass<ErrorBoundaryProps, ErrorBoundaryState>;
16
+
17
+
export type ParsedIconProps = {
18
+
width: number;
19
+
height: number;
20
+
fill: string;
21
+
className: string;
22
+
};
23
+
24
+
export interface Icons {
25
+
/**
26
+
* Parse icon props into their actual width/height.
27
+
* @param props The icon props
28
+
*/
29
+
parseProps(props?: IconProps): ParsedIconProps;
30
+
}
31
+
32
+
// Re-export so extension developers don't need to depend on mappings
33
+
export type { IconProps, IconSize };
+162
packages/types/src/coreExtensions/componentEditor.ts
+162
packages/types/src/coreExtensions/componentEditor.ts
···
1
+
type Patcher<T> = (elements: React.ReactNode[], props: T) => React.ReactNode[];
2
+
3
+
//#region DM List
4
+
export type DMListAnchors =
5
+
| "content"
6
+
| "favorite-server-indicator"
7
+
| "ignored-indicator"
8
+
| "blocked-indicator"
9
+
| "close-button"
10
+
| undefined;
11
+
export type DMListDecoratorAnchors = "system-tag" | undefined;
12
+
13
+
export enum DMListAnchorIndicies {
14
+
content = 0,
15
+
"favorite-server-indicator",
16
+
"ignored-indicator",
17
+
"blocked-indicator",
18
+
"close-button"
19
+
}
20
+
export enum DMListDecoratorAnchorIndicies {
21
+
"system-tag" = 0
22
+
}
23
+
24
+
export type DMListItem = {
25
+
component: React.FC<any>;
26
+
anchor: DMListAnchors;
27
+
before: boolean;
28
+
};
29
+
export type DMListDecorator = {
30
+
component: React.FC<any>;
31
+
anchor: DMListDecoratorAnchors;
32
+
before: boolean;
33
+
};
34
+
35
+
export type DMList = {
36
+
addItem: (id: string, component: React.FC<any>, anchor?: DMListAnchors, before?: boolean) => void;
37
+
addDecorator: (id: string, component: React.FC<any>, anchor?: DMListDecoratorAnchors, before?: boolean) => void;
38
+
//TODO: fix props type
39
+
/**
40
+
* @private
41
+
*/
42
+
_patchItems: Patcher<any>;
43
+
/**
44
+
* @private
45
+
*/
46
+
_patchDecorators: Patcher<any>;
47
+
};
48
+
//#endregion
49
+
50
+
//#region Member List
51
+
export type MemberListDecoratorAnchors = "bot-tag" | "owner-crown" | "boost-icon" | undefined;
52
+
53
+
export enum MemberListDecoratorAnchorIndicies {
54
+
"bot-tag" = 0,
55
+
"owner-crown",
56
+
"boost-icon"
57
+
}
58
+
59
+
export type MemberListDecorator = {
60
+
component: React.FC<any>;
61
+
anchor: MemberListDecoratorAnchors;
62
+
before: boolean;
63
+
};
64
+
65
+
export type MemberList = {
66
+
addItem: (id: string, component: React.FC<any>) => void;
67
+
addDecorator: (id: string, component: React.FC<any>, anchor?: MemberListDecoratorAnchors, before?: boolean) => void;
68
+
//TODO: fix props type
69
+
/**
70
+
* @private
71
+
*/
72
+
_patchItems: Patcher<any>;
73
+
/**
74
+
* @private
75
+
*/
76
+
_patchDecorators: Patcher<any>;
77
+
};
78
+
//#endregion
79
+
80
+
//#region Messages
81
+
export type MessageUsernameAnchors = "communication-disabled" | "username" | undefined;
82
+
export type MessageUsernameBadgeAnchors =
83
+
| "nitro-author"
84
+
| "role-icon"
85
+
| "new-member"
86
+
| "leaderboard-champion"
87
+
| "connections"
88
+
| undefined;
89
+
export type MessageBadgeAnchors = "silent" | "potion" | undefined;
90
+
91
+
export type MessageUsername = {
92
+
component: React.FC<any>;
93
+
anchor: MessageUsernameAnchors;
94
+
before: boolean;
95
+
};
96
+
export type MessageUsernameBadge = {
97
+
component: React.FC<any>;
98
+
anchor: MessageUsernameBadgeAnchors;
99
+
before: boolean;
100
+
};
101
+
export type MessageBadge = {
102
+
component: React.FC<any>;
103
+
anchor: MessageBadgeAnchors;
104
+
before: boolean;
105
+
};
106
+
107
+
export enum MessageUsernameIndicies {
108
+
"communication-disabled" = 0,
109
+
username
110
+
}
111
+
export enum MessageUsernameBadgeIndicies {
112
+
"nitro-author" = 0,
113
+
"role-icon",
114
+
"new-member",
115
+
"leaderboard-champion",
116
+
connections
117
+
}
118
+
export enum MessageBadgeIndicies {
119
+
silent = 0,
120
+
potion
121
+
}
122
+
123
+
export type Messages = {
124
+
/**
125
+
* Adds a component to the username of a message
126
+
*/
127
+
addToUsername: (id: string, component: React.FC<any>, anchor?: MessageUsernameAnchors, before?: boolean) => void;
128
+
/**
129
+
* Adds a component to the username badge area of a message (e.g. where role icons/new member badge is)
130
+
*/
131
+
addUsernameBadge: (
132
+
id: string,
133
+
component: React.FC<any>,
134
+
anchor?: MessageUsernameBadgeAnchors,
135
+
before?: boolean
136
+
) => void;
137
+
/**
138
+
* Adds a component to the end of a message header (e.g. silent indicator)
139
+
*/
140
+
addBadge: (id: string, component: React.FC<any>, anchor?: MessageBadgeAnchors, before?: boolean) => void;
141
+
/**
142
+
* Adds a component to message accessories (e.g. embeds)
143
+
*/
144
+
addAccessory: (id: string, component: React.FC<any>) => void;
145
+
/**
146
+
* @private
147
+
*/
148
+
_patchUsername: Patcher<any>;
149
+
/**
150
+
* @private
151
+
*/
152
+
_patchUsernameBadges: Patcher<any>;
153
+
/**
154
+
* @private
155
+
*/
156
+
_patchBadges: Patcher<any>;
157
+
/**
158
+
* @private
159
+
*/
160
+
_patchAccessories: Patcher<any>;
161
+
};
162
+
//#endregion
-363
packages/types/src/coreExtensions/components.ts
-363
packages/types/src/coreExtensions/components.ts
···
1
-
import type {
2
-
Component,
3
-
Ref,
4
-
PropsWithChildren,
5
-
PropsWithoutRef,
6
-
CSSProperties,
7
-
ReactNode,
8
-
ReactElement,
9
-
ComponentClass,
10
-
ComponentType,
11
-
MouseEventHandler,
12
-
KeyboardEventHandler
13
-
} from "react";
14
-
import * as CSS from "csstype";
15
-
16
-
export enum TextInputSizes {
17
-
DEFAULT = "inputDefault",
18
-
MINI = "inputMini"
19
-
}
20
-
21
-
interface TextInput
22
-
extends ComponentClass<
23
-
PropsWithoutRef<{
24
-
value?: string;
25
-
name?: string;
26
-
className?: string;
27
-
inputClassName?: string;
28
-
inputPrefix?: string;
29
-
disabled?: boolean;
30
-
size?: TextInputSizes;
31
-
editable?: boolean;
32
-
inputRef?: Ref<any>;
33
-
prefixElement?: Component;
34
-
focusProps?: PropsWithoutRef<any>;
35
-
error?: string;
36
-
minLength?: number;
37
-
maxLength?: number;
38
-
onChange?: (value: string, name: string) => void;
39
-
onFocus?: (event: any, name: string) => void;
40
-
onBlur?: (event: any, name: string) => void;
41
-
}>
42
-
> {
43
-
Sizes: typeof TextInputSizes;
44
-
}
45
-
46
-
export enum FormTextTypes {
47
-
DEFAULT = "default",
48
-
DESCRIPTION = "description",
49
-
ERROR = "error",
50
-
INPUT_PLACEHOLDER = "placeholder",
51
-
LABEL_BOLD = "labelBold",
52
-
LABEL_DESCRIPTOR = "labelDescriptor",
53
-
LABEL_SELECTED = "labelSelected",
54
-
SUCCESS = "success"
55
-
}
56
-
57
-
interface FormText
58
-
extends ComponentClass<
59
-
PropsWithChildren<{
60
-
type?: FormTextTypes;
61
-
className?: string;
62
-
disabled?: boolean;
63
-
selectable?: boolean;
64
-
style?: CSSProperties;
65
-
}>
66
-
> {
67
-
Types: FormTextTypes;
68
-
}
69
-
70
-
declare enum SliderMarkerPosition {
71
-
ABOVE,
72
-
BELOW
73
-
}
74
-
75
-
declare enum ButtonLooks {
76
-
FILLED = "lookFilled",
77
-
INVERTED = "lookInverted",
78
-
OUTLINED = "lookOutlined",
79
-
LINK = "lookLink",
80
-
BLANK = "lookBlank"
81
-
}
82
-
declare enum ButtonColors {
83
-
BRAND = "colorBrand",
84
-
RED = "colorRed",
85
-
GREEN = "colorGreen",
86
-
YELLOW = "colorYellow",
87
-
PRIMARY = "colorPrimary",
88
-
LINK = "colorLink",
89
-
WHITE = "colorWhite",
90
-
BLACK = "colorBlack",
91
-
TRANSPARENT = "colorTransparent",
92
-
BRAND_NEW = "colorBrandNew",
93
-
CUSTOM = ""
94
-
}
95
-
declare enum ButtonBorderColors {
96
-
BRAND = "borderBrand",
97
-
RED = "borderRed",
98
-
GREEN = "borderGreen",
99
-
YELLOW = "borderYellow",
100
-
PRIMARY = "borderPrimary",
101
-
LINK = "borderLink",
102
-
WHITE = "borderWhite",
103
-
BLACK = "borderBlack",
104
-
TRANSPARENT = "borderTransparent",
105
-
BRAND_NEW = "borderBrandNew"
106
-
}
107
-
declare enum ButtonHovers {
108
-
DEFAULT = "",
109
-
BRAND = "hoverBrand",
110
-
RED = "hoverRed",
111
-
GREEN = "hoverGreen",
112
-
YELLOW = "hoverYellow",
113
-
PRIMARY = "hoverPrimary",
114
-
LINK = "hoverLink",
115
-
WHITE = "hoverWhite",
116
-
BLACK = "hoverBlack",
117
-
TRANSPARENT = "hoverTransparent"
118
-
}
119
-
declare enum ButtonSizes {
120
-
NONE = "",
121
-
TINY = "sizeTiny",
122
-
SMALL = "sizeSmall",
123
-
MEDIUM = "sizeMedium",
124
-
LARGE = "sizeLarge",
125
-
XLARGE = "sizeXlarge",
126
-
MIN = "sizeMin",
127
-
MAX = "sizeMax",
128
-
ICON = "sizeIcon"
129
-
}
130
-
131
-
type Button = ComponentType<
132
-
PropsWithChildren<{
133
-
look?: ButtonLooks;
134
-
color?: ButtonColors;
135
-
borderColor?: ButtonBorderColors;
136
-
hover?: ButtonHovers;
137
-
size?: ButtonSizes;
138
-
fullWidth?: boolean;
139
-
grow?: boolean;
140
-
disabled?: boolean;
141
-
submitting?: boolean;
142
-
type?: string;
143
-
style?: CSSProperties;
144
-
wrapperClassName?: string;
145
-
className?: string;
146
-
innerClassName?: string;
147
-
onClick?: MouseEventHandler;
148
-
onDoubleClick?: MouseEventHandler;
149
-
onMouseDown?: MouseEventHandler;
150
-
onMouseUp?: MouseEventHandler;
151
-
onMouseEnter?: MouseEventHandler;
152
-
onMouseLeave?: MouseEventHandler;
153
-
onKeyDown?: KeyboardEventHandler;
154
-
rel?: any;
155
-
buttonRef?: Ref<any>;
156
-
focusProps?: PropsWithChildren<any>;
157
-
"aria-label"?: string;
158
-
submittingStartedLabel?: string;
159
-
submittingFinishedLabel?: string;
160
-
}>
161
-
> & {
162
-
Looks: typeof ButtonLooks;
163
-
Colors: typeof ButtonColors;
164
-
BorderColors: typeof ButtonBorderColors;
165
-
Hovers: typeof ButtonHovers;
166
-
Sizes: typeof ButtonSizes;
167
-
};
168
-
169
-
export enum FlexDirection {
170
-
VERTICAL = "vertical",
171
-
HORIZONTAL = "horizontal",
172
-
HORIZONTAL_REVERSE = "horizontalReverse"
173
-
}
174
-
175
-
declare enum FlexAlign {
176
-
START = "alignStart",
177
-
END = "alignEnd",
178
-
CENTER = "alignCenter",
179
-
STRETCH = "alignStretch",
180
-
BASELINE = "alignBaseline"
181
-
}
182
-
declare enum FlexJustify {
183
-
START = "justifyStart",
184
-
END = "justifyEnd",
185
-
CENTER = "justifyCenter",
186
-
BETWEEN = "justifyBetween",
187
-
AROUND = "justifyAround"
188
-
}
189
-
declare enum FlexWrap {
190
-
NO_WRAP = "noWrap",
191
-
WRAP = "wrap",
192
-
WRAP_REVERSE = "wrapReverse"
193
-
}
194
-
interface Flex
195
-
extends ComponentClass<
196
-
PropsWithChildren<{
197
-
className?: string;
198
-
direction?: FlexDirection;
199
-
justify?: FlexJustify;
200
-
align?: FlexAlign;
201
-
wrap?: FlexWrap;
202
-
shrink?: CSS.Property.FlexShrink;
203
-
grow?: CSS.Property.FlexGrow;
204
-
basis?: CSS.Property.FlexBasis;
205
-
style?: CSSProperties;
206
-
}>
207
-
> {
208
-
Direction: typeof FlexDirection;
209
-
Align: typeof FlexAlign;
210
-
Justify: typeof FlexJustify;
211
-
Wrap: typeof FlexWrap;
212
-
Child: Component<
213
-
PropsWithChildren<{
214
-
className?: string;
215
-
shrink?: CSS.Property.FlexShrink;
216
-
grow?: CSS.Property.FlexGrow;
217
-
basis?: CSS.Property.FlexBasis;
218
-
style?: CSSProperties;
219
-
wrap?: boolean;
220
-
}>
221
-
>;
222
-
}
223
-
224
-
// TODO: wtaf is up with react types not working in jsx
225
-
export type CommonComponents = {
226
-
Clickable: ComponentClass<
227
-
PropsWithChildren<{
228
-
onClick?: () => void;
229
-
href?: any;
230
-
onKeyPress?: () => void;
231
-
ignoreKeyPress?: boolean;
232
-
innerRef?: Ref<any>;
233
-
focusProps?: any;
234
-
tag?: string | Component;
235
-
role?: any;
236
-
tabIndex?: any;
237
-
className?: string;
238
-
}>
239
-
>;
240
-
TextInput: TextInput;
241
-
FormDivider: ComponentClass<any>;
242
-
FormSection: ComponentClass<
243
-
PropsWithChildren<{
244
-
className?: string;
245
-
titleClassName?: string;
246
-
title?: ReactNode;
247
-
icon?: ReactNode;
248
-
disabled?: boolean;
249
-
htmlFor?: any;
250
-
tag?: string;
251
-
}>
252
-
>;
253
-
FormText: FormText;
254
-
FormTitle: ComponentClass<
255
-
PropsWithChildren<{
256
-
tag?: string;
257
-
className?: string;
258
-
faded?: boolean;
259
-
disabled?: boolean;
260
-
required?: boolean;
261
-
error?: string;
262
-
}>
263
-
>;
264
-
FormSwitch: ComponentClass<PropsWithChildren<any>>;
265
-
FormItem: ComponentClass<PropsWithChildren<any>>;
266
-
Slider: ComponentClass<
267
-
PropsWithChildren<{
268
-
disabled?: boolean;
269
-
stickToMarkers?: boolean;
270
-
className?: string;
271
-
barStyles?: CSSProperties;
272
-
fillStyles?: CSSProperties;
273
-
mini?: boolean;
274
-
hideBubble?: boolean;
275
-
initialValue?: number;
276
-
orientation?: "horizontal" | "vertical";
277
-
onValueRender?: (value: number) => string;
278
-
renderMarker?: (marker: number) => ReactNode;
279
-
getAriaValueText?: (value: number) => string;
280
-
barClassName?: string;
281
-
grabberClassName?: string;
282
-
grabberStyles?: CSSProperties;
283
-
markerPosition?: SliderMarkerPosition;
284
-
"aria-hidden"?: "true" | "false";
285
-
"aria-label"?: string;
286
-
"aria-labelledby"?: string;
287
-
"aria-describedby"?: string;
288
-
minValue?: number;
289
-
maxValue?: number;
290
-
asValueChanges?: (value: number) => void;
291
-
onValueChange?: (value: number) => void;
292
-
keyboardStep?: number;
293
-
}>
294
-
>;
295
-
Switch: ComponentClass<PropsWithChildren<any>>;
296
-
Button: Button;
297
-
Tooltip: ComponentClass<PropsWithChildren<any>>;
298
-
SmallSlider: Component;
299
-
Avatar: Component;
300
-
Scroller: Component;
301
-
Text: ComponentClass<PropsWithChildren<any>>;
302
-
Heading: ComponentClass<PropsWithChildren<any>>;
303
-
LegacyText: Component;
304
-
Flex: Flex;
305
-
Card: ComponentClass<PropsWithChildren<any>>;
306
-
Popout: ComponentClass<PropsWithChildren<any>>;
307
-
Dialog: ComponentClass<PropsWithChildren<any>>;
308
-
Menu: ComponentClass<PropsWithChildren<any>>;
309
-
MenuItem: ComponentClass<PropsWithChildren<any>>;
310
-
MenuGroup: ComponentClass<PropsWithChildren<any>>;
311
-
MenuCheckboxItem: ComponentClass<PropsWithChildren<any>>;
312
-
CardClasses: {
313
-
card: string;
314
-
cardHeader: string;
315
-
};
316
-
ControlClasses: {
317
-
container: string;
318
-
control: string;
319
-
disabled: string;
320
-
dividerDefault: string;
321
-
labelRow: string;
322
-
note: string;
323
-
title: string;
324
-
titleDefault: string;
325
-
titleMini: string;
326
-
};
327
-
MarkdownParser: {
328
-
parse: (text: string) => ReactElement;
329
-
};
330
-
SettingsNotice: React.ComponentType<{
331
-
submitting: boolean;
332
-
onReset: () => void;
333
-
onSave: () => void;
334
-
}>;
335
-
TabBar: React.ComponentType<any> & {
336
-
Item: React.ComponentType<any>;
337
-
};
338
-
SingleSelect: React.ComponentType<{
339
-
autofocus?: boolean;
340
-
clearable?: boolean;
341
-
value?: string;
342
-
options?: {
343
-
value: string;
344
-
label: string;
345
-
}[];
346
-
onChange?: (value: string) => void;
347
-
}>;
348
-
Select: React.ComponentType<{
349
-
autofocus?: boolean;
350
-
clearable?: boolean;
351
-
value?: string[];
352
-
options?: {
353
-
value: string;
354
-
label: string;
355
-
}[];
356
-
onChange?: (value: string[]) => void;
357
-
}>;
358
-
359
-
// TODO
360
-
useVariableSelect: any;
361
-
multiSelect: any;
362
-
tokens: any;
363
-
};
+64
packages/types/src/coreExtensions/contextMenu.ts
+64
packages/types/src/coreExtensions/contextMenu.ts
···
1
+
import {
2
+
Menu,
3
+
MenuCheckboxItem,
4
+
MenuControlItem,
5
+
MenuGroup,
6
+
MenuRadioItem,
7
+
MenuSeparator,
8
+
MenuItem,
9
+
MenuElement
10
+
} from "@moonlight-mod/mappings/discord/components/common/index";
11
+
12
+
export type ContextMenu = {
13
+
/**
14
+
* Registers a new context menu item for a given context menu type.
15
+
* @param navId The navigation ID for the target context menu (e.g. "user-context", "message")
16
+
* @param item A React component
17
+
* @param anchor An existing item's ID to anchor the new item to
18
+
* @param before Whether to insert the new item before the anchor item
19
+
*/
20
+
addItem: (navId: string, item: React.FC<any>, anchor: string | RegExp, before?: boolean) => void;
21
+
22
+
MenuCheckboxItem: MenuCheckboxItem;
23
+
MenuControlItem: MenuControlItem;
24
+
MenuGroup: MenuGroup;
25
+
MenuItem: MenuItem;
26
+
MenuRadioItem: MenuRadioItem;
27
+
MenuSeparator: MenuSeparator;
28
+
};
29
+
30
+
export type InternalItem = {
31
+
type: string;
32
+
key?: string;
33
+
};
34
+
35
+
export type InternalSeparator = {
36
+
type: "separator";
37
+
navigable: false;
38
+
};
39
+
export type InternalGroupStart = {
40
+
type: "groupstart";
41
+
length: number;
42
+
navigable: false;
43
+
props: React.ComponentProps<MenuGroup>;
44
+
};
45
+
export type InternalGroupEnd = {
46
+
type: "groupend";
47
+
} & Omit<InternalGroupStart, "type">;
48
+
export type InternalCustomItem = {
49
+
type: "customitem";
50
+
key: any;
51
+
navigable?: boolean;
52
+
render: any;
53
+
props: Extract<React.ComponentProps<MenuItem>, { render: any }>;
54
+
};
55
+
export type InternalItem_ = {
56
+
type: "item";
57
+
key: any;
58
+
navigable: true;
59
+
label: string;
60
+
};
61
+
62
+
export type EvilItemParser = (el: MenuElement | MenuElement[]) => InternalItem[];
63
+
64
+
export type { Menu, MenuElement };
+20
-23
packages/types/src/coreExtensions/markdown.ts
+20
-23
packages/types/src/coreExtensions/markdown.ts
···
11
11
12
12
export type ASTNode = SingleASTNode | Array<SingleASTNode>;
13
13
14
-
export type Parser = (
15
-
source: string,
16
-
state?: State | null | undefined
17
-
) => Array<SingleASTNode>;
14
+
export type Parser = (source: string, state?: State | null | undefined) => Array<SingleASTNode>;
18
15
19
-
export type ParseFunction = (
20
-
capture: Capture,
21
-
nestedParse: Parser,
22
-
state: State
23
-
) => UntypedASTNode | ASTNode;
16
+
export type ParseFunction = (capture: Capture, nestedParse: Parser, state: State) => UntypedASTNode | ASTNode;
24
17
25
18
export type Capture =
26
19
| (Array<string> & {
···
38
31
39
32
export type MatchFunction = {
40
33
regex?: RegExp;
41
-
} & ((
42
-
source: string,
43
-
state: State,
44
-
prevCapture: string
45
-
) => Capture | null | undefined);
34
+
} & ((source: string, state: State, prevCapture: string) => Capture | null | undefined);
46
35
47
-
export type Output<Result> = (
48
-
node: ASTNode,
49
-
state?: State | null | undefined
50
-
) => Result;
36
+
export type Output<Result> = (node: ASTNode, state?: State | null | undefined) => Result;
51
37
52
-
export type SingleNodeOutput<Result> = (
53
-
node: SingleASTNode,
54
-
nestedOutput: Output<Result>,
55
-
state: State
56
-
) => Result;
38
+
export type SingleNodeOutput<Result> = (node: SingleASTNode, nestedOutput: Output<Result>, state: State) => Result;
57
39
58
40
// }}}
59
41
···
100
82
slateDecorators: Record<string, string>;
101
83
ruleBlacklists: Record<Ruleset, Record<string, boolean>>;
102
84
85
+
/**
86
+
* Registers a new Markdown rule with simple-markdown.
87
+
* @param name The name of the rule
88
+
* @param markdown A function that returns simple-markdown rules
89
+
* @param slate A function that returns Slate rules
90
+
* @param decorator A decorator name for Slate
91
+
* @see https://www.npmjs.com/package/simple-markdown#adding-a-simple-extension
92
+
* @see https://docs.slatejs.org/
93
+
*/
103
94
addRule: (
104
95
name: string,
105
96
markdown: (rules: Record<string, MarkdownRule>) => MarkdownRule,
106
97
slate: (rules: Record<string, SlateRule>) => SlateRule,
107
98
decorator?: string | undefined
108
99
) => void;
100
+
101
+
/**
102
+
* Blacklist a rule from a ruleset.
103
+
* @param ruleset The ruleset name
104
+
* @param name The rule name
105
+
*/
109
106
blacklistFromRuleset: (ruleset: Ruleset, name: string) => void;
110
107
};
+17
packages/types/src/coreExtensions/moonbase.ts
+17
packages/types/src/coreExtensions/moonbase.ts
···
1
+
export type CustomComponentProps = {
2
+
value: any;
3
+
setValue: (value: any) => void;
4
+
};
5
+
6
+
export type CustomComponent = React.FC<CustomComponentProps>;
7
+
8
+
export type Moonbase = {
9
+
/**
10
+
* Registers a custom component for an extension setting.
11
+
* The extension setting must be of type "custom".
12
+
* @param ext The extension ID
13
+
* @param option The setting ID
14
+
* @param component A React component
15
+
*/
16
+
registerConfigComponent: (ext: string, option: string, component: CustomComponent) => void;
17
+
};
+36
packages/types/src/coreExtensions/notices.ts
+36
packages/types/src/coreExtensions/notices.ts
···
1
+
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux/Store";
2
+
3
+
export type NoticeButton = {
4
+
name: string;
5
+
onClick: () => boolean; // return true to dismiss the notice after the button is clicked
6
+
};
7
+
8
+
export type Notice = {
9
+
element: React.ReactNode;
10
+
color?: string;
11
+
showClose?: boolean;
12
+
buttons?: NoticeButton[];
13
+
onDismiss?: () => void;
14
+
};
15
+
16
+
export type Notices = Store<any> & {
17
+
/**
18
+
* Adds a custom notice to the top of the screen.
19
+
*/
20
+
addNotice: (notice: Notice) => void;
21
+
22
+
/**
23
+
* Removes the current notice from the top of the screen.
24
+
*/
25
+
popNotice: () => void;
26
+
27
+
/**
28
+
* @private
29
+
*/
30
+
getCurrentNotice: () => Notice | null;
31
+
32
+
/**
33
+
* @private
34
+
*/
35
+
shouldShowNotice: () => boolean;
36
+
};
+71
packages/types/src/coreExtensions/settings.ts
+71
packages/types/src/coreExtensions/settings.ts
···
1
+
import React, { ReactElement } from "react";
2
+
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux/Store";
3
+
4
+
export type NoticeProps = {
5
+
stores: Store<any>[];
6
+
element: React.FunctionComponent;
7
+
};
8
+
9
+
export type SettingsSection =
10
+
| { section: "DIVIDER"; pos: number | ((sections: SettingsSection[]) => number) }
11
+
| { section: "HEADER"; label: string; pos: number | ((sections: SettingsSection[]) => number) }
12
+
| {
13
+
section: string;
14
+
label: string;
15
+
color: string | null;
16
+
element: React.FunctionComponent;
17
+
pos: number | ((sections: SettingsSection[]) => number);
18
+
notice?: NoticeProps;
19
+
onClick?: () => void;
20
+
_moonlight_submenu?: () => ReactElement | ReactElement[];
21
+
};
22
+
23
+
export type Settings = {
24
+
ourSections: SettingsSection[];
25
+
sectionNames: string[];
26
+
sectionMenuItems: Record<string, ReactElement[]>;
27
+
28
+
/**
29
+
* Registers a new section in the settings menu.
30
+
* @param section The section ID
31
+
* @param label The label for the section
32
+
* @param element The React component to render
33
+
* @param color A color to use for the section
34
+
* @param pos The position in the settings menu to place the section
35
+
* @param notice A notice to display when in the section
36
+
* @param onClick A custom action to execute when clicked from the context menu
37
+
*/
38
+
addSection: (
39
+
section: string,
40
+
label: string,
41
+
element: React.FunctionComponent,
42
+
color?: string | null,
43
+
pos?: number | ((sections: SettingsSection[]) => number),
44
+
notice?: NoticeProps,
45
+
onClick?: () => void
46
+
) => void;
47
+
48
+
/**
49
+
* Adds new items to a section in the settings menu.
50
+
* @param section The section ID
51
+
* @param items The React components to render
52
+
*/
53
+
addSectionMenuItems: (section: string, ...items: ReactElement[]) => void;
54
+
55
+
/**
56
+
* Places a divider in the settings menu.
57
+
* @param pos The position in the settings menu to place the divider
58
+
*/
59
+
addDivider: (pos: number | ((sections: SettingsSection[]) => number) | null) => void;
60
+
61
+
/**
62
+
* Places a header in the settings menu.
63
+
* @param pos The position in the settings menu to place the header
64
+
*/
65
+
addHeader: (label: string, pos: number | ((sections: SettingsSection[]) => number) | null) => void;
66
+
67
+
/**
68
+
* @private
69
+
*/
70
+
_mutateSections: (sections: SettingsSection[]) => SettingsSection[];
71
+
};
+97
packages/types/src/coreExtensions/spacepack.ts
+97
packages/types/src/coreExtensions/spacepack.ts
···
1
+
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "../discord";
2
+
3
+
export type Spacepack = {
4
+
/**
5
+
* Given a Webpack module ID, returns the function for the Webpack module.
6
+
* Can be double clicked to inspect in DevTools.
7
+
* @param module The module ID
8
+
* @returns The Webpack module, if found
9
+
*/
10
+
inspect: (module: number | string) => WebpackModuleFunc | null;
11
+
12
+
/**
13
+
* Find Webpack modules based on matches in code.
14
+
* @param args A list of finds to match against
15
+
* @returns The Webpack modules, if found
16
+
*/
17
+
findByCode: (...args: (string | RegExp)[]) => WebpackModule[];
18
+
19
+
/**
20
+
* Find Webpack modules based on their exports.
21
+
* @deprecated This has race conditions. Consider using findByCode instead.
22
+
* @param args A list of finds to match exports against
23
+
* @returns The Webpack modules, if found
24
+
*/
25
+
findByExports: (...args: string[]) => WebpackModule[];
26
+
27
+
/**
28
+
* The Webpack require function.
29
+
*/
30
+
require: WebpackRequireType;
31
+
32
+
/**
33
+
* The Webpack module list.
34
+
* Re-export of require.m.
35
+
*/
36
+
modules: Record<string, WebpackModuleFunc>;
37
+
38
+
/**
39
+
* The Webpack module cache.
40
+
* Re-export of require.c.
41
+
*/
42
+
cache: Record<string, any>;
43
+
44
+
/**
45
+
* Finds an object from a module's exports using the given key.
46
+
* @param exports Exports from a Webpack module
47
+
* @param key The key to find with
48
+
* @returns The object, if found
49
+
*/
50
+
findObjectFromKey: (exports: Record<string, any>, key: string) => any | null;
51
+
52
+
/**
53
+
* Finds an object from a module's exports using the given value.
54
+
* @param exports Exports from a Webpack module
55
+
* @param value The value to find with
56
+
* @returns The object, if found
57
+
*/
58
+
findObjectFromValue: (exports: Record<string, any>, value: any) => any | null;
59
+
60
+
/**
61
+
* Finds an object from a module's exports using the given key-value pair.
62
+
* @param exports Exports from a Webpack module
63
+
* @param key The key to find with
64
+
* @param value The value to find with
65
+
* @returns The object, if found
66
+
*/
67
+
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => any | null;
68
+
69
+
/**
70
+
* Finds a function from a module's exports using the given source find.
71
+
* This behaves like findByCode but localized to the exported function.
72
+
* @param exports A module's exports
73
+
* @param strings A list of finds to use
74
+
* @returns The function, if found
75
+
*/
76
+
findFunctionByStrings: (
77
+
exports: Record<string, any>,
78
+
...strings: (string | RegExp)[]
79
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
80
+
) => Function | null;
81
+
82
+
/**
83
+
* Lazy load a Webpack module.
84
+
* @param find A list of finds to discover a target module with
85
+
* @param chunk A RegExp to match chunks to load
86
+
* @param module A RegExp to match the target Webpack module
87
+
* @returns The target Webpack module
88
+
*/
89
+
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => Promise<any>;
90
+
91
+
/**
92
+
* Filter a list of Webpack modules to "real" ones from the Discord client.
93
+
* @param modules A list of Webpack modules
94
+
* @returns A filtered list of Webpack modules
95
+
*/
96
+
filterReal: (modules: WebpackModule[]) => WebpackModule[];
97
+
};
+9
-77
packages/types/src/coreExtensions.ts
+9
-77
packages/types/src/coreExtensions.ts
···
1
-
import { FluxDefault, Store } from "./discord/common/Flux";
2
-
import { CommonComponents as CommonComponents_ } from "./coreExtensions/components";
3
-
import { Dispatcher } from "flux";
4
-
import React from "react";
5
-
import {
6
-
WebpackModule,
7
-
WebpackModuleFunc,
8
-
WebpackRequireType
9
-
} from "./discord";
10
-
11
-
export type Spacepack = {
12
-
inspect: (module: number | string) => WebpackModuleFunc | null;
13
-
findByCode: (...args: (string | RegExp)[]) => any[];
14
-
findByExports: (...args: string[]) => any[];
15
-
require: WebpackRequireType;
16
-
modules: Record<string, WebpackModuleFunc>;
17
-
cache: Record<string, any>;
18
-
findObjectFromKey: (exports: Record<string, any>, key: string) => any | null;
19
-
findObjectFromValue: (exports: Record<string, any>, value: any) => any | null;
20
-
findObjectFromKeyValuePair: (
21
-
exports: Record<string, any>,
22
-
key: string,
23
-
value: any
24
-
) => any | null;
25
-
findFunctionByStrings: (
26
-
exports: Record<string, any>,
27
-
...strings: (string | RegExp)[]
28
-
// eslint-disable-next-line @typescript-eslint/ban-types
29
-
) => Function | null;
30
-
lazyLoad: (
31
-
find: string | RegExp | (string | RegExp)[],
32
-
match: RegExp
33
-
) => Promise<any>;
34
-
filterReal: (modules: WebpackModule[]) => WebpackModule[];
35
-
};
36
-
37
-
export type NoticeProps = {
38
-
stores: Store<any>[];
39
-
element: React.FunctionComponent;
40
-
};
41
-
42
-
export type SettingsSection =
43
-
| { section: "DIVIDER"; pos: number }
44
-
| { section: "HEADER"; label: string; pos: number }
45
-
| {
46
-
section: string;
47
-
label: string;
48
-
color: string | null;
49
-
element: React.FunctionComponent;
50
-
pos: number;
51
-
notice?: NoticeProps;
52
-
_moonlight_submenu?: () => any;
53
-
};
54
-
55
-
export type Settings = {
56
-
ourSections: SettingsSection[];
57
-
sectionNames: string[];
58
-
59
-
addSection: (
60
-
section: string,
61
-
label: string,
62
-
element: React.FunctionComponent,
63
-
color?: string | null,
64
-
pos?: number,
65
-
notice?: NoticeProps
66
-
) => void;
67
-
68
-
addDivider: (pos: number | null) => void;
69
-
addHeader: (label: string, pos: number | null) => void;
70
-
_mutateSections: (sections: SettingsSection[]) => SettingsSection[];
71
-
};
72
-
73
-
export type CommonReact = typeof import("react");
74
-
export type CommonFlux = FluxDefault;
75
-
export type CommonComponents = CommonComponents_; // lol
76
-
export type CommonFluxDispatcher = Dispatcher<any>;
77
-
1
+
export * as Spacepack from "./coreExtensions/spacepack";
2
+
export * as Settings from "./coreExtensions/settings";
78
3
export * as Markdown from "./coreExtensions/markdown";
4
+
export * as ContextMenu from "./coreExtensions/contextMenu";
5
+
export * as Notices from "./coreExtensions/notices";
6
+
export * as Moonbase from "./coreExtensions/moonbase";
7
+
export * as AppPanels from "./coreExtensions/appPanels";
8
+
export * as Commands from "./coreExtensions/commands";
9
+
export * as ComponentEditor from "./coreExtensions/componentEditor";
10
+
export * as Common from "./coreExtensions/common";
-57
packages/types/src/discord/common/Flux.ts
-57
packages/types/src/discord/common/Flux.ts
···
1
-
/*
2
-
It seems like Discord maintains their own version of Flux that doesn't match
3
-
the types on NPM. This is a heavy work in progress - if you encounter rough
4
-
edges, please contribute!
5
-
*/
6
-
7
-
import { DependencyList } from "react";
8
-
import { Store as FluxStore } from "flux/utils";
9
-
import { Dispatcher as FluxDispatcher } from "flux";
10
-
import { ComponentConstructor } from "flux/lib/FluxContainer";
11
-
12
-
export declare abstract class Store<T> extends FluxStore<T> {
13
-
static getAll: () => Store<any>[];
14
-
getName: () => string;
15
-
emitChange: () => void;
16
-
}
17
-
18
-
interface ConnectStores {
19
-
<T>(
20
-
stores: Store<any>[],
21
-
callback: T,
22
-
context?: any
23
-
): ComponentConstructor<T>;
24
-
}
25
-
26
-
export type FluxDefault = {
27
-
DeviceSettingsStore: any; // TODO
28
-
Emitter: any; // @types/fbemitter
29
-
OfflineCacheStore: any; // TODO
30
-
PersistedStore: any; // TODO
31
-
Store: typeof Store;
32
-
Dispatcher: typeof FluxDispatcher;
33
-
connectStores: ConnectStores;
34
-
initialize: () => void;
35
-
initialized: Promise<boolean>;
36
-
destroy: () => void;
37
-
useStateFromStores: UseStateFromStores;
38
-
useStateFromStoresArray: UseStateFromStoresArray;
39
-
useStateFromStoresObject: UseStateFromStoresObject;
40
-
};
41
-
42
-
interface UseStateFromStores {
43
-
<T>(
44
-
stores: Store<any>[],
45
-
callback: () => T,
46
-
deps?: DependencyList,
47
-
shouldUpdate?: (oldState: T, newState: T) => boolean
48
-
): T;
49
-
}
50
-
51
-
interface UseStateFromStoresArray {
52
-
<T>(stores: Store<any>[], callback: () => T, deps?: DependencyList): T;
53
-
}
54
-
55
-
interface UseStateFromStoresObject {
56
-
<T>(stores: Store<any>[], callback: () => T, deps?: DependencyList): T;
57
-
}
+32
-19
packages/types/src/discord/require.ts
+32
-19
packages/types/src/discord/require.ts
···
1
-
import {
2
-
Spacepack,
3
-
CommonReact,
4
-
CommonFlux,
5
-
Settings,
6
-
CommonComponents,
7
-
CommonFluxDispatcher
8
-
} from "../coreExtensions";
1
+
import { AppPanels } from "../coreExtensions/appPanels";
2
+
import { Commands } from "../coreExtensions/commands";
3
+
import { ErrorBoundary, Icons } from "../coreExtensions/common";
4
+
import { DMList, MemberList, Messages } from "../coreExtensions/componentEditor";
5
+
import { ContextMenu, EvilItemParser } from "../coreExtensions/contextMenu";
9
6
import { Markdown } from "../coreExtensions/markdown";
7
+
import { Moonbase } from "../coreExtensions/moonbase";
8
+
import { Notices } from "../coreExtensions/notices";
9
+
import { Settings } from "../coreExtensions/settings";
10
+
import { Spacepack } from "../coreExtensions/spacepack";
10
11
11
12
declare function WebpackRequire(id: string): any;
12
-
declare function WebpackRequire(id: "spacepack_spacepack"): {
13
-
default: Spacepack;
14
-
spacepack: Spacepack;
15
-
};
13
+
14
+
declare function WebpackRequire(id: "appPanels_appPanels"): AppPanels;
15
+
16
+
declare function WebpackRequire(id: "commands_commands"): Commands;
17
+
18
+
declare function WebpackRequire(id: "common_ErrorBoundary"): ErrorBoundary;
19
+
declare function WebpackRequire(id: "common_icons"): Icons;
20
+
21
+
declare function WebpackRequire(id: "componentEditor_dmList"): DMList;
22
+
declare function WebpackRequire(id: "componentEditor_memberList"): MemberList;
23
+
declare function WebpackRequire(id: "componentEditor_messages"): Messages;
24
+
25
+
declare function WebpackRequire(id: "contextMenu_evilMenu"): EvilItemParser;
26
+
declare function WebpackRequire(id: "contextMenu_contextMenu"): ContextMenu;
27
+
28
+
declare function WebpackRequire(id: "markdown_markdown"): Markdown;
29
+
30
+
declare function WebpackRequire(id: "moonbase_moonbase"): Moonbase;
16
31
17
-
declare function WebpackRequire(id: "common_components"): CommonComponents;
18
-
declare function WebpackRequire(id: "common_flux"): CommonFlux;
19
-
declare function WebpackRequire(
20
-
id: "common_fluxDispatcher"
21
-
): CommonFluxDispatcher;
22
-
declare function WebpackRequire(id: "common_react"): CommonReact;
32
+
declare function WebpackRequire(id: "notices_notices"): Notices;
23
33
24
34
declare function WebpackRequire(id: "settings_settings"): {
25
35
Settings: Settings;
26
36
default: Settings;
27
37
};
28
38
29
-
declare function WebpackRequire(id: "markdown_markdown"): Markdown;
39
+
declare function WebpackRequire(id: "spacepack_spacepack"): {
40
+
default: Spacepack;
41
+
spacepack: Spacepack;
42
+
};
30
43
31
44
export default WebpackRequire;
+10
-16
packages/types/src/discord/webpack.ts
+10
-16
packages/types/src/discord/webpack.ts
···
1
1
import WebpackRequire from "./require";
2
+
import { WebpackRequire as MappingsWebpackRequire } from "@moonlight-mod/mappings";
2
3
3
-
export type WebpackRequireType = typeof WebpackRequire & {
4
-
c: Record<string, WebpackModule>;
5
-
m: Record<string, WebpackModuleFunc>;
6
-
el: (module: number | string) => Promise<void>;
7
-
};
4
+
export type WebpackRequireType = typeof MappingsWebpackRequire &
5
+
typeof WebpackRequire & {
6
+
c: Record<string, WebpackModule>;
7
+
m: Record<string, WebpackModuleFunc>;
8
+
e: (module: number | string) => Promise<void>;
9
+
};
8
10
9
11
export type WebpackModule = {
10
12
id: string | number;
11
-
loaded: boolean;
13
+
loaded?: boolean;
12
14
exports: any;
13
15
};
14
16
15
-
export type WebpackModuleFunc = ((
16
-
module: any,
17
-
exports: any,
18
-
require: WebpackRequireType
19
-
) => void) & {
17
+
export type WebpackModuleFunc = ((module: any, exports: any, require: WebpackRequireType) => void) & {
20
18
__moonlight?: boolean;
21
19
};
22
20
23
-
export type WebpackJsonpEntry = [
24
-
number[],
25
-
{ [id: string]: WebpackModuleFunc },
26
-
(require: WebpackRequireType) => any
27
-
];
21
+
export type WebpackJsonpEntry = [number[], { [id: string]: WebpackModuleFunc }, (require: WebpackRequireType) => any];
28
22
29
23
export type WebpackJsonp = WebpackJsonpEntry[] & {
30
24
push: {
+116
-4
packages/types/src/extension.ts
+116
-4
packages/types/src/extension.ts
···
28
28
};
29
29
30
30
export type ExtensionManifest = {
31
+
$schema?: string;
32
+
33
+
/**
34
+
* A unique identifier for your extension.
35
+
*/
31
36
id: string;
37
+
38
+
/**
39
+
* A version string for your extension - doesn't need to follow a specific format. Required for publishing.
40
+
*/
32
41
version?: string;
33
42
43
+
/**
44
+
* The API level this extension targets. If it does not match the current version, the extension will not be loaded.
45
+
*/
46
+
apiLevel?: number;
47
+
48
+
/**
49
+
* Which environment this extension is capable of running in.
50
+
*/
51
+
environment?: ExtensionEnvironment;
52
+
53
+
/**
54
+
* Metadata about your extension for use in Moonbase.
55
+
*/
34
56
meta?: {
57
+
/**
58
+
* A human friendly name for your extension as a proper noun.
59
+
*/
35
60
name?: string;
61
+
62
+
/**
63
+
* A short tagline that appears below the name.
64
+
*/
36
65
tagline?: string;
66
+
67
+
/**
68
+
* A longer description that can use Markdown.
69
+
*/
37
70
description?: string;
71
+
72
+
/**
73
+
* List of authors that worked on this extension - accepts string or object with ID.
74
+
*/
38
75
authors?: ExtensionAuthor[];
39
-
deprecated?: boolean;
76
+
77
+
/**
78
+
* A list of tags that are relevant to the extension.
79
+
*/
40
80
tags?: ExtensionTag[];
81
+
82
+
/**
83
+
* The URL to the source repository.
84
+
*/
41
85
source?: string;
86
+
87
+
/**
88
+
* A donation link (or other method of support). If you don't want financial contributions, consider putting your favorite charity here!
89
+
*/
90
+
donate?: string;
91
+
92
+
/**
93
+
* A changelog to show in Moonbase.
94
+
* Moonbase will show the changelog for the latest version, even if it is not installed.
95
+
*/
96
+
changelog?: string;
97
+
98
+
/**
99
+
* Whether the extension is deprecated and no longer receiving updates.
100
+
*/
101
+
deprecated?: boolean;
42
102
};
43
103
104
+
/**
105
+
* A list of extension IDs that are required for the extension to load.
106
+
*/
44
107
dependencies?: string[];
108
+
109
+
/**
110
+
* A list of extension IDs that the user may want to install.
111
+
*/
45
112
suggested?: string[];
113
+
114
+
/**
115
+
* A list of extension IDs that the extension is incompatible with.
116
+
* If two incompatible extensions are enabled, one of them will not load.
117
+
*/
46
118
incompatible?: string[];
47
119
120
+
/**
121
+
* A list of settings for your extension, where the key is the settings ID.
122
+
*/
48
123
settings?: Record<string, ExtensionSettingsManifest>;
124
+
125
+
/**
126
+
* A list of URLs to bypass CORS for.
127
+
* This is implemented by checking if the start of the URL matches.
128
+
* @example https://moonlight-mod.github.io/
129
+
*/
49
130
cors?: string[];
131
+
132
+
/**
133
+
* A list of URLs to block all requests to.
134
+
* This is implemented by checking if the start of the URL matches.
135
+
* @example https://moonlight-mod.github.io/
136
+
*/
137
+
blocked?: string[];
138
+
139
+
/**
140
+
* A mapping from CSP directives to URLs to allow.
141
+
* @example { "script-src": ["https://example.com"] }
142
+
*/
143
+
csp?: Record<string, string[]>;
50
144
};
51
145
146
+
export enum ExtensionEnvironment {
147
+
/**
148
+
* The extension will run on both platforms, the host/native modules MAY be loaded
149
+
*/
150
+
Both = "both",
151
+
152
+
/**
153
+
* Extension will run on desktop only, the host/native modules are guaranteed to load
154
+
*/
155
+
Desktop = "desktop",
156
+
157
+
/**
158
+
* Currently equivalent to Both
159
+
*/
160
+
Web = "web"
161
+
}
162
+
52
163
export enum ExtensionLoadSource {
53
164
Developer,
54
165
Core,
···
65
176
webpackModules?: Record<string, string>;
66
177
nodePath?: string;
67
178
hostPath?: string;
179
+
style?: string;
68
180
};
69
181
};
70
182
···
96
208
export type Patch = {
97
209
find: PatchMatch;
98
210
replace: PatchReplace | PatchReplace[];
211
+
hardFail?: boolean; // if any patches fail, all fail
99
212
prerequisite?: () => boolean;
100
213
};
101
214
102
215
export type ExplicitExtensionDependency = {
103
-
ext: string;
216
+
ext?: string;
104
217
id: string;
105
218
};
106
219
···
123
236
id: number;
124
237
};
125
238
126
-
export type IdentifiedWebpackModule = ExtensionWebpackModule &
127
-
ExplicitExtensionDependency;
239
+
export type IdentifiedWebpackModule = ExtensionWebpackModule & ExplicitExtensionDependency;
+19
packages/types/src/fs.ts
+19
packages/types/src/fs.ts
···
1
+
export type MoonlightFS = {
2
+
readFile: (path: string) => Promise<Uint8Array>;
3
+
readFileString: (path: string) => Promise<string>;
4
+
writeFile: (path: string, data: Uint8Array) => Promise<void>;
5
+
writeFileString: (path: string, data: string) => Promise<void>;
6
+
unlink: (path: string) => Promise<void>;
7
+
8
+
readdir: (path: string) => Promise<string[]>;
9
+
mkdir: (path: string) => Promise<void>;
10
+
rmdir: (path: string) => Promise<void>;
11
+
12
+
exists: (path: string) => Promise<boolean>;
13
+
isFile: (path: string) => Promise<boolean>;
14
+
isDir: (path: string) => Promise<boolean>;
15
+
16
+
join: (...parts: string[]) => string;
17
+
dirname: (path: string) => string;
18
+
basename: (path: string) => string;
19
+
};
+68
-14
packages/types/src/globals.ts
+68
-14
packages/types/src/globals.ts
···
1
-
import { Logger } from "./logger";
2
-
import { Config, ConfigExtension } from "./config";
3
-
import {
4
-
DetectedExtension,
5
-
IdentifiedPatch,
6
-
ProcessedExtensions
7
-
} from "./extension";
8
-
import EventEmitter from "events";
1
+
import type { Logger } from "./logger";
2
+
import type { Config, ConfigExtension } from "./config";
3
+
import type { DetectedExtension, IdentifiedPatch, IdentifiedWebpackModule, ProcessedExtensions } from "./extension";
4
+
import type EventEmitter from "events";
5
+
import type LunAST from "@moonlight-mod/lunast";
6
+
import type Moonmap from "@moonlight-mod/moonmap";
7
+
import type {
8
+
WebEventPayloads,
9
+
WebEventType,
10
+
MoonlightEventEmitter,
11
+
NodeEventType,
12
+
NodeEventPayloads
13
+
} from "./core/event";
14
+
import type { MoonlightFS } from "./fs";
9
15
10
16
export type MoonlightHost = {
11
-
asarPath: string;
12
17
config: Config;
13
-
events: EventEmitter;
14
18
extensions: DetectedExtension[];
15
19
processedExtensions: ProcessedExtensions;
20
+
asarPath: string;
21
+
events: EventEmitter;
22
+
23
+
version: string;
24
+
branch: MoonlightBranch;
16
25
17
26
getConfig: (ext: string) => ConfigExtension["config"];
27
+
getConfigPath: () => Promise<string>;
18
28
getConfigOption: <T>(ext: string, name: string) => T | undefined;
29
+
setConfigOption: <T>(ext: string, name: string, value: T) => void;
30
+
writeConfig: (config: Config) => Promise<void>;
31
+
19
32
getLogger: (id: string) => Logger;
33
+
getMoonlightDir: () => string;
34
+
getExtensionDir: (ext: string) => string;
20
35
};
21
36
22
37
export type MoonlightNode = {
···
24
39
extensions: DetectedExtension[];
25
40
processedExtensions: ProcessedExtensions;
26
41
nativesCache: Record<string, any>;
42
+
isBrowser: boolean;
43
+
events: MoonlightEventEmitter<NodeEventType, NodeEventPayloads>;
44
+
45
+
version: string;
46
+
branch: MoonlightBranch;
27
47
28
48
getConfig: (ext: string) => ConfigExtension["config"];
29
49
getConfigOption: <T>(ext: string, name: string) => T | undefined;
50
+
setConfigOption: <T>(ext: string, name: string, value: T) => Promise<void>;
51
+
writeConfig: (config: Config) => Promise<void>;
52
+
30
53
getNatives: (ext: string) => any | undefined;
31
54
getLogger: (id: string) => Logger;
55
+
getMoonlightDir: () => string;
56
+
getExtensionDir: (ext: string) => string;
57
+
};
32
58
33
-
getExtensionDir: (ext: string) => string;
34
-
writeConfig: (config: Config) => void;
59
+
export type MoonlightNodeSandboxed = {
60
+
fs: MoonlightFS;
61
+
addCors: (url: string) => void;
62
+
addBlocked: (url: string) => void;
35
63
};
36
64
37
65
export type MoonlightWeb = {
66
+
patched: Map<string, Set<string>>;
38
67
unpatched: Set<IdentifiedPatch>;
68
+
pendingModules: Set<IdentifiedWebpackModule>;
39
69
enabledExtensions: Set<string>;
70
+
events: MoonlightEventEmitter<WebEventType, WebEventPayloads>;
71
+
patchingInternals: {
72
+
onModuleLoad: (moduleId: string | string[], callback: (moduleId: string) => void) => void;
73
+
registerPatch: (patch: IdentifiedPatch) => void;
74
+
registerWebpackModule: (module: IdentifiedWebpackModule) => void;
75
+
};
76
+
localStorage: Storage;
40
77
41
-
getConfig: (ext: string) => ConfigExtension["config"];
42
-
getConfigOption: <T>(ext: string, name: string) => T | undefined;
78
+
version: string;
79
+
branch: MoonlightBranch;
80
+
apiLevel: number;
81
+
82
+
// Re-exports for ease of use
83
+
getConfig: MoonlightNode["getConfig"];
84
+
getConfigOption: MoonlightNode["getConfigOption"];
85
+
setConfigOption: MoonlightNode["setConfigOption"];
86
+
writeConfig: MoonlightNode["writeConfig"];
87
+
43
88
getNatives: (ext: string) => any | undefined;
44
89
getLogger: (id: string) => Logger;
90
+
91
+
lunast: LunAST;
92
+
moonmap: Moonmap;
45
93
};
46
94
47
95
export enum MoonlightEnv {
···
49
97
NodePreload = "node-preload",
50
98
WebPreload = "web-preload"
51
99
}
100
+
101
+
export enum MoonlightBranch {
102
+
STABLE = "stable",
103
+
NIGHTLY = "nightly",
104
+
DEV = "dev"
105
+
}
+59
-21
packages/types/src/import.d.ts
+59
-21
packages/types/src/import.d.ts
···
1
-
declare module "@moonlight-mod/wp/spacepack_spacepack" {
1
+
declare module "@moonlight-mod/wp/appPanels_appPanels" {
2
2
import { CoreExtensions } from "@moonlight-mod/types";
3
-
export const spacepack: CoreExtensions.Spacepack;
4
-
export default spacepack;
3
+
const AppPanels: CoreExtensions.AppPanels.AppPanels;
4
+
export = AppPanels;
5
5
}
6
6
7
-
declare module "@moonlight-mod/wp/common_components" {
7
+
declare module "@moonlight-mod/wp/commands_commands" {
8
8
import { CoreExtensions } from "@moonlight-mod/types";
9
-
const components: CoreExtensions.CommonComponents;
10
-
export default components;
11
-
export = components;
9
+
export const commands: CoreExtensions.Commands.Commands;
10
+
export default commands;
12
11
}
13
12
14
-
declare module "@moonlight-mod/wp/common_flux" {
13
+
declare module "@moonlight-mod/wp/common_ErrorBoundary" {
15
14
import { CoreExtensions } from "@moonlight-mod/types";
16
-
const Flux: CoreExtensions.CommonFlux;
17
-
export default Flux;
15
+
const ErrorBoundary: CoreExtensions.Common.ErrorBoundary;
16
+
export = ErrorBoundary;
18
17
}
19
-
20
-
declare module "@moonlight-mod/wp/common_fluxDispatcher" {
18
+
declare module "@moonlight-mod/wp/common_icons" {
21
19
import { CoreExtensions } from "@moonlight-mod/types";
22
-
const Dispatcher: CoreExtensions.CommonFluxDispatcher;
23
-
export default Dispatcher;
20
+
export const icons: CoreExtensions.Common.Icons;
21
+
export default icons;
24
22
}
23
+
declare module "@moonlight-mod/wp/common_stores";
25
24
26
-
declare module "@moonlight-mod/wp/common_react" {
27
-
import React from "react";
28
-
export = React;
25
+
declare module "@moonlight-mod/wp/componentEditor_dmList" {
26
+
import { CoreExtensions } from "@moonlight-mod/types";
27
+
export const dmList: CoreExtensions.ComponentEditor.DMList;
28
+
export default dmList;
29
+
}
30
+
declare module "@moonlight-mod/wp/componentEditor_memberList" {
31
+
import { CoreExtensions } from "@moonlight-mod/types";
32
+
export const memberList: CoreExtensions.ComponentEditor.MemberList;
33
+
export default memberList;
34
+
}
35
+
declare module "@moonlight-mod/wp/componentEditor_messages" {
36
+
import { CoreExtensions } from "@moonlight-mod/types";
37
+
export const message: CoreExtensions.ComponentEditor.Messages;
38
+
export default message;
29
39
}
30
40
31
-
declare module "@moonlight-mod/wp/settings_settings" {
41
+
declare module "@moonlight-mod/wp/contextMenu_evilMenu" {
32
42
import { CoreExtensions } from "@moonlight-mod/types";
33
-
export const Settings: CoreExtensions.Settings;
34
-
export default Settings;
35
-
export = Settings;
43
+
const EvilParser: CoreExtensions.ContextMenu.EvilItemParser;
44
+
export = EvilParser;
45
+
}
46
+
declare module "@moonlight-mod/wp/contextMenu_contextMenu" {
47
+
import { CoreExtensions } from "@moonlight-mod/types";
48
+
const ContextMenu: CoreExtensions.ContextMenu.ContextMenu;
49
+
export = ContextMenu;
36
50
}
37
51
38
52
declare module "@moonlight-mod/wp/markdown_markdown" {
···
40
54
const Markdown: CoreExtensions.Markdown.Markdown;
41
55
export = Markdown;
42
56
}
57
+
58
+
declare module "@moonlight-mod/wp/moonbase_moonbase" {
59
+
import { CoreExtensions } from "@moonlight-mod/types";
60
+
const Moonbase: CoreExtensions.Moonbase.Moonbase;
61
+
export = Moonbase;
62
+
}
63
+
64
+
declare module "@moonlight-mod/wp/notices_notices" {
65
+
import { CoreExtensions } from "@moonlight-mod/types";
66
+
const Notices: CoreExtensions.Notices.Notices;
67
+
export = Notices;
68
+
}
69
+
70
+
declare module "@moonlight-mod/wp/settings_settings" {
71
+
import { CoreExtensions } from "@moonlight-mod/types";
72
+
export const Settings: CoreExtensions.Settings.Settings;
73
+
export default Settings;
74
+
}
75
+
76
+
declare module "@moonlight-mod/wp/spacepack_spacepack" {
77
+
import { CoreExtensions } from "@moonlight-mod/types";
78
+
export const spacepack: CoreExtensions.Spacepack.Spacepack;
79
+
export default spacepack;
80
+
}
+14
-8
packages/types/src/index.ts
+14
-8
packages/types/src/index.ts
···
1
-
/// <reference types="node" />
2
1
/// <reference types="standalone-electron-types" />
3
2
/// <reference types="react" />
4
-
/// <reference types="flux" />
5
3
/// <reference types="./import" />
4
+
/// <reference types="./mappings" />
6
5
/* eslint-disable no-var */
7
6
8
-
import {
9
-
MoonlightEnv,
10
-
MoonlightHost,
11
-
MoonlightNode,
12
-
MoonlightWeb
13
-
} from "./globals";
7
+
import { MoonlightEnv, MoonlightHost, MoonlightNode, MoonlightNodeSandboxed, MoonlightWeb } from "./globals";
14
8
15
9
export * from "./discord";
16
10
export * from "./config";
···
19
13
export * from "./globals";
20
14
export * from "./logger";
21
15
export * as constants from "./constants";
16
+
export * from "./fs";
17
+
18
+
export type { AST } from "@moonlight-mod/lunast";
19
+
export { ModuleExport, ModuleExportType } from "@moonlight-mod/moonmap";
22
20
23
21
declare global {
24
22
const MOONLIGHT_ENV: MoonlightEnv;
···
26
24
const MOONLIGHT_INJECTOR: boolean;
27
25
const MOONLIGHT_NODE_PRELOAD: boolean;
28
26
const MOONLIGHT_WEB_PRELOAD: boolean;
27
+
const MOONLIGHT_BROWSER: boolean;
28
+
const MOONLIGHT_BRANCH: string;
29
+
const MOONLIGHT_VERSION: string;
29
30
30
31
var moonlightHost: MoonlightHost;
31
32
var moonlightNode: MoonlightNode;
33
+
var moonlightNodeSandboxed: MoonlightNodeSandboxed;
32
34
var moonlight: MoonlightWeb;
35
+
var _moonlight_coreExtensionsStr: string;
36
+
37
+
var _moonlightBrowserInit: undefined | (() => Promise<void>);
38
+
var _moonlightWebLoad: undefined | (() => Promise<void>);
33
39
}
+888
packages/types/src/mappings.d.ts
+888
packages/types/src/mappings.d.ts
···
1
+
// auto-generated
2
+
declare module "@moonlight-mod/wp/chroma-js" {}
3
+
4
+
declare module "@moonlight-mod/wp/classnames" {
5
+
import { MappedModules } from "@moonlight-mod/mappings";
6
+
const _default: MappedModules["classnames"]["default"];
7
+
export default _default;
8
+
}
9
+
10
+
declare module "@moonlight-mod/wp/dependency-graph" {
11
+
import { MappedModules } from "@moonlight-mod/mappings";
12
+
export const DepGraph: MappedModules["dependency-graph"]["DepGraph"];
13
+
}
14
+
15
+
declare module "@moonlight-mod/wp/discord/Constants" {
16
+
import { MappedModules } from "@moonlight-mod/mappings";
17
+
export const ActivityFlags: MappedModules["discord/Constants"]["ActivityFlags"];
18
+
export const ActivityTypes: MappedModules["discord/Constants"]["ActivityTypes"];
19
+
export const AnalyticsLocations: MappedModules["discord/Constants"]["AnalyticsLocations"];
20
+
export const ChannelLayouts: MappedModules["discord/Constants"]["ChannelLayouts"];
21
+
export const ChannelModes: MappedModules["discord/Constants"]["ChannelModes"];
22
+
export const ChannelTypes: MappedModules["discord/Constants"]["ChannelTypes"];
23
+
export const ChannelStreamTypes: MappedModules["discord/Constants"]["ChannelStreamTypes"];
24
+
export const ComponentActions: MappedModules["discord/Constants"]["ComponentActions"];
25
+
export const DEFAULT_ROLE_COLOR: MappedModules["discord/Constants"]["DEFAULT_ROLE_COLOR"];
26
+
export const Endpoints: MappedModules["discord/Constants"]["Endpoints"];
27
+
export const MessageFlags: MappedModules["discord/Constants"]["MessageFlags"];
28
+
export const MessageTypes: MappedModules["discord/Constants"]["MessageTypes"];
29
+
export const Permissions: MappedModules["discord/Constants"]["Permissions"];
30
+
export const PlatformTypes: MappedModules["discord/Constants"]["PlatformTypes"];
31
+
export const RelationshipTypes: MappedModules["discord/Constants"]["RelationshipTypes"];
32
+
export const Routes: MappedModules["discord/Constants"]["Routes"];
33
+
export const StatusTypes: MappedModules["discord/Constants"]["StatusTypes"];
34
+
export const Themes: MappedModules["discord/Constants"]["Themes"];
35
+
export const UserSettingsSections: MappedModules["discord/Constants"]["UserSettingsSections"];
36
+
export const UserFlags: MappedModules["discord/Constants"]["UserFlags"];
37
+
}
38
+
39
+
declare module "@moonlight-mod/wp/discord/Dispatcher" {
40
+
import { MappedModules } from "@moonlight-mod/mappings";
41
+
const _default: MappedModules["discord/Dispatcher"]["default"];
42
+
export default _default;
43
+
}
44
+
45
+
declare module "@moonlight-mod/wp/discord/actions/ContextMenuActionCreators" {
46
+
import { MappedModules } from "@moonlight-mod/mappings";
47
+
export const closeContextMenu: MappedModules["discord/actions/ContextMenuActionCreators"]["closeContextMenu"];
48
+
export const openContextMenu: MappedModules["discord/actions/ContextMenuActionCreators"]["openContextMenu"];
49
+
export const openContextMenuLazy: MappedModules["discord/actions/ContextMenuActionCreators"]["openContextMenuLazy"];
50
+
}
51
+
52
+
declare module "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators" {
53
+
import { MappedModules } from "@moonlight-mod/mappings";
54
+
const _default: MappedModules["discord/actions/UserSettingsModalActionCreators"]["default"];
55
+
export default _default;
56
+
}
57
+
58
+
declare module "@moonlight-mod/wp/discord/common/AppStartPerformance" {
59
+
import { MappedModules } from "@moonlight-mod/mappings";
60
+
const _default: MappedModules["discord/common/AppStartPerformance"]["default"];
61
+
export default _default;
62
+
}
63
+
64
+
declare module "@moonlight-mod/wp/discord/components/common/Alerts" {
65
+
import { MappedModules } from "@moonlight-mod/mappings";
66
+
const _default: MappedModules["discord/components/common/Alerts"]["default"];
67
+
export default _default;
68
+
}
69
+
70
+
declare module "@moonlight-mod/wp/discord/components/common/BaseHeaderBar" {
71
+
import { MappedModules } from "@moonlight-mod/mappings";
72
+
export const Icon: MappedModules["discord/components/common/BaseHeaderBar"]["Icon"];
73
+
export const Divider: MappedModules["discord/components/common/BaseHeaderBar"]["Divider"];
74
+
const _default: MappedModules["discord/components/common/BaseHeaderBar"]["default"];
75
+
export default _default;
76
+
}
77
+
78
+
declare module "@moonlight-mod/wp/discord/components/common/Card" {
79
+
import { MappedModules } from "@moonlight-mod/mappings";
80
+
const _default: MappedModules["discord/components/common/Card"]["default"];
81
+
export default _default;
82
+
export const Types: MappedModules["discord/components/common/Card"]["Types"];
83
+
}
84
+
85
+
declare module "@moonlight-mod/wp/discord/components/common/FileUpload" {
86
+
import { MappedModules } from "@moonlight-mod/mappings";
87
+
const _default: MappedModules["discord/components/common/FileUpload"]["default"];
88
+
export default _default;
89
+
}
90
+
91
+
declare module "@moonlight-mod/wp/discord/components/common/FormSwitch.css" {
92
+
import { MappedModules } from "@moonlight-mod/mappings";
93
+
export const container: MappedModules["discord/components/common/FormSwitch.css"]["container"];
94
+
export const labelRow: MappedModules["discord/components/common/FormSwitch.css"]["labelRow"];
95
+
export const control: MappedModules["discord/components/common/FormSwitch.css"]["control"];
96
+
export const disabled: MappedModules["discord/components/common/FormSwitch.css"]["disabled"];
97
+
export const title: MappedModules["discord/components/common/FormSwitch.css"]["title"];
98
+
export const note: MappedModules["discord/components/common/FormSwitch.css"]["note"];
99
+
export const disabledText: MappedModules["discord/components/common/FormSwitch.css"]["disabledText"];
100
+
export const dividerDefault: MappedModules["discord/components/common/FormSwitch.css"]["dividerDefault"];
101
+
}
102
+
103
+
declare module "@moonlight-mod/wp/discord/components/common/HeaderBar.css" {
104
+
import { MappedModules } from "@moonlight-mod/mappings";
105
+
export const caret: MappedModules["discord/components/common/HeaderBar.css"]["caret"];
106
+
export const children: MappedModules["discord/components/common/HeaderBar.css"]["children"];
107
+
export const clickable: MappedModules["discord/components/common/HeaderBar.css"]["clickable"];
108
+
export const container: MappedModules["discord/components/common/HeaderBar.css"]["container"];
109
+
export const divider: MappedModules["discord/components/common/HeaderBar.css"]["divider"];
110
+
export const dot: MappedModules["discord/components/common/HeaderBar.css"]["dot"];
111
+
export const hamburger: MappedModules["discord/components/common/HeaderBar.css"]["hamburger"];
112
+
export const icon: MappedModules["discord/components/common/HeaderBar.css"]["icon"];
113
+
export const iconBadge: MappedModules["discord/components/common/HeaderBar.css"]["iconBadge"];
114
+
export const iconBadgeBottom: MappedModules["discord/components/common/HeaderBar.css"]["iconBadgeBottom"];
115
+
export const iconBadgeTop: MappedModules["discord/components/common/HeaderBar.css"]["iconBadgeTop"];
116
+
export const iconWrapper: MappedModules["discord/components/common/HeaderBar.css"]["iconWrapper"];
117
+
export const scrollable: MappedModules["discord/components/common/HeaderBar.css"]["scrollable"];
118
+
export const selected: MappedModules["discord/components/common/HeaderBar.css"]["selected"];
119
+
export const themed: MappedModules["discord/components/common/HeaderBar.css"]["themed"];
120
+
export const themedMobile: MappedModules["discord/components/common/HeaderBar.css"]["themedMobile"];
121
+
export const title: MappedModules["discord/components/common/HeaderBar.css"]["title"];
122
+
export const titleWrapper: MappedModules["discord/components/common/HeaderBar.css"]["titleWrapper"];
123
+
export const toolbar: MappedModules["discord/components/common/HeaderBar.css"]["toolbar"];
124
+
export const transparent: MappedModules["discord/components/common/HeaderBar.css"]["transparent"];
125
+
export const upperContainer: MappedModules["discord/components/common/HeaderBar.css"]["upperContainer"];
126
+
}
127
+
128
+
declare module "@moonlight-mod/wp/discord/components/common/HelpMessage.css" {
129
+
import { MappedModules } from "@moonlight-mod/mappings";
130
+
export const container: MappedModules["discord/components/common/HelpMessage.css"]["container"];
131
+
export const icon: MappedModules["discord/components/common/HelpMessage.css"]["icon"];
132
+
export const iconDiv: MappedModules["discord/components/common/HelpMessage.css"]["iconDiv"];
133
+
export const text: MappedModules["discord/components/common/HelpMessage.css"]["text"];
134
+
export const positive: MappedModules["discord/components/common/HelpMessage.css"]["positive"];
135
+
export const warning: MappedModules["discord/components/common/HelpMessage.css"]["warning"];
136
+
export const info: MappedModules["discord/components/common/HelpMessage.css"]["info"];
137
+
export const error: MappedModules["discord/components/common/HelpMessage.css"]["error"];
138
+
}
139
+
140
+
declare module "@moonlight-mod/wp/discord/components/common/Image" {}
141
+
142
+
declare module "@moonlight-mod/wp/discord/components/common/PanelButton" {
143
+
import { MappedModules } from "@moonlight-mod/mappings";
144
+
const _default: MappedModules["discord/components/common/PanelButton"]["default"];
145
+
export default _default;
146
+
}
147
+
148
+
declare module "@moonlight-mod/wp/discord/components/common/Scroller.css" {
149
+
import { MappedModules } from "@moonlight-mod/mappings";
150
+
export const auto: MappedModules["discord/components/common/Scroller.css"]["auto"];
151
+
export const content: MappedModules["discord/components/common/Scroller.css"]["content"];
152
+
export const customTheme: MappedModules["discord/components/common/Scroller.css"]["customTheme"];
153
+
export const disableScrollAnchor: MappedModules["discord/components/common/Scroller.css"]["disableScrollAnchor"];
154
+
export const fade: MappedModules["discord/components/common/Scroller.css"]["fade"];
155
+
export const managedReactiveScroller: MappedModules["discord/components/common/Scroller.css"]["managedReactiveScroller"];
156
+
export const none: MappedModules["discord/components/common/Scroller.css"]["none"];
157
+
export const pointerCover: MappedModules["discord/components/common/Scroller.css"]["pointerCover"];
158
+
export const scrolling: MappedModules["discord/components/common/Scroller.css"]["scrolling"];
159
+
export const thin: MappedModules["discord/components/common/Scroller.css"]["thin"];
160
+
}
161
+
162
+
declare module "@moonlight-mod/wp/discord/components/common/index" {
163
+
import { MappedModules } from "@moonlight-mod/mappings";
164
+
export const Clickable: MappedModules["discord/components/common/index"]["Clickable"];
165
+
export const TextInput: MappedModules["discord/components/common/index"]["TextInput"];
166
+
export const TextArea: MappedModules["discord/components/common/index"]["TextArea"];
167
+
export const FormDivider: MappedModules["discord/components/common/index"]["FormDivider"];
168
+
export const FormSection: MappedModules["discord/components/common/index"]["FormSection"];
169
+
export const FormText: MappedModules["discord/components/common/index"]["FormText"];
170
+
export const FormTitle: MappedModules["discord/components/common/index"]["FormTitle"];
171
+
export const FormSwitch: MappedModules["discord/components/common/index"]["FormSwitch"];
172
+
export const FormItem: MappedModules["discord/components/common/index"]["FormItem"];
173
+
export const Slider: MappedModules["discord/components/common/index"]["Slider"];
174
+
export const Switch: MappedModules["discord/components/common/index"]["Switch"];
175
+
export const Button: MappedModules["discord/components/common/index"]["Button"];
176
+
export const Tooltip: MappedModules["discord/components/common/index"]["Tooltip"];
177
+
export const Avatar: MappedModules["discord/components/common/index"]["Avatar"];
178
+
export const AvatarSizes: MappedModules["discord/components/common/index"]["AvatarSizes"];
179
+
export const AvatarSizeSpecs: MappedModules["discord/components/common/index"]["AvatarSizeSpecs"];
180
+
export const Scroller: MappedModules["discord/components/common/index"]["Scroller"];
181
+
export const Text: MappedModules["discord/components/common/index"]["Text"];
182
+
export const Heading: MappedModules["discord/components/common/index"]["Heading"];
183
+
export const Card: MappedModules["discord/components/common/index"]["Card"];
184
+
export const Popout: MappedModules["discord/components/common/index"]["Popout"];
185
+
export const Dialog: MappedModules["discord/components/common/index"]["Dialog"];
186
+
export const Menu: MappedModules["discord/components/common/index"]["Menu"];
187
+
export const TabBar: MappedModules["discord/components/common/index"]["TabBar"];
188
+
export const SingleSelect: MappedModules["discord/components/common/index"]["SingleSelect"];
189
+
export const Select: MappedModules["discord/components/common/index"]["Select"];
190
+
export const NoticeColors: MappedModules["discord/components/common/index"]["NoticeColors"];
191
+
export const Notice: MappedModules["discord/components/common/index"]["Notice"];
192
+
export const NoticeCloseButton: MappedModules["discord/components/common/index"]["NoticeCloseButton"];
193
+
export const PrimaryCTANoticeButton: MappedModules["discord/components/common/index"]["PrimaryCTANoticeButton"];
194
+
export const Breadcrumbs: MappedModules["discord/components/common/index"]["Breadcrumbs"];
195
+
export const Image: MappedModules["discord/components/common/index"]["Image"];
196
+
export const tokens: MappedModules["discord/components/common/index"]["tokens"];
197
+
export const useVariableSelect: MappedModules["discord/components/common/index"]["useVariableSelect"];
198
+
export const useMultiSelect: MappedModules["discord/components/common/index"]["useMultiSelect"];
199
+
export const multiSelect: MappedModules["discord/components/common/index"]["multiSelect"];
200
+
export const openModal: MappedModules["discord/components/common/index"]["openModal"];
201
+
export const openModalLazy: MappedModules["discord/components/common/index"]["openModalLazy"];
202
+
export const closeModal: MappedModules["discord/components/common/index"]["closeModal"];
203
+
export const AngleBracketsIcon: MappedModules["discord/components/common/index"]["AngleBracketsIcon"];
204
+
export const ArrowAngleLeftUpIcon: MappedModules["discord/components/common/index"]["ArrowAngleLeftUpIcon"];
205
+
export const ArrowAngleRightUpIcon: MappedModules["discord/components/common/index"]["ArrowAngleRightUpIcon"];
206
+
export const ArrowsUpDownIcon: MappedModules["discord/components/common/index"]["ArrowsUpDownIcon"];
207
+
export const BookCheckIcon: MappedModules["discord/components/common/index"]["BookCheckIcon"];
208
+
export const ChannelListIcon: MappedModules["discord/components/common/index"]["ChannelListIcon"];
209
+
export const ChevronSmallDownIcon: MappedModules["discord/components/common/index"]["ChevronSmallDownIcon"];
210
+
export const ChevronSmallUpIcon: MappedModules["discord/components/common/index"]["ChevronSmallUpIcon"];
211
+
export const CircleInformationIcon: MappedModules["discord/components/common/index"]["CircleInformationIcon"];
212
+
export const CircleWarningIcon: MappedModules["discord/components/common/index"]["CircleWarningIcon"];
213
+
export const CircleXIcon: MappedModules["discord/components/common/index"]["CircleXIcon"];
214
+
export const ClydeIcon: MappedModules["discord/components/common/index"]["ClydeIcon"];
215
+
export const CopyIcon: MappedModules["discord/components/common/index"]["CopyIcon"];
216
+
export const DownloadIcon: MappedModules["discord/components/common/index"]["DownloadIcon"];
217
+
export const FullscreenEnterIcon: MappedModules["discord/components/common/index"]["FullscreenEnterIcon"];
218
+
export const GameControllerIcon: MappedModules["discord/components/common/index"]["GameControllerIcon"];
219
+
export const GlobeEarthIcon: MappedModules["discord/components/common/index"]["GlobeEarthIcon"];
220
+
export const HeartIcon: MappedModules["discord/components/common/index"]["HeartIcon"];
221
+
export const LinkIcon: MappedModules["discord/components/common/index"]["LinkIcon"];
222
+
export const MaximizeIcon: MappedModules["discord/components/common/index"]["MaximizeIcon"];
223
+
export const MinusIcon: MappedModules["discord/components/common/index"]["MinusIcon"];
224
+
export const MobilePhoneIcon: MappedModules["discord/components/common/index"]["MobilePhoneIcon"];
225
+
export const PauseIcon: MappedModules["discord/components/common/index"]["PauseIcon"];
226
+
export const PlayIcon: MappedModules["discord/components/common/index"]["PlayIcon"];
227
+
export const PlusLargeIcon: MappedModules["discord/components/common/index"]["PlusLargeIcon"];
228
+
export const RetryIcon: MappedModules["discord/components/common/index"]["RetryIcon"];
229
+
export const ScienceIcon: MappedModules["discord/components/common/index"]["ScienceIcon"];
230
+
export const ScreenIcon: MappedModules["discord/components/common/index"]["ScreenIcon"];
231
+
export const StarIcon: MappedModules["discord/components/common/index"]["StarIcon"];
232
+
export const TrashIcon: MappedModules["discord/components/common/index"]["TrashIcon"];
233
+
export const WarningIcon: MappedModules["discord/components/common/index"]["WarningIcon"];
234
+
export const WindowLaunchIcon: MappedModules["discord/components/common/index"]["WindowLaunchIcon"];
235
+
export const WindowTopOutlineIcon: MappedModules["discord/components/common/index"]["WindowTopOutlineIcon"];
236
+
export const XLargeIcon: MappedModules["discord/components/common/index"]["XLargeIcon"];
237
+
export const XSmallIcon: MappedModules["discord/components/common/index"]["XSmallIcon"];
238
+
export const ConfirmModal: MappedModules["discord/components/common/index"]["ConfirmModal"];
239
+
export const H: MappedModules["discord/components/common/index"]["H"];
240
+
export const HelpMessage: MappedModules["discord/components/common/index"]["HelpMessage"];
241
+
export const ModalCloseButton: MappedModules["discord/components/common/index"]["ModalCloseButton"];
242
+
export const ModalContent: MappedModules["discord/components/common/index"]["ModalContent"];
243
+
export const ModalFooter: MappedModules["discord/components/common/index"]["ModalFooter"];
244
+
export const ModalHeader: MappedModules["discord/components/common/index"]["ModalHeader"];
245
+
export const ModalRoot: MappedModules["discord/components/common/index"]["ModalRoot"];
246
+
export const NumberInputStepper: MappedModules["discord/components/common/index"]["NumberInputStepper"];
247
+
export const SearchableSelect: MappedModules["discord/components/common/index"]["SearchableSelect"];
248
+
export const createToast: MappedModules["discord/components/common/index"]["createToast"];
249
+
export const popToast: MappedModules["discord/components/common/index"]["popToast"];
250
+
export const showToast: MappedModules["discord/components/common/index"]["showToast"];
251
+
export const useThemeContext: MappedModules["discord/components/common/index"]["useThemeContext"];
252
+
export const AccessibilityAnnouncer: MappedModules["discord/components/common/index"]["AccessibilityAnnouncer"];
253
+
export const BackdropStyles: MappedModules["discord/components/common/index"]["BackdropStyles"];
254
+
export const BadgeShapes: MappedModules["discord/components/common/index"]["BadgeShapes"];
255
+
export const CardTypes: MappedModules["discord/components/common/index"]["CardTypes"];
256
+
export const CircleIconButtonColors: MappedModules["discord/components/common/index"]["CircleIconButtonColors"];
257
+
export const CircleIconButtonSizes: MappedModules["discord/components/common/index"]["CircleIconButtonSizes"];
258
+
export const FormErrorBlockColors: MappedModules["discord/components/common/index"]["FormErrorBlockColors"];
259
+
export const FormNoticeImagePositions: MappedModules["discord/components/common/index"]["FormNoticeImagePositions"];
260
+
export const FormTitleTags: MappedModules["discord/components/common/index"]["FormTitleTags"];
261
+
export const HelpMessageTypes: MappedModules["discord/components/common/index"]["HelpMessageTypes"];
262
+
export const ModalSize: MappedModules["discord/components/common/index"]["ModalSize"];
263
+
export const ModalTransitionState: MappedModules["discord/components/common/index"]["ModalTransitionState"];
264
+
export const PRETTY_KEYS: MappedModules["discord/components/common/index"]["PRETTY_KEYS"];
265
+
export const SelectLooks: MappedModules["discord/components/common/index"]["SelectLooks"];
266
+
export const SpinnerTypes: MappedModules["discord/components/common/index"]["SpinnerTypes"];
267
+
export const StatusTypes: MappedModules["discord/components/common/index"]["StatusTypes"];
268
+
export const ToastPosition: MappedModules["discord/components/common/index"]["ToastPosition"];
269
+
export const ToastType: MappedModules["discord/components/common/index"]["ToastType"];
270
+
export const TransitionStates: MappedModules["discord/components/common/index"]["TransitionStates"];
271
+
export const DEFAULT_MODAL_CONTEXT: MappedModules["discord/components/common/index"]["DEFAULT_MODAL_CONTEXT"];
272
+
export const LOW_SATURATION_THRESHOLD: MappedModules["discord/components/common/index"]["LOW_SATURATION_THRESHOLD"];
273
+
export const LayerClassName: MappedModules["discord/components/common/index"]["LayerClassName"];
274
+
export const POPOUT_MODAL_CONTEXT: MappedModules["discord/components/common/index"]["POPOUT_MODAL_CONTEXT"];
275
+
}
276
+
277
+
declare module "@moonlight-mod/wp/discord/components/modals/ConfirmModal" {
278
+
import { MappedModules } from "@moonlight-mod/mappings";
279
+
const _default: MappedModules["discord/components/modals/ConfirmModal"]["default"];
280
+
export default _default;
281
+
}
282
+
283
+
declare module "@moonlight-mod/wp/discord/lib/BaseRecord" {
284
+
import { MappedModules } from "@moonlight-mod/mappings";
285
+
const _default: MappedModules["discord/lib/BaseRecord"]["default"];
286
+
export default _default;
287
+
}
288
+
289
+
declare module "@moonlight-mod/wp/discord/lib/web/Storage" {
290
+
import { MappedModules } from "@moonlight-mod/mappings";
291
+
export const ObjectStorage: MappedModules["discord/lib/web/Storage"]["ObjectStorage"];
292
+
export const impl: MappedModules["discord/lib/web/Storage"]["impl"];
293
+
}
294
+
295
+
declare module "@moonlight-mod/wp/discord/modules/build_overrides/web/BuildOverride.css" {
296
+
import { MappedModules } from "@moonlight-mod/mappings";
297
+
export const wrapper: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["wrapper"];
298
+
export const titleRegion: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["titleRegion"];
299
+
export const title: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["title"];
300
+
export const infoIcon: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["infoIcon"];
301
+
export const copyLink: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["copyLink"];
302
+
export const copied: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["copied"];
303
+
export const copyLinkIcon: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["copyLinkIcon"];
304
+
export const content: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["content"];
305
+
export const infoLink: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["infoLink"];
306
+
export const buildInfo: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buildInfo"];
307
+
export const button: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["button"];
308
+
export const buttonSize: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buttonSize"];
309
+
export const subHead: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["subHead"];
310
+
export const icon: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["icon"];
311
+
export const buildDetails: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buildDetails"];
312
+
export const barLoader: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["barLoader"];
313
+
export const barTitle: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["barTitle"];
314
+
export const buttonLoader: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buttonLoader"];
315
+
export const disabledButtonOverride: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["disabledButtonOverride"];
316
+
}
317
+
318
+
declare module "@moonlight-mod/wp/discord/modules/discovery/web/Discovery.css" {
319
+
import { MappedModules } from "@moonlight-mod/mappings";
320
+
export const header: MappedModules["discord/modules/discovery/web/Discovery.css"]["header"];
321
+
export const headerImage: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerImage"];
322
+
export const headerImageSimple: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerImageSimple"];
323
+
export const headerImageBG: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerImageBG"];
324
+
export const searchTitle: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchTitle"];
325
+
export const searchSubtitle: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchSubtitle"];
326
+
export const headerContentWrapper: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerContentWrapper"];
327
+
export const headerContent: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerContent"];
328
+
export const headerContentSmall: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerContentSmall"];
329
+
export const searchBox: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchBox"];
330
+
export const searchBoxInput: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchBoxInput"];
331
+
export const closeIcon: MappedModules["discord/modules/discovery/web/Discovery.css"]["closeIcon"];
332
+
export const searchIcon: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchIcon"];
333
+
export const tabBar: MappedModules["discord/modules/discovery/web/Discovery.css"]["tabBar"];
334
+
export const tabBarItem: MappedModules["discord/modules/discovery/web/Discovery.css"]["tabBarItem"];
335
+
export const sectionHeader: MappedModules["discord/modules/discovery/web/Discovery.css"]["sectionHeader"];
336
+
}
337
+
338
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/Forums.css" {
339
+
import { MappedModules } from "@moonlight-mod/mappings";
340
+
export const container: MappedModules["discord/modules/forums/web/Forums.css"]["container"];
341
+
export const uploadArea: MappedModules["discord/modules/forums/web/Forums.css"]["uploadArea"];
342
+
export const label: MappedModules["discord/modules/forums/web/Forums.css"]["label"];
343
+
export const content: MappedModules["discord/modules/forums/web/Forums.css"]["content"];
344
+
export const noListContainer: MappedModules["discord/modules/forums/web/Forums.css"]["noListContainer"];
345
+
export const list: MappedModules["discord/modules/forums/web/Forums.css"]["list"];
346
+
export const grid: MappedModules["discord/modules/forums/web/Forums.css"]["grid"];
347
+
export const headerRow: MappedModules["discord/modules/forums/web/Forums.css"]["headerRow"];
348
+
export const card: MappedModules["discord/modules/forums/web/Forums.css"]["card"];
349
+
export const columnsSpan: MappedModules["discord/modules/forums/web/Forums.css"]["columnsSpan"];
350
+
export const emptyStateRow: MappedModules["discord/modules/forums/web/Forums.css"]["emptyStateRow"];
351
+
export const newMemberBanner: MappedModules["discord/modules/forums/web/Forums.css"]["newMemberBanner"];
352
+
export const gridViewBanner: MappedModules["discord/modules/forums/web/Forums.css"]["gridViewBanner"];
353
+
export const placeholder: MappedModules["discord/modules/forums/web/Forums.css"]["placeholder"];
354
+
export const mainCard: MappedModules["discord/modules/forums/web/Forums.css"]["mainCard"];
355
+
export const emptyMainCard: MappedModules["discord/modules/forums/web/Forums.css"]["emptyMainCard"];
356
+
export const outOfDate: MappedModules["discord/modules/forums/web/Forums.css"]["outOfDate"];
357
+
export const header: MappedModules["discord/modules/forums/web/Forums.css"]["header"];
358
+
export const matchingPostsRow: MappedModules["discord/modules/forums/web/Forums.css"]["matchingPostsRow"];
359
+
export const headerWithMatchingPosts: MappedModules["discord/modules/forums/web/Forums.css"]["headerWithMatchingPosts"];
360
+
export const noForm: MappedModules["discord/modules/forums/web/Forums.css"]["noForm"];
361
+
export const sortContainer: MappedModules["discord/modules/forums/web/Forums.css"]["sortContainer"];
362
+
export const sort: MappedModules["discord/modules/forums/web/Forums.css"]["sort"];
363
+
export const sortPopout: MappedModules["discord/modules/forums/web/Forums.css"]["sortPopout"];
364
+
export const archivedDividerRow: MappedModules["discord/modules/forums/web/Forums.css"]["archivedDividerRow"];
365
+
export const archivedDivider: MappedModules["discord/modules/forums/web/Forums.css"]["archivedDivider"];
366
+
export const newPostsButton: MappedModules["discord/modules/forums/web/Forums.css"]["newPostsButton"];
367
+
export const loadingCard: MappedModules["discord/modules/forums/web/Forums.css"]["loadingCard"];
368
+
export const enterIcon: MappedModules["discord/modules/forums/web/Forums.css"]["enterIcon"];
369
+
export const warnIcon: MappedModules["discord/modules/forums/web/Forums.css"]["warnIcon"];
370
+
export const searchIcon: MappedModules["discord/modules/forums/web/Forums.css"]["searchIcon"];
371
+
export const missingReadHistoryPermission: MappedModules["discord/modules/forums/web/Forums.css"]["missingReadHistoryPermission"];
372
+
export const divider: MappedModules["discord/modules/forums/web/Forums.css"]["divider"];
373
+
export const tagsContainer: MappedModules["discord/modules/forums/web/Forums.css"]["tagsContainer"];
374
+
export const filterIcon: MappedModules["discord/modules/forums/web/Forums.css"]["filterIcon"];
375
+
export const tagList: MappedModules["discord/modules/forums/web/Forums.css"]["tagList"];
376
+
export const tagListInner: MappedModules["discord/modules/forums/web/Forums.css"]["tagListInner"];
377
+
export const tag: MappedModules["discord/modules/forums/web/Forums.css"]["tag"];
378
+
export const tagsButton: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButton"];
379
+
export const tagsButtonInner: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButtonInner"];
380
+
export const tagsButtonPlaceholder: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButtonPlaceholder"];
381
+
export const tagsButtonWithCount: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButtonWithCount"];
382
+
export const sortDropdown: MappedModules["discord/modules/forums/web/Forums.css"]["sortDropdown"];
383
+
export const sortDropdownInner: MappedModules["discord/modules/forums/web/Forums.css"]["sortDropdownInner"];
384
+
export const sortDropdownText: MappedModules["discord/modules/forums/web/Forums.css"]["sortDropdownText"];
385
+
export const clear: MappedModules["discord/modules/forums/web/Forums.css"]["clear"];
386
+
export const matchingPosts: MappedModules["discord/modules/forums/web/Forums.css"]["matchingPosts"];
387
+
export const startPostHelp: MappedModules["discord/modules/forums/web/Forums.css"]["startPostHelp"];
388
+
export const tagsSpacer: MappedModules["discord/modules/forums/web/Forums.css"]["tagsSpacer"];
389
+
export const keyboardShortcut: MappedModules["discord/modules/forums/web/Forums.css"]["keyboardShortcut"];
390
+
export const key: MappedModules["discord/modules/forums/web/Forums.css"]["key"];
391
+
export const countContainer: MappedModules["discord/modules/forums/web/Forums.css"]["countContainer"];
392
+
export const countText: MappedModules["discord/modules/forums/web/Forums.css"]["countText"];
393
+
export const optInNotice: MappedModules["discord/modules/forums/web/Forums.css"]["optInNotice"];
394
+
}
395
+
396
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/Header.css" {
397
+
import { MappedModules } from "@moonlight-mod/mappings";
398
+
export const container: MappedModules["discord/modules/forums/web/Header.css"]["container"];
399
+
export const header: MappedModules["discord/modules/forums/web/Header.css"]["header"];
400
+
export const headerLeft: MappedModules["discord/modules/forums/web/Header.css"]["headerLeft"];
401
+
export const headerText: MappedModules["discord/modules/forums/web/Header.css"]["headerText"];
402
+
export const countContainer: MappedModules["discord/modules/forums/web/Header.css"]["countContainer"];
403
+
export const countText: MappedModules["discord/modules/forums/web/Header.css"]["countText"];
404
+
export const tagContainer: MappedModules["discord/modules/forums/web/Header.css"]["tagContainer"];
405
+
export const tag: MappedModules["discord/modules/forums/web/Header.css"]["tag"];
406
+
export const clear: MappedModules["discord/modules/forums/web/Header.css"]["clear"];
407
+
export const row: MappedModules["discord/modules/forums/web/Header.css"]["row"];
408
+
export const separator: MappedModules["discord/modules/forums/web/Header.css"]["separator"];
409
+
}
410
+
411
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/SortMenu.css" {
412
+
import { MappedModules } from "@moonlight-mod/mappings";
413
+
export const container: MappedModules["discord/modules/forums/web/SortMenu.css"]["container"];
414
+
export const clearText: MappedModules["discord/modules/forums/web/SortMenu.css"]["clearText"];
415
+
}
416
+
417
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/Tag" {
418
+
import { MappedModules } from "@moonlight-mod/mappings";
419
+
const _default: MappedModules["discord/modules/forums/web/Tag"]["default"];
420
+
export default _default;
421
+
export const TagBar: MappedModules["discord/modules/forums/web/Tag"]["TagBar"];
422
+
}
423
+
424
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css" {
425
+
import { MappedModules } from "@moonlight-mod/mappings";
426
+
export const addButton: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["addButton"];
427
+
export const container: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["container"];
428
+
export const emptyRowContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["emptyRowContainer"];
429
+
export const emptyRowText: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["emptyRowText"];
430
+
export const headerContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["headerContainer"];
431
+
export const list: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["list"];
432
+
export const memberDetails: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["memberDetails"];
433
+
export const memberRow: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["memberRow"];
434
+
export const removeButton: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeButton"];
435
+
export const removeButtonContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeButtonContainer"];
436
+
export const removeButtonDisabled: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeButtonDisabled"];
437
+
export const removeTip: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeTip"];
438
+
export const searchContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["searchContainer"];
439
+
export const searchWarning: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["searchWarning"];
440
+
}
441
+
442
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/web/AppCard.css" {
443
+
import { MappedModules } from "@moonlight-mod/mappings";
444
+
export const card: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["card"];
445
+
export const inModal: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["inModal"];
446
+
export const cardHeader: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["cardHeader"];
447
+
export const title: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["title"];
448
+
}
449
+
450
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/web/AppCardItem.css" {
451
+
import { MappedModules } from "@moonlight-mod/mappings";
452
+
export const icon: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["icon"];
453
+
export const identifier: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["identifier"];
454
+
export const item: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["item"];
455
+
export const statusContainer: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["statusContainer"];
456
+
export const statusLine: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["statusLine"];
457
+
export const statusIcon: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["statusIcon"];
458
+
}
459
+
460
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/web/SearchSection.css" {
461
+
import { MappedModules } from "@moonlight-mod/mappings";
462
+
export const container: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["container"];
463
+
export const headerContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["headerContainer"];
464
+
export const searchContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["searchContainer"];
465
+
export const searchWarning: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["searchWarning"];
466
+
export const addButton: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["addButton"];
467
+
export const memberRow: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["memberRow"];
468
+
export const emptyRowContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["emptyRowContainer"];
469
+
export const emptyRowText: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["emptyRowText"];
470
+
export const memberDetails: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["memberDetails"];
471
+
export const list: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["list"];
472
+
export const removeButtonContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeButtonContainer"];
473
+
export const removeButton: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeButton"];
474
+
export const removeButtonDisabled: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeButtonDisabled"];
475
+
export const removeTip: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeTip"];
476
+
}
477
+
478
+
declare module "@moonlight-mod/wp/discord/modules/guild_sidebar/web/CategoryChannel.css" {
479
+
import { MappedModules } from "@moonlight-mod/mappings";
480
+
export const containerDefault: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["containerDefault"];
481
+
export const containerDragBefore: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["containerDragBefore"];
482
+
export const containerDragAfter: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["containerDragAfter"];
483
+
export const addButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["addButton"];
484
+
export const forceVisible: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["forceVisible"];
485
+
export const iconVisibility: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["iconVisibility"];
486
+
export const addButtonIcon: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["addButtonIcon"];
487
+
export const wrapper: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["wrapper"];
488
+
export const wrapperStatic: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["wrapperStatic"];
489
+
export const clickable: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["clickable"];
490
+
export const children: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["children"];
491
+
export const mainContent: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["mainContent"];
492
+
export const icon: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["icon"];
493
+
export const collapsed: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["collapsed"];
494
+
export const muted: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["muted"];
495
+
export const name: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["name"];
496
+
export const dismissWrapper: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["dismissWrapper"];
497
+
export const dismissButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["dismissButton"];
498
+
export const dismiss: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["dismiss"];
499
+
export const voiceChannelsButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["voiceChannelsButton"];
500
+
export const voiceChannelsToggleIcon: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["voiceChannelsToggleIcon"];
501
+
export const refreshVoiceChannelsButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["refreshVoiceChannelsButton"];
502
+
export const refreshVoiceChannelsButtonInner: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["refreshVoiceChannelsButtonInner"];
503
+
}
504
+
505
+
declare module "@moonlight-mod/wp/discord/modules/markup/MarkupUtils" {
506
+
import { MappedModules } from "@moonlight-mod/mappings";
507
+
const _default: MappedModules["discord/modules/markup/MarkupUtils"]["default"];
508
+
export default _default;
509
+
}
510
+
511
+
declare module "@moonlight-mod/wp/discord/modules/menus/web/Menu" {
512
+
import { MappedModules } from "@moonlight-mod/mappings";
513
+
export const MenuSpinner: MappedModules["discord/modules/menus/web/Menu"]["MenuSpinner"];
514
+
export const Menu: MappedModules["discord/modules/menus/web/Menu"]["Menu"];
515
+
}
516
+
517
+
declare module "@moonlight-mod/wp/discord/modules/messages/web/Markup.css" {
518
+
import { MappedModules } from "@moonlight-mod/mappings";
519
+
export const markup: MappedModules["discord/modules/messages/web/Markup.css"]["markup"];
520
+
export const inlineFormat: MappedModules["discord/modules/messages/web/Markup.css"]["inlineFormat"];
521
+
export const codeContainer: MappedModules["discord/modules/messages/web/Markup.css"]["codeContainer"];
522
+
export const codeActions: MappedModules["discord/modules/messages/web/Markup.css"]["codeActions"];
523
+
export const blockquoteContainer: MappedModules["discord/modules/messages/web/Markup.css"]["blockquoteContainer"];
524
+
export const blockquoteDivider: MappedModules["discord/modules/messages/web/Markup.css"]["blockquoteDivider"];
525
+
export const slateBlockquoteContainer: MappedModules["discord/modules/messages/web/Markup.css"]["slateBlockquoteContainer"];
526
+
export const roleMention: MappedModules["discord/modules/messages/web/Markup.css"]["roleMention"];
527
+
export const rolePopout: MappedModules["discord/modules/messages/web/Markup.css"]["rolePopout"];
528
+
export const roleHeader: MappedModules["discord/modules/messages/web/Markup.css"]["roleHeader"];
529
+
export const roleScroller: MappedModules["discord/modules/messages/web/Markup.css"]["roleScroller"];
530
+
export const timestamp: MappedModules["discord/modules/messages/web/Markup.css"]["timestamp"];
531
+
export const timestampTooltip: MappedModules["discord/modules/messages/web/Markup.css"]["timestampTooltip"];
532
+
}
533
+
534
+
declare module "@moonlight-mod/wp/discord/modules/messages/web/Message.css" {
535
+
import { MappedModules } from "@moonlight-mod/mappings";
536
+
export const wrapper: MappedModules["discord/modules/messages/web/Message.css"]["wrapper"];
537
+
export const compact: MappedModules["discord/modules/messages/web/Message.css"]["compact"];
538
+
export const cozy: MappedModules["discord/modules/messages/web/Message.css"]["cozy"];
539
+
export const contentOnly: MappedModules["discord/modules/messages/web/Message.css"]["contentOnly"];
540
+
export const repliedMessage: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessage"];
541
+
export const threadMessageAccessory: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessory"];
542
+
export const executedCommand: MappedModules["discord/modules/messages/web/Message.css"]["executedCommand"];
543
+
export const latin12CompactTimeStamp: MappedModules["discord/modules/messages/web/Message.css"]["latin12CompactTimeStamp"];
544
+
export const latin24CompactTimeStamp: MappedModules["discord/modules/messages/web/Message.css"]["latin24CompactTimeStamp"];
545
+
export const asianCompactTimeStamp: MappedModules["discord/modules/messages/web/Message.css"]["asianCompactTimeStamp"];
546
+
export const contextCommandMessage: MappedModules["discord/modules/messages/web/Message.css"]["contextCommandMessage"];
547
+
export const messageSpine: MappedModules["discord/modules/messages/web/Message.css"]["messageSpine"];
548
+
export const repliedMessageClickableSpine: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessageClickableSpine"];
549
+
export const repliedMessageContentHovered: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessageContentHovered"];
550
+
export const threadMessageAccessoryAvatar: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryAvatar"];
551
+
export const replyAvatar: MappedModules["discord/modules/messages/web/Message.css"]["replyAvatar"];
552
+
export const replyBadge: MappedModules["discord/modules/messages/web/Message.css"]["replyBadge"];
553
+
export const executedCommandAvatar: MappedModules["discord/modules/messages/web/Message.css"]["executedCommandAvatar"];
554
+
export const replyChatIconContainer: MappedModules["discord/modules/messages/web/Message.css"]["replyChatIconContainer"];
555
+
export const replyIcon: MappedModules["discord/modules/messages/web/Message.css"]["replyIcon"];
556
+
export const clanTagChiplet: MappedModules["discord/modules/messages/web/Message.css"]["clanTagChiplet"];
557
+
export const userJoinSystemMessageIcon: MappedModules["discord/modules/messages/web/Message.css"]["userJoinSystemMessageIcon"];
558
+
export const ticketIcon: MappedModules["discord/modules/messages/web/Message.css"]["ticketIcon"];
559
+
export const commandIcon: MappedModules["discord/modules/messages/web/Message.css"]["commandIcon"];
560
+
export const username: MappedModules["discord/modules/messages/web/Message.css"]["username"];
561
+
export const roleDot: MappedModules["discord/modules/messages/web/Message.css"]["roleDot"];
562
+
export const commandName: MappedModules["discord/modules/messages/web/Message.css"]["commandName"];
563
+
export const appsIcon: MappedModules["discord/modules/messages/web/Message.css"]["appsIcon"];
564
+
export const appLauncherOnboardingCommandName: MappedModules["discord/modules/messages/web/Message.css"]["appLauncherOnboardingCommandName"];
565
+
export const targetUsername: MappedModules["discord/modules/messages/web/Message.css"]["targetUsername"];
566
+
export const executedCommandSeparator: MappedModules["discord/modules/messages/web/Message.css"]["executedCommandSeparator"];
567
+
export const repliedTextPreview: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextPreview"];
568
+
export const threadMessageAccessoryPreview: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryPreview"];
569
+
export const repliedTextContent: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextContent"];
570
+
export const clickable: MappedModules["discord/modules/messages/web/Message.css"]["clickable"];
571
+
export const repliedMessageClickableSpineHovered: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessageClickableSpineHovered"];
572
+
export const threadMessageAccessoryContent: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryContent"];
573
+
export const repliedTextPlaceholder: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextPlaceholder"];
574
+
export const threadMessageAccessoryPlaceholder: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryPlaceholder"];
575
+
export const repliedTextContentTrailingIcon: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextContentTrailingIcon"];
576
+
export const threadMessageAccessoryContentTrailingIcon: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryContentTrailingIcon"];
577
+
export const repliedTextContentLeadingIcon: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextContentLeadingIcon"];
578
+
export const threadMessageAccessoryContentLeadingIcon: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryContentLeadingIcon"];
579
+
export const contents: MappedModules["discord/modules/messages/web/Message.css"]["contents"];
580
+
export const zalgo: MappedModules["discord/modules/messages/web/Message.css"]["zalgo"];
581
+
export const messageContent: MappedModules["discord/modules/messages/web/Message.css"]["messageContent"];
582
+
export const header: MappedModules["discord/modules/messages/web/Message.css"]["header"];
583
+
export const buttonContainer: MappedModules["discord/modules/messages/web/Message.css"]["buttonContainer"];
584
+
export const avatar: MappedModules["discord/modules/messages/web/Message.css"]["avatar"];
585
+
export const avatarDecoration: MappedModules["discord/modules/messages/web/Message.css"]["avatarDecoration"];
586
+
export const roleIcon: MappedModules["discord/modules/messages/web/Message.css"]["roleIcon"];
587
+
export const timestamp: MappedModules["discord/modules/messages/web/Message.css"]["timestamp"];
588
+
export const timestampInline: MappedModules["discord/modules/messages/web/Message.css"]["timestampInline"];
589
+
export const alt: MappedModules["discord/modules/messages/web/Message.css"]["alt"];
590
+
export const timestampTooltip: MappedModules["discord/modules/messages/web/Message.css"]["timestampTooltip"];
591
+
export const timestampVisibleOnHover: MappedModules["discord/modules/messages/web/Message.css"]["timestampVisibleOnHover"];
592
+
export const nitroAuthorBadgeTootip: MappedModules["discord/modules/messages/web/Message.css"]["nitroAuthorBadgeTootip"];
593
+
export const headerText: MappedModules["discord/modules/messages/web/Message.css"]["headerText"];
594
+
export const hasRoleIcon: MappedModules["discord/modules/messages/web/Message.css"]["hasRoleIcon"];
595
+
export const hasBadges: MappedModules["discord/modules/messages/web/Message.css"]["hasBadges"];
596
+
export const botTagCompact: MappedModules["discord/modules/messages/web/Message.css"]["botTagCompact"];
597
+
export const botTagCozy: MappedModules["discord/modules/messages/web/Message.css"]["botTagCozy"];
598
+
export const nitroBadgeSvg: MappedModules["discord/modules/messages/web/Message.css"]["nitroBadgeSvg"];
599
+
export const nitroAuthorBadgeContainer: MappedModules["discord/modules/messages/web/Message.css"]["nitroAuthorBadgeContainer"];
600
+
export const separator: MappedModules["discord/modules/messages/web/Message.css"]["separator"];
601
+
export const hasThread: MappedModules["discord/modules/messages/web/Message.css"]["hasThread"];
602
+
export const isSystemMessage: MappedModules["discord/modules/messages/web/Message.css"]["isSystemMessage"];
603
+
export const hasReply: MappedModules["discord/modules/messages/web/Message.css"]["hasReply"];
604
+
export const markupRtl: MappedModules["discord/modules/messages/web/Message.css"]["markupRtl"];
605
+
export const isSending: MappedModules["discord/modules/messages/web/Message.css"]["isSending"];
606
+
export const isFailed: MappedModules["discord/modules/messages/web/Message.css"]["isFailed"];
607
+
export const isUnsupported: MappedModules["discord/modules/messages/web/Message.css"]["isUnsupported"];
608
+
export const edited: MappedModules["discord/modules/messages/web/Message.css"]["edited"];
609
+
export const communicationDisabled: MappedModules["discord/modules/messages/web/Message.css"]["communicationDisabled"];
610
+
export const compactCommunicationDisabled: MappedModules["discord/modules/messages/web/Message.css"]["compactCommunicationDisabled"];
611
+
export const communicationDisabledOpacity: MappedModules["discord/modules/messages/web/Message.css"]["communicationDisabledOpacity"];
612
+
export const badgesContainer: MappedModules["discord/modules/messages/web/Message.css"]["badgesContainer"];
613
+
}
614
+
615
+
declare module "@moonlight-mod/wp/discord/modules/modals/Modals" {
616
+
import { MappedModules } from "@moonlight-mod/mappings";
617
+
export const closeAllModals: MappedModules["discord/modules/modals/Modals"]["closeAllModals"];
618
+
export const closeAllModalsForContext: MappedModules["discord/modules/modals/Modals"]["closeAllModalsForContext"];
619
+
export const closeModal: MappedModules["discord/modules/modals/Modals"]["closeModal"];
620
+
export const getInteractingModalContext: MappedModules["discord/modules/modals/Modals"]["getInteractingModalContext"];
621
+
export const hasAnyModalOpen: MappedModules["discord/modules/modals/Modals"]["hasAnyModalOpen"];
622
+
export const hasAnyModalOpenSelector: MappedModules["discord/modules/modals/Modals"]["hasAnyModalOpenSelector"];
623
+
export const hasModalOpen: MappedModules["discord/modules/modals/Modals"]["hasModalOpen"];
624
+
export const hasModalOpenSelector: MappedModules["discord/modules/modals/Modals"]["hasModalOpenSelector"];
625
+
export const openModal: MappedModules["discord/modules/modals/Modals"]["openModal"];
626
+
export const openModalLazy: MappedModules["discord/modules/modals/Modals"]["openModalLazy"];
627
+
export const updateModal: MappedModules["discord/modules/modals/Modals"]["updateModal"];
628
+
export const useHasAnyModalOpen: MappedModules["discord/modules/modals/Modals"]["useHasAnyModalOpen"];
629
+
export const useIsModalAtTop: MappedModules["discord/modules/modals/Modals"]["useIsModalAtTop"];
630
+
export const useModalsStore: MappedModules["discord/modules/modals/Modals"]["useModalsStore"];
631
+
}
632
+
633
+
declare module "@moonlight-mod/wp/discord/modules/oauth2/index" {
634
+
import { MappedModules } from "@moonlight-mod/mappings";
635
+
export const OAuth2AuthorizeModal: MappedModules["discord/modules/oauth2/index"]["OAuth2AuthorizeModal"];
636
+
export const OAuth2AuthorizePage: MappedModules["discord/modules/oauth2/index"]["OAuth2AuthorizePage"];
637
+
export const getOAuth2AuthorizeProps: MappedModules["discord/modules/oauth2/index"]["getOAuth2AuthorizeProps"];
638
+
export const openOAuth2Modal: MappedModules["discord/modules/oauth2/index"]["openOAuth2Modal"];
639
+
export const openOAuth2ModalWithCreateGuildModal: MappedModules["discord/modules/oauth2/index"]["openOAuth2ModalWithCreateGuildModal"];
640
+
export const useOAuth2AuthorizeForm: MappedModules["discord/modules/oauth2/index"]["useOAuth2AuthorizeForm"];
641
+
}
642
+
643
+
declare module "@moonlight-mod/wp/discord/modules/people/web/PeoplePage.css" {
644
+
import { MappedModules } from "@moonlight-mod/mappings";
645
+
export const addFriend: MappedModules["discord/modules/people/web/PeoplePage.css"]["addFriend"];
646
+
export const badge: MappedModules["discord/modules/people/web/PeoplePage.css"]["badge"];
647
+
export const container: MappedModules["discord/modules/people/web/PeoplePage.css"]["container"];
648
+
export const inviteToolbar: MappedModules["discord/modules/people/web/PeoplePage.css"]["inviteToolbar"];
649
+
export const item: MappedModules["discord/modules/people/web/PeoplePage.css"]["item"];
650
+
export const nowPlayingColumn: MappedModules["discord/modules/people/web/PeoplePage.css"]["nowPlayingColumn"];
651
+
export const peopleColumn: MappedModules["discord/modules/people/web/PeoplePage.css"]["peopleColumn"];
652
+
export const tabBar: MappedModules["discord/modules/people/web/PeoplePage.css"]["tabBar"];
653
+
export const tabBody: MappedModules["discord/modules/people/web/PeoplePage.css"]["tabBody"];
654
+
}
655
+
656
+
declare module "@moonlight-mod/wp/discord/modules/user_profile/web/BiteSizeActivity.css" {
657
+
import { MappedModules } from "@moonlight-mod/mappings";
658
+
export const header: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["header"];
659
+
export const headerTag: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["headerTag"];
660
+
export const body: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["body"];
661
+
export const footer: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["footer"];
662
+
export const backdrop: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["backdrop"];
663
+
export const toast: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["toast"];
664
+
export const activity: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["activity"];
665
+
export const upsell: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["upsell"];
666
+
}
667
+
668
+
declare module "@moonlight-mod/wp/discord/packages/flux" {
669
+
import { MappedModules } from "@moonlight-mod/mappings";
670
+
export const BatchedStoreListener: MappedModules["discord/packages/flux"]["BatchedStoreListener"];
671
+
export const Dispatcher: MappedModules["discord/packages/flux"]["Dispatcher"];
672
+
export const Store: MappedModules["discord/packages/flux"]["Store"];
673
+
const _default: MappedModules["discord/packages/flux"]["default"];
674
+
export default _default;
675
+
export const statesWillNeverBeEqual: MappedModules["discord/packages/flux"]["statesWillNeverBeEqual"];
676
+
export const useStateFromStores: MappedModules["discord/packages/flux"]["useStateFromStores"];
677
+
export const useStateFromStoresArray: MappedModules["discord/packages/flux"]["useStateFromStoresArray"];
678
+
export const useStateFromStoresObject: MappedModules["discord/packages/flux"]["useStateFromStoresObject"];
679
+
}
680
+
681
+
declare module "@moonlight-mod/wp/discord/packages/flux/BatchedStoreListener" {
682
+
import { MappedModules } from "@moonlight-mod/mappings";
683
+
const _default: MappedModules["discord/packages/flux/BatchedStoreListener"]["default"];
684
+
export default _default;
685
+
}
686
+
687
+
declare module "@moonlight-mod/wp/discord/packages/flux/ChangeListeners" {
688
+
import { MappedModules } from "@moonlight-mod/mappings";
689
+
const _default: MappedModules["discord/packages/flux/ChangeListeners"]["default"];
690
+
export default _default;
691
+
}
692
+
693
+
declare module "@moonlight-mod/wp/discord/packages/flux/Dispatcher" {
694
+
import { MappedModules } from "@moonlight-mod/mappings";
695
+
export const Dispatcher: MappedModules["discord/packages/flux/Dispatcher"]["Dispatcher"];
696
+
}
697
+
698
+
declare module "@moonlight-mod/wp/discord/packages/flux/Emitter" {
699
+
import { MappedModules } from "@moonlight-mod/mappings";
700
+
const _default: MappedModules["discord/packages/flux/Emitter"]["default"];
701
+
export default _default;
702
+
}
703
+
704
+
declare module "@moonlight-mod/wp/discord/packages/flux/LoggingUtils" {
705
+
import { MappedModules } from "@moonlight-mod/mappings";
706
+
const _default: MappedModules["discord/packages/flux/LoggingUtils"]["default"];
707
+
export default _default;
708
+
}
709
+
710
+
declare module "@moonlight-mod/wp/discord/packages/flux/PersistedStore" {
711
+
import { MappedModules } from "@moonlight-mod/mappings";
712
+
export const PersistedStore: MappedModules["discord/packages/flux/PersistedStore"]["PersistedStore"];
713
+
}
714
+
715
+
declare module "@moonlight-mod/wp/discord/packages/flux/Store" {
716
+
import { MappedModules } from "@moonlight-mod/mappings";
717
+
export const Store: MappedModules["discord/packages/flux/Store"]["Store"];
718
+
}
719
+
720
+
declare module "@moonlight-mod/wp/discord/packages/flux/connectStores" {
721
+
import { MappedModules } from "@moonlight-mod/mappings";
722
+
const _default: MappedModules["discord/packages/flux/connectStores"]["default"];
723
+
export default _default;
724
+
}
725
+
726
+
declare module "@moonlight-mod/wp/discord/records/UserRecord" {
727
+
import { MappedModules } from "@moonlight-mod/mappings";
728
+
const _default: MappedModules["discord/records/UserRecord"]["default"];
729
+
export default _default;
730
+
}
731
+
732
+
declare module "@moonlight-mod/wp/discord/styles/shared/Margins.css" {
733
+
import { MappedModules } from "@moonlight-mod/mappings";
734
+
export const marginReset: MappedModules["discord/styles/shared/Margins.css"]["marginReset"];
735
+
export const marginTop4: MappedModules["discord/styles/shared/Margins.css"]["marginTop4"];
736
+
export const marginBottom4: MappedModules["discord/styles/shared/Margins.css"]["marginBottom4"];
737
+
export const marginTop8: MappedModules["discord/styles/shared/Margins.css"]["marginTop8"];
738
+
export const marginBottom8: MappedModules["discord/styles/shared/Margins.css"]["marginBottom8"];
739
+
export const marginTop20: MappedModules["discord/styles/shared/Margins.css"]["marginTop20"];
740
+
export const marginBottom20: MappedModules["discord/styles/shared/Margins.css"]["marginBottom20"];
741
+
export const marginTop40: MappedModules["discord/styles/shared/Margins.css"]["marginTop40"];
742
+
export const marginBottom40: MappedModules["discord/styles/shared/Margins.css"]["marginBottom40"];
743
+
export const marginTop60: MappedModules["discord/styles/shared/Margins.css"]["marginTop60"];
744
+
export const marginBottom60: MappedModules["discord/styles/shared/Margins.css"]["marginBottom60"];
745
+
export const marginCenterHorz: MappedModules["discord/styles/shared/Margins.css"]["marginCenterHorz"];
746
+
export const marginLeft8: MappedModules["discord/styles/shared/Margins.css"]["marginLeft8"];
747
+
}
748
+
749
+
declare module "@moonlight-mod/wp/discord/uikit/Flex" {
750
+
import { MappedModules } from "@moonlight-mod/mappings";
751
+
const _default: MappedModules["discord/uikit/Flex"]["default"];
752
+
export default _default;
753
+
}
754
+
755
+
declare module "@moonlight-mod/wp/discord/utils/ClipboardUtils" {
756
+
import { MappedModules } from "@moonlight-mod/mappings";
757
+
export const SUPPORTS_COPY: MappedModules["discord/utils/ClipboardUtils"]["SUPPORTS_COPY"];
758
+
export const copy: MappedModules["discord/utils/ClipboardUtils"]["copy"];
759
+
}
760
+
761
+
declare module "@moonlight-mod/wp/discord/utils/ComponentDispatchUtils" {
762
+
import { MappedModules } from "@moonlight-mod/mappings";
763
+
export const ComponentDispatcher: MappedModules["discord/utils/ComponentDispatchUtils"]["ComponentDispatcher"];
764
+
export const ComponentDispatch: MappedModules["discord/utils/ComponentDispatchUtils"]["ComponentDispatch"];
765
+
}
766
+
767
+
declare module "@moonlight-mod/wp/discord/utils/HTTPUtils" {
768
+
import { MappedModules } from "@moonlight-mod/mappings";
769
+
export const HTTP: MappedModules["discord/utils/HTTPUtils"]["HTTP"];
770
+
}
771
+
772
+
declare module "@moonlight-mod/wp/discord/utils/MaskedLinkUtils" {
773
+
import { MappedModules } from "@moonlight-mod/mappings";
774
+
export const isLinkTrusted: MappedModules["discord/utils/MaskedLinkUtils"]["isLinkTrusted"];
775
+
export const handleClick: MappedModules["discord/utils/MaskedLinkUtils"]["handleClick"];
776
+
}
777
+
778
+
declare module "@moonlight-mod/wp/discord/utils/NativeUtils" {
779
+
import { MappedModules } from "@moonlight-mod/mappings";
780
+
const _default: MappedModules["discord/utils/NativeUtils"]["default"];
781
+
export default _default;
782
+
}
783
+
784
+
declare module "@moonlight-mod/wp/highlight.js" {
785
+
import { MappedModules } from "@moonlight-mod/mappings";
786
+
export const highlight: MappedModules["highlight.js"]["highlight"];
787
+
export const highlightAuto: MappedModules["highlight.js"]["highlightAuto"];
788
+
export const fixMarkup: MappedModules["highlight.js"]["fixMarkup"];
789
+
export const highlightBlock: MappedModules["highlight.js"]["highlightBlock"];
790
+
export const configure: MappedModules["highlight.js"]["configure"];
791
+
export const initHighlighting: MappedModules["highlight.js"]["initHighlighting"];
792
+
export const initHighlightingOnLoad: MappedModules["highlight.js"]["initHighlightingOnLoad"];
793
+
export const registerLanguage: MappedModules["highlight.js"]["registerLanguage"];
794
+
export const listLanguages: MappedModules["highlight.js"]["listLanguages"];
795
+
export const getLanguage: MappedModules["highlight.js"]["getLanguage"];
796
+
export const inherit: MappedModules["highlight.js"]["inherit"];
797
+
export const COMMENT: MappedModules["highlight.js"]["COMMENT"];
798
+
export const IDENT_RE: MappedModules["highlight.js"]["IDENT_RE"];
799
+
export const UNDERSCORE_IDENT_RE: MappedModules["highlight.js"]["UNDERSCORE_IDENT_RE"];
800
+
export const NUMBER_RE: MappedModules["highlight.js"]["NUMBER_RE"];
801
+
export const C_NUMBER_RE: MappedModules["highlight.js"]["C_NUMBER_RE"];
802
+
export const BINARY_NUMBER_RE: MappedModules["highlight.js"]["BINARY_NUMBER_RE"];
803
+
export const RE_STARTERS_RE: MappedModules["highlight.js"]["RE_STARTERS_RE"];
804
+
export const BACKSLASH_ESCAPE: MappedModules["highlight.js"]["BACKSLASH_ESCAPE"];
805
+
export const APOS_STRING_MODE: MappedModules["highlight.js"]["APOS_STRING_MODE"];
806
+
export const QUOTE_STRING_MODE: MappedModules["highlight.js"]["QUOTE_STRING_MODE"];
807
+
export const PHRASAL_WORDS_MODE: MappedModules["highlight.js"]["PHRASAL_WORDS_MODE"];
808
+
export const C_LINE_COMMENT_MODE: MappedModules["highlight.js"]["C_LINE_COMMENT_MODE"];
809
+
export const C_BLOCK_COMMENT_MODE: MappedModules["highlight.js"]["C_BLOCK_COMMENT_MODE"];
810
+
export const HASH_COMMENT_MODE: MappedModules["highlight.js"]["HASH_COMMENT_MODE"];
811
+
export const NUMBER_MODE: MappedModules["highlight.js"]["NUMBER_MODE"];
812
+
export const C_NUMBER_MODE: MappedModules["highlight.js"]["C_NUMBER_MODE"];
813
+
export const BINARY_NUMBER_MODE: MappedModules["highlight.js"]["BINARY_NUMBER_MODE"];
814
+
export const CSS_NUMBER_MODE: MappedModules["highlight.js"]["CSS_NUMBER_MODE"];
815
+
export const REGEX_MODE: MappedModules["highlight.js"]["REGEX_MODE"];
816
+
export const TITLE_MODE: MappedModules["highlight.js"]["TITLE_MODE"];
817
+
export const UNDERSCORE_TITLE_MODE: MappedModules["highlight.js"]["UNDERSCORE_TITLE_MODE"];
818
+
const _default: MappedModules["highlight.js"]["default"];
819
+
export default _default;
820
+
export const HighlightJS: MappedModules["highlight.js"]["HighlightJS"];
821
+
}
822
+
823
+
declare module "@moonlight-mod/wp/highlight.js/lib/core" {
824
+
import { MappedModules } from "@moonlight-mod/mappings";
825
+
export const highlight: MappedModules["highlight.js/lib/core"]["highlight"];
826
+
export const highlightAuto: MappedModules["highlight.js/lib/core"]["highlightAuto"];
827
+
export const fixMarkup: MappedModules["highlight.js/lib/core"]["fixMarkup"];
828
+
export const highlightBlock: MappedModules["highlight.js/lib/core"]["highlightBlock"];
829
+
export const configure: MappedModules["highlight.js/lib/core"]["configure"];
830
+
export const initHighlighting: MappedModules["highlight.js/lib/core"]["initHighlighting"];
831
+
export const initHighlightingOnLoad: MappedModules["highlight.js/lib/core"]["initHighlightingOnLoad"];
832
+
export const registerLanguage: MappedModules["highlight.js/lib/core"]["registerLanguage"];
833
+
export const listLanguages: MappedModules["highlight.js/lib/core"]["listLanguages"];
834
+
export const getLanguage: MappedModules["highlight.js/lib/core"]["getLanguage"];
835
+
export const inherit: MappedModules["highlight.js/lib/core"]["inherit"];
836
+
export const COMMENT: MappedModules["highlight.js/lib/core"]["COMMENT"];
837
+
export const IDENT_RE: MappedModules["highlight.js/lib/core"]["IDENT_RE"];
838
+
export const UNDERSCORE_IDENT_RE: MappedModules["highlight.js/lib/core"]["UNDERSCORE_IDENT_RE"];
839
+
export const NUMBER_RE: MappedModules["highlight.js/lib/core"]["NUMBER_RE"];
840
+
export const C_NUMBER_RE: MappedModules["highlight.js/lib/core"]["C_NUMBER_RE"];
841
+
export const BINARY_NUMBER_RE: MappedModules["highlight.js/lib/core"]["BINARY_NUMBER_RE"];
842
+
export const RE_STARTERS_RE: MappedModules["highlight.js/lib/core"]["RE_STARTERS_RE"];
843
+
export const BACKSLASH_ESCAPE: MappedModules["highlight.js/lib/core"]["BACKSLASH_ESCAPE"];
844
+
export const APOS_STRING_MODE: MappedModules["highlight.js/lib/core"]["APOS_STRING_MODE"];
845
+
export const QUOTE_STRING_MODE: MappedModules["highlight.js/lib/core"]["QUOTE_STRING_MODE"];
846
+
export const PHRASAL_WORDS_MODE: MappedModules["highlight.js/lib/core"]["PHRASAL_WORDS_MODE"];
847
+
export const C_LINE_COMMENT_MODE: MappedModules["highlight.js/lib/core"]["C_LINE_COMMENT_MODE"];
848
+
export const C_BLOCK_COMMENT_MODE: MappedModules["highlight.js/lib/core"]["C_BLOCK_COMMENT_MODE"];
849
+
export const HASH_COMMENT_MODE: MappedModules["highlight.js/lib/core"]["HASH_COMMENT_MODE"];
850
+
export const NUMBER_MODE: MappedModules["highlight.js/lib/core"]["NUMBER_MODE"];
851
+
export const C_NUMBER_MODE: MappedModules["highlight.js/lib/core"]["C_NUMBER_MODE"];
852
+
export const BINARY_NUMBER_MODE: MappedModules["highlight.js/lib/core"]["BINARY_NUMBER_MODE"];
853
+
export const CSS_NUMBER_MODE: MappedModules["highlight.js/lib/core"]["CSS_NUMBER_MODE"];
854
+
export const REGEX_MODE: MappedModules["highlight.js/lib/core"]["REGEX_MODE"];
855
+
export const TITLE_MODE: MappedModules["highlight.js/lib/core"]["TITLE_MODE"];
856
+
export const UNDERSCORE_TITLE_MODE: MappedModules["highlight.js/lib/core"]["UNDERSCORE_TITLE_MODE"];
857
+
}
858
+
859
+
declare module "@moonlight-mod/wp/lodash" {}
860
+
861
+
declare module "@moonlight-mod/wp/murmurhash" {
862
+
import { MappedModules } from "@moonlight-mod/mappings";
863
+
export const v2: MappedModules["murmurhash"]["v2"];
864
+
export const v3: MappedModules["murmurhash"]["v3"];
865
+
}
866
+
867
+
declare module "@moonlight-mod/wp/platform.js" {
868
+
import { MappedModules } from "@moonlight-mod/mappings";
869
+
export const description: MappedModules["platform.js"]["description"];
870
+
export const layout: MappedModules["platform.js"]["layout"];
871
+
export const manufacturer: MappedModules["platform.js"]["manufacturer"];
872
+
export const name: MappedModules["platform.js"]["name"];
873
+
export const prerelease: MappedModules["platform.js"]["prerelease"];
874
+
export const product: MappedModules["platform.js"]["product"];
875
+
export const ua: MappedModules["platform.js"]["ua"];
876
+
export const version: MappedModules["platform.js"]["version"];
877
+
export const os: MappedModules["platform.js"]["os"];
878
+
export const parse: MappedModules["platform.js"]["parse"];
879
+
export const toString: MappedModules["platform.js"]["toString"];
880
+
}
881
+
882
+
declare module "@moonlight-mod/wp/react" {
883
+
import { MappedModules } from "@moonlight-mod/mappings";
884
+
const _: Omit<MappedModules["react"], "__mappings_exportEquals">;
885
+
export = _;
886
+
}
887
+
888
+
declare module "@moonlight-mod/wp/uuid/v4" {}
+7
-8
packages/types/tsconfig.json
+7
-8
packages/types/tsconfig.json
···
1
1
{
2
2
"compilerOptions": {
3
-
"target": "es2016",
4
-
"module": "es6",
3
+
"target": "ES2016",
4
+
"jsx": "react",
5
+
"module": "ES6",
6
+
"moduleResolution": "bundler",
7
+
"strict": true,
8
+
"declaration": true,
5
9
"esModuleInterop": true,
6
-
"forceConsistentCasingInFileNames": true,
7
-
"strict": true,
8
-
"skipLibCheck": true,
9
-
"moduleResolution": "bundler",
10
-
"jsx": "react",
11
-
"declaration": true
10
+
"forceConsistentCasingInFileNames": true
12
11
},
13
12
"include": ["./src/**/*", "src/index.ts", "./src/import.d.ts"]
14
13
}
+13
-1
packages/web-preload/package.json
+13
-1
packages/web-preload/package.json
···
1
1
{
2
2
"name": "@moonlight-mod/web-preload",
3
3
"private": true,
4
+
"main": "src/index.ts",
5
+
"engineStrict": true,
6
+
"engines": {
7
+
"node": ">=22",
8
+
"pnpm": ">=10",
9
+
"npm": "pnpm",
10
+
"yarn": "pnpm"
11
+
},
4
12
"dependencies": {
5
-
"@moonlight-mod/core": "workspace:*"
13
+
"@moonlight-mod/core": "workspace:*",
14
+
"@moonlight-mod/lunast": "catalog:prod",
15
+
"@moonlight-mod/mappings": "catalog:prod",
16
+
"@moonlight-mod/moonmap": "catalog:prod",
17
+
"@moonlight-mod/types": "workspace:*"
6
18
}
7
19
}
+41
-8
packages/web-preload/src/index.ts
+41
-8
packages/web-preload/src/index.ts
···
1
1
import { loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
2
-
import { installWebpackPatcher } from "@moonlight-mod/core/patch";
2
+
import { installWebpackPatcher, onModuleLoad, registerPatch, registerWebpackModule } from "@moonlight-mod/core/patch";
3
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
3
4
import { installStyles } from "@moonlight-mod/core/styles";
4
-
import Logger from "@moonlight-mod/core/util/logger";
5
+
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
6
+
import LunAST from "@moonlight-mod/lunast";
7
+
import Moonmap from "@moonlight-mod/moonmap";
8
+
import loadMappings from "@moonlight-mod/mappings";
9
+
import { createEventEmitter } from "@moonlight-mod/core/util/event";
10
+
import { WebEventPayloads, WebEventType } from "@moonlight-mod/types/core/event";
5
11
6
-
(async () => {
12
+
async function load() {
13
+
delete window._moonlightWebLoad;
14
+
initLogger(moonlightNode.config);
7
15
const logger = new Logger("web-preload");
8
16
9
17
window.moonlight = {
18
+
patched: new Map(),
10
19
unpatched: new Set(),
20
+
pendingModules: new Set(),
11
21
enabledExtensions: new Set(),
12
22
23
+
events: createEventEmitter<WebEventType, WebEventPayloads>(),
24
+
patchingInternals: {
25
+
onModuleLoad,
26
+
registerPatch,
27
+
registerWebpackModule
28
+
},
29
+
localStorage: window.localStorage,
30
+
31
+
version: MOONLIGHT_VERSION,
32
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
33
+
apiLevel: constants.apiLevel,
34
+
13
35
getConfig: moonlightNode.getConfig.bind(moonlightNode),
14
36
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
37
+
setConfigOption: moonlightNode.setConfigOption.bind(moonlightNode),
38
+
writeConfig: moonlightNode.writeConfig.bind(moonlightNode),
39
+
15
40
getNatives: moonlightNode.getNatives.bind(moonlightNode),
16
-
getLogger: (id: string) => {
41
+
getLogger(id) {
17
42
return new Logger(id);
18
-
}
43
+
},
44
+
45
+
lunast: new LunAST(),
46
+
moonmap: new Moonmap()
19
47
};
20
48
21
49
try {
50
+
loadMappings(window.moonlight.moonmap, window.moonlight.lunast);
22
51
await loadProcessedExtensions(moonlightNode.processedExtensions);
23
52
await installWebpackPatcher();
24
53
} catch (e) {
25
54
logger.error("Error setting up web-preload", e);
26
55
}
27
56
28
-
window.addEventListener("DOMContentLoaded", () => {
57
+
if (document.readyState === "complete") {
29
58
installStyles();
30
-
});
31
-
})();
59
+
} else {
60
+
window.addEventListener("load", installStyles);
61
+
}
62
+
}
63
+
64
+
window._moonlightWebLoad = load;
+4
-1
packages/web-preload/tsconfig.json
+4
-1
packages/web-preload/tsconfig.json
+2446
-1622
pnpm-lock.yaml
+2446
-1622
pnpm-lock.yaml
···
1
-
lockfileVersion: '6.0'
1
+
lockfileVersion: '9.0'
2
2
3
3
settings:
4
4
autoInstallPeers: true
5
5
excludeLinksFromLockfile: false
6
6
7
+
catalogs:
8
+
dev:
9
+
'@moonlight-mod/eslint-config':
10
+
specifier: github:moonlight-mod/eslint-config
11
+
version: 1.0.1
12
+
'@types/chrome':
13
+
specifier: ^0.0.313
14
+
version: 0.0.313
15
+
'@types/node':
16
+
specifier: ^22.14.0
17
+
version: 22.14.0
18
+
esbuild:
19
+
specifier: ^0.19.3
20
+
version: 0.19.3
21
+
esbuild-copy-static-files:
22
+
specifier: ^0.1.0
23
+
version: 0.1.0
24
+
eslint:
25
+
specifier: ^9.12.0
26
+
version: 9.23.0
27
+
husky:
28
+
specifier: ^8.0.3
29
+
version: 8.0.3
30
+
prettier:
31
+
specifier: ^3.1.0
32
+
version: 3.1.0
33
+
taze:
34
+
specifier: ^19.0.4
35
+
version: 19.0.4
36
+
typescript:
37
+
specifier: ^5.3.3
38
+
version: 5.8.2
39
+
prod:
40
+
'@moonlight-mod/lunast':
41
+
specifier: ^1.0.1
42
+
version: 1.0.1
43
+
'@moonlight-mod/mappings':
44
+
specifier: ^1.1.25
45
+
version: 1.1.25
46
+
'@moonlight-mod/moonmap':
47
+
specifier: ^1.0.5
48
+
version: 1.0.5
49
+
'@zenfs/core':
50
+
specifier: ^2.0.0
51
+
version: 2.0.0
52
+
'@zenfs/dom':
53
+
specifier: ^1.1.3
54
+
version: 1.1.6
55
+
microdiff:
56
+
specifier: ^1.5.0
57
+
version: 1.5.0
58
+
nanotar:
59
+
specifier: ^0.1.1
60
+
version: 0.1.1
61
+
7
62
importers:
8
63
9
64
.:
10
65
devDependencies:
11
-
'@typescript-eslint/eslint-plugin':
12
-
specifier: ^6.13.2
13
-
version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.2)
14
-
'@typescript-eslint/parser':
15
-
specifier: ^6.13.2
16
-
version: 6.13.2(eslint@8.55.0)(typescript@5.3.2)
66
+
'@moonlight-mod/eslint-config':
67
+
specifier: catalog:dev
68
+
version: https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9(@types/eslint@9.6.1)(eslint@9.23.0(jiti@2.4.2))(prettier@3.1.0)(typescript@5.8.2)
69
+
'@types/node':
70
+
specifier: catalog:dev
71
+
version: 22.14.0
17
72
esbuild:
18
-
specifier: ^0.19.3
73
+
specifier: catalog:dev
19
74
version: 0.19.3
20
75
esbuild-copy-static-files:
21
-
specifier: ^0.1.0
76
+
specifier: catalog:dev
22
77
version: 0.1.0
23
78
eslint:
24
-
specifier: ^8.55.0
25
-
version: 8.55.0
26
-
eslint-config-prettier:
27
-
specifier: ^9.1.0
28
-
version: 9.1.0(eslint@8.55.0)
29
-
eslint-plugin-prettier:
30
-
specifier: ^5.0.1
31
-
version: 5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.0)
32
-
eslint-plugin-react:
33
-
specifier: ^7.33.2
34
-
version: 7.33.2(eslint@8.55.0)
79
+
specifier: catalog:dev
80
+
version: 9.23.0(jiti@2.4.2)
35
81
husky:
36
-
specifier: ^8.0.3
82
+
specifier: catalog:dev
37
83
version: 8.0.3
38
84
prettier:
39
-
specifier: ^3.1.0
85
+
specifier: catalog:dev
40
86
version: 3.1.0
87
+
taze:
88
+
specifier: catalog:dev
89
+
version: 19.0.4
41
90
typescript:
42
-
specifier: ^5.3.2
43
-
version: 5.3.2
91
+
specifier: catalog:dev
92
+
version: 5.8.2
93
+
94
+
packages/browser:
95
+
dependencies:
96
+
'@moonlight-mod/core':
97
+
specifier: workspace:*
98
+
version: link:../core
99
+
'@moonlight-mod/types':
100
+
specifier: workspace:*
101
+
version: link:../types
102
+
'@moonlight-mod/web-preload':
103
+
specifier: workspace:*
104
+
version: link:../web-preload
105
+
'@zenfs/core':
106
+
specifier: catalog:prod
107
+
version: 2.0.0
108
+
'@zenfs/dom':
109
+
specifier: catalog:prod
110
+
version: 1.1.6(@zenfs/core@2.0.0)(utilium@1.10.1)
111
+
devDependencies:
112
+
'@types/chrome':
113
+
specifier: catalog:dev
114
+
version: 0.0.313
44
115
45
116
packages/core:
46
117
dependencies:
···
50
121
51
122
packages/core-extensions:
52
123
dependencies:
53
-
'@electron/asar':
54
-
specifier: ^3.2.5
55
-
version: 3.2.5
124
+
'@moonlight-mod/core':
125
+
specifier: workspace:*
126
+
version: link:../core
56
127
'@moonlight-mod/types':
57
128
specifier: workspace:*
58
129
version: link:../types
130
+
microdiff:
131
+
specifier: catalog:prod
132
+
version: 1.5.0
133
+
nanotar:
134
+
specifier: catalog:prod
135
+
version: 0.1.1
59
136
60
137
packages/injector:
61
138
dependencies:
···
77
154
78
155
packages/types:
79
156
dependencies:
80
-
'@types/flux':
81
-
specifier: ^3.1.12
82
-
version: 3.1.12
83
-
'@types/node':
84
-
specifier: ^20.6.2
85
-
version: 20.6.2
157
+
'@moonlight-mod/lunast':
158
+
specifier: ^1.0.1
159
+
version: 1.0.1
160
+
'@moonlight-mod/mappings':
161
+
specifier: ^1.1.25
162
+
version: 1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)
163
+
'@moonlight-mod/moonmap':
164
+
specifier: ^1.0.5
165
+
version: 1.0.5
86
166
'@types/react':
87
-
specifier: ^18.2.22
88
-
version: 18.2.22
167
+
specifier: ^18.3.10
168
+
version: 18.3.20
89
169
csstype:
90
-
specifier: ^3.1.2
91
-
version: 3.1.2
170
+
specifier: ^3.1.3
171
+
version: 3.1.3
92
172
standalone-electron-types:
93
173
specifier: ^1.0.0
94
174
version: 1.0.0
···
98
178
'@moonlight-mod/core':
99
179
specifier: workspace:*
100
180
version: link:../core
181
+
'@moonlight-mod/lunast':
182
+
specifier: catalog:prod
183
+
version: 1.0.1
184
+
'@moonlight-mod/mappings':
185
+
specifier: catalog:prod
186
+
version: 1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)
187
+
'@moonlight-mod/moonmap':
188
+
specifier: catalog:prod
189
+
version: 1.0.5
190
+
'@moonlight-mod/types':
191
+
specifier: workspace:*
192
+
version: link:../types
101
193
102
194
packages:
103
195
104
-
/@aashutoshrathi/word-wrap@1.2.6:
196
+
'@aashutoshrathi/word-wrap@1.2.6':
105
197
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
106
198
engines: {node: '>=0.10.0'}
107
-
dev: true
108
199
109
-
/@electron/asar@3.2.5:
110
-
resolution: {integrity: sha512-Ypahc2ElTj9YOrFvUHuoXv5Z/V1nPA5enlhmQapc578m/HZBHKTbqhoL5JZQjje2+/6Ti5AHh7Gj1/haeJa63Q==}
111
-
engines: {node: '>=10.12.0'}
200
+
'@antfu/ni@24.3.0':
201
+
resolution: {integrity: sha512-wBSav4mBxvHEW9RbdSo1SWLQ6MAlT0Dc423weC58yOWqW4OcMvtnNDdDrxOZeJ88fEIyPK93gDUWIelBxzSf8g==}
112
202
hasBin: true
113
-
dependencies:
114
-
commander: 5.1.0
115
-
glob: 7.2.3
116
-
minimatch: 3.1.2
117
-
dev: false
118
203
119
-
/@esbuild/android-arm64@0.19.3:
204
+
'@esbuild/android-arm64@0.19.3':
120
205
resolution: {integrity: sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==}
121
206
engines: {node: '>=12'}
122
207
cpu: [arm64]
123
208
os: [android]
124
-
requiresBuild: true
125
-
dev: true
126
-
optional: true
127
209
128
-
/@esbuild/android-arm@0.19.3:
210
+
'@esbuild/android-arm@0.19.3':
129
211
resolution: {integrity: sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==}
130
212
engines: {node: '>=12'}
131
213
cpu: [arm]
132
214
os: [android]
133
-
requiresBuild: true
134
-
dev: true
135
-
optional: true
136
215
137
-
/@esbuild/android-x64@0.19.3:
216
+
'@esbuild/android-x64@0.19.3':
138
217
resolution: {integrity: sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==}
139
218
engines: {node: '>=12'}
140
219
cpu: [x64]
141
220
os: [android]
142
-
requiresBuild: true
143
-
dev: true
144
-
optional: true
145
221
146
-
/@esbuild/darwin-arm64@0.19.3:
222
+
'@esbuild/darwin-arm64@0.19.3':
147
223
resolution: {integrity: sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==}
148
224
engines: {node: '>=12'}
149
225
cpu: [arm64]
150
226
os: [darwin]
151
-
requiresBuild: true
152
-
dev: true
153
-
optional: true
154
227
155
-
/@esbuild/darwin-x64@0.19.3:
228
+
'@esbuild/darwin-x64@0.19.3':
156
229
resolution: {integrity: sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==}
157
230
engines: {node: '>=12'}
158
231
cpu: [x64]
159
232
os: [darwin]
160
-
requiresBuild: true
161
-
dev: true
162
-
optional: true
163
233
164
-
/@esbuild/freebsd-arm64@0.19.3:
234
+
'@esbuild/freebsd-arm64@0.19.3':
165
235
resolution: {integrity: sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==}
166
236
engines: {node: '>=12'}
167
237
cpu: [arm64]
168
238
os: [freebsd]
169
-
requiresBuild: true
170
-
dev: true
171
-
optional: true
172
239
173
-
/@esbuild/freebsd-x64@0.19.3:
240
+
'@esbuild/freebsd-x64@0.19.3':
174
241
resolution: {integrity: sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==}
175
242
engines: {node: '>=12'}
176
243
cpu: [x64]
177
244
os: [freebsd]
178
-
requiresBuild: true
179
-
dev: true
180
-
optional: true
181
245
182
-
/@esbuild/linux-arm64@0.19.3:
246
+
'@esbuild/linux-arm64@0.19.3':
183
247
resolution: {integrity: sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==}
184
248
engines: {node: '>=12'}
185
249
cpu: [arm64]
186
250
os: [linux]
187
-
requiresBuild: true
188
-
dev: true
189
-
optional: true
190
251
191
-
/@esbuild/linux-arm@0.19.3:
252
+
'@esbuild/linux-arm@0.19.3':
192
253
resolution: {integrity: sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==}
193
254
engines: {node: '>=12'}
194
255
cpu: [arm]
195
256
os: [linux]
196
-
requiresBuild: true
197
-
dev: true
198
-
optional: true
199
257
200
-
/@esbuild/linux-ia32@0.19.3:
258
+
'@esbuild/linux-ia32@0.19.3':
201
259
resolution: {integrity: sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==}
202
260
engines: {node: '>=12'}
203
261
cpu: [ia32]
204
262
os: [linux]
205
-
requiresBuild: true
206
-
dev: true
207
-
optional: true
208
263
209
-
/@esbuild/linux-loong64@0.19.3:
264
+
'@esbuild/linux-loong64@0.19.3':
210
265
resolution: {integrity: sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==}
211
266
engines: {node: '>=12'}
212
267
cpu: [loong64]
213
268
os: [linux]
214
-
requiresBuild: true
215
-
dev: true
216
-
optional: true
217
269
218
-
/@esbuild/linux-mips64el@0.19.3:
270
+
'@esbuild/linux-mips64el@0.19.3':
219
271
resolution: {integrity: sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==}
220
272
engines: {node: '>=12'}
221
273
cpu: [mips64el]
222
274
os: [linux]
223
-
requiresBuild: true
224
-
dev: true
225
-
optional: true
226
275
227
-
/@esbuild/linux-ppc64@0.19.3:
276
+
'@esbuild/linux-ppc64@0.19.3':
228
277
resolution: {integrity: sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==}
229
278
engines: {node: '>=12'}
230
279
cpu: [ppc64]
231
280
os: [linux]
232
-
requiresBuild: true
233
-
dev: true
234
-
optional: true
235
281
236
-
/@esbuild/linux-riscv64@0.19.3:
282
+
'@esbuild/linux-riscv64@0.19.3':
237
283
resolution: {integrity: sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==}
238
284
engines: {node: '>=12'}
239
285
cpu: [riscv64]
240
286
os: [linux]
241
-
requiresBuild: true
242
-
dev: true
243
-
optional: true
244
287
245
-
/@esbuild/linux-s390x@0.19.3:
288
+
'@esbuild/linux-s390x@0.19.3':
246
289
resolution: {integrity: sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA==}
247
290
engines: {node: '>=12'}
248
291
cpu: [s390x]
249
292
os: [linux]
250
-
requiresBuild: true
251
-
dev: true
252
-
optional: true
253
293
254
-
/@esbuild/linux-x64@0.19.3:
294
+
'@esbuild/linux-x64@0.19.3':
255
295
resolution: {integrity: sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==}
256
296
engines: {node: '>=12'}
257
297
cpu: [x64]
258
298
os: [linux]
259
-
requiresBuild: true
260
-
dev: true
261
-
optional: true
262
299
263
-
/@esbuild/netbsd-x64@0.19.3:
300
+
'@esbuild/netbsd-x64@0.19.3':
264
301
resolution: {integrity: sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==}
265
302
engines: {node: '>=12'}
266
303
cpu: [x64]
267
304
os: [netbsd]
268
-
requiresBuild: true
269
-
dev: true
270
-
optional: true
271
305
272
-
/@esbuild/openbsd-x64@0.19.3:
306
+
'@esbuild/openbsd-x64@0.19.3':
273
307
resolution: {integrity: sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==}
274
308
engines: {node: '>=12'}
275
309
cpu: [x64]
276
310
os: [openbsd]
277
-
requiresBuild: true
278
-
dev: true
279
-
optional: true
280
311
281
-
/@esbuild/sunos-x64@0.19.3:
312
+
'@esbuild/sunos-x64@0.19.3':
282
313
resolution: {integrity: sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==}
283
314
engines: {node: '>=12'}
284
315
cpu: [x64]
285
316
os: [sunos]
286
-
requiresBuild: true
287
-
dev: true
288
-
optional: true
289
317
290
-
/@esbuild/win32-arm64@0.19.3:
318
+
'@esbuild/win32-arm64@0.19.3':
291
319
resolution: {integrity: sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==}
292
320
engines: {node: '>=12'}
293
321
cpu: [arm64]
294
322
os: [win32]
295
-
requiresBuild: true
296
-
dev: true
297
-
optional: true
298
323
299
-
/@esbuild/win32-ia32@0.19.3:
324
+
'@esbuild/win32-ia32@0.19.3':
300
325
resolution: {integrity: sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==}
301
326
engines: {node: '>=12'}
302
327
cpu: [ia32]
303
328
os: [win32]
304
-
requiresBuild: true
305
-
dev: true
306
-
optional: true
307
329
308
-
/@esbuild/win32-x64@0.19.3:
330
+
'@esbuild/win32-x64@0.19.3':
309
331
resolution: {integrity: sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==}
310
332
engines: {node: '>=12'}
311
333
cpu: [x64]
312
334
os: [win32]
313
-
requiresBuild: true
314
-
dev: true
315
-
optional: true
316
335
317
-
/@eslint-community/eslint-utils@4.4.0(eslint@8.55.0):
318
-
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
336
+
'@eslint-community/eslint-utils@4.5.1':
337
+
resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==}
319
338
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
320
339
peerDependencies:
321
340
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
322
-
dependencies:
323
-
eslint: 8.55.0
324
-
eslint-visitor-keys: 3.4.3
325
-
dev: true
326
341
327
-
/@eslint-community/regexpp@4.10.0:
328
-
resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
342
+
'@eslint-community/regexpp@4.12.1':
343
+
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
329
344
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
330
-
dev: true
345
+
346
+
'@eslint/config-array@0.19.2':
347
+
resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==}
348
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
349
+
350
+
'@eslint/config-helpers@0.2.1':
351
+
resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==}
352
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
353
+
354
+
'@eslint/core@0.12.0':
355
+
resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==}
356
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
357
+
358
+
'@eslint/core@0.13.0':
359
+
resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
360
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
361
+
362
+
'@eslint/eslintrc@3.3.1':
363
+
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
364
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
365
+
366
+
'@eslint/js@9.23.0':
367
+
resolution: {integrity: sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==}
368
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
369
+
370
+
'@eslint/object-schema@2.1.6':
371
+
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
372
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
373
+
374
+
'@eslint/plugin-kit@0.2.8':
375
+
resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
376
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
377
+
378
+
'@humanfs/core@0.19.1':
379
+
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
380
+
engines: {node: '>=18.18.0'}
381
+
382
+
'@humanfs/node@0.16.6':
383
+
resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
384
+
engines: {node: '>=18.18.0'}
385
+
386
+
'@humanwhocodes/module-importer@1.0.1':
387
+
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
388
+
engines: {node: '>=12.22'}
389
+
390
+
'@humanwhocodes/retry@0.3.1':
391
+
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
392
+
engines: {node: '>=18.18'}
393
+
394
+
'@humanwhocodes/retry@0.4.2':
395
+
resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
396
+
engines: {node: '>=18.18'}
397
+
398
+
'@moonlight-mod/eslint-config@https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9':
399
+
resolution: {tarball: https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9}
400
+
version: 1.0.1
401
+
peerDependencies:
402
+
eslint: '>= 9'
403
+
typescript: '>= 5.3'
404
+
405
+
'@moonlight-mod/lunast@1.0.1':
406
+
resolution: {integrity: sha512-K3vxzDlfFuYKjciIW2FMlcZ1qrrkAGDGpSBlNqYGtJ0sMt9bRCd2lpSpg6AX/giSljDtmAUXa/5mOfUoDQxjBA==}
407
+
408
+
'@moonlight-mod/mappings@1.1.25':
409
+
resolution: {integrity: sha512-bgnSN9H/IBdMGxGev6RQKXuzhQxwo1090NhIDHnflguZnjiu2pg/usPfh76bqyhxRuX4SS7tiZSNTwBoSflCLg==}
410
+
engines: {node: '>=22', npm: pnpm, pnpm: '>=10', yarn: pnpm}
411
+
peerDependencies:
412
+
'@moonlight-mod/lunast': ^1.0.1
413
+
'@moonlight-mod/moonmap': ^1.0.5
414
+
415
+
'@moonlight-mod/moonmap@1.0.5':
416
+
resolution: {integrity: sha512-Fdpxj8ghdulKB6TlTnchlCPey2YUKgEf1chuO1ofOIcvlqnVPBcQwSf2S80naOUQpXCDo4dQ+LWSE2fmhdDiiw==}
417
+
418
+
'@nodelib/fs.scandir@2.1.5':
419
+
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
420
+
engines: {node: '>= 8'}
421
+
422
+
'@nodelib/fs.stat@2.0.5':
423
+
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
424
+
engines: {node: '>= 8'}
425
+
426
+
'@nodelib/fs.walk@1.2.8':
427
+
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
428
+
engines: {node: '>= 8'}
429
+
430
+
'@pkgr/core@0.2.0':
431
+
resolution: {integrity: sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==}
432
+
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
433
+
434
+
'@quansync/fs@0.1.2':
435
+
resolution: {integrity: sha512-ezIadUb1aFhwJLd++WVqVpi9rnlX8vnd4ju7saPhwLHJN1mJgOv0puePTGV+FbtSnWtwoHDT8lAm4kagDZmpCg==}
436
+
engines: {node: '>=20.0.0'}
437
+
438
+
'@types/chroma-js@3.1.0':
439
+
resolution: {integrity: sha512-Uwl3SOtUkbQ6Ye6ZYu4q4xdLGBzmY839sEHYtOT7i691neeyd+7fXWT5VIkcUSfNwIFrIjQutNYQn9h4q5HFvg==}
440
+
441
+
'@types/chrome@0.0.313':
442
+
resolution: {integrity: sha512-9R5T7gTaYZhkxlu+Ho4wk9FL+y/werWQY2yjGWSqCuiTsqS7nL/BE5UMTP6rU7J+oIG2FRKqrEycHhJATeltVA==}
443
+
444
+
'@types/eslint@9.6.1':
445
+
resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
446
+
447
+
'@types/estree-jsx@1.0.5':
448
+
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
449
+
450
+
'@types/estree@1.0.6':
451
+
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
452
+
453
+
'@types/estree@1.0.7':
454
+
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
455
+
456
+
'@types/fbemitter@2.0.35':
457
+
resolution: {integrity: sha512-Xem6d7qUfmouCHntCrRYgDBwbf+WWRd6G+7WEFlEZFZ67LZXiYRvT2LV8wcZa6mIaAil95+ABQdKgB6hPIsnng==}
458
+
459
+
'@types/filesystem@0.0.36':
460
+
resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==}
461
+
462
+
'@types/filewriter@0.0.33':
463
+
resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==}
464
+
465
+
'@types/flux@3.1.14':
466
+
resolution: {integrity: sha512-WRXN0kQPCnqxN0/PgNgc7WBF6c8rbSHsEep3/qBLpsQ824RONdOmTs0TV7XhIW2GDNRAHO2CqCgAFLR5PChosw==}
467
+
468
+
'@types/har-format@1.2.16':
469
+
resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==}
470
+
471
+
'@types/highlightjs@9.12.6':
472
+
resolution: {integrity: sha512-Qfd1DUrwE851Hc3tExADJY4qY8yeZMt06Xw9AJm/UtpneepJS3MZY29c33BY0wP899veaaHD4gZzYiSuQm84Fg==}
473
+
474
+
'@types/json-schema@7.0.15':
475
+
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
476
+
477
+
'@types/lodash@4.17.14':
478
+
resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==}
479
+
480
+
'@types/node@18.17.17':
481
+
resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==}
482
+
483
+
'@types/node@22.13.6':
484
+
resolution: {integrity: sha512-GYmF65GI7417CpZXsEXMjT8goQQDnpRnJnDw6jIYa+le3V/lMazPZ4vZmK1B/9R17fh2VLr2zuy9d/h5xgrLAg==}
485
+
486
+
'@types/node@22.14.0':
487
+
resolution: {integrity: sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==}
488
+
489
+
'@types/platform@1.3.6':
490
+
resolution: {integrity: sha512-ZmSaqHuvzv+jC232cFoz2QqPUkaj6EvMmCrWcx3WRr7xTPVFCMUOTcOq8m2d+Zw1iKRc1kDiaA+jtNrV0hkVew==}
491
+
492
+
'@types/prop-types@15.7.13':
493
+
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
494
+
495
+
'@types/react@18.3.20':
496
+
resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==}
497
+
498
+
'@typescript-eslint/eslint-plugin@8.29.0':
499
+
resolution: {integrity: sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==}
500
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
501
+
peerDependencies:
502
+
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
503
+
eslint: ^8.57.0 || ^9.0.0
504
+
typescript: '>=4.8.4 <5.9.0'
505
+
506
+
'@typescript-eslint/parser@8.29.0':
507
+
resolution: {integrity: sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==}
508
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
509
+
peerDependencies:
510
+
eslint: ^8.57.0 || ^9.0.0
511
+
typescript: '>=4.8.4 <5.9.0'
512
+
513
+
'@typescript-eslint/scope-manager@8.29.0':
514
+
resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==}
515
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
516
+
517
+
'@typescript-eslint/type-utils@8.29.0':
518
+
resolution: {integrity: sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==}
519
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
520
+
peerDependencies:
521
+
eslint: ^8.57.0 || ^9.0.0
522
+
typescript: '>=4.8.4 <5.9.0'
523
+
524
+
'@typescript-eslint/types@8.29.0':
525
+
resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==}
526
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
527
+
528
+
'@typescript-eslint/typescript-estree@8.29.0':
529
+
resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==}
530
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
531
+
peerDependencies:
532
+
typescript: '>=4.8.4 <5.9.0'
533
+
534
+
'@typescript-eslint/utils@8.29.0':
535
+
resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==}
536
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
537
+
peerDependencies:
538
+
eslint: ^8.57.0 || ^9.0.0
539
+
typescript: '>=4.8.4 <5.9.0'
540
+
541
+
'@typescript-eslint/visitor-keys@8.29.0':
542
+
resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==}
543
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
544
+
545
+
'@xterm/xterm@5.5.0':
546
+
resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==}
547
+
548
+
'@zenfs/core@2.0.0':
549
+
resolution: {integrity: sha512-wOKNFTY1DJ1vdLqKdU7M8cRh0nVYZcDVu7WHuk/3u49hrSwTZVm4PzGxJUjFd8O9Wi3U5nYTbZoN7RX5mS2ldA==}
550
+
engines: {node: '>= 18'}
551
+
hasBin: true
552
+
553
+
'@zenfs/dom@1.1.6':
554
+
resolution: {integrity: sha512-7SBTWgA0esuEv/TE+N/xk6W/XJf8uBF+LhlPNHQdXds0H7aOy/UYsWv/8glvARe+meDMMidoeWFLzUWoMXfjlA==}
555
+
engines: {node: '>= 18'}
556
+
peerDependencies:
557
+
'@zenfs/core': ^2.0.0
558
+
utilium: ^1.9.0
559
+
560
+
abort-controller@3.0.0:
561
+
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
562
+
engines: {node: '>=6.5'}
563
+
564
+
acorn-jsx@5.3.2:
565
+
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
566
+
peerDependencies:
567
+
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
568
+
569
+
acorn@8.14.1:
570
+
resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
571
+
engines: {node: '>=0.4.0'}
572
+
hasBin: true
573
+
574
+
ajv@6.12.6:
575
+
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
576
+
577
+
ansi-styles@4.3.0:
578
+
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
579
+
engines: {node: '>=8'}
580
+
581
+
ansis@3.17.0:
582
+
resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==}
583
+
engines: {node: '>=14'}
584
+
585
+
argparse@2.0.1:
586
+
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
587
+
588
+
array-buffer-byte-length@1.0.2:
589
+
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
590
+
engines: {node: '>= 0.4'}
591
+
592
+
array-includes@3.1.8:
593
+
resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
594
+
engines: {node: '>= 0.4'}
595
+
596
+
array.prototype.findlast@1.2.5:
597
+
resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
598
+
engines: {node: '>= 0.4'}
599
+
600
+
array.prototype.flat@1.3.3:
601
+
resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
602
+
engines: {node: '>= 0.4'}
603
+
604
+
array.prototype.flatmap@1.3.3:
605
+
resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
606
+
engines: {node: '>= 0.4'}
607
+
608
+
array.prototype.tosorted@1.1.4:
609
+
resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
610
+
engines: {node: '>= 0.4'}
611
+
612
+
arraybuffer.prototype.slice@1.0.4:
613
+
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
614
+
engines: {node: '>= 0.4'}
615
+
616
+
astring@1.9.0:
617
+
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
618
+
hasBin: true
619
+
620
+
async-function@1.0.0:
621
+
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
622
+
engines: {node: '>= 0.4'}
623
+
624
+
available-typed-arrays@1.0.7:
625
+
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
626
+
engines: {node: '>= 0.4'}
627
+
628
+
balanced-match@1.0.2:
629
+
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
630
+
631
+
base64-js@1.5.1:
632
+
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
633
+
634
+
brace-expansion@1.1.11:
635
+
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
636
+
637
+
brace-expansion@2.0.1:
638
+
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
639
+
640
+
braces@3.0.3:
641
+
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
642
+
engines: {node: '>=8'}
643
+
644
+
buffer@6.0.3:
645
+
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
646
+
647
+
cac@6.7.14:
648
+
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
649
+
engines: {node: '>=8'}
650
+
651
+
call-bind-apply-helpers@1.0.2:
652
+
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
653
+
engines: {node: '>= 0.4'}
654
+
655
+
call-bind@1.0.8:
656
+
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
657
+
engines: {node: '>= 0.4'}
658
+
659
+
call-bound@1.0.4:
660
+
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
661
+
engines: {node: '>= 0.4'}
662
+
663
+
callsites@3.1.0:
664
+
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
665
+
engines: {node: '>=6'}
666
+
667
+
chalk@4.1.2:
668
+
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
669
+
engines: {node: '>=10'}
670
+
671
+
color-convert@2.0.1:
672
+
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
673
+
engines: {node: '>=7.0.0'}
674
+
675
+
color-name@1.1.4:
676
+
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
331
677
332
-
/@eslint/eslintrc@2.1.4:
333
-
resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
678
+
concat-map@0.0.1:
679
+
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
680
+
681
+
cross-spawn@7.0.6:
682
+
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
683
+
engines: {node: '>= 8'}
684
+
685
+
csstype@3.1.3:
686
+
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
687
+
688
+
data-view-buffer@1.0.2:
689
+
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
690
+
engines: {node: '>= 0.4'}
691
+
692
+
data-view-byte-length@1.0.2:
693
+
resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
694
+
engines: {node: '>= 0.4'}
695
+
696
+
data-view-byte-offset@1.0.1:
697
+
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
698
+
engines: {node: '>= 0.4'}
699
+
700
+
debug@4.4.0:
701
+
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
702
+
engines: {node: '>=6.0'}
703
+
peerDependencies:
704
+
supports-color: '*'
705
+
peerDependenciesMeta:
706
+
supports-color:
707
+
optional: true
708
+
709
+
deep-is@0.1.4:
710
+
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
711
+
712
+
define-data-property@1.1.4:
713
+
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
714
+
engines: {node: '>= 0.4'}
715
+
716
+
define-properties@1.2.1:
717
+
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
718
+
engines: {node: '>= 0.4'}
719
+
720
+
defu@6.1.4:
721
+
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
722
+
723
+
destr@2.0.4:
724
+
resolution: {integrity: sha512-FCAorltMy7QwX0QU38jOkhrv20LBpsHA8ogzvMhhPHCCKVCaN6GxrB0GGaWEWBUYI4eEjjfJ95RdP6dk9IdMQA==}
725
+
726
+
doctrine@2.1.0:
727
+
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
728
+
engines: {node: '>=0.10.0'}
729
+
730
+
dunder-proto@1.0.1:
731
+
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
732
+
engines: {node: '>= 0.4'}
733
+
734
+
es-abstract@1.23.9:
735
+
resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
736
+
engines: {node: '>= 0.4'}
737
+
738
+
es-define-property@1.0.1:
739
+
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
740
+
engines: {node: '>= 0.4'}
741
+
742
+
es-errors@1.3.0:
743
+
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
744
+
engines: {node: '>= 0.4'}
745
+
746
+
es-iterator-helpers@1.2.1:
747
+
resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
748
+
engines: {node: '>= 0.4'}
749
+
750
+
es-object-atoms@1.1.1:
751
+
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
752
+
engines: {node: '>= 0.4'}
753
+
754
+
es-set-tostringtag@2.1.0:
755
+
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
756
+
engines: {node: '>= 0.4'}
757
+
758
+
es-shim-unscopables@1.1.0:
759
+
resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
760
+
engines: {node: '>= 0.4'}
761
+
762
+
es-to-primitive@1.3.0:
763
+
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
764
+
engines: {node: '>= 0.4'}
765
+
766
+
esbuild-copy-static-files@0.1.0:
767
+
resolution: {integrity: sha512-KlpmYqANA1t2nZavEdItfcOjJC6wbHA21v35HJWN32DddGTWKNNGDKljUzbCPojmpD+wAw8/DXr5abJ4jFCE0w==}
768
+
769
+
esbuild@0.19.3:
770
+
resolution: {integrity: sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==}
771
+
engines: {node: '>=12'}
772
+
hasBin: true
773
+
774
+
escape-string-regexp@4.0.0:
775
+
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
776
+
engines: {node: '>=10'}
777
+
778
+
eslint-config-prettier@9.1.0:
779
+
resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
780
+
hasBin: true
781
+
peerDependencies:
782
+
eslint: '>=7.0.0'
783
+
784
+
eslint-plugin-prettier@5.2.6:
785
+
resolution: {integrity: sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==}
786
+
engines: {node: ^14.18.0 || >=16.0.0}
787
+
peerDependencies:
788
+
'@types/eslint': '>=8.0.0'
789
+
eslint: '>=8.0.0'
790
+
eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
791
+
prettier: '>=3.0.0'
792
+
peerDependenciesMeta:
793
+
'@types/eslint':
794
+
optional: true
795
+
eslint-config-prettier:
796
+
optional: true
797
+
798
+
eslint-plugin-react@7.37.5:
799
+
resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
800
+
engines: {node: '>=4'}
801
+
peerDependencies:
802
+
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
803
+
804
+
eslint-scope@8.3.0:
805
+
resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==}
806
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
807
+
808
+
eslint-visitor-keys@3.4.3:
809
+
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
334
810
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
811
+
812
+
eslint-visitor-keys@4.2.0:
813
+
resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
814
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
815
+
816
+
eslint@9.23.0:
817
+
resolution: {integrity: sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==}
818
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
819
+
hasBin: true
820
+
peerDependencies:
821
+
jiti: '*'
822
+
peerDependenciesMeta:
823
+
jiti:
824
+
optional: true
825
+
826
+
espree@10.3.0:
827
+
resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
828
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
829
+
830
+
esquery@1.6.0:
831
+
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
832
+
engines: {node: '>=0.10'}
833
+
834
+
esrecurse@4.3.0:
835
+
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
836
+
engines: {node: '>=4.0'}
837
+
838
+
estraverse@5.3.0:
839
+
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
840
+
engines: {node: '>=4.0'}
841
+
842
+
estree-toolkit@1.7.8:
843
+
resolution: {integrity: sha512-v0Q0L+0agSDFe3x9Sj7aAzrI9afvsfr5r7AM2SNk/8bKYRQ3tUf4PQEUWe99LkWysmT1PsuSpW+W1w/xZmCKeg==}
844
+
845
+
esutils@2.0.3:
846
+
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
847
+
engines: {node: '>=0.10.0'}
848
+
849
+
event-target-shim@5.0.1:
850
+
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
851
+
engines: {node: '>=6'}
852
+
853
+
eventemitter3@5.0.1:
854
+
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
855
+
856
+
events@3.3.0:
857
+
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
858
+
engines: {node: '>=0.8.x'}
859
+
860
+
fast-deep-equal@3.1.3:
861
+
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
862
+
863
+
fast-diff@1.3.0:
864
+
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
865
+
866
+
fast-glob@3.3.2:
867
+
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
868
+
engines: {node: '>=8.6.0'}
869
+
870
+
fast-json-stable-stringify@2.1.0:
871
+
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
872
+
873
+
fast-levenshtein@2.0.6:
874
+
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
875
+
876
+
fastq@1.17.1:
877
+
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
878
+
879
+
fdir@6.4.3:
880
+
resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
881
+
peerDependencies:
882
+
picomatch: ^3 || ^4
883
+
peerDependenciesMeta:
884
+
picomatch:
885
+
optional: true
886
+
887
+
file-entry-cache@8.0.0:
888
+
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
889
+
engines: {node: '>=16.0.0'}
890
+
891
+
fill-range@7.1.1:
892
+
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
893
+
engines: {node: '>=8'}
894
+
895
+
find-up-simple@1.0.1:
896
+
resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==}
897
+
engines: {node: '>=18'}
898
+
899
+
find-up@5.0.0:
900
+
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
901
+
engines: {node: '>=10'}
902
+
903
+
flat-cache@4.0.1:
904
+
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
905
+
engines: {node: '>=16'}
906
+
907
+
flatted@3.2.9:
908
+
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
909
+
910
+
for-each@0.3.5:
911
+
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
912
+
engines: {node: '>= 0.4'}
913
+
914
+
function-bind@1.1.2:
915
+
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
916
+
917
+
function.prototype.name@1.1.8:
918
+
resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
919
+
engines: {node: '>= 0.4'}
920
+
921
+
functions-have-names@1.2.3:
922
+
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
923
+
924
+
fzf@0.5.2:
925
+
resolution: {integrity: sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==}
926
+
927
+
get-intrinsic@1.3.0:
928
+
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
929
+
engines: {node: '>= 0.4'}
930
+
931
+
get-proto@1.0.1:
932
+
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
933
+
engines: {node: '>= 0.4'}
934
+
935
+
get-symbol-description@1.1.0:
936
+
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
937
+
engines: {node: '>= 0.4'}
938
+
939
+
glob-parent@5.1.2:
940
+
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
941
+
engines: {node: '>= 6'}
942
+
943
+
glob-parent@6.0.2:
944
+
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
945
+
engines: {node: '>=10.13.0'}
946
+
947
+
globals@14.0.0:
948
+
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
949
+
engines: {node: '>=18'}
950
+
951
+
globalthis@1.0.4:
952
+
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
953
+
engines: {node: '>= 0.4'}
954
+
955
+
gopd@1.2.0:
956
+
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
957
+
engines: {node: '>= 0.4'}
958
+
959
+
graphemer@1.4.0:
960
+
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
961
+
962
+
has-bigints@1.1.0:
963
+
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
964
+
engines: {node: '>= 0.4'}
965
+
966
+
has-flag@4.0.0:
967
+
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
968
+
engines: {node: '>=8'}
969
+
970
+
has-property-descriptors@1.0.2:
971
+
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
972
+
973
+
has-proto@1.2.0:
974
+
resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
975
+
engines: {node: '>= 0.4'}
976
+
977
+
has-symbols@1.1.0:
978
+
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
979
+
engines: {node: '>= 0.4'}
980
+
981
+
has-tostringtag@1.0.2:
982
+
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
983
+
engines: {node: '>= 0.4'}
984
+
985
+
hasown@2.0.2:
986
+
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
987
+
engines: {node: '>= 0.4'}
988
+
989
+
husky@8.0.3:
990
+
resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==}
991
+
engines: {node: '>=14'}
992
+
hasBin: true
993
+
994
+
ieee754@1.2.1:
995
+
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
996
+
997
+
ignore@5.3.2:
998
+
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
999
+
engines: {node: '>= 4'}
1000
+
1001
+
import-fresh@3.3.0:
1002
+
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
1003
+
engines: {node: '>=6'}
1004
+
1005
+
imurmurhash@0.1.4:
1006
+
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
1007
+
engines: {node: '>=0.8.19'}
1008
+
1009
+
internal-slot@1.1.0:
1010
+
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
1011
+
engines: {node: '>= 0.4'}
1012
+
1013
+
is-array-buffer@3.0.5:
1014
+
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
1015
+
engines: {node: '>= 0.4'}
1016
+
1017
+
is-async-function@2.1.1:
1018
+
resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
1019
+
engines: {node: '>= 0.4'}
1020
+
1021
+
is-bigint@1.1.0:
1022
+
resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
1023
+
engines: {node: '>= 0.4'}
1024
+
1025
+
is-boolean-object@1.2.2:
1026
+
resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
1027
+
engines: {node: '>= 0.4'}
1028
+
1029
+
is-callable@1.2.7:
1030
+
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
1031
+
engines: {node: '>= 0.4'}
1032
+
1033
+
is-core-module@2.16.1:
1034
+
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
1035
+
engines: {node: '>= 0.4'}
1036
+
1037
+
is-data-view@1.0.2:
1038
+
resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
1039
+
engines: {node: '>= 0.4'}
1040
+
1041
+
is-date-object@1.1.0:
1042
+
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
1043
+
engines: {node: '>= 0.4'}
1044
+
1045
+
is-extglob@2.1.1:
1046
+
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
1047
+
engines: {node: '>=0.10.0'}
1048
+
1049
+
is-finalizationregistry@1.1.1:
1050
+
resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
1051
+
engines: {node: '>= 0.4'}
1052
+
1053
+
is-generator-function@1.1.0:
1054
+
resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
1055
+
engines: {node: '>= 0.4'}
1056
+
1057
+
is-glob@4.0.3:
1058
+
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
1059
+
engines: {node: '>=0.10.0'}
1060
+
1061
+
is-map@2.0.3:
1062
+
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
1063
+
engines: {node: '>= 0.4'}
1064
+
1065
+
is-number-object@1.1.1:
1066
+
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
1067
+
engines: {node: '>= 0.4'}
1068
+
1069
+
is-number@7.0.0:
1070
+
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
1071
+
engines: {node: '>=0.12.0'}
1072
+
1073
+
is-regex@1.2.1:
1074
+
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
1075
+
engines: {node: '>= 0.4'}
1076
+
1077
+
is-set@2.0.3:
1078
+
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
1079
+
engines: {node: '>= 0.4'}
1080
+
1081
+
is-shared-array-buffer@1.0.4:
1082
+
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
1083
+
engines: {node: '>= 0.4'}
1084
+
1085
+
is-string@1.1.1:
1086
+
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
1087
+
engines: {node: '>= 0.4'}
1088
+
1089
+
is-symbol@1.1.1:
1090
+
resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
1091
+
engines: {node: '>= 0.4'}
1092
+
1093
+
is-typed-array@1.1.15:
1094
+
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
1095
+
engines: {node: '>= 0.4'}
1096
+
1097
+
is-weakmap@2.0.2:
1098
+
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
1099
+
engines: {node: '>= 0.4'}
1100
+
1101
+
is-weakref@1.1.1:
1102
+
resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
1103
+
engines: {node: '>= 0.4'}
1104
+
1105
+
is-weakset@2.0.4:
1106
+
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
1107
+
engines: {node: '>= 0.4'}
1108
+
1109
+
isarray@2.0.5:
1110
+
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
1111
+
1112
+
isexe@2.0.0:
1113
+
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
1114
+
1115
+
iterator.prototype@1.1.5:
1116
+
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
1117
+
engines: {node: '>= 0.4'}
1118
+
1119
+
jiti@2.4.2:
1120
+
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
1121
+
hasBin: true
1122
+
1123
+
js-tokens@4.0.0:
1124
+
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
1125
+
1126
+
js-yaml@4.1.0:
1127
+
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
1128
+
hasBin: true
1129
+
1130
+
json-buffer@3.0.1:
1131
+
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
1132
+
1133
+
json-schema-traverse@0.4.1:
1134
+
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
1135
+
1136
+
json-stable-stringify-without-jsonify@1.0.1:
1137
+
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
1138
+
1139
+
jsx-ast-utils@3.3.5:
1140
+
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
1141
+
engines: {node: '>=4.0'}
1142
+
1143
+
keyv@4.5.4:
1144
+
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
1145
+
1146
+
levn@0.4.1:
1147
+
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
1148
+
engines: {node: '>= 0.8.0'}
1149
+
1150
+
locate-path@6.0.0:
1151
+
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
1152
+
engines: {node: '>=10'}
1153
+
1154
+
lodash.merge@4.6.2:
1155
+
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
1156
+
1157
+
loose-envify@1.4.0:
1158
+
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
1159
+
hasBin: true
1160
+
1161
+
math-intrinsics@1.1.0:
1162
+
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
1163
+
engines: {node: '>= 0.4'}
1164
+
1165
+
merge2@1.4.1:
1166
+
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
1167
+
engines: {node: '>= 8'}
1168
+
1169
+
meriyah@6.0.1:
1170
+
resolution: {integrity: sha512-OyvYIOgpzXREySYJ1cqEb2pOKdeQMTfF9M8dRU6nC4hi/GXMmNpe9ssZCrSoTHazu05BSAoRBN/uYeco+ymfOg==}
1171
+
engines: {node: '>=18.0.0'}
1172
+
1173
+
microdiff@1.5.0:
1174
+
resolution: {integrity: sha512-Drq+/THMvDdzRYrK0oxJmOKiC24ayUV8ahrt8l3oRK51PWt6gdtrIGrlIH3pT/lFh1z93FbAcidtsHcWbnRz8Q==}
1175
+
1176
+
micromatch@4.0.8:
1177
+
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
1178
+
engines: {node: '>=8.6'}
1179
+
1180
+
mimic-function@5.0.1:
1181
+
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
1182
+
engines: {node: '>=18'}
1183
+
1184
+
minimatch@3.1.2:
1185
+
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
1186
+
1187
+
minimatch@9.0.5:
1188
+
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
1189
+
engines: {node: '>=16 || 14 >=14.17'}
1190
+
1191
+
ms@2.1.3:
1192
+
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
1193
+
1194
+
nanotar@0.1.1:
1195
+
resolution: {integrity: sha512-AiJsGsSF3O0havL1BydvI4+wR76sKT+okKRwWIaK96cZUnXqH0uNBOsHlbwZq3+m2BR1VKqHDVudl3gO4mYjpQ==}
1196
+
1197
+
natural-compare@1.4.0:
1198
+
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
1199
+
1200
+
node-fetch-native@1.6.6:
1201
+
resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==}
1202
+
1203
+
object-assign@4.1.1:
1204
+
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1205
+
engines: {node: '>=0.10.0'}
1206
+
1207
+
object-inspect@1.13.4:
1208
+
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
1209
+
engines: {node: '>= 0.4'}
1210
+
1211
+
object-keys@1.1.1:
1212
+
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
1213
+
engines: {node: '>= 0.4'}
1214
+
1215
+
object.assign@4.1.7:
1216
+
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
1217
+
engines: {node: '>= 0.4'}
1218
+
1219
+
object.entries@1.1.9:
1220
+
resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
1221
+
engines: {node: '>= 0.4'}
1222
+
1223
+
object.fromentries@2.0.8:
1224
+
resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
1225
+
engines: {node: '>= 0.4'}
1226
+
1227
+
object.values@1.2.1:
1228
+
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
1229
+
engines: {node: '>= 0.4'}
1230
+
1231
+
ofetch@1.4.1:
1232
+
resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==}
1233
+
1234
+
onetime@7.0.0:
1235
+
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
1236
+
engines: {node: '>=18'}
1237
+
1238
+
optionator@0.9.3:
1239
+
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
1240
+
engines: {node: '>= 0.8.0'}
1241
+
1242
+
own-keys@1.0.1:
1243
+
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
1244
+
engines: {node: '>= 0.4'}
1245
+
1246
+
p-limit@3.1.0:
1247
+
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
1248
+
engines: {node: '>=10'}
1249
+
1250
+
p-locate@5.0.0:
1251
+
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
1252
+
engines: {node: '>=10'}
1253
+
1254
+
package-manager-detector@1.1.0:
1255
+
resolution: {integrity: sha512-Y8f9qUlBzW8qauJjd/eu6jlpJZsuPJm2ZAV0cDVd420o4EdpH5RPdoCv+60/TdJflGatr4sDfpAL6ArWZbM5tA==}
1256
+
1257
+
parent-module@1.0.1:
1258
+
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
1259
+
engines: {node: '>=6'}
1260
+
1261
+
path-exists@4.0.0:
1262
+
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
1263
+
engines: {node: '>=8'}
1264
+
1265
+
path-key@3.1.1:
1266
+
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1267
+
engines: {node: '>=8'}
1268
+
1269
+
path-parse@1.0.7:
1270
+
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1271
+
1272
+
pathe@2.0.3:
1273
+
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
1274
+
1275
+
picomatch@2.3.1:
1276
+
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1277
+
engines: {node: '>=8.6'}
1278
+
1279
+
picomatch@4.0.2:
1280
+
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
1281
+
engines: {node: '>=12'}
1282
+
1283
+
pnpm-workspace-yaml@0.3.1:
1284
+
resolution: {integrity: sha512-3nW5RLmREmZ8Pm8MbPsO2RM+99RRjYd25ynj3NV0cFsN7CcEl4sDFzgoFmSyduFwxFQ2Qbu3y2UdCh6HlyUOeA==}
1285
+
1286
+
possible-typed-array-names@1.1.0:
1287
+
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
1288
+
engines: {node: '>= 0.4'}
1289
+
1290
+
prelude-ls@1.2.1:
1291
+
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
1292
+
engines: {node: '>= 0.8.0'}
1293
+
1294
+
prettier-linter-helpers@1.0.0:
1295
+
resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
1296
+
engines: {node: '>=6.0.0'}
1297
+
1298
+
prettier@3.1.0:
1299
+
resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==}
1300
+
engines: {node: '>=14'}
1301
+
hasBin: true
1302
+
1303
+
process@0.11.10:
1304
+
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
1305
+
engines: {node: '>= 0.6.0'}
1306
+
1307
+
prop-types@15.8.1:
1308
+
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
1309
+
1310
+
punycode@2.3.1:
1311
+
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
1312
+
engines: {node: '>=6'}
1313
+
1314
+
quansync@0.2.10:
1315
+
resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
1316
+
1317
+
queue-microtask@1.2.3:
1318
+
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1319
+
1320
+
react-is@16.13.1:
1321
+
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
1322
+
1323
+
readable-stream@4.5.2:
1324
+
resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==}
1325
+
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
1326
+
1327
+
reflect.getprototypeof@1.0.10:
1328
+
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
1329
+
engines: {node: '>= 0.4'}
1330
+
1331
+
regexp.prototype.flags@1.5.4:
1332
+
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
1333
+
engines: {node: '>= 0.4'}
1334
+
1335
+
resolve-from@4.0.0:
1336
+
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
1337
+
engines: {node: '>=4'}
1338
+
1339
+
resolve@2.0.0-next.5:
1340
+
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
1341
+
hasBin: true
1342
+
1343
+
restore-cursor@5.1.0:
1344
+
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
1345
+
engines: {node: '>=18'}
1346
+
1347
+
reusify@1.0.4:
1348
+
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
1349
+
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
1350
+
1351
+
run-parallel@1.2.0:
1352
+
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
1353
+
1354
+
safe-array-concat@1.1.3:
1355
+
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
1356
+
engines: {node: '>=0.4'}
1357
+
1358
+
safe-buffer@5.2.1:
1359
+
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
1360
+
1361
+
safe-push-apply@1.0.0:
1362
+
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
1363
+
engines: {node: '>= 0.4'}
1364
+
1365
+
safe-regex-test@1.1.0:
1366
+
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
1367
+
engines: {node: '>= 0.4'}
1368
+
1369
+
semver@6.3.1:
1370
+
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1371
+
hasBin: true
1372
+
1373
+
semver@7.7.1:
1374
+
resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
1375
+
engines: {node: '>=10'}
1376
+
hasBin: true
1377
+
1378
+
set-function-length@1.2.2:
1379
+
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
1380
+
engines: {node: '>= 0.4'}
1381
+
1382
+
set-function-name@2.0.2:
1383
+
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
1384
+
engines: {node: '>= 0.4'}
1385
+
1386
+
set-proto@1.0.0:
1387
+
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
1388
+
engines: {node: '>= 0.4'}
1389
+
1390
+
shebang-command@2.0.0:
1391
+
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
1392
+
engines: {node: '>=8'}
1393
+
1394
+
shebang-regex@3.0.0:
1395
+
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
1396
+
engines: {node: '>=8'}
1397
+
1398
+
side-channel-list@1.0.0:
1399
+
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
1400
+
engines: {node: '>= 0.4'}
1401
+
1402
+
side-channel-map@1.0.1:
1403
+
resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
1404
+
engines: {node: '>= 0.4'}
1405
+
1406
+
side-channel-weakmap@1.0.2:
1407
+
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
1408
+
engines: {node: '>= 0.4'}
1409
+
1410
+
side-channel@1.1.0:
1411
+
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
1412
+
engines: {node: '>= 0.4'}
1413
+
1414
+
signal-exit@4.1.0:
1415
+
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
1416
+
engines: {node: '>=14'}
1417
+
1418
+
standalone-electron-types@1.0.0:
1419
+
resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==}
1420
+
1421
+
string.prototype.matchall@4.0.12:
1422
+
resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
1423
+
engines: {node: '>= 0.4'}
1424
+
1425
+
string.prototype.repeat@1.0.0:
1426
+
resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
1427
+
1428
+
string.prototype.trim@1.2.10:
1429
+
resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
1430
+
engines: {node: '>= 0.4'}
1431
+
1432
+
string.prototype.trimend@1.0.9:
1433
+
resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
1434
+
engines: {node: '>= 0.4'}
1435
+
1436
+
string.prototype.trimstart@1.0.8:
1437
+
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
1438
+
engines: {node: '>= 0.4'}
1439
+
1440
+
string_decoder@1.3.0:
1441
+
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
1442
+
1443
+
strip-json-comments@3.1.1:
1444
+
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
1445
+
engines: {node: '>=8'}
1446
+
1447
+
supports-color@7.2.0:
1448
+
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
1449
+
engines: {node: '>=8'}
1450
+
1451
+
supports-preserve-symlinks-flag@1.0.0:
1452
+
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
1453
+
engines: {node: '>= 0.4'}
1454
+
1455
+
synckit@0.11.1:
1456
+
resolution: {integrity: sha512-fWZqNBZNNFp/7mTUy1fSsydhKsAKJ+u90Nk7kOK5Gcq9vObaqLBLjWFDBkyVU9Vvc6Y71VbOevMuGhqv02bT+Q==}
1457
+
engines: {node: ^14.18.0 || >=16.0.0}
1458
+
1459
+
taze@19.0.4:
1460
+
resolution: {integrity: sha512-bviyNotzqcIWpVBCC4QYVb2yupzKyUDGQi2m/8GERdiPaudVMtgAqaE98+x0cDDaByYRMJCyhQWM04ikUL6+kQ==}
1461
+
hasBin: true
1462
+
1463
+
tinyexec@1.0.1:
1464
+
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
1465
+
1466
+
tinyglobby@0.2.12:
1467
+
resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
1468
+
engines: {node: '>=12.0.0'}
1469
+
1470
+
to-regex-range@5.0.1:
1471
+
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
1472
+
engines: {node: '>=8.0'}
1473
+
1474
+
ts-api-utils@2.1.0:
1475
+
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
1476
+
engines: {node: '>=18.12'}
1477
+
peerDependencies:
1478
+
typescript: '>=4.8.4'
1479
+
1480
+
tslib@2.8.1:
1481
+
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
1482
+
1483
+
type-check@0.4.0:
1484
+
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
1485
+
engines: {node: '>= 0.8.0'}
1486
+
1487
+
typed-array-buffer@1.0.3:
1488
+
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
1489
+
engines: {node: '>= 0.4'}
1490
+
1491
+
typed-array-byte-length@1.0.3:
1492
+
resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
1493
+
engines: {node: '>= 0.4'}
1494
+
1495
+
typed-array-byte-offset@1.0.4:
1496
+
resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
1497
+
engines: {node: '>= 0.4'}
1498
+
1499
+
typed-array-length@1.0.7:
1500
+
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
1501
+
engines: {node: '>= 0.4'}
1502
+
1503
+
typescript-eslint@8.29.0:
1504
+
resolution: {integrity: sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==}
1505
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1506
+
peerDependencies:
1507
+
eslint: ^8.57.0 || ^9.0.0
1508
+
typescript: '>=4.8.4 <5.9.0'
1509
+
1510
+
typescript@5.8.2:
1511
+
resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
1512
+
engines: {node: '>=14.17'}
1513
+
hasBin: true
1514
+
1515
+
ufo@1.5.4:
1516
+
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
1517
+
1518
+
unbox-primitive@1.1.0:
1519
+
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
1520
+
engines: {node: '>= 0.4'}
1521
+
1522
+
unconfig@7.3.1:
1523
+
resolution: {integrity: sha512-LH5WL+un92tGAzWS87k7LkAfwpMdm7V0IXG2FxEjZz/QxiIW5J5LkcrKQThj0aRz6+h/lFmKI9EUXmK/T0bcrw==}
1524
+
1525
+
undici-types@6.20.0:
1526
+
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
1527
+
1528
+
undici-types@6.21.0:
1529
+
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
1530
+
1531
+
uri-js@4.4.1:
1532
+
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
1533
+
1534
+
utilium@1.10.1:
1535
+
resolution: {integrity: sha512-GQINDTb/ocyz4acQj3GXAe0wipYxws6L+9ouqaq10KlInTk9DGvW9TJd0pYa/Xu3cppNnZuB4T/sBuSXpcN2ng==}
1536
+
1537
+
which-boxed-primitive@1.1.1:
1538
+
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
1539
+
engines: {node: '>= 0.4'}
1540
+
1541
+
which-builtin-type@1.2.1:
1542
+
resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
1543
+
engines: {node: '>= 0.4'}
1544
+
1545
+
which-collection@1.0.2:
1546
+
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
1547
+
engines: {node: '>= 0.4'}
1548
+
1549
+
which-typed-array@1.1.19:
1550
+
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
1551
+
engines: {node: '>= 0.4'}
1552
+
1553
+
which@2.0.2:
1554
+
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
1555
+
engines: {node: '>= 8'}
1556
+
hasBin: true
1557
+
1558
+
yaml@2.7.1:
1559
+
resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==}
1560
+
engines: {node: '>= 14'}
1561
+
hasBin: true
1562
+
1563
+
yocto-queue@0.1.0:
1564
+
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
1565
+
engines: {node: '>=10'}
1566
+
1567
+
zustand@5.0.3:
1568
+
resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==}
1569
+
engines: {node: '>=12.20.0'}
1570
+
peerDependencies:
1571
+
'@types/react': '>=18.0.0'
1572
+
immer: '>=9.0.6'
1573
+
react: '>=18.0.0'
1574
+
use-sync-external-store: '>=1.2.0'
1575
+
peerDependenciesMeta:
1576
+
'@types/react':
1577
+
optional: true
1578
+
immer:
1579
+
optional: true
1580
+
react:
1581
+
optional: true
1582
+
use-sync-external-store:
1583
+
optional: true
1584
+
1585
+
snapshots:
1586
+
1587
+
'@aashutoshrathi/word-wrap@1.2.6': {}
1588
+
1589
+
'@antfu/ni@24.3.0':
1590
+
dependencies:
1591
+
ansis: 3.17.0
1592
+
fzf: 0.5.2
1593
+
package-manager-detector: 1.1.0
1594
+
tinyexec: 1.0.1
1595
+
1596
+
'@esbuild/android-arm64@0.19.3':
1597
+
optional: true
1598
+
1599
+
'@esbuild/android-arm@0.19.3':
1600
+
optional: true
1601
+
1602
+
'@esbuild/android-x64@0.19.3':
1603
+
optional: true
1604
+
1605
+
'@esbuild/darwin-arm64@0.19.3':
1606
+
optional: true
1607
+
1608
+
'@esbuild/darwin-x64@0.19.3':
1609
+
optional: true
1610
+
1611
+
'@esbuild/freebsd-arm64@0.19.3':
1612
+
optional: true
1613
+
1614
+
'@esbuild/freebsd-x64@0.19.3':
1615
+
optional: true
1616
+
1617
+
'@esbuild/linux-arm64@0.19.3':
1618
+
optional: true
1619
+
1620
+
'@esbuild/linux-arm@0.19.3':
1621
+
optional: true
1622
+
1623
+
'@esbuild/linux-ia32@0.19.3':
1624
+
optional: true
1625
+
1626
+
'@esbuild/linux-loong64@0.19.3':
1627
+
optional: true
1628
+
1629
+
'@esbuild/linux-mips64el@0.19.3':
1630
+
optional: true
1631
+
1632
+
'@esbuild/linux-ppc64@0.19.3':
1633
+
optional: true
1634
+
1635
+
'@esbuild/linux-riscv64@0.19.3':
1636
+
optional: true
1637
+
1638
+
'@esbuild/linux-s390x@0.19.3':
1639
+
optional: true
1640
+
1641
+
'@esbuild/linux-x64@0.19.3':
1642
+
optional: true
1643
+
1644
+
'@esbuild/netbsd-x64@0.19.3':
1645
+
optional: true
1646
+
1647
+
'@esbuild/openbsd-x64@0.19.3':
1648
+
optional: true
1649
+
1650
+
'@esbuild/sunos-x64@0.19.3':
1651
+
optional: true
1652
+
1653
+
'@esbuild/win32-arm64@0.19.3':
1654
+
optional: true
1655
+
1656
+
'@esbuild/win32-ia32@0.19.3':
1657
+
optional: true
1658
+
1659
+
'@esbuild/win32-x64@0.19.3':
1660
+
optional: true
1661
+
1662
+
'@eslint-community/eslint-utils@4.5.1(eslint@9.23.0(jiti@2.4.2))':
1663
+
dependencies:
1664
+
eslint: 9.23.0(jiti@2.4.2)
1665
+
eslint-visitor-keys: 3.4.3
1666
+
1667
+
'@eslint-community/regexpp@4.12.1': {}
1668
+
1669
+
'@eslint/config-array@0.19.2':
1670
+
dependencies:
1671
+
'@eslint/object-schema': 2.1.6
1672
+
debug: 4.4.0
1673
+
minimatch: 3.1.2
1674
+
transitivePeerDependencies:
1675
+
- supports-color
1676
+
1677
+
'@eslint/config-helpers@0.2.1': {}
1678
+
1679
+
'@eslint/core@0.12.0':
1680
+
dependencies:
1681
+
'@types/json-schema': 7.0.15
1682
+
1683
+
'@eslint/core@0.13.0':
1684
+
dependencies:
1685
+
'@types/json-schema': 7.0.15
1686
+
1687
+
'@eslint/eslintrc@3.3.1':
335
1688
dependencies:
336
1689
ajv: 6.12.6
337
-
debug: 4.3.4
338
-
espree: 9.6.1
339
-
globals: 13.23.0
340
-
ignore: 5.3.0
1690
+
debug: 4.4.0
1691
+
espree: 10.3.0
1692
+
globals: 14.0.0
1693
+
ignore: 5.3.2
341
1694
import-fresh: 3.3.0
342
1695
js-yaml: 4.1.0
343
1696
minimatch: 3.1.2
344
1697
strip-json-comments: 3.1.1
345
1698
transitivePeerDependencies:
346
1699
- supports-color
347
-
dev: true
348
1700
349
-
/@eslint/js@8.55.0:
350
-
resolution: {integrity: sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==}
351
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
352
-
dev: true
1701
+
'@eslint/js@9.23.0': {}
353
1702
354
-
/@humanwhocodes/config-array@0.11.13:
355
-
resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
356
-
engines: {node: '>=10.10.0'}
1703
+
'@eslint/object-schema@2.1.6': {}
1704
+
1705
+
'@eslint/plugin-kit@0.2.8':
357
1706
dependencies:
358
-
'@humanwhocodes/object-schema': 2.0.1
359
-
debug: 4.3.4
360
-
minimatch: 3.1.2
1707
+
'@eslint/core': 0.13.0
1708
+
levn: 0.4.1
1709
+
1710
+
'@humanfs/core@0.19.1': {}
1711
+
1712
+
'@humanfs/node@0.16.6':
1713
+
dependencies:
1714
+
'@humanfs/core': 0.19.1
1715
+
'@humanwhocodes/retry': 0.3.1
1716
+
1717
+
'@humanwhocodes/module-importer@1.0.1': {}
1718
+
1719
+
'@humanwhocodes/retry@0.3.1': {}
1720
+
1721
+
'@humanwhocodes/retry@0.4.2': {}
1722
+
1723
+
'@moonlight-mod/eslint-config@https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9(@types/eslint@9.6.1)(eslint@9.23.0(jiti@2.4.2))(prettier@3.1.0)(typescript@5.8.2)':
1724
+
dependencies:
1725
+
'@eslint/js': 9.23.0
1726
+
eslint: 9.23.0(jiti@2.4.2)
1727
+
eslint-config-prettier: 9.1.0(eslint@9.23.0(jiti@2.4.2))
1728
+
eslint-plugin-prettier: 5.2.6(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.23.0(jiti@2.4.2)))(eslint@9.23.0(jiti@2.4.2))(prettier@3.1.0)
1729
+
eslint-plugin-react: 7.37.5(eslint@9.23.0(jiti@2.4.2))
1730
+
typescript: 5.8.2
1731
+
typescript-eslint: 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
361
1732
transitivePeerDependencies:
1733
+
- '@types/eslint'
1734
+
- prettier
362
1735
- supports-color
363
-
dev: true
364
1736
365
-
/@humanwhocodes/module-importer@1.0.1:
366
-
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
367
-
engines: {node: '>=12.22'}
368
-
dev: true
1737
+
'@moonlight-mod/lunast@1.0.1':
1738
+
dependencies:
1739
+
astring: 1.9.0
1740
+
estree-toolkit: 1.7.8
1741
+
meriyah: 6.0.1
1742
+
1743
+
'@moonlight-mod/mappings@1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)':
1744
+
dependencies:
1745
+
'@moonlight-mod/lunast': 1.0.1
1746
+
'@moonlight-mod/moonmap': 1.0.5
1747
+
'@types/chroma-js': 3.1.0
1748
+
'@types/flux': 3.1.14
1749
+
'@types/highlightjs': 9.12.6
1750
+
'@types/lodash': 4.17.14
1751
+
'@types/platform': 1.3.6
1752
+
'@types/react': 18.3.20
1753
+
csstype: 3.1.3
1754
+
zustand: 5.0.3(@types/react@18.3.20)
1755
+
transitivePeerDependencies:
1756
+
- immer
1757
+
- react
1758
+
- use-sync-external-store
369
1759
370
-
/@humanwhocodes/object-schema@2.0.1:
371
-
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
372
-
dev: true
1760
+
'@moonlight-mod/moonmap@1.0.5': {}
373
1761
374
-
/@nodelib/fs.scandir@2.1.5:
375
-
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
376
-
engines: {node: '>= 8'}
1762
+
'@nodelib/fs.scandir@2.1.5':
377
1763
dependencies:
378
1764
'@nodelib/fs.stat': 2.0.5
379
1765
run-parallel: 1.2.0
380
-
dev: true
381
1766
382
-
/@nodelib/fs.stat@2.0.5:
383
-
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
384
-
engines: {node: '>= 8'}
385
-
dev: true
1767
+
'@nodelib/fs.stat@2.0.5': {}
386
1768
387
-
/@nodelib/fs.walk@1.2.8:
388
-
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
389
-
engines: {node: '>= 8'}
1769
+
'@nodelib/fs.walk@1.2.8':
390
1770
dependencies:
391
1771
'@nodelib/fs.scandir': 2.1.5
392
-
fastq: 1.15.0
393
-
dev: true
1772
+
fastq: 1.17.1
394
1773
395
-
/@pkgr/utils@2.4.2:
396
-
resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==}
397
-
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
1774
+
'@pkgr/core@0.2.0': {}
1775
+
1776
+
'@quansync/fs@0.1.2':
398
1777
dependencies:
399
-
cross-spawn: 7.0.3
400
-
fast-glob: 3.3.2
401
-
is-glob: 4.0.3
402
-
open: 9.1.0
403
-
picocolors: 1.0.0
404
-
tslib: 2.6.2
405
-
dev: true
1778
+
quansync: 0.2.10
406
1779
407
-
/@types/fbemitter@2.0.33:
408
-
resolution: {integrity: sha512-KcSilwdl0D8YgXGL6l9d+rTBm2W7pDyTZrDEw0+IzqQ724676KJtMeO+xHodJewKFWZT+GFWaJubA5mpMxSkcg==}
409
-
dev: false
1780
+
'@types/chroma-js@3.1.0': {}
410
1781
411
-
/@types/flux@3.1.12:
412
-
resolution: {integrity: sha512-HZ8o/DTVNgcgnXoDyn0ZnjqEZMT4Chr4w5ktMQSbQAnqVDklasmRqNGd2agZDsk5i0jYHQLgQQuM782bWG7fUA==}
1782
+
'@types/chrome@0.0.313':
413
1783
dependencies:
414
-
'@types/fbemitter': 2.0.33
415
-
'@types/react': 18.2.22
416
-
dev: false
1784
+
'@types/filesystem': 0.0.36
1785
+
'@types/har-format': 1.2.16
417
1786
418
-
/@types/json-schema@7.0.15:
419
-
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
420
-
dev: true
1787
+
'@types/eslint@9.6.1':
1788
+
dependencies:
1789
+
'@types/estree': 1.0.7
1790
+
'@types/json-schema': 7.0.15
1791
+
optional: true
421
1792
422
-
/@types/node@18.17.17:
423
-
resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==}
424
-
dev: false
1793
+
'@types/estree-jsx@1.0.5':
1794
+
dependencies:
1795
+
'@types/estree': 1.0.6
425
1796
426
-
/@types/node@20.6.2:
427
-
resolution: {integrity: sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==}
428
-
dev: false
1797
+
'@types/estree@1.0.6': {}
429
1798
430
-
/@types/prop-types@15.7.6:
431
-
resolution: {integrity: sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==}
432
-
dev: false
1799
+
'@types/estree@1.0.7':
1800
+
optional: true
433
1801
434
-
/@types/react@18.2.22:
435
-
resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==}
1802
+
'@types/fbemitter@2.0.35': {}
1803
+
1804
+
'@types/filesystem@0.0.36':
436
1805
dependencies:
437
-
'@types/prop-types': 15.7.6
438
-
'@types/scheduler': 0.16.3
439
-
csstype: 3.1.2
440
-
dev: false
1806
+
'@types/filewriter': 0.0.33
441
1807
442
-
/@types/scheduler@0.16.3:
443
-
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
444
-
dev: false
1808
+
'@types/filewriter@0.0.33': {}
445
1809
446
-
/@types/semver@7.5.6:
447
-
resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==}
448
-
dev: true
1810
+
'@types/flux@3.1.14':
1811
+
dependencies:
1812
+
'@types/fbemitter': 2.0.35
1813
+
'@types/react': 18.3.20
449
1814
450
-
/@typescript-eslint/eslint-plugin@6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.2):
451
-
resolution: {integrity: sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==}
452
-
engines: {node: ^16.0.0 || >=18.0.0}
453
-
peerDependencies:
454
-
'@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
455
-
eslint: ^7.0.0 || ^8.0.0
456
-
typescript: '*'
457
-
peerDependenciesMeta:
458
-
typescript:
459
-
optional: true
1815
+
'@types/har-format@1.2.16': {}
1816
+
1817
+
'@types/highlightjs@9.12.6': {}
1818
+
1819
+
'@types/json-schema@7.0.15': {}
1820
+
1821
+
'@types/lodash@4.17.14': {}
1822
+
1823
+
'@types/node@18.17.17': {}
1824
+
1825
+
'@types/node@22.13.6':
1826
+
dependencies:
1827
+
undici-types: 6.20.0
1828
+
1829
+
'@types/node@22.14.0':
1830
+
dependencies:
1831
+
undici-types: 6.21.0
1832
+
1833
+
'@types/platform@1.3.6': {}
1834
+
1835
+
'@types/prop-types@15.7.13': {}
1836
+
1837
+
'@types/react@18.3.20':
1838
+
dependencies:
1839
+
'@types/prop-types': 15.7.13
1840
+
csstype: 3.1.3
1841
+
1842
+
'@typescript-eslint/eslint-plugin@8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
460
1843
dependencies:
461
-
'@eslint-community/regexpp': 4.10.0
462
-
'@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
463
-
'@typescript-eslint/scope-manager': 6.13.2
464
-
'@typescript-eslint/type-utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
465
-
'@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
466
-
'@typescript-eslint/visitor-keys': 6.13.2
467
-
debug: 4.3.4
468
-
eslint: 8.55.0
1844
+
'@eslint-community/regexpp': 4.12.1
1845
+
'@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
1846
+
'@typescript-eslint/scope-manager': 8.29.0
1847
+
'@typescript-eslint/type-utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
1848
+
'@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
1849
+
'@typescript-eslint/visitor-keys': 8.29.0
1850
+
eslint: 9.23.0(jiti@2.4.2)
469
1851
graphemer: 1.4.0
470
-
ignore: 5.3.0
1852
+
ignore: 5.3.2
471
1853
natural-compare: 1.4.0
472
-
semver: 7.5.4
473
-
ts-api-utils: 1.0.3(typescript@5.3.2)
474
-
typescript: 5.3.2
1854
+
ts-api-utils: 2.1.0(typescript@5.8.2)
1855
+
typescript: 5.8.2
475
1856
transitivePeerDependencies:
476
1857
- supports-color
477
-
dev: true
478
1858
479
-
/@typescript-eslint/parser@6.13.2(eslint@8.55.0)(typescript@5.3.2):
480
-
resolution: {integrity: sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==}
481
-
engines: {node: ^16.0.0 || >=18.0.0}
482
-
peerDependencies:
483
-
eslint: ^7.0.0 || ^8.0.0
484
-
typescript: '*'
485
-
peerDependenciesMeta:
486
-
typescript:
487
-
optional: true
1859
+
'@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
488
1860
dependencies:
489
-
'@typescript-eslint/scope-manager': 6.13.2
490
-
'@typescript-eslint/types': 6.13.2
491
-
'@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2)
492
-
'@typescript-eslint/visitor-keys': 6.13.2
493
-
debug: 4.3.4
494
-
eslint: 8.55.0
495
-
typescript: 5.3.2
1861
+
'@typescript-eslint/scope-manager': 8.29.0
1862
+
'@typescript-eslint/types': 8.29.0
1863
+
'@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
1864
+
'@typescript-eslint/visitor-keys': 8.29.0
1865
+
debug: 4.4.0
1866
+
eslint: 9.23.0(jiti@2.4.2)
1867
+
typescript: 5.8.2
496
1868
transitivePeerDependencies:
497
1869
- supports-color
498
-
dev: true
499
1870
500
-
/@typescript-eslint/scope-manager@6.13.2:
501
-
resolution: {integrity: sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==}
502
-
engines: {node: ^16.0.0 || >=18.0.0}
1871
+
'@typescript-eslint/scope-manager@8.29.0':
503
1872
dependencies:
504
-
'@typescript-eslint/types': 6.13.2
505
-
'@typescript-eslint/visitor-keys': 6.13.2
506
-
dev: true
1873
+
'@typescript-eslint/types': 8.29.0
1874
+
'@typescript-eslint/visitor-keys': 8.29.0
507
1875
508
-
/@typescript-eslint/type-utils@6.13.2(eslint@8.55.0)(typescript@5.3.2):
509
-
resolution: {integrity: sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==}
510
-
engines: {node: ^16.0.0 || >=18.0.0}
511
-
peerDependencies:
512
-
eslint: ^7.0.0 || ^8.0.0
513
-
typescript: '*'
514
-
peerDependenciesMeta:
515
-
typescript:
516
-
optional: true
1876
+
'@typescript-eslint/type-utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
517
1877
dependencies:
518
-
'@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2)
519
-
'@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
520
-
debug: 4.3.4
521
-
eslint: 8.55.0
522
-
ts-api-utils: 1.0.3(typescript@5.3.2)
523
-
typescript: 5.3.2
1878
+
'@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
1879
+
'@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
1880
+
debug: 4.4.0
1881
+
eslint: 9.23.0(jiti@2.4.2)
1882
+
ts-api-utils: 2.1.0(typescript@5.8.2)
1883
+
typescript: 5.8.2
524
1884
transitivePeerDependencies:
525
1885
- supports-color
526
-
dev: true
527
1886
528
-
/@typescript-eslint/types@6.13.2:
529
-
resolution: {integrity: sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==}
530
-
engines: {node: ^16.0.0 || >=18.0.0}
531
-
dev: true
1887
+
'@typescript-eslint/types@8.29.0': {}
532
1888
533
-
/@typescript-eslint/typescript-estree@6.13.2(typescript@5.3.2):
534
-
resolution: {integrity: sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==}
535
-
engines: {node: ^16.0.0 || >=18.0.0}
536
-
peerDependencies:
537
-
typescript: '*'
538
-
peerDependenciesMeta:
539
-
typescript:
540
-
optional: true
1889
+
'@typescript-eslint/typescript-estree@8.29.0(typescript@5.8.2)':
541
1890
dependencies:
542
-
'@typescript-eslint/types': 6.13.2
543
-
'@typescript-eslint/visitor-keys': 6.13.2
544
-
debug: 4.3.4
545
-
globby: 11.1.0
1891
+
'@typescript-eslint/types': 8.29.0
1892
+
'@typescript-eslint/visitor-keys': 8.29.0
1893
+
debug: 4.4.0
1894
+
fast-glob: 3.3.2
546
1895
is-glob: 4.0.3
547
-
semver: 7.5.4
548
-
ts-api-utils: 1.0.3(typescript@5.3.2)
549
-
typescript: 5.3.2
1896
+
minimatch: 9.0.5
1897
+
semver: 7.7.1
1898
+
ts-api-utils: 2.1.0(typescript@5.8.2)
1899
+
typescript: 5.8.2
550
1900
transitivePeerDependencies:
551
1901
- supports-color
552
-
dev: true
553
1902
554
-
/@typescript-eslint/utils@6.13.2(eslint@8.55.0)(typescript@5.3.2):
555
-
resolution: {integrity: sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==}
556
-
engines: {node: ^16.0.0 || >=18.0.0}
557
-
peerDependencies:
558
-
eslint: ^7.0.0 || ^8.0.0
1903
+
'@typescript-eslint/utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
559
1904
dependencies:
560
-
'@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
561
-
'@types/json-schema': 7.0.15
562
-
'@types/semver': 7.5.6
563
-
'@typescript-eslint/scope-manager': 6.13.2
564
-
'@typescript-eslint/types': 6.13.2
565
-
'@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2)
566
-
eslint: 8.55.0
567
-
semver: 7.5.4
1905
+
'@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
1906
+
'@typescript-eslint/scope-manager': 8.29.0
1907
+
'@typescript-eslint/types': 8.29.0
1908
+
'@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
1909
+
eslint: 9.23.0(jiti@2.4.2)
1910
+
typescript: 5.8.2
568
1911
transitivePeerDependencies:
569
1912
- supports-color
570
-
- typescript
571
-
dev: true
572
1913
573
-
/@typescript-eslint/visitor-keys@6.13.2:
574
-
resolution: {integrity: sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==}
575
-
engines: {node: ^16.0.0 || >=18.0.0}
1914
+
'@typescript-eslint/visitor-keys@8.29.0':
1915
+
dependencies:
1916
+
'@typescript-eslint/types': 8.29.0
1917
+
eslint-visitor-keys: 4.2.0
1918
+
1919
+
'@xterm/xterm@5.5.0':
1920
+
optional: true
1921
+
1922
+
'@zenfs/core@2.0.0':
1923
+
dependencies:
1924
+
'@types/node': 22.13.6
1925
+
buffer: 6.0.3
1926
+
eventemitter3: 5.0.1
1927
+
readable-stream: 4.5.2
1928
+
utilium: 1.10.1
1929
+
1930
+
'@zenfs/dom@1.1.6(@zenfs/core@2.0.0)(utilium@1.10.1)':
576
1931
dependencies:
577
-
'@typescript-eslint/types': 6.13.2
578
-
eslint-visitor-keys: 3.4.3
579
-
dev: true
1932
+
'@zenfs/core': 2.0.0
1933
+
utilium: 1.10.1
580
1934
581
-
/@ungap/structured-clone@1.2.0:
582
-
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
583
-
dev: true
1935
+
abort-controller@3.0.0:
1936
+
dependencies:
1937
+
event-target-shim: 5.0.1
584
1938
585
-
/acorn-jsx@5.3.2(acorn@8.11.2):
586
-
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
587
-
peerDependencies:
588
-
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
1939
+
acorn-jsx@5.3.2(acorn@8.14.1):
589
1940
dependencies:
590
-
acorn: 8.11.2
591
-
dev: true
1941
+
acorn: 8.14.1
592
1942
593
-
/acorn@8.11.2:
594
-
resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
595
-
engines: {node: '>=0.4.0'}
596
-
hasBin: true
597
-
dev: true
1943
+
acorn@8.14.1: {}
598
1944
599
-
/ajv@6.12.6:
600
-
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
1945
+
ajv@6.12.6:
601
1946
dependencies:
602
1947
fast-deep-equal: 3.1.3
603
1948
fast-json-stable-stringify: 2.1.0
604
1949
json-schema-traverse: 0.4.1
605
1950
uri-js: 4.4.1
606
-
dev: true
607
1951
608
-
/ansi-regex@5.0.1:
609
-
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
610
-
engines: {node: '>=8'}
611
-
dev: true
612
-
613
-
/ansi-styles@4.3.0:
614
-
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
615
-
engines: {node: '>=8'}
1952
+
ansi-styles@4.3.0:
616
1953
dependencies:
617
1954
color-convert: 2.0.1
618
-
dev: true
619
1955
620
-
/argparse@2.0.1:
621
-
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
622
-
dev: true
1956
+
ansis@3.17.0: {}
623
1957
624
-
/array-buffer-byte-length@1.0.0:
625
-
resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
1958
+
argparse@2.0.1: {}
1959
+
1960
+
array-buffer-byte-length@1.0.2:
626
1961
dependencies:
627
-
call-bind: 1.0.5
628
-
is-array-buffer: 3.0.2
629
-
dev: true
1962
+
call-bound: 1.0.4
1963
+
is-array-buffer: 3.0.5
630
1964
631
-
/array-includes@3.1.7:
632
-
resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
633
-
engines: {node: '>= 0.4'}
1965
+
array-includes@3.1.8:
634
1966
dependencies:
635
-
call-bind: 1.0.5
1967
+
call-bind: 1.0.8
636
1968
define-properties: 1.2.1
637
-
es-abstract: 1.22.3
638
-
get-intrinsic: 1.2.2
639
-
is-string: 1.0.7
640
-
dev: true
641
-
642
-
/array-union@2.1.0:
643
-
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
644
-
engines: {node: '>=8'}
645
-
dev: true
1969
+
es-abstract: 1.23.9
1970
+
es-object-atoms: 1.1.1
1971
+
get-intrinsic: 1.3.0
1972
+
is-string: 1.1.1
646
1973
647
-
/array.prototype.flat@1.3.2:
648
-
resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
649
-
engines: {node: '>= 0.4'}
1974
+
array.prototype.findlast@1.2.5:
650
1975
dependencies:
651
-
call-bind: 1.0.5
1976
+
call-bind: 1.0.8
652
1977
define-properties: 1.2.1
653
-
es-abstract: 1.22.3
654
-
es-shim-unscopables: 1.0.2
655
-
dev: true
1978
+
es-abstract: 1.23.9
1979
+
es-errors: 1.3.0
1980
+
es-object-atoms: 1.1.1
1981
+
es-shim-unscopables: 1.1.0
656
1982
657
-
/array.prototype.flatmap@1.3.2:
658
-
resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
659
-
engines: {node: '>= 0.4'}
1983
+
array.prototype.flat@1.3.3:
660
1984
dependencies:
661
-
call-bind: 1.0.5
1985
+
call-bind: 1.0.8
662
1986
define-properties: 1.2.1
663
-
es-abstract: 1.22.3
664
-
es-shim-unscopables: 1.0.2
665
-
dev: true
1987
+
es-abstract: 1.23.9
1988
+
es-shim-unscopables: 1.1.0
666
1989
667
-
/array.prototype.tosorted@1.1.2:
668
-
resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==}
1990
+
array.prototype.flatmap@1.3.3:
669
1991
dependencies:
670
-
call-bind: 1.0.5
1992
+
call-bind: 1.0.8
671
1993
define-properties: 1.2.1
672
-
es-abstract: 1.22.3
673
-
es-shim-unscopables: 1.0.2
674
-
get-intrinsic: 1.2.2
675
-
dev: true
1994
+
es-abstract: 1.23.9
1995
+
es-shim-unscopables: 1.1.0
676
1996
677
-
/arraybuffer.prototype.slice@1.0.2:
678
-
resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==}
679
-
engines: {node: '>= 0.4'}
1997
+
array.prototype.tosorted@1.1.4:
680
1998
dependencies:
681
-
array-buffer-byte-length: 1.0.0
682
-
call-bind: 1.0.5
1999
+
call-bind: 1.0.8
683
2000
define-properties: 1.2.1
684
-
es-abstract: 1.22.3
685
-
get-intrinsic: 1.2.2
686
-
is-array-buffer: 3.0.2
687
-
is-shared-array-buffer: 1.0.2
688
-
dev: true
2001
+
es-abstract: 1.23.9
2002
+
es-errors: 1.3.0
2003
+
es-shim-unscopables: 1.1.0
689
2004
690
-
/asynciterator.prototype@1.0.0:
691
-
resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==}
2005
+
arraybuffer.prototype.slice@1.0.4:
692
2006
dependencies:
693
-
has-symbols: 1.0.3
694
-
dev: true
2007
+
array-buffer-byte-length: 1.0.2
2008
+
call-bind: 1.0.8
2009
+
define-properties: 1.2.1
2010
+
es-abstract: 1.23.9
2011
+
es-errors: 1.3.0
2012
+
get-intrinsic: 1.3.0
2013
+
is-array-buffer: 3.0.5
695
2014
696
-
/available-typed-arrays@1.0.5:
697
-
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
698
-
engines: {node: '>= 0.4'}
699
-
dev: true
2015
+
astring@1.9.0: {}
700
2016
701
-
/balanced-match@1.0.2:
702
-
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
2017
+
async-function@1.0.0: {}
703
2018
704
-
/big-integer@1.6.52:
705
-
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
706
-
engines: {node: '>=0.6'}
707
-
dev: true
2019
+
available-typed-arrays@1.0.7:
2020
+
dependencies:
2021
+
possible-typed-array-names: 1.1.0
708
2022
709
-
/bplist-parser@0.2.0:
710
-
resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==}
711
-
engines: {node: '>= 5.10.0'}
2023
+
balanced-match@1.0.2: {}
2024
+
2025
+
base64-js@1.5.1: {}
2026
+
2027
+
brace-expansion@1.1.11:
712
2028
dependencies:
713
-
big-integer: 1.6.52
714
-
dev: true
2029
+
balanced-match: 1.0.2
2030
+
concat-map: 0.0.1
715
2031
716
-
/brace-expansion@1.1.11:
717
-
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
2032
+
brace-expansion@2.0.1:
718
2033
dependencies:
719
2034
balanced-match: 1.0.2
720
-
concat-map: 0.0.1
721
2035
722
-
/braces@3.0.2:
723
-
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
724
-
engines: {node: '>=8'}
2036
+
braces@3.0.3:
725
2037
dependencies:
726
-
fill-range: 7.0.1
727
-
dev: true
2038
+
fill-range: 7.1.1
728
2039
729
-
/bundle-name@3.0.0:
730
-
resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==}
731
-
engines: {node: '>=12'}
2040
+
buffer@6.0.3:
732
2041
dependencies:
733
-
run-applescript: 5.0.0
734
-
dev: true
2042
+
base64-js: 1.5.1
2043
+
ieee754: 1.2.1
735
2044
736
-
/call-bind@1.0.5:
737
-
resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
2045
+
cac@6.7.14: {}
2046
+
2047
+
call-bind-apply-helpers@1.0.2:
738
2048
dependencies:
2049
+
es-errors: 1.3.0
739
2050
function-bind: 1.1.2
740
-
get-intrinsic: 1.2.2
741
-
set-function-length: 1.1.1
742
-
dev: true
743
2051
744
-
/callsites@3.1.0:
745
-
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
746
-
engines: {node: '>=6'}
747
-
dev: true
2052
+
call-bind@1.0.8:
2053
+
dependencies:
2054
+
call-bind-apply-helpers: 1.0.2
2055
+
es-define-property: 1.0.1
2056
+
get-intrinsic: 1.3.0
2057
+
set-function-length: 1.2.2
748
2058
749
-
/chalk@4.1.2:
750
-
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
751
-
engines: {node: '>=10'}
2059
+
call-bound@1.0.4:
2060
+
dependencies:
2061
+
call-bind-apply-helpers: 1.0.2
2062
+
get-intrinsic: 1.3.0
2063
+
2064
+
callsites@3.1.0: {}
2065
+
2066
+
chalk@4.1.2:
752
2067
dependencies:
753
2068
ansi-styles: 4.3.0
754
2069
supports-color: 7.2.0
755
-
dev: true
756
2070
757
-
/color-convert@2.0.1:
758
-
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
759
-
engines: {node: '>=7.0.0'}
2071
+
color-convert@2.0.1:
760
2072
dependencies:
761
2073
color-name: 1.1.4
762
-
dev: true
763
2074
764
-
/color-name@1.1.4:
765
-
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
766
-
dev: true
2075
+
color-name@1.1.4: {}
767
2076
768
-
/commander@5.1.0:
769
-
resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==}
770
-
engines: {node: '>= 6'}
771
-
dev: false
2077
+
concat-map@0.0.1: {}
772
2078
773
-
/concat-map@0.0.1:
774
-
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
775
-
776
-
/cross-spawn@7.0.3:
777
-
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
778
-
engines: {node: '>= 8'}
2079
+
cross-spawn@7.0.6:
779
2080
dependencies:
780
2081
path-key: 3.1.1
781
2082
shebang-command: 2.0.0
782
2083
which: 2.0.2
783
-
dev: true
784
2084
785
-
/csstype@3.1.2:
786
-
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
787
-
dev: false
2085
+
csstype@3.1.3: {}
788
2086
789
-
/debug@4.3.4:
790
-
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
791
-
engines: {node: '>=6.0'}
792
-
peerDependencies:
793
-
supports-color: '*'
794
-
peerDependenciesMeta:
795
-
supports-color:
796
-
optional: true
2087
+
data-view-buffer@1.0.2:
797
2088
dependencies:
798
-
ms: 2.1.2
799
-
dev: true
2089
+
call-bound: 1.0.4
2090
+
es-errors: 1.3.0
2091
+
is-data-view: 1.0.2
800
2092
801
-
/deep-is@0.1.4:
802
-
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
803
-
dev: true
2093
+
data-view-byte-length@1.0.2:
2094
+
dependencies:
2095
+
call-bound: 1.0.4
2096
+
es-errors: 1.3.0
2097
+
is-data-view: 1.0.2
804
2098
805
-
/default-browser-id@3.0.0:
806
-
resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==}
807
-
engines: {node: '>=12'}
2099
+
data-view-byte-offset@1.0.1:
808
2100
dependencies:
809
-
bplist-parser: 0.2.0
810
-
untildify: 4.0.0
811
-
dev: true
2101
+
call-bound: 1.0.4
2102
+
es-errors: 1.3.0
2103
+
is-data-view: 1.0.2
812
2104
813
-
/default-browser@4.0.0:
814
-
resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==}
815
-
engines: {node: '>=14.16'}
2105
+
debug@4.4.0:
816
2106
dependencies:
817
-
bundle-name: 3.0.0
818
-
default-browser-id: 3.0.0
819
-
execa: 7.2.0
820
-
titleize: 3.0.0
821
-
dev: true
2107
+
ms: 2.1.3
2108
+
2109
+
deep-is@0.1.4: {}
822
2110
823
-
/define-data-property@1.1.1:
824
-
resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
825
-
engines: {node: '>= 0.4'}
2111
+
define-data-property@1.1.4:
826
2112
dependencies:
827
-
get-intrinsic: 1.2.2
828
-
gopd: 1.0.1
829
-
has-property-descriptors: 1.0.1
830
-
dev: true
831
-
832
-
/define-lazy-prop@3.0.0:
833
-
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
834
-
engines: {node: '>=12'}
835
-
dev: true
2113
+
es-define-property: 1.0.1
2114
+
es-errors: 1.3.0
2115
+
gopd: 1.2.0
836
2116
837
-
/define-properties@1.2.1:
838
-
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
839
-
engines: {node: '>= 0.4'}
2117
+
define-properties@1.2.1:
840
2118
dependencies:
841
-
define-data-property: 1.1.1
842
-
has-property-descriptors: 1.0.1
2119
+
define-data-property: 1.1.4
2120
+
has-property-descriptors: 1.0.2
843
2121
object-keys: 1.1.1
844
-
dev: true
845
2122
846
-
/dir-glob@3.0.1:
847
-
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
848
-
engines: {node: '>=8'}
849
-
dependencies:
850
-
path-type: 4.0.0
851
-
dev: true
2123
+
defu@6.1.4: {}
852
2124
853
-
/doctrine@2.1.0:
854
-
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
855
-
engines: {node: '>=0.10.0'}
2125
+
destr@2.0.4: {}
2126
+
2127
+
doctrine@2.1.0:
856
2128
dependencies:
857
2129
esutils: 2.0.3
858
-
dev: true
859
2130
860
-
/doctrine@3.0.0:
861
-
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
862
-
engines: {node: '>=6.0.0'}
2131
+
dunder-proto@1.0.1:
863
2132
dependencies:
864
-
esutils: 2.0.3
865
-
dev: true
2133
+
call-bind-apply-helpers: 1.0.2
2134
+
es-errors: 1.3.0
2135
+
gopd: 1.2.0
866
2136
867
-
/es-abstract@1.22.3:
868
-
resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==}
869
-
engines: {node: '>= 0.4'}
2137
+
es-abstract@1.23.9:
870
2138
dependencies:
871
-
array-buffer-byte-length: 1.0.0
872
-
arraybuffer.prototype.slice: 1.0.2
873
-
available-typed-arrays: 1.0.5
874
-
call-bind: 1.0.5
875
-
es-set-tostringtag: 2.0.2
876
-
es-to-primitive: 1.2.1
877
-
function.prototype.name: 1.1.6
878
-
get-intrinsic: 1.2.2
879
-
get-symbol-description: 1.0.0
880
-
globalthis: 1.0.3
881
-
gopd: 1.0.1
882
-
has-property-descriptors: 1.0.1
883
-
has-proto: 1.0.1
884
-
has-symbols: 1.0.3
885
-
hasown: 2.0.0
886
-
internal-slot: 1.0.6
887
-
is-array-buffer: 3.0.2
2139
+
array-buffer-byte-length: 1.0.2
2140
+
arraybuffer.prototype.slice: 1.0.4
2141
+
available-typed-arrays: 1.0.7
2142
+
call-bind: 1.0.8
2143
+
call-bound: 1.0.4
2144
+
data-view-buffer: 1.0.2
2145
+
data-view-byte-length: 1.0.2
2146
+
data-view-byte-offset: 1.0.1
2147
+
es-define-property: 1.0.1
2148
+
es-errors: 1.3.0
2149
+
es-object-atoms: 1.1.1
2150
+
es-set-tostringtag: 2.1.0
2151
+
es-to-primitive: 1.3.0
2152
+
function.prototype.name: 1.1.8
2153
+
get-intrinsic: 1.3.0
2154
+
get-proto: 1.0.1
2155
+
get-symbol-description: 1.1.0
2156
+
globalthis: 1.0.4
2157
+
gopd: 1.2.0
2158
+
has-property-descriptors: 1.0.2
2159
+
has-proto: 1.2.0
2160
+
has-symbols: 1.1.0
2161
+
hasown: 2.0.2
2162
+
internal-slot: 1.1.0
2163
+
is-array-buffer: 3.0.5
888
2164
is-callable: 1.2.7
889
-
is-negative-zero: 2.0.2
890
-
is-regex: 1.1.4
891
-
is-shared-array-buffer: 1.0.2
892
-
is-string: 1.0.7
893
-
is-typed-array: 1.1.12
894
-
is-weakref: 1.0.2
895
-
object-inspect: 1.13.1
2165
+
is-data-view: 1.0.2
2166
+
is-regex: 1.2.1
2167
+
is-shared-array-buffer: 1.0.4
2168
+
is-string: 1.1.1
2169
+
is-typed-array: 1.1.15
2170
+
is-weakref: 1.1.1
2171
+
math-intrinsics: 1.1.0
2172
+
object-inspect: 1.13.4
896
2173
object-keys: 1.1.1
897
-
object.assign: 4.1.5
898
-
regexp.prototype.flags: 1.5.1
899
-
safe-array-concat: 1.0.1
900
-
safe-regex-test: 1.0.0
901
-
string.prototype.trim: 1.2.8
902
-
string.prototype.trimend: 1.0.7
903
-
string.prototype.trimstart: 1.0.7
904
-
typed-array-buffer: 1.0.0
905
-
typed-array-byte-length: 1.0.0
906
-
typed-array-byte-offset: 1.0.0
907
-
typed-array-length: 1.0.4
908
-
unbox-primitive: 1.0.2
909
-
which-typed-array: 1.1.13
910
-
dev: true
2174
+
object.assign: 4.1.7
2175
+
own-keys: 1.0.1
2176
+
regexp.prototype.flags: 1.5.4
2177
+
safe-array-concat: 1.1.3
2178
+
safe-push-apply: 1.0.0
2179
+
safe-regex-test: 1.1.0
2180
+
set-proto: 1.0.0
2181
+
string.prototype.trim: 1.2.10
2182
+
string.prototype.trimend: 1.0.9
2183
+
string.prototype.trimstart: 1.0.8
2184
+
typed-array-buffer: 1.0.3
2185
+
typed-array-byte-length: 1.0.3
2186
+
typed-array-byte-offset: 1.0.4
2187
+
typed-array-length: 1.0.7
2188
+
unbox-primitive: 1.1.0
2189
+
which-typed-array: 1.1.19
911
2190
912
-
/es-iterator-helpers@1.0.15:
913
-
resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==}
2191
+
es-define-property@1.0.1: {}
2192
+
2193
+
es-errors@1.3.0: {}
2194
+
2195
+
es-iterator-helpers@1.2.1:
914
2196
dependencies:
915
-
asynciterator.prototype: 1.0.0
916
-
call-bind: 1.0.5
2197
+
call-bind: 1.0.8
2198
+
call-bound: 1.0.4
917
2199
define-properties: 1.2.1
918
-
es-abstract: 1.22.3
919
-
es-set-tostringtag: 2.0.2
2200
+
es-abstract: 1.23.9
2201
+
es-errors: 1.3.0
2202
+
es-set-tostringtag: 2.1.0
920
2203
function-bind: 1.1.2
921
-
get-intrinsic: 1.2.2
922
-
globalthis: 1.0.3
923
-
has-property-descriptors: 1.0.1
924
-
has-proto: 1.0.1
925
-
has-symbols: 1.0.3
926
-
internal-slot: 1.0.6
927
-
iterator.prototype: 1.1.2
928
-
safe-array-concat: 1.0.1
929
-
dev: true
2204
+
get-intrinsic: 1.3.0
2205
+
globalthis: 1.0.4
2206
+
gopd: 1.2.0
2207
+
has-property-descriptors: 1.0.2
2208
+
has-proto: 1.2.0
2209
+
has-symbols: 1.1.0
2210
+
internal-slot: 1.1.0
2211
+
iterator.prototype: 1.1.5
2212
+
safe-array-concat: 1.1.3
930
2213
931
-
/es-set-tostringtag@2.0.2:
932
-
resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==}
933
-
engines: {node: '>= 0.4'}
2214
+
es-object-atoms@1.1.1:
934
2215
dependencies:
935
-
get-intrinsic: 1.2.2
936
-
has-tostringtag: 1.0.0
937
-
hasown: 2.0.0
938
-
dev: true
2216
+
es-errors: 1.3.0
939
2217
940
-
/es-shim-unscopables@1.0.2:
941
-
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
2218
+
es-set-tostringtag@2.1.0:
942
2219
dependencies:
943
-
hasown: 2.0.0
944
-
dev: true
2220
+
es-errors: 1.3.0
2221
+
get-intrinsic: 1.3.0
2222
+
has-tostringtag: 1.0.2
2223
+
hasown: 2.0.2
945
2224
946
-
/es-to-primitive@1.2.1:
947
-
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
948
-
engines: {node: '>= 0.4'}
2225
+
es-shim-unscopables@1.1.0:
2226
+
dependencies:
2227
+
hasown: 2.0.2
2228
+
2229
+
es-to-primitive@1.3.0:
949
2230
dependencies:
950
2231
is-callable: 1.2.7
951
-
is-date-object: 1.0.5
952
-
is-symbol: 1.0.4
953
-
dev: true
2232
+
is-date-object: 1.1.0
2233
+
is-symbol: 1.1.1
954
2234
955
-
/esbuild-copy-static-files@0.1.0:
956
-
resolution: {integrity: sha512-KlpmYqANA1t2nZavEdItfcOjJC6wbHA21v35HJWN32DddGTWKNNGDKljUzbCPojmpD+wAw8/DXr5abJ4jFCE0w==}
957
-
dev: true
2235
+
esbuild-copy-static-files@0.1.0: {}
958
2236
959
-
/esbuild@0.19.3:
960
-
resolution: {integrity: sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==}
961
-
engines: {node: '>=12'}
962
-
hasBin: true
963
-
requiresBuild: true
2237
+
esbuild@0.19.3:
964
2238
optionalDependencies:
965
2239
'@esbuild/android-arm': 0.19.3
966
2240
'@esbuild/android-arm64': 0.19.3
···
984
2258
'@esbuild/win32-arm64': 0.19.3
985
2259
'@esbuild/win32-ia32': 0.19.3
986
2260
'@esbuild/win32-x64': 0.19.3
987
-
dev: true
988
2261
989
-
/escape-string-regexp@4.0.0:
990
-
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
991
-
engines: {node: '>=10'}
992
-
dev: true
2262
+
escape-string-regexp@4.0.0: {}
993
2263
994
-
/eslint-config-prettier@9.1.0(eslint@8.55.0):
995
-
resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
996
-
hasBin: true
997
-
peerDependencies:
998
-
eslint: '>=7.0.0'
2264
+
eslint-config-prettier@9.1.0(eslint@9.23.0(jiti@2.4.2)):
999
2265
dependencies:
1000
-
eslint: 8.55.0
1001
-
dev: true
2266
+
eslint: 9.23.0(jiti@2.4.2)
1002
2267
1003
-
/eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.0):
1004
-
resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==}
1005
-
engines: {node: ^14.18.0 || >=16.0.0}
1006
-
peerDependencies:
1007
-
'@types/eslint': '>=8.0.0'
1008
-
eslint: '>=8.0.0'
1009
-
eslint-config-prettier: '*'
1010
-
prettier: '>=3.0.0'
1011
-
peerDependenciesMeta:
1012
-
'@types/eslint':
1013
-
optional: true
1014
-
eslint-config-prettier:
1015
-
optional: true
2268
+
eslint-plugin-prettier@5.2.6(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.23.0(jiti@2.4.2)))(eslint@9.23.0(jiti@2.4.2))(prettier@3.1.0):
1016
2269
dependencies:
1017
-
eslint: 8.55.0
1018
-
eslint-config-prettier: 9.1.0(eslint@8.55.0)
2270
+
eslint: 9.23.0(jiti@2.4.2)
1019
2271
prettier: 3.1.0
1020
2272
prettier-linter-helpers: 1.0.0
1021
-
synckit: 0.8.6
1022
-
dev: true
2273
+
synckit: 0.11.1
2274
+
optionalDependencies:
2275
+
'@types/eslint': 9.6.1
2276
+
eslint-config-prettier: 9.1.0(eslint@9.23.0(jiti@2.4.2))
1023
2277
1024
-
/eslint-plugin-react@7.33.2(eslint@8.55.0):
1025
-
resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==}
1026
-
engines: {node: '>=4'}
1027
-
peerDependencies:
1028
-
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
2278
+
eslint-plugin-react@7.37.5(eslint@9.23.0(jiti@2.4.2)):
1029
2279
dependencies:
1030
-
array-includes: 3.1.7
1031
-
array.prototype.flatmap: 1.3.2
1032
-
array.prototype.tosorted: 1.1.2
2280
+
array-includes: 3.1.8
2281
+
array.prototype.findlast: 1.2.5
2282
+
array.prototype.flatmap: 1.3.3
2283
+
array.prototype.tosorted: 1.1.4
1033
2284
doctrine: 2.1.0
1034
-
es-iterator-helpers: 1.0.15
1035
-
eslint: 8.55.0
2285
+
es-iterator-helpers: 1.2.1
2286
+
eslint: 9.23.0(jiti@2.4.2)
1036
2287
estraverse: 5.3.0
2288
+
hasown: 2.0.2
1037
2289
jsx-ast-utils: 3.3.5
1038
2290
minimatch: 3.1.2
1039
-
object.entries: 1.1.7
1040
-
object.fromentries: 2.0.7
1041
-
object.hasown: 1.1.3
1042
-
object.values: 1.1.7
2291
+
object.entries: 1.1.9
2292
+
object.fromentries: 2.0.8
2293
+
object.values: 1.2.1
1043
2294
prop-types: 15.8.1
1044
2295
resolve: 2.0.0-next.5
1045
2296
semver: 6.3.1
1046
-
string.prototype.matchall: 4.0.10
1047
-
dev: true
2297
+
string.prototype.matchall: 4.0.12
2298
+
string.prototype.repeat: 1.0.0
1048
2299
1049
-
/eslint-scope@7.2.2:
1050
-
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
1051
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
2300
+
eslint-scope@8.3.0:
1052
2301
dependencies:
1053
2302
esrecurse: 4.3.0
1054
2303
estraverse: 5.3.0
1055
-
dev: true
1056
2304
1057
-
/eslint-visitor-keys@3.4.3:
1058
-
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
1059
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
1060
-
dev: true
2305
+
eslint-visitor-keys@3.4.3: {}
1061
2306
1062
-
/eslint@8.55.0:
1063
-
resolution: {integrity: sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==}
1064
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
1065
-
hasBin: true
2307
+
eslint-visitor-keys@4.2.0: {}
2308
+
2309
+
eslint@9.23.0(jiti@2.4.2):
1066
2310
dependencies:
1067
-
'@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
1068
-
'@eslint-community/regexpp': 4.10.0
1069
-
'@eslint/eslintrc': 2.1.4
1070
-
'@eslint/js': 8.55.0
1071
-
'@humanwhocodes/config-array': 0.11.13
2311
+
'@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
2312
+
'@eslint-community/regexpp': 4.12.1
2313
+
'@eslint/config-array': 0.19.2
2314
+
'@eslint/config-helpers': 0.2.1
2315
+
'@eslint/core': 0.12.0
2316
+
'@eslint/eslintrc': 3.3.1
2317
+
'@eslint/js': 9.23.0
2318
+
'@eslint/plugin-kit': 0.2.8
2319
+
'@humanfs/node': 0.16.6
1072
2320
'@humanwhocodes/module-importer': 1.0.1
1073
-
'@nodelib/fs.walk': 1.2.8
1074
-
'@ungap/structured-clone': 1.2.0
2321
+
'@humanwhocodes/retry': 0.4.2
2322
+
'@types/estree': 1.0.6
2323
+
'@types/json-schema': 7.0.15
1075
2324
ajv: 6.12.6
1076
2325
chalk: 4.1.2
1077
-
cross-spawn: 7.0.3
1078
-
debug: 4.3.4
1079
-
doctrine: 3.0.0
2326
+
cross-spawn: 7.0.6
2327
+
debug: 4.4.0
1080
2328
escape-string-regexp: 4.0.0
1081
-
eslint-scope: 7.2.2
1082
-
eslint-visitor-keys: 3.4.3
1083
-
espree: 9.6.1
1084
-
esquery: 1.5.0
2329
+
eslint-scope: 8.3.0
2330
+
eslint-visitor-keys: 4.2.0
2331
+
espree: 10.3.0
2332
+
esquery: 1.6.0
1085
2333
esutils: 2.0.3
1086
2334
fast-deep-equal: 3.1.3
1087
-
file-entry-cache: 6.0.1
2335
+
file-entry-cache: 8.0.0
1088
2336
find-up: 5.0.0
1089
2337
glob-parent: 6.0.2
1090
-
globals: 13.23.0
1091
-
graphemer: 1.4.0
1092
-
ignore: 5.3.0
2338
+
ignore: 5.3.2
1093
2339
imurmurhash: 0.1.4
1094
2340
is-glob: 4.0.3
1095
-
is-path-inside: 3.0.3
1096
-
js-yaml: 4.1.0
1097
2341
json-stable-stringify-without-jsonify: 1.0.1
1098
-
levn: 0.4.1
1099
2342
lodash.merge: 4.6.2
1100
2343
minimatch: 3.1.2
1101
2344
natural-compare: 1.4.0
1102
2345
optionator: 0.9.3
1103
-
strip-ansi: 6.0.1
1104
-
text-table: 0.2.0
2346
+
optionalDependencies:
2347
+
jiti: 2.4.2
1105
2348
transitivePeerDependencies:
1106
2349
- supports-color
1107
-
dev: true
1108
2350
1109
-
/espree@9.6.1:
1110
-
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
1111
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
2351
+
espree@10.3.0:
1112
2352
dependencies:
1113
-
acorn: 8.11.2
1114
-
acorn-jsx: 5.3.2(acorn@8.11.2)
1115
-
eslint-visitor-keys: 3.4.3
1116
-
dev: true
2353
+
acorn: 8.14.1
2354
+
acorn-jsx: 5.3.2(acorn@8.14.1)
2355
+
eslint-visitor-keys: 4.2.0
1117
2356
1118
-
/esquery@1.5.0:
1119
-
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
1120
-
engines: {node: '>=0.10'}
2357
+
esquery@1.6.0:
1121
2358
dependencies:
1122
2359
estraverse: 5.3.0
1123
-
dev: true
1124
2360
1125
-
/esrecurse@4.3.0:
1126
-
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
1127
-
engines: {node: '>=4.0'}
2361
+
esrecurse@4.3.0:
1128
2362
dependencies:
1129
2363
estraverse: 5.3.0
1130
-
dev: true
1131
2364
1132
-
/estraverse@5.3.0:
1133
-
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
1134
-
engines: {node: '>=4.0'}
1135
-
dev: true
2365
+
estraverse@5.3.0: {}
1136
2366
1137
-
/esutils@2.0.3:
1138
-
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
1139
-
engines: {node: '>=0.10.0'}
1140
-
dev: true
1141
-
1142
-
/execa@5.1.1:
1143
-
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
1144
-
engines: {node: '>=10'}
2367
+
estree-toolkit@1.7.8:
1145
2368
dependencies:
1146
-
cross-spawn: 7.0.3
1147
-
get-stream: 6.0.1
1148
-
human-signals: 2.1.0
1149
-
is-stream: 2.0.1
1150
-
merge-stream: 2.0.0
1151
-
npm-run-path: 4.0.1
1152
-
onetime: 5.1.2
1153
-
signal-exit: 3.0.7
1154
-
strip-final-newline: 2.0.0
1155
-
dev: true
2369
+
'@types/estree': 1.0.6
2370
+
'@types/estree-jsx': 1.0.5
1156
2371
1157
-
/execa@7.2.0:
1158
-
resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==}
1159
-
engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0}
1160
-
dependencies:
1161
-
cross-spawn: 7.0.3
1162
-
get-stream: 6.0.1
1163
-
human-signals: 4.3.1
1164
-
is-stream: 3.0.0
1165
-
merge-stream: 2.0.0
1166
-
npm-run-path: 5.1.0
1167
-
onetime: 6.0.0
1168
-
signal-exit: 3.0.7
1169
-
strip-final-newline: 3.0.0
1170
-
dev: true
2372
+
esutils@2.0.3: {}
1171
2373
1172
-
/fast-deep-equal@3.1.3:
1173
-
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
1174
-
dev: true
2374
+
event-target-shim@5.0.1: {}
1175
2375
1176
-
/fast-diff@1.3.0:
1177
-
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
1178
-
dev: true
2376
+
eventemitter3@5.0.1: {}
1179
2377
1180
-
/fast-glob@3.3.2:
1181
-
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
1182
-
engines: {node: '>=8.6.0'}
2378
+
events@3.3.0: {}
2379
+
2380
+
fast-deep-equal@3.1.3: {}
2381
+
2382
+
fast-diff@1.3.0: {}
2383
+
2384
+
fast-glob@3.3.2:
1183
2385
dependencies:
1184
2386
'@nodelib/fs.stat': 2.0.5
1185
2387
'@nodelib/fs.walk': 1.2.8
1186
2388
glob-parent: 5.1.2
1187
2389
merge2: 1.4.1
1188
-
micromatch: 4.0.5
1189
-
dev: true
2390
+
micromatch: 4.0.8
1190
2391
1191
-
/fast-json-stable-stringify@2.1.0:
1192
-
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
1193
-
dev: true
2392
+
fast-json-stable-stringify@2.1.0: {}
1194
2393
1195
-
/fast-levenshtein@2.0.6:
1196
-
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
1197
-
dev: true
2394
+
fast-levenshtein@2.0.6: {}
1198
2395
1199
-
/fastq@1.15.0:
1200
-
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
2396
+
fastq@1.17.1:
1201
2397
dependencies:
1202
2398
reusify: 1.0.4
1203
-
dev: true
1204
2399
1205
-
/file-entry-cache@6.0.1:
1206
-
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
1207
-
engines: {node: ^10.12.0 || >=12.0.0}
2400
+
fdir@6.4.3(picomatch@4.0.2):
2401
+
optionalDependencies:
2402
+
picomatch: 4.0.2
2403
+
2404
+
file-entry-cache@8.0.0:
1208
2405
dependencies:
1209
-
flat-cache: 3.2.0
1210
-
dev: true
2406
+
flat-cache: 4.0.1
1211
2407
1212
-
/fill-range@7.0.1:
1213
-
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
1214
-
engines: {node: '>=8'}
2408
+
fill-range@7.1.1:
1215
2409
dependencies:
1216
2410
to-regex-range: 5.0.1
1217
-
dev: true
1218
2411
1219
-
/find-up@5.0.0:
1220
-
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
1221
-
engines: {node: '>=10'}
2412
+
find-up-simple@1.0.1: {}
2413
+
2414
+
find-up@5.0.0:
1222
2415
dependencies:
1223
2416
locate-path: 6.0.0
1224
2417
path-exists: 4.0.0
1225
-
dev: true
1226
2418
1227
-
/flat-cache@3.2.0:
1228
-
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
1229
-
engines: {node: ^10.12.0 || >=12.0.0}
2419
+
flat-cache@4.0.1:
1230
2420
dependencies:
1231
2421
flatted: 3.2.9
1232
2422
keyv: 4.5.4
1233
-
rimraf: 3.0.2
1234
-
dev: true
1235
2423
1236
-
/flatted@3.2.9:
1237
-
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
1238
-
dev: true
2424
+
flatted@3.2.9: {}
1239
2425
1240
-
/for-each@0.3.3:
1241
-
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
2426
+
for-each@0.3.5:
1242
2427
dependencies:
1243
2428
is-callable: 1.2.7
1244
-
dev: true
1245
2429
1246
-
/fs.realpath@1.0.0:
1247
-
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
2430
+
function-bind@1.1.2: {}
1248
2431
1249
-
/function-bind@1.1.2:
1250
-
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
1251
-
dev: true
1252
-
1253
-
/function.prototype.name@1.1.6:
1254
-
resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
1255
-
engines: {node: '>= 0.4'}
2432
+
function.prototype.name@1.1.8:
1256
2433
dependencies:
1257
-
call-bind: 1.0.5
2434
+
call-bind: 1.0.8
2435
+
call-bound: 1.0.4
1258
2436
define-properties: 1.2.1
1259
-
es-abstract: 1.22.3
1260
2437
functions-have-names: 1.2.3
1261
-
dev: true
2438
+
hasown: 2.0.2
2439
+
is-callable: 1.2.7
1262
2440
1263
-
/functions-have-names@1.2.3:
1264
-
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
1265
-
dev: true
2441
+
functions-have-names@1.2.3: {}
2442
+
2443
+
fzf@0.5.2: {}
1266
2444
1267
-
/get-intrinsic@1.2.2:
1268
-
resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
2445
+
get-intrinsic@1.3.0:
1269
2446
dependencies:
2447
+
call-bind-apply-helpers: 1.0.2
2448
+
es-define-property: 1.0.1
2449
+
es-errors: 1.3.0
2450
+
es-object-atoms: 1.1.1
1270
2451
function-bind: 1.1.2
1271
-
has-proto: 1.0.1
1272
-
has-symbols: 1.0.3
1273
-
hasown: 2.0.0
1274
-
dev: true
1275
-
1276
-
/get-stream@6.0.1:
1277
-
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
1278
-
engines: {node: '>=10'}
1279
-
dev: true
2452
+
get-proto: 1.0.1
2453
+
gopd: 1.2.0
2454
+
has-symbols: 1.1.0
2455
+
hasown: 2.0.2
2456
+
math-intrinsics: 1.1.0
1280
2457
1281
-
/get-symbol-description@1.0.0:
1282
-
resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
1283
-
engines: {node: '>= 0.4'}
2458
+
get-proto@1.0.1:
1284
2459
dependencies:
1285
-
call-bind: 1.0.5
1286
-
get-intrinsic: 1.2.2
1287
-
dev: true
2460
+
dunder-proto: 1.0.1
2461
+
es-object-atoms: 1.1.1
1288
2462
1289
-
/glob-parent@5.1.2:
1290
-
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
1291
-
engines: {node: '>= 6'}
2463
+
get-symbol-description@1.1.0:
1292
2464
dependencies:
1293
-
is-glob: 4.0.3
1294
-
dev: true
2465
+
call-bound: 1.0.4
2466
+
es-errors: 1.3.0
2467
+
get-intrinsic: 1.3.0
1295
2468
1296
-
/glob-parent@6.0.2:
1297
-
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
1298
-
engines: {node: '>=10.13.0'}
2469
+
glob-parent@5.1.2:
1299
2470
dependencies:
1300
2471
is-glob: 4.0.3
1301
-
dev: true
1302
2472
1303
-
/glob@7.2.3:
1304
-
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
2473
+
glob-parent@6.0.2:
1305
2474
dependencies:
1306
-
fs.realpath: 1.0.0
1307
-
inflight: 1.0.6
1308
-
inherits: 2.0.4
1309
-
minimatch: 3.1.2
1310
-
once: 1.4.0
1311
-
path-is-absolute: 1.0.1
2475
+
is-glob: 4.0.3
1312
2476
1313
-
/globals@13.23.0:
1314
-
resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==}
1315
-
engines: {node: '>=8'}
1316
-
dependencies:
1317
-
type-fest: 0.20.2
1318
-
dev: true
2477
+
globals@14.0.0: {}
1319
2478
1320
-
/globalthis@1.0.3:
1321
-
resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
1322
-
engines: {node: '>= 0.4'}
2479
+
globalthis@1.0.4:
1323
2480
dependencies:
1324
2481
define-properties: 1.2.1
1325
-
dev: true
2482
+
gopd: 1.2.0
1326
2483
1327
-
/globby@11.1.0:
1328
-
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
1329
-
engines: {node: '>=10'}
1330
-
dependencies:
1331
-
array-union: 2.1.0
1332
-
dir-glob: 3.0.1
1333
-
fast-glob: 3.3.2
1334
-
ignore: 5.3.0
1335
-
merge2: 1.4.1
1336
-
slash: 3.0.0
1337
-
dev: true
2484
+
gopd@1.2.0: {}
1338
2485
1339
-
/gopd@1.0.1:
1340
-
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
1341
-
dependencies:
1342
-
get-intrinsic: 1.2.2
1343
-
dev: true
2486
+
graphemer@1.4.0: {}
1344
2487
1345
-
/graphemer@1.4.0:
1346
-
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
1347
-
dev: true
2488
+
has-bigints@1.1.0: {}
1348
2489
1349
-
/has-bigints@1.0.2:
1350
-
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
1351
-
dev: true
2490
+
has-flag@4.0.0: {}
1352
2491
1353
-
/has-flag@4.0.0:
1354
-
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
1355
-
engines: {node: '>=8'}
1356
-
dev: true
2492
+
has-property-descriptors@1.0.2:
2493
+
dependencies:
2494
+
es-define-property: 1.0.1
1357
2495
1358
-
/has-property-descriptors@1.0.1:
1359
-
resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==}
2496
+
has-proto@1.2.0:
1360
2497
dependencies:
1361
-
get-intrinsic: 1.2.2
1362
-
dev: true
2498
+
dunder-proto: 1.0.1
1363
2499
1364
-
/has-proto@1.0.1:
1365
-
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
1366
-
engines: {node: '>= 0.4'}
1367
-
dev: true
2500
+
has-symbols@1.1.0: {}
1368
2501
1369
-
/has-symbols@1.0.3:
1370
-
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
1371
-
engines: {node: '>= 0.4'}
1372
-
dev: true
1373
-
1374
-
/has-tostringtag@1.0.0:
1375
-
resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
1376
-
engines: {node: '>= 0.4'}
2502
+
has-tostringtag@1.0.2:
1377
2503
dependencies:
1378
-
has-symbols: 1.0.3
1379
-
dev: true
2504
+
has-symbols: 1.1.0
1380
2505
1381
-
/hasown@2.0.0:
1382
-
resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
1383
-
engines: {node: '>= 0.4'}
2506
+
hasown@2.0.2:
1384
2507
dependencies:
1385
2508
function-bind: 1.1.2
1386
-
dev: true
1387
2509
1388
-
/human-signals@2.1.0:
1389
-
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
1390
-
engines: {node: '>=10.17.0'}
1391
-
dev: true
2510
+
husky@8.0.3: {}
1392
2511
1393
-
/human-signals@4.3.1:
1394
-
resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==}
1395
-
engines: {node: '>=14.18.0'}
1396
-
dev: true
1397
-
1398
-
/husky@8.0.3:
1399
-
resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==}
1400
-
engines: {node: '>=14'}
1401
-
hasBin: true
1402
-
dev: true
2512
+
ieee754@1.2.1: {}
1403
2513
1404
-
/ignore@5.3.0:
1405
-
resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==}
1406
-
engines: {node: '>= 4'}
1407
-
dev: true
2514
+
ignore@5.3.2: {}
1408
2515
1409
-
/import-fresh@3.3.0:
1410
-
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
1411
-
engines: {node: '>=6'}
2516
+
import-fresh@3.3.0:
1412
2517
dependencies:
1413
2518
parent-module: 1.0.1
1414
2519
resolve-from: 4.0.0
1415
-
dev: true
1416
2520
1417
-
/imurmurhash@0.1.4:
1418
-
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
1419
-
engines: {node: '>=0.8.19'}
1420
-
dev: true
2521
+
imurmurhash@0.1.4: {}
1421
2522
1422
-
/inflight@1.0.6:
1423
-
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
2523
+
internal-slot@1.1.0:
1424
2524
dependencies:
1425
-
once: 1.4.0
1426
-
wrappy: 1.0.2
2525
+
es-errors: 1.3.0
2526
+
hasown: 2.0.2
2527
+
side-channel: 1.1.0
1427
2528
1428
-
/inherits@2.0.4:
1429
-
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
2529
+
is-array-buffer@3.0.5:
2530
+
dependencies:
2531
+
call-bind: 1.0.8
2532
+
call-bound: 1.0.4
2533
+
get-intrinsic: 1.3.0
1430
2534
1431
-
/internal-slot@1.0.6:
1432
-
resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==}
1433
-
engines: {node: '>= 0.4'}
2535
+
is-async-function@2.1.1:
1434
2536
dependencies:
1435
-
get-intrinsic: 1.2.2
1436
-
hasown: 2.0.0
1437
-
side-channel: 1.0.4
1438
-
dev: true
2537
+
async-function: 1.0.0
2538
+
call-bound: 1.0.4
2539
+
get-proto: 1.0.1
2540
+
has-tostringtag: 1.0.2
2541
+
safe-regex-test: 1.1.0
1439
2542
1440
-
/is-array-buffer@3.0.2:
1441
-
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
2543
+
is-bigint@1.1.0:
1442
2544
dependencies:
1443
-
call-bind: 1.0.5
1444
-
get-intrinsic: 1.2.2
1445
-
is-typed-array: 1.1.12
1446
-
dev: true
2545
+
has-bigints: 1.1.0
1447
2546
1448
-
/is-async-function@2.0.0:
1449
-
resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==}
1450
-
engines: {node: '>= 0.4'}
2547
+
is-boolean-object@1.2.2:
1451
2548
dependencies:
1452
-
has-tostringtag: 1.0.0
1453
-
dev: true
2549
+
call-bound: 1.0.4
2550
+
has-tostringtag: 1.0.2
1454
2551
1455
-
/is-bigint@1.0.4:
1456
-
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
1457
-
dependencies:
1458
-
has-bigints: 1.0.2
1459
-
dev: true
2552
+
is-callable@1.2.7: {}
1460
2553
1461
-
/is-boolean-object@1.1.2:
1462
-
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
1463
-
engines: {node: '>= 0.4'}
2554
+
is-core-module@2.16.1:
1464
2555
dependencies:
1465
-
call-bind: 1.0.5
1466
-
has-tostringtag: 1.0.0
1467
-
dev: true
1468
-
1469
-
/is-callable@1.2.7:
1470
-
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
1471
-
engines: {node: '>= 0.4'}
1472
-
dev: true
2556
+
hasown: 2.0.2
1473
2557
1474
-
/is-core-module@2.13.1:
1475
-
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
2558
+
is-data-view@1.0.2:
1476
2559
dependencies:
1477
-
hasown: 2.0.0
1478
-
dev: true
2560
+
call-bound: 1.0.4
2561
+
get-intrinsic: 1.3.0
2562
+
is-typed-array: 1.1.15
1479
2563
1480
-
/is-date-object@1.0.5:
1481
-
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
1482
-
engines: {node: '>= 0.4'}
2564
+
is-date-object@1.1.0:
1483
2565
dependencies:
1484
-
has-tostringtag: 1.0.0
1485
-
dev: true
1486
-
1487
-
/is-docker@2.2.1:
1488
-
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
1489
-
engines: {node: '>=8'}
1490
-
hasBin: true
1491
-
dev: true
2566
+
call-bound: 1.0.4
2567
+
has-tostringtag: 1.0.2
1492
2568
1493
-
/is-docker@3.0.0:
1494
-
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
1495
-
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
1496
-
hasBin: true
1497
-
dev: true
2569
+
is-extglob@2.1.1: {}
1498
2570
1499
-
/is-extglob@2.1.1:
1500
-
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
1501
-
engines: {node: '>=0.10.0'}
1502
-
dev: true
1503
-
1504
-
/is-finalizationregistry@1.0.2:
1505
-
resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==}
2571
+
is-finalizationregistry@1.1.1:
1506
2572
dependencies:
1507
-
call-bind: 1.0.5
1508
-
dev: true
2573
+
call-bound: 1.0.4
1509
2574
1510
-
/is-generator-function@1.0.10:
1511
-
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
1512
-
engines: {node: '>= 0.4'}
2575
+
is-generator-function@1.1.0:
1513
2576
dependencies:
1514
-
has-tostringtag: 1.0.0
1515
-
dev: true
2577
+
call-bound: 1.0.4
2578
+
get-proto: 1.0.1
2579
+
has-tostringtag: 1.0.2
2580
+
safe-regex-test: 1.1.0
1516
2581
1517
-
/is-glob@4.0.3:
1518
-
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
1519
-
engines: {node: '>=0.10.0'}
2582
+
is-glob@4.0.3:
1520
2583
dependencies:
1521
2584
is-extglob: 2.1.1
1522
-
dev: true
1523
2585
1524
-
/is-inside-container@1.0.0:
1525
-
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
1526
-
engines: {node: '>=14.16'}
1527
-
hasBin: true
1528
-
dependencies:
1529
-
is-docker: 3.0.0
1530
-
dev: true
1531
-
1532
-
/is-map@2.0.2:
1533
-
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
1534
-
dev: true
1535
-
1536
-
/is-negative-zero@2.0.2:
1537
-
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
1538
-
engines: {node: '>= 0.4'}
1539
-
dev: true
2586
+
is-map@2.0.3: {}
1540
2587
1541
-
/is-number-object@1.0.7:
1542
-
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
1543
-
engines: {node: '>= 0.4'}
2588
+
is-number-object@1.1.1:
1544
2589
dependencies:
1545
-
has-tostringtag: 1.0.0
1546
-
dev: true
1547
-
1548
-
/is-number@7.0.0:
1549
-
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
1550
-
engines: {node: '>=0.12.0'}
1551
-
dev: true
2590
+
call-bound: 1.0.4
2591
+
has-tostringtag: 1.0.2
1552
2592
1553
-
/is-path-inside@3.0.3:
1554
-
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
1555
-
engines: {node: '>=8'}
1556
-
dev: true
2593
+
is-number@7.0.0: {}
1557
2594
1558
-
/is-regex@1.1.4:
1559
-
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
1560
-
engines: {node: '>= 0.4'}
2595
+
is-regex@1.2.1:
1561
2596
dependencies:
1562
-
call-bind: 1.0.5
1563
-
has-tostringtag: 1.0.0
1564
-
dev: true
2597
+
call-bound: 1.0.4
2598
+
gopd: 1.2.0
2599
+
has-tostringtag: 1.0.2
2600
+
hasown: 2.0.2
1565
2601
1566
-
/is-set@2.0.2:
1567
-
resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
1568
-
dev: true
2602
+
is-set@2.0.3: {}
1569
2603
1570
-
/is-shared-array-buffer@1.0.2:
1571
-
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
2604
+
is-shared-array-buffer@1.0.4:
1572
2605
dependencies:
1573
-
call-bind: 1.0.5
1574
-
dev: true
1575
-
1576
-
/is-stream@2.0.1:
1577
-
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
1578
-
engines: {node: '>=8'}
1579
-
dev: true
1580
-
1581
-
/is-stream@3.0.0:
1582
-
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
1583
-
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
1584
-
dev: true
2606
+
call-bound: 1.0.4
1585
2607
1586
-
/is-string@1.0.7:
1587
-
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
1588
-
engines: {node: '>= 0.4'}
2608
+
is-string@1.1.1:
1589
2609
dependencies:
1590
-
has-tostringtag: 1.0.0
1591
-
dev: true
2610
+
call-bound: 1.0.4
2611
+
has-tostringtag: 1.0.2
1592
2612
1593
-
/is-symbol@1.0.4:
1594
-
resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
1595
-
engines: {node: '>= 0.4'}
2613
+
is-symbol@1.1.1:
1596
2614
dependencies:
1597
-
has-symbols: 1.0.3
1598
-
dev: true
2615
+
call-bound: 1.0.4
2616
+
has-symbols: 1.1.0
2617
+
safe-regex-test: 1.1.0
1599
2618
1600
-
/is-typed-array@1.1.12:
1601
-
resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==}
1602
-
engines: {node: '>= 0.4'}
2619
+
is-typed-array@1.1.15:
1603
2620
dependencies:
1604
-
which-typed-array: 1.1.13
1605
-
dev: true
2621
+
which-typed-array: 1.1.19
1606
2622
1607
-
/is-weakmap@2.0.1:
1608
-
resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
1609
-
dev: true
2623
+
is-weakmap@2.0.2: {}
1610
2624
1611
-
/is-weakref@1.0.2:
1612
-
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
2625
+
is-weakref@1.1.1:
1613
2626
dependencies:
1614
-
call-bind: 1.0.5
1615
-
dev: true
2627
+
call-bound: 1.0.4
1616
2628
1617
-
/is-weakset@2.0.2:
1618
-
resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
2629
+
is-weakset@2.0.4:
1619
2630
dependencies:
1620
-
call-bind: 1.0.5
1621
-
get-intrinsic: 1.2.2
1622
-
dev: true
2631
+
call-bound: 1.0.4
2632
+
get-intrinsic: 1.3.0
1623
2633
1624
-
/is-wsl@2.2.0:
1625
-
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
1626
-
engines: {node: '>=8'}
1627
-
dependencies:
1628
-
is-docker: 2.2.1
1629
-
dev: true
2634
+
isarray@2.0.5: {}
1630
2635
1631
-
/isarray@2.0.5:
1632
-
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
1633
-
dev: true
2636
+
isexe@2.0.0: {}
1634
2637
1635
-
/isexe@2.0.0:
1636
-
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
1637
-
dev: true
1638
-
1639
-
/iterator.prototype@1.1.2:
1640
-
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
2638
+
iterator.prototype@1.1.5:
1641
2639
dependencies:
1642
-
define-properties: 1.2.1
1643
-
get-intrinsic: 1.2.2
1644
-
has-symbols: 1.0.3
1645
-
reflect.getprototypeof: 1.0.4
1646
-
set-function-name: 2.0.1
1647
-
dev: true
2640
+
define-data-property: 1.1.4
2641
+
es-object-atoms: 1.1.1
2642
+
get-intrinsic: 1.3.0
2643
+
get-proto: 1.0.1
2644
+
has-symbols: 1.1.0
2645
+
set-function-name: 2.0.2
1648
2646
1649
-
/js-tokens@4.0.0:
1650
-
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
1651
-
dev: true
2647
+
jiti@2.4.2: {}
1652
2648
1653
-
/js-yaml@4.1.0:
1654
-
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
1655
-
hasBin: true
2649
+
js-tokens@4.0.0: {}
2650
+
2651
+
js-yaml@4.1.0:
1656
2652
dependencies:
1657
2653
argparse: 2.0.1
1658
-
dev: true
1659
2654
1660
-
/json-buffer@3.0.1:
1661
-
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
1662
-
dev: true
2655
+
json-buffer@3.0.1: {}
1663
2656
1664
-
/json-schema-traverse@0.4.1:
1665
-
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
1666
-
dev: true
2657
+
json-schema-traverse@0.4.1: {}
1667
2658
1668
-
/json-stable-stringify-without-jsonify@1.0.1:
1669
-
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
1670
-
dev: true
2659
+
json-stable-stringify-without-jsonify@1.0.1: {}
1671
2660
1672
-
/jsx-ast-utils@3.3.5:
1673
-
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
1674
-
engines: {node: '>=4.0'}
2661
+
jsx-ast-utils@3.3.5:
1675
2662
dependencies:
1676
-
array-includes: 3.1.7
1677
-
array.prototype.flat: 1.3.2
1678
-
object.assign: 4.1.5
1679
-
object.values: 1.1.7
1680
-
dev: true
2663
+
array-includes: 3.1.8
2664
+
array.prototype.flat: 1.3.3
2665
+
object.assign: 4.1.7
2666
+
object.values: 1.2.1
1681
2667
1682
-
/keyv@4.5.4:
1683
-
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
2668
+
keyv@4.5.4:
1684
2669
dependencies:
1685
2670
json-buffer: 3.0.1
1686
-
dev: true
1687
2671
1688
-
/levn@0.4.1:
1689
-
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
1690
-
engines: {node: '>= 0.8.0'}
2672
+
levn@0.4.1:
1691
2673
dependencies:
1692
2674
prelude-ls: 1.2.1
1693
2675
type-check: 0.4.0
1694
-
dev: true
1695
2676
1696
-
/locate-path@6.0.0:
1697
-
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
1698
-
engines: {node: '>=10'}
2677
+
locate-path@6.0.0:
1699
2678
dependencies:
1700
2679
p-locate: 5.0.0
1701
-
dev: true
1702
2680
1703
-
/lodash.merge@4.6.2:
1704
-
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
1705
-
dev: true
2681
+
lodash.merge@4.6.2: {}
1706
2682
1707
-
/loose-envify@1.4.0:
1708
-
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
1709
-
hasBin: true
2683
+
loose-envify@1.4.0:
1710
2684
dependencies:
1711
2685
js-tokens: 4.0.0
1712
-
dev: true
1713
2686
1714
-
/lru-cache@6.0.0:
1715
-
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
1716
-
engines: {node: '>=10'}
1717
-
dependencies:
1718
-
yallist: 4.0.0
1719
-
dev: true
2687
+
math-intrinsics@1.1.0: {}
1720
2688
1721
-
/merge-stream@2.0.0:
1722
-
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
1723
-
dev: true
2689
+
merge2@1.4.1: {}
1724
2690
1725
-
/merge2@1.4.1:
1726
-
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
1727
-
engines: {node: '>= 8'}
1728
-
dev: true
2691
+
meriyah@6.0.1: {}
2692
+
2693
+
microdiff@1.5.0: {}
1729
2694
1730
-
/micromatch@4.0.5:
1731
-
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
1732
-
engines: {node: '>=8.6'}
2695
+
micromatch@4.0.8:
1733
2696
dependencies:
1734
-
braces: 3.0.2
2697
+
braces: 3.0.3
1735
2698
picomatch: 2.3.1
1736
-
dev: true
1737
2699
1738
-
/mimic-fn@2.1.0:
1739
-
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
1740
-
engines: {node: '>=6'}
1741
-
dev: true
2700
+
mimic-function@5.0.1: {}
1742
2701
1743
-
/mimic-fn@4.0.0:
1744
-
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
1745
-
engines: {node: '>=12'}
1746
-
dev: true
2702
+
minimatch@3.1.2:
2703
+
dependencies:
2704
+
brace-expansion: 1.1.11
1747
2705
1748
-
/minimatch@3.1.2:
1749
-
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
2706
+
minimatch@9.0.5:
1750
2707
dependencies:
1751
-
brace-expansion: 1.1.11
2708
+
brace-expansion: 2.0.1
1752
2709
1753
-
/ms@2.1.2:
1754
-
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
1755
-
dev: true
2710
+
ms@2.1.3: {}
1756
2711
1757
-
/natural-compare@1.4.0:
1758
-
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
1759
-
dev: true
2712
+
nanotar@0.1.1: {}
1760
2713
1761
-
/npm-run-path@4.0.1:
1762
-
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
1763
-
engines: {node: '>=8'}
1764
-
dependencies:
1765
-
path-key: 3.1.1
1766
-
dev: true
2714
+
natural-compare@1.4.0: {}
1767
2715
1768
-
/npm-run-path@5.1.0:
1769
-
resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
1770
-
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
1771
-
dependencies:
1772
-
path-key: 4.0.0
1773
-
dev: true
2716
+
node-fetch-native@1.6.6: {}
1774
2717
1775
-
/object-assign@4.1.1:
1776
-
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1777
-
engines: {node: '>=0.10.0'}
1778
-
dev: true
2718
+
object-assign@4.1.1: {}
1779
2719
1780
-
/object-inspect@1.13.1:
1781
-
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
1782
-
dev: true
2720
+
object-inspect@1.13.4: {}
1783
2721
1784
-
/object-keys@1.1.1:
1785
-
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
1786
-
engines: {node: '>= 0.4'}
1787
-
dev: true
2722
+
object-keys@1.1.1: {}
1788
2723
1789
-
/object.assign@4.1.5:
1790
-
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
1791
-
engines: {node: '>= 0.4'}
2724
+
object.assign@4.1.7:
1792
2725
dependencies:
1793
-
call-bind: 1.0.5
2726
+
call-bind: 1.0.8
2727
+
call-bound: 1.0.4
1794
2728
define-properties: 1.2.1
1795
-
has-symbols: 1.0.3
2729
+
es-object-atoms: 1.1.1
2730
+
has-symbols: 1.1.0
1796
2731
object-keys: 1.1.1
1797
-
dev: true
1798
2732
1799
-
/object.entries@1.1.7:
1800
-
resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==}
1801
-
engines: {node: '>= 0.4'}
2733
+
object.entries@1.1.9:
1802
2734
dependencies:
1803
-
call-bind: 1.0.5
2735
+
call-bind: 1.0.8
2736
+
call-bound: 1.0.4
1804
2737
define-properties: 1.2.1
1805
-
es-abstract: 1.22.3
1806
-
dev: true
2738
+
es-object-atoms: 1.1.1
1807
2739
1808
-
/object.fromentries@2.0.7:
1809
-
resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==}
1810
-
engines: {node: '>= 0.4'}
2740
+
object.fromentries@2.0.8:
1811
2741
dependencies:
1812
-
call-bind: 1.0.5
2742
+
call-bind: 1.0.8
1813
2743
define-properties: 1.2.1
1814
-
es-abstract: 1.22.3
1815
-
dev: true
2744
+
es-abstract: 1.23.9
2745
+
es-object-atoms: 1.1.1
1816
2746
1817
-
/object.hasown@1.1.3:
1818
-
resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==}
2747
+
object.values@1.2.1:
1819
2748
dependencies:
1820
-
define-properties: 1.2.1
1821
-
es-abstract: 1.22.3
1822
-
dev: true
1823
-
1824
-
/object.values@1.1.7:
1825
-
resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==}
1826
-
engines: {node: '>= 0.4'}
1827
-
dependencies:
1828
-
call-bind: 1.0.5
2749
+
call-bind: 1.0.8
2750
+
call-bound: 1.0.4
1829
2751
define-properties: 1.2.1
1830
-
es-abstract: 1.22.3
1831
-
dev: true
2752
+
es-object-atoms: 1.1.1
1832
2753
1833
-
/once@1.4.0:
1834
-
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
2754
+
ofetch@1.4.1:
1835
2755
dependencies:
1836
-
wrappy: 1.0.2
2756
+
destr: 2.0.4
2757
+
node-fetch-native: 1.6.6
2758
+
ufo: 1.5.4
1837
2759
1838
-
/onetime@5.1.2:
1839
-
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
1840
-
engines: {node: '>=6'}
2760
+
onetime@7.0.0:
1841
2761
dependencies:
1842
-
mimic-fn: 2.1.0
1843
-
dev: true
2762
+
mimic-function: 5.0.1
1844
2763
1845
-
/onetime@6.0.0:
1846
-
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
1847
-
engines: {node: '>=12'}
1848
-
dependencies:
1849
-
mimic-fn: 4.0.0
1850
-
dev: true
1851
-
1852
-
/open@9.1.0:
1853
-
resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==}
1854
-
engines: {node: '>=14.16'}
1855
-
dependencies:
1856
-
default-browser: 4.0.0
1857
-
define-lazy-prop: 3.0.0
1858
-
is-inside-container: 1.0.0
1859
-
is-wsl: 2.2.0
1860
-
dev: true
1861
-
1862
-
/optionator@0.9.3:
1863
-
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
1864
-
engines: {node: '>= 0.8.0'}
2764
+
optionator@0.9.3:
1865
2765
dependencies:
1866
2766
'@aashutoshrathi/word-wrap': 1.2.6
1867
2767
deep-is: 0.1.4
···
1869
2769
levn: 0.4.1
1870
2770
prelude-ls: 1.2.1
1871
2771
type-check: 0.4.0
1872
-
dev: true
1873
2772
1874
-
/p-limit@3.1.0:
1875
-
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
1876
-
engines: {node: '>=10'}
2773
+
own-keys@1.0.1:
2774
+
dependencies:
2775
+
get-intrinsic: 1.3.0
2776
+
object-keys: 1.1.1
2777
+
safe-push-apply: 1.0.0
2778
+
2779
+
p-limit@3.1.0:
1877
2780
dependencies:
1878
2781
yocto-queue: 0.1.0
1879
-
dev: true
1880
2782
1881
-
/p-locate@5.0.0:
1882
-
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
1883
-
engines: {node: '>=10'}
2783
+
p-locate@5.0.0:
1884
2784
dependencies:
1885
2785
p-limit: 3.1.0
1886
-
dev: true
1887
2786
1888
-
/parent-module@1.0.1:
1889
-
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
1890
-
engines: {node: '>=6'}
2787
+
package-manager-detector@1.1.0: {}
2788
+
2789
+
parent-module@1.0.1:
1891
2790
dependencies:
1892
2791
callsites: 3.1.0
1893
-
dev: true
1894
2792
1895
-
/path-exists@4.0.0:
1896
-
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
1897
-
engines: {node: '>=8'}
1898
-
dev: true
2793
+
path-exists@4.0.0: {}
1899
2794
1900
-
/path-is-absolute@1.0.1:
1901
-
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
1902
-
engines: {node: '>=0.10.0'}
2795
+
path-key@3.1.1: {}
1903
2796
1904
-
/path-key@3.1.1:
1905
-
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1906
-
engines: {node: '>=8'}
1907
-
dev: true
2797
+
path-parse@1.0.7: {}
1908
2798
1909
-
/path-key@4.0.0:
1910
-
resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
1911
-
engines: {node: '>=12'}
1912
-
dev: true
2799
+
pathe@2.0.3: {}
1913
2800
1914
-
/path-parse@1.0.7:
1915
-
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1916
-
dev: true
2801
+
picomatch@2.3.1: {}
1917
2802
1918
-
/path-type@4.0.0:
1919
-
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
1920
-
engines: {node: '>=8'}
1921
-
dev: true
2803
+
picomatch@4.0.2: {}
1922
2804
1923
-
/picocolors@1.0.0:
1924
-
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
1925
-
dev: true
2805
+
pnpm-workspace-yaml@0.3.1:
2806
+
dependencies:
2807
+
yaml: 2.7.1
1926
2808
1927
-
/picomatch@2.3.1:
1928
-
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1929
-
engines: {node: '>=8.6'}
1930
-
dev: true
2809
+
possible-typed-array-names@1.1.0: {}
1931
2810
1932
-
/prelude-ls@1.2.1:
1933
-
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
1934
-
engines: {node: '>= 0.8.0'}
1935
-
dev: true
2811
+
prelude-ls@1.2.1: {}
1936
2812
1937
-
/prettier-linter-helpers@1.0.0:
1938
-
resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
1939
-
engines: {node: '>=6.0.0'}
2813
+
prettier-linter-helpers@1.0.0:
1940
2814
dependencies:
1941
2815
fast-diff: 1.3.0
1942
-
dev: true
1943
2816
1944
-
/prettier@3.1.0:
1945
-
resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==}
1946
-
engines: {node: '>=14'}
1947
-
hasBin: true
1948
-
dev: true
2817
+
prettier@3.1.0: {}
2818
+
2819
+
process@0.11.10: {}
1949
2820
1950
-
/prop-types@15.8.1:
1951
-
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
2821
+
prop-types@15.8.1:
1952
2822
dependencies:
1953
2823
loose-envify: 1.4.0
1954
2824
object-assign: 4.1.1
1955
2825
react-is: 16.13.1
1956
-
dev: true
1957
2826
1958
-
/punycode@2.3.1:
1959
-
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
1960
-
engines: {node: '>=6'}
1961
-
dev: true
2827
+
punycode@2.3.1: {}
1962
2828
1963
-
/queue-microtask@1.2.3:
1964
-
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1965
-
dev: true
2829
+
quansync@0.2.10: {}
1966
2830
1967
-
/react-is@16.13.1:
1968
-
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
1969
-
dev: true
2831
+
queue-microtask@1.2.3: {}
1970
2832
1971
-
/reflect.getprototypeof@1.0.4:
1972
-
resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==}
1973
-
engines: {node: '>= 0.4'}
2833
+
react-is@16.13.1: {}
2834
+
2835
+
readable-stream@4.5.2:
2836
+
dependencies:
2837
+
abort-controller: 3.0.0
2838
+
buffer: 6.0.3
2839
+
events: 3.3.0
2840
+
process: 0.11.10
2841
+
string_decoder: 1.3.0
2842
+
2843
+
reflect.getprototypeof@1.0.10:
1974
2844
dependencies:
1975
-
call-bind: 1.0.5
2845
+
call-bind: 1.0.8
1976
2846
define-properties: 1.2.1
1977
-
es-abstract: 1.22.3
1978
-
get-intrinsic: 1.2.2
1979
-
globalthis: 1.0.3
1980
-
which-builtin-type: 1.1.3
1981
-
dev: true
2847
+
es-abstract: 1.23.9
2848
+
es-errors: 1.3.0
2849
+
es-object-atoms: 1.1.1
2850
+
get-intrinsic: 1.3.0
2851
+
get-proto: 1.0.1
2852
+
which-builtin-type: 1.2.1
1982
2853
1983
-
/regexp.prototype.flags@1.5.1:
1984
-
resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==}
1985
-
engines: {node: '>= 0.4'}
2854
+
regexp.prototype.flags@1.5.4:
1986
2855
dependencies:
1987
-
call-bind: 1.0.5
2856
+
call-bind: 1.0.8
1988
2857
define-properties: 1.2.1
1989
-
set-function-name: 2.0.1
1990
-
dev: true
2858
+
es-errors: 1.3.0
2859
+
get-proto: 1.0.1
2860
+
gopd: 1.2.0
2861
+
set-function-name: 2.0.2
1991
2862
1992
-
/resolve-from@4.0.0:
1993
-
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
1994
-
engines: {node: '>=4'}
1995
-
dev: true
2863
+
resolve-from@4.0.0: {}
1996
2864
1997
-
/resolve@2.0.0-next.5:
1998
-
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
1999
-
hasBin: true
2865
+
resolve@2.0.0-next.5:
2000
2866
dependencies:
2001
-
is-core-module: 2.13.1
2867
+
is-core-module: 2.16.1
2002
2868
path-parse: 1.0.7
2003
2869
supports-preserve-symlinks-flag: 1.0.0
2004
-
dev: true
2005
2870
2006
-
/reusify@1.0.4:
2007
-
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
2008
-
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
2009
-
dev: true
2010
-
2011
-
/rimraf@3.0.2:
2012
-
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
2013
-
hasBin: true
2871
+
restore-cursor@5.1.0:
2014
2872
dependencies:
2015
-
glob: 7.2.3
2016
-
dev: true
2873
+
onetime: 7.0.0
2874
+
signal-exit: 4.1.0
2017
2875
2018
-
/run-applescript@5.0.0:
2019
-
resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==}
2020
-
engines: {node: '>=12'}
2021
-
dependencies:
2022
-
execa: 5.1.1
2023
-
dev: true
2876
+
reusify@1.0.4: {}
2024
2877
2025
-
/run-parallel@1.2.0:
2026
-
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
2878
+
run-parallel@1.2.0:
2027
2879
dependencies:
2028
2880
queue-microtask: 1.2.3
2029
-
dev: true
2030
2881
2031
-
/safe-array-concat@1.0.1:
2032
-
resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==}
2033
-
engines: {node: '>=0.4'}
2882
+
safe-array-concat@1.1.3:
2034
2883
dependencies:
2035
-
call-bind: 1.0.5
2036
-
get-intrinsic: 1.2.2
2037
-
has-symbols: 1.0.3
2884
+
call-bind: 1.0.8
2885
+
call-bound: 1.0.4
2886
+
get-intrinsic: 1.3.0
2887
+
has-symbols: 1.1.0
2038
2888
isarray: 2.0.5
2039
-
dev: true
2040
2889
2041
-
/safe-regex-test@1.0.0:
2042
-
resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==}
2890
+
safe-buffer@5.2.1: {}
2891
+
2892
+
safe-push-apply@1.0.0:
2043
2893
dependencies:
2044
-
call-bind: 1.0.5
2045
-
get-intrinsic: 1.2.2
2046
-
is-regex: 1.1.4
2047
-
dev: true
2894
+
es-errors: 1.3.0
2895
+
isarray: 2.0.5
2048
2896
2049
-
/semver@6.3.1:
2050
-
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
2051
-
hasBin: true
2052
-
dev: true
2897
+
safe-regex-test@1.1.0:
2898
+
dependencies:
2899
+
call-bound: 1.0.4
2900
+
es-errors: 1.3.0
2901
+
is-regex: 1.2.1
2902
+
2903
+
semver@6.3.1: {}
2904
+
2905
+
semver@7.7.1: {}
2053
2906
2054
-
/semver@7.5.4:
2055
-
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
2056
-
engines: {node: '>=10'}
2057
-
hasBin: true
2907
+
set-function-length@1.2.2:
2058
2908
dependencies:
2059
-
lru-cache: 6.0.0
2060
-
dev: true
2909
+
define-data-property: 1.1.4
2910
+
es-errors: 1.3.0
2911
+
function-bind: 1.1.2
2912
+
get-intrinsic: 1.3.0
2913
+
gopd: 1.2.0
2914
+
has-property-descriptors: 1.0.2
2061
2915
2062
-
/set-function-length@1.1.1:
2063
-
resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==}
2064
-
engines: {node: '>= 0.4'}
2916
+
set-function-name@2.0.2:
2065
2917
dependencies:
2066
-
define-data-property: 1.1.1
2067
-
get-intrinsic: 1.2.2
2068
-
gopd: 1.0.1
2069
-
has-property-descriptors: 1.0.1
2070
-
dev: true
2918
+
define-data-property: 1.1.4
2919
+
es-errors: 1.3.0
2920
+
functions-have-names: 1.2.3
2921
+
has-property-descriptors: 1.0.2
2071
2922
2072
-
/set-function-name@2.0.1:
2073
-
resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
2074
-
engines: {node: '>= 0.4'}
2923
+
set-proto@1.0.0:
2075
2924
dependencies:
2076
-
define-data-property: 1.1.1
2077
-
functions-have-names: 1.2.3
2078
-
has-property-descriptors: 1.0.1
2079
-
dev: true
2925
+
dunder-proto: 1.0.1
2926
+
es-errors: 1.3.0
2927
+
es-object-atoms: 1.1.1
2080
2928
2081
-
/shebang-command@2.0.0:
2082
-
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
2083
-
engines: {node: '>=8'}
2929
+
shebang-command@2.0.0:
2084
2930
dependencies:
2085
2931
shebang-regex: 3.0.0
2086
-
dev: true
2087
2932
2088
-
/shebang-regex@3.0.0:
2089
-
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
2090
-
engines: {node: '>=8'}
2091
-
dev: true
2933
+
shebang-regex@3.0.0: {}
2092
2934
2093
-
/side-channel@1.0.4:
2094
-
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
2935
+
side-channel-list@1.0.0:
2095
2936
dependencies:
2096
-
call-bind: 1.0.5
2097
-
get-intrinsic: 1.2.2
2098
-
object-inspect: 1.13.1
2099
-
dev: true
2937
+
es-errors: 1.3.0
2938
+
object-inspect: 1.13.4
2100
2939
2101
-
/signal-exit@3.0.7:
2102
-
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
2103
-
dev: true
2940
+
side-channel-map@1.0.1:
2941
+
dependencies:
2942
+
call-bound: 1.0.4
2943
+
es-errors: 1.3.0
2944
+
get-intrinsic: 1.3.0
2945
+
object-inspect: 1.13.4
2104
2946
2105
-
/slash@3.0.0:
2106
-
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
2107
-
engines: {node: '>=8'}
2108
-
dev: true
2947
+
side-channel-weakmap@1.0.2:
2948
+
dependencies:
2949
+
call-bound: 1.0.4
2950
+
es-errors: 1.3.0
2951
+
get-intrinsic: 1.3.0
2952
+
object-inspect: 1.13.4
2953
+
side-channel-map: 1.0.1
2109
2954
2110
-
/standalone-electron-types@1.0.0:
2111
-
resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==}
2955
+
side-channel@1.1.0:
2956
+
dependencies:
2957
+
es-errors: 1.3.0
2958
+
object-inspect: 1.13.4
2959
+
side-channel-list: 1.0.0
2960
+
side-channel-map: 1.0.1
2961
+
side-channel-weakmap: 1.0.2
2962
+
2963
+
signal-exit@4.1.0: {}
2964
+
2965
+
standalone-electron-types@1.0.0:
2112
2966
dependencies:
2113
2967
'@types/node': 18.17.17
2114
-
dev: false
2115
2968
2116
-
/string.prototype.matchall@4.0.10:
2117
-
resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==}
2969
+
string.prototype.matchall@4.0.12:
2118
2970
dependencies:
2119
-
call-bind: 1.0.5
2971
+
call-bind: 1.0.8
2972
+
call-bound: 1.0.4
2120
2973
define-properties: 1.2.1
2121
-
es-abstract: 1.22.3
2122
-
get-intrinsic: 1.2.2
2123
-
has-symbols: 1.0.3
2124
-
internal-slot: 1.0.6
2125
-
regexp.prototype.flags: 1.5.1
2126
-
set-function-name: 2.0.1
2127
-
side-channel: 1.0.4
2128
-
dev: true
2974
+
es-abstract: 1.23.9
2975
+
es-errors: 1.3.0
2976
+
es-object-atoms: 1.1.1
2977
+
get-intrinsic: 1.3.0
2978
+
gopd: 1.2.0
2979
+
has-symbols: 1.1.0
2980
+
internal-slot: 1.1.0
2981
+
regexp.prototype.flags: 1.5.4
2982
+
set-function-name: 2.0.2
2983
+
side-channel: 1.1.0
2129
2984
2130
-
/string.prototype.trim@1.2.8:
2131
-
resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
2132
-
engines: {node: '>= 0.4'}
2985
+
string.prototype.repeat@1.0.0:
2133
2986
dependencies:
2134
-
call-bind: 1.0.5
2135
2987
define-properties: 1.2.1
2136
-
es-abstract: 1.22.3
2137
-
dev: true
2988
+
es-abstract: 1.23.9
2138
2989
2139
-
/string.prototype.trimend@1.0.7:
2140
-
resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==}
2990
+
string.prototype.trim@1.2.10:
2141
2991
dependencies:
2142
-
call-bind: 1.0.5
2992
+
call-bind: 1.0.8
2993
+
call-bound: 1.0.4
2994
+
define-data-property: 1.1.4
2143
2995
define-properties: 1.2.1
2144
-
es-abstract: 1.22.3
2145
-
dev: true
2996
+
es-abstract: 1.23.9
2997
+
es-object-atoms: 1.1.1
2998
+
has-property-descriptors: 1.0.2
2146
2999
2147
-
/string.prototype.trimstart@1.0.7:
2148
-
resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==}
3000
+
string.prototype.trimend@1.0.9:
2149
3001
dependencies:
2150
-
call-bind: 1.0.5
3002
+
call-bind: 1.0.8
3003
+
call-bound: 1.0.4
2151
3004
define-properties: 1.2.1
2152
-
es-abstract: 1.22.3
2153
-
dev: true
3005
+
es-object-atoms: 1.1.1
2154
3006
2155
-
/strip-ansi@6.0.1:
2156
-
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
2157
-
engines: {node: '>=8'}
3007
+
string.prototype.trimstart@1.0.8:
2158
3008
dependencies:
2159
-
ansi-regex: 5.0.1
2160
-
dev: true
3009
+
call-bind: 1.0.8
3010
+
define-properties: 1.2.1
3011
+
es-object-atoms: 1.1.1
2161
3012
2162
-
/strip-final-newline@2.0.0:
2163
-
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
2164
-
engines: {node: '>=6'}
2165
-
dev: true
2166
-
2167
-
/strip-final-newline@3.0.0:
2168
-
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
2169
-
engines: {node: '>=12'}
2170
-
dev: true
3013
+
string_decoder@1.3.0:
3014
+
dependencies:
3015
+
safe-buffer: 5.2.1
2171
3016
2172
-
/strip-json-comments@3.1.1:
2173
-
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
2174
-
engines: {node: '>=8'}
2175
-
dev: true
3017
+
strip-json-comments@3.1.1: {}
2176
3018
2177
-
/supports-color@7.2.0:
2178
-
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
2179
-
engines: {node: '>=8'}
3019
+
supports-color@7.2.0:
2180
3020
dependencies:
2181
3021
has-flag: 4.0.0
2182
-
dev: true
2183
3022
2184
-
/supports-preserve-symlinks-flag@1.0.0:
2185
-
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
2186
-
engines: {node: '>= 0.4'}
2187
-
dev: true
3023
+
supports-preserve-symlinks-flag@1.0.0: {}
2188
3024
2189
-
/synckit@0.8.6:
2190
-
resolution: {integrity: sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==}
2191
-
engines: {node: ^14.18.0 || >=16.0.0}
3025
+
synckit@0.11.1:
2192
3026
dependencies:
2193
-
'@pkgr/utils': 2.4.2
2194
-
tslib: 2.6.2
2195
-
dev: true
3027
+
'@pkgr/core': 0.2.0
3028
+
tslib: 2.8.1
2196
3029
2197
-
/text-table@0.2.0:
2198
-
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
2199
-
dev: true
3030
+
taze@19.0.4:
3031
+
dependencies:
3032
+
'@antfu/ni': 24.3.0
3033
+
cac: 6.7.14
3034
+
find-up-simple: 1.0.1
3035
+
ofetch: 1.4.1
3036
+
package-manager-detector: 1.1.0
3037
+
pathe: 2.0.3
3038
+
pnpm-workspace-yaml: 0.3.1
3039
+
restore-cursor: 5.1.0
3040
+
tinyexec: 1.0.1
3041
+
tinyglobby: 0.2.12
3042
+
unconfig: 7.3.1
3043
+
yaml: 2.7.1
2200
3044
2201
-
/titleize@3.0.0:
2202
-
resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==}
2203
-
engines: {node: '>=12'}
2204
-
dev: true
3045
+
tinyexec@1.0.1: {}
2205
3046
2206
-
/to-regex-range@5.0.1:
2207
-
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
2208
-
engines: {node: '>=8.0'}
3047
+
tinyglobby@0.2.12:
3048
+
dependencies:
3049
+
fdir: 6.4.3(picomatch@4.0.2)
3050
+
picomatch: 4.0.2
3051
+
3052
+
to-regex-range@5.0.1:
2209
3053
dependencies:
2210
3054
is-number: 7.0.0
2211
-
dev: true
2212
3055
2213
-
/ts-api-utils@1.0.3(typescript@5.3.2):
2214
-
resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==}
2215
-
engines: {node: '>=16.13.0'}
2216
-
peerDependencies:
2217
-
typescript: '>=4.2.0'
3056
+
ts-api-utils@2.1.0(typescript@5.8.2):
2218
3057
dependencies:
2219
-
typescript: 5.3.2
2220
-
dev: true
3058
+
typescript: 5.8.2
2221
3059
2222
-
/tslib@2.6.2:
2223
-
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
2224
-
dev: true
3060
+
tslib@2.8.1: {}
2225
3061
2226
-
/type-check@0.4.0:
2227
-
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
2228
-
engines: {node: '>= 0.8.0'}
3062
+
type-check@0.4.0:
2229
3063
dependencies:
2230
3064
prelude-ls: 1.2.1
2231
-
dev: true
2232
3065
2233
-
/type-fest@0.20.2:
2234
-
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
2235
-
engines: {node: '>=10'}
2236
-
dev: true
3066
+
typed-array-buffer@1.0.3:
3067
+
dependencies:
3068
+
call-bound: 1.0.4
3069
+
es-errors: 1.3.0
3070
+
is-typed-array: 1.1.15
2237
3071
2238
-
/typed-array-buffer@1.0.0:
2239
-
resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
2240
-
engines: {node: '>= 0.4'}
3072
+
typed-array-byte-length@1.0.3:
2241
3073
dependencies:
2242
-
call-bind: 1.0.5
2243
-
get-intrinsic: 1.2.2
2244
-
is-typed-array: 1.1.12
2245
-
dev: true
3074
+
call-bind: 1.0.8
3075
+
for-each: 0.3.5
3076
+
gopd: 1.2.0
3077
+
has-proto: 1.2.0
3078
+
is-typed-array: 1.1.15
2246
3079
2247
-
/typed-array-byte-length@1.0.0:
2248
-
resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==}
2249
-
engines: {node: '>= 0.4'}
3080
+
typed-array-byte-offset@1.0.4:
2250
3081
dependencies:
2251
-
call-bind: 1.0.5
2252
-
for-each: 0.3.3
2253
-
has-proto: 1.0.1
2254
-
is-typed-array: 1.1.12
2255
-
dev: true
3082
+
available-typed-arrays: 1.0.7
3083
+
call-bind: 1.0.8
3084
+
for-each: 0.3.5
3085
+
gopd: 1.2.0
3086
+
has-proto: 1.2.0
3087
+
is-typed-array: 1.1.15
3088
+
reflect.getprototypeof: 1.0.10
2256
3089
2257
-
/typed-array-byte-offset@1.0.0:
2258
-
resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==}
2259
-
engines: {node: '>= 0.4'}
3090
+
typed-array-length@1.0.7:
2260
3091
dependencies:
2261
-
available-typed-arrays: 1.0.5
2262
-
call-bind: 1.0.5
2263
-
for-each: 0.3.3
2264
-
has-proto: 1.0.1
2265
-
is-typed-array: 1.1.12
2266
-
dev: true
3092
+
call-bind: 1.0.8
3093
+
for-each: 0.3.5
3094
+
gopd: 1.2.0
3095
+
is-typed-array: 1.1.15
3096
+
possible-typed-array-names: 1.1.0
3097
+
reflect.getprototypeof: 1.0.10
2267
3098
2268
-
/typed-array-length@1.0.4:
2269
-
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
3099
+
typescript-eslint@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2):
2270
3100
dependencies:
2271
-
call-bind: 1.0.5
2272
-
for-each: 0.3.3
2273
-
is-typed-array: 1.1.12
2274
-
dev: true
3101
+
'@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
3102
+
'@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
3103
+
'@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
3104
+
eslint: 9.23.0(jiti@2.4.2)
3105
+
typescript: 5.8.2
3106
+
transitivePeerDependencies:
3107
+
- supports-color
2275
3108
2276
-
/typescript@5.3.2:
2277
-
resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==}
2278
-
engines: {node: '>=14.17'}
2279
-
hasBin: true
2280
-
dev: true
3109
+
typescript@5.8.2: {}
2281
3110
2282
-
/unbox-primitive@1.0.2:
2283
-
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
3111
+
ufo@1.5.4: {}
3112
+
3113
+
unbox-primitive@1.1.0:
2284
3114
dependencies:
2285
-
call-bind: 1.0.5
2286
-
has-bigints: 1.0.2
2287
-
has-symbols: 1.0.3
2288
-
which-boxed-primitive: 1.0.2
2289
-
dev: true
3115
+
call-bound: 1.0.4
3116
+
has-bigints: 1.1.0
3117
+
has-symbols: 1.1.0
3118
+
which-boxed-primitive: 1.1.1
2290
3119
2291
-
/untildify@4.0.0:
2292
-
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
2293
-
engines: {node: '>=8'}
2294
-
dev: true
3120
+
unconfig@7.3.1:
3121
+
dependencies:
3122
+
'@quansync/fs': 0.1.2
3123
+
defu: 6.1.4
3124
+
jiti: 2.4.2
3125
+
quansync: 0.2.10
2295
3126
2296
-
/uri-js@4.4.1:
2297
-
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
3127
+
undici-types@6.20.0: {}
3128
+
3129
+
undici-types@6.21.0: {}
3130
+
3131
+
uri-js@4.4.1:
2298
3132
dependencies:
2299
3133
punycode: 2.3.1
2300
-
dev: true
2301
3134
2302
-
/which-boxed-primitive@1.0.2:
2303
-
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
3135
+
utilium@1.10.1:
2304
3136
dependencies:
2305
-
is-bigint: 1.0.4
2306
-
is-boolean-object: 1.1.2
2307
-
is-number-object: 1.0.7
2308
-
is-string: 1.0.7
2309
-
is-symbol: 1.0.4
2310
-
dev: true
3137
+
eventemitter3: 5.0.1
3138
+
optionalDependencies:
3139
+
'@xterm/xterm': 5.5.0
2311
3140
2312
-
/which-builtin-type@1.1.3:
2313
-
resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==}
2314
-
engines: {node: '>= 0.4'}
3141
+
which-boxed-primitive@1.1.1:
2315
3142
dependencies:
2316
-
function.prototype.name: 1.1.6
2317
-
has-tostringtag: 1.0.0
2318
-
is-async-function: 2.0.0
2319
-
is-date-object: 1.0.5
2320
-
is-finalizationregistry: 1.0.2
2321
-
is-generator-function: 1.0.10
2322
-
is-regex: 1.1.4
2323
-
is-weakref: 1.0.2
3143
+
is-bigint: 1.1.0
3144
+
is-boolean-object: 1.2.2
3145
+
is-number-object: 1.1.1
3146
+
is-string: 1.1.1
3147
+
is-symbol: 1.1.1
3148
+
3149
+
which-builtin-type@1.2.1:
3150
+
dependencies:
3151
+
call-bound: 1.0.4
3152
+
function.prototype.name: 1.1.8
3153
+
has-tostringtag: 1.0.2
3154
+
is-async-function: 2.1.1
3155
+
is-date-object: 1.1.0
3156
+
is-finalizationregistry: 1.1.1
3157
+
is-generator-function: 1.1.0
3158
+
is-regex: 1.2.1
3159
+
is-weakref: 1.1.1
2324
3160
isarray: 2.0.5
2325
-
which-boxed-primitive: 1.0.2
2326
-
which-collection: 1.0.1
2327
-
which-typed-array: 1.1.13
2328
-
dev: true
3161
+
which-boxed-primitive: 1.1.1
3162
+
which-collection: 1.0.2
3163
+
which-typed-array: 1.1.19
2329
3164
2330
-
/which-collection@1.0.1:
2331
-
resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
3165
+
which-collection@1.0.2:
2332
3166
dependencies:
2333
-
is-map: 2.0.2
2334
-
is-set: 2.0.2
2335
-
is-weakmap: 2.0.1
2336
-
is-weakset: 2.0.2
2337
-
dev: true
3167
+
is-map: 2.0.3
3168
+
is-set: 2.0.3
3169
+
is-weakmap: 2.0.2
3170
+
is-weakset: 2.0.4
2338
3171
2339
-
/which-typed-array@1.1.13:
2340
-
resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==}
2341
-
engines: {node: '>= 0.4'}
3172
+
which-typed-array@1.1.19:
2342
3173
dependencies:
2343
-
available-typed-arrays: 1.0.5
2344
-
call-bind: 1.0.5
2345
-
for-each: 0.3.3
2346
-
gopd: 1.0.1
2347
-
has-tostringtag: 1.0.0
2348
-
dev: true
3174
+
available-typed-arrays: 1.0.7
3175
+
call-bind: 1.0.8
3176
+
call-bound: 1.0.4
3177
+
for-each: 0.3.5
3178
+
get-proto: 1.0.1
3179
+
gopd: 1.2.0
3180
+
has-tostringtag: 1.0.2
2349
3181
2350
-
/which@2.0.2:
2351
-
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
2352
-
engines: {node: '>= 8'}
2353
-
hasBin: true
3182
+
which@2.0.2:
2354
3183
dependencies:
2355
3184
isexe: 2.0.0
2356
-
dev: true
2357
3185
2358
-
/wrappy@1.0.2:
2359
-
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
3186
+
yaml@2.7.1: {}
2360
3187
2361
-
/yallist@4.0.0:
2362
-
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
2363
-
dev: true
3188
+
yocto-queue@0.1.0: {}
2364
3189
2365
-
/yocto-queue@0.1.0:
2366
-
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
2367
-
engines: {node: '>=10'}
2368
-
dev: true
3190
+
zustand@5.0.3(@types/react@18.3.20):
3191
+
optionalDependencies:
3192
+
'@types/react': 18.3.20
+31
-1
pnpm-workspace.yaml
+31
-1
pnpm-workspace.yaml
···
1
1
packages:
2
-
- "packages/*"
2
+
- packages/*
3
+
4
+
catalogs:
5
+
dev:
6
+
esbuild: ^0.19.3
7
+
esbuild-copy-static-files: ^0.1.0
8
+
"@types/node": ^22.14.0
9
+
"@moonlight-mod/eslint-config": "github:moonlight-mod/eslint-config"
10
+
eslint: ^9.12.0
11
+
"@types/chrome": ^0.0.313
12
+
husky: ^8.0.3
13
+
prettier: ^3.1.0
14
+
typescript: ^5.3.3
15
+
taze: ^19.0.4
16
+
prod:
17
+
"@moonlight-mod/lunast": ^1.0.1
18
+
"@moonlight-mod/mappings": ^1.1.25
19
+
"@moonlight-mod/moonmap": ^1.0.5
20
+
microdiff: ^1.5.0
21
+
nanotar: ^0.1.1
22
+
"@zenfs/core": ^2.0.0
23
+
"@zenfs/dom": ^1.1.3
24
+
25
+
onlyBuiltDependencies:
26
+
- esbuild
27
+
28
+
engineStrict: true
29
+
strictSsl: true
30
+
strictDepBuilds: true
31
+
packageManagerStrict: true
32
+
registry: https://registry.npmjs.org/
+78
scripts/link.mjs
+78
scripts/link.mjs
···
1
+
// Janky script to get around pnpm link issues
2
+
// Probably don't use this. Probably
3
+
/* eslint-disable no-console */
4
+
const fs = require("fs");
5
+
const path = require("path");
6
+
const child_process = require("child_process");
7
+
8
+
const cwd = process.cwd();
9
+
const onDisk = {
10
+
//"@moonlight-mod/lunast": "../lunast",
11
+
//"@moonlight-mod/moonmap": "../moonmap",
12
+
"@moonlight-mod/mappings": "../mappings"
13
+
};
14
+
15
+
function exec(cmd, dir) {
16
+
child_process.execSync(cmd, { cwd: dir, stdio: "inherit" });
17
+
}
18
+
19
+
function getDeps(packageJSON) {
20
+
const ret = {};
21
+
Object.assign(ret, packageJSON.dependencies || {});
22
+
Object.assign(ret, packageJSON.devDependencies || {});
23
+
Object.assign(ret, packageJSON.peerDependencies || {});
24
+
return ret;
25
+
}
26
+
27
+
function link(dir) {
28
+
const packageJSONPath = path.join(dir, "package.json");
29
+
if (!fs.existsSync(packageJSONPath)) return;
30
+
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, "utf8"));
31
+
const deps = getDeps(packageJSON);
32
+
33
+
for (const [dep, relativePath] of Object.entries(onDisk)) {
34
+
const fullPath = path.join(cwd, relativePath);
35
+
if (deps[dep]) {
36
+
exec(`pnpm link ${fullPath}`, dir);
37
+
}
38
+
}
39
+
}
40
+
41
+
function undo(dir) {
42
+
exec("pnpm unlink", dir);
43
+
try {
44
+
if (fs.existsSync(path.join(dir, "pnpm-lock.yaml"))) {
45
+
exec("git restore pnpm-lock.yaml", dir);
46
+
}
47
+
} catch {
48
+
// ignored
49
+
}
50
+
}
51
+
52
+
const shouldUndo = process.argv.includes("--undo");
53
+
const packages = fs.readdirSync("./packages");
54
+
55
+
for (const path of Object.values(onDisk)) {
56
+
console.log(path);
57
+
if (shouldUndo) {
58
+
undo(path);
59
+
} else {
60
+
link(path);
61
+
}
62
+
}
63
+
64
+
if (shouldUndo) {
65
+
console.log(cwd);
66
+
undo(cwd);
67
+
for (const pkg of packages) {
68
+
const dir = path.join(cwd, "packages", pkg);
69
+
console.log(dir);
70
+
undo(dir);
71
+
}
72
+
} else {
73
+
for (const pkg of packages) {
74
+
const dir = path.join(cwd, "packages", pkg);
75
+
console.log(dir);
76
+
link(dir);
77
+
}
78
+
}
+35
tsconfig.base.json
+35
tsconfig.base.json
···
1
+
{
2
+
"$schema": "https://json.schemastore.org/tsconfig.json",
3
+
"display": "Base",
4
+
"_version": "1.0.0",
5
+
"compilerOptions": {
6
+
"incremental": true,
7
+
"target": "ES2022",
8
+
"jsx": "react",
9
+
"lib": ["ESNext", "ESNext.Disposable", "DOM", "DOM.Iterable"],
10
+
"module": "ES2020",
11
+
"moduleResolution": "Bundler",
12
+
"resolveJsonModule": true,
13
+
"allowArbitraryExtensions": false,
14
+
"allowImportingTsExtensions": true,
15
+
"allowJs": true,
16
+
"strict": true,
17
+
"strictNullChecks": true,
18
+
19
+
// disable unreachable code detection because it breaks with esbuild labels
20
+
"allowUnreachableCode": true,
21
+
"noFallthroughCasesInSwitch": true,
22
+
"noImplicitReturns": true,
23
+
"declaration": true,
24
+
"declarationMap": true,
25
+
"outDir": "dist",
26
+
"sourceMap": true,
27
+
"stripInternal": true,
28
+
"esModuleInterop": true,
29
+
"forceConsistentCasingInFileNames": true,
30
+
"noErrorTruncation": true,
31
+
"verbatimModuleSyntax": false,
32
+
// meriyah has a broken import lol
33
+
"skipLibCheck": true
34
+
}
35
+
}
+7
-14
tsconfig.json
+7
-14
tsconfig.json
···
1
1
{
2
+
"extends": ["./tsconfig.base.json"],
2
3
"compilerOptions": {
3
-
"target": "es2016",
4
-
"module": "es6",
5
-
"esModuleInterop": true,
6
-
"forceConsistentCasingInFileNames": true,
7
-
"strict": true,
8
-
"skipLibCheck": true,
9
-
"moduleResolution": "bundler",
10
4
"baseUrl": "./packages/",
11
-
"jsx": "react",
12
-
"noEmit": true,
13
-
14
-
// disable unreachable code detection because it breaks with esbuild labels
15
-
"allowUnreachableCode": true
5
+
"noEmit": true
16
6
},
17
-
"include": ["./packages/**/*", "./env.d.ts"],
18
-
"exclude": ["node_modules"]
7
+
"exclude": [
8
+
"**/node_modules/**",
9
+
"**/dist/**",
10
+
"**/build/**"
11
+
]
19
12
}