-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
-11
.github/workflows/nightly.yml
+9
-11
.github/workflows/nightly.yml
···
15
name: Nightly builds on GitHub Pages
16
runs-on: ubuntu-latest
17
steps:
18
-
- uses: actions/checkout@v3
19
-
20
-
- uses: pnpm/action-setup@v2
21
-
with:
22
-
version: 9
23
-
run_install: false
24
-
- uses: actions/setup-node@v3
25
with:
26
-
node-version: 18
27
cache: pnpm
28
29
- name: Install dependencies
···
31
- name: Build moonlight
32
env:
33
NODE_ENV: production
34
run: pnpm run build
35
36
- name: Write ref/commit to file
···
45
echo "$(date +%s)" >> ./dist/ref
46
47
- name: Setup GitHub Pages
48
-
uses: actions/configure-pages@v3
49
- name: Upload artifact
50
-
uses: actions/upload-pages-artifact@v1
51
with:
52
path: ./dist
53
- name: Deploy to GitHub Pages
54
-
uses: actions/deploy-pages@v2
···
15
name: Nightly builds on GitHub Pages
16
runs-on: ubuntu-latest
17
steps:
18
+
- uses: actions/checkout@v4
19
+
- uses: pnpm/action-setup@v4
20
+
- uses: actions/setup-node@v4
21
with:
22
+
node-version: 22
23
cache: pnpm
24
25
- name: Install dependencies
···
27
- name: Build moonlight
28
env:
29
NODE_ENV: production
30
+
MOONLIGHT_BRANCH: nightly
31
+
MOONLIGHT_VERSION: ${{ github.sha }}
32
run: pnpm run build
33
34
- name: Write ref/commit to file
···
43
echo "$(date +%s)" >> ./dist/ref
44
45
- name: Setup GitHub Pages
46
+
uses: actions/configure-pages@v5
47
- name: Upload artifact
48
+
uses: actions/upload-pages-artifact@v3
49
with:
50
path: ./dist
51
- name: Deploy to GitHub Pages
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
name: Release builds to GitHub Releases
14
runs-on: ubuntu-latest
15
steps:
16
-
- uses: actions/checkout@v3
17
-
18
-
- uses: pnpm/action-setup@v2
19
-
with:
20
-
version: 9
21
-
run_install: false
22
-
- uses: actions/setup-node@v3
23
with:
24
-
node-version: 18
25
cache: pnpm
26
27
- name: Install dependencies
···
29
- name: Build moonlight
30
env:
31
NODE_ENV: production
32
run: pnpm run build
33
- name: Create archive
34
run: |
···
13
name: Release builds to GitHub Releases
14
runs-on: ubuntu-latest
15
steps:
16
+
- uses: actions/checkout@v4
17
+
- uses: pnpm/action-setup@v4
18
+
- uses: actions/setup-node@v4
19
with:
20
+
node-version: 22
21
cache: pnpm
22
23
- name: Install dependencies
···
25
- name: Build moonlight
26
env:
27
NODE_ENV: production
28
+
MOONLIGHT_BRANCH: stable
29
+
MOONLIGHT_VERSION: ${{ github.ref_name }}
30
run: pnpm run build
31
- name: Create archive
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 }}
+5
-1
.gitignore
+5
-1
.gitignore
+4
-4
.prettierrc
+4
-4
.prettierrc
-14
.vscode/tasks.json
-14
.vscode/tasks.json
+4
-1
CHANGELOG.md
+4
-1
CHANGELOG.md
+15
-4
README.md
+15
-4
README.md
···
1
<h3 align="center">
2
-
<img src="./img/wordmark.png" alt="moonlight" />
3
4
-
<a href="https://discord.gg/FdZBTFCP6F">Discord server</a>
5
\- <a href="https://github.com/moonlight-mod/moonlight">GitHub</a>
6
-
\- <a href="https://moonlight-mod.github.io/">Docs</a>
7
8
<hr />
9
</h3>
10
11
**moonlight** is yet another Discord client mod, focused on providing a decent user and developer experience.
12
13
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
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.
16
17
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.
···
1
<h3 align="center">
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>
7
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>
11
\- <a href="https://github.com/moonlight-mod/moonlight">GitHub</a>
12
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>
20
</h3>
21
22
**moonlight** is yet another Discord client mod, focused on providing a decent user and developer experience.
23
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.
25
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.
27
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.
+127
-41
build.mjs
+127
-41
build.mjs
···
13
14
const prod = process.env.NODE_ENV === "production";
15
const watch = process.argv.includes("--watch");
16
17
const external = [
18
"electron",
19
"fs",
20
"path",
21
"module",
22
-
"events",
23
-
"original-fs", // wtf asar?
24
25
// Silence an esbuild warning
26
"./node-preload.js"
···
65
name: "build-log",
66
setup(build) {
67
build.onEnd((result) => {
68
-
console.log(
69
-
`[${timeFormatter.format(new Date())}] [${tag}] build finished`
70
-
);
71
});
72
}
73
});
74
75
async function build(name, entry) {
76
-
const outfile = path.join("./dist", name + ".js");
77
78
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");
82
83
const define = {
84
MOONLIGHT_ENV: `"${name}"`,
85
-
MOONLIGHT_PROD: prod.toString()
86
};
87
88
-
for (const iterName of Object.keys(config)) {
89
const snake = iterName.replace(/-/g, "_").toUpperCase();
90
define[`MOONLIGHT_${snake}`] = (name === iterName).toString();
91
}
···
93
const nodeDependencies = ["glob"];
94
const ignoredExternal = name === "web-preload" ? nodeDependencies : [];
95
96
/** @type {import("esbuild").BuildOptions} */
97
const esbuildConfig = {
98
entryPoints: [entry],
99
outfile,
100
101
-
format: "cjs",
102
-
platform: name === "web-preload" ? "browser" : "node",
103
104
treeShaking: true,
105
bundle: true,
···
112
dropLabels,
113
114
logLevel: "silent",
115
-
plugins: [deduplicatedLogging, taggedBuildLog(name)]
116
};
117
118
if (watch) {
119
const ctx = await esbuild.context(esbuildConfig);
120
await ctx.watch();
···
123
}
124
}
125
126
-
async function buildExt(ext, side, copyManifest, fileExt) {
127
const outdir = path.join("./dist", "core-extensions", ext);
128
if (!fs.existsSync(outdir)) {
129
fs.mkdirSync(outdir, { recursive: true });
130
}
131
132
-
const entryPoints = [
133
-
`packages/core-extensions/src/${ext}/${side}.${fileExt}`
134
-
];
135
136
const wpModulesDir = `packages/core-extensions/src/${ext}/webpackModules`;
137
if (fs.existsSync(wpModulesDir) && side === "index") {
138
const wpModules = fs.opendirSync(wpModulesDir);
139
for await (const wpModule of wpModules) {
140
if (wpModule.isFile()) {
141
-
entryPoints.push(
142
-
`packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}`
143
-
);
144
} else {
145
for (const fileExt of ["ts", "tsx"]) {
146
const path = `packages/core-extensions/src/${ext}/webpackModules/${wpModule.name}/index.${fileExt}`;
···
168
}
169
};
170
171
const esbuildConfig = {
172
entryPoints,
173
outdir,
174
175
-
format: "cjs",
176
platform: "node",
177
178
treeShaking: true,
···
186
},
187
logLevel: "silent",
188
plugins: [
189
-
...(copyManifest
190
? [
191
copyStaticFiles({
192
-
src: `./packages/core-extensions/src/${ext}/manifest.json`,
193
-
dest: `./dist/core-extensions/${ext}/manifest.json`
194
})
195
]
196
: []),
···
210
211
const promises = [];
212
213
-
for (const [name, entry] of Object.entries(config)) {
214
-
promises.push(build(name, entry));
215
-
}
216
-
217
-
const coreExtensions = fs.readdirSync("./packages/core-extensions/src");
218
-
for (const ext of coreExtensions) {
219
-
let copiedManifest = false;
220
221
-
for (const fileExt of ["ts", "tsx"]) {
222
-
for (const type of ["index", "node", "host"]) {
223
-
if (
224
-
fs.existsSync(
225
-
`./packages/core-extensions/src/${ext}/${type}.${fileExt}`
226
-
)
227
-
) {
228
-
promises.push(buildExt(ext, type, !copiedManifest, fileExt));
229
-
copiedManifest = true;
230
}
231
}
232
}
···
13
14
const prod = process.env.NODE_ENV === "production";
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";
22
23
const external = [
24
"electron",
25
"fs",
26
"path",
27
"module",
28
+
"discord", // mappings
29
30
// Silence an esbuild warning
31
"./node-preload.js"
···
70
name: "build-log",
71
setup(build) {
72
build.onEnd((result) => {
73
+
console.log(`[${timeFormatter.format(new Date())}] [${tag}] build finished`);
74
});
75
}
76
});
77
78
async function build(name, entry) {
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");
82
83
const dropLabels = [];
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
+
}
98
99
const define = {
100
MOONLIGHT_ENV: `"${name}"`,
101
+
MOONLIGHT_PROD: prod.toString(),
102
+
MOONLIGHT_BRANCH: `"${buildBranch}"`,
103
+
MOONLIGHT_VERSION: `"${buildVersion}"`
104
};
105
106
+
for (const iterName of ["injector", "node-preload", "web-preload", "browser"]) {
107
const snake = iterName.replace(/-/g, "_").toUpperCase();
108
define[`MOONLIGHT_${snake}`] = (name === iterName).toString();
109
}
···
111
const nodeDependencies = ["glob"];
112
const ignoredExternal = name === "web-preload" ? nodeDependencies : [];
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
+
146
/** @type {import("esbuild").BuildOptions} */
147
const esbuildConfig = {
148
entryPoints: [entry],
149
outfile,
150
151
+
format: "iife",
152
+
globalName: "module.exports",
153
+
154
+
platform: ["web-preload", "browser"].includes(name) ? "browser" : "node",
155
156
treeShaking: true,
157
bundle: true,
···
164
dropLabels,
165
166
logLevel: "silent",
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
176
};
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
+
201
if (watch) {
202
const ctx = await esbuild.context(esbuildConfig);
203
await ctx.watch();
···
206
}
207
}
208
209
+
async function buildExt(ext, side, fileExt) {
210
const outdir = path.join("./dist", "core-extensions", ext);
211
if (!fs.existsSync(outdir)) {
212
fs.mkdirSync(outdir, { recursive: true });
213
}
214
215
+
const entryPoints = [`packages/core-extensions/src/${ext}/${side}.${fileExt}`];
216
217
const wpModulesDir = `packages/core-extensions/src/${ext}/webpackModules`;
218
if (fs.existsSync(wpModulesDir) && side === "index") {
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}`;
···
247
}
248
};
249
250
+
const styleInput = `packages/core-extensions/src/${ext}/style.css`;
251
+
const styleOutput = `dist/core-extensions/${ext}/style.css`;
252
+
253
const esbuildConfig = {
254
entryPoints,
255
outdir,
256
257
+
format: "iife",
258
+
globalName: "module.exports",
259
platform: "node",
260
261
treeShaking: true,
···
269
},
270
logLevel: "silent",
271
plugins: [
272
+
copyStaticFiles({
273
+
src: `./packages/core-extensions/src/${ext}/manifest.json`,
274
+
dest: `./dist/core-extensions/${ext}/manifest.json`
275
+
}),
276
+
...(fs.existsSync(styleInput)
277
? [
278
copyStaticFiles({
279
+
src: styleInput,
280
+
dest: styleOutput
281
})
282
]
283
: []),
···
297
298
const promises = [];
299
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
+
}
308
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
+
}
316
}
317
}
318
}
+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
+
];
+4
-73
flake.lock
+4
-73
flake.lock
···
18
"type": "github"
19
}
20
},
21
-
"flake-utils_2": {
22
-
"inputs": {
23
-
"systems": "systems_2"
24
-
},
25
-
"locked": {
26
-
"lastModified": 1701680307,
27
-
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
28
-
"owner": "numtide",
29
-
"repo": "flake-utils",
30
-
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
31
-
"type": "github"
32
-
},
33
-
"original": {
34
-
"owner": "numtide",
35
-
"repo": "flake-utils",
36
-
"type": "github"
37
-
}
38
-
},
39
"nixpkgs": {
40
"locked": {
41
-
"lastModified": 1704295289,
42
-
"narHash": "sha256-9WZDRfpMqCYL6g/HNWVvXF0hxdaAgwgIGeLYiOhmes8=",
43
"owner": "NixOS",
44
"repo": "nixpkgs",
45
-
"rev": "b0b2c5445c64191fd8d0b31f2b1a34e45a64547d",
46
"type": "github"
47
},
48
"original": {
49
"owner": "NixOS",
50
-
"ref": "nixos-23.11",
51
-
"repo": "nixpkgs",
52
-
"type": "github"
53
-
}
54
-
},
55
-
"nixpkgs_2": {
56
-
"locked": {
57
-
"lastModified": 1702151865,
58
-
"narHash": "sha256-9VAt19t6yQa7pHZLDbil/QctAgVsA66DLnzdRGqDisg=",
59
-
"owner": "nixos",
60
-
"repo": "nixpkgs",
61
-
"rev": "666fc80e7b2afb570462423cb0e1cf1a3a34fedd",
62
-
"type": "github"
63
-
},
64
-
"original": {
65
-
"owner": "nixos",
66
"ref": "nixos-unstable",
67
"repo": "nixpkgs",
68
"type": "github"
69
}
70
},
71
-
"pnpm2nix": {
72
-
"inputs": {
73
-
"flake-utils": "flake-utils_2",
74
-
"nixpkgs": "nixpkgs_2"
75
-
},
76
-
"locked": {
77
-
"lastModified": 1709572248,
78
-
"narHash": "sha256-WhaKD4cIvZLbwI2vZTkpH/oEeqGiyMvdW3bLi24P0eU=",
79
-
"owner": "mojotech",
80
-
"repo": "pnpm2nix-nzbr",
81
-
"rev": "c3cfff81ea297cfb9dc18928652f375314dc287d",
82
-
"type": "github"
83
-
},
84
-
"original": {
85
-
"owner": "mojotech",
86
-
"repo": "pnpm2nix-nzbr",
87
-
"type": "github"
88
-
}
89
-
},
90
"root": {
91
"inputs": {
92
"flake-utils": "flake-utils",
93
-
"nixpkgs": "nixpkgs",
94
-
"pnpm2nix": "pnpm2nix"
95
}
96
},
97
"systems": {
98
-
"locked": {
99
-
"lastModified": 1681028828,
100
-
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
101
-
"owner": "nix-systems",
102
-
"repo": "default",
103
-
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
104
-
"type": "github"
105
-
},
106
-
"original": {
107
-
"owner": "nix-systems",
108
-
"repo": "default",
109
-
"type": "github"
110
-
}
111
-
},
112
-
"systems_2": {
113
"locked": {
114
"lastModified": 1681028828,
115
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
···
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=",
+5
-89
flake.nix
+5
-89
flake.nix
···
2
description = "Yet another Discord mod";
3
4
inputs = {
5
-
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
6
flake-utils.url = "github:numtide/flake-utils";
7
-
pnpm2nix.url = "github:mojotech/pnpm2nix-nzbr";
8
};
9
10
-
outputs = { self, nixpkgs, flake-utils, pnpm2nix }:
11
-
let
12
-
mkMoonlight = { pkgs, mkPnpmPackage }:
13
-
mkPnpmPackage rec {
14
-
workspace = ./.;
15
-
src = ./.;
16
-
components = [
17
-
"packages/core"
18
-
"packages/core-extensions"
19
-
"packages/injector"
20
-
"packages/node-preload"
21
-
"packages/types"
22
-
"packages/web-preload"
23
-
];
24
-
distDirs = [ "dist" ];
25
-
26
-
copyNodeModules = true;
27
-
buildPhase = "pnpm run build";
28
-
installPhase = "cp -r dist $out";
29
-
30
-
meta = with pkgs.lib; {
31
-
description = "Yet another Discord mod";
32
-
homepage = "https://moonlight-mod.github.io/";
33
-
license = licenses.lgpl3;
34
-
maintainers = with maintainers; [ notnite ];
35
-
};
36
-
};
37
-
38
-
nameTable = {
39
-
discord = "Discord";
40
-
discord-ptb = "DiscordPTB";
41
-
discord-canary = "DiscordCanary";
42
-
discord-development = "DiscordDevelopment";
43
-
};
44
-
45
-
darwinNameTable = {
46
-
discord = "Discord";
47
-
discord-ptb = "Discord PTB";
48
-
discord-canary = "Discord Canary";
49
-
discord-development = "Discord Development";
50
-
};
51
-
52
-
mkOverride = prev: moonlight: name:
53
-
let discord = prev.${name};
54
-
in discord.overrideAttrs (old: {
55
-
installPhase = let
56
-
folderName = nameTable.${name};
57
-
darwinFolderName = darwinNameTable.${name};
58
-
59
-
injected = ''
60
-
require("${moonlight}/injector").inject(
61
-
require("path").join(__dirname, "../_app.asar")
62
-
);
63
-
'';
64
-
65
-
packageJson = ''
66
-
{"name":"discord","main":"./injector.js","private":true}
67
-
'';
68
-
69
-
in old.installPhase + "\n" + ''
70
-
resources="$out/opt/${folderName}/resources"
71
-
if [ ! -d "$resources" ]; then
72
-
resources="$out/Applications/${darwinFolderName}.app/Contents/Resources"
73
-
fi
74
-
75
-
mv "$resources/app.asar" "$resources/_app.asar"
76
-
mkdir -p "$resources/app"
77
-
78
-
cat > "$resources/app/injector.js" <<EOF
79
-
${injected}
80
-
EOF
81
-
82
-
echo '${packageJson}' > "$resources/app/package.json"
83
-
'';
84
-
});
85
-
86
-
overlay = final: prev: rec {
87
-
moonlight-mod = mkMoonlight {
88
-
pkgs = final;
89
-
mkPnpmPackage = pnpm2nix.packages.${final.system}.mkPnpmPackage;
90
-
};
91
-
discord = mkOverride prev moonlight-mod "discord";
92
-
discord-ptb = mkOverride prev moonlight-mod "discord-ptb";
93
-
discord-canary = mkOverride prev moonlight-mod "discord-canary";
94
-
discord-development =
95
-
mkOverride prev moonlight-mod "discord-development";
96
-
};
97
in flake-utils.lib.eachDefaultSystem (system:
98
let
99
pkgs = import nixpkgs {
···
102
overlays = [ overlay ];
103
};
104
in {
105
packages.default = pkgs.moonlight-mod;
106
packages.moonlight-mod = pkgs.moonlight-mod;
107
···
111
packages.discord-development = pkgs.discord-development;
112
}) // {
113
overlays.default = overlay;
114
};
115
}
···
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 {
···
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
···
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.
+51
nix/default.nix
+51
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
+
nativeBuildInputs = [
15
+
nodejs_22
16
+
pnpm_10.configHook
17
+
];
18
+
19
+
pnpmDeps = pnpm_10.fetchDeps {
20
+
inherit (finalAttrs) pname version src;
21
+
hash = "sha256-I+zRCUqJabpGJRFBGW0NrM9xzyzeCjioF54zlCpynBU=";
22
+
};
23
+
24
+
env = {
25
+
NODE_ENV = "production";
26
+
MOONLIGHT_VERSION = "v${finalAttrs.version}";
27
+
};
28
+
29
+
buildPhase = ''
30
+
runHook preBuild
31
+
32
+
pnpm run build
33
+
34
+
runHook postBuild
35
+
'';
36
+
37
+
installPhase = ''
38
+
runHook preInstall
39
+
40
+
cp -r dist $out
41
+
42
+
runHook postInstall
43
+
'';
44
+
45
+
meta = with lib; {
46
+
description = "Yet another Discord mod";
47
+
homepage = "https://moonlight-mod.github.io/";
48
+
license = licenses.lgpl3;
49
+
maintainers = with maintainers; [ notnite ];
50
+
};
51
+
})
+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
-16
package.json
+26
-16
package.json
···
1
{
2
"name": "moonlight",
3
-
"version": "1.0.10",
4
"description": "Yet another Discord mod",
5
-
"homepage": "https://moonlight-mod.github.io/",
6
"license": "LGPL-3.0-or-later",
7
"repository": {
8
"type": "git",
9
"url": "git+https://github.com/moonlight-mod/moonlight.git"
···
11
"bugs": {
12
"url": "https://github.com/moonlight-mod/moonlight/issues"
13
},
14
"scripts": {
15
"build": "node build.mjs",
16
"dev": "node build.mjs --watch",
17
"lint": "eslint packages",
18
-
"lint:fix": "eslint packages",
19
-
"lint:report": "eslint --output-file eslint_report.json --format json packages",
20
"typecheck": "tsc --noEmit",
21
"check": "pnpm run lint && pnpm run typecheck",
22
-
"prepare": "husky install"
23
},
24
"devDependencies": {
25
-
"@typescript-eslint/eslint-plugin": "^6.13.2",
26
-
"@typescript-eslint/parser": "^6.13.2",
27
-
"esbuild": "^0.19.3",
28
-
"esbuild-copy-static-files": "^0.1.0",
29
-
"eslint": "^8.55.0",
30
-
"eslint-config-prettier": "^9.1.0",
31
-
"eslint-plugin-prettier": "^5.0.1",
32
-
"eslint-plugin-react": "^7.33.2",
33
-
"husky": "^8.0.3",
34
-
"prettier": "^3.1.0",
35
-
"typescript": "^5.3.2"
36
}
37
}
···
1
{
2
"name": "moonlight",
3
+
"version": "1.3.14",
4
+
"packageManager": "pnpm@10.7.1",
5
"description": "Yet another Discord mod",
6
"license": "LGPL-3.0-or-later",
7
+
"homepage": "https://moonlight-mod.github.io/",
8
"repository": {
9
"type": "git",
10
"url": "git+https://github.com/moonlight-mod/moonlight.git"
···
12
"bugs": {
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"
21
+
},
22
"scripts": {
23
"build": "node build.mjs",
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",
28
"lint": "eslint packages",
29
+
"lint:fix": "pnpm lint --fix",
30
+
"lint:report": "pnpm lint --output-file eslint_report.json --format json",
31
"typecheck": "tsc --noEmit",
32
"check": "pnpm run lint && pnpm run typecheck",
33
+
"prepare": "husky install",
34
+
"updates": "pnpm taze -r"
35
},
36
"devDependencies": {
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"
46
}
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
+
}
+28
packages/browser/manifestv2.json
+28
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
+
}
+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
+
);
+157
packages/browser/src/index.ts
+157
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
+
},
95
+
// TODO
96
+
addCors(url) {},
97
+
addBlocked(url) {}
98
+
};
99
+
100
+
// Actual loading begins here
101
+
let config = await readConfig();
102
+
initLogger(config);
103
+
104
+
const extensions = await getExtensions();
105
+
const processedExtensions = await loadExtensions(extensions);
106
+
107
+
const moonlightNode: MoonlightNode = {
108
+
get config() {
109
+
return config;
110
+
},
111
+
extensions,
112
+
processedExtensions,
113
+
nativesCache: {},
114
+
isBrowser: true,
115
+
events: createEventEmitter<NodeEventType, NodeEventPayloads>(),
116
+
117
+
version: MOONLIGHT_VERSION,
118
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
119
+
120
+
getConfig(ext) {
121
+
return getConfig(ext, config);
122
+
},
123
+
getConfigOption(ext, name) {
124
+
const manifest = getManifest(extensions, ext);
125
+
return getConfigOption(ext, name, config, manifest?.settings);
126
+
},
127
+
async setConfigOption(ext, name, value) {
128
+
setConfigOption(config, ext, name, value);
129
+
await this.writeConfig(config);
130
+
},
131
+
132
+
getNatives: () => {},
133
+
getLogger: (id: string) => {
134
+
return new Logger(id);
135
+
},
136
+
137
+
getMoonlightDir() {
138
+
return "/";
139
+
},
140
+
getExtensionDir: (ext: string) => {
141
+
return `/extensions/${ext}`;
142
+
},
143
+
144
+
async writeConfig(newConfig) {
145
+
await writeConfig(newConfig);
146
+
config = newConfig;
147
+
this.events.dispatchEvent(NodeEventType.ConfigSaved, newConfig);
148
+
}
149
+
};
150
+
151
+
Object.assign(window, {
152
+
moonlightNode
153
+
});
154
+
155
+
// This is set by web-preload for us
156
+
await window._moonlightWebLoad!();
157
+
};
+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
import { Config } from "@moonlight-mod/types";
2
-
import requireImport from "./util/import";
3
import { getConfigPath } from "./util/data";
4
5
const defaultConfig: Config = {
6
extensions: {
7
moonbase: true,
8
disableSentry: true,
9
noTrack: true,
10
noHideToken: true
11
},
12
-
repositories: ["https://moonlight-mod.github.io/extensions-dist/repo.json"]
13
};
14
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;
28
}
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
}
38
39
-
export function readConfig(): Config {
40
webPreload: {
41
return moonlightNode.config;
42
}
43
44
-
nodePreload: {
45
-
return readConfigNode();
46
-
}
47
48
-
injector: {
49
-
return readConfigNode();
50
}
51
-
52
-
throw new Error("Called readConfig() in an impossible environment");
53
}
···
1
import { Config } from "@moonlight-mod/types";
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");
7
8
const defaultConfig: Config = {
9
+
// If you're updating this, update `builtinExtensions` in constants as well
10
extensions: {
11
moonbase: true,
12
disableSentry: true,
13
noTrack: true,
14
noHideToken: true
15
},
16
+
repositories: [constants.mainRepo]
17
};
18
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);
25
}
26
}
27
28
+
export async function readConfig(): Promise<Config> {
29
webPreload: {
30
return moonlightNode.config;
31
}
32
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);
43
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
+
}
52
}
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
ExtensionWebExports,
3
DetectedExtension,
4
ProcessedExtensions,
5
-
WebpackModuleFunc
6
} from "@moonlight-mod/types";
7
import { readConfig } from "../config";
8
import Logger from "../util/logger";
···
10
import calculateDependencies from "../util/dependency";
11
import { createEventEmitter } from "../util/event";
12
import { registerStyles } from "../styles";
13
14
const logger = new Logger("core/extension/loader");
15
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);
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
-
]);
31
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
-
});
41
42
-
registerPatch({ ...newPatch, ext: ext.id, id: idx });
43
-
idx++;
44
-
}
45
-
} else {
46
-
registerPatch({ ...patch, ext: ext.id, id: idx });
47
-
idx++;
48
-
}
49
}
50
}
51
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
-
}
70
}
71
}
72
73
-
if (exports.styles != null) {
74
-
registerStyles(
75
-
exports.styles.map((style, i) => `/* ${ext.id}#${i} */ ${style}`)
76
-
);
77
-
}
78
}
79
}
80
···
100
}
101
}
102
103
/*
104
This function resolves extensions and loads them, split into a few stages:
105
···
114
extensions fires an event on completion, which allows us to await the loading
115
of another extension, resolving dependencies & load order effectively.
116
*/
117
-
export async function loadExtensions(
118
-
exts: DetectedExtension[]
119
-
): Promise<ProcessedExtensions> {
120
-
const config = readConfig();
121
const items = exts
122
.map((ext) => {
123
return {
···
155
};
156
}
157
158
-
export async function loadProcessedExtensions({
159
-
extensions,
160
-
dependencyGraph
161
-
}: ProcessedExtensions) {
162
-
const eventEmitter = createEventEmitter();
163
const finished: Set<string> = new Set();
164
165
logger.trace(
···
181
}
182
183
function done() {
184
-
eventEmitter.removeEventListener("ext-ready", cb);
185
r();
186
}
187
188
-
eventEmitter.addEventListener("ext-ready", cb);
189
if (finished.has(dep)) done();
190
})
191
);
192
193
if (waitPromises.length > 0) {
194
-
logger.debug(
195
-
`Waiting on ${waitPromises.length} dependencies for "${ext.id}"`
196
-
);
197
await Promise.all(waitPromises);
198
}
199
···
201
await loadExt(ext);
202
203
finished.add(ext.id);
204
-
eventEmitter.dispatchEvent("ext-ready", ext.id);
205
logger.debug(`Loaded "${ext.id}"`);
206
}
207
208
-
webPreload: {
209
for (const ext of extensions) {
210
moonlight.enabledExtensions.add(ext.id);
211
}
···
2
ExtensionWebExports,
3
DetectedExtension,
4
ProcessedExtensions,
5
+
WebpackModuleFunc,
6
+
constants,
7
+
ExtensionManifest,
8
+
ExtensionEnvironment
9
} from "@moonlight-mod/types";
10
import { readConfig } from "../config";
11
import Logger from "../util/logger";
···
13
import calculateDependencies from "../util/dependency";
14
import { createEventEmitter } from "../util/event";
15
import { registerStyles } from "../styles";
16
+
import { WebEventPayloads, WebEventType } from "@moonlight-mod/types/core/event";
17
18
const logger = new Logger("core/extension/loader");
19
20
+
function evalIIFE(id: string, source: string): ExtensionWebExports {
21
+
const fn = new Function("require", "module", "exports", source);
22
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;
51
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
+
}
58
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 });
66
}
67
+
idx++;
68
}
69
+
}
70
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 });
84
}
85
}
86
+
}
87
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);
103
}
104
}
105
···
125
}
126
}
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
+
149
/*
150
This function resolves extensions and loads them, split into a few stages:
151
···
160
extensions fires an event on completion, which allows us to await the loading
161
of another extension, resolving dependencies & load order effectively.
162
*/
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();
167
const items = exts
168
.map((ext) => {
169
return {
···
201
};
202
}
203
204
+
export async function loadProcessedExtensions({ extensions, dependencyGraph }: ProcessedExtensions) {
205
+
const eventEmitter = createEventEmitter<WebEventType, WebEventPayloads>();
206
const finished: Set<string> = new Set();
207
208
logger.trace(
···
224
}
225
226
function done() {
227
+
eventEmitter.removeEventListener(WebEventType.ExtensionLoad, cb);
228
r();
229
}
230
231
+
eventEmitter.addEventListener(WebEventType.ExtensionLoad, cb);
232
if (finished.has(dep)) done();
233
})
234
);
235
236
if (waitPromises.length > 0) {
237
+
logger.debug(`Waiting on ${waitPromises.length} dependencies for "${ext.id}"`);
238
await Promise.all(waitPromises);
239
}
240
···
242
await loadExt(ext);
243
244
finished.add(ext.id);
245
+
eventEmitter.dispatchEvent(WebEventType.ExtensionLoad, ext.id);
246
logger.debug(`Loaded "${ext.id}"`);
247
}
248
249
+
webTarget: {
250
for (const ext of extensions) {
251
moonlight.enabledExtensions.add(ext.id);
252
}
+128
-85
packages/core/src/extension.ts
+128
-85
packages/core/src/extension.ts
···
1
-
import {
2
-
ExtensionManifest,
3
-
DetectedExtension,
4
-
ExtensionLoadSource,
5
-
constants
6
-
} from "@moonlight-mod/types";
7
import { readConfig } from "./config";
8
-
import requireImport from "./util/import";
9
import { getCoreExtensionsPath, getExtensionsPath } from "./util/data";
10
11
-
function findManifests(dir: string): string[] {
12
-
const fs = requireImport("fs");
13
-
const path = requireImport("path");
14
const ret = [];
15
16
-
if (fs.existsSync(dir)) {
17
-
for (const file of fs.readdirSync(dir)) {
18
if (file === "manifest.json") {
19
-
ret.push(path.join(dir, file));
20
}
21
22
-
if (fs.statSync(path.join(dir, file)).isDirectory()) {
23
-
ret.push(...findManifests(path.join(dir, file)));
24
}
25
}
26
}
···
28
return ret;
29
}
30
31
-
function loadDetectedExtensions(
32
dir: string,
33
-
type: ExtensionLoadSource
34
-
): DetectedExtension[] {
35
-
const fs = requireImport("fs");
36
-
const path = requireImport("path");
37
const ret: DetectedExtension[] = [];
38
39
-
const manifests = findManifests(dir);
40
for (const manifestPath of manifests) {
41
-
if (!fs.existsSync(manifestPath)) continue;
42
-
const dir = path.dirname(manifestPath);
43
44
-
const manifest: ExtensionManifest = JSON.parse(
45
-
fs.readFileSync(manifestPath, "utf8")
46
-
);
47
48
-
const webPath = path.join(dir, "index.js");
49
-
const nodePath = path.join(dir, "node.js");
50
-
const hostPath = path.join(dir, "host.js");
51
52
-
// if none exist (empty manifest) don't give a shit
53
-
if (
54
-
!fs.existsSync(webPath) &&
55
-
!fs.existsSync(nodePath) &&
56
-
!fs.existsSync(hostPath)
57
-
) {
58
-
continue;
59
}
60
61
-
const web = fs.existsSync(webPath)
62
-
? fs.readFileSync(webPath, "utf8")
63
-
: undefined;
64
65
-
let url: string | undefined = undefined;
66
-
const urlPath = path.join(dir, constants.repoUrlFile);
67
-
if (type === ExtensionLoadSource.Normal && fs.existsSync(urlPath)) {
68
-
url = fs.readFileSync(urlPath, "utf8");
69
-
}
70
71
-
const wpModules: Record<string, string> = {};
72
-
const wpModulesPath = path.join(dir, "webpackModules");
73
-
if (fs.existsSync(wpModulesPath)) {
74
-
const wpModulesFile = fs.readdirSync(wpModulesPath);
75
76
-
for (const wpModuleFile of wpModulesFile) {
77
-
if (wpModuleFile.endsWith(".js")) {
78
-
wpModules[wpModuleFile.replace(".js", "")] = fs.readFileSync(
79
-
path.join(wpModulesPath, wpModuleFile),
80
-
"utf8"
81
-
);
82
-
}
83
}
84
}
85
···
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: fs.existsSync(nodePath) ? nodePath : undefined,
98
-
hostPath: fs.existsSync(hostPath) ? hostPath : undefined
99
}
100
});
101
}
102
103
-
return ret;
104
-
}
105
-
106
-
function getExtensionsNative(): DetectedExtension[] {
107
-
const config = readConfig();
108
-
const res = [];
109
-
110
-
res.push(
111
-
...loadDetectedExtensions(getCoreExtensionsPath(), ExtensionLoadSource.Core)
112
-
);
113
-
114
-
res.push(
115
-
...loadDetectedExtensions(getExtensionsPath(), ExtensionLoadSource.Normal)
116
-
);
117
-
118
-
for (const devSearchPath of config.devSearchPaths ?? []) {
119
-
res.push(
120
-
...loadDetectedExtensions(devSearchPath, ExtensionLoadSource.Developer)
121
-
);
122
}
123
124
-
return res;
125
}
126
127
-
export function getExtensions(): DetectedExtension[] {
128
webPreload: {
129
return moonlightNode.extensions;
130
}
131
132
-
nodePreload: {
133
-
return getExtensionsNative();
134
}
135
136
-
injector: {
137
-
return getExtensionsNative();
138
}
139
140
throw new Error("Called getExtensions() outside of node-preload/web-preload");
···
1
+
import { ExtensionManifest, DetectedExtension, ExtensionLoadSource, constants } from "@moonlight-mod/types";
2
import { readConfig } from "./config";
3
import { getCoreExtensionsPath, getExtensionsPath } from "./util/data";
4
+
import Logger from "./util/logger";
5
+
6
+
const logger = new Logger("core/extension");
7
8
+
async function findManifests(dir: string): Promise<string[]> {
9
const ret = [];
10
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
}
17
18
+
if (!(await moonlightNodeSandboxed.fs.isFile(path))) {
19
+
ret.push(...(await findManifests(path)));
20
}
21
}
22
}
···
24
return ret;
25
}
26
27
+
async function loadDetectedExtensions(
28
dir: string,
29
+
type: ExtensionLoadSource,
30
+
seen: Set<string>
31
+
): Promise<DetectedExtension[]> {
32
const ret: DetectedExtension[] = [];
33
34
+
const manifests = await findManifests(dir);
35
for (const manifestPath of manifests) {
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");
50
+
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;
63
+
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
+
}
69
+
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);
106
}
107
+
}
108
+
109
+
return ret;
110
+
}
111
+
112
+
async function getExtensionsNative(): Promise<DetectedExtension[]> {
113
+
const config = await readConfig();
114
+
const res = [];
115
+
const seen = new Set<string>();
116
+
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>();
131
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];
145
}
146
}
147
···
149
id: manifest.id,
150
manifest,
151
source: {
152
+
type: ExtensionLoadSource.Core
153
},
154
scripts: {
155
web,
156
webpackModules: wpModules,
157
+
style: coreExtensionsFs[`${ext}/style.css`]
158
}
159
});
160
+
seen.add(manifest.id);
161
}
162
163
+
if (await moonlightNodeSandboxed.fs.exists("/extensions")) {
164
+
ret.push(...(await loadDetectedExtensions("/extensions", ExtensionLoadSource.Normal, seen)));
165
}
166
167
+
return ret;
168
}
169
170
+
export async function getExtensions(): Promise<DetectedExtension[]> {
171
webPreload: {
172
return moonlightNode.extensions;
173
}
174
175
+
browser: {
176
+
return await getExtensionsBrowser();
177
}
178
179
+
nodeTarget: {
180
+
return await getExtensionsNative();
181
}
182
183
throw new Error("Called getExtensions() outside of node-preload/web-preload");
+53
packages/core/src/fs.ts
+53
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
+
};
53
+
}
+238
-106
packages/core/src/patch.ts
+238
-106
packages/core/src/patch.ts
···
6
IdentifiedWebpackModule,
7
WebpackJsonp,
8
WebpackJsonpEntry,
9
-
WebpackModuleFunc
10
} from "@moonlight-mod/types";
11
import Logger from "./util/logger";
12
import calculateDependencies, { Dependency } from "./util/dependency";
13
-
import WebpackRequire from "@moonlight-mod/types/discord/require";
14
15
const logger = new Logger("core/patch");
16
17
// Can't be Set because we need splice
18
const patches: IdentifiedPatch[] = [];
19
let webpackModules: Set<IdentifiedWebpackModule> = new Set();
20
21
export function registerPatch(patch: IdentifiedPatch) {
22
patches.push(patch);
23
moonlight.unpatched.add(patch);
24
}
···
30
}
31
}
32
33
/*
34
The patching system functions by matching a string or regex against the
35
.toString()'d copy of a Webpack module. When a patch happens, we reconstruct
···
42
const moduleCache: Record<string, string> = {};
43
const patched: Record<string, Array<string>> = {};
44
45
function patchModules(entry: WebpackJsonpEntry[1]) {
46
for (const [id, func] of Object.entries(entry)) {
47
-
let moduleString = Object.prototype.hasOwnProperty.call(moduleCache, id)
48
-
? moduleCache[id]
49
-
: func.toString().replace(/\n/g, "");
50
51
for (let i = 0; i < patches.length; i++) {
52
const patch = patches[i];
53
if (patch.prerequisite != null && !patch.prerequisite()) {
54
continue;
55
}
56
···
59
patch.find.lastIndex = 0;
60
}
61
62
-
// indexOf is faster than includes by 0.25% lmao
63
-
const match =
64
-
typeof patch.find === "string"
65
-
? moduleString.indexOf(patch.find) !== -1
66
-
: patch.find.test(moduleString);
67
68
// Global regexes apply to all modules
69
-
const shouldRemove =
70
-
typeof patch.find === "string" ? true : !patch.find.global;
71
72
if (match) {
73
-
moonlight.unpatched.delete(patch);
74
75
-
// We ensured all arrays get turned into normal PatchReplace objects on register
76
-
const replace = patch.replace as PatchReplace;
77
78
-
if (
79
-
replace.type === undefined ||
80
-
replace.type === PatchReplaceType.Normal
81
-
) {
82
-
// Add support for \i to match rspack's minified names
83
-
if (typeof replace.match !== "string") {
84
-
replace.match = new RegExp(
85
-
replace.match.source.replace(/\\i/g, "[A-Za-z_$][\\w$]*"),
86
-
replace.match.flags
87
-
);
88
-
}
89
-
// tsc fails to detect the overloads for this, so I'll just do this
90
-
// Verbose, but it works
91
-
let replaced;
92
-
if (typeof replace.replacement === "string") {
93
-
replaced = moduleString.replace(replace.match, replace.replacement);
94
-
} else {
95
-
replaced = moduleString.replace(replace.match, replace.replacement);
96
-
}
97
98
-
if (replaced === moduleString) {
99
-
logger.warn("Patch replacement failed", id, patch);
100
-
continue;
101
}
102
103
-
// Store what extensions patched what modules for easier debugging
104
-
patched[id] = patched[id] || [];
105
-
patched[id].push(`${patch.ext}#${patch.id}`);
106
107
-
// Webpack module arguments are minified, so we replace them with consistent names
108
-
// We have to wrap it so things don't break, though
109
-
const patchedStr = patched[id].sort().join(", ");
110
111
-
const wrapped =
112
-
`(${replaced}).apply(this, arguments)\n` +
113
-
`// Patched by moonlight: ${patchedStr}\n` +
114
-
`//# sourceURL=Webpack-Module-${id}`;
115
116
-
try {
117
-
const func = new Function(
118
-
"module",
119
-
"exports",
120
-
"require",
121
-
wrapped
122
-
) as WebpackModuleFunc;
123
-
entry[id] = func;
124
-
entry[id].__moonlight = true;
125
-
moduleString = replaced;
126
-
} catch (e) {
127
-
logger.warn("Error constructing function for patch", patch, e);
128
-
patched[id].pop();
129
}
130
-
} else if (replace.type === PatchReplaceType.Module) {
131
-
// Directly replace the module with a new one
132
-
const newModule = replace.replacement(moduleString);
133
-
entry[id] = newModule;
134
-
entry[id].__moonlight = true;
135
-
moduleString =
136
-
newModule.toString().replace(/\n/g, "") +
137
-
`//# sourceURL=Webpack-Module-${id}`;
138
-
}
139
-
140
-
if (shouldRemove) {
141
-
patches.splice(i--, 1);
142
}
143
}
144
}
145
146
if (moonlightNode.config.patchAll === true) {
147
-
if (
148
-
(typeof id !== "string" || !id.includes("_")) &&
149
-
!entry[id].__moonlight
150
-
) {
151
-
const wrapped =
152
-
`(${moduleString}).apply(this, arguments)\n` +
153
-
`//# sourceURL=Webpack-Module-${id}`;
154
-
entry[id] = new Function(
155
-
"module",
156
-
"exports",
157
-
"require",
158
-
wrapped
159
-
) as WebpackModuleFunc;
160
entry[id].__moonlight = true;
161
}
162
}
163
164
moduleCache[id] = moduleString;
165
}
166
}
···
172
*/
173
let chunkId = Number.MAX_SAFE_INTEGER;
174
175
function handleModuleDependencies() {
176
const modules = Array.from(webpackModules.values());
177
178
-
const dependencies: Dependency<string, IdentifiedWebpackModule>[] =
179
-
modules.map((wp) => {
180
-
return {
181
-
id: `${wp.ext}_${wp.id}`,
182
-
data: wp
183
-
};
184
-
});
185
186
const [sorted, _] = calculateDependencies(dependencies, {
187
fetchDep: (id) => {
188
-
return modules.find((x) => id === `${x.ext}_${x.id}`) ?? null;
189
},
190
191
getDeps: (item) => {
192
const deps = item.data?.dependencies ?? [];
193
return (
194
deps.filter(
195
-
(dep) => !(dep instanceof RegExp || typeof dep === "string")
196
) as ExplicitExtensionDependency[]
197
-
).map((x) => `${x.ext}_${x.id}`);
198
}
199
});
200
···
210
for (const [_modId, mod] of Object.entries(entry)) {
211
const modStr = mod.toString();
212
for (const wpModule of webpackModules) {
213
-
const id = wpModule.ext + "_" + wpModule.id;
214
if (wpModule.dependencies) {
215
const deps = new Set(wpModule.dependencies);
216
···
223
} else if (dep instanceof RegExp) {
224
if (dep.test(modStr)) deps.delete(dep);
225
} else if (
226
-
injectedWpModules.find(
227
-
(x) => x.ext === dep.ext && x.id === dep.id
228
-
)
229
) {
230
deps.delete(dep);
231
}
232
}
233
234
if (deps.size !== 0) {
235
-
wpModule.dependencies = Array.from(deps);
236
continue;
237
}
238
-
239
-
wpModule.dependencies = Array.from(deps);
240
}
241
}
242
···
249
if (wpModule.run) {
250
modules[id] = wpModule.run;
251
wpModule.run.__moonlight = true;
252
}
253
-
if (wpModule.entrypoint) entrypoints.push(id);
254
}
255
if (!webpackModules.size) break;
256
}
257
258
if (inject) {
···
260
window.webpackChunkdiscord_app.push([
261
[--chunkId],
262
modules,
263
-
(require: typeof WebpackRequire) => entrypoints.map(require)
264
]);
265
}
266
}
···
269
interface Window {
270
webpackChunkdiscord_app: WebpackJsonp;
271
}
272
}
273
274
/*
···
282
export async function installWebpackPatcher() {
283
await handleModuleDependencies();
284
285
let realWebpackJsonp: WebpackJsonp | null = null;
286
Object.defineProperty(window, "webpackChunkdiscord_app", {
287
set: (jsonp: WebpackJsonp) => {
···
293
const realPush = jsonp.push;
294
if (jsonp.push.__moonlight !== true) {
295
jsonp.push = (items) => {
296
patchModules(items[1]);
297
298
try {
···
335
set(modules: any) {
336
const { stack } = new Error();
337
if (stack!.includes("/assets/") && !Array.isArray(modules)) {
338
patchModules(modules);
339
-
if (!window.webpackChunkdiscord_app)
340
-
window.webpackChunkdiscord_app = [];
341
injectModules(modules);
342
}
343
···
6
IdentifiedWebpackModule,
7
WebpackJsonp,
8
WebpackJsonpEntry,
9
+
WebpackModuleFunc,
10
+
WebpackRequireType
11
} from "@moonlight-mod/types";
12
import Logger from "./util/logger";
13
import calculateDependencies, { Dependency } from "./util/dependency";
14
+
import { WebEventType } from "@moonlight-mod/types/core/event";
15
+
import { processFind, processReplace, testFind } from "./util/patch";
16
17
const logger = new Logger("core/patch");
18
19
// Can't be Set because we need splice
20
const patches: IdentifiedPatch[] = [];
21
let webpackModules: Set<IdentifiedWebpackModule> = new Set();
22
+
let webpackRequire: WebpackRequireType | null = null;
23
+
24
+
const moduleLoadSubscriptions: Map<string, ((moduleId: string) => void)[]> = new Map();
25
26
export function registerPatch(patch: IdentifiedPatch) {
27
+
patch.find = processFind(patch.find);
28
+
processReplace(patch.replace);
29
+
30
patches.push(patch);
31
moonlight.unpatched.add(patch);
32
}
···
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
+
}
55
+
}
56
+
57
/*
58
The patching system functions by matching a string or regex against the
59
.toString()'d copy of a Webpack module. When a patch happens, we reconstruct
···
66
const moduleCache: Record<string, string> = {};
67
const patched: Record<string, Array<string>> = {};
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
+
103
function patchModules(entry: WebpackJsonpEntry[1]) {
104
+
// Populate the module cache
105
for (const [id, func] of Object.entries(entry)) {
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
+
}
111
+
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];
127
if (patch.prerequisite != null && !patch.prerequisite()) {
128
+
moonlight.unpatched.delete(patch);
129
continue;
130
}
131
···
134
patch.find.lastIndex = 0;
135
}
136
137
+
const match = testFind(origModuleString, patch.find) || patch.find === mappedName;
138
139
// Global regexes apply to all modules
140
+
const shouldRemove = typeof patch.find === "string" ? true : !patch.find.global;
141
142
+
let replaced = moduleString;
143
+
let hardFailed = false;
144
if (match) {
145
+
// We ensured normal PatchReplace objects get turned into arrays on register
146
+
const replaces = patch.replace as PatchReplace[];
147
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);
154
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
+
}
163
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;
181
}
182
+
}
183
184
+
if (!hardFailed) {
185
+
moduleString = replaced;
186
+
modified = true;
187
+
exts.add(patch.ext);
188
+
}
189
190
+
if (isPatched) moonlight.unpatched.delete(patch);
191
+
if (shouldRemove) patches.splice(i--, 1);
192
+
}
193
+
}
194
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
+
}
201
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;
208
}
209
}
210
}
211
+
} catch (e) {
212
+
logger.error("Failed to parse script for LunAST", id, e);
213
}
214
215
if (moonlightNode.config.patchAll === true) {
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;
219
entry[id].__moonlight = true;
220
}
221
}
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
+
236
moduleCache[id] = moduleString;
237
}
238
}
···
244
*/
245
let chunkId = Number.MAX_SAFE_INTEGER;
246
247
+
function depToString(x: ExplicitExtensionDependency) {
248
+
return x.ext != null ? `${x.ext}_${x.id}` : x.id;
249
+
}
250
+
251
function handleModuleDependencies() {
252
const modules = Array.from(webpackModules.values());
253
254
+
const dependencies: Dependency<string, IdentifiedWebpackModule>[] = modules.map((wp) => {
255
+
return {
256
+
id: depToString(wp),
257
+
data: wp
258
+
};
259
+
});
260
261
const [sorted, _] = calculateDependencies(dependencies, {
262
fetchDep: (id) => {
263
+
return modules.find((x) => id === depToString(x)) ?? null;
264
},
265
266
getDeps: (item) => {
267
const deps = item.data?.dependencies ?? [];
268
return (
269
deps.filter(
270
+
(dep) => !(dep instanceof RegExp || typeof dep === "string") && dep.ext != null
271
) as ExplicitExtensionDependency[]
272
+
).map(depToString);
273
}
274
});
275
···
285
for (const [_modId, mod] of Object.entries(entry)) {
286
const modStr = mod.toString();
287
for (const wpModule of webpackModules) {
288
+
const id = depToString(wpModule);
289
if (wpModule.dependencies) {
290
const deps = new Set(wpModule.dependencies);
291
···
298
} else if (dep instanceof RegExp) {
299
if (dep.test(modStr)) deps.delete(dep);
300
} else if (
301
+
dep.ext != null
302
+
? injectedWpModules.find((x) => x.ext === dep.ext && x.id === dep.id)
303
+
: injectedWpModules.find((x) => x.id === dep.id)
304
) {
305
deps.delete(dep);
306
}
307
}
308
309
+
wpModule.dependencies = Array.from(deps);
310
if (deps.size !== 0) {
311
continue;
312
}
313
}
314
}
315
···
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
}
335
}
336
if (!webpackModules.size) break;
337
+
}
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
353
if (inject) {
···
355
window.webpackChunkdiscord_app.push([
356
[--chunkId],
357
modules,
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
+
})
370
]);
371
}
372
}
···
375
interface Window {
376
webpackChunkdiscord_app: WebpackJsonp;
377
}
378
+
}
379
+
380
+
function moduleSourceGetter(id: string) {
381
+
return moduleCache[id] ?? null;
382
}
383
384
/*
···
392
export async function installWebpackPatcher() {
393
await handleModuleDependencies();
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
+
408
let realWebpackJsonp: WebpackJsonp | null = null;
409
Object.defineProperty(window, "webpackChunkdiscord_app", {
410
set: (jsonp: WebpackJsonp) => {
···
416
const realPush = jsonp.push;
417
if (jsonp.push.__moonlight !== true) {
418
jsonp.push = (items) => {
419
+
moonlight.events.dispatchEvent(WebEventType.ChunkLoad, {
420
+
chunkId: items[0],
421
+
modules: items[1],
422
+
require: items[2]
423
+
});
424
+
425
patchModules(items[1]);
426
427
try {
···
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
+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
import { constants } from "@moonlight-mod/types";
2
-
import requireImport from "./import";
3
4
-
export function getMoonlightDir(): string {
5
const electron = require("electron");
6
-
const fs = requireImport("fs");
7
-
const path = requireImport("path");
8
9
let appData = "";
10
injector: {
···
15
appData = electron.ipcRenderer.sendSync(constants.ipcGetAppData);
16
}
17
18
-
const dir = path.join(appData, "moonlight-mod");
19
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
20
21
return dir;
22
}
···
26
version: string;
27
};
28
29
-
export function getConfigPath(): string {
30
-
const dir = getMoonlightDir();
31
-
const fs = requireImport("fs");
32
-
const path = requireImport("path");
33
34
let configPath = "";
35
36
-
const buildInfoPath = path.join(process.resourcesPath, "build_info.json");
37
-
if (!fs.existsSync(buildInfoPath)) {
38
-
configPath = path.join(dir, "desktop.json");
39
} else {
40
-
const buildInfo: BuildInfo = JSON.parse(
41
-
fs.readFileSync(buildInfoPath, "utf8")
42
-
);
43
-
configPath = path.join(dir, buildInfo.releaseChannel + ".json");
44
}
45
46
return configPath;
47
}
48
49
-
function getPathFromMoonlight(...names: string[]): string {
50
-
const dir = getMoonlightDir();
51
-
const fs = requireImport("fs");
52
-
const path = requireImport("path");
53
54
-
const target = path.join(dir, ...names);
55
-
if (!fs.existsSync(target)) fs.mkdirSync(target);
56
57
return target;
58
}
59
60
-
export function getExtensionsPath(): string {
61
-
return getPathFromMoonlight(constants.extensionsDir);
62
}
63
64
export function getCoreExtensionsPath(): string {
65
-
const path = requireImport("path");
66
-
const a = path.join(__dirname, constants.coreExtensionsDir);
67
-
return a;
68
}
···
1
import { constants } from "@moonlight-mod/types";
2
+
3
+
export async function getMoonlightDir() {
4
+
browser: {
5
+
return "/";
6
+
}
7
8
const electron = require("electron");
9
10
let appData = "";
11
injector: {
···
16
appData = electron.ipcRenderer.sendSync(constants.ipcGetAppData);
17
}
18
19
+
const dir = moonlightNodeSandboxed.fs.join(appData, "moonlight-mod");
20
+
if (!(await moonlightNodeSandboxed.fs.exists(dir))) await moonlightNodeSandboxed.fs.mkdir(dir);
21
22
return dir;
23
}
···
27
version: string;
28
};
29
30
+
export async function getConfigPath() {
31
+
browser: {
32
+
return "/config.json";
33
+
}
34
+
35
+
const dir = await getMoonlightDir();
36
37
let configPath = "";
38
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");
42
} else {
43
+
const buildInfo: BuildInfo = JSON.parse(await moonlightNodeSandboxed.fs.readFileString(buildInfoPath));
44
+
configPath = moonlightNodeSandboxed.fs.join(dir, buildInfo.releaseChannel + ".json");
45
}
46
47
return configPath;
48
}
49
50
+
async function getPathFromMoonlight(...names: string[]) {
51
+
const dir = await getMoonlightDir();
52
53
+
const target = moonlightNodeSandboxed.fs.join(dir, ...names);
54
+
if (!(await moonlightNodeSandboxed.fs.exists(target))) await moonlightNodeSandboxed.fs.mkdir(target);
55
56
return target;
57
}
58
59
+
export async function getExtensionsPath() {
60
+
return await getPathFromMoonlight(constants.extensionsDir);
61
}
62
63
export function getCoreExtensionsPath(): string {
64
+
return moonlightNodeSandboxed.fs.join(__dirname, constants.coreExtensionsDir);
65
}
+4
-14
packages/core/src/util/dependency.ts
+4
-14
packages/core/src/util/dependency.ts
···
35
const fullDeps: Set<T> = new Set();
36
let failed = false;
37
38
-
// eslint-disable-next-line no-inner-declarations
39
function resolveDeps(id: T, root: boolean) {
40
if (id === item.id && !root) {
41
logger.warn(`Circular dependency detected: "${item.id}"`);
···
113
logger.trace("Enabled stage", itemsOrig);
114
const implicitlyEnabled: T[] = [];
115
116
-
// eslint-disable-next-line no-inner-declarations
117
function validateDeps(dep: Dependency<T, D>) {
118
if (getEnabled!(dep)) {
119
const deps = dependencyGraphOrig.get(dep.id)!;
···
122
validateDeps({ id, data });
123
}
124
} else {
125
-
const dependsOnMe = Array.from(dependencyGraphOrig.entries()).filter(
126
-
([, v]) => v?.has(dep.id)
127
-
);
128
129
if (dependsOnMe.length > 0) {
130
logger.debug("Implicitly enabling dependency", dep.id);
···
134
}
135
136
for (const dep of itemsOrig) validateDeps(dep);
137
-
itemsOrig = itemsOrig.filter(
138
-
(x) => getEnabled(x) || implicitlyEnabled.includes(x.id)
139
-
);
140
}
141
142
if (getIncompatible != null) {
···
176
dependencyGraph.set(item.id, new Set(dependencyGraph.get(item.id)));
177
}
178
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
-
);
185
186
if (noDependents.length === 0) {
187
logger.warn("Stuck dependency graph detected", dependencyGraph);
···
35
const fullDeps: Set<T> = new Set();
36
let failed = false;
37
38
function resolveDeps(id: T, root: boolean) {
39
if (id === item.id && !root) {
40
logger.warn(`Circular dependency detected: "${item.id}"`);
···
112
logger.trace("Enabled stage", itemsOrig);
113
const implicitlyEnabled: T[] = [];
114
115
function validateDeps(dep: Dependency<T, D>) {
116
if (getEnabled!(dep)) {
117
const deps = dependencyGraphOrig.get(dep.id)!;
···
120
validateDeps({ id, data });
121
}
122
} else {
123
+
const dependsOnMe = Array.from(dependencyGraphOrig.entries()).filter(([, v]) => v?.has(dep.id));
124
125
if (dependsOnMe.length > 0) {
126
logger.debug("Implicitly enabling dependency", dep.id);
···
130
}
131
132
for (const dep of itemsOrig) validateDeps(dep);
133
+
itemsOrig = itemsOrig.filter((x) => getEnabled(x) || implicitlyEnabled.includes(x.id));
134
}
135
136
if (getIncompatible != null) {
···
170
dependencyGraph.set(item.id, new Set(dependencyGraph.get(item.id)));
171
}
172
173
+
while (Array.from(dependencyGraph.values()).filter((x) => x != null).length > 0) {
174
+
const noDependents = items.filter((e) => dependencyGraph.get(e.id)?.size === 0);
175
176
if (noDependents.length === 0) {
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;
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
-
}
8
9
-
function nodeMethod(): MoonlightEventEmitter {
10
-
const EventEmitter = require("events");
11
-
const eventEmitter = new EventEmitter();
12
-
const listeners = new Map<MoonlightEventCallback, (...args: any[]) => void>();
13
14
-
return {
15
-
dispatchEvent: (id: string, data: string) => {
16
-
eventEmitter.emit(id, data);
17
-
},
18
19
-
addEventListener: (id: string, cb: (data: string) => void) => {
20
-
if (listeners.has(cb)) return;
21
22
-
function listener(data: string) {
23
-
cb(data);
24
-
}
25
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
-
}
38
39
-
export function createEventEmitter(): MoonlightEventEmitter {
40
-
webPreload: {
41
-
const eventEmitter = new EventTarget();
42
-
const listeners = new Map<MoonlightEventCallback, (e: Event) => void>();
43
44
return {
45
-
dispatchEvent: (id: string, data: string) => {
46
-
eventEmitter.dispatchEvent(new CustomEvent(id, { detail: data }));
47
},
48
49
-
addEventListener: (id: string, cb: (data: string) => void) => {
50
-
if (listeners.has(cb)) return;
51
52
function listener(e: Event) {
53
const event = e as CustomEvent<string>;
54
-
cb(event.detail);
55
}
56
57
-
listeners.set(cb, listener);
58
-
eventEmitter.addEventListener(id, listener);
59
},
60
61
-
removeEventListener: (id: string, cb: (data: string) => void) => {
62
-
const listener = listeners.get(cb);
63
if (listener == null) return;
64
-
listeners.delete(cb);
65
-
eventEmitter.removeEventListener(id, listener);
66
}
67
};
68
-
}
69
-
70
-
nodePreload: {
71
-
return nodeMethod();
72
-
}
73
-
74
-
injector: {
75
-
return nodeMethod();
76
}
77
78
throw new Error("Called createEventEmitter() in an impossible environment");
···
1
+
import { MoonlightEventEmitter } from "@moonlight-mod/types/core/event";
2
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>();
10
11
+
return {
12
+
dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => {
13
+
eventEmitter.dispatchEvent(new CustomEvent(id as string, { detail: data }));
14
+
},
15
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;
19
20
+
function listener(e: Event) {
21
+
const event = e as CustomEvent<string>;
22
+
cb(event.detail as EventData[Id]);
23
+
}
24
25
+
listeners.set(untyped, listener);
26
+
eventEmitter.addEventListener(id as string, listener);
27
+
},
28
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
39
+
nodeTarget: {
40
+
const EventEmitter = require("events");
41
+
const eventEmitter = new EventEmitter();
42
+
const listeners = new Map<(data: EventData) => void, (e: Event) => void>();
43
44
return {
45
+
dispatchEvent: <Id extends keyof EventData>(id: Id, data: EventData[Id]) => {
46
+
eventEmitter.emit(id as string, data);
47
},
48
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;
52
53
function listener(e: Event) {
54
const event = e as CustomEvent<string>;
55
+
cb(event as EventData[Id]);
56
}
57
58
+
listeners.set(untyped, listener);
59
+
eventEmitter.on(id as string, listener);
60
},
61
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);
65
if (listener == null) return;
66
+
listeners.delete(untyped);
67
+
eventEmitter.off(id as string, listener);
68
}
69
};
70
}
71
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
cemented if import is passed a string literal.
10
*/
11
12
-
const canRequire = ["path", "fs"] as const;
13
-
type CanRequire = (typeof canRequire)[number];
14
15
type ImportTypes = {
16
path: typeof import("path");
17
fs: typeof import("fs");
18
};
19
20
-
export default function requireImport<T extends CanRequire>(
21
-
type: T
22
-
): Awaited<ImportTypes[T]> {
23
return require(type);
24
}
···
9
cemented if import is passed a string literal.
10
*/
11
12
+
const _canRequire = ["path", "fs"] as const;
13
+
type CanRequire = (typeof _canRequire)[number];
14
15
type ImportTypes = {
16
path: typeof import("path");
17
fs: typeof import("fs");
18
};
19
20
+
export default function requireImport<T extends CanRequire>(type: T): Awaited<ImportTypes[T]> {
21
return require(type);
22
}
+12
-16
packages/core/src/util/logger.ts
+12
-16
packages/core/src/util/logger.ts
···
1
/* eslint-disable no-console */
2
import { LogLevel } from "@moonlight-mod/types/logger";
3
-
import { readConfig } from "../config";
4
5
const colors = {
6
[LogLevel.SILLY]: "#EDD3E9",
···
11
[LogLevel.ERROR]: "#FF0000"
12
};
13
14
-
const config = readConfig();
15
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
24
export default class Logger {
25
private name: string;
···
57
const logLevel = LogLevel[level].toUpperCase();
58
if (maxLevel > level) return;
59
60
-
if (MOONLIGHT_WEB_PRELOAD) {
61
-
args = [
62
-
`%c[${logLevel}]`,
63
-
`background-color: ${colors[level]}; color: #FFFFFF;`,
64
-
`[${this.name}]`,
65
-
...obj
66
-
];
67
} else {
68
args = [`[${logLevel}]`, `[${this.name}]`, ...obj];
69
}
···
92
}
93
}
94
}
···
1
/* eslint-disable no-console */
2
import { LogLevel } from "@moonlight-mod/types/logger";
3
+
import { Config } from "@moonlight-mod/types";
4
5
const colors = {
6
[LogLevel.SILLY]: "#EDD3E9",
···
11
[LogLevel.ERROR]: "#FF0000"
12
};
13
14
let maxLevel = LogLevel.INFO;
15
16
export default class Logger {
17
private name: string;
···
49
const logLevel = LogLevel[level].toUpperCase();
50
if (maxLevel > level) return;
51
52
+
if (MOONLIGHT_WEB_PRELOAD || MOONLIGHT_BROWSER) {
53
+
args = [`%c[${logLevel}]`, `background-color: ${colors[level]}; color: #FFFFFF;`, `[${this.name}]`, ...obj];
54
} else {
55
args = [`[${logLevel}]`, `[${this.name}]`, ...obj];
56
}
···
79
}
80
}
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
{
2
"name": "@moonlight-mod/core-extensions",
3
"private": true,
4
+
"engineStrict": true,
5
+
"engines": {
6
+
"node": ">=22",
7
+
"pnpm": ">=10",
8
+
"npm": "pnpm",
9
+
"yarn": "pnpm"
10
+
},
11
"dependencies": {
12
+
"@moonlight-mod/core": "workspace:*",
13
+
"@moonlight-mod/types": "workspace:*",
14
+
"microdiff": "catalog:prod",
15
+
"nanotar": "catalog:prod"
16
}
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
-29
packages/core-extensions/src/common/index.ts
+6
-29
packages/core-extensions/src/common/index.ts
···
1
import { ExtensionWebExports } from "@moonlight-mod/types";
2
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: [{ ext: "spacepack", id: "spacepack" }, "connectStores:"]
14
},
15
-
16
-
fluxDispatcher: {
17
-
dependencies: [
18
-
{ ext: "spacepack", id: "spacepack" },
19
-
"isDispatching",
20
-
"dispatch"
21
-
]
22
-
},
23
-
24
-
react: {
25
-
dependencies: [
26
-
{ ext: "spacepack", id: "spacepack" },
27
-
"__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED",
28
-
/\.?version(?:=|:)/,
29
-
/\.?createElement(?:=|:)/
30
-
]
31
-
},
32
-
33
-
stores: {
34
-
dependencies: [{ ext: "common", id: "flux" }]
35
}
36
};
···
1
import { ExtensionWebExports } from "@moonlight-mod/types";
2
3
export const webpackModules: ExtensionWebExports["webpackModules"] = {
4
+
stores: {
5
+
dependencies: [{ id: "discord/packages/flux" }]
6
},
7
+
ErrorBoundary: {
8
+
dependencies: [{ id: "react" }]
9
},
10
+
icons: {
11
+
dependencies: [{ id: "react" }, { id: "discord/components/common/index" }]
12
}
13
};
+3
-1
packages/core-extensions/src/common/manifest.json
+3
-1
packages/core-extensions/src/common/manifest.json
+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;
-41
packages/core-extensions/src/common/webpackModules/components.ts
-41
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.Z;
7
-
const LegacyText = spacepack.findByCode(".selectable", ".colorStandard")[0]
8
-
.exports.default;
9
-
const Flex = Object.values(
10
-
spacepack.findByCode(".flex" + "GutterSmall,")[0].exports
11
-
)[0];
12
-
13
-
const CardClasses = {};
14
-
spacepack
15
-
.lazyLoad(
16
-
"renderArtisanalHack",
17
-
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"ChannelSettings"/,
18
-
/webpackId:(\d+),name:"ChannelSettings"/
19
-
)
20
-
.then(() =>
21
-
Object.assign(
22
-
CardClasses,
23
-
spacepack.findByExports("card", "cardHeader", "inModal")[0].exports
24
-
)
25
-
);
26
-
27
-
const ControlClasses = spacepack.findByCode(
28
-
"title",
29
-
"titleDefault",
30
-
"dividerDefault"
31
-
)[0].exports;
32
-
33
-
// We use CJS export here because merging the exports from Components is annoying as shit
34
-
module.exports = {
35
-
...Components,
36
-
MarkdownParser,
37
-
LegacyText,
38
-
Flex,
39
-
CardClasses,
40
-
ControlClasses
41
-
};
···
-28
packages/core-extensions/src/common/webpackModules/flux.ts
-28
packages/core-extensions/src/common/webpackModules/flux.ts
···
1
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
2
-
3
-
const mod = spacepack.findByCode("connectStores:")[0].exports;
4
-
5
-
const useStateFromStores = spacepack.findFunctionByStrings(
6
-
mod,
7
-
'"useStateFromStores"'
8
-
)!;
9
-
10
-
module.exports = {
11
-
BatchedStoreListener: spacepack.findFunctionByStrings(
12
-
mod,
13
-
" tried to load a non-existent store."
14
-
),
15
-
Dispatcher: spacepack.findFunctionByStrings(mod, "_dispatchWithDevtools("),
16
-
Store: spacepack.findFunctionByStrings(mod, "registerActionHandlers("),
17
-
default: mod.ZP,
18
-
statesWillNeverBeEqual: spacepack.findFunctionByStrings(mod, "return!1"),
19
-
useStateFromStores,
20
-
useStateFromStoresArray: spacepack.findFunctionByStrings(
21
-
mod,
22
-
new RegExp(`return ${useStateFromStores.name}\\(.+?\\.[^Z]\\)`)
23
-
),
24
-
useStateFromStoresObject: spacepack.findFunctionByStrings(
25
-
mod,
26
-
new RegExp(`return ${useStateFromStores.name}\\(.+?\\.Z\\)`)
27
-
)
28
-
};
···
-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
+83
packages/core-extensions/src/componentEditor/index.ts
+83
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: /(?<=\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[\i\(\),.+?\i\(\)])/,
31
+
replacement: (_, decorators) =>
32
+
`children:require("componentEditor_memberList").default._patchDecorators(${decorators},arguments[0])`
33
+
},
34
+
{
35
+
match: /name:null==\i\?\(0,\i\.jsx\)\("span"/,
36
+
replacement: (orig: string) =>
37
+
`children:require("componentEditor_memberList").default._patchItems([],arguments[0]),${orig}`
38
+
}
39
+
]
40
+
},
41
+
42
+
// messages
43
+
{
44
+
find: '},"new-member")),',
45
+
replace: [
46
+
{
47
+
match: /(?<=\.BADGES](=|:))(\i)(;|})/,
48
+
replacement: (_, leading, badges, trailing) =>
49
+
`require("componentEditor_messages").default._patchUsernameBadges(${badges},arguments[0])${trailing}`
50
+
},
51
+
{
52
+
match: /(?<=className:\i,)badges:(\i)/,
53
+
replacement: (_, badges) =>
54
+
`badges:require("componentEditor_messages").default._patchBadges(${badges},arguments[0])`
55
+
},
56
+
{
57
+
match: /(?<=username:\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[.+?])}\),usernameSpanId:/,
58
+
replacement: (_, elements) =>
59
+
`children:require("componentEditor_messages").default._patchUsername(${elements},arguments[0])}),usernameSpanId:`
60
+
}
61
+
]
62
+
},
63
+
{
64
+
find: '.provider&&"Discord"===',
65
+
replace: {
66
+
match: /(?<=\.container\),)children:(\[.+?this\.renderSuppressConfirmModal\(\),.+?\])}\)/,
67
+
replacement: (_, elements) =>
68
+
`children:require("componentEditor_messages").default._patchAccessories(${elements},this.props)})`
69
+
}
70
+
}
71
+
];
72
+
73
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
74
+
dmList: {
75
+
dependencies: [{ id: "react" }]
76
+
},
77
+
memberList: {
78
+
dependencies: [{ id: "react" }]
79
+
},
80
+
messages: {
81
+
dependencies: [{ id: "react" }]
82
+
}
83
+
};
+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;
+5
-13
packages/core-extensions/src/contextMenu/index.tsx
+5
-13
packages/core-extensions/src/contextMenu/index.tsx
···
5
find: "Menu API only allows Items and groups of Items as children.",
6
replace: [
7
{
8
-
match: /(?<=let{navId[^}]+?}=(.),(.)=.\(.\))/,
9
-
replacement: (_, props, items) =>
10
-
`,__contextMenu=!${props}.__contextMenu_evilMenu&&require("contextMenu_contextMenu")._patchMenu(${props}, ${items})`
11
}
12
]
13
},
···
16
replace: [
17
{
18
match: /(?<=let\{[^}]+?\}=.;return ).\({[^}]+?}\)/,
19
-
replacement: (render) =>
20
-
`require("contextMenu_contextMenu")._saveProps(this,${render})`
21
}
22
]
23
}
···
25
26
export const webpackModules: Record<string, ExtensionWebpackModule> = {
27
contextMenu: {
28
-
dependencies: [
29
-
{ ext: "spacepack", id: "spacepack" },
30
-
"Menu API only allows Items and groups of Items as children."
31
-
]
32
},
33
evilMenu: {
34
-
dependencies: [
35
-
{ ext: "spacepack", id: "spacepack" },
36
-
"Menu API only allows Items and groups of Items as children."
37
-
]
38
}
39
};
···
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
},
···
15
replace: [
16
{
17
match: /(?<=let\{[^}]+?\}=.;return ).\({[^}]+?}\)/,
18
+
replacement: (render) => `require("contextMenu_contextMenu")._saveProps(this,${render})`
19
}
20
]
21
}
···
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
};
+2
packages/core-extensions/src/contextMenu/manifest.json
+2
packages/core-extensions/src/contextMenu/manifest.json
+25
-30
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
+25
-30
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
···
1
-
import {
2
-
InternalItem,
3
-
MenuElement,
4
-
MenuProps
5
-
} from "@moonlight-mod/types/coreExtensions/contextMenu";
6
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
7
import parser from "@moonlight-mod/wp/contextMenu_evilMenu";
8
9
type Patch = {
10
navId: string;
11
-
item: (
12
-
props: any
13
-
) =>
14
-
| React.ReactComponentElement<MenuElement>
15
-
| React.ReactComponentElement<MenuElement>[];
16
-
anchorId: string;
17
before: boolean;
18
};
19
20
-
export function addItem<T>(
21
-
navId: string,
22
-
item: (
23
-
props: T
24
-
) =>
25
-
| React.ReactComponentElement<MenuElement>
26
-
| React.ReactComponentElement<MenuElement>[],
27
-
anchorId: string,
28
-
before = false
29
-
) {
30
-
patches.push({ navId, item, anchorId, before });
31
}
32
33
-
export const patches: Patch[] = [];
34
-
function _patchMenu(props: MenuProps, items: InternalItem[]) {
35
const matches = patches.filter((p) => p.navId === props.navId);
36
-
if (!matches.length) return;
37
38
for (const patch of matches) {
39
-
const idx = items.findIndex((i) => i.key === patch.anchorId);
40
if (idx === -1) continue;
41
-
items.splice(idx + 1 - +patch.before, 0, ...parser(patch.item(menuProps)));
42
}
43
}
44
45
let menuProps: any;
···
56
}
57
58
module.exports = {
59
addItem,
60
_patchMenu,
61
_saveProps
62
};
63
64
// Unmangle Menu elements
65
const code =
66
spacepack.require.m[
67
-
spacepack.findByCode(
68
-
"Menu API only allows Items and groups of Items as children."
69
-
)[0].id
70
].toString();
71
72
let MangledMenu;
···
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;
···
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;
+11
-20
packages/core-extensions/src/contextMenu/webpackModules/evilMenu.ts
+11
-20
packages/core-extensions/src/contextMenu/webpackModules/evilMenu.ts
···
1
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
2
3
let code =
4
spacepack.require.m[
5
-
spacepack.findByCode(
6
-
"Menu API only allows Items and groups of Items as children."
7
-
)[0].id
8
].toString();
9
-
code = code.replace(
10
-
/onSelect:(.)}=(.),.=(.\(.\)),/,
11
-
`onSelect:$1}=$2;return $3;let `
12
-
);
13
-
const mod = new Function(
14
-
"module",
15
-
"exports",
16
-
"require",
17
-
`(${code}).apply(this, arguments)`
18
-
);
19
const exp: any = {};
20
mod({}, exp, require);
21
-
const Menu = spacepack.findFunctionByStrings(exp, "isUsingKeyboardNavigation")!;
22
-
module.exports = (el: any) => {
23
-
return Menu({
24
-
children: el,
25
-
__contextMenu_evilMenu: true
26
-
});
27
-
};
···
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
-25
packages/core-extensions/src/disableSentry/host.ts
+4
-25
packages/core-extensions/src/disableSentry/host.ts
···
1
import { join } from "node:path";
2
import { Module } from "node:module";
3
-
import { BrowserWindow } from "electron";
4
5
const logger = moonlightHost.getLogger("disableSentry");
6
7
if (moonlightHost.asarPath !== "moonlightDesktop") {
8
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
-
);
16
require.cache[hostSentryPath]!.exports = {
17
init: () => {},
18
captureException: () => {},
19
setTag: () => {},
20
-
setUser: () => {}
21
};
22
logger.debug("Stubbed Sentry host side!");
23
} catch (err) {
24
logger.error("Failed to stub Sentry host side:", err);
25
}
26
}
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
-
"https://discord.com/assets/sentry.*.js",
35
-
"https://*.discord.com/assets/sentry.*.js"
36
-
]
37
-
},
38
-
function (details, callback) {
39
-
callback({ cancel: true });
40
-
}
41
-
);
42
-
});
···
1
import { join } from "node:path";
2
import { Module } from "node:module";
3
4
const logger = moonlightHost.getLogger("disableSentry");
5
6
if (moonlightHost.asarPath !== "moonlightDesktop") {
7
try {
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)]);
10
require.cache[hostSentryPath]!.exports = {
11
init: () => {},
12
captureException: () => {},
13
setTag: () => {},
14
+
setUser: () => {},
15
+
captureMessage: () => {}
16
};
17
logger.debug("Stubbed Sentry host side!");
18
} catch (err) {
19
logger.error("Failed to stub Sentry host side:", err);
20
}
21
}
+5
-6
packages/core-extensions/src/disableSentry/index.ts
+5
-6
packages/core-extensions/src/disableSentry/index.ts
···
6
find: "profiledRootComponent:",
7
replace: {
8
type: PatchReplaceType.Normal,
9
-
match: /(?<=\.Z=){.+?}}/,
10
-
replacement: 'require("disableSentry_stub").proxy()'
11
}
12
},
13
{
14
-
find: "window.DiscordSentry.addBreadcrumb",
15
replace: {
16
type: PatchReplaceType.Normal,
17
-
match: /Z:function\(\){return .}/,
18
-
replacement:
19
-
'default:function(){return (...args)=>{moonlight.getLogger("disableSentry").debug("Sentry calling addBreadcrumb passthrough:", ...args);}}'
20
}
21
},
22
{
···
6
find: "profiledRootComponent:",
7
replace: {
8
type: PatchReplaceType.Normal,
9
+
match: /Z:\(\)=>\i/,
10
+
replacement: 'Z:()=>require("disableSentry_stub").proxy()'
11
}
12
},
13
{
14
+
find: "this._sentryUtils=",
15
replace: {
16
type: PatchReplaceType.Normal,
17
+
match: /(?<=this._sentryUtils=)./,
18
+
replacement: "undefined"
19
}
20
},
21
{
+12
-1
packages/core-extensions/src/disableSentry/manifest.json
+12
-1
packages/core-extensions/src/disableSentry/manifest.json
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "disableSentry",
4
+
"apiLevel": 2,
5
"meta": {
6
"name": "Disable Sentry",
7
"tagline": "Turns off Discord's error reporting systems",
8
"authors": ["Cynosphere", "NotNite"],
9
"tags": ["privacy"]
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
+
]
20
}
+13
-19
packages/core-extensions/src/disableSentry/node.ts
+13
-19
packages/core-extensions/src/disableSentry/node.ts
···
5
6
const logger = moonlightNode.getLogger("disableSentry");
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
-
}
27
}
···
5
6
const logger = moonlightNode.getLogger("disableSentry");
7
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);
21
}
+1
-2
packages/core-extensions/src/disableSentry/webpackModules/stub.ts
+1
-2
packages/core-extensions/src/disableSentry/webpackModules/stub.ts
+39
-2
packages/core-extensions/src/experiments/index.ts
+39
-2
packages/core-extensions/src/experiments/index.ts
···
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)",
22
+
replace: {
23
+
match: /&&\((.)\?\(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:(.),/,
51
+
replacement: (_, isStaff) =>
52
+
`.useReducedMotion,isStaff:(moonlight.getConfigOption("experiments","staffSettings")??false)?true:${isStaff},`
53
}
54
}
55
];
+12
-2
packages/core-extensions/src/experiments/manifest.json
+12
-2
packages/core-extensions/src/experiments/manifest.json
···
1
{
2
"id": "experiments",
3
"meta": {
4
"name": "Experiments",
5
"tagline": "Allows you to configure Discord's internal A/B testing features",
···
7
"tags": ["dangerZone"]
8
},
9
"settings": {
10
-
"sections": {
11
"displayName": "Allow access to other staff settings elsewhere",
12
-
"type": "boolean"
13
}
14
}
15
}
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "experiments",
4
+
"apiLevel": 2,
5
"meta": {
6
"name": "Experiments",
7
"tagline": "Allows you to configure Discord's internal A/B testing features",
···
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
}
24
}
25
}
+7
-19
packages/core-extensions/src/markdown/index.ts
+7
-19
packages/core-extensions/src/markdown/index.ts
···
6
replace: [
7
{
8
match: /={newline:(.+?)},(.{1,2})=\(0,/,
9
-
replacement: (_, rules, RULES) =>
10
-
`=require("markdown_markdown")._addRules({newline:${rules}}),${RULES}=(0,`
11
},
12
{
13
-
match: /(?<=var (.{1,2})={RULES:.+?})/,
14
-
replacement: (_, rulesets) =>
15
-
`;require("markdown_markdown")._applyRulesetBlacklist(${rulesets});`
16
}
17
]
18
},
···
25
`__slateRules,${rulesDef}=__slateRules=require("markdown_markdown")._addSlateRules({link:{${rules}}),${syntaxBefore}=new Set`
26
},
27
{
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:`
40
}
41
]
42
},
···
44
find: '"Slate: Unknown decoration attribute: "',
45
replace: {
46
match: /=({strong:.+?});/,
47
-
replacement: (_, rules) =>
48
-
`=require("markdown_markdown")._addSlateDecorators(${rules});`
49
}
50
}
51
];
···
6
replace: [
7
{
8
match: /={newline:(.+?)},(.{1,2})=\(0,/,
9
+
replacement: (_, rules, RULES) => `=require("markdown_markdown")._addRules({newline:${rules}}),${RULES}=(0,`
10
},
11
{
12
+
match: /(?<=;(.{1,2}\.Z)={RULES:.+?})/,
13
+
replacement: (_, rulesets) => `;require("markdown_markdown")._applyRulesetBlacklist(${rulesets});`
14
}
15
]
16
},
···
23
`__slateRules,${rulesDef}=__slateRules=require("markdown_markdown")._addSlateRules({link:{${rules}}),${syntaxBefore}=new Set`
24
},
25
{
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:`
29
}
30
]
31
},
···
33
find: '"Slate: Unknown decoration attribute: "',
34
replace: {
35
match: /=({strong:.+?});/,
36
+
replacement: (_, rules) => `=require("markdown_markdown")._addSlateDecorators(${rules});`
37
}
38
}
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";
7
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
-
> = {};
16
export const slateDecorators: Record<string, string> = {};
17
export const ruleBlacklists: Record<Ruleset, Record<string, boolean>> = {
18
RULES: {},
···
67
return originalRules;
68
}
69
70
-
export function _applyRulesetBlacklist(
71
-
rulesets: Record<Ruleset, Record<string, MarkdownRule>>
72
-
) {
73
for (const ruleset of Object.keys(rulesets) as Ruleset[]) {
74
if (ruleset === "RULES") continue;
75
···
1
+
import { MarkdownRule, Ruleset, SlateRule } from "@moonlight-mod/types/coreExtensions/markdown";
2
3
+
export const rules: Record<string, (rules: Record<string, MarkdownRule>) => MarkdownRule> = {};
4
+
export const slateRules: Record<string, (rules: Record<string, SlateRule>) => SlateRule> = {};
5
export const slateDecorators: Record<string, string> = {};
6
export const ruleBlacklists: Record<Ruleset, Record<string, boolean>> = {
7
RULES: {},
···
56
return originalRules;
57
}
58
59
+
export function _applyRulesetBlacklist(rulesets: Record<Ruleset, Record<string, MarkdownRule>>) {
60
for (const ruleset of Object.keys(rulesets) as Ruleset[]) {
61
if (ruleset === "RULES") continue;
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
+
});
+80
-17
packages/core-extensions/src/moonbase/index.tsx
+80
-17
packages/core-extensions/src/moonbase/index.tsx
···
1
-
import { ExtensionWebExports } from "@moonlight-mod/types";
2
3
-
export const webpackModules: ExtensionWebExports["webpackModules"] = {
4
stores: {
5
-
dependencies: [
6
-
{ ext: "common", id: "flux" },
7
-
{ ext: "common", id: "fluxDispatcher" }
8
-
]
9
},
10
11
ui: {
12
dependencies: [
13
{ ext: "spacepack", id: "spacepack" },
14
-
{ ext: "common", id: "react" },
15
-
{ ext: "common", id: "components" },
16
{ ext: "moonbase", id: "stores" },
17
"Masks.PANEL_BUTTON",
18
-
"renderArtisanalHack(){",
19
'"Missing channel in Channel.openChannelContextMenu"',
20
".forumOrHome]:"
21
]
22
},
23
24
-
moonbase: {
25
dependencies: [
26
{ ext: "spacepack", id: "spacepack" },
27
{ ext: "settings", id: "settings" },
28
-
{ ext: "common", id: "react" },
29
-
{ ext: "moonbase", id: "ui" }
30
],
31
entrypoint: true
32
}
33
};
34
-
35
-
export const styles = [
36
-
".moonbase-settings > :first-child { margin-top: 0px; }",
37
-
"textarea.moonbase-resizeable { resize: vertical }"
38
-
];
···
1
+
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
{
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
+
]
31
+
}
32
+
];
33
34
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
35
stores: {
36
+
dependencies: [{ id: "discord/packages/flux" }, { id: "discord/Dispatcher" }]
37
},
38
39
ui: {
40
dependencies: [
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]:"
52
]
53
},
54
55
+
ThemeDarkIcon: {
56
+
dependencies: [{ ext: "common", id: "icons" }, { id: "react" }]
57
+
},
58
+
59
+
settings: {
60
dependencies: [
61
{ ext: "spacepack", id: "spacepack" },
62
{ ext: "settings", id: "settings" },
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" },
74
+
{ ext: "moonbase", id: "stores" },
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" }
83
+
],
84
+
entrypoint: true
85
+
},
86
+
87
+
moonbase: {
88
+
dependencies: [{ ext: "moonbase", id: "stores" }]
89
+
},
90
+
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
+
]
100
}
101
};
+35
-5
packages/core-extensions/src/moonbase/manifest.json
+35
-5
packages/core-extensions/src/moonbase/manifest.json
···
1
{
2
"id": "moonbase",
3
"meta": {
4
"name": "Moonbase",
5
"tagline": "The official settings UI for moonlight",
6
-
"authors": ["Cynosphere", "NotNite"]
7
},
8
-
"dependencies": ["spacepack", "settings", "common"],
9
"settings": {
10
"sections": {
11
"displayName": "Split into sections",
12
"description": "Show the Moonbase tabs as separate sections",
13
-
"type": "boolean"
14
},
15
"saveFilter": {
16
"displayName": "Persist filter",
17
"description": "Save extension filter in config",
18
-
"type": "boolean"
19
}
20
-
}
21
}
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "moonbase",
4
+
"apiLevel": 2,
5
"meta": {
6
"name": "Moonbase",
7
"tagline": "The official settings UI for moonlight",
8
+
"authors": ["Cynosphere", "NotNite", "redstonekasi"]
9
},
10
+
"dependencies": ["spacepack", "settings", "common", "notices", "contextMenu"],
11
"settings": {
12
"sections": {
13
+
"advice": "reload",
14
"displayName": "Split into sections",
15
"description": "Show the Moonbase tabs as separate sections",
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
24
},
25
"saveFilter": {
26
+
"advice": "none",
27
"displayName": "Persist filter",
28
"description": "Save extension filter in config",
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
45
}
46
+
},
47
+
"cors": [
48
+
"https://github.com/moonlight-mod/moonlight/releases/download/",
49
+
"https://objects.githubusercontent.com/github-production-release-asset-"
50
+
]
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;
+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
+
}
+26
-10
packages/core-extensions/src/moonbase/types.ts
+26
-10
packages/core-extensions/src/moonbase/types.ts
···
1
-
import { DetectedExtension, ExtensionManifest } from "types/src";
2
3
export type MoonbaseNatives = {
4
-
fetchRepositories(
5
-
repos: string[]
6
-
): Promise<Record<string, RepositoryManifest[]>>;
7
-
installExtension(
8
-
manifest: RepositoryManifest,
9
-
url: string,
10
-
repo: string
11
-
): Promise<void>;
12
deleteExtension(id: string): Promise<void>;
13
-
getExtensionConfig(id: string, key: string): any;
14
};
15
16
export type RepositoryManifest = ExtensionManifest & {
···
29
manifest: ExtensionManifest | RepositoryManifest;
30
source: DetectedExtension["source"];
31
state: ExtensionState;
32
};
···
1
+
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
2
+
import { DetectedExtension, ExtensionManifest, MoonlightBranch } from "@moonlight-mod/types";
3
4
export type MoonbaseNatives = {
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>;
10
deleteExtension(id: string): Promise<void>;
11
};
12
13
export type RepositoryManifest = ExtensionManifest & {
···
26
manifest: ExtensionManifest | RepositoryManifest;
27
source: DetectedExtension["source"];
28
state: ExtensionState;
29
+
compat: ExtensionCompat;
30
+
hasUpdate: boolean;
31
+
changelog?: string;
32
+
settingsOverride?: ExtensionManifest["settings"];
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
+
}
+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
+
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
+
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;
-44
packages/core-extensions/src/moonbase/webpackModules/moonbase.tsx
-44
packages/core-extensions/src/moonbase/webpackModules/moonbase.tsx
···
1
-
import settings from "@moonlight-mod/wp/settings_settings";
2
-
import React from "@moonlight-mod/wp/common_react";
3
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
4
-
import { Moonbase, pages } from "@moonlight-mod/wp/moonbase_ui";
5
-
6
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
7
-
import { MenuItem } from "@moonlight-mod/wp/common_components";
8
-
9
-
const { open } = spacepack.findByExports("setSection", "clearSubsection")[0]
10
-
.exports.Z;
11
-
12
-
settings.addSection("moonbase", "Moonbase", Moonbase, null, -2, {
13
-
stores: [MoonbaseSettingsStore],
14
-
element: () => {
15
-
// Require it here because lazy loading SUX
16
-
const SettingsNotice = spacepack.findByCode(
17
-
"onSaveButtonColor",
18
-
"FocusRingScope"
19
-
)[0].exports.Z;
20
-
return (
21
-
<SettingsNotice
22
-
submitting={MoonbaseSettingsStore.submitting}
23
-
onReset={() => {
24
-
MoonbaseSettingsStore.reset();
25
-
}}
26
-
onSave={() => {
27
-
MoonbaseSettingsStore.writeConfig();
28
-
}}
29
-
/>
30
-
);
31
-
}
32
-
});
33
-
34
-
settings.addSectionMenuItems(
35
-
"moonbase",
36
-
...pages.map((page, i) => (
37
-
<MenuItem
38
-
key={page.id}
39
-
id={`moonbase-${page.id}`}
40
-
label={page.name}
41
-
action={() => open("moonbase", i)}
42
-
/>
43
-
))
44
-
);
···
+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={() => {
23
+
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
+
}
+340
-89
packages/core-extensions/src/moonbase/webpackModules/stores.ts
+340
-89
packages/core-extensions/src/moonbase/webpackModules/stores.ts
···
1
-
import { Config, ExtensionLoadSource } from "@moonlight-mod/types";
2
-
import { ExtensionState, MoonbaseExtension, MoonbaseNatives } from "../types";
3
-
import Flux from "@moonlight-mod/wp/common_flux";
4
-
import Dispatcher from "@moonlight-mod/wp/common_fluxDispatcher";
5
6
-
const natives: MoonbaseNatives = moonlight.getNatives("moonbase");
7
const logger = moonlight.getLogger("moonbase");
8
9
-
class MoonbaseSettingsStore extends Flux.Store<any> {
10
-
private origConfig: Config;
11
private config: Config;
12
private extensionIndex: number;
13
14
modified: boolean;
15
submitting: boolean;
16
installing: boolean;
17
18
extensions: { [id: number]: MoonbaseExtension };
19
-
updates: { [id: number]: { version: string; download: string } };
20
21
constructor() {
22
super(Dispatcher);
23
24
-
this.origConfig = moonlightNode.config;
25
-
this.config = this.clone(this.origConfig);
26
this.extensionIndex = 0;
27
28
this.modified = false;
29
this.submitting = false;
30
this.installing = false;
31
32
this.extensions = {};
33
this.updates = {};
34
for (const ext of moonlightNode.extensions) {
···
36
this.extensions[uniqueId] = {
37
...ext,
38
uniqueId,
39
-
state: moonlight.enabledExtensions.has(ext.id)
40
-
? ExtensionState.Enabled
41
-
: ExtensionState.Disabled
42
};
43
}
44
45
-
natives.fetchRepositories(this.config.repositories).then((ret) => {
46
-
for (const [repo, exts] of Object.entries(ret)) {
47
-
try {
48
-
for (const ext of exts) {
49
-
const uniqueId = this.extensionIndex++;
50
-
const extensionData = {
51
-
id: ext.id,
52
-
uniqueId,
53
-
manifest: ext,
54
-
source: { type: ExtensionLoadSource.Normal, url: repo },
55
-
state: ExtensionState.NotDownloaded
56
-
};
57
58
-
if (this.alreadyExists(extensionData)) {
59
-
if (this.hasUpdate(extensionData)) {
60
-
this.updates[uniqueId] = {
61
-
version: ext.version!,
62
-
download: ext.download
63
-
};
64
-
}
65
66
-
continue;
67
-
}
68
69
-
this.extensions[uniqueId] = extensionData;
70
}
71
-
} catch (e) {
72
-
logger.error(`Error processing repository ${repo}`, e);
73
}
74
}
75
76
-
this.emitChange();
77
-
});
78
}
79
80
-
private alreadyExists(ext: MoonbaseExtension) {
81
-
return Object.values(this.extensions).some(
82
-
(e) => e.id === ext.id && e.source.url === ext.source.url
83
-
);
84
}
85
86
private hasUpdate(ext: MoonbaseExtension) {
87
-
const existing = Object.values(this.extensions).find(
88
-
(e) => e.id === ext.id && e.source.url === ext.source.url
89
-
);
90
if (existing == null) return false;
91
92
-
return (
93
-
existing.manifest.version !== ext.manifest.version &&
94
-
existing.state !== ExtensionState.NotDownloaded
95
-
);
96
}
97
98
// Jank
99
private isModified() {
100
-
const orig = JSON.stringify(this.origConfig);
101
const curr = JSON.stringify(this.config);
102
return orig !== curr;
103
}
···
106
return this.submitting || this.installing;
107
}
108
109
showNotice() {
110
return this.modified;
111
}
···
115
}
116
117
getExtensionUniqueId(id: string) {
118
-
return Object.values(this.extensions).find((ext) => ext.id === id)
119
-
?.uniqueId;
120
}
121
122
getExtensionConflicting(uniqueId: number) {
123
const ext = this.getExtension(uniqueId);
124
if (ext.state !== ExtensionState.NotDownloaded) return false;
125
return Object.values(this.extensions).some(
126
-
(e) =>
127
-
e.id === ext.id &&
128
-
e.uniqueId !== uniqueId &&
129
-
e.state !== ExtensionState.NotDownloaded
130
);
131
}
132
···
149
150
getExtensionConfig<T>(uniqueId: number, key: string): T | undefined {
151
const ext = this.getExtension(uniqueId);
152
-
const defaultValue = ext.manifest.settings?.[key]?.default;
153
-
const clonedDefaultValue = this.clone(defaultValue);
154
-
const cfg = this.config.extensions[ext.id];
155
156
-
if (cfg == null || typeof cfg === "boolean") return clonedDefaultValue;
157
-
return cfg.config?.[key] ?? clonedDefaultValue;
158
}
159
160
getExtensionConfigName(uniqueId: number, key: string) {
161
const ext = this.getExtension(uniqueId);
162
-
return ext.manifest.settings?.[key]?.displayName ?? key;
163
}
164
165
getExtensionConfigDescription(uniqueId: number, key: string) {
166
const ext = this.getExtension(uniqueId);
167
-
return ext.manifest.settings?.[key]?.description;
168
}
169
170
-
setExtensionConfig(uniqueId: number, key: string, value: any) {
171
-
const ext = this.getExtension(uniqueId);
172
-
const oldConfig = this.config.extensions[ext.id];
173
-
const newConfig =
174
-
typeof oldConfig === "boolean"
175
-
? {
176
-
enabled: oldConfig,
177
-
config: { [key]: value }
178
-
}
179
-
: {
180
-
...oldConfig,
181
-
config: { ...(oldConfig?.config ?? {}), [key]: value }
182
-
};
183
-
184
-
this.config.extensions[ext.id] = newConfig;
185
this.modified = this.isModified();
186
this.emitChange();
187
}
···
208
this.emitChange();
209
}
210
211
async installExtension(uniqueId: number) {
212
const ext = this.getExtension(uniqueId);
213
if (!("download" in ext.manifest)) {
···
216
217
this.installing = true;
218
try {
219
-
const url = this.updates[uniqueId]?.download ?? ext.manifest.download;
220
-
await natives.installExtension(ext.manifest, url, ext.source.url!);
221
if (ext.state === ExtensionState.NotDownloaded) {
222
this.extensions[uniqueId].state = ExtensionState.Disabled;
223
}
224
225
delete this.updates[uniqueId];
226
} catch (e) {
227
logger.error("Error installing extension:", e);
228
}
229
230
this.installing = false;
231
this.emitChange();
232
}
233
234
async deleteExtension(uniqueId: number) {
235
const ext = this.getExtension(uniqueId);
236
if (ext == null) return;
237
238
this.installing = true;
239
try {
240
-
await natives.deleteExtension(ext.id);
241
this.extensions[uniqueId].state = ExtensionState.NotDownloaded;
242
} catch (e) {
243
logger.error("Error deleting extension:", e);
244
}
245
246
this.installing = false;
247
this.emitChange();
248
}
249
···
257
this.emitChange();
258
}
259
260
writeConfig() {
261
this.submitting = true;
262
263
-
try {
264
-
moonlightNode.writeConfig(this.config);
265
-
this.origConfig = this.clone(this.config);
266
-
} catch (e) {
267
-
logger.error("Error writing config", e);
268
-
}
269
270
this.submitting = false;
271
this.modified = false;
272
this.emitChange();
273
}
274
275
reset() {
276
this.submitting = false;
277
this.modified = false;
278
-
this.config = this.clone(this.origConfig);
279
this.emitChange();
280
}
281
282
// Required because electron likes to make it immutable sometimes.
···
1
+
import { Config, ExtensionEnvironment, ExtensionLoadSource, ExtensionSettingsAdvice } from "@moonlight-mod/types";
2
+
import {
3
+
ExtensionState,
4
+
MoonbaseExtension,
5
+
MoonbaseNatives,
6
+
RepositoryManifest,
7
+
RestartAdvice,
8
+
UpdateState
9
+
} from "../types";
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 { getConfigOption, setConfigOption } from "@moonlight-mod/core/util/config";
17
+
import diff from "microdiff";
18
19
const logger = moonlight.getLogger("moonbase");
20
21
+
let natives: MoonbaseNatives = moonlight.getNatives("moonbase");
22
+
if (moonlightNode.isBrowser) natives = getNatives();
23
+
24
+
class MoonbaseSettingsStore extends Store<any> {
25
+
private initialConfig: Config;
26
+
private savedConfig: Config;
27
private config: Config;
28
private extensionIndex: number;
29
+
private configComponents: Record<string, Record<string, CustomComponent>> = {};
30
31
modified: boolean;
32
submitting: boolean;
33
installing: boolean;
34
35
+
#updateState = UpdateState.Ready;
36
+
get updateState() {
37
+
return this.#updateState;
38
+
}
39
+
newVersion: string | null;
40
+
shouldShowNotice: boolean;
41
+
42
+
restartAdvice = RestartAdvice.NotNeeded;
43
+
44
extensions: { [id: number]: MoonbaseExtension };
45
+
updates: {
46
+
[id: number]: {
47
+
version: string;
48
+
download: string;
49
+
updateManifest: RepositoryManifest;
50
+
};
51
+
};
52
53
constructor() {
54
super(Dispatcher);
55
56
+
this.initialConfig = moonlightNode.config;
57
+
this.savedConfig = moonlightNode.config;
58
+
this.config = this.clone(this.savedConfig);
59
this.extensionIndex = 0;
60
61
this.modified = false;
62
this.submitting = false;
63
this.installing = false;
64
65
+
this.newVersion = null;
66
+
this.shouldShowNotice = false;
67
+
68
this.extensions = {};
69
this.updates = {};
70
for (const ext of moonlightNode.extensions) {
···
72
this.extensions[uniqueId] = {
73
...ext,
74
uniqueId,
75
+
state: moonlight.enabledExtensions.has(ext.id) ? ExtensionState.Enabled : ExtensionState.Disabled,
76
+
compat: checkExtensionCompat(ext.manifest),
77
+
hasUpdate: false
78
};
79
}
80
81
+
this.checkUpdates();
82
+
}
83
84
+
async checkUpdates() {
85
+
await Promise.all([this.checkExtensionUpdates(), this.checkMoonlightUpdates()]);
86
+
this.shouldShowNotice = this.newVersion != null || Object.keys(this.updates).length > 0;
87
+
this.emitChange();
88
+
}
89
+
90
+
private async checkExtensionUpdates() {
91
+
const repositories = await natives!.fetchRepositories(this.savedConfig.repositories);
92
+
93
+
// Reset update state
94
+
for (const id in this.extensions) {
95
+
const ext = this.extensions[id];
96
+
ext.hasUpdate = false;
97
+
ext.changelog = undefined;
98
+
}
99
+
this.updates = {};
100
+
101
+
for (const [repo, exts] of Object.entries(repositories)) {
102
+
for (const ext of exts) {
103
+
const uniqueId = this.extensionIndex++;
104
+
const extensionData = {
105
+
id: ext.id,
106
+
uniqueId,
107
+
manifest: ext,
108
+
source: { type: ExtensionLoadSource.Normal, url: repo },
109
+
state: ExtensionState.NotDownloaded,
110
+
compat: ExtensionCompat.Compatible,
111
+
hasUpdate: false
112
+
};
113
+
114
+
// Don't present incompatible updates
115
+
if (checkExtensionCompat(ext) !== ExtensionCompat.Compatible) continue;
116
117
+
const existing = this.getExisting(extensionData);
118
+
if (existing != null) {
119
+
// Make sure the download URL is properly updated
120
+
existing.manifest = {
121
+
...existing.manifest,
122
+
download: ext.download
123
+
};
124
125
+
if (this.hasUpdate(extensionData)) {
126
+
this.updates[existing.uniqueId] = {
127
+
version: ext.version!,
128
+
download: ext.download,
129
+
updateManifest: ext
130
+
};
131
+
existing.hasUpdate = true;
132
+
existing.changelog = ext.meta?.changelog;
133
}
134
+
} else {
135
+
this.extensions[uniqueId] = extensionData;
136
}
137
}
138
+
}
139
+
}
140
141
+
private async checkMoonlightUpdates() {
142
+
this.newVersion = this.getExtensionConfigRaw("moonbase", "updateChecking", true)
143
+
? await natives!.checkForMoonlightUpdate()
144
+
: null;
145
}
146
147
+
private getExisting(ext: MoonbaseExtension) {
148
+
return Object.values(this.extensions).find((e) => e.id === ext.id && e.source.url === ext.source.url);
149
}
150
151
private hasUpdate(ext: MoonbaseExtension) {
152
+
const existing = Object.values(this.extensions).find((e) => e.id === ext.id && e.source.url === ext.source.url);
153
if (existing == null) return false;
154
155
+
return existing.manifest.version !== ext.manifest.version && existing.state !== ExtensionState.NotDownloaded;
156
}
157
158
// Jank
159
private isModified() {
160
+
const orig = JSON.stringify(this.savedConfig);
161
const curr = JSON.stringify(this.config);
162
return orig !== curr;
163
}
···
166
return this.submitting || this.installing;
167
}
168
169
+
// Required for the settings store contract
170
showNotice() {
171
return this.modified;
172
}
···
176
}
177
178
getExtensionUniqueId(id: string) {
179
+
return Object.values(this.extensions).find((ext) => ext.id === id)?.uniqueId;
180
}
181
182
getExtensionConflicting(uniqueId: number) {
183
const ext = this.getExtension(uniqueId);
184
if (ext.state !== ExtensionState.NotDownloaded) return false;
185
return Object.values(this.extensions).some(
186
+
(e) => e.id === ext.id && e.uniqueId !== uniqueId && e.state !== ExtensionState.NotDownloaded
187
);
188
}
189
···
206
207
getExtensionConfig<T>(uniqueId: number, key: string): T | undefined {
208
const ext = this.getExtension(uniqueId);
209
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
210
+
return getConfigOption(ext.id, key, this.config, settings);
211
+
}
212
213
+
getExtensionConfigRaw<T>(id: string, key: string, defaultValue: T | undefined): T | undefined {
214
+
const cfg = this.config.extensions[id];
215
+
if (cfg == null || typeof cfg === "boolean") return defaultValue;
216
+
return cfg.config?.[key] ?? defaultValue;
217
}
218
219
getExtensionConfigName(uniqueId: number, key: string) {
220
const ext = this.getExtension(uniqueId);
221
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
222
+
return settings?.[key]?.displayName ?? key;
223
}
224
225
getExtensionConfigDescription(uniqueId: number, key: string) {
226
const ext = this.getExtension(uniqueId);
227
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
228
+
return settings?.[key]?.description;
229
}
230
231
+
setExtensionConfig(id: string, key: string, value: any) {
232
+
setConfigOption(this.config, id, key, value);
233
this.modified = this.isModified();
234
this.emitChange();
235
}
···
256
this.emitChange();
257
}
258
259
+
dismissAllExtensionUpdates() {
260
+
for (const id in this.extensions) {
261
+
this.extensions[id].hasUpdate = false;
262
+
}
263
+
this.emitChange();
264
+
}
265
+
266
+
async updateAllExtensions() {
267
+
for (const id of Object.keys(this.updates)) {
268
+
try {
269
+
await this.installExtension(parseInt(id));
270
+
} catch (e) {
271
+
logger.error("Error bulk updating extension", id, e);
272
+
}
273
+
}
274
+
}
275
+
276
async installExtension(uniqueId: number) {
277
const ext = this.getExtension(uniqueId);
278
if (!("download" in ext.manifest)) {
···
281
282
this.installing = true;
283
try {
284
+
const update = this.updates[uniqueId];
285
+
const url = update?.download ?? ext.manifest.download;
286
+
await natives!.installExtension(ext.manifest, url, ext.source.url!);
287
if (ext.state === ExtensionState.NotDownloaded) {
288
this.extensions[uniqueId].state = ExtensionState.Disabled;
289
}
290
291
+
if (update != null) {
292
+
const existing = this.extensions[uniqueId];
293
+
existing.settingsOverride = update.updateManifest.settings;
294
+
existing.compat = checkExtensionCompat(update.updateManifest);
295
+
existing.manifest = update.updateManifest;
296
+
existing.changelog = update.updateManifest.meta?.changelog;
297
+
}
298
+
299
delete this.updates[uniqueId];
300
} catch (e) {
301
logger.error("Error installing extension:", e);
302
}
303
304
this.installing = false;
305
+
this.restartAdvice = this.#computeRestartAdvice();
306
this.emitChange();
307
}
308
309
+
private getRank(ext: MoonbaseExtension) {
310
+
if (ext.source.type === ExtensionLoadSource.Developer) return 3;
311
+
if (ext.source.type === ExtensionLoadSource.Core) return 2;
312
+
if (ext.source.url === mainRepo) return 1;
313
+
return 0;
314
+
}
315
+
316
+
async getDependencies(uniqueId: number) {
317
+
const ext = this.getExtension(uniqueId);
318
+
319
+
const missingDeps = [];
320
+
for (const dep of ext.manifest.dependencies ?? []) {
321
+
const anyInstalled = Object.values(this.extensions).some(
322
+
(e) => e.id === dep && e.state !== ExtensionState.NotDownloaded
323
+
);
324
+
if (!anyInstalled) missingDeps.push(dep);
325
+
}
326
+
327
+
if (missingDeps.length === 0) return null;
328
+
329
+
const deps: Record<string, MoonbaseExtension[]> = {};
330
+
for (const dep of missingDeps) {
331
+
const candidates = Object.values(this.extensions).filter((e) => e.id === dep);
332
+
333
+
deps[dep] = candidates.sort((a, b) => {
334
+
const aRank = this.getRank(a);
335
+
const bRank = this.getRank(b);
336
+
if (aRank === bRank) {
337
+
const repoIndex = this.savedConfig.repositories.indexOf(a.source.url!);
338
+
const otherRepoIndex = this.savedConfig.repositories.indexOf(b.source.url!);
339
+
return repoIndex - otherRepoIndex;
340
+
} else {
341
+
return bRank - aRank;
342
+
}
343
+
});
344
+
}
345
+
346
+
return deps;
347
+
}
348
+
349
async deleteExtension(uniqueId: number) {
350
const ext = this.getExtension(uniqueId);
351
if (ext == null) return;
352
353
this.installing = true;
354
try {
355
+
await natives!.deleteExtension(ext.id);
356
this.extensions[uniqueId].state = ExtensionState.NotDownloaded;
357
} catch (e) {
358
logger.error("Error deleting extension:", e);
359
}
360
361
this.installing = false;
362
+
this.restartAdvice = this.#computeRestartAdvice();
363
+
this.emitChange();
364
+
}
365
+
366
+
async updateMoonlight() {
367
+
this.#updateState = UpdateState.Working;
368
+
this.emitChange();
369
+
370
+
await natives
371
+
.updateMoonlight()
372
+
.then(() => (this.#updateState = UpdateState.Installed))
373
+
.catch((e) => {
374
+
logger.error(e);
375
+
this.#updateState = UpdateState.Failed;
376
+
});
377
+
378
this.emitChange();
379
}
380
···
388
this.emitChange();
389
}
390
391
+
tryGetExtensionName(id: string) {
392
+
const uniqueId = this.getExtensionUniqueId(id);
393
+
return (uniqueId != null ? this.getExtensionName(uniqueId) : null) ?? id;
394
+
}
395
+
396
+
registerConfigComponent(ext: string, name: string, component: CustomComponent) {
397
+
if (!(ext in this.configComponents)) this.configComponents[ext] = {};
398
+
this.configComponents[ext][name] = component;
399
+
}
400
+
401
+
getExtensionConfigComponent(ext: string, name: string) {
402
+
return this.configComponents[ext]?.[name];
403
+
}
404
+
405
+
#computeRestartAdvice() {
406
+
// If moonlight update needs a restart, always hide advice.
407
+
if (this.#updateState === UpdateState.Installed) return RestartAdvice.NotNeeded;
408
+
409
+
const i = this.initialConfig; // Initial config, from startup
410
+
const n = this.config; // New config about to be saved
411
+
412
+
let returnedAdvice = RestartAdvice.NotNeeded;
413
+
const updateAdvice = (r: RestartAdvice) => (returnedAdvice < r ? (returnedAdvice = r) : returnedAdvice);
414
+
415
+
// Top-level keys, repositories is not needed here because Moonbase handles it.
416
+
if (i.patchAll !== n.patchAll) updateAdvice(RestartAdvice.ReloadNeeded);
417
+
if (i.loggerLevel !== n.loggerLevel) updateAdvice(RestartAdvice.ReloadNeeded);
418
+
if (diff(i.devSearchPaths ?? [], n.devSearchPaths ?? [], { cyclesFix: false }).length !== 0)
419
+
return updateAdvice(RestartAdvice.RestartNeeded);
420
+
421
+
// Extension specific logic
422
+
for (const id in n.extensions) {
423
+
// Installed extension (might not be detected yet)
424
+
const ext = Object.values(this.extensions).find((e) => e.id === id && e.state !== ExtensionState.NotDownloaded);
425
+
// Installed and detected extension
426
+
const detected = moonlightNode.extensions.find((e) => e.id === id);
427
+
428
+
// If it's not installed at all, we don't care
429
+
if (!ext) continue;
430
+
431
+
const initState = i.extensions[id];
432
+
const newState = n.extensions[id];
433
+
434
+
const newEnabled = typeof newState === "boolean" ? newState : newState.enabled;
435
+
// If it's enabled but not detected yet, restart.
436
+
if (newEnabled && !detected) {
437
+
return updateAdvice(RestartAdvice.RestartNeeded);
438
+
}
439
+
440
+
// Toggling extensions specifically wants to rely on the initial state,
441
+
// that's what was considered when loading extensions.
442
+
const initEnabled = initState && (typeof initState === "boolean" ? initState : initState.enabled);
443
+
if (initEnabled !== newEnabled || detected?.manifest.version !== ext.manifest.version) {
444
+
// If we have the extension locally, we confidently know if it has host/preload scripts.
445
+
// If not, we have to respect the environment specified in the manifest.
446
+
// If that is the default, we can't know what's needed.
447
+
448
+
if (detected?.scripts.hostPath || detected?.scripts.nodePath) {
449
+
return updateAdvice(RestartAdvice.RestartNeeded);
450
+
}
451
+
452
+
switch (ext.manifest.environment) {
453
+
case ExtensionEnvironment.Both:
454
+
case ExtensionEnvironment.Web:
455
+
updateAdvice(RestartAdvice.ReloadNeeded);
456
+
continue;
457
+
case ExtensionEnvironment.Desktop:
458
+
return updateAdvice(RestartAdvice.RestartNeeded);
459
+
default:
460
+
updateAdvice(RestartAdvice.ReloadNeeded);
461
+
continue;
462
+
}
463
+
}
464
+
465
+
const initConfig = typeof initState === "boolean" ? {} : { ...initState?.config };
466
+
const newConfig = typeof newState === "boolean" ? {} : { ...newState?.config };
467
+
468
+
const def = ext.manifest.settings;
469
+
if (!def) continue;
470
+
471
+
for (const key in def) {
472
+
const defaultValue = def[key].default;
473
+
474
+
initConfig[key] ??= defaultValue;
475
+
newConfig[key] ??= defaultValue;
476
+
}
477
+
478
+
const changedKeys = diff(initConfig, newConfig, { cyclesFix: false }).map((c) => c.path[0]);
479
+
for (const key in def) {
480
+
if (!changedKeys.includes(key)) continue;
481
+
482
+
const advice = def[key].advice;
483
+
switch (advice) {
484
+
case ExtensionSettingsAdvice.None:
485
+
updateAdvice(RestartAdvice.NotNeeded);
486
+
continue;
487
+
case ExtensionSettingsAdvice.Reload:
488
+
updateAdvice(RestartAdvice.ReloadNeeded);
489
+
continue;
490
+
case ExtensionSettingsAdvice.Restart:
491
+
updateAdvice(RestartAdvice.RestartNeeded);
492
+
continue;
493
+
default:
494
+
updateAdvice(RestartAdvice.ReloadSuggested);
495
+
}
496
+
}
497
+
}
498
+
499
+
return returnedAdvice;
500
+
}
501
+
502
writeConfig() {
503
this.submitting = true;
504
+
this.restartAdvice = this.#computeRestartAdvice();
505
+
const modifiedRepos = diff(this.savedConfig.repositories, this.config.repositories);
506
507
+
moonlightNode.writeConfig(this.config);
508
+
this.savedConfig = this.clone(this.config);
509
510
this.submitting = false;
511
this.modified = false;
512
this.emitChange();
513
+
514
+
if (modifiedRepos.length !== 0) this.checkUpdates();
515
}
516
517
reset() {
518
this.submitting = false;
519
this.modified = false;
520
+
this.config = this.clone(this.savedConfig);
521
this.emitChange();
522
+
}
523
+
524
+
restartDiscord() {
525
+
if (moonlightNode.isBrowser) {
526
+
window.location.reload();
527
+
} else {
528
+
// @ts-expect-error TODO: DiscordNative
529
+
window.DiscordNative.app.relaunch();
530
+
}
531
}
532
533
// 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
+
}
+29
-34
packages/core-extensions/src/moonbase/webpackModules/ui/config/index.tsx
+29
-34
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(
4
-
(v) => typeof v === "string"
5
-
) as string[];
6
7
-
import React from "@moonlight-mod/wp/common_react";
8
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
9
import {
10
FormDivider,
···
12
FormText,
13
FormSwitch,
14
TextInput,
15
-
Flex,
16
Button,
17
SingleSelect,
18
Tooltip,
19
Clickable
20
-
} from "@moonlight-mod/wp/common_components";
21
-
import CommonComponents from "@moonlight-mod/wp/common_components";
22
23
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
24
25
-
const FormClasses = spacepack.findByCode("dividerDefault:")[0].exports;
26
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
27
-
28
-
let RemoveButtonClasses: any;
29
spacepack
30
.lazyLoad(
31
"renderArtisanalHack",
···
34
)
35
.then(
36
() =>
37
-
(RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0]
38
-
.exports)
39
);
40
41
-
const { CircleXIcon } = CommonComponents;
42
function RemoveEntryButton({ onClick }: { onClick: () => void }) {
43
return (
44
-
<div className={RemoveButtonClasses.removeButtonContainer}>
45
<Tooltip text="Remove entry" position="top">
46
{(props: any) => (
47
-
<Clickable
48
-
{...props}
49
-
className={RemoveButtonClasses.removeButton}
50
-
onClick={onClick}
51
-
>
52
<CircleXIcon width={24} height={24} />
53
</Clickable>
54
)}
···
57
);
58
}
59
60
-
function ArrayFormItem({
61
-
config
62
-
}: {
63
-
config: "repositories" | "devSearchPaths";
64
-
}) {
65
const items = MoonbaseSettingsStore.getConfigOption(config) ?? [];
66
return (
67
<Flex
···
119
export default function ConfigPage() {
120
return (
121
<>
122
<FormItem title="Repositories">
123
-
<FormText className={Margins.marginBottom4}>
124
-
A list of remote repositories to display extensions from
125
-
</FormText>
126
<ArrayFormItem config="repositories" />
127
</FormItem>
128
-
<FormDivider className={FormClasses.dividerDefault} />
129
<FormItem title="Extension search paths" className={Margins.marginTop20}>
130
<FormText className={Margins.marginBottom4}>
131
A list of local directories to search for built extensions
132
</FormText>
133
<ArrayFormItem config="devSearchPaths" />
134
</FormItem>
135
-
<FormDivider className={FormClasses.dividerDefault} />
136
<FormSwitch
137
className={Margins.marginTop20}
138
-
value={MoonbaseSettingsStore.getConfigOption("patchAll")}
139
onChange={(value: boolean) => {
140
MoonbaseSettingsStore.setConfigOption("patchAll", value);
141
}}
···
152
value: o.toLowerCase(),
153
label: o[0] + o.slice(1).toLowerCase()
154
}))}
155
-
onChange={(v) =>
156
-
MoonbaseSettingsStore.setConfigOption("loggerLevel", v)
157
-
}
158
/>
159
</FormItem>
160
</>
···
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,
···
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",
···
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
)}
···
50
);
51
}
52
53
+
function ArrayFormItem({ config }: { config: "repositories" | "devSearchPaths" }) {
54
const items = MoonbaseSettingsStore.getConfigOption(config) ?? [];
55
return (
56
<Flex
···
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
}}
···
149
value: o.toLowerCase(),
150
label: o[0] + o.slice(1).toLowerCase()
151
}))}
152
+
onChange={(v) => MoonbaseSettingsStore.setConfigOption("loggerLevel", v)}
153
/>
154
</FormItem>
155
</>
+285
-156
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
+285
-156
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
···
1
import { ExtensionState } from "../../../types";
2
-
import { ExtensionLoadSource } from "@moonlight-mod/types";
3
-
4
-
import React from "@moonlight-mod/wp/common_react";
5
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
6
-
import CommonComponents from "@moonlight-mod/wp/common_components";
7
-
import * as Flux from "@moonlight-mod/wp/common_flux";
8
9
import ExtensionInfo from "./info";
10
import Settings from "./settings";
11
12
export enum ExtensionPage {
13
Info,
14
Description,
15
Settings
16
}
17
18
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
19
20
-
const { DownloadIcon, TrashIcon, CircleWarningIcon } = CommonComponents;
21
22
-
const PanelButton = spacepack.findByCode("Masks.PANEL_BUTTON")[0].exports.Z;
23
-
const TabBarClasses = spacepack.findByExports(
24
-
"tabBar",
25
-
"tabBarItem",
26
-
"headerContentWrapper"
27
-
)[0].exports;
28
29
-
export default function ExtensionCard({ uniqueId }: { uniqueId: number }) {
30
-
const [tab, setTab] = React.useState(ExtensionPage.Info);
31
-
const [restartNeeded, setRestartNeeded] = React.useState(false);
32
33
-
const { ext, enabled, busy, update, conflicting } = Flux.useStateFromStores(
34
-
[MoonbaseSettingsStore],
35
-
() => {
36
-
return {
37
-
ext: MoonbaseSettingsStore.getExtension(uniqueId),
38
-
enabled: MoonbaseSettingsStore.getExtensionEnabled(uniqueId),
39
-
busy: MoonbaseSettingsStore.busy,
40
-
update: MoonbaseSettingsStore.getExtensionUpdate(uniqueId),
41
-
conflicting: MoonbaseSettingsStore.getExtensionConflicting(uniqueId)
42
-
};
43
-
}
44
);
45
46
-
// Why it work like that :sob:
47
-
if (ext == null) return <></>;
48
49
-
const {
50
-
Card,
51
-
CardClasses,
52
-
Flex,
53
-
Text,
54
-
MarkdownParser,
55
-
Switch,
56
-
TabBar,
57
-
Button
58
-
} = CommonComponents;
59
60
-
const tagline = ext.manifest?.meta?.tagline;
61
-
const settings = ext.manifest?.settings;
62
-
const description = ext.manifest?.meta?.description;
63
64
-
return (
65
-
<Card editable={true} className={CardClasses.card}>
66
-
<div className={CardClasses.cardHeader}>
67
-
<Flex direction={Flex.Direction.VERTICAL}>
68
-
<Flex direction={Flex.Direction.HORIZONTAL}>
69
-
<Text variant="text-md/semibold">
70
-
{ext.manifest?.meta?.name ?? ext.id}
71
-
</Text>
72
</Flex>
73
74
-
{tagline != null && (
75
-
<Text variant="text-sm/normal">
76
-
{MarkdownParser.parse(tagline)}
77
-
</Text>
78
-
)}
79
</Flex>
80
81
-
<Flex
82
-
direction={Flex.Direction.HORIZONTAL}
83
-
align={Flex.Align.END}
84
-
justify={Flex.Justify.END}
85
-
>
86
-
{ext.state === ExtensionState.NotDownloaded ? (
87
-
<Button
88
-
color={Button.Colors.BRAND}
89
-
submitting={busy}
90
-
disabled={conflicting}
91
-
onClick={() => {
92
-
MoonbaseSettingsStore.installExtension(uniqueId);
93
-
}}
94
-
>
95
-
Install
96
-
</Button>
97
-
) : (
98
-
<div
99
-
// too lazy to learn how <Flex /> works lmao
100
style={{
101
-
display: "flex",
102
-
alignItems: "center",
103
-
gap: "1rem"
104
}}
105
>
106
-
{ext.source.type === ExtensionLoadSource.Normal && (
107
-
<PanelButton
108
-
icon={TrashIcon}
109
-
tooltipText="Delete"
110
-
onClick={() => {
111
-
MoonbaseSettingsStore.deleteExtension(uniqueId);
112
-
}}
113
-
/>
114
-
)}
115
116
-
{update != null && (
117
-
<PanelButton
118
-
icon={DownloadIcon}
119
-
tooltipText="Update"
120
-
onClick={() => {
121
-
MoonbaseSettingsStore.installExtension(uniqueId);
122
-
}}
123
-
/>
124
)}
125
126
-
{restartNeeded && (
127
-
<PanelButton
128
-
icon={() => (
129
-
<CircleWarningIcon
130
-
color={CommonComponents.tokens.colors.STATUS_DANGER}
131
-
/>
132
-
)}
133
-
onClick={() => window.location.reload()}
134
-
tooltipText="You will need to reload/restart your client for this extension to work properly."
135
-
/>
136
)}
137
138
-
<Switch
139
-
checked={enabled}
140
-
onChange={() => {
141
-
setRestartNeeded(true);
142
-
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, !enabled);
143
-
}}
144
-
/>
145
-
</div>
146
-
)}
147
-
</Flex>
148
-
</div>
149
150
-
<div>
151
-
{(description != null || settings != null) && (
152
-
<TabBar
153
-
selectedItem={tab}
154
-
type="top"
155
-
onItemSelect={setTab}
156
-
className={TabBarClasses.tabBar}
157
-
style={{
158
-
padding: "0 20px"
159
-
}}
160
-
>
161
-
<TabBar.Item
162
-
className={TabBarClasses.tabBarItem}
163
-
id={ExtensionPage.Info}
164
>
165
-
Info
166
-
</TabBar.Item>
167
-
168
-
{description != null && (
169
-
<TabBar.Item
170
-
className={TabBarClasses.tabBarItem}
171
-
id={ExtensionPage.Description}
172
-
>
173
-
Description
174
-
</TabBar.Item>
175
-
)}
176
-
177
-
{settings != null && (
178
-
<TabBar.Item
179
-
className={TabBarClasses.tabBarItem}
180
-
id={ExtensionPage.Settings}
181
-
>
182
-
Settings
183
-
</TabBar.Item>
184
-
)}
185
-
</TabBar>
186
)}
187
188
<Flex
189
justify={Flex.Justify.START}
190
wrap={Flex.Wrap.WRAP}
191
style={{
192
-
padding: "16px 16px"
193
}}
194
>
195
-
{tab === ExtensionPage.Info && <ExtensionInfo ext={ext} />}
196
{tab === ExtensionPage.Description && (
197
-
<Text variant="text-md/normal">
198
-
{MarkdownParser.parse(description ?? "*No description*")}
199
</Text>
200
)}
201
-
{tab === ExtensionPage.Settings && <Settings ext={ext} />}
202
</Flex>
203
</div>
204
</Card>
···
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>
+119
-119
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/filterBar.tsx
+119
-119
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/filterBar.tsx
···
1
import { tagNames } from "./info";
2
-
3
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
4
-
import React from "@moonlight-mod/wp/common_react";
5
-
import * as Flux from "@moonlight-mod/wp/common_flux";
6
import { WindowStore } from "@moonlight-mod/wp/common_stores";
7
import {
8
Button,
···
11
Popout,
12
Dialog,
13
Menu,
14
-
MenuGroup,
15
-
MenuCheckboxItem,
16
-
MenuItem
17
-
} from "@moonlight-mod/wp/common_components";
18
-
import CommonComponents from "@moonlight-mod/wp/common_components";
19
20
export enum Filter {
21
Core = 1 << 0,
···
24
Enabled = 1 << 3,
25
Disabled = 1 << 4,
26
Installed = 1 << 5,
27
-
Repository = 1 << 6
28
}
29
-
export const defaultFilter = ~(~0 << 7);
30
-
31
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
32
-
const SortMenuClasses = spacepack.findByCode("container:", "clearText:")[0]
33
-
.exports;
34
35
-
let FilterDialogClasses: any;
36
-
let FilterBarClasses: any;
37
spacepack
38
-
.lazyLoad(
39
-
'"Missing channel in Channel.openChannelContextMenu"',
40
-
/e\("(\d+)"\)/g,
41
-
/webpackId:(\d+?),/
42
-
)
43
.then(() => {
44
-
FilterBarClasses = spacepack.findByCode("tagsButtonWithCount:")[0].exports;
45
-
FilterDialogClasses = spacepack.findByCode(
46
-
"countContainer:",
47
-
"tagContainer:"
48
-
)[0].exports;
49
});
50
51
-
const TagItem = spacepack.findByCode(".FORUM_TAG_A11Y_FILTER_BY_TAG")[0].exports
52
-
.Z;
53
-
54
-
const { ChevronSmallDownIcon, ChevronSmallUpIcon, ArrowsUpDownIcon } =
55
-
CommonComponents;
56
-
57
-
function toggleTag(
58
-
selectedTags: Set<string>,
59
-
setSelectedTags: (tags: Set<string>) => void,
60
-
tag: string
61
-
) {
62
const newState = new Set(selectedTags);
63
if (newState.has(tag)) newState.delete(tag);
64
else newState.add(tag);
···
74
setFilter: (filter: Filter) => void;
75
closePopout: () => void;
76
}) {
77
-
const toggleFilter = (set: Filter) =>
78
-
setFilter(filter & set ? filter & ~set : filter | set);
79
80
return (
81
<div className={SortMenuClasses.container}>
82
-
<Menu navId="sort-filter" hideScrollbar={true} onClose={closePopout}>
83
<MenuGroup label="Type">
84
<MenuCheckboxItem
85
id="t-core"
86
label="Core"
87
-
checked={filter & Filter.Core}
88
action={() => toggleFilter(Filter.Core)}
89
/>
90
<MenuCheckboxItem
91
id="t-normal"
92
label="Normal"
93
-
checked={filter & Filter.Normal}
94
action={() => toggleFilter(Filter.Normal)}
95
/>
96
<MenuCheckboxItem
97
id="t-developer"
98
label="Developer"
99
-
checked={filter & Filter.Developer}
100
action={() => toggleFilter(Filter.Developer)}
101
/>
102
</MenuGroup>
···
104
<MenuCheckboxItem
105
id="s-enabled"
106
label="Enabled"
107
-
checked={filter & Filter.Enabled}
108
action={() => toggleFilter(Filter.Enabled)}
109
/>
110
<MenuCheckboxItem
111
id="s-disabled"
112
label="Disabled"
113
-
checked={filter & Filter.Disabled}
114
action={() => toggleFilter(Filter.Disabled)}
115
/>
116
</MenuGroup>
···
118
<MenuCheckboxItem
119
id="l-installed"
120
label="Installed"
121
-
checked={filter & Filter.Installed}
122
action={() => toggleFilter(Filter.Installed)}
123
/>
124
<MenuCheckboxItem
125
id="l-repository"
126
label="Repository"
127
-
checked={filter & Filter.Repository}
128
action={() => toggleFilter(Filter.Repository)}
129
/>
130
</MenuGroup>
131
<MenuGroup>
132
<MenuItem
133
id="reset-all"
134
className={SortMenuClasses.clearText}
135
-
label={
136
-
<Text variant="text-sm/medium" color="none">
137
-
Reset to default
138
-
</Text>
139
-
}
140
action={() => {
141
setFilter(defaultFilter);
142
closePopout();
···
148
);
149
}
150
151
-
function TagButtonPopout({
152
-
selectedTags,
153
-
setSelectedTags,
154
-
setPopoutRef,
155
-
closePopout
156
-
}: any) {
157
return (
158
-
<Dialog ref={setPopoutRef} className={FilterDialogClasses.container}>
159
-
<div className={FilterDialogClasses.header}>
160
-
<div className={FilterDialogClasses.headerLeft}>
161
-
<Heading
162
-
color="interactive-normal"
163
-
variant="text-xs/bold"
164
-
className={FilterDialogClasses.headerText}
165
-
>
166
Select tags
167
</Heading>
168
-
<div className={FilterDialogClasses.countContainer}>
169
-
<Text
170
-
className={FilterDialogClasses.countText}
171
-
color="none"
172
-
variant="text-xs/medium"
173
-
>
174
{selectedTags.size}
175
</Text>
176
</div>
177
</div>
178
</div>
179
-
<div className={FilterDialogClasses.tagContainer}>
180
{Object.keys(tagNames).map((tag) => (
181
<TagItem
182
key={tag}
183
-
className={FilterDialogClasses.tag}
184
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
185
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
186
selected={selectedTags.has(tag)}
187
/>
188
))}
189
</div>
190
-
<div className={FilterDialogClasses.separator} />
191
<Button
192
look={Button.Looks.LINK}
193
size={Button.Sizes.MIN}
194
color={Button.Colors.CUSTOM}
195
-
className={FilterDialogClasses.clear}
196
onClick={() => {
197
setSelectedTags(new Set());
198
closePopout();
···
217
selectedTags: Set<string>;
218
setSelectedTags: (tags: Set<string>) => void;
219
}) {
220
-
const windowSize = Flux.useStateFromStores([WindowStore], () =>
221
-
WindowStore.windowSize()
222
-
);
223
224
const tagsContainer = React.useRef<HTMLDivElement>(null);
225
const tagListInner = React.useRef<HTMLDivElement>(null);
226
const [tagsButtonOffset, setTagsButtonOffset] = React.useState(0);
227
React.useLayoutEffect(() => {
228
if (tagsContainer.current === null || tagListInner.current === null) return;
229
-
const { left: containerX, top: containerY } =
230
-
tagsContainer.current.getBoundingClientRect();
231
let offset = 0;
232
for (const child of tagListInner.current.children) {
233
-
const {
234
-
right: childX,
235
-
top: childY,
236
-
height
237
-
} = child.getBoundingClientRect();
238
if (childY - containerY > height) break;
239
const newOffset = childX - containerX;
240
if (newOffset > offset) {
···
242
}
243
}
244
setTagsButtonOffset(offset);
245
-
}, [windowSize]);
246
247
return (
248
<div
···
250
style={{
251
paddingTop: "12px"
252
}}
253
-
className={`${FilterBarClasses.tagsContainer} ${Margins.marginBottom8}`}
254
>
255
<Popout
256
renderPopout={({ closePopout }: any) => (
257
-
<FilterButtonPopout
258
-
filter={filter}
259
-
setFilter={setFilter}
260
-
closePopout={closePopout}
261
-
/>
262
)}
263
position="bottom"
264
align="left"
···
268
{...props}
269
size={Button.Sizes.MIN}
270
color={Button.Colors.CUSTOM}
271
-
className={FilterBarClasses.sortDropdown}
272
-
innerClassName={FilterBarClasses.sortDropdownInner}
273
>
274
<ArrowsUpDownIcon size="xs" />
275
-
<Text
276
-
className={FilterBarClasses.sortDropdownText}
277
-
variant="text-sm/medium"
278
-
color="interactive-normal"
279
-
>
280
Sort & filter
281
</Text>
282
{isShown ? (
···
287
</Button>
288
)}
289
</Popout>
290
-
<div className={FilterBarClasses.divider} />
291
-
<div className={FilterBarClasses.tagList}>
292
-
<div ref={tagListInner} className={FilterBarClasses.tagListInner}>
293
{Object.keys(tagNames).map((tag) => (
294
<TagItem
295
key={tag}
296
-
className={FilterBarClasses.tag}
297
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
298
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
299
selected={selectedTags.has(tag)}
300
/>
···
322
left: tagsButtonOffset
323
}}
324
// TODO: Use Discord's class name utility
325
-
className={`${FilterBarClasses.tagsButton} ${
326
-
selectedTags.size > 0 ? FilterBarClasses.tagsButtonWithCount : ""
327
-
}`}
328
-
innerClassName={FilterBarClasses.tagsButtonInner}
329
>
330
{selectedTags.size > 0 ? (
331
-
<div
332
-
style={{ boxSizing: "content-box" }}
333
-
className={FilterBarClasses.countContainer}
334
-
>
335
-
<Text
336
-
className={FilterBarClasses.countText}
337
-
color="none"
338
-
variant="text-xs/medium"
339
-
>
340
{selectedTags.size}
341
</Text>
342
</div>
···
351
</Button>
352
)}
353
</Popout>
354
</div>
355
);
356
}
···
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,
···
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,
···
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);
···
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>
···
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>
···
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();
···
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();
···
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) {
···
218
}
219
}
220
setTagsButtonOffset(offset);
221
+
}, [windowSize, tagsContainer.current, tagListInner.current, tagListInner.current?.getBoundingClientRect()?.width]);
222
223
return (
224
<div
···
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"
···
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 ? (
···
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
/>
···
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>
···
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
}
+110
-51
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
+110
-51
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
···
3
import FilterBar, { Filter, defaultFilter } from "./filterBar";
4
import ExtensionCard from "./card";
5
6
-
import React from "@moonlight-mod/wp/common_react";
7
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
8
-
import * as Flux from "@moonlight-mod/wp/common_flux";
9
10
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
11
12
-
const SearchBar: any = Object.values(
13
-
spacepack.findByCode("Messages.SEARCH", "hideSearchIcon")[0].exports
14
-
)[0];
15
16
export default function ExtensionsPage() {
17
-
const moonbaseId = MoonbaseSettingsStore.getExtensionUniqueId("moonbase")!;
18
-
const { extensions, savedFilter } = Flux.useStateFromStoresObject(
19
-
[MoonbaseSettingsStore],
20
-
() => {
21
-
return {
22
-
extensions: MoonbaseSettingsStore.extensions,
23
-
savedFilter: MoonbaseSettingsStore.getExtensionConfig(
24
-
moonbaseId,
25
-
"filter"
26
-
)
27
-
};
28
-
}
29
-
);
30
31
const [query, setQuery] = React.useState("");
32
33
let filter: Filter, setFilter: (filter: Filter) => void;
34
-
if (moonlight.getConfigOption<boolean>("moonbase", "saveFilter")) {
35
filter = savedFilter ?? defaultFilter;
36
-
setFilter = (filter) =>
37
-
MoonbaseSettingsStore.setExtensionConfig(moonbaseId, "filter", filter);
38
} else {
39
-
const state = React.useState(defaultFilter);
40
-
filter = state[0];
41
-
setFilter = state[1];
42
}
43
const [selectedTags, setSelectedTags] = React.useState(new Set<string>());
44
const sorted = Object.values(extensions).sort((a, b) => {
45
const aName = a.manifest.meta?.name ?? a.id;
46
const bName = b.manifest.meta?.name ?? b.id;
···
49
50
const filtered = sorted.filter(
51
(ext) =>
52
-
(ext.manifest.meta?.name?.toLowerCase().includes(query) ||
53
ext.manifest.meta?.tagline?.toLowerCase().includes(query) ||
54
ext.manifest.meta?.description?.toLowerCase().includes(query)) &&
55
-
[...selectedTags.values()].every(
56
-
(tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag)
57
-
) &&
58
// This seems very bad, sorry
59
!(
60
-
(!(filter & Filter.Core) &&
61
-
ext.source.type === ExtensionLoadSource.Core) ||
62
-
(!(filter & Filter.Normal) &&
63
-
ext.source.type === ExtensionLoadSource.Normal) ||
64
-
(!(filter & Filter.Developer) &&
65
-
ext.source.type === ExtensionLoadSource.Developer) ||
66
-
(!(filter & Filter.Enabled) &&
67
-
MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) ||
68
-
(!(filter & Filter.Disabled) &&
69
-
!MoonbaseSettingsStore.getExtensionEnabled(ext.uniqueId)) ||
70
-
(!(filter & Filter.Installed) &&
71
-
ext.state !== ExtensionState.NotDownloaded) ||
72
-
(!(filter & Filter.Repository) &&
73
-
ext.state === ExtensionState.NotDownloaded)
74
-
)
75
);
76
77
return (
78
<>
···
89
spellCheck: "false"
90
}}
91
/>
92
-
<FilterBar
93
-
filter={filter}
94
-
setFilter={setFilter}
95
-
selectedTags={selectedTags}
96
-
setSelectedTags={setSelectedTags}
97
-
/>
98
-
{filtered.map((ext) => (
99
-
<ExtensionCard uniqueId={ext.uniqueId} key={ext.id} />
100
))}
101
</>
102
);
···
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;
···
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
<>
···
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
);
+55
-54
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/info.tsx
+55
-54
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/common_react";
5
-
import CommonComponents from "@moonlight-mod/wp/common_components";
6
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
7
8
type Dependency = {
9
id: string;
···
34
[ExtensionTag.Library]: "Library"
35
};
36
37
-
const UserInfoClasses = spacepack.findByCode(
38
-
"infoScroller",
39
-
"userInfoSection",
40
-
"userInfoSectionHeader"
41
-
)[0].exports;
42
-
43
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
44
45
-
function InfoSection({
46
-
title,
47
-
children
48
-
}: {
49
-
title: string;
50
-
children: React.ReactNode;
51
-
}) {
52
return (
53
<div
54
style={{
55
marginRight: "1em"
56
}}
57
>
58
-
<CommonComponents.Text
59
-
variant="eyebrow"
60
-
className={UserInfoClasses.userInfoSectionHeader}
61
-
>
62
{title}
63
-
</CommonComponents.Text>
64
65
-
<CommonComponents.Text variant="text-sm/normal">
66
-
{children}
67
-
</CommonComponents.Text>
68
</div>
69
);
70
}
71
72
function Badge({
73
color,
74
-
children
75
}: {
76
color: string;
77
children: React.ReactNode;
78
}) {
79
return (
80
<span
81
-
style={{
82
-
borderRadius: ".1875rem",
83
-
padding: "0 0.275rem",
84
-
marginRight: "0.4em",
85
-
backgroundColor: color,
86
-
color: "#fff"
87
-
}}
88
>
89
{children}
90
</span>
91
);
92
}
93
94
-
export default function ExtensionInfo({ ext }: { ext: MoonbaseExtension }) {
95
const authors = ext.manifest?.meta?.authors;
96
const tags = ext.manifest?.meta?.tags;
97
const version = ext.manifest?.version;
98
99
const dependencies: Dependency[] = [];
100
if (ext.manifest.dependencies != null) {
101
dependencies.push(
102
...ext.manifest.dependencies.map((dep) => ({
···
116
}
117
118
if (ext.manifest.incompatible != null) {
119
-
dependencies.push(
120
...ext.manifest.incompatible.map((dep) => ({
121
id: dep,
122
type: DependencyType.Incompatible
···
154
<InfoSection title="Tags">
155
{tags.map((tag, i) => {
156
const name = tagNames[tag];
157
158
return (
159
-
<Badge
160
-
key={i}
161
-
color={
162
-
tag === ExtensionTag.DangerZone
163
-
? "var(--red-400)"
164
-
: "var(--brand-500)"
165
-
}
166
-
>
167
{name}
168
</Badge>
169
);
···
174
{dependencies.length > 0 && (
175
<InfoSection title="Dependencies">
176
{dependencies.map((dep) => {
177
-
const colors = {
178
-
[DependencyType.Dependency]: "var(--brand-500)",
179
-
[DependencyType.Optional]: "var(--orange-400)",
180
-
[DependencyType.Incompatible]: "var(--red-400)"
181
-
};
182
-
const color = colors[dep.type];
183
-
const id = MoonbaseSettingsStore.getExtensionUniqueId(dep.id);
184
-
const name =
185
-
(id !== null
186
-
? MoonbaseSettingsStore.getExtensionName(id!)
187
-
: null) ?? dep.id;
188
return (
189
-
<Badge color={color} key={dep.id}>
190
{name}
191
</Badge>
192
);
···
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;
···
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) => ({
···
112
}
113
114
if (ext.manifest.incompatible != null) {
115
+
incompatible.push(
116
...ext.manifest.incompatible.map((dep) => ({
117
id: dep,
118
type: DependencyType.Incompatible
···
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
);
···
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
);
+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
+
}
+133
-138
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
+133
-138
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
···
9
10
import { ExtensionState, MoonbaseExtension } from "../../../types";
11
12
-
import React from "@moonlight-mod/wp/common_react";
13
-
import CommonComponents from "@moonlight-mod/wp/common_components";
14
-
import * as Flux from "@moonlight-mod/wp/common_flux";
15
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
16
17
type SettingsProps = {
18
ext: MoonbaseExtension;
···
20
setting: ExtensionSettingsManifest;
21
disabled: boolean;
22
};
23
-
24
type SettingsComponent = React.ComponentType<SettingsProps>;
25
26
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
27
28
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
29
30
function useConfigEntry<T>(uniqueId: number, name: string) {
31
-
return Flux.useStateFromStores(
32
[MoonbaseSettingsStore],
33
() => {
34
return {
35
value: MoonbaseSettingsStore.getExtensionConfig<T>(uniqueId, name),
36
-
displayName: MoonbaseSettingsStore.getExtensionConfigName(
37
-
uniqueId,
38
-
name
39
-
),
40
-
description: MoonbaseSettingsStore.getExtensionConfigDescription(
41
-
uniqueId,
42
-
name
43
-
)
44
};
45
},
46
[uniqueId, name]
···
48
}
49
50
function Boolean({ ext, name, setting, disabled }: SettingsProps) {
51
-
const { FormSwitch } = CommonComponents;
52
-
const { value, displayName, description } = useConfigEntry<boolean>(
53
-
ext.uniqueId,
54
-
name
55
-
);
56
57
return (
58
<FormSwitch
···
60
hideBorder={true}
61
disabled={disabled}
62
onChange={(value: boolean) => {
63
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, value);
64
}}
65
-
note={description}
66
className={`${Margins.marginReset} ${Margins.marginTop20}`}
67
>
68
{displayName}
···
71
}
72
73
function Number({ ext, name, setting, disabled }: SettingsProps) {
74
-
const { FormItem, FormText, Slider } = CommonComponents;
75
-
const { value, displayName, description } = useConfigEntry<number>(
76
-
ext.uniqueId,
77
-
name
78
-
);
79
80
const castedSetting = setting as NumberSettingType;
81
-
const min = castedSetting.min ?? 0;
82
-
const max = castedSetting.max ?? 100;
83
84
return (
85
<FormItem className={Margins.marginTop20} title={displayName}>
86
-
{description && <FormText>{description}</FormText>}
87
-
<Slider
88
-
initialValue={value ?? 0}
89
-
disabled={disabled}
90
-
minValue={castedSetting.min ?? 0}
91
-
maxValue={castedSetting.max ?? 100}
92
-
onValueChange={(value: number) => {
93
-
const rounded = Math.max(min, Math.min(max, Math.round(value)));
94
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, rounded);
95
-
}}
96
-
/>
97
</FormItem>
98
);
99
}
100
101
function String({ ext, name, setting, disabled }: SettingsProps) {
102
-
const { FormItem, FormText, TextInput } = CommonComponents;
103
-
const { value, displayName, description } = useConfigEntry<string>(
104
-
ext.uniqueId,
105
-
name
106
-
);
107
108
return (
109
<FormItem className={Margins.marginTop20} title={displayName}>
110
-
{description && (
111
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
112
-
)}
113
<TextInput
114
value={value ?? ""}
115
-
onChange={(value: string) => {
116
-
if (disabled) return;
117
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, value);
118
-
}}
119
/>
120
</FormItem>
121
);
122
}
123
124
function MultilineString({ ext, name, setting, disabled }: SettingsProps) {
125
-
const { FormItem, FormText, TextArea } = CommonComponents;
126
-
const { value, displayName, description } = useConfigEntry<string>(
127
-
ext.uniqueId,
128
-
name
129
-
);
130
131
return (
132
<FormItem className={Margins.marginTop20} title={displayName}>
133
-
{description && (
134
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
135
-
)}
136
<TextArea
137
rows={5}
138
value={value ?? ""}
139
className={"moonbase-resizeable"}
140
-
onChange={(value: string) => {
141
-
if (disabled) return;
142
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, value);
143
-
}}
144
/>
145
</FormItem>
146
);
147
}
148
149
function Select({ ext, name, setting, disabled }: SettingsProps) {
150
-
const { FormItem, FormText, SingleSelect } = CommonComponents;
151
-
const { value, displayName, description } = useConfigEntry<string>(
152
-
ext.uniqueId,
153
-
name
154
-
);
155
156
const castedSetting = setting as SelectSettingType;
157
const options = castedSetting.options;
158
159
return (
160
<FormItem className={Margins.marginTop20} title={displayName}>
161
-
{description && (
162
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
163
-
)}
164
<SingleSelect
165
autofocus={false}
166
clearable={false}
167
value={value ?? ""}
168
-
options={options.map((o: SelectOption) =>
169
-
typeof o === "string" ? { value: o, label: o } : o
170
-
)}
171
onChange={(value: string) => {
172
if (disabled) return;
173
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, value);
174
}}
175
/>
176
</FormItem>
···
178
}
179
180
function MultiSelect({ ext, name, setting, disabled }: SettingsProps) {
181
-
const { FormItem, FormText, Select, useVariableSelect, multiSelect } =
182
-
CommonComponents;
183
-
const { value, displayName, description } = useConfigEntry<string | string[]>(
184
-
ext.uniqueId,
185
-
name
186
-
);
187
188
const castedSetting = setting as MultiSelectSettingType;
189
const options = castedSetting.options;
190
191
return (
192
<FormItem className={Margins.marginTop20} title={displayName}>
193
-
{description && (
194
-
<FormText className={Margins.marginBottom8}>{description}</FormText>
195
-
)}
196
-
<Select
197
autofocus={false}
198
clearable={false}
199
closeOnSelect={false}
200
-
options={options.map((o: SelectOption) =>
201
-
typeof o === "string" ? { value: o, label: o } : o
202
-
)}
203
{...useVariableSelect({
204
onSelectInteraction: multiSelect,
205
-
value: new Set(Array.isArray(value) ? value : [value]),
206
onChange: (value: string) => {
207
if (disabled) return;
208
-
MoonbaseSettingsStore.setExtensionConfig(
209
-
ext.uniqueId,
210
-
name,
211
-
Array.from(value)
212
-
);
213
}
214
})}
215
/>
···
217
);
218
}
219
220
-
const RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0]
221
-
.exports;
222
-
const CircleXIcon = CommonComponents.CircleXIcon;
223
-
function RemoveEntryButton({
224
-
onClick,
225
-
disabled
226
-
}: {
227
-
onClick: () => void;
228
-
disabled: boolean;
229
-
}) {
230
-
const { Tooltip, Clickable } = CommonComponents;
231
return (
232
-
<div className={RemoveButtonClasses.removeButtonContainer}>
233
<Tooltip text="Remove entry" position="top">
234
{(props: any) => (
235
-
<Clickable
236
-
{...props}
237
-
className={RemoveButtonClasses.removeButton}
238
-
onClick={onClick}
239
-
>
240
<CircleXIcon width={16} height={16} />
241
</Clickable>
242
)}
···
246
}
247
248
function List({ ext, name, setting, disabled }: SettingsProps) {
249
-
const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents;
250
-
const { value, displayName, description } = useConfigEntry<string[]>(
251
-
ext.uniqueId,
252
-
name
253
-
);
254
255
const entries = value ?? [];
256
-
const updateConfig = () =>
257
-
MoonbaseSettingsStore.setExtensionConfig(ext.uniqueId, name, entries);
258
259
return (
260
<FormItem className={Margins.marginTop20} title={displayName}>
261
-
{description && (
262
-
<FormText className={Margins.marginBottom4}>{description}</FormText>
263
-
)}
264
<Flex direction={Flex.Direction.VERTICAL}>
265
{entries.map((val, i) => (
266
// FIXME: stylesheets
···
312
}
313
314
function Dictionary({ ext, name, setting, disabled }: SettingsProps) {
315
-
const { FormItem, FormText, TextInput, Button, Flex } = CommonComponents;
316
-
const { value, displayName, description } = useConfigEntry<
317
-
Record<string, string>
318
-
>(ext.uniqueId, name);
319
320
const entries = Object.entries(value ?? {});
321
-
const updateConfig = () =>
322
-
MoonbaseSettingsStore.setExtensionConfig(
323
-
ext.uniqueId,
324
-
name,
325
-
Object.fromEntries(entries)
326
-
);
327
328
return (
329
<FormItem className={Margins.marginTop20} title={displayName}>
330
-
{description && (
331
-
<FormText className={Margins.marginBottom4}>{description}</FormText>
332
-
)}
333
<Flex direction={Flex.Direction.VERTICAL}>
334
{entries.map(([key, val], i) => (
335
// FIXME: stylesheets
···
389
);
390
}
391
392
function Setting({ ext, name, setting, disabled }: SettingsProps) {
393
const elements: Partial<Record<ExtensionSettingType, SettingsComponent>> = {
394
[ExtensionSettingType.Boolean]: Boolean,
···
398
[ExtensionSettingType.Select]: Select,
399
[ExtensionSettingType.MultiSelect]: MultiSelect,
400
[ExtensionSettingType.List]: List,
401
-
[ExtensionSettingType.Dictionary]: Dictionary
402
};
403
const element = elements[setting.type];
404
if (element == null) return <></>;
···
406
}
407
408
export default function Settings({ ext }: { ext: MoonbaseExtension }) {
409
-
const { Flex } = CommonComponents;
410
return (
411
<Flex className="moonbase-settings" direction={Flex.Direction.VERTICAL}>
412
-
{Object.entries(ext.manifest.settings!).map(([name, setting]) => (
413
<Setting
414
ext={ext}
415
key={name}
···
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;
···
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]
···
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
···
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}
···
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>
···
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
/>
···
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
)}
···
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
···
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
···
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,
···
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 <></>;
···
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}
+28
-29
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
+28
-29
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
···
1
-
import React from "@moonlight-mod/wp/common_react";
2
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
3
-
import { Text, TabBar } from "@moonlight-mod/wp/common_components";
4
-
import * as Flux from "@moonlight-mod/wp/common_flux";
5
import { UserSettingsModalStore } from "@moonlight-mod/wp/common_stores";
6
7
import ExtensionsPage from "./extensions";
8
import ConfigPage from "./config";
9
-
10
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
11
-
12
-
const { Divider } = spacepack.findByCode(".forumOrHome]:")[0].exports.Z;
13
-
const TitleBarClasses = spacepack.findByCode("iconWrapper:", "children:")[0]
14
-
.exports;
15
-
const TabBarClasses = spacepack.findByCode("nowPlayingColumn:")[0].exports;
16
-
const { setSection, clearSubsection } = spacepack.findByExports(
17
-
"setSection",
18
-
"clearSubsection"
19
-
)[0].exports.Z;
20
21
export const pages: {
22
id: string;
···
32
id: "config",
33
name: "Config",
34
element: ConfigPage
35
}
36
];
37
38
export function Moonbase(props: { initialTab?: number } = {}) {
39
-
const subsection = Flux.useStateFromStores(
40
-
[UserSettingsModalStore],
41
-
() => UserSettingsModalStore.getSubsection() ?? 0
42
-
);
43
const setSubsection = React.useCallback(
44
(to: string) => {
45
-
if (subsection !== to) setSection("moonbase", to);
46
},
47
[subsection]
48
);
···
50
React.useEffect(
51
() => () => {
52
// 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
53
-
clearSubsection("moonbase");
54
},
55
[]
56
);
57
58
return (
59
<>
60
-
<div className={`${TitleBarClasses.children} ${Margins.marginBottom20}`}>
61
-
<Text
62
-
className={TitleBarClasses.titleWrapper}
63
-
variant="heading-lg/semibold"
64
-
tag="h2"
65
-
>
66
Moonbase
67
</Text>
68
<Divider />
···
70
selectedItem={subsection}
71
onItemSelect={setSubsection}
72
type="top-pill"
73
-
className={TabBarClasses.tabBar}
74
>
75
{pages.map((page, i) => (
76
-
<TabBar.Item key={page.id} id={i} className={TabBarClasses.item}>
77
{page.name}
78
</TabBar.Item>
79
))}
80
</TabBar>
81
</div>
82
83
{React.createElement(pages[subsection].element)}
84
</>
85
);
86
}
···
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;
···
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
);
···
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 />
···
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);
+5
packages/core-extensions/src/moonbase/wp.d.ts
+5
packages/core-extensions/src/moonbase/wp.d.ts
···
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
+4
-1
packages/core-extensions/src/noHideToken/manifest.json
+4
-1
packages/core-extensions/src/noHideToken/manifest.json
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "noHideToken",
4
+
"apiLevel": 2,
5
"meta": {
6
"name": "No Hide Token",
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",
9
"authors": ["adryd"],
10
"tags": ["dangerZone", "development"]
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
+9
-1
packages/core-extensions/src/noTrack/manifest.json
+9
-1
packages/core-extensions/src/noTrack/manifest.json
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "noTrack",
4
+
"apiLevel": 2,
5
"meta": {
6
"name": "No Track",
7
"tagline": "Disables /api/science and analytics",
8
"authors": ["Cynosphere", "NotNite"],
9
"tags": ["privacy"]
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
+
]
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;
+34
-44
packages/core-extensions/src/quietLoggers/index.ts
+34
-44
packages/core-extensions/src/quietLoggers/index.ts
···
1
import { Patch } from "@moonlight-mod/types";
2
3
const notXssDefensesOnly = () =>
4
-
(moonlight.getConfigOption<boolean>("quietLoggers", "xssDefensesOnly") ??
5
-
false) === false;
6
7
// These patches MUST run before the simple patches, these are to remove loggers
8
// that end up causing syntax errors by the normal patch
9
const loggerFixes: Patch[] = [
10
{
11
-
find: '"./ggsans-800-extrabolditalic.woff2":',
12
replace: {
13
-
match: /throw .+?,./,
14
-
replacement: "return{}"
15
}
16
},
17
{
···
29
// Patches to simply remove a logger call
30
const stubPatches = [
31
// "sh" is not a valid locale.
32
-
[
33
-
"is not a valid locale",
34
-
/(.)\.error\(""\.concat\((.)," is not a valid locale\."\)\)/g
35
-
],
36
-
['="RunningGameStore"', /.\.info\("games",{.+?}\),/],
37
-
[
38
-
'"[BUILD INFO] Release Channel: "',
39
-
/new .{1,2}\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?"\)\),/
40
-
],
41
-
[
42
-
'.APP_NATIVE_CRASH,"Storage"',
43
-
/console\.log\("AppCrashedFatalReport lastCrash:",.,.\);/
44
-
],
45
-
[
46
-
'.APP_NATIVE_CRASH,"Storage"',
47
-
'console.log("AppCrashedFatalReport: getLastCrash not supported.");'
48
-
],
49
['"[NATIVE INFO] ', /new .{1,2}\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/],
50
['"Spellchecker"', /.\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
51
-
[
52
-
'throw Error("Messages are still loading.");',
53
-
/console\.warn\("Unsupported Locale",.\),/
54
-
],
55
-
["}_dispatchWithDevtools(", /.\.totalTime>100&&.\.verbose\(.+?\);/],
56
-
[
57
-
'"NativeDispatchUtils"',
58
-
/null==.&&.\.warn\("Tried getting Dispatch instance before instantiated"\),/
59
-
],
60
['("DatabaseManager")', /.\.log\("removing database \(user: ".+?\)\),/],
61
[
62
'"Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. Action: "',
63
/.\.has\(.\.type\)&&.\.log\(.+?\.type\)\),/
64
],
65
-
[
66
-
'console.warn("Window state not initialized"',
67
-
/console\.warn\("Window state not initialized",.\),/
68
-
]
69
];
70
71
const simplePatches = [
72
// Moment.js deprecation warnings
73
-
["suppressDeprecationWarnings=!1", "suppressDeprecationWarnings=!0"],
74
-
75
-
// Zustand related
76
-
[
77
-
/console\.warn\("\[DEPRECATED\] Please use `subscribeWithSelector` middleware"\)/g,
78
-
"/*$&*/"
79
-
],
80
-
["this.getDebugLogging()", "false"]
81
] as { [0]: string | RegExp; [1]: string }[];
82
83
export const patches: Patch[] = [
84
{
85
-
find: ".Messages.XSSDefenses",
86
replace: {
87
match: /\(null!=.{1,2}&&"0\.0\.0"===.{1,2}\.remoteApp\.getVersion\(\)\)/,
88
replacement: "(true)"
89
}
90
},
91
...loggerFixes,
92
...stubPatches.map((patch) => ({
···
1
import { Patch } from "@moonlight-mod/types";
2
3
const notXssDefensesOnly = () =>
4
+
(moonlight.getConfigOption<boolean>("quietLoggers", "xssDefensesOnly") ?? false) === false;
5
+
6
+
const silenceDiscordLogger = moonlight.getConfigOption<boolean>("quietLoggers", "silenceDiscordLogger") ?? false;
7
8
// These patches MUST run before the simple patches, these are to remove loggers
9
// that end up causing syntax errors by the normal patch
10
const loggerFixes: Patch[] = [
11
{
12
+
find: '"./gg-sans/ggsans-800-extrabolditalic.woff2":',
13
replace: {
14
+
match: /var .=Error.+?;throw .+?,./,
15
+
replacement: ""
16
}
17
},
18
{
···
30
// Patches to simply remove a logger call
31
const stubPatches = [
32
// "sh" is not a valid locale.
33
+
["is not a valid locale", /void (.)\.error\(""\.concat\((.)," is not a valid locale\."\)\)/g],
34
+
['"[BUILD INFO] Release Channel: "', /new .{1,2}\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?\)\),/],
35
+
['.APP_NATIVE_CRASH,"Storage"', /console\.log\("AppCrashedFatalReport lastCrash:",.,.\);/],
36
+
['.APP_NATIVE_CRASH,"Storage"', 'void console.log("AppCrashedFatalReport: getLastCrash not supported.")'],
37
['"[NATIVE INFO] ', /new .{1,2}\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/],
38
['"Spellchecker"', /.\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
39
+
['throw Error("Messages are still loading.");', /console\.warn\("Unsupported Locale",.\),/],
40
+
["}_dispatchWithDevtools(", /.\.totalTime>.{1,2}&&.\.verbose\(.+?\);/],
41
+
['"NativeDispatchUtils"', /null==.&&.\.warn\("Tried getting Dispatch instance before instantiated"\),/],
42
['("DatabaseManager")', /.\.log\("removing database \(user: ".+?\)\),/],
43
[
44
'"Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. Action: "',
45
/.\.has\(.\.type\)&&.\.log\(.+?\.type\)\),/
46
],
47
+
['console.warn("Window state not initialized"', /console\.warn\("Window state not initialized",.\),/]
48
];
49
50
const simplePatches = [
51
// Moment.js deprecation warnings
52
+
["suppressDeprecationWarnings=!1", "suppressDeprecationWarnings=!0"]
53
] as { [0]: string | RegExp; [1]: string }[];
54
55
export const patches: Patch[] = [
56
{
57
+
find: ".Messages.SELF_XSS_HEADER",
58
replace: {
59
match: /\(null!=.{1,2}&&"0\.0\.0"===.{1,2}\.remoteApp\.getVersion\(\)\)/,
60
replacement: "(true)"
61
}
62
+
},
63
+
// Highlight.js deprecation warnings
64
+
{
65
+
find: "Deprecated as of",
66
+
replace: {
67
+
match: /console\./g,
68
+
replacement: "false&&console."
69
+
},
70
+
prerequisite: notXssDefensesOnly
71
+
},
72
+
// Discord's logger
73
+
{
74
+
find: "ฮฃ:",
75
+
replace: {
76
+
match: "for",
77
+
replacement: "return;for"
78
+
},
79
+
prerequisite: () => silenceDiscordLogger && notXssDefensesOnly()
80
},
81
...loggerFixes,
82
...stubPatches.map((patch) => ({
+10
packages/core-extensions/src/quietLoggers/manifest.json
+10
packages/core-extensions/src/quietLoggers/manifest.json
···
1
{
2
"id": "quietLoggers",
3
"meta": {
4
"name": "Quiet Loggers",
5
"tagline": "Quiet errors on startup, and disable unnecesary loggers",
···
8
},
9
"settings": {
10
"xssDefensesOnly": {
11
"displayName": "Only hide self-XSS",
12
"description": "Only disable self XSS prevention log",
13
"type": "boolean",
14
"default": false
15
}
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "quietLoggers",
4
+
"apiLevel": 2,
5
"meta": {
6
"name": "Quiet Loggers",
7
"tagline": "Quiet errors on startup, and disable unnecesary loggers",
···
10
},
11
"settings": {
12
"xssDefensesOnly": {
13
+
"advice": "reload",
14
"displayName": "Only hide self-XSS",
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)",
23
"type": "boolean",
24
"default": false
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
+
}
+4
-6
packages/core-extensions/src/settings/index.ts
+4
-6
packages/core-extensions/src/settings/index.ts
···
5
{
6
find: '"useGenerateUserSettingsSections"',
7
replace: {
8
-
match: /(?<=\.push\(.+?\)}\)\)}\),)./,
9
-
replacement: (sections: string) =>
10
-
`require("settings_settings").Settings._mutateSections(${sections})`
11
}
12
},
13
{
14
find: 'navId:"user-settings-cog",',
15
replace: {
16
-
match: /children:\[(.)\.map\(.+?\),children:.\((.)\)/,
17
replacement: (orig, sections, section) =>
18
`${orig.replace(
19
/Object\.values\(.\..+?\)/,
20
-
(orig) =>
21
-
`[...require("settings_settings").Settings.sectionNames,...${orig}]`
22
)}??${sections}.find(x=>x.section==${section})?._moonlight_submenu?.()`
23
}
24
}
···
5
{
6
find: '"useGenerateUserSettingsSections"',
7
replace: {
8
+
match: /(?<=\.push\(.+?\)}\)\)}\),)(.+?)}/,
9
+
replacement: (_, sections: string) => `require("settings_settings").Settings._mutateSections(${sections})}`
10
}
11
},
12
{
13
find: 'navId:"user-settings-cog",',
14
replace: {
15
+
match: /children:\[(\i)\.map\(.+?\),.*?children:\i\((\i)\)/,
16
replacement: (orig, sections, section) =>
17
`${orig.replace(
18
/Object\.values\(.\..+?\)/,
19
+
(orig) => `[...require("settings_settings").Settings.sectionNames,...${orig}]`
20
)}??${sections}.find(x=>x.section==${section})?._moonlight_submenu?.()`
21
}
22
}
+2
packages/core-extensions/src/settings/manifest.json
+2
packages/core-extensions/src/settings/manifest.json
+11
-13
packages/core-extensions/src/settings/webpackModules/settings.ts
+11
-13
packages/core-extensions/src/settings/webpackModules/settings.ts
···
1
-
import {
2
-
SettingsSection,
3
-
Settings as SettingsType
4
-
} from "@moonlight-mod/types/coreExtensions";
5
6
export const Settings: SettingsType = {
7
ourSections: [],
8
sectionNames: [],
9
sectionMenuItems: {},
10
11
-
addSection: (section, label, element, color = null, pos, notice) => {
12
const data: SettingsSection = {
13
section,
14
label,
15
color,
16
element,
17
pos: pos ?? -4,
18
-
notice: notice
19
};
20
21
Settings.ourSections.push(data);
···
24
},
25
addSectionMenuItems(section, ...newItems) {
26
const data = Settings.ourSections.find((x) => x.section === section);
27
-
if (!data || !("element" in data))
28
-
throw new Error(`Could not find section "${section}"`);
29
(Settings.sectionMenuItems[section] ??= []).push(...newItems);
30
data._moonlight_submenu ??= () => Settings.sectionMenuItems[section];
31
},
···
47
48
_mutateSections: (sections) => {
49
for (const section of Settings.ourSections) {
50
-
sections.splice(
51
-
section.pos < 0 ? sections.length + section.pos : section.pos,
52
-
0,
53
-
section
54
-
);
55
}
56
57
return sections;
···
1
+
import { SettingsSection, Settings as SettingsType } from "@moonlight-mod/types/coreExtensions/settings";
2
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
3
4
export const Settings: SettingsType = {
5
ourSections: [],
6
sectionNames: [],
7
sectionMenuItems: {},
8
9
+
addSection: (section, label, element, color = null, pos, notice, onClick) => {
10
const data: SettingsSection = {
11
section,
12
label,
13
color,
14
element,
15
pos: pos ?? -4,
16
+
notice: notice,
17
+
onClick: onClick ?? (() => UserSettingsModalActionCreators.open(section))
18
};
19
20
Settings.ourSections.push(data);
···
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
},
···
45
46
_mutateSections: (sections) => {
47
for (const section of Settings.ourSections) {
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);
53
}
54
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
{
2
"id": "spacepack",
3
"meta": {
4
"name": "Spacepack",
5
"tagline": "Search utilities across all Webpack modules",
···
8
},
9
"settings": {
10
"addToGlobalScope": {
11
"displayName": "Add to global scope",
12
"description": "Populates window.spacepack for easier usage in DevTools",
13
"type": "boolean",
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "spacepack",
4
+
"apiLevel": 2,
5
"meta": {
6
"name": "Spacepack",
7
"tagline": "Search utilities across all Webpack modules",
···
10
},
11
"settings": {
12
"addToGlobalScope": {
13
+
"advice": "reload",
14
"displayName": "Add to global scope",
15
"description": "Populates window.spacepack for easier usage in DevTools",
16
"type": "boolean",
+89
-68
packages/core-extensions/src/spacepack/webpackModules/spacepack.ts
+89
-68
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";
7
8
const webpackRequire = require as unknown as WebpackRequireType;
9
const cache = webpackRequire.c;
···
21
module = module.toString();
22
}
23
24
if (!(module in modules)) {
25
return null;
26
}
···
36
"module",
37
"exports",
38
"require",
39
-
`(${funcStr}).apply(this, arguments)\n` +
40
-
`//# sourceURL=Webpack-Module-${module}`
41
) as WebpackModuleFunc;
42
},
43
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
-
)
55
.map(([id]) => {
56
//if (!(id in cache)) require(id);
57
//return cache[id];
···
60
try {
61
exports = require(id);
62
} catch (e) {
63
-
logger.error(`Error requiring module "${id}": `, e);
64
}
65
66
return {
···
69
};
70
})
71
.filter((item) => item !== null);
72
},
73
74
findByExports: (...args: string[]) => {
···
80
!(
81
exports !== undefined &&
82
exports !== window &&
83
-
(exports?.[item] ||
84
-
exports?.default?.[item] ||
85
-
exports?.Z?.[item] ||
86
-
exports?.ZP?.[item])
87
)
88
)
89
)
···
95
},
96
97
findObjectFromKey: (exports: Record<string, any>, key: string) => {
98
let subKey;
99
if (key.indexOf(".") > -1) {
100
const splitKey = key.split(".");
···
105
const obj = exports[exportKey];
106
if (obj && obj[key] !== undefined) {
107
if (subKey) {
108
-
if (obj[key][subKey]) return obj;
109
} else {
110
-
return obj;
111
}
112
}
113
}
114
-
return null;
115
},
116
117
findObjectFromValue: (exports: Record<string, any>, value: any) => {
118
for (const exportKey in exports) {
119
const obj = exports[exportKey];
120
// eslint-disable-next-line eqeqeq
121
-
if (obj == value) return obj;
122
for (const subKey in obj) {
123
// eslint-disable-next-line eqeqeq
124
if (obj && obj[subKey] == value) {
125
-
return obj;
126
}
127
}
128
}
129
-
return null;
130
},
131
132
-
findObjectFromKeyValuePair: (
133
-
exports: Record<string, any>,
134
-
key: string,
135
-
value: any
136
-
) => {
137
for (const exportKey in exports) {
138
const obj = exports[exportKey];
139
// eslint-disable-next-line eqeqeq
140
if (obj && obj[key] == value) {
141
-
return obj;
142
}
143
}
144
return null;
145
},
146
147
-
findFunctionByStrings: (
148
-
exports: Record<string, any>,
149
-
...strings: (string | RegExp)[]
150
-
) => {
151
-
return (
152
Object.entries(exports).filter(
153
([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
-
);
163
},
164
165
-
lazyLoad: (
166
-
find: string | RegExp | (string | RegExp)[],
167
-
chunk: RegExp,
168
-
module: RegExp
169
-
) => {
170
-
const mod = Array.isArray(find)
171
-
? spacepack.findByCode(...find)
172
-
: spacepack.findByCode(find);
173
-
if (mod.length < 1) return Promise.reject("Module find failed");
174
175
const findId = mod[0].id;
176
const findCode = webpackRequire.m[findId].toString().replace(/\n/g, "");
···
180
chunkIds = [...findCode.matchAll(chunk)].map(([, id]) => id);
181
} else {
182
const match = findCode.match(chunk);
183
-
if (match)
184
-
chunkIds = [...match[0].matchAll(/"(\d+)"/g)].map(([, id]) => id);
185
}
186
187
-
if (!chunkIds || chunkIds.length === 0)
188
return Promise.reject("Chunk ID match failed");
189
190
const moduleId = findCode.match(module)?.[1];
191
-
if (!moduleId) return Promise.reject("Module ID match failed");
192
193
-
return Promise.all(chunkIds.map((c) => webpackRequire.e(c))).then(() =>
194
-
webpackRequire(moduleId)
195
-
);
196
},
197
198
filterReal: (modules: WebpackModule[]) => {
···
200
}
201
};
202
203
-
if (
204
-
moonlight.getConfigOption<boolean>("spacepack", "addToGlobalScope") === true
205
-
) {
206
window.spacepack = spacepack;
207
}
208
···
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";
4
5
const webpackRequire = require as unknown as WebpackRequireType;
6
const cache = webpackRequire.c;
···
18
module = module.toString();
19
}
20
21
+
if (module in moonlight.moonmap.modules) {
22
+
module = moonlight.moonmap.modules[module];
23
+
}
24
+
25
if (!(module in modules)) {
26
return null;
27
}
···
37
"module",
38
"exports",
39
"require",
40
+
`(${funcStr}).apply(this, arguments)\n` + `//# sourceURL=Webpack-Module/${module.slice(0, 3)}/${module}`
41
) as WebpackModuleFunc;
42
},
43
44
findByCode: (...args: (string | RegExp)[]) => {
45
+
const ret = Object.entries(modules)
46
+
.filter(([id, mod]) => !args.some((item) => !testFind(mod.toString(), processFind(item))))
47
.map(([id]) => {
48
//if (!(id in cache)) require(id);
49
//return cache[id];
···
52
try {
53
exports = require(id);
54
} catch (e) {
55
+
logger.error(`findByCode: Error requiring module "${id}": `, args, e);
56
}
57
58
return {
···
61
};
62
})
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;
70
},
71
72
findByExports: (...args: string[]) => {
···
78
!(
79
exports !== undefined &&
80
exports !== window &&
81
+
(exports?.[item] || exports?.default?.[item] || exports?.Z?.[item] || exports?.ZP?.[item])
82
)
83
)
84
)
···
90
},
91
92
findObjectFromKey: (exports: Record<string, any>, key: string) => {
93
+
let ret = null;
94
let subKey;
95
if (key.indexOf(".") > -1) {
96
const splitKey = key.split(".");
···
101
const obj = exports[exportKey];
102
if (obj && obj[key] !== undefined) {
103
if (subKey) {
104
+
if (obj[key][subKey]) {
105
+
ret = obj;
106
+
break;
107
+
}
108
} else {
109
+
ret = obj;
110
+
break;
111
}
112
}
113
}
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;
120
},
121
122
findObjectFromValue: (exports: Record<string, any>, value: any) => {
123
+
let ret = null;
124
for (const exportKey in exports) {
125
const obj = exports[exportKey];
126
// eslint-disable-next-line eqeqeq
127
+
if (obj == value) {
128
+
ret = obj;
129
+
break;
130
+
}
131
for (const subKey in obj) {
132
// eslint-disable-next-line eqeqeq
133
if (obj && obj[subKey] == value) {
134
+
ret = obj;
135
+
break;
136
}
137
}
138
}
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;
145
},
146
147
+
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => {
148
+
let ret = null;
149
for (const exportKey in exports) {
150
const obj = exports[exportKey];
151
// eslint-disable-next-line eqeqeq
152
if (obj && obj[key] == value) {
153
+
ret = obj;
154
+
break;
155
}
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
+
169
return null;
170
},
171
172
+
findFunctionByStrings: (exports: Record<string, any>, ...strings: (string | RegExp)[]) => {
173
+
const ret =
174
Object.entries(exports).filter(
175
([index, func]) =>
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;
184
},
185
186
+
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => {
187
+
chunk = processFind(chunk);
188
+
module = processFind(module);
189
+
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;
197
const findCode = webpackRequire.m[findId].toString().replace(/\n/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
}
206
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));
219
},
220
221
filterReal: (modules: WebpackModule[]) => {
···
223
}
224
};
225
226
+
if (moonlight.getConfigOption<boolean>("spacepack", "addToGlobalScope") === true) {
227
window.spacepack = spacepack;
228
}
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
{
2
"name": "@moonlight-mod/injector",
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
+
},
14
+
"engineStrict": true
15
}
+173
-113
packages/injector/src/index.ts
+173
-113
packages/injector/src/index.ts
···
5
app
6
} from "electron";
7
import Module from "node:module";
8
-
import { constants } from "@moonlight-mod/types";
9
-
import { readConfig } from "@moonlight-mod/core/config";
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";
16
import EventEmitter from "node:events";
17
-
import { join, resolve } from "node:path";
18
19
const logger = new Logger("injector");
20
21
let oldPreloadPath: string | undefined;
22
let corsAllow: string[] = [];
23
-
let isMoonlightDesktop = false;
24
-
let hasOpenAsar = false;
25
-
let openAsarConfigPreload: string | undefined;
26
27
ipcMain.on(constants.ipcGetOldPreloadPath, (e) => {
28
e.returnValue = oldPreloadPath;
29
});
30
ipcMain.on(constants.ipcGetAppData, (e) => {
31
e.returnValue = app.getPath("appData");
32
});
33
-
ipcMain.on(constants.ipcGetIsMoonlightDesktop, (e) => {
34
-
e.returnValue = isMoonlightDesktop;
35
});
36
ipcMain.handle(constants.ipcMessageBox, (_, opts) => {
37
electron.dialog.showMessageBoxSync(opts);
···
40
corsAllow = list;
41
});
42
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:"];
54
55
const csp = "content-security-policy";
56
if (headers[csp] == null) return;
···
67
68
for (const directive of directives) {
69
parts[directive] = values;
70
}
71
72
const stringified = Object.entries<string[]>(parts)
···
77
headers[csp] = [stringified];
78
}
79
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
class BrowserWindow extends ElectronBrowserWindow {
88
constructor(opts: BrowserWindowConstructorOptions) {
89
-
oldPreloadPath = opts.webPreferences!.preload;
90
91
-
// Only overwrite preload if its the actual main client window
92
-
if (opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1) {
93
opts.webPreferences!.preload = require.resolve("./node-preload.js");
94
}
95
96
// Event for modifying window options
97
-
moonlightHost.events.emit("window-options", opts);
98
99
super(opts);
100
101
// Event for when a window is created
102
-
moonlightHost.events.emit("window-created", this);
103
104
this.webContents.session.webRequest.onHeadersReceived((details, cb) => {
105
if (details.responseHeaders != null) {
106
// Patch CSP so things can use externally hosted assets
107
if (details.resourceType === "mainFrame") {
108
-
patchCsp(details.responseHeaders);
109
}
110
111
// Allow plugins to bypass CORS for specific URLs
112
if (corsAllow.some((x) => details.url.startsWith(x))) {
113
-
details.responseHeaders["access-control-allow-origin"] = ["*"];
114
}
115
116
cb({ cancel: false, responseHeaders: details.responseHeaders });
117
}
118
});
119
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);
129
}
130
-
} else if (events != null) {
131
-
removeOpenAsarEventIfPresent(events);
132
}
133
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
-
}
140
}
141
}
142
···
157
writable: false
158
});
159
160
-
export async function inject(asarPath: string) {
161
-
isMoonlightDesktop = asarPath === "moonlightDesktop";
162
try {
163
-
const config = readConfig();
164
-
const extensions = getExtensions();
165
166
// Duplicated in node-preload... oops
167
-
// eslint-disable-next-line no-inner-declarations
168
function getConfig(ext: string) {
169
const val = config.extensions[ext];
170
if (val == null || typeof val === "boolean") return undefined;
171
return val.config;
172
}
173
-
174
global.moonlightHost = {
175
asarPath,
176
-
config,
177
events: new EventEmitter(),
178
-
extensions,
179
-
processedExtensions: {
180
-
extensions: [],
181
-
dependencyGraph: new Map()
182
-
},
183
184
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;
191
},
192
-
getLogger: (id: string) => {
193
return new Logger(id);
194
}
195
};
196
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
patchElectron();
227
228
-
global.moonlightHost.processedExtensions = await loadExtensions(extensions);
229
await loadProcessedExtensions(global.moonlightHost.processedExtensions);
230
} catch (error) {
231
logger.error("Failed to inject:", error);
232
}
233
234
-
if (isMoonlightDesktop) return;
235
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);
239
}
240
241
function patchElectron() {
···
249
configurable: false
250
});
251
} else {
252
-
Object.defineProperty(
253
-
electronClone,
254
-
property,
255
-
Object.getOwnPropertyDescriptor(electron, property)!
256
-
);
257
}
258
}
259
···
5
app
6
} from "electron";
7
import Module from "node:module";
8
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
9
+
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
10
import { getExtensions } from "@moonlight-mod/core/extension";
11
+
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
12
+
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
13
import EventEmitter from "node:events";
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";
19
20
const logger = new Logger("injector");
21
22
let oldPreloadPath: string | undefined;
23
let corsAllow: string[] = [];
24
+
let blockedUrls: RegExp[] = [];
25
+
let injectorConfig: InjectorConfig | undefined;
26
+
27
+
const scriptUrls = ["web.", "sentry."];
28
+
const blockedScripts = new Set<string>();
29
30
ipcMain.on(constants.ipcGetOldPreloadPath, (e) => {
31
e.returnValue = oldPreloadPath;
32
});
33
+
34
ipcMain.on(constants.ipcGetAppData, (e) => {
35
e.returnValue = app.getPath("appData");
36
});
37
+
ipcMain.on(constants.ipcGetInjectorConfig, (e) => {
38
+
e.returnValue = injectorConfig;
39
});
40
ipcMain.handle(constants.ipcMessageBox, (_, opts) => {
41
electron.dialog.showMessageBoxSync(opts);
···
44
corsAllow = list;
45
});
46
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:"];
82
83
const csp = "content-security-policy";
84
if (headers[csp] == null) return;
···
95
96
for (const directive of directives) {
97
parts[directive] = values;
98
+
}
99
+
100
+
for (const [directive, urls] of Object.entries(extensionCspOverrides)) {
101
+
parts[directive] ??= [];
102
+
parts[directive].push(...urls);
103
}
104
105
const stringified = Object.entries<string[]>(parts)
···
110
headers[csp] = [stringified];
111
}
112
113
class BrowserWindow extends ElectronBrowserWindow {
114
constructor(opts: BrowserWindowConstructorOptions) {
115
+
const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;
116
117
+
if (isMainWindow) {
118
+
if (!oldPreloadPath) oldPreloadPath = opts.webPreferences!.preload;
119
opts.webPreferences!.preload = require.resolve("./node-preload.js");
120
}
121
122
// Event for modifying window options
123
+
moonlightHost.events.emit("window-options", opts, isMainWindow);
124
125
super(opts);
126
127
// Event for when a window is created
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
+
}
141
142
this.webContents.session.webRequest.onHeadersReceived((details, cb) => {
143
if (details.responseHeaders != null) {
144
// Patch CSP so things can use externally hosted assets
145
if (details.resourceType === "mainFrame") {
146
+
patchCsp(details.responseHeaders, extensionCspOverrides);
147
}
148
149
// Allow plugins to bypass CORS for specific URLs
150
if (corsAllow.some((x) => details.url.startsWith(x))) {
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] = ["*"];
158
}
159
+
160
+
moonlightHost.events.emit("headers-received", details, isMainWindow);
161
162
cb({ cancel: false, responseHeaders: details.responseHeaders });
163
}
164
});
165
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);
195
}
196
+
197
+
if (hasUrl) return cb({ cancel: true });
198
}
199
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
+
});
204
}
205
}
206
···
221
writable: false
222
});
223
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
+
235
try {
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();
242
243
// Duplicated in node-preload... oops
244
function getConfig(ext: string) {
245
const val = config.extensions[ext];
246
if (val == null || typeof val === "boolean") return undefined;
247
return val.config;
248
}
249
global.moonlightHost = {
250
+
get config() {
251
+
return config;
252
+
},
253
+
extensions,
254
+
processedExtensions,
255
asarPath,
256
events: new EventEmitter(),
257
+
258
+
version: MOONLIGHT_VERSION,
259
+
branch: MOONLIGHT_BRANCH as MoonlightBranch,
260
261
getConfig,
262
+
getConfigPath,
263
+
getConfigOption(ext, name) {
264
+
const manifest = getManifest(extensions, ext);
265
+
return getConfigOption(ext, name, config, manifest?.settings);
266
},
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) {
277
return new Logger(id);
278
+
},
279
+
getMoonlightDir() {
280
+
return moonlightDir;
281
+
},
282
+
getExtensionDir: (ext: string) => {
283
+
return path.join(extensionsPath, ext);
284
}
285
};
286
287
patchElectron();
288
289
await loadProcessedExtensions(global.moonlightHost.processedExtensions);
290
} catch (error) {
291
logger.error("Failed to inject:", error);
292
}
293
294
+
if (injectorConfig?.disablePersist !== true) {
295
+
persist(asarPath);
296
+
}
297
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
+
}
303
}
304
305
function patchElectron() {
···
313
configurable: false
314
});
315
} else {
316
+
Object.defineProperty(electronClone, property, Object.getOwnPropertyDescriptor(electron, property)!);
317
}
318
}
319
+8
-1
packages/node-preload/package.json
+8
-1
packages/node-preload/package.json
···
1
{
2
"name": "@moonlight-mod/node-preload",
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
+
},
14
+
"engineStrict": true
15
}
+144
-46
packages/node-preload/src/index.ts
+144
-46
packages/node-preload/src/index.ts
···
1
import { webFrame, ipcRenderer, contextBridge } from "electron";
2
-
import fs from "fs";
3
-
import path from "path";
4
5
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
6
-
import { constants } from "@moonlight-mod/types";
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";
14
15
async function injectGlobals() {
16
-
const config = readConfig();
17
-
const extensions = getExtensions();
18
-
const processed = await loadExtensions(extensions);
19
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
-
}
25
26
global.moonlightNode = {
27
-
config,
28
-
extensions: getExtensions(),
29
-
processedExtensions: processed,
30
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;
38
},
39
getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
40
getLogger: (id: string) => {
41
return new Logger(id);
42
},
43
-
44
-
getExtensionDir: (ext: string) => {
45
-
const extPath = getExtensionsPath();
46
-
return path.join(extPath, ext);
47
},
48
-
writeConfig
49
};
50
51
-
await loadProcessedExtensions(processed);
52
contextBridge.exposeInMainWorld("moonlightNode", moonlightNode);
53
54
-
const extCors = moonlightNode.processedExtensions.extensions
55
-
.map((x) => x.manifest.cors ?? [])
56
-
.flat();
57
58
for (const repo of moonlightNode.config.repositories) {
59
const url = new URL(repo);
60
url.pathname = "/";
61
-
extCors.push(url.toString());
62
}
63
64
-
ipcRenderer.invoke(constants.ipcSetCorsList, extCors);
65
}
66
67
async function loadPreload() {
68
const webPreloadPath = path.join(__dirname, "web-preload.js");
69
const webPreload = fs.readFileSync(webPreloadPath, "utf8");
70
await webFrame.executeJavaScript(webPreload);
71
}
72
73
-
async function init(oldPreloadPath: string) {
74
try {
75
await injectGlobals();
76
await loadPreload();
···
81
message: message
82
});
83
}
84
85
-
// Let Discord start even if we fail
86
-
if (oldPreloadPath) require(oldPreloadPath);
87
}
88
-
89
-
const oldPreloadPath: string = ipcRenderer.sendSync(
90
-
constants.ipcGetOldPreloadPath
91
-
);
92
-
init(oldPreloadPath);
···
1
import { webFrame, ipcRenderer, contextBridge } from "electron";
2
+
import fs from "node:fs";
3
+
import path from "node:path";
4
5
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
6
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
7
import { getExtensions } from "@moonlight-mod/core/extension";
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
+
}
25
26
async function injectGlobals() {
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
+
};
38
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();
47
48
global.moonlightNode = {
49
+
get config() {
50
+
return config;
51
+
},
52
+
extensions,
53
+
processedExtensions,
54
nativesCache: {},
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);
76
},
77
+
78
getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
79
getLogger: (id: string) => {
80
return new Logger(id);
81
},
82
+
getMoonlightDir() {
83
+
return moonlightDir;
84
},
85
+
getExtensionDir: (ext: string) => {
86
+
return path.join(extensionsPath, ext);
87
+
}
88
};
89
90
+
await loadProcessedExtensions(processedExtensions);
91
contextBridge.exposeInMainWorld("moonlightNode", moonlightNode);
92
93
+
const extCors = moonlightNode.processedExtensions.extensions.flatMap((x) => x.manifest.cors ?? []);
94
+
for (const cors of extCors) {
95
+
registerCors(cors);
96
+
}
97
98
for (const repo of moonlightNode.config.repositories) {
99
const url = new URL(repo);
100
url.pathname = "/";
101
+
registerCors(url.toString());
102
}
103
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;
112
}
113
114
async function loadPreload() {
115
const webPreloadPath = path.join(__dirname, "web-preload.js");
116
const webPreload = fs.readFileSync(webPreloadPath, "utf8");
117
await webFrame.executeJavaScript(webPreload);
118
+
119
+
const func = await webFrame.executeJavaScript("async () => { await window._moonlightWebLoad(); }");
120
+
await func();
121
}
122
123
+
async function init() {
124
try {
125
await injectGlobals();
126
await loadPreload();
···
131
message: message
132
});
133
}
134
+
}
135
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
+
});
190
}
+4
-1
packages/node-preload/tsconfig.json
+4
-1
packages/node-preload/tsconfig.json
+15
-6
packages/types/package.json
+15
-6
packages/types/package.json
···
1
{
2
"name": "@moonlight-mod/types",
3
-
"version": "1.1.8",
4
-
"main": "./src/index.ts",
5
-
"types": "./src/index.ts",
6
"exports": {
7
".": "./src/index.ts",
8
"./import": "./src/import.d.ts",
9
"./*": "./src/*.ts"
10
},
11
"dependencies": {
12
-
"@types/flux": "^3.1.12",
13
-
"@types/react": "^18.2.22",
14
-
"csstype": "^3.1.2",
15
"standalone-electron-types": "^1.0.0"
16
}
17
}
···
1
{
2
"name": "@moonlight-mod/types",
3
+
"version": "1.3.17",
4
"exports": {
5
".": "./src/index.ts",
6
"./import": "./src/import.d.ts",
7
"./*": "./src/*.ts"
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
+
},
18
"dependencies": {
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",
24
"standalone-electron-types": "^1.0.0"
25
}
26
}
+49
-3
packages/types/src/config.ts
+49
-3
packages/types/src/config.ts
···
6
patchAll?: boolean;
7
};
8
9
-
export type ConfigExtensions =
10
-
| { [key: string]: boolean }
11
-
| { [key: string]: ConfigExtension };
12
13
export type ConfigExtension = {
14
enabled: boolean;
···
35
};
36
37
export type BooleanSettingType = {
38
type: ExtensionSettingType.Boolean;
39
default?: boolean;
40
};
41
42
export type NumberSettingType = {
43
type: ExtensionSettingType.Number;
44
default?: number;
45
min?: number;
···
47
};
48
49
export type StringSettingType = {
50
type: ExtensionSettingType.String;
51
default?: string;
52
};
53
54
export type MultilineTextInputSettingType = {
55
type: ExtensionSettingType.MultilineString;
56
default?: string;
57
};
58
59
export type SelectSettingType = {
60
type: ExtensionSettingType.Select;
61
options: SelectOption[];
62
default?: string;
63
};
64
65
export type MultiSelectSettingType = {
66
type: ExtensionSettingType.MultiSelect;
67
options: string[];
68
default?: string[];
69
};
70
71
export type ListSettingType = {
72
type: ExtensionSettingType.List;
73
default?: string[];
74
};
75
76
export type DictionarySettingType = {
77
type: ExtensionSettingType.Dictionary;
78
default?: Record<string, string>;
79
};
80
81
export type CustomSettingType = {
82
type: ExtensionSettingType.Custom;
83
default?: any;
84
};
85
86
export type ExtensionSettingsManifest = {
87
displayName?: string;
88
description?: string;
89
} & (
90
| BooleanSettingType
91
| NumberSettingType
···
6
patchAll?: boolean;
7
};
8
9
+
export type ConfigExtensions = { [key: string]: boolean } | { [key: string]: ConfigExtension };
10
11
export type ConfigExtension = {
12
enabled: boolean;
···
33
};
34
35
export type BooleanSettingType = {
36
+
/**
37
+
* Displays as a simple switch.
38
+
*/
39
type: ExtensionSettingType.Boolean;
40
default?: boolean;
41
};
42
43
export type NumberSettingType = {
44
+
/**
45
+
* Displays as a simple slider.
46
+
*/
47
type: ExtensionSettingType.Number;
48
default?: number;
49
min?: number;
···
51
};
52
53
export type StringSettingType = {
54
+
/**
55
+
* Displays as a single line string input.
56
+
*/
57
type: ExtensionSettingType.String;
58
default?: string;
59
};
60
61
export type MultilineTextInputSettingType = {
62
+
/**
63
+
* Displays as a multiple line string input.
64
+
*/
65
type: ExtensionSettingType.MultilineString;
66
default?: string;
67
};
68
69
export type SelectSettingType = {
70
+
/**
71
+
* A dropdown to pick between one of many values.
72
+
*/
73
type: ExtensionSettingType.Select;
74
options: SelectOption[];
75
default?: string;
76
};
77
78
export type MultiSelectSettingType = {
79
+
/**
80
+
* A dropdown to pick multiple values.
81
+
*/
82
type: ExtensionSettingType.MultiSelect;
83
options: string[];
84
default?: string[];
85
};
86
87
export type ListSettingType = {
88
+
/**
89
+
* A list of strings that the user can add or remove from.
90
+
*/
91
type: ExtensionSettingType.List;
92
default?: string[];
93
};
94
95
export type DictionarySettingType = {
96
+
/**
97
+
* A dictionary (key-value pair) that the user can add or remove from.
98
+
*/
99
type: ExtensionSettingType.Dictionary;
100
default?: Record<string, string>;
101
};
102
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
+
*/
108
type: ExtensionSettingType.Custom;
109
default?: any;
110
};
111
112
+
export enum ExtensionSettingsAdvice {
113
+
None = "none",
114
+
Reload = "reload",
115
+
Restart = "restart"
116
+
}
117
+
118
export type ExtensionSettingsManifest = {
119
+
/**
120
+
* A human friendly name for the setting.
121
+
*/
122
displayName?: string;
123
+
124
+
/**
125
+
* A longer description for the setting.
126
+
* Markdown is not supported.
127
+
*/
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;
135
} & (
136
| BooleanSettingType
137
| NumberSettingType
+11
-1
packages/types/src/constants.ts
+11
-1
packages/types/src/constants.ts
···
2
export const distDir = "dist";
3
export const coreExtensionsDir = "core-extensions";
4
export const repoUrlFile = ".moonlight-repo-url";
5
6
export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";
7
export const ipcGetAppData = "_moonlight_getAppData";
8
-
export const ipcGetIsMoonlightDesktop = "_moonlight_getIsMoonlightDesktop";
9
export const ipcMessageBox = "_moonlight_messageBox";
10
export const ipcSetCorsList = "_moonlight_setCorsList";
···
2
export const distDir = "dist";
3
export const coreExtensionsDir = "core-extensions";
4
export const repoUrlFile = ".moonlight-repo-url";
5
+
export const installedVersionFile = ".moonlight-installed-version";
6
7
+
export const ipcNodePreloadKickoff = "_moonlight_nodePreloadKickoff";
8
export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";
9
+
10
export const ipcGetAppData = "_moonlight_getAppData";
11
+
export const ipcGetInjectorConfig = "_moonlight_getInjectorConfig";
12
export const ipcMessageBox = "_moonlight_messageBox";
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
-409
packages/types/src/coreExtensions/components.ts
-409
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 TextAreaAutoComplete {
47
-
ON = "on",
48
-
OFF = "off"
49
-
}
50
-
51
-
export enum TextAreaWrap {
52
-
HARD = "hard",
53
-
SOFT = "soft",
54
-
OFF = "off"
55
-
}
56
-
57
-
interface TextArea
58
-
extends ComponentClass<
59
-
PropsWithoutRef<{
60
-
value?: string;
61
-
defaultValue?: string;
62
-
autoComplete?: TextAreaAutoComplete;
63
-
autoFocus?: boolean;
64
-
cols?: number;
65
-
disabled?: boolean;
66
-
form?: string;
67
-
maxLength?: number;
68
-
minLength?: number;
69
-
name?: string;
70
-
onChange?: (value: string, name: string) => void;
71
-
onChangeCapture?: (value: string, name: string) => void;
72
-
onInput?: (value: string, name: string) => void;
73
-
onInputCapture?: (value: string, name: string) => void;
74
-
onInvalid?: (value: string, name: string) => void;
75
-
onInvalidCapture?: (value: string, name: string) => void;
76
-
onSelect?: (value: string, name: string) => void;
77
-
onSelectCapture?: (value: string, name: string) => void;
78
-
placeholder?: string;
79
-
readOnly?: boolean;
80
-
required?: boolean;
81
-
rows?: number;
82
-
wrap?: TextAreaWrap;
83
-
className?: string;
84
-
}>
85
-
> {
86
-
AutoCompletes: typeof TextAreaAutoComplete;
87
-
Wraps: typeof TextAreaWrap;
88
-
}
89
-
90
-
export enum FormTextTypes {
91
-
DEFAULT = "default",
92
-
DESCRIPTION = "description",
93
-
ERROR = "error",
94
-
INPUT_PLACEHOLDER = "placeholder",
95
-
LABEL_BOLD = "labelBold",
96
-
LABEL_DESCRIPTOR = "labelDescriptor",
97
-
LABEL_SELECTED = "labelSelected",
98
-
SUCCESS = "success"
99
-
}
100
-
101
-
interface FormText
102
-
extends ComponentClass<
103
-
PropsWithChildren<{
104
-
type?: FormTextTypes;
105
-
className?: string;
106
-
disabled?: boolean;
107
-
selectable?: boolean;
108
-
style?: CSSProperties;
109
-
}>
110
-
> {
111
-
Types: FormTextTypes;
112
-
}
113
-
114
-
declare enum SliderMarkerPosition {
115
-
ABOVE,
116
-
BELOW
117
-
}
118
-
119
-
declare enum ButtonLooks {
120
-
FILLED = "lookFilled",
121
-
INVERTED = "lookInverted",
122
-
OUTLINED = "lookOutlined",
123
-
LINK = "lookLink",
124
-
BLANK = "lookBlank"
125
-
}
126
-
declare enum ButtonColors {
127
-
BRAND = "colorBrand",
128
-
RED = "colorRed",
129
-
GREEN = "colorGreen",
130
-
YELLOW = "colorYellow",
131
-
PRIMARY = "colorPrimary",
132
-
LINK = "colorLink",
133
-
WHITE = "colorWhite",
134
-
BLACK = "colorBlack",
135
-
TRANSPARENT = "colorTransparent",
136
-
BRAND_NEW = "colorBrandNew",
137
-
CUSTOM = ""
138
-
}
139
-
declare enum ButtonBorderColors {
140
-
BRAND = "borderBrand",
141
-
RED = "borderRed",
142
-
GREEN = "borderGreen",
143
-
YELLOW = "borderYellow",
144
-
PRIMARY = "borderPrimary",
145
-
LINK = "borderLink",
146
-
WHITE = "borderWhite",
147
-
BLACK = "borderBlack",
148
-
TRANSPARENT = "borderTransparent",
149
-
BRAND_NEW = "borderBrandNew"
150
-
}
151
-
declare enum ButtonHovers {
152
-
DEFAULT = "",
153
-
BRAND = "hoverBrand",
154
-
RED = "hoverRed",
155
-
GREEN = "hoverGreen",
156
-
YELLOW = "hoverYellow",
157
-
PRIMARY = "hoverPrimary",
158
-
LINK = "hoverLink",
159
-
WHITE = "hoverWhite",
160
-
BLACK = "hoverBlack",
161
-
TRANSPARENT = "hoverTransparent"
162
-
}
163
-
declare enum ButtonSizes {
164
-
NONE = "",
165
-
TINY = "sizeTiny",
166
-
SMALL = "sizeSmall",
167
-
MEDIUM = "sizeMedium",
168
-
LARGE = "sizeLarge",
169
-
XLARGE = "sizeXlarge",
170
-
MIN = "sizeMin",
171
-
MAX = "sizeMax",
172
-
ICON = "sizeIcon"
173
-
}
174
-
175
-
type Button = ComponentType<
176
-
PropsWithChildren<{
177
-
look?: ButtonLooks;
178
-
color?: ButtonColors;
179
-
borderColor?: ButtonBorderColors;
180
-
hover?: ButtonHovers;
181
-
size?: ButtonSizes;
182
-
fullWidth?: boolean;
183
-
grow?: boolean;
184
-
disabled?: boolean;
185
-
submitting?: boolean;
186
-
type?: string;
187
-
style?: CSSProperties;
188
-
wrapperClassName?: string;
189
-
className?: string;
190
-
innerClassName?: string;
191
-
onClick?: MouseEventHandler;
192
-
onDoubleClick?: MouseEventHandler;
193
-
onMouseDown?: MouseEventHandler;
194
-
onMouseUp?: MouseEventHandler;
195
-
onMouseEnter?: MouseEventHandler;
196
-
onMouseLeave?: MouseEventHandler;
197
-
onKeyDown?: KeyboardEventHandler;
198
-
rel?: any;
199
-
buttonRef?: Ref<any>;
200
-
focusProps?: PropsWithChildren<any>;
201
-
"aria-label"?: string;
202
-
submittingStartedLabel?: string;
203
-
submittingFinishedLabel?: string;
204
-
}>
205
-
> & {
206
-
Looks: typeof ButtonLooks;
207
-
Colors: typeof ButtonColors;
208
-
BorderColors: typeof ButtonBorderColors;
209
-
Hovers: typeof ButtonHovers;
210
-
Sizes: typeof ButtonSizes;
211
-
};
212
-
213
-
export enum FlexDirection {
214
-
VERTICAL = "vertical",
215
-
HORIZONTAL = "horizontal",
216
-
HORIZONTAL_REVERSE = "horizontalReverse"
217
-
}
218
-
219
-
declare enum FlexAlign {
220
-
START = "alignStart",
221
-
END = "alignEnd",
222
-
CENTER = "alignCenter",
223
-
STRETCH = "alignStretch",
224
-
BASELINE = "alignBaseline"
225
-
}
226
-
declare enum FlexJustify {
227
-
START = "justifyStart",
228
-
END = "justifyEnd",
229
-
CENTER = "justifyCenter",
230
-
BETWEEN = "justifyBetween",
231
-
AROUND = "justifyAround"
232
-
}
233
-
declare enum FlexWrap {
234
-
NO_WRAP = "noWrap",
235
-
WRAP = "wrap",
236
-
WRAP_REVERSE = "wrapReverse"
237
-
}
238
-
interface Flex
239
-
extends ComponentClass<
240
-
PropsWithChildren<{
241
-
className?: string;
242
-
direction?: FlexDirection;
243
-
justify?: FlexJustify;
244
-
align?: FlexAlign;
245
-
wrap?: FlexWrap;
246
-
shrink?: CSS.Property.FlexShrink;
247
-
grow?: CSS.Property.FlexGrow;
248
-
basis?: CSS.Property.FlexBasis;
249
-
style?: CSSProperties;
250
-
}>
251
-
> {
252
-
Direction: typeof FlexDirection;
253
-
Align: typeof FlexAlign;
254
-
Justify: typeof FlexJustify;
255
-
Wrap: typeof FlexWrap;
256
-
Child: Component<
257
-
PropsWithChildren<{
258
-
className?: string;
259
-
shrink?: CSS.Property.FlexShrink;
260
-
grow?: CSS.Property.FlexGrow;
261
-
basis?: CSS.Property.FlexBasis;
262
-
style?: CSSProperties;
263
-
wrap?: boolean;
264
-
}>
265
-
>;
266
-
}
267
-
268
-
// TODO: wtaf is up with react types not working in jsx
269
-
export type CommonComponents = {
270
-
[index: string]: any;
271
-
Clickable: ComponentClass<
272
-
PropsWithChildren<{
273
-
onClick?: () => void;
274
-
href?: any;
275
-
onKeyPress?: () => void;
276
-
ignoreKeyPress?: boolean;
277
-
innerRef?: Ref<any>;
278
-
focusProps?: any;
279
-
tag?: string | Component;
280
-
role?: any;
281
-
tabIndex?: any;
282
-
className?: string;
283
-
}>
284
-
>;
285
-
TextInput: TextInput;
286
-
TextArea: TextArea;
287
-
FormDivider: ComponentClass<any>;
288
-
FormSection: ComponentClass<
289
-
PropsWithChildren<{
290
-
className?: string;
291
-
titleClassName?: string;
292
-
title?: ReactNode;
293
-
icon?: ReactNode;
294
-
disabled?: boolean;
295
-
htmlFor?: any;
296
-
tag?: string;
297
-
}>
298
-
>;
299
-
FormText: FormText;
300
-
FormTitle: ComponentClass<
301
-
PropsWithChildren<{
302
-
tag?: string;
303
-
className?: string;
304
-
faded?: boolean;
305
-
disabled?: boolean;
306
-
required?: boolean;
307
-
error?: string;
308
-
}>
309
-
>;
310
-
FormSwitch: ComponentClass<PropsWithChildren<any>>;
311
-
FormItem: ComponentClass<PropsWithChildren<any>>;
312
-
Slider: ComponentClass<
313
-
PropsWithChildren<{
314
-
disabled?: boolean;
315
-
stickToMarkers?: boolean;
316
-
className?: string;
317
-
barStyles?: CSSProperties;
318
-
fillStyles?: CSSProperties;
319
-
mini?: boolean;
320
-
hideBubble?: boolean;
321
-
initialValue?: number;
322
-
orientation?: "horizontal" | "vertical";
323
-
onValueRender?: (value: number) => string;
324
-
renderMarker?: (marker: number) => ReactNode;
325
-
getAriaValueText?: (value: number) => string;
326
-
barClassName?: string;
327
-
grabberClassName?: string;
328
-
grabberStyles?: CSSProperties;
329
-
markerPosition?: SliderMarkerPosition;
330
-
"aria-hidden"?: "true" | "false";
331
-
"aria-label"?: string;
332
-
"aria-labelledby"?: string;
333
-
"aria-describedby"?: string;
334
-
minValue?: number;
335
-
maxValue?: number;
336
-
asValueChanges?: (value: number) => void;
337
-
onValueChange?: (value: number) => void;
338
-
keyboardStep?: number;
339
-
}>
340
-
>;
341
-
Switch: ComponentClass<PropsWithChildren<any>>;
342
-
Button: Button;
343
-
Tooltip: ComponentClass<PropsWithChildren<any>>;
344
-
SmallSlider: Component;
345
-
Avatar: Component;
346
-
Scroller: Component;
347
-
Text: ComponentClass<PropsWithChildren<any>>;
348
-
Heading: ComponentClass<PropsWithChildren<any>>;
349
-
LegacyText: Component;
350
-
Flex: Flex;
351
-
Card: ComponentClass<PropsWithChildren<any>>;
352
-
Popout: ComponentClass<PropsWithChildren<any>>;
353
-
Dialog: ComponentClass<PropsWithChildren<any>>;
354
-
Menu: ComponentClass<PropsWithChildren<any>>;
355
-
MenuItem: ComponentClass<PropsWithChildren<any>>;
356
-
MenuGroup: ComponentClass<PropsWithChildren<any>>;
357
-
MenuCheckboxItem: ComponentClass<PropsWithChildren<any>>;
358
-
CardClasses: {
359
-
card: string;
360
-
cardHeader: string;
361
-
};
362
-
ControlClasses: {
363
-
container: string;
364
-
control: string;
365
-
disabled: string;
366
-
dividerDefault: string;
367
-
labelRow: string;
368
-
note: string;
369
-
title: string;
370
-
titleDefault: string;
371
-
titleMini: string;
372
-
};
373
-
MarkdownParser: {
374
-
parse: (text: string) => ReactElement;
375
-
};
376
-
SettingsNotice: React.ComponentType<{
377
-
submitting: boolean;
378
-
onReset: () => void;
379
-
onSave: () => void;
380
-
}>;
381
-
TabBar: React.ComponentType<any> & {
382
-
Item: React.ComponentType<any>;
383
-
};
384
-
SingleSelect: React.ComponentType<{
385
-
autofocus?: boolean;
386
-
clearable?: boolean;
387
-
value?: string;
388
-
options?: {
389
-
value: string;
390
-
label: string;
391
-
}[];
392
-
onChange?: (value: string) => void;
393
-
}>;
394
-
Select: React.ComponentType<{
395
-
autofocus?: boolean;
396
-
clearable?: boolean;
397
-
value?: string[];
398
-
options?: {
399
-
value: string;
400
-
label: string;
401
-
}[];
402
-
onChange?: (value: string[]) => void;
403
-
}>;
404
-
405
-
// TODO
406
-
useVariableSelect: any;
407
-
multiSelect: any;
408
-
tokens: any;
409
-
};
···
+21
-121
packages/types/src/coreExtensions/contextMenu.ts
+21
-121
packages/types/src/coreExtensions/contextMenu.ts
···
1
-
// TODO: Deduplicate common props
2
-
3
-
export type Menu = React.FunctionComponent<{
4
-
navId: string;
5
-
variant?: string;
6
-
hideScrollbar?: boolean;
7
-
className?: string;
8
-
children: React.ReactComponentElement<MenuElement>[];
9
-
onClose?: () => void;
10
-
onSelect?: () => void;
11
-
}>;
12
-
export type MenuProps = React.ComponentProps<Menu>;
13
-
14
-
export type MenuElement =
15
-
| MenuSeparator
16
-
| MenuGroup
17
-
| MenuItem
18
-
| MenuCheckboxItem
19
-
| MenuRadioItem
20
-
| MenuControlItem;
21
-
22
-
/* eslint-disable prettier/prettier */
23
-
export type MenuSeparator = React.FunctionComponent;
24
-
export type MenuGroup = React.FunctionComponent<{
25
-
label?: string;
26
-
className?: string;
27
-
color?: string;
28
-
children: React.ReactComponentElement<MenuElement>[];
29
-
}>;
30
-
export type MenuItem = React.FunctionComponent<
31
-
{
32
-
id: any;
33
-
dontCloseOnActionIfHoldingShiftKey?: boolean;
34
-
} & (
35
-
| {
36
-
label: string;
37
-
subtext?: string;
38
-
color?: string;
39
-
hint?: string;
40
-
disabled?: boolean;
41
-
icon?: any;
42
-
showIconFirst?: boolean;
43
-
imageUrl?: string;
44
-
45
-
className?: string;
46
-
focusedClassName?: string;
47
-
subMenuIconClassName?: string;
48
-
49
-
action?: () => void;
50
-
onFocus?: () => void;
51
-
52
-
iconProps?: any;
53
-
sparkle?: any;
54
-
55
-
children?: React.ReactComponentElement<MenuElement>[];
56
-
onChildrenScroll?: any;
57
-
childRowHeight?: any;
58
-
listClassName?: string;
59
-
subMenuClassName?: string;
60
-
}
61
-
| {
62
-
color?: string;
63
-
disabled?: boolean;
64
-
keepItemStyles?: boolean;
65
-
66
-
action?: () => void;
67
-
68
-
render: any;
69
-
navigable?: boolean;
70
-
}
71
-
)
72
-
>;
73
-
export type MenuCheckboxItem = React.FunctionComponent<{
74
-
id: any;
75
-
label: string;
76
-
subtext?: string;
77
-
color?: string;
78
-
className?: string;
79
-
focusedClassName?: string;
80
-
disabled?: boolean;
81
-
checked: boolean;
82
-
action?: () => void;
83
-
}>;
84
-
export type MenuRadioItem = React.FunctionComponent<{
85
-
id: any;
86
-
label: string;
87
-
subtext?: string;
88
-
color?: string;
89
-
disabled?: boolean;
90
-
action?: () => void;
91
-
}>;
92
-
export type MenuControlItem = React.FunctionComponent<
93
-
{
94
-
id: any;
95
-
label: string;
96
-
color?: string;
97
-
disabled?: boolean;
98
-
showDefaultFocus?: boolean;
99
-
} & (
100
-
| {
101
-
control: any;
102
-
}
103
-
| {
104
-
control?: undefined;
105
-
interactive?: boolean;
106
-
children?: React.ReactComponentElement<MenuElement>[];
107
-
}
108
-
)
109
-
>;
110
-
/* eslint-disable prettier/prettier */
111
112
export type ContextMenu = {
113
-
addItem: (
114
-
navId: string,
115
-
item: (props: any) => React.ReactComponentElement<MenuElement>,
116
-
anchorId: string,
117
-
before?: boolean
118
-
) => void;
119
120
MenuCheckboxItem: MenuCheckboxItem;
121
MenuControlItem: MenuControlItem;
···
157
label: string;
158
};
159
160
-
export type EvilItemParser = (
161
-
el:
162
-
| React.ReactComponentElement<MenuElement>
163
-
| React.ReactComponentElement<MenuElement>[]
164
-
) => InternalItem[];
···
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;
···
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
12
export type ASTNode = SingleASTNode | Array<SingleASTNode>;
13
14
-
export type Parser = (
15
-
source: string,
16
-
state?: State | null | undefined
17
-
) => Array<SingleASTNode>;
18
19
-
export type ParseFunction = (
20
-
capture: Capture,
21
-
nestedParse: Parser,
22
-
state: State
23
-
) => UntypedASTNode | ASTNode;
24
25
export type Capture =
26
| (Array<string> & {
···
38
39
export type MatchFunction = {
40
regex?: RegExp;
41
-
} & ((
42
-
source: string,
43
-
state: State,
44
-
prevCapture: string
45
-
) => Capture | null | undefined);
46
47
-
export type Output<Result> = (
48
-
node: ASTNode,
49
-
state?: State | null | undefined
50
-
) => Result;
51
52
-
export type SingleNodeOutput<Result> = (
53
-
node: SingleASTNode,
54
-
nestedOutput: Output<Result>,
55
-
state: State
56
-
) => Result;
57
58
// }}}
59
···
100
slateDecorators: Record<string, string>;
101
ruleBlacklists: Record<Ruleset, Record<string, boolean>>;
102
103
addRule: (
104
name: string,
105
markdown: (rules: Record<string, MarkdownRule>) => MarkdownRule,
106
slate: (rules: Record<string, SlateRule>) => SlateRule,
107
decorator?: string | undefined
108
) => void;
109
blacklistFromRuleset: (ruleset: Ruleset, name: string) => void;
110
};
···
11
12
export type ASTNode = SingleASTNode | Array<SingleASTNode>;
13
14
+
export type Parser = (source: string, state?: State | null | undefined) => Array<SingleASTNode>;
15
16
+
export type ParseFunction = (capture: Capture, nestedParse: Parser, state: State) => UntypedASTNode | ASTNode;
17
18
export type Capture =
19
| (Array<string> & {
···
31
32
export type MatchFunction = {
33
regex?: RegExp;
34
+
} & ((source: string, state: State, prevCapture: string) => Capture | null | undefined);
35
36
+
export type Output<Result> = (node: ASTNode, state?: State | null | undefined) => Result;
37
38
+
export type SingleNodeOutput<Result> = (node: SingleASTNode, nestedOutput: Output<Result>, state: State) => Result;
39
40
// }}}
41
···
82
slateDecorators: Record<string, string>;
83
ruleBlacklists: Record<Ruleset, Record<string, boolean>>;
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
+
*/
94
addRule: (
95
name: string,
96
markdown: (rules: Record<string, MarkdownRule>) => MarkdownRule,
97
slate: (rules: Record<string, SlateRule>) => SlateRule,
98
decorator?: string | undefined
99
) => void;
100
+
101
+
/**
102
+
* Blacklist a rule from a ruleset.
103
+
* @param ruleset The ruleset name
104
+
* @param name The rule name
105
+
*/
106
blacklistFromRuleset: (ruleset: Ruleset, name: string) => void;
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
+
};
+8
-80
packages/types/src/coreExtensions.ts
+8
-80
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, { ReactElement } 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
-
chunk: RegExp,
33
-
module: RegExp
34
-
) => Promise<any>;
35
-
filterReal: (modules: WebpackModule[]) => WebpackModule[];
36
-
};
37
-
38
-
export type NoticeProps = {
39
-
stores: Store<any>[];
40
-
element: React.FunctionComponent;
41
-
};
42
-
43
-
export type SettingsSection =
44
-
| { section: "DIVIDER"; pos: number }
45
-
| { section: "HEADER"; label: string; pos: number }
46
-
| {
47
-
section: string;
48
-
label: string;
49
-
color: string | null;
50
-
element: React.FunctionComponent;
51
-
pos: number;
52
-
notice?: NoticeProps;
53
-
_moonlight_submenu?: () => ReactElement | ReactElement[];
54
-
};
55
-
56
-
export type Settings = {
57
-
ourSections: SettingsSection[];
58
-
sectionNames: string[];
59
-
sectionMenuItems: Record<string, ReactElement[]>;
60
-
61
-
addSection: (
62
-
section: string,
63
-
label: string,
64
-
element: React.FunctionComponent,
65
-
color?: string | null,
66
-
pos?: number,
67
-
notice?: NoticeProps
68
-
) => void;
69
-
addSectionMenuItems: (section: string, ...items: ReactElement[]) => void;
70
-
71
-
addDivider: (pos: number | null) => void;
72
-
addHeader: (label: string, pos: number | null) => void;
73
-
_mutateSections: (sections: SettingsSection[]) => SettingsSection[];
74
-
};
75
-
76
-
export type CommonReact = typeof import("react");
77
-
export type CommonFlux = FluxDefault;
78
-
export type CommonComponents = CommonComponents_; // lol
79
-
export type CommonFluxDispatcher = Dispatcher<any>;
80
-
81
export * as Markdown from "./coreExtensions/markdown";
82
export * as ContextMenu from "./coreExtensions/contextMenu";
···
1
+
export * as Spacepack from "./coreExtensions/spacepack";
2
+
export * as Settings from "./coreExtensions/settings";
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
-
}
···
+31
-22
packages/types/src/discord/require.ts
+31
-22
packages/types/src/discord/require.ts
···
1
-
import {
2
-
Spacepack,
3
-
CommonReact,
4
-
CommonFlux,
5
-
Settings,
6
-
CommonComponents,
7
-
CommonFluxDispatcher
8
-
} from "../coreExtensions";
9
import { ContextMenu, EvilItemParser } from "../coreExtensions/contextMenu";
10
import { Markdown } from "../coreExtensions/markdown";
11
12
declare function WebpackRequire(id: string): any;
13
-
declare function WebpackRequire(id: "spacepack_spacepack"): {
14
-
default: Spacepack;
15
-
spacepack: Spacepack;
16
-
};
17
18
-
declare function WebpackRequire(id: "common_components"): CommonComponents;
19
-
declare function WebpackRequire(id: "common_flux"): CommonFlux;
20
-
declare function WebpackRequire(
21
-
id: "common_fluxDispatcher"
22
-
): CommonFluxDispatcher;
23
-
declare function WebpackRequire(id: "common_react"): CommonReact;
24
25
declare function WebpackRequire(id: "settings_settings"): {
26
Settings: Settings;
27
default: Settings;
28
};
29
30
-
declare function WebpackRequire(id: "markdown_markdown"): Markdown;
31
-
32
-
declare function WebpackRequire(id: "contextMenu_evilMenu"): EvilItemParser;
33
-
declare function WebpackRequire(id: "contextMenu_contextMenu"): ContextMenu;
34
35
export default WebpackRequire;
···
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";
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";
11
12
declare function WebpackRequire(id: string): any;
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;
31
+
32
+
declare function WebpackRequire(id: "notices_notices"): Notices;
33
34
declare function WebpackRequire(id: "settings_settings"): {
35
Settings: Settings;
36
default: Settings;
37
};
38
39
+
declare function WebpackRequire(id: "spacepack_spacepack"): {
40
+
default: Spacepack;
41
+
spacepack: Spacepack;
42
+
};
43
44
export default WebpackRequire;
+10
-16
packages/types/src/discord/webpack.ts
+10
-16
packages/types/src/discord/webpack.ts
···
1
import WebpackRequire from "./require";
2
3
-
export type WebpackRequireType = typeof WebpackRequire & {
4
-
c: Record<string, WebpackModule>;
5
-
m: Record<string, WebpackModuleFunc>;
6
-
e: (module: number | string) => Promise<void>;
7
-
};
8
9
export type WebpackModule = {
10
id: string | number;
11
-
loaded: boolean;
12
exports: any;
13
};
14
15
-
export type WebpackModuleFunc = ((
16
-
module: any,
17
-
exports: any,
18
-
require: WebpackRequireType
19
-
) => void) & {
20
__moonlight?: boolean;
21
};
22
23
-
export type WebpackJsonpEntry = [
24
-
number[],
25
-
{ [id: string]: WebpackModuleFunc },
26
-
(require: WebpackRequireType) => any
27
-
];
28
29
export type WebpackJsonp = WebpackJsonpEntry[] & {
30
push: {
···
1
import WebpackRequire from "./require";
2
+
import { WebpackRequire as MappingsWebpackRequire } from "@moonlight-mod/mappings";
3
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
+
};
10
11
export type WebpackModule = {
12
id: string | number;
13
+
loaded?: boolean;
14
exports: any;
15
};
16
17
+
export type WebpackModuleFunc = ((module: any, exports: any, require: WebpackRequireType) => void) & {
18
__moonlight?: boolean;
19
};
20
21
+
export type WebpackJsonpEntry = [number[], { [id: string]: WebpackModuleFunc }, (require: WebpackRequireType) => any];
22
23
export type WebpackJsonp = WebpackJsonpEntry[] & {
24
push: {
+116
-4
packages/types/src/extension.ts
+116
-4
packages/types/src/extension.ts
···
28
};
29
30
export type ExtensionManifest = {
31
id: string;
32
version?: string;
33
34
meta?: {
35
name?: string;
36
tagline?: string;
37
description?: string;
38
authors?: ExtensionAuthor[];
39
-
deprecated?: boolean;
40
tags?: ExtensionTag[];
41
source?: string;
42
};
43
44
dependencies?: string[];
45
suggested?: string[];
46
incompatible?: string[];
47
48
settings?: Record<string, ExtensionSettingsManifest>;
49
cors?: string[];
50
};
51
52
export enum ExtensionLoadSource {
53
Developer,
54
Core,
···
65
webpackModules?: Record<string, string>;
66
nodePath?: string;
67
hostPath?: string;
68
};
69
};
70
···
96
export type Patch = {
97
find: PatchMatch;
98
replace: PatchReplace | PatchReplace[];
99
prerequisite?: () => boolean;
100
};
101
102
export type ExplicitExtensionDependency = {
103
-
ext: string;
104
id: string;
105
};
106
···
123
id: number;
124
};
125
126
-
export type IdentifiedWebpackModule = ExtensionWebpackModule &
127
-
ExplicitExtensionDependency;
···
28
};
29
30
export type ExtensionManifest = {
31
+
$schema?: string;
32
+
33
+
/**
34
+
* A unique identifier for your extension.
35
+
*/
36
id: string;
37
+
38
+
/**
39
+
* A version string for your extension - doesn't need to follow a specific format. Required for publishing.
40
+
*/
41
version?: string;
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
+
*/
56
meta?: {
57
+
/**
58
+
* A human friendly name for your extension as a proper noun.
59
+
*/
60
name?: string;
61
+
62
+
/**
63
+
* A short tagline that appears below the name.
64
+
*/
65
tagline?: string;
66
+
67
+
/**
68
+
* A longer description that can use Markdown.
69
+
*/
70
description?: string;
71
+
72
+
/**
73
+
* List of authors that worked on this extension - accepts string or object with ID.
74
+
*/
75
authors?: ExtensionAuthor[];
76
+
77
+
/**
78
+
* A list of tags that are relevant to the extension.
79
+
*/
80
tags?: ExtensionTag[];
81
+
82
+
/**
83
+
* The URL to the source repository.
84
+
*/
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;
102
};
103
104
+
/**
105
+
* A list of extension IDs that are required for the extension to load.
106
+
*/
107
dependencies?: string[];
108
+
109
+
/**
110
+
* A list of extension IDs that the user may want to install.
111
+
*/
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
+
*/
118
incompatible?: string[];
119
120
+
/**
121
+
* A list of settings for your extension, where the key is the settings ID.
122
+
*/
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
+
*/
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[]>;
144
};
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
+
163
export enum ExtensionLoadSource {
164
Developer,
165
Core,
···
176
webpackModules?: Record<string, string>;
177
nodePath?: string;
178
hostPath?: string;
179
+
style?: string;
180
};
181
};
182
···
208
export type Patch = {
209
find: PatchMatch;
210
replace: PatchReplace | PatchReplace[];
211
+
hardFail?: boolean; // if any patches fail, all fail
212
prerequisite?: () => boolean;
213
};
214
215
export type ExplicitExtensionDependency = {
216
+
ext?: string;
217
id: string;
218
};
219
···
236
id: number;
237
};
238
239
+
export type IdentifiedWebpackModule = ExtensionWebpackModule & ExplicitExtensionDependency;
+18
packages/types/src/fs.ts
+18
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
+
};
+67
-15
packages/types/src/globals.ts
+67
-15
packages/types/src/globals.ts
···
1
-
import { Logger } from "./logger";
2
-
import { Config, ConfigExtension } from "./config";
3
-
import {
4
-
DetectedExtension,
5
-
IdentifiedPatch,
6
-
IdentifiedWebpackModule,
7
-
ProcessedExtensions
8
-
} from "./extension";
9
-
import EventEmitter from "events";
10
11
export type MoonlightHost = {
12
-
asarPath: string;
13
config: Config;
14
-
events: EventEmitter;
15
extensions: DetectedExtension[];
16
processedExtensions: ProcessedExtensions;
17
18
getConfig: (ext: string) => ConfigExtension["config"];
19
getConfigOption: <T>(ext: string, name: string) => T | undefined;
20
getLogger: (id: string) => Logger;
21
};
22
23
export type MoonlightNode = {
···
25
extensions: DetectedExtension[];
26
processedExtensions: ProcessedExtensions;
27
nativesCache: Record<string, any>;
28
29
getConfig: (ext: string) => ConfigExtension["config"];
30
getConfigOption: <T>(ext: string, name: string) => T | undefined;
31
getNatives: (ext: string) => any | undefined;
32
getLogger: (id: string) => Logger;
33
34
-
getExtensionDir: (ext: string) => string;
35
-
writeConfig: (config: Config) => void;
36
};
37
38
export type MoonlightWeb = {
39
unpatched: Set<IdentifiedPatch>;
40
pendingModules: Set<IdentifiedWebpackModule>;
41
enabledExtensions: Set<string>;
42
43
-
getConfig: (ext: string) => ConfigExtension["config"];
44
-
getConfigOption: <T>(ext: string, name: string) => T | undefined;
45
getNatives: (ext: string) => any | undefined;
46
getLogger: (id: string) => Logger;
47
};
48
49
export enum MoonlightEnv {
···
51
NodePreload = "node-preload",
52
WebPreload = "web-preload"
53
}
···
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";
15
16
export type MoonlightHost = {
17
config: Config;
18
extensions: DetectedExtension[];
19
processedExtensions: ProcessedExtensions;
20
+
asarPath: string;
21
+
events: EventEmitter;
22
+
23
+
version: string;
24
+
branch: MoonlightBranch;
25
26
getConfig: (ext: string) => ConfigExtension["config"];
27
+
getConfigPath: () => Promise<string>;
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
+
32
getLogger: (id: string) => Logger;
33
+
getMoonlightDir: () => string;
34
+
getExtensionDir: (ext: string) => string;
35
};
36
37
export type MoonlightNode = {
···
39
extensions: DetectedExtension[];
40
processedExtensions: ProcessedExtensions;
41
nativesCache: Record<string, any>;
42
+
isBrowser: boolean;
43
+
events: MoonlightEventEmitter<NodeEventType, NodeEventPayloads>;
44
+
45
+
version: string;
46
+
branch: MoonlightBranch;
47
48
getConfig: (ext: string) => ConfigExtension["config"];
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
+
53
getNatives: (ext: string) => any | undefined;
54
getLogger: (id: string) => Logger;
55
+
getMoonlightDir: () => string;
56
+
getExtensionDir: (ext: string) => string;
57
+
};
58
59
+
export type MoonlightNodeSandboxed = {
60
+
fs: MoonlightFS;
61
+
addCors: (url: string) => void;
62
+
addBlocked: (url: string) => void;
63
};
64
65
export type MoonlightWeb = {
66
+
patched: Map<string, Set<string>>;
67
unpatched: Set<IdentifiedPatch>;
68
pendingModules: Set<IdentifiedWebpackModule>;
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;
77
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
+
88
getNatives: (ext: string) => any | undefined;
89
getLogger: (id: string) => Logger;
90
+
91
+
lunast: LunAST;
92
+
moonmap: Moonmap;
93
};
94
95
export enum MoonlightEnv {
···
97
NodePreload = "node-preload",
98
WebPreload = "web-preload"
99
}
100
+
101
+
export enum MoonlightBranch {
102
+
STABLE = "stable",
103
+
NIGHTLY = "nightly",
104
+
DEV = "dev"
105
+
}
+52
-27
packages/types/src/import.d.ts
+52
-27
packages/types/src/import.d.ts
···
1
-
declare module "@moonlight-mod/wp/spacepack_spacepack" {
2
import { CoreExtensions } from "@moonlight-mod/types";
3
-
export const spacepack: CoreExtensions.Spacepack;
4
-
export default spacepack;
5
}
6
7
-
declare module "@moonlight-mod/wp/common_components" {
8
import { CoreExtensions } from "@moonlight-mod/types";
9
-
const CommonComponent: CoreExtensions.CommonComponents;
10
-
export = CommonComponent;
11
}
12
13
-
declare module "@moonlight-mod/wp/common_flux" {
14
import { CoreExtensions } from "@moonlight-mod/types";
15
-
const Flux: CoreExtensions.CommonFlux;
16
-
// FIXME: This is wrong, the default export differs from the named exports.
17
-
export = Flux;
18
}
19
-
20
-
declare module "@moonlight-mod/wp/common_fluxDispatcher" {
21
import { CoreExtensions } from "@moonlight-mod/types";
22
-
const Dispatcher: CoreExtensions.CommonFluxDispatcher;
23
-
export default Dispatcher;
24
}
25
-
26
declare module "@moonlight-mod/wp/common_stores";
27
28
-
declare module "@moonlight-mod/wp/common_react" {
29
-
import React from "react";
30
-
export = React;
31
}
32
33
-
declare module "@moonlight-mod/wp/settings_settings" {
34
import { CoreExtensions } from "@moonlight-mod/types";
35
-
export const Settings: CoreExtensions.Settings;
36
-
export default Settings;
37
}
38
39
declare module "@moonlight-mod/wp/markdown_markdown" {
···
42
export = Markdown;
43
}
44
45
-
declare module "@moonlight-mod/wp/contextMenu_evilMenu" {
46
import { CoreExtensions } from "@moonlight-mod/types";
47
-
const EvilParser: CoreExtensions.ContextMenu.EvilItemParser;
48
-
export = EvilParser;
49
}
50
51
-
declare module "@moonlight-mod/wp/contextMenu_contextMenu" {
52
import { CoreExtensions } from "@moonlight-mod/types";
53
-
const ContextMenu: CoreExtensions.ContextMenu.ContextMenu;
54
-
export = ContextMenu;
55
}
···
1
+
declare module "@moonlight-mod/wp/appPanels_appPanels" {
2
import { CoreExtensions } from "@moonlight-mod/types";
3
+
const AppPanels: CoreExtensions.AppPanels.AppPanels;
4
+
export = AppPanels;
5
}
6
7
+
declare module "@moonlight-mod/wp/commands_commands" {
8
import { CoreExtensions } from "@moonlight-mod/types";
9
+
export const commands: CoreExtensions.Commands.Commands;
10
+
export default commands;
11
}
12
13
+
declare module "@moonlight-mod/wp/common_ErrorBoundary" {
14
import { CoreExtensions } from "@moonlight-mod/types";
15
+
const ErrorBoundary: CoreExtensions.Common.ErrorBoundary;
16
+
export = ErrorBoundary;
17
}
18
+
declare module "@moonlight-mod/wp/common_icons" {
19
import { CoreExtensions } from "@moonlight-mod/types";
20
+
export const icons: CoreExtensions.Common.Icons;
21
+
export default icons;
22
}
23
declare module "@moonlight-mod/wp/common_stores";
24
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;
39
}
40
41
+
declare module "@moonlight-mod/wp/contextMenu_evilMenu" {
42
import { CoreExtensions } from "@moonlight-mod/types";
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;
50
}
51
52
declare module "@moonlight-mod/wp/markdown_markdown" {
···
55
export = Markdown;
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
-7
packages/types/src/index.ts
+14
-7
packages/types/src/index.ts
···
1
/// <reference types="standalone-electron-types" />
2
/// <reference types="react" />
3
-
/// <reference types="flux" />
4
/// <reference types="./import" />
5
/* eslint-disable no-var */
6
7
-
import {
8
-
MoonlightEnv,
9
-
MoonlightHost,
10
-
MoonlightNode,
11
-
MoonlightWeb
12
-
} from "./globals";
13
14
export * from "./discord";
15
export * from "./config";
···
18
export * from "./globals";
19
export * from "./logger";
20
export * as constants from "./constants";
21
22
declare global {
23
const MOONLIGHT_ENV: MoonlightEnv;
···
25
const MOONLIGHT_INJECTOR: boolean;
26
const MOONLIGHT_NODE_PRELOAD: boolean;
27
const MOONLIGHT_WEB_PRELOAD: boolean;
28
29
var moonlightHost: MoonlightHost;
30
var moonlightNode: MoonlightNode;
31
var moonlight: MoonlightWeb;
32
}
···
1
/// <reference types="standalone-electron-types" />
2
/// <reference types="react" />
3
/// <reference types="./import" />
4
+
/// <reference types="./mappings" />
5
/* eslint-disable no-var */
6
7
+
import { MoonlightEnv, MoonlightHost, MoonlightNode, MoonlightNodeSandboxed, MoonlightWeb } from "./globals";
8
9
export * from "./discord";
10
export * from "./config";
···
13
export * from "./globals";
14
export * from "./logger";
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";
20
21
declare global {
22
const MOONLIGHT_ENV: MoonlightEnv;
···
24
const MOONLIGHT_INJECTOR: boolean;
25
const MOONLIGHT_NODE_PRELOAD: boolean;
26
const MOONLIGHT_WEB_PRELOAD: boolean;
27
+
const MOONLIGHT_BROWSER: boolean;
28
+
const MOONLIGHT_BRANCH: string;
29
+
const MOONLIGHT_VERSION: string;
30
31
var moonlightHost: MoonlightHost;
32
var moonlightNode: MoonlightNode;
33
+
var moonlightNodeSandboxed: MoonlightNodeSandboxed;
34
var moonlight: MoonlightWeb;
35
+
var _moonlight_coreExtensionsStr: string;
36
+
37
+
var _moonlightBrowserInit: undefined | (() => Promise<void>);
38
+
var _moonlightWebLoad: undefined | (() => Promise<void>);
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
-7
packages/types/tsconfig.json
+7
-7
packages/types/tsconfig.json
···
1
{
2
"compilerOptions": {
3
-
"target": "es2016",
4
-
"module": "es6",
5
-
"esModuleInterop": true,
6
-
"forceConsistentCasingInFileNames": true,
7
-
"strict": true,
8
-
"moduleResolution": "bundler",
9
"jsx": "react",
10
-
"declaration": true
11
},
12
"include": ["./src/**/*", "src/index.ts", "./src/import.d.ts"]
13
}
···
1
{
2
"compilerOptions": {
3
+
"target": "ES2016",
4
"jsx": "react",
5
+
"module": "ES6",
6
+
"moduleResolution": "bundler",
7
+
"strict": true,
8
+
"declaration": true,
9
+
"esModuleInterop": true,
10
+
"forceConsistentCasingInFileNames": true
11
},
12
"include": ["./src/**/*", "src/index.ts", "./src/import.d.ts"]
13
}
+13
-1
packages/web-preload/package.json
+13
-1
packages/web-preload/package.json
···
1
{
2
"name": "@moonlight-mod/web-preload",
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
+
},
12
"dependencies": {
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:*"
18
}
19
}
+40
-8
packages/web-preload/src/index.ts
+40
-8
packages/web-preload/src/index.ts
···
1
import { loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
2
-
import { installWebpackPatcher } from "@moonlight-mod/core/patch";
3
import { installStyles } from "@moonlight-mod/core/styles";
4
-
import Logger from "@moonlight-mod/core/util/logger";
5
6
-
(async () => {
7
const logger = new Logger("web-preload");
8
9
window.moonlight = {
10
unpatched: new Set(),
11
pendingModules: new Set(),
12
enabledExtensions: new Set(),
13
14
getConfig: moonlightNode.getConfig.bind(moonlightNode),
15
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
16
getNatives: moonlightNode.getNatives.bind(moonlightNode),
17
-
getLogger: (id: string) => {
18
return new Logger(id);
19
-
}
20
};
21
22
try {
23
await loadProcessedExtensions(moonlightNode.processedExtensions);
24
await installWebpackPatcher();
25
} catch (e) {
26
logger.error("Error setting up web-preload", e);
27
}
28
29
-
window.addEventListener("DOMContentLoaded", () => {
30
installStyles();
31
-
});
32
-
})();
···
1
import { loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
2
+
import { installWebpackPatcher, onModuleLoad, registerPatch, registerWebpackModule } from "@moonlight-mod/core/patch";
3
+
import { constants, MoonlightBranch } from "@moonlight-mod/types";
4
import { installStyles } from "@moonlight-mod/core/styles";
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";
11
12
+
async function load() {
13
+
delete window._moonlightWebLoad;
14
+
initLogger(moonlightNode.config);
15
const logger = new Logger("web-preload");
16
17
window.moonlight = {
18
+
patched: new Map(),
19
unpatched: new Set(),
20
pendingModules: new Set(),
21
enabledExtensions: new Set(),
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
+
35
getConfig: moonlightNode.getConfig.bind(moonlightNode),
36
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
37
+
setConfigOption: moonlightNode.setConfigOption.bind(moonlightNode),
38
+
writeConfig: moonlightNode.writeConfig.bind(moonlightNode),
39
+
40
getNatives: moonlightNode.getNatives.bind(moonlightNode),
41
+
getLogger(id) {
42
return new Logger(id);
43
+
},
44
+
45
+
lunast: new LunAST(),
46
+
moonmap: new Moonmap()
47
};
48
49
try {
50
+
loadMappings(window.moonlight.moonmap, window.moonlight.lunast);
51
await loadProcessedExtensions(moonlightNode.processedExtensions);
52
await installWebpackPatcher();
53
} catch (e) {
54
logger.error("Error setting up web-preload", e);
55
}
56
57
+
if (document.readyState === "complete") {
58
installStyles();
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
+1683
-1139
pnpm-lock.yaml
+1683
-1139
pnpm-lock.yaml
···
4
autoInstallPeers: true
5
excludeLinksFromLockfile: false
6
7
importers:
8
9
.:
10
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)
17
esbuild:
18
-
specifier: ^0.19.3
19
version: 0.19.3
20
esbuild-copy-static-files:
21
-
specifier: ^0.1.0
22
version: 0.1.0
23
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)
35
husky:
36
-
specifier: ^8.0.3
37
version: 8.0.3
38
prettier:
39
-
specifier: ^3.1.0
40
version: 3.1.0
41
typescript:
42
-
specifier: ^5.3.2
43
-
version: 5.3.2
44
45
packages/core:
46
dependencies:
···
50
51
packages/core-extensions:
52
dependencies:
53
-
'@electron/asar':
54
-
specifier: ^3.2.5
55
-
version: 3.2.5
56
'@moonlight-mod/types':
57
specifier: workspace:*
58
version: link:../types
59
60
packages/injector:
61
dependencies:
···
77
78
packages/types:
79
dependencies:
80
-
'@types/flux':
81
-
specifier: ^3.1.12
82
-
version: 3.1.12
83
'@types/react':
84
-
specifier: ^18.2.22
85
-
version: 18.2.22
86
csstype:
87
-
specifier: ^3.1.2
88
-
version: 3.1.2
89
standalone-electron-types:
90
specifier: ^1.0.0
91
version: 1.0.0
···
95
'@moonlight-mod/core':
96
specifier: workspace:*
97
version: link:../core
98
99
packages:
100
···
102
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
103
engines: {node: '>=0.10.0'}
104
105
-
'@electron/asar@3.2.5':
106
-
resolution: {integrity: sha512-Ypahc2ElTj9YOrFvUHuoXv5Z/V1nPA5enlhmQapc578m/HZBHKTbqhoL5JZQjje2+/6Ti5AHh7Gj1/haeJa63Q==}
107
-
engines: {node: '>=10.12.0'}
108
hasBin: true
109
110
'@esbuild/android-arm64@0.19.3':
···
239
cpu: [x64]
240
os: [win32]
241
242
-
'@eslint-community/eslint-utils@4.4.0':
243
-
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
244
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
245
peerDependencies:
246
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
247
248
-
'@eslint-community/regexpp@4.10.0':
249
-
resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
250
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
251
252
-
'@eslint/eslintrc@2.1.4':
253
-
resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
254
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
255
256
-
'@eslint/js@8.55.0':
257
-
resolution: {integrity: sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==}
258
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
259
260
-
'@humanwhocodes/config-array@0.11.13':
261
-
resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
262
-
engines: {node: '>=10.10.0'}
263
264
'@humanwhocodes/module-importer@1.0.1':
265
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
266
engines: {node: '>=12.22'}
267
268
-
'@humanwhocodes/object-schema@2.0.1':
269
-
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
270
271
'@nodelib/fs.scandir@2.1.5':
272
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
···
280
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
281
engines: {node: '>= 8'}
282
283
-
'@pkgr/utils@2.4.2':
284
-
resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==}
285
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
286
287
-
'@types/fbemitter@2.0.33':
288
-
resolution: {integrity: sha512-KcSilwdl0D8YgXGL6l9d+rTBm2W7pDyTZrDEw0+IzqQ724676KJtMeO+xHodJewKFWZT+GFWaJubA5mpMxSkcg==}
289
290
-
'@types/flux@3.1.12':
291
-
resolution: {integrity: sha512-HZ8o/DTVNgcgnXoDyn0ZnjqEZMT4Chr4w5ktMQSbQAnqVDklasmRqNGd2agZDsk5i0jYHQLgQQuM782bWG7fUA==}
292
293
'@types/json-schema@7.0.15':
294
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
295
296
'@types/node@18.17.17':
297
resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==}
298
299
-
'@types/prop-types@15.7.6':
300
-
resolution: {integrity: sha512-RK/kBbYOQQHLYj9Z95eh7S6t7gq4Ojt/NT8HTk8bWVhA5DaF+5SMnxHKkP4gPNN3wAZkKP+VjAf0ebtYzf+fxg==}
301
302
-
'@types/react@18.2.22':
303
-
resolution: {integrity: sha512-60fLTOLqzarLED2O3UQImc/lsNRgG0jE/a1mPW9KjMemY0LMITWEsbS4VvZ4p6rorEHd5YKxxmMKSDK505GHpA==}
304
305
-
'@types/scheduler@0.16.3':
306
-
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
307
308
-
'@types/semver@7.5.6':
309
-
resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==}
310
311
-
'@typescript-eslint/eslint-plugin@6.13.2':
312
-
resolution: {integrity: sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==}
313
-
engines: {node: ^16.0.0 || >=18.0.0}
314
peerDependencies:
315
-
'@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
316
-
eslint: ^7.0.0 || ^8.0.0
317
-
typescript: '*'
318
-
peerDependenciesMeta:
319
-
typescript:
320
-
optional: true
321
322
-
'@typescript-eslint/parser@6.13.2':
323
-
resolution: {integrity: sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==}
324
-
engines: {node: ^16.0.0 || >=18.0.0}
325
peerDependencies:
326
-
eslint: ^7.0.0 || ^8.0.0
327
-
typescript: '*'
328
-
peerDependenciesMeta:
329
-
typescript:
330
-
optional: true
331
332
-
'@typescript-eslint/scope-manager@6.13.2':
333
-
resolution: {integrity: sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==}
334
-
engines: {node: ^16.0.0 || >=18.0.0}
335
336
-
'@typescript-eslint/type-utils@6.13.2':
337
-
resolution: {integrity: sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==}
338
-
engines: {node: ^16.0.0 || >=18.0.0}
339
peerDependencies:
340
-
eslint: ^7.0.0 || ^8.0.0
341
-
typescript: '*'
342
-
peerDependenciesMeta:
343
-
typescript:
344
-
optional: true
345
346
-
'@typescript-eslint/types@6.13.2':
347
-
resolution: {integrity: sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==}
348
-
engines: {node: ^16.0.0 || >=18.0.0}
349
350
-
'@typescript-eslint/typescript-estree@6.13.2':
351
-
resolution: {integrity: sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==}
352
-
engines: {node: ^16.0.0 || >=18.0.0}
353
peerDependencies:
354
-
typescript: '*'
355
-
peerDependenciesMeta:
356
-
typescript:
357
-
optional: true
358
359
-
'@typescript-eslint/utils@6.13.2':
360
-
resolution: {integrity: sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==}
361
-
engines: {node: ^16.0.0 || >=18.0.0}
362
peerDependencies:
363
-
eslint: ^7.0.0 || ^8.0.0
364
365
-
'@typescript-eslint/visitor-keys@6.13.2':
366
-
resolution: {integrity: sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==}
367
-
engines: {node: ^16.0.0 || >=18.0.0}
368
369
-
'@ungap/structured-clone@1.2.0':
370
-
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
371
372
acorn-jsx@5.3.2:
373
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
374
peerDependencies:
375
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
376
377
-
acorn@8.11.2:
378
-
resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
379
engines: {node: '>=0.4.0'}
380
hasBin: true
381
382
ajv@6.12.6:
383
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
384
385
-
ansi-regex@5.0.1:
386
-
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
387
-
engines: {node: '>=8'}
388
-
389
ansi-styles@4.3.0:
390
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
391
engines: {node: '>=8'}
392
393
argparse@2.0.1:
394
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
395
396
-
array-buffer-byte-length@1.0.0:
397
-
resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
398
399
-
array-includes@3.1.7:
400
-
resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
401
engines: {node: '>= 0.4'}
402
403
-
array-union@2.1.0:
404
-
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
405
-
engines: {node: '>=8'}
406
407
-
array.prototype.flat@1.3.2:
408
-
resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
409
engines: {node: '>= 0.4'}
410
411
-
array.prototype.flatmap@1.3.2:
412
-
resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
413
engines: {node: '>= 0.4'}
414
415
-
array.prototype.tosorted@1.1.2:
416
-
resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==}
417
418
-
arraybuffer.prototype.slice@1.0.2:
419
-
resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==}
420
engines: {node: '>= 0.4'}
421
422
-
asynciterator.prototype@1.0.0:
423
-
resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==}
424
425
-
available-typed-arrays@1.0.5:
426
-
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
427
engines: {node: '>= 0.4'}
428
429
balanced-match@1.0.2:
430
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
431
432
-
big-integer@1.6.52:
433
-
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
434
-
engines: {node: '>=0.6'}
435
-
436
-
bplist-parser@0.2.0:
437
-
resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==}
438
-
engines: {node: '>= 5.10.0'}
439
440
brace-expansion@1.1.11:
441
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
442
443
-
braces@3.0.2:
444
-
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
445
engines: {node: '>=8'}
446
447
-
bundle-name@3.0.0:
448
-
resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==}
449
-
engines: {node: '>=12'}
450
451
-
call-bind@1.0.5:
452
-
resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
453
454
callsites@3.1.0:
455
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
···
466
color-name@1.1.4:
467
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
468
469
-
commander@5.1.0:
470
-
resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==}
471
-
engines: {node: '>= 6'}
472
-
473
concat-map@0.0.1:
474
-
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
475
476
-
cross-spawn@7.0.3:
477
-
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
478
engines: {node: '>= 8'}
479
480
-
csstype@3.1.2:
481
-
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
482
483
-
debug@4.3.4:
484
-
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
485
engines: {node: '>=6.0'}
486
peerDependencies:
487
supports-color: '*'
···
492
deep-is@0.1.4:
493
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
494
495
-
default-browser-id@3.0.0:
496
-
resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==}
497
-
engines: {node: '>=12'}
498
-
499
-
default-browser@4.0.0:
500
-
resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==}
501
-
engines: {node: '>=14.16'}
502
-
503
-
define-data-property@1.1.1:
504
-
resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
505
engines: {node: '>= 0.4'}
506
-
507
-
define-lazy-prop@3.0.0:
508
-
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
509
-
engines: {node: '>=12'}
510
511
define-properties@1.2.1:
512
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
513
engines: {node: '>= 0.4'}
514
515
-
dir-glob@3.0.1:
516
-
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
517
-
engines: {node: '>=8'}
518
519
doctrine@2.1.0:
520
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
521
engines: {node: '>=0.10.0'}
522
523
-
doctrine@3.0.0:
524
-
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
525
-
engines: {node: '>=6.0.0'}
526
527
-
es-abstract@1.22.3:
528
-
resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==}
529
engines: {node: '>= 0.4'}
530
531
-
es-iterator-helpers@1.0.15:
532
-
resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==}
533
534
-
es-set-tostringtag@2.0.2:
535
-
resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==}
536
engines: {node: '>= 0.4'}
537
538
-
es-shim-unscopables@1.0.2:
539
-
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
540
541
-
es-to-primitive@1.2.1:
542
-
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
543
engines: {node: '>= 0.4'}
544
545
esbuild-copy-static-files@0.1.0:
···
560
peerDependencies:
561
eslint: '>=7.0.0'
562
563
-
eslint-plugin-prettier@5.0.1:
564
-
resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==}
565
engines: {node: ^14.18.0 || >=16.0.0}
566
peerDependencies:
567
'@types/eslint': '>=8.0.0'
568
eslint: '>=8.0.0'
569
-
eslint-config-prettier: '*'
570
prettier: '>=3.0.0'
571
peerDependenciesMeta:
572
'@types/eslint':
···
574
eslint-config-prettier:
575
optional: true
576
577
-
eslint-plugin-react@7.33.2:
578
-
resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==}
579
engines: {node: '>=4'}
580
peerDependencies:
581
-
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
582
583
-
eslint-scope@7.2.2:
584
-
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
585
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
586
587
eslint-visitor-keys@3.4.3:
588
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
589
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
590
591
-
eslint@8.55.0:
592
-
resolution: {integrity: sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==}
593
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
594
hasBin: true
595
596
-
espree@9.6.1:
597
-
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
598
-
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
599
600
-
esquery@1.5.0:
601
-
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
602
engines: {node: '>=0.10'}
603
604
esrecurse@4.3.0:
···
608
estraverse@5.3.0:
609
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
610
engines: {node: '>=4.0'}
611
612
esutils@2.0.3:
613
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
614
engines: {node: '>=0.10.0'}
615
616
-
execa@5.1.1:
617
-
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
618
-
engines: {node: '>=10'}
619
620
-
execa@7.2.0:
621
-
resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==}
622
-
engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0}
623
624
fast-deep-equal@3.1.3:
625
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
···
637
fast-levenshtein@2.0.6:
638
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
639
640
-
fastq@1.15.0:
641
-
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
642
643
-
file-entry-cache@6.0.1:
644
-
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
645
-
engines: {node: ^10.12.0 || >=12.0.0}
646
647
-
fill-range@7.0.1:
648
-
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
649
engines: {node: '>=8'}
650
651
find-up@5.0.0:
652
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
653
engines: {node: '>=10'}
654
655
-
flat-cache@3.2.0:
656
-
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
657
-
engines: {node: ^10.12.0 || >=12.0.0}
658
659
flatted@3.2.9:
660
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
661
662
-
for-each@0.3.3:
663
-
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
664
-
665
-
fs.realpath@1.0.0:
666
-
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
667
668
function-bind@1.1.2:
669
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
670
671
-
function.prototype.name@1.1.6:
672
-
resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
673
engines: {node: '>= 0.4'}
674
675
functions-have-names@1.2.3:
676
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
677
678
-
get-intrinsic@1.2.2:
679
-
resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
680
681
-
get-stream@6.0.1:
682
-
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
683
-
engines: {node: '>=10'}
684
685
-
get-symbol-description@1.0.0:
686
-
resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
687
engines: {node: '>= 0.4'}
688
689
glob-parent@5.1.2:
···
694
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
695
engines: {node: '>=10.13.0'}
696
697
-
glob@7.2.3:
698
-
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
699
700
-
globals@13.23.0:
701
-
resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==}
702
-
engines: {node: '>=8'}
703
-
704
-
globalthis@1.0.3:
705
-
resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
706
engines: {node: '>= 0.4'}
707
708
-
globby@11.1.0:
709
-
resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
710
-
engines: {node: '>=10'}
711
-
712
-
gopd@1.0.1:
713
-
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
714
715
graphemer@1.4.0:
716
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
717
718
-
has-bigints@1.0.2:
719
-
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
720
721
has-flag@4.0.0:
722
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
723
engines: {node: '>=8'}
724
725
-
has-property-descriptors@1.0.1:
726
-
resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==}
727
728
-
has-proto@1.0.1:
729
-
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
730
engines: {node: '>= 0.4'}
731
732
-
has-symbols@1.0.3:
733
-
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
734
engines: {node: '>= 0.4'}
735
736
-
has-tostringtag@1.0.0:
737
-
resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
738
engines: {node: '>= 0.4'}
739
740
-
hasown@2.0.0:
741
-
resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
742
engines: {node: '>= 0.4'}
743
744
-
human-signals@2.1.0:
745
-
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
746
-
engines: {node: '>=10.17.0'}
747
-
748
-
human-signals@4.3.1:
749
-
resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==}
750
-
engines: {node: '>=14.18.0'}
751
-
752
husky@8.0.3:
753
resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==}
754
engines: {node: '>=14'}
755
hasBin: true
756
757
-
ignore@5.3.0:
758
-
resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==}
759
engines: {node: '>= 4'}
760
761
import-fresh@3.3.0:
···
766
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
767
engines: {node: '>=0.8.19'}
768
769
-
inflight@1.0.6:
770
-
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
771
772
-
inherits@2.0.4:
773
-
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
774
-
775
-
internal-slot@1.0.6:
776
-
resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==}
777
engines: {node: '>= 0.4'}
778
779
-
is-array-buffer@3.0.2:
780
-
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
781
-
782
-
is-async-function@2.0.0:
783
-
resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==}
784
engines: {node: '>= 0.4'}
785
786
-
is-bigint@1.0.4:
787
-
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
788
789
-
is-boolean-object@1.1.2:
790
-
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
791
engines: {node: '>= 0.4'}
792
793
is-callable@1.2.7:
794
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
795
engines: {node: '>= 0.4'}
796
797
-
is-core-module@2.13.1:
798
-
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
799
-
800
-
is-date-object@1.0.5:
801
-
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
802
engines: {node: '>= 0.4'}
803
804
-
is-docker@2.2.1:
805
-
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
806
-
engines: {node: '>=8'}
807
-
hasBin: true
808
809
-
is-docker@3.0.0:
810
-
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
811
-
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
812
-
hasBin: true
813
814
is-extglob@2.1.1:
815
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
816
engines: {node: '>=0.10.0'}
817
818
-
is-finalizationregistry@1.0.2:
819
-
resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==}
820
821
-
is-generator-function@1.0.10:
822
-
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
823
engines: {node: '>= 0.4'}
824
825
is-glob@4.0.3:
826
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
827
engines: {node: '>=0.10.0'}
828
829
-
is-inside-container@1.0.0:
830
-
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
831
-
engines: {node: '>=14.16'}
832
-
hasBin: true
833
-
834
-
is-map@2.0.2:
835
-
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
836
-
837
-
is-negative-zero@2.0.2:
838
-
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
839
engines: {node: '>= 0.4'}
840
841
-
is-number-object@1.0.7:
842
-
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
843
engines: {node: '>= 0.4'}
844
845
is-number@7.0.0:
846
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
847
engines: {node: '>=0.12.0'}
848
849
-
is-path-inside@3.0.3:
850
-
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
851
-
engines: {node: '>=8'}
852
853
-
is-regex@1.1.4:
854
-
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
855
engines: {node: '>= 0.4'}
856
857
-
is-set@2.0.2:
858
-
resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
859
860
-
is-shared-array-buffer@1.0.2:
861
-
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
862
863
-
is-stream@2.0.1:
864
-
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
865
-
engines: {node: '>=8'}
866
-
867
-
is-stream@3.0.0:
868
-
resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
869
-
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
870
-
871
-
is-string@1.0.7:
872
-
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
873
engines: {node: '>= 0.4'}
874
875
-
is-symbol@1.0.4:
876
-
resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
877
engines: {node: '>= 0.4'}
878
879
-
is-typed-array@1.1.12:
880
-
resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==}
881
engines: {node: '>= 0.4'}
882
883
-
is-weakmap@2.0.1:
884
-
resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
885
886
-
is-weakref@1.0.2:
887
-
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
888
-
889
-
is-weakset@2.0.2:
890
-
resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
891
-
892
-
is-wsl@2.2.0:
893
-
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
894
-
engines: {node: '>=8'}
895
896
isarray@2.0.5:
897
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
···
899
isexe@2.0.0:
900
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
901
902
-
iterator.prototype@1.1.2:
903
-
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
904
905
js-tokens@4.0.0:
906
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
···
940
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
941
hasBin: true
942
943
-
lru-cache@6.0.0:
944
-
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
945
-
engines: {node: '>=10'}
946
-
947
-
merge-stream@2.0.0:
948
-
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
949
950
merge2@1.4.1:
951
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
952
engines: {node: '>= 8'}
953
954
-
micromatch@4.0.5:
955
-
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
956
engines: {node: '>=8.6'}
957
958
-
mimic-fn@2.1.0:
959
-
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
960
-
engines: {node: '>=6'}
961
-
962
-
mimic-fn@4.0.0:
963
-
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
964
-
engines: {node: '>=12'}
965
966
minimatch@3.1.2:
967
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
968
969
-
ms@2.1.2:
970
-
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
971
972
natural-compare@1.4.0:
973
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
974
975
-
npm-run-path@4.0.1:
976
-
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
977
-
engines: {node: '>=8'}
978
-
979
-
npm-run-path@5.1.0:
980
-
resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
981
-
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
982
983
object-assign@4.1.1:
984
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
985
engines: {node: '>=0.10.0'}
986
987
-
object-inspect@1.13.1:
988
-
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
989
990
object-keys@1.1.1:
991
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
992
engines: {node: '>= 0.4'}
993
994
-
object.assign@4.1.5:
995
-
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
996
engines: {node: '>= 0.4'}
997
998
-
object.entries@1.1.7:
999
-
resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==}
1000
engines: {node: '>= 0.4'}
1001
1002
-
object.fromentries@2.0.7:
1003
-
resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==}
1004
engines: {node: '>= 0.4'}
1005
1006
-
object.hasown@1.1.3:
1007
-
resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==}
1008
-
1009
-
object.values@1.1.7:
1010
-
resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==}
1011
engines: {node: '>= 0.4'}
1012
1013
-
once@1.4.0:
1014
-
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
1015
-
1016
-
onetime@5.1.2:
1017
-
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
1018
-
engines: {node: '>=6'}
1019
-
1020
-
onetime@6.0.0:
1021
-
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
1022
-
engines: {node: '>=12'}
1023
1024
-
open@9.1.0:
1025
-
resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==}
1026
-
engines: {node: '>=14.16'}
1027
1028
optionator@0.9.3:
1029
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
1030
engines: {node: '>= 0.8.0'}
1031
1032
p-limit@3.1.0:
1033
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
···
1037
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
1038
engines: {node: '>=10'}
1039
1040
parent-module@1.0.1:
1041
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
1042
engines: {node: '>=6'}
···
1045
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
1046
engines: {node: '>=8'}
1047
1048
-
path-is-absolute@1.0.1:
1049
-
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
1050
-
engines: {node: '>=0.10.0'}
1051
-
1052
path-key@3.1.1:
1053
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1054
engines: {node: '>=8'}
1055
-
1056
-
path-key@4.0.0:
1057
-
resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
1058
-
engines: {node: '>=12'}
1059
1060
path-parse@1.0.7:
1061
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1062
1063
-
path-type@4.0.0:
1064
-
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
1065
-
engines: {node: '>=8'}
1066
-
1067
-
picocolors@1.0.0:
1068
-
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
1069
1070
picomatch@2.3.1:
1071
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1072
engines: {node: '>=8.6'}
1073
1074
prelude-ls@1.2.1:
1075
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
1076
engines: {node: '>= 0.8.0'}
···
1084
engines: {node: '>=14'}
1085
hasBin: true
1086
1087
prop-types@15.8.1:
1088
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
1089
···
1091
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
1092
engines: {node: '>=6'}
1093
1094
queue-microtask@1.2.3:
1095
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1096
1097
react-is@16.13.1:
1098
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
1099
1100
-
reflect.getprototypeof@1.0.4:
1101
-
resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==}
1102
engines: {node: '>= 0.4'}
1103
1104
-
regexp.prototype.flags@1.5.1:
1105
-
resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==}
1106
engines: {node: '>= 0.4'}
1107
1108
resolve-from@4.0.0:
···
1113
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
1114
hasBin: true
1115
1116
reusify@1.0.4:
1117
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
1118
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
1119
1120
-
rimraf@3.0.2:
1121
-
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
1122
-
hasBin: true
1123
-
1124
-
run-applescript@5.0.0:
1125
-
resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==}
1126
-
engines: {node: '>=12'}
1127
-
1128
run-parallel@1.2.0:
1129
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
1130
1131
-
safe-array-concat@1.0.1:
1132
-
resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==}
1133
engines: {node: '>=0.4'}
1134
1135
-
safe-regex-test@1.0.0:
1136
-
resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==}
1137
1138
semver@6.3.1:
1139
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1140
hasBin: true
1141
1142
-
semver@7.5.4:
1143
-
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
1144
engines: {node: '>=10'}
1145
hasBin: true
1146
1147
-
set-function-length@1.1.1:
1148
-
resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==}
1149
engines: {node: '>= 0.4'}
1150
1151
-
set-function-name@2.0.1:
1152
-
resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
1153
engines: {node: '>= 0.4'}
1154
1155
shebang-command@2.0.0:
···
1160
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
1161
engines: {node: '>=8'}
1162
1163
-
side-channel@1.0.4:
1164
-
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
1165
1166
-
signal-exit@3.0.7:
1167
-
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
1168
1169
-
slash@3.0.0:
1170
-
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
1171
-
engines: {node: '>=8'}
1172
1173
standalone-electron-types@1.0.0:
1174
resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==}
1175
1176
-
string.prototype.matchall@4.0.10:
1177
-
resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==}
1178
-
1179
-
string.prototype.trim@1.2.8:
1180
-
resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
1181
engines: {node: '>= 0.4'}
1182
1183
-
string.prototype.trimend@1.0.7:
1184
-
resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==}
1185
1186
-
string.prototype.trimstart@1.0.7:
1187
-
resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==}
1188
1189
-
strip-ansi@6.0.1:
1190
-
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
1191
-
engines: {node: '>=8'}
1192
1193
-
strip-final-newline@2.0.0:
1194
-
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
1195
-
engines: {node: '>=6'}
1196
1197
-
strip-final-newline@3.0.0:
1198
-
resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
1199
-
engines: {node: '>=12'}
1200
1201
strip-json-comments@3.1.1:
1202
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
···
1210
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
1211
engines: {node: '>= 0.4'}
1212
1213
-
synckit@0.8.6:
1214
-
resolution: {integrity: sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==}
1215
engines: {node: ^14.18.0 || >=16.0.0}
1216
1217
-
text-table@0.2.0:
1218
-
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
1219
1220
-
titleize@3.0.0:
1221
-
resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==}
1222
-
engines: {node: '>=12'}
1223
1224
to-regex-range@5.0.1:
1225
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
1226
engines: {node: '>=8.0'}
1227
1228
-
ts-api-utils@1.0.3:
1229
-
resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==}
1230
-
engines: {node: '>=16.13.0'}
1231
peerDependencies:
1232
-
typescript: '>=4.2.0'
1233
1234
-
tslib@2.6.2:
1235
-
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
1236
1237
type-check@0.4.0:
1238
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
1239
engines: {node: '>= 0.8.0'}
1240
1241
-
type-fest@0.20.2:
1242
-
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
1243
-
engines: {node: '>=10'}
1244
1245
-
typed-array-buffer@1.0.0:
1246
-
resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
1247
engines: {node: '>= 0.4'}
1248
1249
-
typed-array-byte-length@1.0.0:
1250
-
resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==}
1251
engines: {node: '>= 0.4'}
1252
1253
-
typed-array-byte-offset@1.0.0:
1254
-
resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==}
1255
engines: {node: '>= 0.4'}
1256
1257
-
typed-array-length@1.0.4:
1258
-
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
1259
1260
-
typescript@5.3.2:
1261
-
resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==}
1262
engines: {node: '>=14.17'}
1263
hasBin: true
1264
1265
-
unbox-primitive@1.0.2:
1266
-
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
1267
1268
-
untildify@4.0.0:
1269
-
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
1270
-
engines: {node: '>=8'}
1271
1272
uri-js@4.4.1:
1273
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
1274
1275
-
which-boxed-primitive@1.0.2:
1276
-
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
1277
1278
-
which-builtin-type@1.1.3:
1279
-
resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==}
1280
engines: {node: '>= 0.4'}
1281
1282
-
which-collection@1.0.1:
1283
-
resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
1284
1285
-
which-typed-array@1.1.13:
1286
-
resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==}
1287
engines: {node: '>= 0.4'}
1288
1289
which@2.0.2:
···
1291
engines: {node: '>= 8'}
1292
hasBin: true
1293
1294
-
wrappy@1.0.2:
1295
-
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
1296
-
1297
-
yallist@4.0.0:
1298
-
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
1299
1300
yocto-queue@0.1.0:
1301
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
1302
engines: {node: '>=10'}
1303
1304
snapshots:
1305
1306
'@aashutoshrathi/word-wrap@1.2.6': {}
1307
1308
-
'@electron/asar@3.2.5':
1309
dependencies:
1310
-
commander: 5.1.0
1311
-
glob: 7.2.3
1312
-
minimatch: 3.1.2
1313
1314
'@esbuild/android-arm64@0.19.3':
1315
optional: true
···
1377
'@esbuild/win32-x64@0.19.3':
1378
optional: true
1379
1380
-
'@eslint-community/eslint-utils@4.4.0(eslint@8.55.0)':
1381
dependencies:
1382
-
eslint: 8.55.0
1383
eslint-visitor-keys: 3.4.3
1384
1385
-
'@eslint-community/regexpp@4.10.0': {}
1386
1387
-
'@eslint/eslintrc@2.1.4':
1388
dependencies:
1389
ajv: 6.12.6
1390
-
debug: 4.3.4
1391
-
espree: 9.6.1
1392
-
globals: 13.23.0
1393
-
ignore: 5.3.0
1394
import-fresh: 3.3.0
1395
js-yaml: 4.1.0
1396
minimatch: 3.1.2
···
1398
transitivePeerDependencies:
1399
- supports-color
1400
1401
-
'@eslint/js@8.55.0': {}
1402
1403
-
'@humanwhocodes/config-array@0.11.13':
1404
dependencies:
1405
-
'@humanwhocodes/object-schema': 2.0.1
1406
-
debug: 4.3.4
1407
-
minimatch: 3.1.2
1408
transitivePeerDependencies:
1409
- supports-color
1410
1411
-
'@humanwhocodes/module-importer@1.0.1': {}
1412
1413
-
'@humanwhocodes/object-schema@2.0.1': {}
1414
1415
'@nodelib/fs.scandir@2.1.5':
1416
dependencies:
···
1422
'@nodelib/fs.walk@1.2.8':
1423
dependencies:
1424
'@nodelib/fs.scandir': 2.1.5
1425
-
fastq: 1.15.0
1426
1427
-
'@pkgr/utils@2.4.2':
1428
dependencies:
1429
-
cross-spawn: 7.0.3
1430
-
fast-glob: 3.3.2
1431
-
is-glob: 4.0.3
1432
-
open: 9.1.0
1433
-
picocolors: 1.0.0
1434
-
tslib: 2.6.2
1435
1436
-
'@types/fbemitter@2.0.33': {}
1437
1438
-
'@types/flux@3.1.12':
1439
dependencies:
1440
-
'@types/fbemitter': 2.0.33
1441
-
'@types/react': 18.2.22
1442
1443
'@types/json-schema@7.0.15': {}
1444
1445
'@types/node@18.17.17': {}
1446
1447
-
'@types/prop-types@15.7.6': {}
1448
1449
-
'@types/react@18.2.22':
1450
dependencies:
1451
-
'@types/prop-types': 15.7.6
1452
-
'@types/scheduler': 0.16.3
1453
-
csstype: 3.1.2
1454
1455
-
'@types/scheduler@0.16.3': {}
1456
1457
-
'@types/semver@7.5.6': {}
1458
1459
-
'@typescript-eslint/eslint-plugin@6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.2)':
1460
dependencies:
1461
-
'@eslint-community/regexpp': 4.10.0
1462
-
'@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
1463
-
'@typescript-eslint/scope-manager': 6.13.2
1464
-
'@typescript-eslint/type-utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
1465
-
'@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
1466
-
'@typescript-eslint/visitor-keys': 6.13.2
1467
-
debug: 4.3.4
1468
-
eslint: 8.55.0
1469
graphemer: 1.4.0
1470
-
ignore: 5.3.0
1471
natural-compare: 1.4.0
1472
-
semver: 7.5.4
1473
-
ts-api-utils: 1.0.3(typescript@5.3.2)
1474
-
typescript: 5.3.2
1475
transitivePeerDependencies:
1476
- supports-color
1477
1478
-
'@typescript-eslint/parser@6.13.2(eslint@8.55.0)(typescript@5.3.2)':
1479
dependencies:
1480
-
'@typescript-eslint/scope-manager': 6.13.2
1481
-
'@typescript-eslint/types': 6.13.2
1482
-
'@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2)
1483
-
'@typescript-eslint/visitor-keys': 6.13.2
1484
-
debug: 4.3.4
1485
-
eslint: 8.55.0
1486
-
typescript: 5.3.2
1487
transitivePeerDependencies:
1488
- supports-color
1489
1490
-
'@typescript-eslint/scope-manager@6.13.2':
1491
dependencies:
1492
-
'@typescript-eslint/types': 6.13.2
1493
-
'@typescript-eslint/visitor-keys': 6.13.2
1494
1495
-
'@typescript-eslint/type-utils@6.13.2(eslint@8.55.0)(typescript@5.3.2)':
1496
dependencies:
1497
-
'@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2)
1498
-
'@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.2)
1499
-
debug: 4.3.4
1500
-
eslint: 8.55.0
1501
-
ts-api-utils: 1.0.3(typescript@5.3.2)
1502
-
typescript: 5.3.2
1503
transitivePeerDependencies:
1504
- supports-color
1505
1506
-
'@typescript-eslint/types@6.13.2': {}
1507
1508
-
'@typescript-eslint/typescript-estree@6.13.2(typescript@5.3.2)':
1509
dependencies:
1510
-
'@typescript-eslint/types': 6.13.2
1511
-
'@typescript-eslint/visitor-keys': 6.13.2
1512
-
debug: 4.3.4
1513
-
globby: 11.1.0
1514
is-glob: 4.0.3
1515
-
semver: 7.5.4
1516
-
ts-api-utils: 1.0.3(typescript@5.3.2)
1517
-
typescript: 5.3.2
1518
transitivePeerDependencies:
1519
- supports-color
1520
1521
-
'@typescript-eslint/utils@6.13.2(eslint@8.55.0)(typescript@5.3.2)':
1522
dependencies:
1523
-
'@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
1524
-
'@types/json-schema': 7.0.15
1525
-
'@types/semver': 7.5.6
1526
-
'@typescript-eslint/scope-manager': 6.13.2
1527
-
'@typescript-eslint/types': 6.13.2
1528
-
'@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.2)
1529
-
eslint: 8.55.0
1530
-
semver: 7.5.4
1531
transitivePeerDependencies:
1532
- supports-color
1533
-
- typescript
1534
1535
-
'@typescript-eslint/visitor-keys@6.13.2':
1536
dependencies:
1537
-
'@typescript-eslint/types': 6.13.2
1538
-
eslint-visitor-keys: 3.4.3
1539
1540
-
'@ungap/structured-clone@1.2.0': {}
1541
1542
-
acorn-jsx@5.3.2(acorn@8.11.2):
1543
dependencies:
1544
-
acorn: 8.11.2
1545
1546
-
acorn@8.11.2: {}
1547
1548
ajv@6.12.6:
1549
dependencies:
···
1552
json-schema-traverse: 0.4.1
1553
uri-js: 4.4.1
1554
1555
-
ansi-regex@5.0.1: {}
1556
-
1557
ansi-styles@4.3.0:
1558
dependencies:
1559
color-convert: 2.0.1
1560
1561
argparse@2.0.1: {}
1562
1563
-
array-buffer-byte-length@1.0.0:
1564
dependencies:
1565
-
call-bind: 1.0.5
1566
-
is-array-buffer: 3.0.2
1567
1568
-
array-includes@3.1.7:
1569
dependencies:
1570
-
call-bind: 1.0.5
1571
define-properties: 1.2.1
1572
-
es-abstract: 1.22.3
1573
-
get-intrinsic: 1.2.2
1574
-
is-string: 1.0.7
1575
-
1576
-
array-union@2.1.0: {}
1577
1578
-
array.prototype.flat@1.3.2:
1579
dependencies:
1580
-
call-bind: 1.0.5
1581
define-properties: 1.2.1
1582
-
es-abstract: 1.22.3
1583
-
es-shim-unscopables: 1.0.2
1584
1585
-
array.prototype.flatmap@1.3.2:
1586
dependencies:
1587
-
call-bind: 1.0.5
1588
define-properties: 1.2.1
1589
-
es-abstract: 1.22.3
1590
-
es-shim-unscopables: 1.0.2
1591
1592
-
array.prototype.tosorted@1.1.2:
1593
dependencies:
1594
-
call-bind: 1.0.5
1595
define-properties: 1.2.1
1596
-
es-abstract: 1.22.3
1597
-
es-shim-unscopables: 1.0.2
1598
-
get-intrinsic: 1.2.2
1599
1600
-
arraybuffer.prototype.slice@1.0.2:
1601
dependencies:
1602
-
array-buffer-byte-length: 1.0.0
1603
-
call-bind: 1.0.5
1604
define-properties: 1.2.1
1605
-
es-abstract: 1.22.3
1606
-
get-intrinsic: 1.2.2
1607
-
is-array-buffer: 3.0.2
1608
-
is-shared-array-buffer: 1.0.2
1609
1610
-
asynciterator.prototype@1.0.0:
1611
dependencies:
1612
-
has-symbols: 1.0.3
1613
-
1614
-
available-typed-arrays@1.0.5: {}
1615
1616
-
balanced-match@1.0.2: {}
1617
1618
-
big-integer@1.6.52: {}
1619
1620
-
bplist-parser@0.2.0:
1621
dependencies:
1622
-
big-integer: 1.6.52
1623
1624
brace-expansion@1.1.11:
1625
dependencies:
1626
balanced-match: 1.0.2
1627
concat-map: 0.0.1
1628
1629
-
braces@3.0.2:
1630
dependencies:
1631
-
fill-range: 7.0.1
1632
1633
-
bundle-name@3.0.0:
1634
dependencies:
1635
-
run-applescript: 5.0.0
1636
1637
-
call-bind@1.0.5:
1638
dependencies:
1639
function-bind: 1.1.2
1640
-
get-intrinsic: 1.2.2
1641
-
set-function-length: 1.1.1
1642
1643
callsites@3.1.0: {}
1644
···
1653
1654
color-name@1.1.4: {}
1655
1656
-
commander@5.1.0: {}
1657
-
1658
concat-map@0.0.1: {}
1659
1660
-
cross-spawn@7.0.3:
1661
dependencies:
1662
path-key: 3.1.1
1663
shebang-command: 2.0.0
1664
which: 2.0.2
1665
1666
-
csstype@3.1.2: {}
1667
1668
-
debug@4.3.4:
1669
dependencies:
1670
-
ms: 2.1.2
1671
1672
-
deep-is@0.1.4: {}
1673
-
1674
-
default-browser-id@3.0.0:
1675
dependencies:
1676
-
bplist-parser: 0.2.0
1677
-
untildify: 4.0.0
1678
1679
-
default-browser@4.0.0:
1680
dependencies:
1681
-
bundle-name: 3.0.0
1682
-
default-browser-id: 3.0.0
1683
-
execa: 7.2.0
1684
-
titleize: 3.0.0
1685
1686
-
define-data-property@1.1.1:
1687
dependencies:
1688
-
get-intrinsic: 1.2.2
1689
-
gopd: 1.0.1
1690
-
has-property-descriptors: 1.0.1
1691
1692
-
define-lazy-prop@3.0.0: {}
1693
1694
define-properties@1.2.1:
1695
dependencies:
1696
-
define-data-property: 1.1.1
1697
-
has-property-descriptors: 1.0.1
1698
object-keys: 1.1.1
1699
1700
-
dir-glob@3.0.1:
1701
-
dependencies:
1702
-
path-type: 4.0.0
1703
1704
doctrine@2.1.0:
1705
dependencies:
1706
esutils: 2.0.3
1707
1708
-
doctrine@3.0.0:
1709
dependencies:
1710
-
esutils: 2.0.3
1711
1712
-
es-abstract@1.22.3:
1713
dependencies:
1714
-
array-buffer-byte-length: 1.0.0
1715
-
arraybuffer.prototype.slice: 1.0.2
1716
-
available-typed-arrays: 1.0.5
1717
-
call-bind: 1.0.5
1718
-
es-set-tostringtag: 2.0.2
1719
-
es-to-primitive: 1.2.1
1720
-
function.prototype.name: 1.1.6
1721
-
get-intrinsic: 1.2.2
1722
-
get-symbol-description: 1.0.0
1723
-
globalthis: 1.0.3
1724
-
gopd: 1.0.1
1725
-
has-property-descriptors: 1.0.1
1726
-
has-proto: 1.0.1
1727
-
has-symbols: 1.0.3
1728
-
hasown: 2.0.0
1729
-
internal-slot: 1.0.6
1730
-
is-array-buffer: 3.0.2
1731
is-callable: 1.2.7
1732
-
is-negative-zero: 2.0.2
1733
-
is-regex: 1.1.4
1734
-
is-shared-array-buffer: 1.0.2
1735
-
is-string: 1.0.7
1736
-
is-typed-array: 1.1.12
1737
-
is-weakref: 1.0.2
1738
-
object-inspect: 1.13.1
1739
object-keys: 1.1.1
1740
-
object.assign: 4.1.5
1741
-
regexp.prototype.flags: 1.5.1
1742
-
safe-array-concat: 1.0.1
1743
-
safe-regex-test: 1.0.0
1744
-
string.prototype.trim: 1.2.8
1745
-
string.prototype.trimend: 1.0.7
1746
-
string.prototype.trimstart: 1.0.7
1747
-
typed-array-buffer: 1.0.0
1748
-
typed-array-byte-length: 1.0.0
1749
-
typed-array-byte-offset: 1.0.0
1750
-
typed-array-length: 1.0.4
1751
-
unbox-primitive: 1.0.2
1752
-
which-typed-array: 1.1.13
1753
1754
-
es-iterator-helpers@1.0.15:
1755
dependencies:
1756
-
asynciterator.prototype: 1.0.0
1757
-
call-bind: 1.0.5
1758
define-properties: 1.2.1
1759
-
es-abstract: 1.22.3
1760
-
es-set-tostringtag: 2.0.2
1761
function-bind: 1.1.2
1762
-
get-intrinsic: 1.2.2
1763
-
globalthis: 1.0.3
1764
-
has-property-descriptors: 1.0.1
1765
-
has-proto: 1.0.1
1766
-
has-symbols: 1.0.3
1767
-
internal-slot: 1.0.6
1768
-
iterator.prototype: 1.1.2
1769
-
safe-array-concat: 1.0.1
1770
1771
-
es-set-tostringtag@2.0.2:
1772
dependencies:
1773
-
get-intrinsic: 1.2.2
1774
-
has-tostringtag: 1.0.0
1775
-
hasown: 2.0.0
1776
1777
-
es-shim-unscopables@1.0.2:
1778
dependencies:
1779
-
hasown: 2.0.0
1780
1781
-
es-to-primitive@1.2.1:
1782
dependencies:
1783
is-callable: 1.2.7
1784
-
is-date-object: 1.0.5
1785
-
is-symbol: 1.0.4
1786
1787
esbuild-copy-static-files@0.1.0: {}
1788
···
1813
1814
escape-string-regexp@4.0.0: {}
1815
1816
-
eslint-config-prettier@9.1.0(eslint@8.55.0):
1817
dependencies:
1818
-
eslint: 8.55.0
1819
1820
-
eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.0):
1821
dependencies:
1822
-
eslint: 8.55.0
1823
-
eslint-config-prettier: 9.1.0(eslint@8.55.0)
1824
prettier: 3.1.0
1825
prettier-linter-helpers: 1.0.0
1826
-
synckit: 0.8.6
1827
1828
-
eslint-plugin-react@7.33.2(eslint@8.55.0):
1829
dependencies:
1830
-
array-includes: 3.1.7
1831
-
array.prototype.flatmap: 1.3.2
1832
-
array.prototype.tosorted: 1.1.2
1833
doctrine: 2.1.0
1834
-
es-iterator-helpers: 1.0.15
1835
-
eslint: 8.55.0
1836
estraverse: 5.3.0
1837
jsx-ast-utils: 3.3.5
1838
minimatch: 3.1.2
1839
-
object.entries: 1.1.7
1840
-
object.fromentries: 2.0.7
1841
-
object.hasown: 1.1.3
1842
-
object.values: 1.1.7
1843
prop-types: 15.8.1
1844
resolve: 2.0.0-next.5
1845
semver: 6.3.1
1846
-
string.prototype.matchall: 4.0.10
1847
1848
-
eslint-scope@7.2.2:
1849
dependencies:
1850
esrecurse: 4.3.0
1851
estraverse: 5.3.0
1852
1853
eslint-visitor-keys@3.4.3: {}
1854
1855
-
eslint@8.55.0:
1856
dependencies:
1857
-
'@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
1858
-
'@eslint-community/regexpp': 4.10.0
1859
-
'@eslint/eslintrc': 2.1.4
1860
-
'@eslint/js': 8.55.0
1861
-
'@humanwhocodes/config-array': 0.11.13
1862
'@humanwhocodes/module-importer': 1.0.1
1863
-
'@nodelib/fs.walk': 1.2.8
1864
-
'@ungap/structured-clone': 1.2.0
1865
ajv: 6.12.6
1866
chalk: 4.1.2
1867
-
cross-spawn: 7.0.3
1868
-
debug: 4.3.4
1869
-
doctrine: 3.0.0
1870
escape-string-regexp: 4.0.0
1871
-
eslint-scope: 7.2.2
1872
-
eslint-visitor-keys: 3.4.3
1873
-
espree: 9.6.1
1874
-
esquery: 1.5.0
1875
esutils: 2.0.3
1876
fast-deep-equal: 3.1.3
1877
-
file-entry-cache: 6.0.1
1878
find-up: 5.0.0
1879
glob-parent: 6.0.2
1880
-
globals: 13.23.0
1881
-
graphemer: 1.4.0
1882
-
ignore: 5.3.0
1883
imurmurhash: 0.1.4
1884
is-glob: 4.0.3
1885
-
is-path-inside: 3.0.3
1886
-
js-yaml: 4.1.0
1887
json-stable-stringify-without-jsonify: 1.0.1
1888
-
levn: 0.4.1
1889
lodash.merge: 4.6.2
1890
minimatch: 3.1.2
1891
natural-compare: 1.4.0
1892
optionator: 0.9.3
1893
-
strip-ansi: 6.0.1
1894
-
text-table: 0.2.0
1895
transitivePeerDependencies:
1896
- supports-color
1897
1898
-
espree@9.6.1:
1899
dependencies:
1900
-
acorn: 8.11.2
1901
-
acorn-jsx: 5.3.2(acorn@8.11.2)
1902
-
eslint-visitor-keys: 3.4.3
1903
1904
-
esquery@1.5.0:
1905
dependencies:
1906
estraverse: 5.3.0
1907
···
1911
1912
estraverse@5.3.0: {}
1913
1914
esutils@2.0.3: {}
1915
1916
-
execa@5.1.1:
1917
-
dependencies:
1918
-
cross-spawn: 7.0.3
1919
-
get-stream: 6.0.1
1920
-
human-signals: 2.1.0
1921
-
is-stream: 2.0.1
1922
-
merge-stream: 2.0.0
1923
-
npm-run-path: 4.0.1
1924
-
onetime: 5.1.2
1925
-
signal-exit: 3.0.7
1926
-
strip-final-newline: 2.0.0
1927
1928
-
execa@7.2.0:
1929
-
dependencies:
1930
-
cross-spawn: 7.0.3
1931
-
get-stream: 6.0.1
1932
-
human-signals: 4.3.1
1933
-
is-stream: 3.0.0
1934
-
merge-stream: 2.0.0
1935
-
npm-run-path: 5.1.0
1936
-
onetime: 6.0.0
1937
-
signal-exit: 3.0.7
1938
-
strip-final-newline: 3.0.0
1939
1940
fast-deep-equal@3.1.3: {}
1941
···
1947
'@nodelib/fs.walk': 1.2.8
1948
glob-parent: 5.1.2
1949
merge2: 1.4.1
1950
-
micromatch: 4.0.5
1951
1952
fast-json-stable-stringify@2.1.0: {}
1953
1954
fast-levenshtein@2.0.6: {}
1955
1956
-
fastq@1.15.0:
1957
dependencies:
1958
reusify: 1.0.4
1959
1960
-
file-entry-cache@6.0.1:
1961
dependencies:
1962
-
flat-cache: 3.2.0
1963
1964
-
fill-range@7.0.1:
1965
dependencies:
1966
to-regex-range: 5.0.1
1967
1968
find-up@5.0.0:
1969
dependencies:
1970
locate-path: 6.0.0
1971
path-exists: 4.0.0
1972
1973
-
flat-cache@3.2.0:
1974
dependencies:
1975
flatted: 3.2.9
1976
keyv: 4.5.4
1977
-
rimraf: 3.0.2
1978
1979
flatted@3.2.9: {}
1980
1981
-
for-each@0.3.3:
1982
dependencies:
1983
is-callable: 1.2.7
1984
1985
-
fs.realpath@1.0.0: {}
1986
-
1987
function-bind@1.1.2: {}
1988
1989
-
function.prototype.name@1.1.6:
1990
dependencies:
1991
-
call-bind: 1.0.5
1992
define-properties: 1.2.1
1993
-
es-abstract: 1.22.3
1994
functions-have-names: 1.2.3
1995
1996
functions-have-names@1.2.3: {}
1997
1998
-
get-intrinsic@1.2.2:
1999
dependencies:
2000
function-bind: 1.1.2
2001
-
has-proto: 1.0.1
2002
-
has-symbols: 1.0.3
2003
-
hasown: 2.0.0
2004
2005
-
get-stream@6.0.1: {}
2006
2007
-
get-symbol-description@1.0.0:
2008
dependencies:
2009
-
call-bind: 1.0.5
2010
-
get-intrinsic: 1.2.2
2011
2012
glob-parent@5.1.2:
2013
dependencies:
···
2017
dependencies:
2018
is-glob: 4.0.3
2019
2020
-
glob@7.2.3:
2021
-
dependencies:
2022
-
fs.realpath: 1.0.0
2023
-
inflight: 1.0.6
2024
-
inherits: 2.0.4
2025
-
minimatch: 3.1.2
2026
-
once: 1.4.0
2027
-
path-is-absolute: 1.0.1
2028
2029
-
globals@13.23.0:
2030
-
dependencies:
2031
-
type-fest: 0.20.2
2032
-
2033
-
globalthis@1.0.3:
2034
dependencies:
2035
define-properties: 1.2.1
2036
2037
-
globby@11.1.0:
2038
-
dependencies:
2039
-
array-union: 2.1.0
2040
-
dir-glob: 3.0.1
2041
-
fast-glob: 3.3.2
2042
-
ignore: 5.3.0
2043
-
merge2: 1.4.1
2044
-
slash: 3.0.0
2045
-
2046
-
gopd@1.0.1:
2047
-
dependencies:
2048
-
get-intrinsic: 1.2.2
2049
2050
graphemer@1.4.0: {}
2051
2052
-
has-bigints@1.0.2: {}
2053
2054
has-flag@4.0.0: {}
2055
2056
-
has-property-descriptors@1.0.1:
2057
dependencies:
2058
-
get-intrinsic: 1.2.2
2059
2060
-
has-proto@1.0.1: {}
2061
2062
-
has-symbols@1.0.3: {}
2063
2064
-
has-tostringtag@1.0.0:
2065
dependencies:
2066
-
has-symbols: 1.0.3
2067
2068
-
hasown@2.0.0:
2069
dependencies:
2070
function-bind: 1.1.2
2071
2072
-
human-signals@2.1.0: {}
2073
-
2074
-
human-signals@4.3.1: {}
2075
-
2076
husky@8.0.3: {}
2077
2078
-
ignore@5.3.0: {}
2079
2080
import-fresh@3.3.0:
2081
dependencies:
···
2084
2085
imurmurhash@0.1.4: {}
2086
2087
-
inflight@1.0.6:
2088
dependencies:
2089
-
once: 1.4.0
2090
-
wrappy: 1.0.2
2091
2092
-
inherits@2.0.4: {}
2093
-
2094
-
internal-slot@1.0.6:
2095
dependencies:
2096
-
get-intrinsic: 1.2.2
2097
-
hasown: 2.0.0
2098
-
side-channel: 1.0.4
2099
2100
-
is-array-buffer@3.0.2:
2101
dependencies:
2102
-
call-bind: 1.0.5
2103
-
get-intrinsic: 1.2.2
2104
-
is-typed-array: 1.1.12
2105
2106
-
is-async-function@2.0.0:
2107
dependencies:
2108
-
has-tostringtag: 1.0.0
2109
2110
-
is-bigint@1.0.4:
2111
dependencies:
2112
-
has-bigints: 1.0.2
2113
-
2114
-
is-boolean-object@1.1.2:
2115
-
dependencies:
2116
-
call-bind: 1.0.5
2117
-
has-tostringtag: 1.0.0
2118
2119
is-callable@1.2.7: {}
2120
2121
-
is-core-module@2.13.1:
2122
dependencies:
2123
-
hasown: 2.0.0
2124
2125
-
is-date-object@1.0.5:
2126
dependencies:
2127
-
has-tostringtag: 1.0.0
2128
2129
-
is-docker@2.2.1: {}
2130
-
2131
-
is-docker@3.0.0: {}
2132
2133
is-extglob@2.1.1: {}
2134
2135
-
is-finalizationregistry@1.0.2:
2136
dependencies:
2137
-
call-bind: 1.0.5
2138
2139
-
is-generator-function@1.0.10:
2140
dependencies:
2141
-
has-tostringtag: 1.0.0
2142
2143
is-glob@4.0.3:
2144
dependencies:
2145
is-extglob: 2.1.1
2146
2147
-
is-inside-container@1.0.0:
2148
-
dependencies:
2149
-
is-docker: 3.0.0
2150
-
2151
-
is-map@2.0.2: {}
2152
-
2153
-
is-negative-zero@2.0.2: {}
2154
2155
-
is-number-object@1.0.7:
2156
dependencies:
2157
-
has-tostringtag: 1.0.0
2158
2159
is-number@7.0.0: {}
2160
2161
-
is-path-inside@3.0.3: {}
2162
-
2163
-
is-regex@1.1.4:
2164
dependencies:
2165
-
call-bind: 1.0.5
2166
-
has-tostringtag: 1.0.0
2167
2168
-
is-set@2.0.2: {}
2169
2170
-
is-shared-array-buffer@1.0.2:
2171
dependencies:
2172
-
call-bind: 1.0.5
2173
-
2174
-
is-stream@2.0.1: {}
2175
-
2176
-
is-stream@3.0.0: {}
2177
2178
-
is-string@1.0.7:
2179
dependencies:
2180
-
has-tostringtag: 1.0.0
2181
2182
-
is-symbol@1.0.4:
2183
dependencies:
2184
-
has-symbols: 1.0.3
2185
2186
-
is-typed-array@1.1.12:
2187
dependencies:
2188
-
which-typed-array: 1.1.13
2189
2190
-
is-weakmap@2.0.1: {}
2191
2192
-
is-weakref@1.0.2:
2193
dependencies:
2194
-
call-bind: 1.0.5
2195
2196
-
is-weakset@2.0.2:
2197
dependencies:
2198
-
call-bind: 1.0.5
2199
-
get-intrinsic: 1.2.2
2200
-
2201
-
is-wsl@2.2.0:
2202
-
dependencies:
2203
-
is-docker: 2.2.1
2204
2205
isarray@2.0.5: {}
2206
2207
isexe@2.0.0: {}
2208
2209
-
iterator.prototype@1.1.2:
2210
dependencies:
2211
-
define-properties: 1.2.1
2212
-
get-intrinsic: 1.2.2
2213
-
has-symbols: 1.0.3
2214
-
reflect.getprototypeof: 1.0.4
2215
-
set-function-name: 2.0.1
2216
2217
js-tokens@4.0.0: {}
2218
···
2228
2229
jsx-ast-utils@3.3.5:
2230
dependencies:
2231
-
array-includes: 3.1.7
2232
-
array.prototype.flat: 1.3.2
2233
-
object.assign: 4.1.5
2234
-
object.values: 1.1.7
2235
2236
keyv@4.5.4:
2237
dependencies:
···
2252
dependencies:
2253
js-tokens: 4.0.0
2254
2255
-
lru-cache@6.0.0:
2256
-
dependencies:
2257
-
yallist: 4.0.0
2258
-
2259
-
merge-stream@2.0.0: {}
2260
2261
merge2@1.4.1: {}
2262
2263
-
micromatch@4.0.5:
2264
dependencies:
2265
-
braces: 3.0.2
2266
picomatch: 2.3.1
2267
2268
-
mimic-fn@2.1.0: {}
2269
-
2270
-
mimic-fn@4.0.0: {}
2271
2272
minimatch@3.1.2:
2273
dependencies:
2274
brace-expansion: 1.1.11
2275
2276
-
ms@2.1.2: {}
2277
2278
-
natural-compare@1.4.0: {}
2279
2280
-
npm-run-path@4.0.1:
2281
-
dependencies:
2282
-
path-key: 3.1.1
2283
2284
-
npm-run-path@5.1.0:
2285
-
dependencies:
2286
-
path-key: 4.0.0
2287
2288
object-assign@4.1.1: {}
2289
2290
-
object-inspect@1.13.1: {}
2291
2292
object-keys@1.1.1: {}
2293
2294
-
object.assign@4.1.5:
2295
dependencies:
2296
-
call-bind: 1.0.5
2297
define-properties: 1.2.1
2298
-
has-symbols: 1.0.3
2299
object-keys: 1.1.1
2300
2301
-
object.entries@1.1.7:
2302
-
dependencies:
2303
-
call-bind: 1.0.5
2304
-
define-properties: 1.2.1
2305
-
es-abstract: 1.22.3
2306
-
2307
-
object.fromentries@2.0.7:
2308
dependencies:
2309
-
call-bind: 1.0.5
2310
define-properties: 1.2.1
2311
-
es-abstract: 1.22.3
2312
2313
-
object.hasown@1.1.3:
2314
dependencies:
2315
define-properties: 1.2.1
2316
-
es-abstract: 1.22.3
2317
2318
-
object.values@1.1.7:
2319
dependencies:
2320
-
call-bind: 1.0.5
2321
define-properties: 1.2.1
2322
-
es-abstract: 1.22.3
2323
-
2324
-
once@1.4.0:
2325
-
dependencies:
2326
-
wrappy: 1.0.2
2327
-
2328
-
onetime@5.1.2:
2329
-
dependencies:
2330
-
mimic-fn: 2.1.0
2331
2332
-
onetime@6.0.0:
2333
dependencies:
2334
-
mimic-fn: 4.0.0
2335
2336
-
open@9.1.0:
2337
dependencies:
2338
-
default-browser: 4.0.0
2339
-
define-lazy-prop: 3.0.0
2340
-
is-inside-container: 1.0.0
2341
-
is-wsl: 2.2.0
2342
2343
optionator@0.9.3:
2344
dependencies:
···
2349
prelude-ls: 1.2.1
2350
type-check: 0.4.0
2351
2352
p-limit@3.1.0:
2353
dependencies:
2354
yocto-queue: 0.1.0
···
2356
p-locate@5.0.0:
2357
dependencies:
2358
p-limit: 3.1.0
2359
2360
parent-module@1.0.1:
2361
dependencies:
···
2363
2364
path-exists@4.0.0: {}
2365
2366
-
path-is-absolute@1.0.1: {}
2367
-
2368
path-key@3.1.1: {}
2369
2370
-
path-key@4.0.0: {}
2371
-
2372
path-parse@1.0.7: {}
2373
2374
-
path-type@4.0.0: {}
2375
-
2376
-
picocolors@1.0.0: {}
2377
2378
picomatch@2.3.1: {}
2379
2380
prelude-ls@1.2.1: {}
2381
2382
prettier-linter-helpers@1.0.0:
···
2385
2386
prettier@3.1.0: {}
2387
2388
prop-types@15.8.1:
2389
dependencies:
2390
loose-envify: 1.4.0
···
2393
2394
punycode@2.3.1: {}
2395
2396
queue-microtask@1.2.3: {}
2397
2398
react-is@16.13.1: {}
2399
2400
-
reflect.getprototypeof@1.0.4:
2401
dependencies:
2402
-
call-bind: 1.0.5
2403
define-properties: 1.2.1
2404
-
es-abstract: 1.22.3
2405
-
get-intrinsic: 1.2.2
2406
-
globalthis: 1.0.3
2407
-
which-builtin-type: 1.1.3
2408
2409
-
regexp.prototype.flags@1.5.1:
2410
dependencies:
2411
-
call-bind: 1.0.5
2412
define-properties: 1.2.1
2413
-
set-function-name: 2.0.1
2414
2415
resolve-from@4.0.0: {}
2416
2417
resolve@2.0.0-next.5:
2418
dependencies:
2419
-
is-core-module: 2.13.1
2420
path-parse: 1.0.7
2421
supports-preserve-symlinks-flag: 1.0.0
2422
2423
-
reusify@1.0.4: {}
2424
-
2425
-
rimraf@3.0.2:
2426
dependencies:
2427
-
glob: 7.2.3
2428
2429
-
run-applescript@5.0.0:
2430
-
dependencies:
2431
-
execa: 5.1.1
2432
2433
run-parallel@1.2.0:
2434
dependencies:
2435
queue-microtask: 1.2.3
2436
2437
-
safe-array-concat@1.0.1:
2438
dependencies:
2439
-
call-bind: 1.0.5
2440
-
get-intrinsic: 1.2.2
2441
-
has-symbols: 1.0.3
2442
isarray: 2.0.5
2443
2444
-
safe-regex-test@1.0.0:
2445
dependencies:
2446
-
call-bind: 1.0.5
2447
-
get-intrinsic: 1.2.2
2448
-
is-regex: 1.1.4
2449
2450
semver@6.3.1: {}
2451
2452
-
semver@7.5.4:
2453
-
dependencies:
2454
-
lru-cache: 6.0.0
2455
2456
-
set-function-length@1.1.1:
2457
dependencies:
2458
-
define-data-property: 1.1.1
2459
-
get-intrinsic: 1.2.2
2460
-
gopd: 1.0.1
2461
-
has-property-descriptors: 1.0.1
2462
2463
-
set-function-name@2.0.1:
2464
dependencies:
2465
-
define-data-property: 1.1.1
2466
functions-have-names: 1.2.3
2467
-
has-property-descriptors: 1.0.1
2468
2469
shebang-command@2.0.0:
2470
dependencies:
···
2472
2473
shebang-regex@3.0.0: {}
2474
2475
-
side-channel@1.0.4:
2476
dependencies:
2477
-
call-bind: 1.0.5
2478
-
get-intrinsic: 1.2.2
2479
-
object-inspect: 1.13.1
2480
2481
-
signal-exit@3.0.7: {}
2482
2483
-
slash@3.0.0: {}
2484
2485
standalone-electron-types@1.0.0:
2486
dependencies:
2487
'@types/node': 18.17.17
2488
2489
-
string.prototype.matchall@4.0.10:
2490
dependencies:
2491
-
call-bind: 1.0.5
2492
define-properties: 1.2.1
2493
-
es-abstract: 1.22.3
2494
-
get-intrinsic: 1.2.2
2495
-
has-symbols: 1.0.3
2496
-
internal-slot: 1.0.6
2497
-
regexp.prototype.flags: 1.5.1
2498
-
set-function-name: 2.0.1
2499
-
side-channel: 1.0.4
2500
2501
-
string.prototype.trim@1.2.8:
2502
dependencies:
2503
-
call-bind: 1.0.5
2504
define-properties: 1.2.1
2505
-
es-abstract: 1.22.3
2506
2507
-
string.prototype.trimend@1.0.7:
2508
dependencies:
2509
-
call-bind: 1.0.5
2510
define-properties: 1.2.1
2511
-
es-abstract: 1.22.3
2512
2513
-
string.prototype.trimstart@1.0.7:
2514
dependencies:
2515
-
call-bind: 1.0.5
2516
define-properties: 1.2.1
2517
-
es-abstract: 1.22.3
2518
2519
-
strip-ansi@6.0.1:
2520
dependencies:
2521
-
ansi-regex: 5.0.1
2522
-
2523
-
strip-final-newline@2.0.0: {}
2524
2525
-
strip-final-newline@3.0.0: {}
2526
2527
strip-json-comments@3.1.1: {}
2528
···
2532
2533
supports-preserve-symlinks-flag@1.0.0: {}
2534
2535
-
synckit@0.8.6:
2536
dependencies:
2537
-
'@pkgr/utils': 2.4.2
2538
-
tslib: 2.6.2
2539
2540
-
text-table@0.2.0: {}
2541
2542
-
titleize@3.0.0: {}
2543
2544
to-regex-range@5.0.1:
2545
dependencies:
2546
is-number: 7.0.0
2547
2548
-
ts-api-utils@1.0.3(typescript@5.3.2):
2549
dependencies:
2550
-
typescript: 5.3.2
2551
2552
-
tslib@2.6.2: {}
2553
2554
type-check@0.4.0:
2555
dependencies:
2556
prelude-ls: 1.2.1
2557
2558
-
type-fest@0.20.2: {}
2559
2560
-
typed-array-buffer@1.0.0:
2561
dependencies:
2562
-
call-bind: 1.0.5
2563
-
get-intrinsic: 1.2.2
2564
-
is-typed-array: 1.1.12
2565
2566
-
typed-array-byte-length@1.0.0:
2567
dependencies:
2568
-
call-bind: 1.0.5
2569
-
for-each: 0.3.3
2570
-
has-proto: 1.0.1
2571
-
is-typed-array: 1.1.12
2572
2573
-
typed-array-byte-offset@1.0.0:
2574
dependencies:
2575
-
available-typed-arrays: 1.0.5
2576
-
call-bind: 1.0.5
2577
-
for-each: 0.3.3
2578
-
has-proto: 1.0.1
2579
-
is-typed-array: 1.1.12
2580
2581
-
typed-array-length@1.0.4:
2582
dependencies:
2583
-
call-bind: 1.0.5
2584
-
for-each: 0.3.3
2585
-
is-typed-array: 1.1.12
2586
2587
-
typescript@5.3.2: {}
2588
2589
-
unbox-primitive@1.0.2:
2590
dependencies:
2591
-
call-bind: 1.0.5
2592
-
has-bigints: 1.0.2
2593
-
has-symbols: 1.0.3
2594
-
which-boxed-primitive: 1.0.2
2595
2596
-
untildify@4.0.0: {}
2597
2598
uri-js@4.4.1:
2599
dependencies:
2600
punycode: 2.3.1
2601
2602
-
which-boxed-primitive@1.0.2:
2603
dependencies:
2604
-
is-bigint: 1.0.4
2605
-
is-boolean-object: 1.1.2
2606
-
is-number-object: 1.0.7
2607
-
is-string: 1.0.7
2608
-
is-symbol: 1.0.4
2609
2610
-
which-builtin-type@1.1.3:
2611
dependencies:
2612
-
function.prototype.name: 1.1.6
2613
-
has-tostringtag: 1.0.0
2614
-
is-async-function: 2.0.0
2615
-
is-date-object: 1.0.5
2616
-
is-finalizationregistry: 1.0.2
2617
-
is-generator-function: 1.0.10
2618
-
is-regex: 1.1.4
2619
-
is-weakref: 1.0.2
2620
isarray: 2.0.5
2621
-
which-boxed-primitive: 1.0.2
2622
-
which-collection: 1.0.1
2623
-
which-typed-array: 1.1.13
2624
2625
-
which-collection@1.0.1:
2626
dependencies:
2627
-
is-map: 2.0.2
2628
-
is-set: 2.0.2
2629
-
is-weakmap: 2.0.1
2630
-
is-weakset: 2.0.2
2631
2632
-
which-typed-array@1.1.13:
2633
dependencies:
2634
-
available-typed-arrays: 1.0.5
2635
-
call-bind: 1.0.5
2636
-
for-each: 0.3.3
2637
-
gopd: 1.0.1
2638
-
has-tostringtag: 1.0.0
2639
2640
which@2.0.2:
2641
dependencies:
2642
isexe: 2.0.0
2643
2644
-
wrappy@1.0.2: {}
2645
2646
-
yallist@4.0.0: {}
2647
2648
-
yocto-queue@0.1.0: {}
···
4
autoInstallPeers: true
5
excludeLinksFromLockfile: false
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
+
62
importers:
63
64
.:
65
devDependencies:
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
72
esbuild:
73
+
specifier: catalog:dev
74
version: 0.19.3
75
esbuild-copy-static-files:
76
+
specifier: catalog:dev
77
version: 0.1.0
78
eslint:
79
+
specifier: catalog:dev
80
+
version: 9.23.0(jiti@2.4.2)
81
husky:
82
+
specifier: catalog:dev
83
version: 8.0.3
84
prettier:
85
+
specifier: catalog:dev
86
version: 3.1.0
87
+
taze:
88
+
specifier: catalog:dev
89
+
version: 19.0.4
90
typescript:
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
115
116
packages/core:
117
dependencies:
···
121
122
packages/core-extensions:
123
dependencies:
124
+
'@moonlight-mod/core':
125
+
specifier: workspace:*
126
+
version: link:../core
127
'@moonlight-mod/types':
128
specifier: workspace:*
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
136
137
packages/injector:
138
dependencies:
···
154
155
packages/types:
156
dependencies:
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
166
'@types/react':
167
+
specifier: ^18.3.10
168
+
version: 18.3.20
169
csstype:
170
+
specifier: ^3.1.3
171
+
version: 3.1.3
172
standalone-electron-types:
173
specifier: ^1.0.0
174
version: 1.0.0
···
178
'@moonlight-mod/core':
179
specifier: workspace:*
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
193
194
packages:
195
···
197
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
198
engines: {node: '>=0.10.0'}
199
200
+
'@antfu/ni@24.3.0':
201
+
resolution: {integrity: sha512-wBSav4mBxvHEW9RbdSo1SWLQ6MAlT0Dc423weC58yOWqW4OcMvtnNDdDrxOZeJ88fEIyPK93gDUWIelBxzSf8g==}
202
hasBin: true
203
204
'@esbuild/android-arm64@0.19.3':
···
333
cpu: [x64]
334
os: [win32]
335
336
+
'@eslint-community/eslint-utils@4.5.1':
337
+
resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==}
338
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
339
peerDependencies:
340
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
341
342
+
'@eslint-community/regexpp@4.12.1':
343
+
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
344
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
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==}
···
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==}
···
675
color-name@1.1.4:
676
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
677
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: '*'
···
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:
···
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':
···
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==}
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:
···
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==}
···
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:
···
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:
···
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==}
···
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==}
···
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==}
···
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'}
···
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'}
···
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
···
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:
···
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:
···
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==}
···
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:
···
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
···
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':
1688
dependencies:
1689
ajv: 6.12.6
1690
+
debug: 4.4.0
1691
+
espree: 10.3.0
1692
+
globals: 14.0.0
1693
+
ignore: 5.3.2
1694
import-fresh: 3.3.0
1695
js-yaml: 4.1.0
1696
minimatch: 3.1.2
···
1698
transitivePeerDependencies:
1699
- supports-color
1700
1701
+
'@eslint/js@9.23.0': {}
1702
+
1703
+
'@eslint/object-schema@2.1.6': {}
1704
1705
+
'@eslint/plugin-kit@0.2.8':
1706
dependencies:
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)
1732
transitivePeerDependencies:
1733
+
- '@types/eslint'
1734
+
- prettier
1735
- supports-color
1736
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
1759
+
1760
+
'@moonlight-mod/moonmap@1.0.5': {}
1761
1762
'@nodelib/fs.scandir@2.1.5':
1763
dependencies:
···
1769
'@nodelib/fs.walk@1.2.8':
1770
dependencies:
1771
'@nodelib/fs.scandir': 2.1.5
1772
+
fastq: 1.17.1
1773
1774
+
'@pkgr/core@0.2.0': {}
1775
+
1776
+
'@quansync/fs@0.1.2':
1777
dependencies:
1778
+
quansync: 0.2.10
1779
+
1780
+
'@types/chroma-js@3.1.0': {}
1781
+
1782
+
'@types/chrome@0.0.313':
1783
+
dependencies:
1784
+
'@types/filesystem': 0.0.36
1785
+
'@types/har-format': 1.2.16
1786
+
1787
+
'@types/eslint@9.6.1':
1788
+
dependencies:
1789
+
'@types/estree': 1.0.7
1790
+
'@types/json-schema': 7.0.15
1791
+
optional: true
1792
+
1793
+
'@types/estree-jsx@1.0.5':
1794
+
dependencies:
1795
+
'@types/estree': 1.0.6
1796
+
1797
+
'@types/estree@1.0.6': {}
1798
+
1799
+
'@types/estree@1.0.7':
1800
+
optional: true
1801
1802
+
'@types/fbemitter@2.0.35': {}
1803
1804
+
'@types/filesystem@0.0.36':
1805
dependencies:
1806
+
'@types/filewriter': 0.0.33
1807
+
1808
+
'@types/filewriter@0.0.33': {}
1809
+
1810
+
'@types/flux@3.1.14':
1811
+
dependencies:
1812
+
'@types/fbemitter': 2.0.35
1813
+
'@types/react': 18.3.20
1814
+
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)':
1843
+
dependencies:
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)
1851
graphemer: 1.4.0
1852
+
ignore: 5.3.2
1853
natural-compare: 1.4.0
1854
+
ts-api-utils: 2.1.0(typescript@5.8.2)
1855
+
typescript: 5.8.2
1856
transitivePeerDependencies:
1857
- supports-color
1858
1859
+
'@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
1860
dependencies:
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
1868
transitivePeerDependencies:
1869
- supports-color
1870
1871
+
'@typescript-eslint/scope-manager@8.29.0':
1872
dependencies:
1873
+
'@typescript-eslint/types': 8.29.0
1874
+
'@typescript-eslint/visitor-keys': 8.29.0
1875
1876
+
'@typescript-eslint/type-utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
1877
dependencies:
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
1884
transitivePeerDependencies:
1885
- supports-color
1886
1887
+
'@typescript-eslint/types@8.29.0': {}
1888
1889
+
'@typescript-eslint/typescript-estree@8.29.0(typescript@5.8.2)':
1890
dependencies:
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
1895
is-glob: 4.0.3
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
1900
transitivePeerDependencies:
1901
- supports-color
1902
1903
+
'@typescript-eslint/utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
1904
dependencies:
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
1911
transitivePeerDependencies:
1912
- supports-color
1913
+
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)':
1931
dependencies:
1932
+
'@zenfs/core': 2.0.0
1933
+
utilium: 1.10.1
1934
1935
+
abort-controller@3.0.0:
1936
+
dependencies:
1937
+
event-target-shim: 5.0.1
1938
1939
+
acorn-jsx@5.3.2(acorn@8.14.1):
1940
dependencies:
1941
+
acorn: 8.14.1
1942
1943
+
acorn@8.14.1: {}
1944
1945
ajv@6.12.6:
1946
dependencies:
···
1949
json-schema-traverse: 0.4.1
1950
uri-js: 4.4.1
1951
1952
ansi-styles@4.3.0:
1953
dependencies:
1954
color-convert: 2.0.1
1955
+
1956
+
ansis@3.17.0: {}
1957
1958
argparse@2.0.1: {}
1959
1960
+
array-buffer-byte-length@1.0.2:
1961
dependencies:
1962
+
call-bound: 1.0.4
1963
+
is-array-buffer: 3.0.5
1964
1965
+
array-includes@3.1.8:
1966
dependencies:
1967
+
call-bind: 1.0.8
1968
define-properties: 1.2.1
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
1973
1974
+
array.prototype.findlast@1.2.5:
1975
dependencies:
1976
+
call-bind: 1.0.8
1977
define-properties: 1.2.1
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
1982
1983
+
array.prototype.flat@1.3.3:
1984
dependencies:
1985
+
call-bind: 1.0.8
1986
define-properties: 1.2.1
1987
+
es-abstract: 1.23.9
1988
+
es-shim-unscopables: 1.1.0
1989
1990
+
array.prototype.flatmap@1.3.3:
1991
dependencies:
1992
+
call-bind: 1.0.8
1993
define-properties: 1.2.1
1994
+
es-abstract: 1.23.9
1995
+
es-shim-unscopables: 1.1.0
1996
1997
+
array.prototype.tosorted@1.1.4:
1998
dependencies:
1999
+
call-bind: 1.0.8
2000
define-properties: 1.2.1
2001
+
es-abstract: 1.23.9
2002
+
es-errors: 1.3.0
2003
+
es-shim-unscopables: 1.1.0
2004
2005
+
arraybuffer.prototype.slice@1.0.4:
2006
dependencies:
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
2014
2015
+
astring@1.9.0: {}
2016
2017
+
async-function@1.0.0: {}
2018
2019
+
available-typed-arrays@1.0.7:
2020
dependencies:
2021
+
possible-typed-array-names: 1.1.0
2022
+
2023
+
balanced-match@1.0.2: {}
2024
+
2025
+
base64-js@1.5.1: {}
2026
2027
brace-expansion@1.1.11:
2028
dependencies:
2029
balanced-match: 1.0.2
2030
concat-map: 0.0.1
2031
2032
+
brace-expansion@2.0.1:
2033
dependencies:
2034
+
balanced-match: 1.0.2
2035
2036
+
braces@3.0.3:
2037
dependencies:
2038
+
fill-range: 7.1.1
2039
2040
+
buffer@6.0.3:
2041
dependencies:
2042
+
base64-js: 1.5.1
2043
+
ieee754: 1.2.1
2044
+
2045
+
cac@6.7.14: {}
2046
+
2047
+
call-bind-apply-helpers@1.0.2:
2048
+
dependencies:
2049
+
es-errors: 1.3.0
2050
function-bind: 1.1.2
2051
+
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
2058
+
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
···
2074
2075
color-name@1.1.4: {}
2076
2077
concat-map@0.0.1: {}
2078
2079
+
cross-spawn@7.0.6:
2080
dependencies:
2081
path-key: 3.1.1
2082
shebang-command: 2.0.0
2083
which: 2.0.2
2084
2085
+
csstype@3.1.3: {}
2086
2087
+
data-view-buffer@1.0.2:
2088
dependencies:
2089
+
call-bound: 1.0.4
2090
+
es-errors: 1.3.0
2091
+
is-data-view: 1.0.2
2092
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
2098
2099
+
data-view-byte-offset@1.0.1:
2100
dependencies:
2101
+
call-bound: 1.0.4
2102
+
es-errors: 1.3.0
2103
+
is-data-view: 1.0.2
2104
2105
+
debug@4.4.0:
2106
dependencies:
2107
+
ms: 2.1.3
2108
2109
+
deep-is@0.1.4: {}
2110
+
2111
+
define-data-property@1.1.4:
2112
+
dependencies:
2113
+
es-define-property: 1.0.1
2114
+
es-errors: 1.3.0
2115
+
gopd: 1.2.0
2116
2117
define-properties@1.2.1:
2118
dependencies:
2119
+
define-data-property: 1.1.4
2120
+
has-property-descriptors: 1.0.2
2121
object-keys: 1.1.1
2122
2123
+
defu@6.1.4: {}
2124
+
2125
+
destr@2.0.4: {}
2126
2127
doctrine@2.1.0:
2128
dependencies:
2129
esutils: 2.0.3
2130
2131
+
dunder-proto@1.0.1:
2132
dependencies:
2133
+
call-bind-apply-helpers: 1.0.2
2134
+
es-errors: 1.3.0
2135
+
gopd: 1.2.0
2136
2137
+
es-abstract@1.23.9:
2138
dependencies:
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
2164
is-callable: 1.2.7
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
2173
object-keys: 1.1.1
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
2190
2191
+
es-define-property@1.0.1: {}
2192
+
2193
+
es-errors@1.3.0: {}
2194
+
2195
+
es-iterator-helpers@1.2.1:
2196
dependencies:
2197
+
call-bind: 1.0.8
2198
+
call-bound: 1.0.4
2199
define-properties: 1.2.1
2200
+
es-abstract: 1.23.9
2201
+
es-errors: 1.3.0
2202
+
es-set-tostringtag: 2.1.0
2203
function-bind: 1.1.2
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
2213
2214
+
es-object-atoms@1.1.1:
2215
dependencies:
2216
+
es-errors: 1.3.0
2217
2218
+
es-set-tostringtag@2.1.0:
2219
dependencies:
2220
+
es-errors: 1.3.0
2221
+
get-intrinsic: 1.3.0
2222
+
has-tostringtag: 1.0.2
2223
+
hasown: 2.0.2
2224
2225
+
es-shim-unscopables@1.1.0:
2226
+
dependencies:
2227
+
hasown: 2.0.2
2228
+
2229
+
es-to-primitive@1.3.0:
2230
dependencies:
2231
is-callable: 1.2.7
2232
+
is-date-object: 1.1.0
2233
+
is-symbol: 1.1.1
2234
2235
esbuild-copy-static-files@0.1.0: {}
2236
···
2261
2262
escape-string-regexp@4.0.0: {}
2263
2264
+
eslint-config-prettier@9.1.0(eslint@9.23.0(jiti@2.4.2)):
2265
dependencies:
2266
+
eslint: 9.23.0(jiti@2.4.2)
2267
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):
2269
dependencies:
2270
+
eslint: 9.23.0(jiti@2.4.2)
2271
prettier: 3.1.0
2272
prettier-linter-helpers: 1.0.0
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))
2277
2278
+
eslint-plugin-react@7.37.5(eslint@9.23.0(jiti@2.4.2)):
2279
dependencies:
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
2284
doctrine: 2.1.0
2285
+
es-iterator-helpers: 1.2.1
2286
+
eslint: 9.23.0(jiti@2.4.2)
2287
estraverse: 5.3.0
2288
+
hasown: 2.0.2
2289
jsx-ast-utils: 3.3.5
2290
minimatch: 3.1.2
2291
+
object.entries: 1.1.9
2292
+
object.fromentries: 2.0.8
2293
+
object.values: 1.2.1
2294
prop-types: 15.8.1
2295
resolve: 2.0.0-next.5
2296
semver: 6.3.1
2297
+
string.prototype.matchall: 4.0.12
2298
+
string.prototype.repeat: 1.0.0
2299
2300
+
eslint-scope@8.3.0:
2301
dependencies:
2302
esrecurse: 4.3.0
2303
estraverse: 5.3.0
2304
2305
eslint-visitor-keys@3.4.3: {}
2306
2307
+
eslint-visitor-keys@4.2.0: {}
2308
+
2309
+
eslint@9.23.0(jiti@2.4.2):
2310
dependencies:
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
2320
'@humanwhocodes/module-importer': 1.0.1
2321
+
'@humanwhocodes/retry': 0.4.2
2322
+
'@types/estree': 1.0.6
2323
+
'@types/json-schema': 7.0.15
2324
ajv: 6.12.6
2325
chalk: 4.1.2
2326
+
cross-spawn: 7.0.6
2327
+
debug: 4.4.0
2328
escape-string-regexp: 4.0.0
2329
+
eslint-scope: 8.3.0
2330
+
eslint-visitor-keys: 4.2.0
2331
+
espree: 10.3.0
2332
+
esquery: 1.6.0
2333
esutils: 2.0.3
2334
fast-deep-equal: 3.1.3
2335
+
file-entry-cache: 8.0.0
2336
find-up: 5.0.0
2337
glob-parent: 6.0.2
2338
+
ignore: 5.3.2
2339
imurmurhash: 0.1.4
2340
is-glob: 4.0.3
2341
json-stable-stringify-without-jsonify: 1.0.1
2342
lodash.merge: 4.6.2
2343
minimatch: 3.1.2
2344
natural-compare: 1.4.0
2345
optionator: 0.9.3
2346
+
optionalDependencies:
2347
+
jiti: 2.4.2
2348
transitivePeerDependencies:
2349
- supports-color
2350
2351
+
espree@10.3.0:
2352
dependencies:
2353
+
acorn: 8.14.1
2354
+
acorn-jsx: 5.3.2(acorn@8.14.1)
2355
+
eslint-visitor-keys: 4.2.0
2356
2357
+
esquery@1.6.0:
2358
dependencies:
2359
estraverse: 5.3.0
2360
···
2364
2365
estraverse@5.3.0: {}
2366
2367
+
estree-toolkit@1.7.8:
2368
+
dependencies:
2369
+
'@types/estree': 1.0.6
2370
+
'@types/estree-jsx': 1.0.5
2371
+
2372
esutils@2.0.3: {}
2373
2374
+
event-target-shim@5.0.1: {}
2375
2376
+
eventemitter3@5.0.1: {}
2377
+
2378
+
events@3.3.0: {}
2379
2380
fast-deep-equal@3.1.3: {}
2381
···
2387
'@nodelib/fs.walk': 1.2.8
2388
glob-parent: 5.1.2
2389
merge2: 1.4.1
2390
+
micromatch: 4.0.8
2391
2392
fast-json-stable-stringify@2.1.0: {}
2393
2394
fast-levenshtein@2.0.6: {}
2395
2396
+
fastq@1.17.1:
2397
dependencies:
2398
reusify: 1.0.4
2399
2400
+
fdir@6.4.3(picomatch@4.0.2):
2401
+
optionalDependencies:
2402
+
picomatch: 4.0.2
2403
+
2404
+
file-entry-cache@8.0.0:
2405
dependencies:
2406
+
flat-cache: 4.0.1
2407
2408
+
fill-range@7.1.1:
2409
dependencies:
2410
to-regex-range: 5.0.1
2411
+
2412
+
find-up-simple@1.0.1: {}
2413
2414
find-up@5.0.0:
2415
dependencies:
2416
locate-path: 6.0.0
2417
path-exists: 4.0.0
2418
2419
+
flat-cache@4.0.1:
2420
dependencies:
2421
flatted: 3.2.9
2422
keyv: 4.5.4
2423
2424
flatted@3.2.9: {}
2425
2426
+
for-each@0.3.5:
2427
dependencies:
2428
is-callable: 1.2.7
2429
2430
function-bind@1.1.2: {}
2431
2432
+
function.prototype.name@1.1.8:
2433
dependencies:
2434
+
call-bind: 1.0.8
2435
+
call-bound: 1.0.4
2436
define-properties: 1.2.1
2437
functions-have-names: 1.2.3
2438
+
hasown: 2.0.2
2439
+
is-callable: 1.2.7
2440
2441
functions-have-names@1.2.3: {}
2442
2443
+
fzf@0.5.2: {}
2444
+
2445
+
get-intrinsic@1.3.0:
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
2451
function-bind: 1.1.2
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
2457
2458
+
get-proto@1.0.1:
2459
+
dependencies:
2460
+
dunder-proto: 1.0.1
2461
+
es-object-atoms: 1.1.1
2462
2463
+
get-symbol-description@1.1.0:
2464
dependencies:
2465
+
call-bound: 1.0.4
2466
+
es-errors: 1.3.0
2467
+
get-intrinsic: 1.3.0
2468
2469
glob-parent@5.1.2:
2470
dependencies:
···
2474
dependencies:
2475
is-glob: 4.0.3
2476
2477
+
globals@14.0.0: {}
2478
2479
+
globalthis@1.0.4:
2480
dependencies:
2481
define-properties: 1.2.1
2482
+
gopd: 1.2.0
2483
2484
+
gopd@1.2.0: {}
2485
2486
graphemer@1.4.0: {}
2487
2488
+
has-bigints@1.1.0: {}
2489
2490
has-flag@4.0.0: {}
2491
2492
+
has-property-descriptors@1.0.2:
2493
dependencies:
2494
+
es-define-property: 1.0.1
2495
2496
+
has-proto@1.2.0:
2497
+
dependencies:
2498
+
dunder-proto: 1.0.1
2499
2500
+
has-symbols@1.1.0: {}
2501
2502
+
has-tostringtag@1.0.2:
2503
dependencies:
2504
+
has-symbols: 1.1.0
2505
2506
+
hasown@2.0.2:
2507
dependencies:
2508
function-bind: 1.1.2
2509
2510
husky@8.0.3: {}
2511
2512
+
ieee754@1.2.1: {}
2513
+
2514
+
ignore@5.3.2: {}
2515
2516
import-fresh@3.3.0:
2517
dependencies:
···
2520
2521
imurmurhash@0.1.4: {}
2522
2523
+
internal-slot@1.1.0:
2524
dependencies:
2525
+
es-errors: 1.3.0
2526
+
hasown: 2.0.2
2527
+
side-channel: 1.1.0
2528
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
2534
2535
+
is-async-function@2.1.1:
2536
dependencies:
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
2542
2543
+
is-bigint@1.1.0:
2544
dependencies:
2545
+
has-bigints: 1.1.0
2546
2547
+
is-boolean-object@1.2.2:
2548
dependencies:
2549
+
call-bound: 1.0.4
2550
+
has-tostringtag: 1.0.2
2551
2552
is-callable@1.2.7: {}
2553
2554
+
is-core-module@2.16.1:
2555
dependencies:
2556
+
hasown: 2.0.2
2557
2558
+
is-data-view@1.0.2:
2559
dependencies:
2560
+
call-bound: 1.0.4
2561
+
get-intrinsic: 1.3.0
2562
+
is-typed-array: 1.1.15
2563
2564
+
is-date-object@1.1.0:
2565
+
dependencies:
2566
+
call-bound: 1.0.4
2567
+
has-tostringtag: 1.0.2
2568
2569
is-extglob@2.1.1: {}
2570
2571
+
is-finalizationregistry@1.1.1:
2572
dependencies:
2573
+
call-bound: 1.0.4
2574
2575
+
is-generator-function@1.1.0:
2576
dependencies:
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
2581
2582
is-glob@4.0.3:
2583
dependencies:
2584
is-extglob: 2.1.1
2585
2586
+
is-map@2.0.3: {}
2587
2588
+
is-number-object@1.1.1:
2589
dependencies:
2590
+
call-bound: 1.0.4
2591
+
has-tostringtag: 1.0.2
2592
2593
is-number@7.0.0: {}
2594
2595
+
is-regex@1.2.1:
2596
dependencies:
2597
+
call-bound: 1.0.4
2598
+
gopd: 1.2.0
2599
+
has-tostringtag: 1.0.2
2600
+
hasown: 2.0.2
2601
2602
+
is-set@2.0.3: {}
2603
2604
+
is-shared-array-buffer@1.0.4:
2605
dependencies:
2606
+
call-bound: 1.0.4
2607
2608
+
is-string@1.1.1:
2609
dependencies:
2610
+
call-bound: 1.0.4
2611
+
has-tostringtag: 1.0.2
2612
2613
+
is-symbol@1.1.1:
2614
dependencies:
2615
+
call-bound: 1.0.4
2616
+
has-symbols: 1.1.0
2617
+
safe-regex-test: 1.1.0
2618
2619
+
is-typed-array@1.1.15:
2620
dependencies:
2621
+
which-typed-array: 1.1.19
2622
2623
+
is-weakmap@2.0.2: {}
2624
2625
+
is-weakref@1.1.1:
2626
dependencies:
2627
+
call-bound: 1.0.4
2628
2629
+
is-weakset@2.0.4:
2630
dependencies:
2631
+
call-bound: 1.0.4
2632
+
get-intrinsic: 1.3.0
2633
2634
isarray@2.0.5: {}
2635
2636
isexe@2.0.0: {}
2637
2638
+
iterator.prototype@1.1.5:
2639
dependencies:
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
2646
+
2647
+
jiti@2.4.2: {}
2648
2649
js-tokens@4.0.0: {}
2650
···
2660
2661
jsx-ast-utils@3.3.5:
2662
dependencies:
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
2667
2668
keyv@4.5.4:
2669
dependencies:
···
2684
dependencies:
2685
js-tokens: 4.0.0
2686
2687
+
math-intrinsics@1.1.0: {}
2688
2689
merge2@1.4.1: {}
2690
2691
+
meriyah@6.0.1: {}
2692
+
2693
+
microdiff@1.5.0: {}
2694
+
2695
+
micromatch@4.0.8:
2696
dependencies:
2697
+
braces: 3.0.3
2698
picomatch: 2.3.1
2699
2700
+
mimic-function@5.0.1: {}
2701
2702
minimatch@3.1.2:
2703
dependencies:
2704
brace-expansion: 1.1.11
2705
2706
+
minimatch@9.0.5:
2707
+
dependencies:
2708
+
brace-expansion: 2.0.1
2709
2710
+
ms@2.1.3: {}
2711
2712
+
nanotar@0.1.1: {}
2713
2714
+
natural-compare@1.4.0: {}
2715
+
2716
+
node-fetch-native@1.6.6: {}
2717
2718
object-assign@4.1.1: {}
2719
2720
+
object-inspect@1.13.4: {}
2721
2722
object-keys@1.1.1: {}
2723
2724
+
object.assign@4.1.7:
2725
dependencies:
2726
+
call-bind: 1.0.8
2727
+
call-bound: 1.0.4
2728
define-properties: 1.2.1
2729
+
es-object-atoms: 1.1.1
2730
+
has-symbols: 1.1.0
2731
object-keys: 1.1.1
2732
2733
+
object.entries@1.1.9:
2734
dependencies:
2735
+
call-bind: 1.0.8
2736
+
call-bound: 1.0.4
2737
define-properties: 1.2.1
2738
+
es-object-atoms: 1.1.1
2739
2740
+
object.fromentries@2.0.8:
2741
dependencies:
2742
+
call-bind: 1.0.8
2743
define-properties: 1.2.1
2744
+
es-abstract: 1.23.9
2745
+
es-object-atoms: 1.1.1
2746
2747
+
object.values@1.2.1:
2748
dependencies:
2749
+
call-bind: 1.0.8
2750
+
call-bound: 1.0.4
2751
define-properties: 1.2.1
2752
+
es-object-atoms: 1.1.1
2753
2754
+
ofetch@1.4.1:
2755
dependencies:
2756
+
destr: 2.0.4
2757
+
node-fetch-native: 1.6.6
2758
+
ufo: 1.5.4
2759
2760
+
onetime@7.0.0:
2761
dependencies:
2762
+
mimic-function: 5.0.1
2763
2764
optionator@0.9.3:
2765
dependencies:
···
2770
prelude-ls: 1.2.1
2771
type-check: 0.4.0
2772
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:
2780
dependencies:
2781
yocto-queue: 0.1.0
···
2783
p-locate@5.0.0:
2784
dependencies:
2785
p-limit: 3.1.0
2786
+
2787
+
package-manager-detector@1.1.0: {}
2788
2789
parent-module@1.0.1:
2790
dependencies:
···
2792
2793
path-exists@4.0.0: {}
2794
2795
path-key@3.1.1: {}
2796
2797
path-parse@1.0.7: {}
2798
2799
+
pathe@2.0.3: {}
2800
2801
picomatch@2.3.1: {}
2802
2803
+
picomatch@4.0.2: {}
2804
+
2805
+
pnpm-workspace-yaml@0.3.1:
2806
+
dependencies:
2807
+
yaml: 2.7.1
2808
+
2809
+
possible-typed-array-names@1.1.0: {}
2810
+
2811
prelude-ls@1.2.1: {}
2812
2813
prettier-linter-helpers@1.0.0:
···
2816
2817
prettier@3.1.0: {}
2818
2819
+
process@0.11.10: {}
2820
+
2821
prop-types@15.8.1:
2822
dependencies:
2823
loose-envify: 1.4.0
···
2826
2827
punycode@2.3.1: {}
2828
2829
+
quansync@0.2.10: {}
2830
+
2831
queue-microtask@1.2.3: {}
2832
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:
2844
+
dependencies:
2845
+
call-bind: 1.0.8
2846
define-properties: 1.2.1
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
2853
2854
+
regexp.prototype.flags@1.5.4:
2855
dependencies:
2856
+
call-bind: 1.0.8
2857
define-properties: 1.2.1
2858
+
es-errors: 1.3.0
2859
+
get-proto: 1.0.1
2860
+
gopd: 1.2.0
2861
+
set-function-name: 2.0.2
2862
2863
resolve-from@4.0.0: {}
2864
2865
resolve@2.0.0-next.5:
2866
dependencies:
2867
+
is-core-module: 2.16.1
2868
path-parse: 1.0.7
2869
supports-preserve-symlinks-flag: 1.0.0
2870
2871
+
restore-cursor@5.1.0:
2872
dependencies:
2873
+
onetime: 7.0.0
2874
+
signal-exit: 4.1.0
2875
2876
+
reusify@1.0.4: {}
2877
2878
run-parallel@1.2.0:
2879
dependencies:
2880
queue-microtask: 1.2.3
2881
2882
+
safe-array-concat@1.1.3:
2883
dependencies:
2884
+
call-bind: 1.0.8
2885
+
call-bound: 1.0.4
2886
+
get-intrinsic: 1.3.0
2887
+
has-symbols: 1.1.0
2888
isarray: 2.0.5
2889
2890
+
safe-buffer@5.2.1: {}
2891
+
2892
+
safe-push-apply@1.0.0:
2893
dependencies:
2894
+
es-errors: 1.3.0
2895
+
isarray: 2.0.5
2896
+
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: {}
2906
2907
+
set-function-length@1.2.2:
2908
dependencies:
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
2915
2916
+
set-function-name@2.0.2:
2917
dependencies:
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
2922
+
2923
+
set-proto@1.0.0:
2924
+
dependencies:
2925
+
dunder-proto: 1.0.1
2926
+
es-errors: 1.3.0
2927
+
es-object-atoms: 1.1.1
2928
2929
shebang-command@2.0.0:
2930
dependencies:
···
2932
2933
shebang-regex@3.0.0: {}
2934
2935
+
side-channel-list@1.0.0:
2936
dependencies:
2937
+
es-errors: 1.3.0
2938
+
object-inspect: 1.13.4
2939
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
2946
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
2954
+
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:
2966
dependencies:
2967
'@types/node': 18.17.17
2968
2969
+
string.prototype.matchall@4.0.12:
2970
dependencies:
2971
+
call-bind: 1.0.8
2972
+
call-bound: 1.0.4
2973
define-properties: 1.2.1
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
2984
2985
+
string.prototype.repeat@1.0.0:
2986
dependencies:
2987
define-properties: 1.2.1
2988
+
es-abstract: 1.23.9
2989
2990
+
string.prototype.trim@1.2.10:
2991
dependencies:
2992
+
call-bind: 1.0.8
2993
+
call-bound: 1.0.4
2994
+
define-data-property: 1.1.4
2995
define-properties: 1.2.1
2996
+
es-abstract: 1.23.9
2997
+
es-object-atoms: 1.1.1
2998
+
has-property-descriptors: 1.0.2
2999
3000
+
string.prototype.trimend@1.0.9:
3001
dependencies:
3002
+
call-bind: 1.0.8
3003
+
call-bound: 1.0.4
3004
define-properties: 1.2.1
3005
+
es-object-atoms: 1.1.1
3006
3007
+
string.prototype.trimstart@1.0.8:
3008
dependencies:
3009
+
call-bind: 1.0.8
3010
+
define-properties: 1.2.1
3011
+
es-object-atoms: 1.1.1
3012
3013
+
string_decoder@1.3.0:
3014
+
dependencies:
3015
+
safe-buffer: 5.2.1
3016
3017
strip-json-comments@3.1.1: {}
3018
···
3022
3023
supports-preserve-symlinks-flag@1.0.0: {}
3024
3025
+
synckit@0.11.1:
3026
dependencies:
3027
+
'@pkgr/core': 0.2.0
3028
+
tslib: 2.8.1
3029
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
3044
3045
+
tinyexec@1.0.1: {}
3046
+
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:
3053
dependencies:
3054
is-number: 7.0.0
3055
3056
+
ts-api-utils@2.1.0(typescript@5.8.2):
3057
dependencies:
3058
+
typescript: 5.8.2
3059
3060
+
tslib@2.8.1: {}
3061
3062
type-check@0.4.0:
3063
dependencies:
3064
prelude-ls: 1.2.1
3065
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
3071
3072
+
typed-array-byte-length@1.0.3:
3073
dependencies:
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
3079
3080
+
typed-array-byte-offset@1.0.4:
3081
dependencies:
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
3089
3090
+
typed-array-length@1.0.7:
3091
dependencies:
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
3098
3099
+
typescript-eslint@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2):
3100
dependencies:
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
3108
3109
+
typescript@5.8.2: {}
3110
3111
+
ufo@1.5.4: {}
3112
+
3113
+
unbox-primitive@1.1.0:
3114
dependencies:
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
3119
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
3126
+
3127
+
undici-types@6.20.0: {}
3128
+
3129
+
undici-types@6.21.0: {}
3130
3131
uri-js@4.4.1:
3132
dependencies:
3133
punycode: 2.3.1
3134
3135
+
utilium@1.10.1:
3136
dependencies:
3137
+
eventemitter3: 5.0.1
3138
+
optionalDependencies:
3139
+
'@xterm/xterm': 5.5.0
3140
3141
+
which-boxed-primitive@1.1.1:
3142
dependencies:
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
3160
isarray: 2.0.5
3161
+
which-boxed-primitive: 1.1.1
3162
+
which-collection: 1.0.2
3163
+
which-typed-array: 1.1.19
3164
3165
+
which-collection@1.0.2:
3166
dependencies:
3167
+
is-map: 2.0.3
3168
+
is-set: 2.0.3
3169
+
is-weakmap: 2.0.2
3170
+
is-weakset: 2.0.4
3171
3172
+
which-typed-array@1.1.19:
3173
dependencies:
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
3181
3182
which@2.0.2:
3183
dependencies:
3184
isexe: 2.0.0
3185
3186
+
yaml@2.7.1: {}
3187
3188
+
yocto-queue@0.1.0: {}
3189
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
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
-13
tsconfig.json
+7
-13
tsconfig.json
···
1
{
2
"compilerOptions": {
3
-
"target": "es2016",
4
-
"module": "es6",
5
-
"esModuleInterop": true,
6
-
"forceConsistentCasingInFileNames": true,
7
-
"strict": true,
8
-
"moduleResolution": "bundler",
9
"baseUrl": "./packages/",
10
-
"jsx": "react",
11
-
"noEmit": true,
12
-
13
-
// disable unreachable code detection because it breaks with esbuild labels
14
-
"allowUnreachableCode": true
15
},
16
-
"include": ["./packages/**/*", "./env.d.ts"],
17
-
"exclude": ["node_modules"]
18
}