+4
-8
.github/workflows/browser.yml
+4
-8
.github/workflows/browser.yml
···
10
name: Browser extension builds
11
runs-on: ubuntu-latest
12
steps:
13
-
- uses: actions/checkout@v3
14
-
15
-
- uses: pnpm/action-setup@v2
16
-
with:
17
-
version: 9
18
-
run_install: false
19
-
- uses: actions/setup-node@v3
20
with:
21
-
node-version: 18
22
cache: pnpm
23
24
- name: Install dependencies
+4
-8
.github/workflows/lint.yml
+4
-8
.github/workflows/lint.yml
+7
-11
.github/workflows/nightly.yml
+7
-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
···
47
echo "$(date +%s)" >> ./dist/ref
48
49
- name: Setup GitHub Pages
50
-
uses: actions/configure-pages@v3
51
- name: Upload artifact
52
-
uses: actions/upload-pages-artifact@v1
53
with:
54
path: ./dist
55
- name: Deploy to GitHub Pages
56
-
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
···
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
+4
-8
.github/workflows/release.yml
+4
-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
+5
-11
.github/workflows/types.yml
+5
-11
.github/workflows/types.yml
···
11
name: Publish types on npm
12
runs-on: ubuntu-latest
13
steps:
14
-
- uses: actions/checkout@v3
15
-
16
-
- uses: pnpm/action-setup@v2
17
-
with:
18
-
version: 9
19
-
run_install: false
20
-
- uses: actions/setup-node@v3
21
with:
22
-
node-version: 18
23
cache: pnpm
24
registry-url: https://registry.npmjs.org
25
···
31
run: pnpm run build
32
33
- name: Publish types
34
-
run: |
35
-
cd packages/types
36
-
pnpm publish --access public --no-git-checks
37
env:
38
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
···
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
···
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
+3
-7
CHANGELOG.md
+3
-7
CHANGELOG.md
···
1
-
- Fixed CSP in the browser extension
2
-
- Added support for ESM when loading an extension entrypoint
3
-
- Added screenshots of Moonbase to the README
4
-
- Added an API to write to the extension settings
5
-
- Added isDir to the filesystem API
6
-
- Added localStorage to the moonlight global
7
- Updated mappings
8
-
- The extension loader now prioritizes loading developer extensions
+4
-3
README.md
+4
-3
README.md
···
5
<img src="./img/wordmark.png" alt="moonlight" />
6
</picture>
7
8
-
<a href="https://discord.gg/FdZBTFCP6F">Discord server</a>
9
\- <a href="https://github.com/moonlight-mod/moonlight">GitHub</a>
10
-
\- <a href="https://moonlight-mod.github.io/">Docs</a>
11
12
<hr />
13
···
22
23
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.
24
25
-
**_This is an experimental passion project._** Anything and everything is subject to change, but it is stable enough for developers to experiment with.
26
27
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.
···
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
···
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.
+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": 1728067476,
42
-
"narHash": "sha256-/uJcVXuBt+VFCPQIX+4YnYrHaubJSx4HoNsJVNRgANM=",
43
"owner": "NixOS",
44
"repo": "nixpkgs",
45
-
"rev": "6e6b3dd395c3b1eb9be9f2d096383a8d05add030",
46
"type": "github"
47
},
48
"original": {
49
"owner": "NixOS",
50
-
"ref": "nixos-24.05",
51
-
"repo": "nixpkgs",
52
-
"type": "github"
53
-
}
54
-
},
55
-
"nixpkgs_2": {
56
-
"locked": {
57
-
"lastModified": 1727802920,
58
-
"narHash": "sha256-HP89HZOT0ReIbI7IJZJQoJgxvB2Tn28V6XS3MNKnfLs=",
59
-
"owner": "nixos",
60
-
"repo": "nixpkgs",
61
-
"rev": "27e30d177e57d912d614c88c622dcfdb2e6e6515",
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": 1728137762,
78
-
"narHash": "sha256-iEFvPR3BopGyI5KjQ1DK+gEZ1dKDugq838tKdet2moQ=",
79
-
"owner": "NotNite",
80
-
"repo": "pnpm2nix-nzbr",
81
-
"rev": "b7a60d3c7d106b601665e3f05dba6cdc6f59f959",
82
-
"type": "github"
83
-
},
84
-
"original": {
85
-
"owner": "NotNite",
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=",
+3
-4
flake.nix
+3
-4
flake.nix
···
2
description = "Yet another Discord mod";
3
4
inputs = {
5
-
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
6
flake-utils.url = "github:numtide/flake-utils";
7
-
pnpm2nix.url = "github:NotNite/pnpm2nix-nzbr";
8
};
9
10
-
outputs = { self, nixpkgs, flake-utils, pnpm2nix }:
11
-
let overlay = import ./nix/overlay.nix { inherit pnpm2nix; };
12
in flake-utils.lib.eachDefaultSystem (system:
13
let
14
pkgs = import nixpkgs {
···
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 {
+46
-17
nix/default.nix
+46
-17
nix/default.nix
···
1
-
{ pkgs, mkPnpmPackage }:
2
3
-
mkPnpmPackage rec {
4
-
workspace = ./..;
5
src = ./..;
6
7
-
# Work around a bug with how it expects dist
8
-
components = [
9
-
"packages/core"
10
-
"packages/core-extensions"
11
-
"packages/injector"
12
-
"packages/node-preload"
13
-
"packages/types"
14
-
"packages/web-preload"
15
];
16
-
distDirs = [ "dist" ];
17
18
-
copyNodeModules = true;
19
-
buildPhase = "pnpm run build";
20
-
installPhase = "cp -r dist $out";
21
22
-
meta = with pkgs.lib; {
23
description = "Yet another Discord mod";
24
homepage = "https://moonlight-mod.github.io/";
25
license = licenses.lgpl3;
26
maintainers = with maintainers; [ notnite ];
27
};
28
-
}
···
1
+
{
2
+
lib,
3
+
stdenv,
4
+
nodejs_22,
5
+
pnpm_10,
6
+
}:
7
8
+
stdenv.mkDerivation (finalAttrs: {
9
+
pname = "moonlight";
10
+
version = (builtins.fromJSON (builtins.readFile ./../package.json)).version;
11
+
12
src = ./..;
13
14
+
outputs = [ "out" "firefox" ];
15
+
16
+
nativeBuildInputs = [
17
+
nodejs_22
18
+
pnpm_10.configHook
19
];
20
+
21
+
pnpmDeps = pnpm_10.fetchDeps {
22
+
inherit (finalAttrs) pname version src;
23
+
hash = "sha256-I+zRCUqJabpGJRFBGW0NrM9xzyzeCjioF54zlCpynBU=";
24
+
};
25
+
26
+
env = {
27
+
NODE_ENV = "production";
28
+
MOONLIGHT_VERSION = "v${finalAttrs.version}";
29
+
};
30
+
31
+
buildPhase = ''
32
+
runHook preBuild
33
+
34
+
pnpm run build
35
+
pnpm run browser-mv2
36
+
37
+
runHook postBuild
38
+
'';
39
+
40
+
installPhase = ''
41
+
runHook preInstall
42
43
+
cp -r dist $out
44
+
45
+
mkdir -p $firefox/share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/
46
+
mv $out/browser-mv2 $firefox/share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/{0fb6d66f-f22d-4555-a87b-34ef4bea5e2a}
47
+
48
+
runHook postInstall
49
+
'';
50
51
+
meta = with lib; {
52
description = "Yet another Discord mod";
53
homepage = "https://moonlight-mod.github.io/";
54
license = licenses.lgpl3;
55
maintainers = with maintainers; [ notnite ];
56
};
57
+
})
+3
-6
nix/overlay.nix
+3
-6
nix/overlay.nix
···
1
-
{ pnpm2nix }:
2
3
let
4
nameTable = {
···
29
'';
30
31
packageJson = ''
32
-
{"name":"discord","main":"./injector.js","private":true}
33
'';
34
35
in old.installPhase + "\n" + ''
···
49
'';
50
});
51
in final: prev: rec {
52
-
moonlight-mod = final.callPackage ./default.nix {
53
-
pkgs = final;
54
-
mkPnpmPackage = pnpm2nix.packages.${final.system}.mkPnpmPackage;
55
-
};
56
discord = mkOverride prev moonlight-mod "discord";
57
discord-ptb = mkOverride prev moonlight-mod "discord-ptb";
58
discord-canary = mkOverride prev moonlight-mod "discord-canary";
···
1
+
{ ... }:
2
3
let
4
nameTable = {
···
29
'';
30
31
packageJson = ''
32
+
{"name":"${name}","main":"./injector.js","private":true}
33
'';
34
35
in old.installPhase + "\n" + ''
···
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";
+23
-12
package.json
+23
-12
package.json
···
1
{
2
"name": "moonlight",
3
-
"version": "1.2.5",
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",
···
18
"browser": "node build.mjs --browser",
19
"browser-mv2": "node build.mjs --browser --mv2",
20
"lint": "eslint packages",
21
-
"lint:fix": "eslint packages --fix",
22
-
"lint:report": "eslint --output-file eslint_report.json --format json packages",
23
"typecheck": "tsc --noEmit",
24
"check": "pnpm run lint && pnpm run typecheck",
25
-
"prepare": "husky install"
26
},
27
"devDependencies": {
28
-
"esbuild": "^0.19.3",
29
-
"esbuild-copy-static-files": "^0.1.0",
30
-
"eslint": "^9.12.0",
31
-
"@moonlight-mod/eslint-config": "github:moonlight-mod/eslint-config",
32
-
"husky": "^8.0.3",
33
-
"prettier": "^3.1.0",
34
-
"typescript": "^5.3.2"
35
}
36
}
···
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",
···
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
}
+2
-1
packages/browser/blockLoading.json
+2
-1
packages/browser/blockLoading.json
+10
-4
packages/browser/manifest.json
+10
-4
packages/browser/manifest.json
···
1
{
2
"manifest_version": 3,
3
"name": "moonlight",
4
"description": "Yet another Discord mod",
5
-
"version": "1.2.5",
6
"permissions": ["declarativeNetRequestWithHostAccess", "webRequest", "scripting", "webNavigation"],
7
-
"host_permissions": ["https://moonlight-mod.github.io/*", "https://api.github.com/*", "https://*.discord.com/*"],
8
"content_scripts": [
9
{
10
"js": ["index.js"],
11
-
"matches": ["https://*.discord.com/*"],
12
"run_at": "document_start",
13
"world": "MAIN"
14
}
···
34
"web_accessible_resources": [
35
{
36
"resources": ["index.js"],
37
-
"matches": ["https://*.discord.com/*"]
38
}
39
]
40
}
···
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
}
···
40
"web_accessible_resources": [
41
{
42
"resources": ["index.js"],
43
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"]
44
}
45
]
46
}
+12
-6
packages/browser/manifestv2.json
+12
-6
packages/browser/manifestv2.json
···
1
{
2
"manifest_version": 2,
3
"name": "moonlight",
4
"description": "Yet another Discord mod",
5
-
"version": "1.2.5",
6
"permissions": [
7
"webRequest",
8
"webRequestBlocking",
9
"scripting",
10
"webNavigation",
11
-
"https://*.discord.com/assets/*.js",
12
"https://moonlight-mod.github.io/*",
13
-
"https://api.github.com/*",
14
-
"https://*.discord.com/*"
15
],
16
"background": {
17
"scripts": ["background.js"]
···
19
"content_scripts": [
20
{
21
"js": ["index.js"],
22
-
"matches": ["https://*.discord.com/*"],
23
"run_at": "document_start",
24
"world": "MAIN"
25
}
26
-
]
27
}
···
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"]
···
20
"content_scripts": [
21
{
22
"js": ["index.js"],
23
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"],
24
"run_at": "document_start",
25
"world": "MAIN"
26
}
27
+
],
28
+
"browser_specific_settings": {
29
+
"gecko": {
30
+
"id": "{0fb6d66f-f22d-4555-a87b-34ef4bea5e2a}"
31
+
}
32
+
}
33
}
+12
-2
packages/browser/package.json
+12
-2
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
}
+55
-70
packages/browser/src/background-mv2.js
+55
-70
packages/browser/src/background-mv2.js
···
1
/* eslint-disable no-console */
2
/* eslint-disable no-undef */
3
4
-
const starterUrls = ["web.", "sentry."];
5
-
let blockLoading = true;
6
-
let doing = false;
7
-
let collectedUrls = new Set();
8
9
-
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
10
-
const url = new URL(details.url);
11
-
if (!blockLoading && url.hostname.endsWith("discord.com")) {
12
-
console.log("Blocking", details.url);
13
-
blockLoading = true;
14
-
collectedUrls.clear();
15
-
}
16
-
});
17
18
-
async function doTheThing(urls, tabId) {
19
-
console.log("Doing", urls, tabId);
20
21
-
blockLoading = false;
22
23
-
try {
24
-
await chrome.scripting.executeScript({
25
-
target: { tabId },
26
-
world: "MAIN",
27
-
args: [urls],
28
-
func: async (urls) => {
29
-
try {
30
-
await window._moonlightBrowserInit();
31
-
} catch (e) {
32
-
console.log(e);
33
-
}
34
35
-
const scripts = [...document.querySelectorAll("script")].filter(
36
-
(script) => script.src && urls.some((url) => url.includes(script.src))
37
-
);
38
39
-
// backwards
40
-
urls.reverse();
41
-
for (const url of urls) {
42
-
const script = scripts.find((script) => url.includes(script.src));
43
-
console.log("adding new script", script);
44
45
-
const newScript = document.createElement("script");
46
-
for (const { name, value } of script.attributes) {
47
-
newScript.setAttribute(name, value);
48
}
49
-
50
-
script.remove();
51
-
document.documentElement.appendChild(newScript);
52
-
}
53
-
}
54
-
});
55
-
} catch (e) {
56
-
console.log(e);
57
-
}
58
-
59
-
doing = false;
60
-
collectedUrls.clear();
61
-
}
62
-
63
-
chrome.webRequest.onBeforeRequest.addListener(
64
-
async (details) => {
65
-
if (starterUrls.some((url) => details.url.includes(url))) {
66
-
console.log("Adding", details.url);
67
-
collectedUrls.add(details.url);
68
}
69
70
-
if (collectedUrls.size === starterUrls.length) {
71
-
if (doing) return;
72
-
if (!blockLoading) return;
73
-
doing = true;
74
-
const urls = [...collectedUrls];
75
-
const tabId = details.tabId;
76
-
77
-
// yes this is a load-bearing sleep
78
-
setTimeout(() => doTheThing(urls, tabId), 0);
79
-
}
80
-
81
-
if (blockLoading) return { cancel: true };
82
},
83
{
84
-
urls: ["https://*.discord.com/assets/*.js"]
85
},
86
["blocking"]
87
);
···
94
)
95
};
96
},
97
-
{ urls: ["https://*.discord.com/*"] },
98
["blocking", "responseHeaders"]
99
);
···
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
);
···
79
)
80
};
81
},
82
+
{ urls: ["https://*.discord.com/*", "https://*.discordapp.com/*"] },
83
["blocking", "responseHeaders"]
84
);
+37
-39
packages/browser/src/background.js
+37
-39
packages/browser/src/background.js
···
1
/* eslint-disable no-console */
2
/* eslint-disable no-undef */
3
4
-
const starterUrls = ["web.", "sentry."];
5
-
let blockLoading = true;
6
-
let doing = false;
7
-
let collectedUrls = new Set();
8
9
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
10
const url = new URL(details.url);
11
-
if (!blockLoading && url.hostname.endsWith("discord.com")) {
12
await chrome.declarativeNetRequest.updateEnabledRulesets({
13
enableRulesetIds: ["modifyResponseHeaders", "blockLoading"]
14
});
15
-
blockLoading = true;
16
-
collectedUrls.clear();
17
}
18
});
19
20
chrome.webRequest.onBeforeRequest.addListener(
21
async (details) => {
22
if (details.tabId === -1) return;
23
-
if (starterUrls.some((url) => details.url.includes(url))) {
24
-
console.log("Adding", details.url);
25
-
collectedUrls.add(details.url);
26
-
}
27
28
-
if (collectedUrls.size === starterUrls.length) {
29
-
if (doing) return;
30
-
if (!blockLoading) return;
31
-
doing = true;
32
-
const urls = [...collectedUrls];
33
-
console.log("Doing", urls);
34
35
console.log("Running moonlight script");
36
try {
···
40
files: ["index.js"]
41
});
42
} catch (e) {
43
-
console.log(e);
44
}
45
46
console.log("Initializing moonlight");
···
52
try {
53
await window._moonlightBrowserInit();
54
} catch (e) {
55
-
console.log(e);
56
}
57
}
58
});
···
60
console.log(e);
61
}
62
63
-
console.log("Updating rulesets");
64
try {
65
-
blockLoading = false;
66
await chrome.declarativeNetRequest.updateEnabledRulesets({
67
disableRulesetIds: ["blockLoading"],
68
enableRulesetIds: ["modifyResponseHeaders"]
69
});
70
} catch (e) {
71
-
console.log(e);
72
}
73
74
console.log("Readding scripts");
···
76
await chrome.scripting.executeScript({
77
target: { tabId: details.tabId },
78
world: "MAIN",
79
-
args: [urls],
80
-
func: async (urls) => {
81
const scripts = [...document.querySelectorAll("script")].filter(
82
-
(script) => script.src && urls.some((url) => url.includes(script.src))
83
);
84
85
-
// backwards
86
-
urls.reverse();
87
-
for (const url of urls) {
88
-
const script = scripts.find((script) => url.includes(script.src));
89
-
console.log("adding new script", script);
90
91
const newScript = document.createElement("script");
92
-
for (const { name, value } of script.attributes) {
93
-
newScript.setAttribute(name, value);
94
}
95
-
96
script.remove();
97
document.documentElement.appendChild(newScript);
98
}
99
}
100
});
101
} catch (e) {
102
-
console.log(e);
103
}
104
-
105
-
console.log("Done");
106
-
doing = false;
107
-
collectedUrls.clear();
108
}
109
},
110
{
111
-
urls: ["*://*.discord.com/assets/*.js"]
112
}
113
);
···
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 {
···
44
files: ["index.js"]
45
});
46
} catch (e) {
47
+
console.error(e);
48
}
49
50
console.log("Initializing moonlight");
···
56
try {
57
await window._moonlightBrowserInit();
58
} catch (e) {
59
+
console.error(e);
60
}
61
}
62
});
···
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");
···
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
);
+18
-14
packages/browser/src/index.ts
+18
-14
packages/browser/src/index.ts
···
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 { configure } from "@zenfs/core";
10
import * as fs from "@zenfs/core/promises";
11
12
function getParts(path: string) {
13
if (path.startsWith("/")) path = path.substring(1);
···
15
}
16
17
window._moonlightBrowserInit = async () => {
18
// Set up a virtual filesystem with IndexedDB
19
-
await configure({
20
-
mounts: {
21
-
"/": {
22
-
backend: IndexedDB,
23
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
24
-
// @ts-ignore tsc tweaking
25
-
storeName: "moonlight-fs"
26
-
}
27
-
}
28
});
29
30
window.moonlightNodeSandboxed = {
···
92
dirname(path) {
93
const parts = getParts(path);
94
return "/" + parts.slice(0, parts.length - 1).join("/");
95
}
96
},
97
// TODO
···
114
processedExtensions,
115
nativesCache: {},
116
isBrowser: true,
117
118
version: MOONLIGHT_VERSION,
119
branch: MOONLIGHT_BRANCH as MoonlightBranch,
···
123
},
124
getConfigOption(ext, name) {
125
const manifest = getManifest(extensions, ext);
126
-
return getConfigOption(ext, name, config, manifest);
127
},
128
-
setConfigOption(ext, name, value) {
129
setConfigOption(config, ext, name, value);
130
-
this.writeConfig(config);
131
},
132
133
getNatives: () => {},
···
145
async writeConfig(newConfig) {
146
await writeConfig(newConfig);
147
config = newConfig;
148
}
149
};
150
···
153
});
154
155
// This is set by web-preload for us
156
-
await window._moonlightBrowserLoad();
157
};
···
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);
···
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 = {
···
90
dirname(path) {
91
const parts = getParts(path);
92
return "/" + parts.slice(0, parts.length - 1).join("/");
93
+
},
94
+
basename(path) {
95
+
const parts = getParts(path);
96
+
return parts[parts.length - 1];
97
}
98
},
99
// TODO
···
116
processedExtensions,
117
nativesCache: {},
118
isBrowser: true,
119
+
events: createEventEmitter<NodeEventType, NodeEventPayloads>(),
120
121
version: MOONLIGHT_VERSION,
122
branch: MOONLIGHT_BRANCH as MoonlightBranch,
···
126
},
127
getConfigOption(ext, name) {
128
const manifest = getManifest(extensions, ext);
129
+
return getConfigOption(ext, name, config, manifest?.settings);
130
},
131
+
async setConfigOption(ext, name, value) {
132
setConfigOption(config, ext, name, value);
133
+
await this.writeConfig(config);
134
},
135
136
getNatives: () => {},
···
148
async writeConfig(newConfig) {
149
await writeConfig(newConfig);
150
config = newConfig;
151
+
this.events.dispatchEvent(NodeEventType.ConfigSaved, newConfig);
152
}
153
};
154
···
157
});
158
159
// This is set by web-preload for us
160
+
await window._moonlightWebLoad!();
161
};
+1
packages/browser/tsconfig.json
+1
packages/browser/tsconfig.json
+7
packages/core/package.json
+7
packages/core/package.json
+8
-15
packages/core/src/extension/loader.ts
+8
-15
packages/core/src/extension/loader.ts
···
13
import calculateDependencies from "../util/dependency";
14
import { createEventEmitter } from "../util/event";
15
import { registerStyles } from "../styles";
16
-
import { EventPayloads, EventType } from "@moonlight-mod/types/core/event";
17
18
const logger = new Logger("core/extension/loader");
19
···
60
let idx = 0;
61
for (const patch of exports.patches) {
62
if (Array.isArray(patch.replace)) {
63
-
for (const replacement of patch.replace) {
64
-
const newPatch = Object.assign({}, patch, {
65
-
replace: replacement
66
-
});
67
-
68
-
registerPatch({ ...newPatch, ext: ext.id, id: idx });
69
-
idx++;
70
-
}
71
} else {
72
-
registerPatch({ ...patch, ext: ext.id, id: idx });
73
-
idx++;
74
}
75
}
76
}
77
···
209
}
210
211
export async function loadProcessedExtensions({ extensions, dependencyGraph }: ProcessedExtensions) {
212
-
const eventEmitter = createEventEmitter<EventType, EventPayloads>();
213
const finished: Set<string> = new Set();
214
215
logger.trace(
···
231
}
232
233
function done() {
234
-
eventEmitter.removeEventListener(EventType.ExtensionLoad, cb);
235
r();
236
}
237
238
-
eventEmitter.addEventListener(EventType.ExtensionLoad, cb);
239
if (finished.has(dep)) done();
240
})
241
);
···
249
await loadExt(ext);
250
251
finished.add(ext.id);
252
-
eventEmitter.dispatchEvent(EventType.ExtensionLoad, ext.id);
253
logger.debug(`Loaded "${ext.id}"`);
254
}
255
···
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
···
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
···
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
);
···
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
+1
-4
packages/core/src/extension.ts
+1
-4
packages/core/src/extension.ts
···
129
const ret: DetectedExtension[] = [];
130
const seen = new Set<string>();
131
132
-
const coreExtensionsFs: Record<string, string> = JSON.parse(
133
-
// @ts-expect-error shut up
134
-
_moonlight_coreExtensionsStr
135
-
);
136
const coreExtensions = Array.from(new Set(Object.keys(coreExtensionsFs).map((x) => x.split("/")[0])));
137
138
for (const ext of coreExtensions) {
···
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) {
+3
packages/core/src/fs.ts
+3
packages/core/src/fs.ts
+123
-69
packages/core/src/patch.ts
+123
-69
packages/core/src/patch.ts
···
11
} from "@moonlight-mod/types";
12
import Logger from "./util/logger";
13
import calculateDependencies, { Dependency } from "./util/dependency";
14
-
import WebpackRequire from "@moonlight-mod/types/discord/require";
15
-
import { EventType } from "@moonlight-mod/types/core/event";
16
17
const logger = new Logger("core/patch");
18
···
24
const moduleLoadSubscriptions: Map<string, ((moduleId: string) => void)[]> = new Map();
25
26
export function registerPatch(patch: IdentifiedPatch) {
27
patches.push(patch);
28
moonlight.unpatched.add(patch);
29
}
···
63
const moduleCache: Record<string, string> = {};
64
const patched: Record<string, Array<string>> = {};
65
66
-
function patchModules(entry: WebpackJsonpEntry[1]) {
67
-
function patchModule(id: string, patchId: string, replaced: string) {
68
-
// Store what extensions patched what modules for easier debugging
69
-
patched[id] = patched[id] || [];
70
-
patched[id].push(patchId);
71
72
-
// Webpack module arguments are minified, so we replace them with consistent names
73
-
// We have to wrap it so things don't break, though
74
-
const patchedStr = patched[id].sort().join(", ");
75
76
-
const wrapped =
77
-
`(${replaced}).apply(this, arguments)\n` +
78
-
`// Patched by moonlight: ${patchedStr}\n` +
79
-
`//# sourceURL=Webpack-Module-${id}`;
80
81
-
try {
82
-
const func = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
83
-
entry[id] = func;
84
-
entry[id].__moonlight = true;
85
-
return true;
86
-
} catch (e) {
87
-
logger.warn("Error constructing function for patch", patchId, e);
88
-
patched[id].pop();
89
-
return false;
90
-
}
91
}
92
93
// Populate the module cache
94
for (const [id, func] of Object.entries(entry)) {
95
if (!Object.hasOwn(moduleCache, id) && func.__moonlight !== true) {
···
100
101
for (const [id, func] of Object.entries(entry)) {
102
if (func.__moonlight === true) continue;
103
-
let moduleString = moduleCache[id];
104
105
for (let i = 0; i < patches.length; i++) {
106
const patch = patches[i];
107
if (patch.prerequisite != null && !patch.prerequisite()) {
108
continue;
109
}
110
···
113
patch.find.lastIndex = 0;
114
}
115
116
-
// indexOf is faster than includes by 0.25% lmao
117
-
const match =
118
-
typeof patch.find === "string" ? moduleString.indexOf(patch.find) !== -1 : patch.find.test(moduleString);
119
120
// Global regexes apply to all modules
121
const shouldRemove = typeof patch.find === "string" ? true : !patch.find.global;
122
123
if (match) {
124
-
// We ensured all arrays get turned into normal PatchReplace objects on register
125
-
const replace = patch.replace as PatchReplace;
126
127
-
if (replace.type === undefined || replace.type === PatchReplaceType.Normal) {
128
-
// Add support for \i to match rspack's minified names
129
-
if (typeof replace.match !== "string") {
130
-
replace.match = new RegExp(replace.match.source.replace(/\\i/g, "[A-Za-z_$][\\w$]*"), replace.match.flags);
131
-
}
132
-
// tsc fails to detect the overloads for this, so I'll just do this
133
-
// Verbose, but it works
134
-
let replaced;
135
-
if (typeof replace.replacement === "string") {
136
-
replaced = moduleString.replace(replace.match, replace.replacement);
137
-
} else {
138
-
replaced = moduleString.replace(replace.match, replace.replacement);
139
-
}
140
141
-
if (replaced === moduleString) {
142
-
logger.warn("Patch replacement failed", id, patch);
143
-
continue;
144
-
}
145
146
-
if (patchModule(id, `${patch.ext}#${patch.id}`, replaced)) {
147
-
moduleString = replaced;
148
}
149
-
} else if (replace.type === PatchReplaceType.Module) {
150
-
// Directly replace the module with a new one
151
-
const newModule = replace.replacement(moduleString);
152
-
entry[id] = newModule;
153
-
entry[id].__moonlight = true;
154
-
moduleString = newModule.toString().replace(/\n/g, "") + `//# sourceURL=Webpack-Module-${id}`;
155
}
156
157
-
moonlight.unpatched.delete(patch);
158
-
159
-
if (shouldRemove) {
160
-
patches.splice(i--, 1);
161
}
162
}
163
}
164
165
-
moduleCache[id] = moduleString;
166
167
try {
168
const parsed = moonlight.lunast.parseScript(id, moduleString);
169
if (parsed != null) {
170
for (const [parsedId, parsedScript] of Object.entries(parsed)) {
171
-
if (patchModule(parsedId, "lunast", parsedScript)) {
172
moduleCache[parsedId] = parsedScript;
173
}
174
}
···
179
180
if (moonlightNode.config.patchAll === true) {
181
if ((typeof id !== "string" || !id.includes("_")) && !entry[id].__moonlight) {
182
-
const wrapped = `(${moduleCache[id]}).apply(this, arguments)\n` + `//# sourceURL=Webpack-Module-${id}`;
183
entry[id] = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
184
entry[id].__moonlight = true;
185
}
···
271
}
272
}
273
274
if (deps.size !== 0) {
275
-
wpModule.dependencies = Array.from(deps);
276
continue;
277
}
278
-
279
-
wpModule.dependencies = Array.from(deps);
280
}
281
}
282
···
289
if (wpModule.run) {
290
modules[id] = wpModule.run;
291
wpModule.run.__moonlight = true;
292
}
293
-
if (wpModule.entrypoint) entrypoints.push(id);
294
}
295
if (!webpackModules.size) break;
296
}
297
298
for (const [name, func] of Object.entries(moonlight.moonmap.getWebpackModules("window.moonlight.moonmap"))) {
299
injectedWpModules.push({ id: name, run: func });
300
modules[name] = func;
301
inject = true;
···
312
window.webpackChunkdiscord_app.push([
313
[--chunkId],
314
modules,
315
-
(require: typeof WebpackRequire) => entrypoints.map(require)
316
]);
317
}
318
}
···
362
const realPush = jsonp.push;
363
if (jsonp.push.__moonlight !== true) {
364
jsonp.push = (items) => {
365
-
moonlight.events.dispatchEvent(EventType.ChunkLoad, {
366
chunkId: items[0],
367
modules: items[1],
368
require: items[2]
···
410
set(modules: any) {
411
const { stack } = new Error();
412
if (stack!.includes("/assets/") && !Array.isArray(modules)) {
413
-
moonlight.events.dispatchEvent(EventType.ChunkLoad, {
414
modules: modules
415
});
416
patchModules(modules);
···
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
···
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
}
···
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) {
···
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
}
···
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
}
···
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;
···
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
}
···
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]
···
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);
+2
-2
packages/core/src/util/config.ts
+2
-2
packages/core/src/util/config.ts
···
14
ext: string,
15
key: string,
16
config: Config,
17
-
manifest?: ExtensionManifest
18
): T | undefined {
19
-
const defaultValue: T | undefined = structuredClone(manifest?.settings?.[key]?.default);
20
const cfg = getConfig(ext, config);
21
if (cfg == null || typeof cfg === "boolean") return defaultValue;
22
return cfg?.[key] ?? defaultValue;
···
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;
+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
+9
-1
packages/core-extensions/package.json
+9
-1
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
}
+1
packages/core-extensions/src/appPanels/manifest.json
+1
packages/core-extensions/src/appPanels/manifest.json
+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;
+7
-5
packages/core-extensions/src/common/index.ts
+7
-5
packages/core-extensions/src/common/index.ts
···
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
};
+2
-1
packages/core-extensions/src/common/manifest.json
+2
-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;
+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;
+84
packages/core-extensions/src/componentEditor/index.ts
+84
packages/core-extensions/src/componentEditor/index.ts
···
···
1
+
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
2
+
3
+
export const patches: Patch[] = [
4
+
// dm list
5
+
{
6
+
find: ".interactiveSystemDM]:",
7
+
replace: [
8
+
{
9
+
match: /decorators:(\i\.isSystemDM\(\)\?\(0,\i\.jsx\)\(.+?verified:!0}\):null)/,
10
+
replacement: (_, decorators) =>
11
+
`decorators:require("componentEditor_dmList").default._patchDecorators([${decorators}],arguments[0])`
12
+
},
13
+
{
14
+
match: /(?<=selected:\i,)children:\[/,
15
+
replacement: 'children:require("componentEditor_dmList").default._patchItems(['
16
+
},
17
+
{
18
+
match: /(?<=(onMouseDown|nameplate):\i}\))]/,
19
+
replacement: "],arguments[0])"
20
+
}
21
+
],
22
+
hardFail: true
23
+
},
24
+
25
+
// member list
26
+
{
27
+
find: ".lostPermission",
28
+
replace: [
29
+
{
30
+
match:
31
+
/(?<=\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[\(0,\i\.jsx\)\(\i,{user:\i}\),.+?onClickPremiumGuildIcon:\i}\)])/,
32
+
replacement: (_, decorators) =>
33
+
`children:require("componentEditor_memberList").default._patchDecorators(${decorators},arguments[0])`
34
+
},
35
+
{
36
+
match: /name:null==\i\?\(0,\i\.jsx\)\("span"/,
37
+
replacement: (orig: string) =>
38
+
`children:require("componentEditor_memberList").default._patchItems([],arguments[0]),${orig}`
39
+
}
40
+
]
41
+
},
42
+
43
+
// messages
44
+
{
45
+
find: '},"new-member")),',
46
+
replace: [
47
+
{
48
+
match: /(?<=\.BADGES](=|:))(\i)(;|})/,
49
+
replacement: (_, leading, badges, trailing) =>
50
+
`require("componentEditor_messages").default._patchUsernameBadges(${badges},arguments[0])${trailing}`
51
+
},
52
+
{
53
+
match: /(?<=className:\i,)badges:(\i)/,
54
+
replacement: (_, badges) =>
55
+
`badges:require("componentEditor_messages").default._patchBadges(${badges},arguments[0])`
56
+
},
57
+
{
58
+
match: /(?<=username:\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[.+?])}\),usernameSpanId:/,
59
+
replacement: (_, elements) =>
60
+
`children:require("componentEditor_messages").default._patchUsername(${elements},arguments[0])}),usernameSpanId:`
61
+
}
62
+
]
63
+
},
64
+
{
65
+
find: '.provider&&"Discord"===',
66
+
replace: {
67
+
match: /(?<=\.container\),)children:(\[.+?this\.renderSuppressConfirmModal\(\),.+?\])}\)/,
68
+
replacement: (_, elements) =>
69
+
`children:require("componentEditor_messages").default._patchAccessories(${elements},this.props)})`
70
+
}
71
+
}
72
+
];
73
+
74
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
75
+
dmList: {
76
+
dependencies: [{ id: "react" }]
77
+
},
78
+
memberList: {
79
+
dependencies: [{ id: "react" }]
80
+
},
81
+
messages: {
82
+
dependencies: [{ id: "react" }]
83
+
}
84
+
};
+11
packages/core-extensions/src/componentEditor/manifest.json
+11
packages/core-extensions/src/componentEditor/manifest.json
···
···
1
+
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
+
"id": "componentEditor",
4
+
"apiLevel": 2,
5
+
"meta": {
6
+
"name": "Component Editor",
7
+
"tagline": "A library to add to commonly patched components",
8
+
"authors": ["Cynosphere"],
9
+
"tags": ["library"]
10
+
}
11
+
}
+61
packages/core-extensions/src/componentEditor/webpackModules/dmList.tsx
+61
packages/core-extensions/src/componentEditor/webpackModules/dmList.tsx
···
···
1
+
import {
2
+
DMList,
3
+
DMListItem,
4
+
DMListDecorator,
5
+
DMListAnchorIndicies,
6
+
DMListDecoratorAnchorIndicies
7
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
8
+
import React from "@moonlight-mod/wp/react";
9
+
10
+
const items: Record<string, DMListItem> = {};
11
+
const decorators: Record<string, DMListDecorator> = {};
12
+
13
+
function addEntries(
14
+
elements: React.ReactNode[],
15
+
entries: Record<string, DMListItem | DMListDecorator>,
16
+
indicies: Partial<Record<keyof typeof DMListAnchorIndicies | keyof typeof DMListDecoratorAnchorIndicies, number>>,
17
+
props: any
18
+
) {
19
+
const originalElements = [...elements];
20
+
for (const [id, entry] of Object.entries(entries)) {
21
+
const component = <entry.component {...props} key={id} />;
22
+
23
+
if (entry.anchor === undefined) {
24
+
if (entry.before) {
25
+
elements.splice(0, 0, component);
26
+
} else {
27
+
elements.push(component);
28
+
}
29
+
} else {
30
+
const index = elements.indexOf(originalElements[indicies[entry.anchor]!]);
31
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
32
+
}
33
+
}
34
+
}
35
+
36
+
export const dmList: DMList = {
37
+
addItem(id, component, anchor, before = false) {
38
+
items[id] = {
39
+
component,
40
+
anchor,
41
+
before
42
+
};
43
+
},
44
+
addDecorator(id, component, anchor, before = false) {
45
+
decorators[id] = {
46
+
component,
47
+
anchor,
48
+
before
49
+
};
50
+
},
51
+
_patchItems(elements, props) {
52
+
addEntries(elements, items, DMListAnchorIndicies, props);
53
+
return elements;
54
+
},
55
+
_patchDecorators(elements, props) {
56
+
addEntries(elements, decorators, DMListDecoratorAnchorIndicies, props);
57
+
return elements;
58
+
}
59
+
};
60
+
61
+
export default dmList;
+50
packages/core-extensions/src/componentEditor/webpackModules/memberList.tsx
+50
packages/core-extensions/src/componentEditor/webpackModules/memberList.tsx
···
···
1
+
import {
2
+
MemberList,
3
+
MemberListDecorator,
4
+
MemberListDecoratorAnchorIndicies
5
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
6
+
import React from "@moonlight-mod/wp/react";
7
+
8
+
const items: Record<string, React.FC<any>> = {};
9
+
const decorators: Record<string, MemberListDecorator> = {};
10
+
11
+
export const memberList: MemberList = {
12
+
addItem(id, component) {
13
+
items[id] = component;
14
+
},
15
+
addDecorator(id, component, anchor, before = false) {
16
+
decorators[id] = {
17
+
component,
18
+
anchor,
19
+
before
20
+
};
21
+
},
22
+
_patchItems(elements, props) {
23
+
for (const [id, Component] of Object.entries(items)) {
24
+
elements.push(<Component {...props} key={id} />);
25
+
}
26
+
27
+
return elements;
28
+
},
29
+
_patchDecorators(elements, props) {
30
+
const originalElements = [...elements];
31
+
for (const [id, entry] of Object.entries(decorators)) {
32
+
const component = <entry.component {...props} key={id} />;
33
+
34
+
if (entry.anchor === undefined) {
35
+
if (entry.before) {
36
+
elements.splice(0, 0, component);
37
+
} else {
38
+
elements.push(component);
39
+
}
40
+
} else {
41
+
const index = elements.indexOf(originalElements[MemberListDecoratorAnchorIndicies[entry.anchor]!]);
42
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
43
+
}
44
+
}
45
+
46
+
return elements;
47
+
}
48
+
};
49
+
50
+
export default memberList;
+97
packages/core-extensions/src/componentEditor/webpackModules/messages.tsx
+97
packages/core-extensions/src/componentEditor/webpackModules/messages.tsx
···
···
1
+
import {
2
+
MessageBadge,
3
+
MessageBadgeIndicies,
4
+
Messages,
5
+
MessageUsername,
6
+
MessageUsernameBadge,
7
+
MessageUsernameBadgeIndicies,
8
+
MessageUsernameIndicies
9
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
10
+
import React from "@moonlight-mod/wp/react";
11
+
12
+
const username: Record<string, MessageUsername> = {};
13
+
const usernameBadges: Record<string, MessageUsernameBadge> = {};
14
+
const badges: Record<string, MessageBadge> = {};
15
+
const accessories: Record<string, React.FC<any>> = {};
16
+
17
+
function addEntries(
18
+
elements: React.ReactNode[],
19
+
entries: Record<string, MessageUsername | MessageUsernameBadge | MessageBadge>,
20
+
indicies: Partial<
21
+
Record<
22
+
| keyof typeof MessageUsernameIndicies
23
+
| keyof typeof MessageUsernameBadgeIndicies
24
+
| keyof typeof MessageBadgeIndicies,
25
+
number
26
+
>
27
+
>,
28
+
props: any
29
+
) {
30
+
const originalElements = [...elements];
31
+
for (const [id, entry] of Object.entries(entries)) {
32
+
const component = <entry.component {...props} key={id} />;
33
+
34
+
if (entry.anchor === undefined) {
35
+
if (entry.before) {
36
+
elements.splice(0, 0, component);
37
+
} else {
38
+
elements.push(component);
39
+
}
40
+
} else {
41
+
const index = elements.indexOf(originalElements[indicies[entry.anchor]!]);
42
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
43
+
}
44
+
}
45
+
}
46
+
47
+
function addComponents(elements: React.ReactNode[], components: Record<string, React.FC<any>>, props: any) {
48
+
for (const [id, Component] of Object.entries(components)) {
49
+
const component = <Component {...props} key={id} />;
50
+
elements.push(component);
51
+
}
52
+
}
53
+
54
+
export const messages: Messages = {
55
+
addToUsername(id, component, anchor, before = false) {
56
+
username[id] = {
57
+
component,
58
+
anchor,
59
+
before
60
+
};
61
+
},
62
+
addUsernameBadge(id, component, anchor, before = false) {
63
+
usernameBadges[id] = {
64
+
component,
65
+
anchor,
66
+
before
67
+
};
68
+
},
69
+
addBadge(id, component, anchor, before = false) {
70
+
badges[id] = {
71
+
component,
72
+
anchor,
73
+
before
74
+
};
75
+
},
76
+
addAccessory(id, component) {
77
+
accessories[id] = component;
78
+
},
79
+
_patchUsername(elements, props) {
80
+
addEntries(elements, username, MessageUsernameIndicies, props);
81
+
return elements;
82
+
},
83
+
_patchUsernameBadges(elements, props) {
84
+
addEntries(elements, usernameBadges, MessageUsernameBadgeIndicies, props);
85
+
return elements;
86
+
},
87
+
_patchBadges(elements, props) {
88
+
addEntries(elements, badges, MessageBadgeIndicies, props);
89
+
return elements;
90
+
},
91
+
_patchAccessories(elements, props) {
92
+
addComponents(elements, accessories, props);
93
+
return elements;
94
+
}
95
+
};
96
+
97
+
export default messages;
+1
packages/core-extensions/src/contextMenu/manifest.json
+1
packages/core-extensions/src/contextMenu/manifest.json
+17
-6
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
+17
-6
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
···
2
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
3
import parser from "@moonlight-mod/wp/contextMenu_evilMenu";
4
5
type Patch = {
6
navId: string;
7
-
item: (props: any) => MenuElement | MenuElement[];
8
-
anchorId: string;
9
before: boolean;
10
};
11
12
-
function addItem<T>(navId: string, item: (props: T) => MenuElement | MenuElement[], anchorId: string, before = false) {
13
-
patches.push({ navId, item, anchorId, before });
14
}
15
16
const patches: Patch[] = [];
···
19
if (!matches.length) return items;
20
21
for (const patch of matches) {
22
-
const idx = items.findIndex((i) => i.key === patch.anchorId);
23
if (idx === -1) continue;
24
-
items.splice(idx + 1 - +patch.before, 0, ...parser(patch.item(menuProps)));
25
}
26
27
return items;
···
48
};
49
50
// Unmangle Menu elements
51
const code =
52
spacepack.require.m[
53
spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id
···
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[] = [];
···
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;
···
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
+2
-1
packages/core-extensions/src/contextMenu/webpackModules/evilMenu.ts
+2
-1
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("Menu API only allows Items and groups of Items as children.")[0].id
···
7
8
const parserSym = code.match(/(?<=_patchMenu\(.,).+?(?=\()/)![0];
9
10
-
code = code.replace(/(?<=function\(\){return ).(?=})/, parserSym);
11
const mod = new Function("module", "exports", "require", `(${code}).apply(this, arguments)`);
12
13
const exp: any = {};
···
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
···
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 = {};
+1
packages/core-extensions/src/devToolsExtensions/manifest.json
+1
packages/core-extensions/src/devToolsExtensions/manifest.json
+2
-2
packages/core-extensions/src/disableSentry/index.ts
+2
-2
packages/core-extensions/src/disableSentry/index.ts
+5
-1
packages/core-extensions/src/disableSentry/manifest.json
+5
-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": {
···
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
-15
packages/core-extensions/src/disableSentry/node.ts
+13
-15
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(resolve(preloadPath, "..", "node_modules", "@sentry", "electron"));
12
-
require.cache[sentryPath] = new Module(sentryPath, require.cache[require.resolve(preloadPath)]);
13
-
require.cache[sentryPath]!.exports = {
14
-
init: () => {},
15
-
setTag: () => {},
16
-
setUser: () => {},
17
-
captureMessage: () => {}
18
-
};
19
-
logger.debug("Stubbed Sentry node side!");
20
-
} catch (err) {
21
-
logger.error("Failed to stub Sentry:", err);
22
-
}
23
}
···
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
}
+23
-9
packages/core-extensions/src/experiments/index.ts
+23
-9
packages/core-extensions/src/experiments/index.ts
···
11
{
12
find: '"scientist:triggered"', // Scientist? Triggered.
13
replace: {
14
-
match: /(?<=personal_connection_id\|\|)!1/,
15
-
replacement: "!0"
16
}
17
},
18
19
// Enable staff help menu
20
-
// FIXME: either make this actually work live (needs a state hook) or just
21
-
// wait for #122
22
{
23
find: ".HEADER_BAR)",
24
replace: {
25
-
match: /&&\((.)\?\(0,/,
26
replacement: (_, isStaff) =>
27
`&&(((moonlight.getConfigOption("experiments","devtools")??false)?true:${isStaff})?(0,`
28
}
29
},
30
31
// Enable further staff-locked options
32
-
// FIXME: #122, this doesn't work live
33
{
34
find: "shouldShowLurkerModeUpsellPopout:",
35
replace: {
36
-
match: /\.useReducedMotion,isStaff:(.),/,
37
-
replacement: (_, isStaff) =>
38
-
`.useReducedMotion,isStaff:(moonlight.getConfigOption("experiments","staffSettings")??false)?true:${isStaff},`
39
}
40
}
41
];
···
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: /&&\((\i)\?\(0,/,
24
replacement: (_, isStaff) =>
25
`&&(((moonlight.getConfigOption("experiments","devtools")??false)?true:${isStaff})?(0,`
26
}
27
},
28
+
// staff help menu - visual refresh
29
+
{
30
+
find: '("AppTitleBar")',
31
+
replace: {
32
+
match: /{hasBugReporterAccess:(\i)}=\i\.\i\.useExperiment\({location:"HeaderBar"},{autoTrackExposure:!1}\);/,
33
+
replacement: (orig, isStaff) =>
34
+
`${orig}if(moonlight.getConfigOption("experiments","devtools")??false)${isStaff}=true;`
35
+
}
36
+
},
37
+
{
38
+
find: 'navId:"staff-help-popout",',
39
+
replace: {
40
+
match: /isDiscordDeveloper:(\i)}\),/,
41
+
replacement: (_, isStaff) =>
42
+
`isDiscordDeveloper:(moonlight.getConfigOption("experiments","devtools")??false)||${isStaff}}),`
43
+
}
44
+
},
45
46
// Enable further staff-locked options
47
{
48
find: "shouldShowLurkerModeUpsellPopout:",
49
replace: {
50
+
match: /\.useReducedMotion,isStaff:(\i)(,|})/,
51
+
replacement: (_, isStaff, trail) =>
52
+
`.useReducedMotion,isStaff:(moonlight.getConfigOption("experiments","staffSettings")??false)?true:${isStaff}${trail}`
53
}
54
}
55
];
+3
packages/core-extensions/src/experiments/manifest.json
+3
packages/core-extensions/src/experiments/manifest.json
···
1
{
2
"id": "experiments",
3
"apiLevel": 2,
4
"meta": {
···
9
},
10
"settings": {
11
"devtools": {
12
"displayName": "Enable staff help menu (DevTools)",
13
"type": "boolean",
14
"default": false
15
},
16
"staffSettings": {
17
"displayName": "Allow access to other staff settings elsewhere",
18
"type": "boolean",
19
"default": false
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "experiments",
4
"apiLevel": 2,
5
"meta": {
···
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
+1
packages/core-extensions/src/markdown/manifest.json
+1
packages/core-extensions/src/markdown/manifest.json
+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
+
});
+18
-6
packages/core-extensions/src/moonbase/index.tsx
+18
-6
packages/core-extensions/src/moonbase/index.tsx
···
8
{
9
// CvQlAA mapped to ERRORS_ACTION_TO_TAKE
10
// FIXME: Better patch find?
11
-
match: /,(\(0,(.)\.jsx\))\("p",{children:.\.intl\.string\(.\..\.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,(.)\.jsx\))\(.+?,)action:(.),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
},
···
23
// add update button
24
// +hivLS -> ERRORS_RELOAD
25
{
26
-
match: /(?<=\["\+hivLS"\]\)}\),(\(0,(.)\.jsx\))\(.,{}\))/,
27
replacement: (_, createElement, ReactJSX) =>
28
`,${createElement}(require("moonbase_crashScreen")?.UpdateButton??${ReactJSX}.Fragment,{state:this.state,setState:this.setState.bind(this)})`
29
}
···
42
{ id: "react" },
43
{ id: "discord/components/common/index" },
44
{ ext: "moonbase", id: "stores" },
45
-
{ id: "discord/modules/guild_settings/IntegrationCard.css" },
46
"Masks.PANEL_BUTTON",
47
'"Missing channel in Channel.openChannelContextMenu"',
48
".forumOrHome]:"
49
]
50
},
51
52
settings: {
53
dependencies: [
54
{ ext: "spacepack", id: "spacepack" },
55
{ ext: "settings", id: "settings" },
56
{ id: "react" },
57
-
{ ext: "moonbase", id: "ui" }
58
],
59
entrypoint: true
60
},
···
63
dependencies: [
64
{ id: "react" },
65
{ ext: "moonbase", id: "stores" },
66
{ ext: "notices", id: "notices" },
67
{
68
ext: "spacepack",
69
id: "spacepack"
70
-
}
71
],
72
entrypoint: true
73
},
···
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
},
···
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
}
···
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
},
···
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
},
+18
-5
packages/core-extensions/src/moonbase/manifest.json
+18
-5
packages/core-extensions/src/moonbase/manifest.json
···
1
{
2
"id": "moonbase",
3
"apiLevel": 2,
4
"meta": {
···
6
"tagline": "The official settings UI for moonlight",
7
"authors": ["Cynosphere", "NotNite", "redstonekasi"]
8
},
9
-
"dependencies": ["spacepack", "settings", "common", "notices"],
10
"settings": {
11
"sections": {
12
"displayName": "Split into sections",
13
"description": "Show the Moonbase tabs as separate sections",
14
-
"type": "boolean"
15
},
16
"saveFilter": {
17
"displayName": "Persist filter",
18
"description": "Save extension filter in config",
19
-
"type": "boolean"
20
},
21
"updateChecking": {
22
"displayName": "Automatic update checking",
23
"description": "Checks for updates to moonlight",
24
"type": "boolean",
25
-
"default": "true"
26
},
27
"updateBanner": {
28
"displayName": "Show update banner",
29
"description": "Shows a banner for moonlight and extension updates",
30
"type": "boolean",
31
-
"default": "true"
32
}
33
},
34
"cors": [
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "moonbase",
4
"apiLevel": 2,
5
"meta": {
···
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": [
+29
-34
packages/core-extensions/src/moonbase/native.ts
+29
-34
packages/core-extensions/src/moonbase/native.ts
···
4
import { distDir, repoUrlFile, installedVersionFile } from "@moonlight-mod/types/constants";
5
import { parseTarGzip } from "nanotar";
6
7
const githubRepo = "moonlight-mod/moonlight";
8
const githubApiUrl = `https://api.github.com/repos/${githubRepo}/releases/latest`;
9
const artifactName = "dist.tar.gz";
···
11
const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref";
12
const nightlyZipUrl = "https://moonlight-mod.github.io/moonlight/dist.tar.gz";
13
14
-
export const userAgent = `moonlight/${moonlightNode.version} (https://github.com/moonlight-mod/moonlight)`;
15
16
async function getStableRelease(): Promise<{
17
name: string;
···
22
}> {
23
const req = await fetch(githubApiUrl, {
24
cache: "no-store",
25
-
headers: {
26
-
"User-Agent": userAgent
27
-
}
28
});
29
return await req.json();
30
}
31
32
export default function getNatives(): MoonbaseNatives {
33
-
const logger = moonlightNode.getLogger("moonbase/natives");
34
35
return {
36
async checkForMoonlightUpdate() {
37
try {
38
-
if (moonlightNode.branch === MoonlightBranch.STABLE) {
39
const json = await getStableRelease();
40
-
return json.name !== moonlightNode.version ? json.name : null;
41
-
} else if (moonlightNode.branch === MoonlightBranch.NIGHTLY) {
42
const req = await fetch(nightlyRefUrl, {
43
cache: "no-store",
44
-
headers: {
45
-
"User-Agent": userAgent
46
-
}
47
});
48
const ref = (await req.text()).split("\n")[0];
49
-
return ref !== moonlightNode.version ? ref : null;
50
}
51
52
return null;
···
56
}
57
},
58
59
-
async updateMoonlight() {
60
// Note: this won't do anything on browser, we should probably disable it
61
// entirely when running in browser.
62
async function downloadStable(): Promise<[ArrayBuffer, string]> {
···
67
logger.debug(`Downloading ${asset.browser_download_url}`);
68
const req = await fetch(asset.browser_download_url, {
69
cache: "no-store",
70
-
headers: {
71
-
"User-Agent": userAgent
72
-
}
73
});
74
75
return [await req.arrayBuffer(), json.name];
···
79
logger.debug(`Downloading ${nightlyZipUrl}`);
80
const zipReq = await fetch(nightlyZipUrl, {
81
cache: "no-store",
82
-
headers: {
83
-
"User-Agent": userAgent
84
-
}
85
});
86
87
const refReq = await fetch(nightlyRefUrl, {
88
cache: "no-store",
89
-
headers: {
90
-
"User-Agent": userAgent
91
-
}
92
});
93
const ref = (await refReq.text()).split("\n")[0];
94
···
96
}
97
98
const [tar, ref] =
99
-
moonlightNode.branch === MoonlightBranch.STABLE
100
? await downloadStable()
101
-
: moonlightNode.branch === MoonlightBranch.NIGHTLY
102
? await downloadNightly()
103
: [null, null];
104
105
if (!tar || !ref) return;
106
107
-
const dist = moonlightNodeSandboxed.fs.join(moonlightNode.getMoonlightDir(), distDir);
108
if (await moonlightNodeSandboxed.fs.exists(dist)) await moonlightNodeSandboxed.fs.rmdir(dist);
109
await moonlightNodeSandboxed.fs.mkdir(dist);
110
···
122
}
123
124
logger.debug("Writing version file:", ref);
125
-
const versionFile = moonlightNodeSandboxed.fs.join(moonlightNode.getMoonlightDir(), installedVersionFile);
126
await moonlightNodeSandboxed.fs.writeFileString(versionFile, ref.trim());
127
128
logger.debug("Update extracted");
···
135
try {
136
const req = await fetch(repo, {
137
cache: "no-store",
138
-
headers: {
139
-
"User-Agent": userAgent
140
-
}
141
});
142
const json = await req.json();
143
ret[repo] = json;
···
152
async installExtension(manifest, url, repo) {
153
const req = await fetch(url, {
154
cache: "no-store",
155
-
headers: {
156
-
"User-Agent": userAgent
157
-
}
158
});
159
160
-
const dir = moonlightNode.getExtensionDir(manifest.id);
161
// remake it in case of updates
162
if (await moonlightNodeSandboxed.fs.exists(dir)) await moonlightNodeSandboxed.fs.rmdir(dir);
163
await moonlightNodeSandboxed.fs.mkdir(dir);
···
176
},
177
178
async deleteExtension(id) {
179
-
const dir = moonlightNode.getExtensionDir(id);
180
await moonlightNodeSandboxed.fs.rmdir(dir);
181
}
182
};
···
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";
···
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;
···
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;
···
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]> {
···
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];
···
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
···
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
···
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");
···
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;
···
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);
···
171
},
172
173
async deleteExtension(id) {
174
+
const dir = moonlightGlobal.getExtensionDir(id);
175
await moonlightNodeSandboxed.fs.rmdir(dir);
176
}
177
};
+99
-11
packages/core-extensions/src/moonbase/style.css
+99
-11
packages/core-extensions/src/moonbase/style.css
···
3
--moonbase-fg: #fffba6;
4
}
5
6
-
.moonbase-settings> :first-child {
7
margin-top: 0px;
8
}
9
10
textarea.moonbase-resizeable {
11
resize: vertical;
12
}
13
14
.moonbase-updates-notice {
15
background-color: var(--moonbase-bg);
16
color: var(--moonbase-fg);
17
line-height: unset;
18
height: 36px;
19
}
···
30
gap: 2px;
31
}
32
33
.moonbase-update-section {
34
background-color: var(--moonbase-bg);
35
--info-help-foreground: var(--moonbase-fg);
36
border: none !important;
37
color: var(--moonbase-fg);
38
-
39
-
display: flex;
40
-
flex-direction: row;
41
-
justify-content: space-between;
42
}
43
44
.moonbase-update-section button {
···
48
border-color: var(--moonbase-fg);
49
}
50
51
-
.moonbase-update-section-buttons {
52
display: flex;
53
flex-direction: row;
54
gap: 8px;
55
}
56
57
-
/* crash screen */
58
-
.moonbase-crash-wrapper>[class^="buttons_"] {
59
gap: 1rem;
60
}
61
···
106
box-sizing: border-box;
107
padding: 0;
108
font-family: var(--font-code);
109
-
font-size: .75rem;
110
line-height: 1rem;
111
margin: 6px;
112
white-space: pre-wrap;
113
background-clip: border-box;
114
115
-
&>code {
116
-
font-size: .875rem;
117
line-height: 1.125rem;
118
text-indent: 0;
119
white-space: pre-wrap;
···
179
line-height: 1.286;
180
font-weight: 400;
181
}
···
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
}
···
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 {
···
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
···
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;
···
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
+
}
+12
-2
packages/core-extensions/src/moonbase/types.ts
+12
-2
packages/core-extensions/src/moonbase/types.ts
···
1
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
2
-
import { DetectedExtension, ExtensionManifest } from "@moonlight-mod/types";
3
4
export type MoonbaseNatives = {
5
checkForMoonlightUpdate(): Promise<string | null>;
6
-
updateMoonlight(): Promise<void>;
7
8
fetchRepositories(repos: string[]): Promise<Record<string, RepositoryManifest[]>>;
9
installExtension(manifest: RepositoryManifest, url: string, repo: string): Promise<void>;
···
28
state: ExtensionState;
29
compat: ExtensionCompat;
30
hasUpdate: boolean;
31
};
32
33
export enum UpdateState {
···
36
Installed,
37
Failed
38
}
···
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>;
···
28
state: ExtensionState;
29
compat: ExtensionCompat;
30
hasUpdate: boolean;
31
+
changelog?: string;
32
+
settingsOverride?: ExtensionManifest["settings"];
33
};
34
35
export enum UpdateState {
···
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
+
}
+90
-9
packages/core-extensions/src/moonbase/webpackModules/crashScreen.tsx
+90
-9
packages/core-extensions/src/moonbase/webpackModules/crashScreen.tsx
···
1
import React from "@moonlight-mod/wp/react";
2
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
3
import { useStateFromStores, useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
4
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
5
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
6
import { RepositoryManifest, UpdateState } from "../types";
7
8
-
const { Button, TabBar } = Components;
9
-
const TabBarClasses = spacepack.findByCode(/tabBar:"tabBar_[a-z0-9]+",tabBarItem:"tabBarItem_[a-z0-9]+"/)[0].exports;
10
11
const logger = moonlight.getLogger("moonbase/crashScreen");
12
···
77
}}
78
>
79
{extensionButtonStrings[state]}
80
</Button>
81
</div>
82
</div>
···
94
};
95
});
96
97
return (
98
<div className="moonbase-crash-wrapper">
99
{action}
100
<TabBar
101
-
className={`${TabBarClasses.tabBar} moonbase-crash-tabs`}
102
type="top"
103
selectedItem={tab}
104
onItemSelect={(v) => setTab(v)}
105
>
106
-
<TabBar.Item className={TabBarClasses.tabBarItem} id="crash">
107
-
Crash Details
108
</TabBar.Item>
109
-
<TabBar.Item className={TabBarClasses.tabBarItem} id="extensions" disabled={updateCount === 0}>
110
-
{`Extension Updates (${updateCount})`}
111
</TabBar.Item>
112
</TabBar>
113
{tab === "crash" ? (
···
126
{updates.map(([id, ext]) => (
127
<ExtensionUpdateCard id={Number(id)} ext={ext} />
128
))}
129
</div>
130
) : null}
131
</div>
···
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
···
77
}}
78
>
79
{extensionButtonStrings[state]}
80
+
</Button>
81
+
</div>
82
+
</div>
83
+
);
84
+
}
85
+
86
+
function ExtensionDisableCard({ ext }: { ext: DetectedExtension }) {
87
+
async function disableWithDependents() {
88
+
const disable = new Set<string>();
89
+
disable.add(ext.id);
90
+
for (const [id, dependencies] of moonlightNode.processedExtensions.dependencyGraph) {
91
+
if (dependencies?.has(ext.id)) disable.add(id);
92
+
}
93
+
94
+
const config = structuredClone(moonlightNode.config);
95
+
for (const id in config.extensions) {
96
+
if (!disable.has(id)) continue;
97
+
if (typeof config.extensions[id] === "boolean") config.extensions[id] = false;
98
+
else (config.extensions[id] as ConfigExtension).enabled = false;
99
+
}
100
+
101
+
let msg = `Are you sure you want to disable "${ext.manifest.meta?.name ?? ext.id}"`;
102
+
if (disable.size > 1) {
103
+
msg += ` and its ${disable.size - 1} dependent${disable.size - 1 === 1 ? "" : "s"}`;
104
+
}
105
+
msg += "?";
106
+
107
+
if (confirm(msg)) {
108
+
await moonlightNode.writeConfig(config);
109
+
window.location.reload();
110
+
}
111
+
}
112
+
113
+
return (
114
+
<div className="moonbase-crash-extensionCard">
115
+
<div className="moonbase-crash-extensionCard-meta">
116
+
<div className="moonbase-crash-extensionCard-title">{ext.manifest.meta?.name ?? ext.id}</div>
117
+
<div className="moonbase-crash-extensionCard-version">{`v${ext.manifest.version ?? "???"}`}</div>
118
+
</div>
119
+
<div className="moonbase-crash-extensionCard-button">
120
+
<Button color={Button.Colors.RED} onClick={disableWithDependents}>
121
+
Disable
122
</Button>
123
</div>
124
</div>
···
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" ? (
···
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>
+29
-19
packages/core-extensions/src/moonbase/webpackModules/settings.tsx
+29
-19
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 } from "@moonlight-mod/wp/moonbase_ui";
5
-
6
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
7
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
8
-
9
-
import Update from "./ui/update";
10
-
11
-
const { MenuItem, Text, Breadcrumbs } = Components;
12
-
13
-
const Margins = spacepack.require("discord/styles/shared/Margins.css");
14
-
15
-
const { open } = spacepack.findByExports("setSection", "clearSubsection")[0].exports.Z;
16
17
const notice = {
18
stores: [MoonbaseSettingsStore],
19
element: () => {
20
// Require it here because lazy loading SUX
21
-
const SettingsNotice = spacepack.findByCode("onSaveButtonColor", "FocusRingScope")[0].exports.Z;
22
return (
23
<SettingsNotice
24
submitting={MoonbaseSettingsStore.submitting}
25
onReset={() => {
26
MoonbaseSettingsStore.reset();
27
}}
28
-
onSave={() => {
29
-
MoonbaseSettingsStore.writeConfig();
30
}}
31
/>
32
);
33
}
34
};
35
36
function addSection(id: string, name: string, element: React.FunctionComponent) {
37
-
settings.addSection(`moonbase-${id}`, name, element, null, -2, notice);
38
}
39
40
// FIXME: move to component types
···
51
);
52
}
53
54
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
55
-
settings.addHeader("Moonbase", -2);
56
57
-
for (const page of pages) {
58
addSection(page.id, page.name, () => {
59
const breadcrumbs = [
60
{ id: "moonbase", label: "Moonbase" },
···
71
{page.name}
72
</Breadcrumbs>
73
74
<Update />
75
76
<page.element />
···
78
);
79
});
80
}
81
} else {
82
-
settings.addSection("moonbase", "Moonbase", Moonbase, null, -2, notice);
83
84
settings.addSectionMenuItems(
85
"moonbase",
86
...pages.map((page, i) => (
87
-
<MenuItem key={page.id} id={`moonbase-${page.id}`} label={page.name} action={() => open("moonbase", i)} />
88
))
89
);
90
}
···
1
import settings from "@moonlight-mod/wp/settings_settings";
2
import React from "@moonlight-mod/wp/react";
3
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
4
+
import { Moonbase, pages, RestartAdviceMessage, Update } from "@moonlight-mod/wp/moonbase_ui";
5
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
6
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
7
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
8
+
import { Text, Breadcrumbs } from "@moonlight-mod/wp/discord/components/common/index";
9
+
import { MenuItem } from "@moonlight-mod/wp/contextMenu_contextMenu";
10
11
const notice = {
12
stores: [MoonbaseSettingsStore],
13
element: () => {
14
// Require it here because lazy loading SUX
15
+
const SettingsNotice = spacepack.require("discord/components/common/SettingsNotice").default;
16
return (
17
<SettingsNotice
18
submitting={MoonbaseSettingsStore.submitting}
19
onReset={() => {
20
MoonbaseSettingsStore.reset();
21
}}
22
+
onSave={async () => {
23
+
await MoonbaseSettingsStore.writeConfig();
24
}}
25
/>
26
);
27
}
28
};
29
30
+
const oldLocation = MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "oldLocation", false);
31
+
const position = oldLocation ? -2 : -9999;
32
+
33
function addSection(id: string, name: string, element: React.FunctionComponent) {
34
+
settings.addSection(`moonbase-${id}`, name, element, null, position, notice);
35
}
36
37
// FIXME: move to component types
···
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" },
···
73
{page.name}
74
</Breadcrumbs>
75
76
+
<RestartAdviceMessage />
77
<Update />
78
79
<page.element />
···
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
}
+262
-79
packages/core-extensions/src/moonbase/webpackModules/stores.ts
+262
-79
packages/core-extensions/src/moonbase/webpackModules/stores.ts
···
1
-
import { Config, ExtensionLoadSource } from "@moonlight-mod/types";
2
-
import { ExtensionState, MoonbaseExtension, MoonbaseNatives, RepositoryManifest } from "../types";
3
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
4
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
5
import getNatives from "../native";
6
import { mainRepo } from "@moonlight-mod/types/constants";
7
import { checkExtensionCompat, ExtensionCompat } from "@moonlight-mod/core/extension/loader";
8
import { CustomComponent } from "@moonlight-mod/types/coreExtensions/moonbase";
9
import { getConfigOption, setConfigOption } from "@moonlight-mod/core/util/config";
10
11
const logger = moonlight.getLogger("moonbase");
12
···
14
if (moonlightNode.isBrowser) natives = getNatives();
15
16
class MoonbaseSettingsStore extends Store<any> {
17
-
private origConfig: Config;
18
private config: Config;
19
private extensionIndex: number;
20
private configComponents: Record<string, Record<string, CustomComponent>> = {};
···
23
submitting: boolean;
24
installing: boolean;
25
26
newVersion: string | null;
27
shouldShowNotice: boolean;
28
29
extensions: { [id: number]: MoonbaseExtension };
30
updates: {
···
38
constructor() {
39
super(Dispatcher);
40
41
-
this.origConfig = moonlightNode.config;
42
-
this.config = this.clone(this.origConfig);
43
this.extensionIndex = 0;
44
45
this.modified = false;
···
62
};
63
}
64
65
-
natives!
66
-
.fetchRepositories(this.config.repositories)
67
-
.then((ret) => {
68
-
for (const [repo, exts] of Object.entries(ret)) {
69
-
try {
70
-
for (const ext of exts) {
71
-
const uniqueId = this.extensionIndex++;
72
-
const extensionData = {
73
-
id: ext.id,
74
-
uniqueId,
75
-
manifest: ext,
76
-
source: { type: ExtensionLoadSource.Normal, url: repo },
77
-
state: ExtensionState.NotDownloaded,
78
-
compat: ExtensionCompat.Compatible,
79
-
hasUpdate: false
80
-
};
81
82
-
// Don't present incompatible updates
83
-
if (checkExtensionCompat(ext) !== ExtensionCompat.Compatible) continue;
84
85
-
const existing = this.getExisting(extensionData);
86
-
if (existing != null) {
87
-
// Make sure the download URL is properly updated
88
-
for (const [id, e] of Object.entries(this.extensions)) {
89
-
if (e.id === ext.id && e.source.url === repo) {
90
-
this.extensions[parseInt(id)].manifest = {
91
-
...e.manifest,
92
-
download: ext.download
93
-
};
94
-
break;
95
-
}
96
-
}
97
98
-
if (this.hasUpdate(extensionData)) {
99
-
this.updates[existing.uniqueId] = {
100
-
version: ext.version!,
101
-
download: ext.download,
102
-
updateManifest: ext
103
-
};
104
-
existing.hasUpdate = true;
105
-
}
106
107
-
continue;
108
-
}
109
110
-
this.extensions[uniqueId] = extensionData;
111
-
}
112
-
} catch (e) {
113
-
logger.error(`Error processing repository ${repo}`, e);
114
}
115
}
116
117
-
this.emitChange();
118
-
})
119
-
.then(() =>
120
-
this.getExtensionConfigRaw("moonbase", "updateChecking", true)
121
-
? natives!.checkForMoonlightUpdate()
122
-
: new Promise<null>((resolve) => resolve(null))
123
-
)
124
-
.then((version) => {
125
-
this.newVersion = version;
126
-
this.emitChange();
127
-
})
128
-
.then(() => {
129
-
this.shouldShowNotice = this.newVersion != null || Object.keys(this.updates).length > 0;
130
-
this.emitChange();
131
-
});
132
}
133
134
private getExisting(ext: MoonbaseExtension) {
···
144
145
// Jank
146
private isModified() {
147
-
const orig = JSON.stringify(this.origConfig);
148
const curr = JSON.stringify(this.config);
149
return orig !== curr;
150
}
···
153
return this.submitting || this.installing;
154
}
155
156
showNotice() {
157
return this.modified;
158
}
···
192
193
getExtensionConfig<T>(uniqueId: number, key: string): T | undefined {
194
const ext = this.getExtension(uniqueId);
195
-
return getConfigOption(ext.id, key, this.config, ext.manifest);
196
}
197
198
getExtensionConfigRaw<T>(id: string, key: string, defaultValue: T | undefined): T | undefined {
···
203
204
getExtensionConfigName(uniqueId: number, key: string) {
205
const ext = this.getExtension(uniqueId);
206
-
return ext.manifest.settings?.[key]?.displayName ?? key;
207
}
208
209
getExtensionConfigDescription(uniqueId: number, key: string) {
210
const ext = this.getExtension(uniqueId);
211
-
return ext.manifest.settings?.[key]?.description;
212
}
213
214
setExtensionConfig(id: string, key: string, value: any) {
···
222
let val = this.config.extensions[ext.id];
223
224
if (val == null) {
225
-
this.config.extensions[ext.id] = { enabled };
226
this.modified = this.isModified();
227
this.emitChange();
228
return;
···
239
this.emitChange();
240
}
241
242
async installExtension(uniqueId: number) {
243
const ext = this.getExtension(uniqueId);
244
if (!("download" in ext.manifest)) {
···
254
this.extensions[uniqueId].state = ExtensionState.Disabled;
255
}
256
257
-
if (update != null) this.extensions[uniqueId].compat = checkExtensionCompat(update.updateManifest);
258
259
delete this.updates[uniqueId];
260
} catch (e) {
···
262
}
263
264
this.installing = false;
265
this.emitChange();
266
}
267
···
293
const aRank = this.getRank(a);
294
const bRank = this.getRank(b);
295
if (aRank === bRank) {
296
-
const repoIndex = this.config.repositories.indexOf(a.source.url!);
297
-
const otherRepoIndex = this.config.repositories.indexOf(b.source.url!);
298
return repoIndex - otherRepoIndex;
299
} else {
300
return bRank - aRank;
···
318
}
319
320
this.installing = false;
321
this.emitChange();
322
}
323
324
async updateMoonlight() {
325
-
await natives.updateMoonlight();
326
}
327
328
getConfigOption<K extends keyof Config>(key: K): Config[K] {
···
349
return this.configComponents[ext]?.[name];
350
}
351
352
-
writeConfig() {
353
-
this.submitting = true;
354
355
-
moonlightNode.writeConfig(this.config);
356
-
this.origConfig = this.clone(this.config);
357
358
-
this.submitting = false;
359
this.modified = false;
360
this.emitChange();
361
}
362
363
reset() {
364
this.submitting = false;
365
this.modified = false;
366
-
this.config = this.clone(this.origConfig);
367
this.emitChange();
368
}
369
370
// 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 { NodeEventType } from "@moonlight-mod/types/core/event";
17
import { getConfigOption, setConfigOption } from "@moonlight-mod/core/util/config";
18
+
import diff from "microdiff";
19
20
const logger = moonlight.getLogger("moonbase");
21
···
23
if (moonlightNode.isBrowser) natives = getNatives();
24
25
class MoonbaseSettingsStore extends Store<any> {
26
+
private initialConfig: Config;
27
+
private savedConfig: Config;
28
private config: Config;
29
private extensionIndex: number;
30
private configComponents: Record<string, Record<string, CustomComponent>> = {};
···
33
submitting: boolean;
34
installing: boolean;
35
36
+
#updateState = UpdateState.Ready;
37
+
get updateState() {
38
+
return this.#updateState;
39
+
}
40
newVersion: string | null;
41
shouldShowNotice: boolean;
42
+
43
+
restartAdvice = RestartAdvice.NotNeeded;
44
45
extensions: { [id: number]: MoonbaseExtension };
46
updates: {
···
54
constructor() {
55
super(Dispatcher);
56
57
+
this.initialConfig = moonlightNode.config;
58
+
this.savedConfig = moonlightNode.config;
59
+
this.config = this.clone(this.savedConfig);
60
this.extensionIndex = 0;
61
62
this.modified = false;
···
79
};
80
}
81
82
+
// This is async but we're calling it without
83
+
this.checkUpdates();
84
85
+
// Update our state if another extension edited the config programatically
86
+
moonlightNode.events.addEventListener(NodeEventType.ConfigSaved, (config) => {
87
+
if (!this.submitting) {
88
+
this.config = this.clone(config);
89
+
// NOTE: This is also async but we're calling it without
90
+
this.processConfigChanged();
91
+
}
92
+
});
93
+
}
94
+
95
+
async checkUpdates() {
96
+
await Promise.all([this.checkExtensionUpdates(), this.checkMoonlightUpdates()]);
97
+
this.shouldShowNotice = this.newVersion != null || Object.keys(this.updates).length > 0;
98
+
this.emitChange();
99
+
}
100
+
101
+
private async checkExtensionUpdates() {
102
+
const repositories = await natives!.fetchRepositories(this.savedConfig.repositories);
103
+
104
+
// Reset update state
105
+
for (const id in this.extensions) {
106
+
const ext = this.extensions[id];
107
+
ext.hasUpdate = false;
108
+
ext.changelog = undefined;
109
+
}
110
+
this.updates = {};
111
112
+
for (const [repo, exts] of Object.entries(repositories)) {
113
+
for (const ext of exts) {
114
+
const uniqueId = this.extensionIndex++;
115
+
const extensionData = {
116
+
id: ext.id,
117
+
uniqueId,
118
+
manifest: ext,
119
+
source: { type: ExtensionLoadSource.Normal, url: repo },
120
+
state: ExtensionState.NotDownloaded,
121
+
compat: ExtensionCompat.Compatible,
122
+
hasUpdate: false
123
+
};
124
125
+
// Don't present incompatible updates
126
+
if (checkExtensionCompat(ext) !== ExtensionCompat.Compatible) continue;
127
128
+
const existing = this.getExisting(extensionData);
129
+
if (existing != null) {
130
+
// Make sure the download URL is properly updated
131
+
existing.manifest = {
132
+
...existing.manifest,
133
+
download: ext.download
134
+
};
135
136
+
if (this.hasUpdate(extensionData)) {
137
+
this.updates[existing.uniqueId] = {
138
+
version: ext.version!,
139
+
download: ext.download,
140
+
updateManifest: ext
141
+
};
142
+
existing.hasUpdate = true;
143
+
existing.changelog = ext.meta?.changelog;
144
}
145
+
} else {
146
+
this.extensions[uniqueId] = extensionData;
147
}
148
+
}
149
+
}
150
+
}
151
152
+
private async checkMoonlightUpdates() {
153
+
this.newVersion = this.getExtensionConfigRaw("moonbase", "updateChecking", true)
154
+
? await natives!.checkForMoonlightUpdate()
155
+
: null;
156
}
157
158
private getExisting(ext: MoonbaseExtension) {
···
168
169
// Jank
170
private isModified() {
171
+
const orig = JSON.stringify(this.savedConfig);
172
const curr = JSON.stringify(this.config);
173
return orig !== curr;
174
}
···
177
return this.submitting || this.installing;
178
}
179
180
+
// Required for the settings store contract
181
showNotice() {
182
return this.modified;
183
}
···
217
218
getExtensionConfig<T>(uniqueId: number, key: string): T | undefined {
219
const ext = this.getExtension(uniqueId);
220
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
221
+
return getConfigOption(ext.id, key, this.config, settings);
222
}
223
224
getExtensionConfigRaw<T>(id: string, key: string, defaultValue: T | undefined): T | undefined {
···
229
230
getExtensionConfigName(uniqueId: number, key: string) {
231
const ext = this.getExtension(uniqueId);
232
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
233
+
return settings?.[key]?.displayName ?? key;
234
}
235
236
getExtensionConfigDescription(uniqueId: number, key: string) {
237
const ext = this.getExtension(uniqueId);
238
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
239
+
return settings?.[key]?.description;
240
}
241
242
setExtensionConfig(id: string, key: string, value: any) {
···
250
let val = this.config.extensions[ext.id];
251
252
if (val == null) {
253
+
this.config.extensions[ext.id] = enabled;
254
this.modified = this.isModified();
255
this.emitChange();
256
return;
···
267
this.emitChange();
268
}
269
270
+
dismissAllExtensionUpdates() {
271
+
for (const id in this.extensions) {
272
+
this.extensions[id].hasUpdate = false;
273
+
}
274
+
this.emitChange();
275
+
}
276
+
277
+
async updateAllExtensions() {
278
+
for (const id of Object.keys(this.updates)) {
279
+
try {
280
+
await this.installExtension(parseInt(id));
281
+
} catch (e) {
282
+
logger.error("Error bulk updating extension", id, e);
283
+
}
284
+
}
285
+
}
286
+
287
async installExtension(uniqueId: number) {
288
const ext = this.getExtension(uniqueId);
289
if (!("download" in ext.manifest)) {
···
299
this.extensions[uniqueId].state = ExtensionState.Disabled;
300
}
301
302
+
if (update != null) {
303
+
const existing = this.extensions[uniqueId];
304
+
existing.settingsOverride = update.updateManifest.settings;
305
+
existing.compat = checkExtensionCompat(update.updateManifest);
306
+
existing.manifest = update.updateManifest;
307
+
existing.changelog = update.updateManifest.meta?.changelog;
308
+
}
309
310
delete this.updates[uniqueId];
311
} catch (e) {
···
313
}
314
315
this.installing = false;
316
+
this.restartAdvice = this.#computeRestartAdvice();
317
this.emitChange();
318
}
319
···
345
const aRank = this.getRank(a);
346
const bRank = this.getRank(b);
347
if (aRank === bRank) {
348
+
const repoIndex = this.savedConfig.repositories.indexOf(a.source.url!);
349
+
const otherRepoIndex = this.savedConfig.repositories.indexOf(b.source.url!);
350
return repoIndex - otherRepoIndex;
351
} else {
352
return bRank - aRank;
···
370
}
371
372
this.installing = false;
373
+
this.restartAdvice = this.#computeRestartAdvice();
374
this.emitChange();
375
}
376
377
async updateMoonlight() {
378
+
this.#updateState = UpdateState.Working;
379
+
this.emitChange();
380
+
381
+
await natives
382
+
.updateMoonlight()
383
+
.then(() => (this.#updateState = UpdateState.Installed))
384
+
.catch((e) => {
385
+
logger.error(e);
386
+
this.#updateState = UpdateState.Failed;
387
+
});
388
+
389
+
this.emitChange();
390
}
391
392
getConfigOption<K extends keyof Config>(key: K): Config[K] {
···
413
return this.configComponents[ext]?.[name];
414
}
415
416
+
#computeRestartAdvice() {
417
+
// If moonlight update needs a restart, always hide advice.
418
+
if (this.#updateState === UpdateState.Installed) return RestartAdvice.NotNeeded;
419
+
420
+
const i = this.initialConfig; // Initial config, from startup
421
+
const n = this.config; // New config about to be saved
422
+
423
+
let returnedAdvice = RestartAdvice.NotNeeded;
424
+
const updateAdvice = (r: RestartAdvice) => (returnedAdvice < r ? (returnedAdvice = r) : returnedAdvice);
425
426
+
// Top-level keys, repositories is not needed here because Moonbase handles it.
427
+
if (i.patchAll !== n.patchAll) updateAdvice(RestartAdvice.ReloadNeeded);
428
+
if (i.loggerLevel !== n.loggerLevel) updateAdvice(RestartAdvice.ReloadNeeded);
429
+
if (diff(i.devSearchPaths ?? [], n.devSearchPaths ?? [], { cyclesFix: false }).length !== 0)
430
+
return updateAdvice(RestartAdvice.RestartNeeded);
431
432
+
// Extension specific logic
433
+
for (const id in n.extensions) {
434
+
// Installed extension (might not be detected yet)
435
+
const ext = Object.values(this.extensions).find((e) => e.id === id && e.state !== ExtensionState.NotDownloaded);
436
+
// Installed and detected extension
437
+
const detected = moonlightNode.extensions.find((e) => e.id === id);
438
+
439
+
// If it's not installed at all, we don't care
440
+
if (!ext) continue;
441
+
442
+
const initState = i.extensions[id];
443
+
const newState = n.extensions[id];
444
+
445
+
const newEnabled = typeof newState === "boolean" ? newState : newState.enabled;
446
+
// If it's enabled but not detected yet, restart.
447
+
if (newEnabled && !detected) {
448
+
return updateAdvice(RestartAdvice.RestartNeeded);
449
+
}
450
+
451
+
// Toggling extensions specifically wants to rely on the initial state,
452
+
// that's what was considered when loading extensions.
453
+
const initEnabled = initState && (typeof initState === "boolean" ? initState : initState.enabled);
454
+
if (initEnabled !== newEnabled || detected?.manifest.version !== ext.manifest.version) {
455
+
// If we have the extension locally, we confidently know if it has host/preload scripts.
456
+
// If not, we have to respect the environment specified in the manifest.
457
+
// If that is the default, we can't know what's needed.
458
+
459
+
if (detected?.scripts.hostPath || detected?.scripts.nodePath) {
460
+
return updateAdvice(RestartAdvice.RestartNeeded);
461
+
}
462
+
463
+
switch (ext.manifest.environment) {
464
+
case ExtensionEnvironment.Both:
465
+
case ExtensionEnvironment.Web:
466
+
updateAdvice(RestartAdvice.ReloadNeeded);
467
+
continue;
468
+
case ExtensionEnvironment.Desktop:
469
+
return updateAdvice(RestartAdvice.RestartNeeded);
470
+
default:
471
+
updateAdvice(RestartAdvice.ReloadNeeded);
472
+
continue;
473
+
}
474
+
}
475
+
476
+
const initConfig = typeof initState === "boolean" ? {} : { ...initState?.config };
477
+
const newConfig = typeof newState === "boolean" ? {} : { ...newState?.config };
478
+
479
+
const def = ext.manifest.settings;
480
+
if (!def) continue;
481
+
482
+
for (const key in def) {
483
+
const defaultValue = def[key].default;
484
+
485
+
initConfig[key] ??= defaultValue;
486
+
newConfig[key] ??= defaultValue;
487
+
}
488
+
489
+
const changedKeys = diff(initConfig, newConfig, { cyclesFix: false }).map((c) => c.path[0]);
490
+
for (const key in def) {
491
+
if (!changedKeys.includes(key)) continue;
492
+
493
+
const advice = def[key].advice;
494
+
switch (advice) {
495
+
case ExtensionSettingsAdvice.None:
496
+
updateAdvice(RestartAdvice.NotNeeded);
497
+
continue;
498
+
case ExtensionSettingsAdvice.Reload:
499
+
updateAdvice(RestartAdvice.ReloadNeeded);
500
+
continue;
501
+
case ExtensionSettingsAdvice.Restart:
502
+
updateAdvice(RestartAdvice.RestartNeeded);
503
+
continue;
504
+
default:
505
+
updateAdvice(RestartAdvice.ReloadSuggested);
506
+
}
507
+
}
508
+
}
509
+
510
+
return returnedAdvice;
511
+
}
512
+
513
+
async writeConfig() {
514
+
try {
515
+
this.submitting = true;
516
+
this.emitChange();
517
+
518
+
await moonlightNode.writeConfig(this.config);
519
+
await this.processConfigChanged();
520
+
} finally {
521
+
this.submitting = false;
522
+
this.emitChange();
523
+
}
524
+
}
525
+
526
+
private async processConfigChanged() {
527
+
this.savedConfig = this.clone(this.config);
528
+
this.restartAdvice = this.#computeRestartAdvice();
529
this.modified = false;
530
+
531
+
const modifiedRepos = diff(this.savedConfig.repositories, this.config.repositories);
532
+
if (modifiedRepos.length !== 0) await this.checkUpdates();
533
+
534
this.emitChange();
535
}
536
537
reset() {
538
this.submitting = false;
539
this.modified = false;
540
+
this.config = this.clone(this.savedConfig);
541
this.emitChange();
542
+
}
543
+
544
+
restartDiscord() {
545
+
if (moonlightNode.isBrowser) {
546
+
window.location.reload();
547
+
} else {
548
+
// @ts-expect-error TODO: DiscordNative
549
+
window.DiscordNative.app.relaunch();
550
+
}
551
}
552
553
// Required because electron likes to make it immutable sometimes.
+47
packages/core-extensions/src/moonbase/webpackModules/ui/HelpMessage.tsx
+47
packages/core-extensions/src/moonbase/webpackModules/ui/HelpMessage.tsx
···
···
1
+
import React from "@moonlight-mod/wp/react";
2
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
3
+
import { Text } from "@moonlight-mod/wp/discord/components/common/index";
4
+
import HelpMessageClasses from "@moonlight-mod/wp/discord/components/common/HelpMessage.css";
5
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
6
+
7
+
// reimpl of HelpMessage but with a custom icon
8
+
export default function HelpMessage({
9
+
className,
10
+
text,
11
+
icon,
12
+
children,
13
+
type = "info"
14
+
}: {
15
+
className?: string;
16
+
text: string;
17
+
icon: React.ComponentType<any>;
18
+
type?: "warning" | "positive" | "error" | "info";
19
+
children?: React.ReactNode;
20
+
}) {
21
+
return (
22
+
<div
23
+
className={`${Margins.marginBottom20} ${HelpMessageClasses[type]} ${HelpMessageClasses.container} moonbase-help-message ${className}`}
24
+
>
25
+
<Flex direction={Flex.Direction.HORIZONTAL}>
26
+
<div
27
+
className={HelpMessageClasses.iconDiv}
28
+
style={{
29
+
alignItems: "center"
30
+
}}
31
+
>
32
+
{React.createElement(icon, {
33
+
size: "sm",
34
+
color: "currentColor",
35
+
className: HelpMessageClasses.icon
36
+
})}
37
+
</div>
38
+
39
+
<Text variant="text-sm/medium" color="currentColor" className={HelpMessageClasses.text}>
40
+
{text}
41
+
</Text>
42
+
43
+
{children}
44
+
</Flex>
45
+
</div>
46
+
);
47
+
}
+43
packages/core-extensions/src/moonbase/webpackModules/ui/RestartAdvice.tsx
+43
packages/core-extensions/src/moonbase/webpackModules/ui/RestartAdvice.tsx
···
···
1
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
2
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
3
+
import { Button, CircleWarningIcon } from "@moonlight-mod/wp/discord/components/common/index";
4
+
import React from "@moonlight-mod/wp/react";
5
+
import { RestartAdvice } from "../../types";
6
+
import HelpMessage from "./HelpMessage";
7
+
8
+
const strings: Record<RestartAdvice, string> = {
9
+
[RestartAdvice.NotNeeded]: "how did you even",
10
+
[RestartAdvice.ReloadSuggested]: "A reload might be needed to apply some of the changed options.",
11
+
[RestartAdvice.ReloadNeeded]: "A reload is needed to apply some of the changed options.",
12
+
[RestartAdvice.RestartNeeded]: "A restart is needed to apply some of the changed options."
13
+
};
14
+
15
+
const buttonStrings: Record<RestartAdvice, string> = {
16
+
[RestartAdvice.NotNeeded]: "huh?",
17
+
[RestartAdvice.ReloadSuggested]: "Reload",
18
+
[RestartAdvice.ReloadNeeded]: "Reload",
19
+
[RestartAdvice.RestartNeeded]: "Restart"
20
+
};
21
+
22
+
const actions: Record<RestartAdvice, () => void> = {
23
+
[RestartAdvice.NotNeeded]: () => {},
24
+
[RestartAdvice.ReloadSuggested]: () => window.location.reload(),
25
+
[RestartAdvice.ReloadNeeded]: () => window.location.reload(),
26
+
[RestartAdvice.RestartNeeded]: () => MoonbaseSettingsStore.restartDiscord()
27
+
};
28
+
29
+
export default function RestartAdviceMessage() {
30
+
const restartAdvice = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.restartAdvice);
31
+
32
+
if (restartAdvice === RestartAdvice.NotNeeded) return null;
33
+
34
+
return (
35
+
<div className="moonbase-help-message-sticky">
36
+
<HelpMessage text={strings[restartAdvice]} icon={CircleWarningIcon} type="warning">
37
+
<Button color={Button.Colors.YELLOW} size={Button.Sizes.TINY} onClick={actions[restartAdvice]}>
38
+
{buttonStrings[restartAdvice]}
39
+
</Button>
40
+
</HelpMessage>
41
+
</div>
42
+
);
43
+
}
+110
packages/core-extensions/src/moonbase/webpackModules/ui/about.tsx
+110
packages/core-extensions/src/moonbase/webpackModules/ui/about.tsx
···
···
1
+
import {
2
+
Text,
3
+
useThemeContext,
4
+
Button,
5
+
AngleBracketsIcon,
6
+
BookCheckIcon,
7
+
ClydeIcon
8
+
} from "@moonlight-mod/wp/discord/components/common/index";
9
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
10
+
import React from "@moonlight-mod/wp/react";
11
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
12
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
13
+
14
+
const wordmark = "https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/heads/main/img/wordmark.png";
15
+
const wordmarkLight =
16
+
"https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/heads/main/img/wordmark-light.png";
17
+
18
+
function parse(str: string) {
19
+
return MarkupUtils.parse(str, true, {
20
+
allowHeading: true,
21
+
allowLinks: true,
22
+
allowList: true
23
+
});
24
+
}
25
+
26
+
function Dev({ name, picture, link }: { name: string; picture: string; link: string }) {
27
+
return (
28
+
<Button onClick={() => window.open(link)} color={Button.Colors.PRIMARY} className="moonbase-dev">
29
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
30
+
<img src={picture} alt={name} className="moonbase-dev-avatar" />
31
+
32
+
<Text variant="text-md/semibold">{name}</Text>
33
+
</Flex>
34
+
</Button>
35
+
);
36
+
}
37
+
38
+
function IconButton({
39
+
text,
40
+
link,
41
+
icon,
42
+
openInClient
43
+
}: {
44
+
text: string;
45
+
link: string;
46
+
icon: React.FC<any>;
47
+
openInClient?: boolean;
48
+
}) {
49
+
return (
50
+
<Button
51
+
onClick={() => {
52
+
if (openInClient) {
53
+
try {
54
+
const { handleClick } = spacepack.require("discord/utils/MaskedLinkUtils");
55
+
handleClick({ href: link });
56
+
} catch {
57
+
window.open(link);
58
+
}
59
+
} else {
60
+
// Will open externally in the user's browser
61
+
window.open(link);
62
+
}
63
+
}}
64
+
>
65
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
66
+
{React.createElement(icon, {
67
+
size: "sm",
68
+
color: "currentColor"
69
+
})}
70
+
{text}
71
+
</Flex>
72
+
</Button>
73
+
);
74
+
}
75
+
76
+
export default function AboutPage() {
77
+
const darkTheme = useThemeContext()?.theme !== "light";
78
+
79
+
return (
80
+
<Flex direction={Flex.Direction.VERTICAL} align={Flex.Align.CENTER} className="moonbase-about-page">
81
+
<img src={darkTheme ? wordmarkLight : wordmark} alt="moonlight wordmark" className="moonbase-wordmark" />
82
+
83
+
<Text variant="heading-lg/medium">created by:</Text>
84
+
<div className="moonbase-devs">
85
+
<Dev name="Cynosphere" picture="https://github.com/Cynosphere.png" link="https://github.com/Cynosphere" />
86
+
<Dev name="NotNite" picture="https://github.com/NotNite.png" link="https://github.com/NotNite" />
87
+
<Dev name="adryd" picture="https://github.com/adryd325.png" link="https://github.com/adryd325" />
88
+
<Dev name="redstonekasi" picture="https://github.com/redstonekasi.png" link="https://github.com/redstonekasi" />
89
+
</div>
90
+
91
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
92
+
<IconButton text="View source" icon={AngleBracketsIcon} link="https://github.com/moonlight-mod/moonlight" />
93
+
<IconButton text="Open the docs" icon={BookCheckIcon} link="https://moonlight-mod.github.io/" />
94
+
<IconButton text="Join the server" icon={ClydeIcon} link="https://discord.gg/FdZBTFCP6F" openInClient={true} />
95
+
</Flex>
96
+
97
+
<Flex direction={Flex.Direction.VERTICAL} align={Flex.Align.START}>
98
+
<Text variant="text-sm/normal">
99
+
{parse(`moonlight \`${window.moonlight.version}\` on \`${window.moonlight.branch}\``)}
100
+
</Text>
101
+
102
+
<Text variant="text-sm/normal">
103
+
{parse(
104
+
"moonlight is licensed under the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.html) (`LGPL-3.0-or-later`)."
105
+
)}
106
+
</Text>
107
+
</Flex>
108
+
</Flex>
109
+
);
110
+
}
+14
-13
packages/core-extensions/src/moonbase/webpackModules/ui/config/index.tsx
+14
-13
packages/core-extensions/src/moonbase/webpackModules/ui/config/index.tsx
···
16
Clickable
17
} from "@moonlight-mod/wp/discord/components/common/index";
18
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
19
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
20
21
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
22
23
-
const FormClasses = spacepack.findByCode("dividerDefault:")[0].exports;
24
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
25
-
26
-
let RemoveButtonClasses: any;
27
spacepack
28
.lazyLoad(
29
"renderArtisanalHack",
30
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"GuildSettings"/,
31
/webpackId:(\d+),name:"GuildSettings"/
32
)
33
-
.then(() => (RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0].exports));
34
-
35
-
// FIXME: type component keys
36
-
const { CircleXIcon } = Components;
37
38
function RemoveEntryButton({ onClick }: { onClick: () => void }) {
39
return (
40
-
<div className={RemoveButtonClasses.removeButtonContainer}>
41
<Tooltip text="Remove entry" position="top">
42
{(props: any) => (
43
-
<Clickable {...props} className={RemoveButtonClasses.removeButton} onClick={onClick}>
44
<CircleXIcon width={24} height={24} />
45
</Clickable>
46
)}
···
121
<FormText className={Margins.marginBottom4}>A list of remote repositories to display extensions from</FormText>
122
<ArrayFormItem config="repositories" />
123
</FormItem>
124
-
<FormDivider className={FormClasses.dividerDefault} />
125
<FormItem title="Extension search paths" className={Margins.marginTop20}>
126
<FormText className={Margins.marginBottom4}>
127
A list of local directories to search for built extensions
128
</FormText>
129
<ArrayFormItem config="devSearchPaths" />
130
</FormItem>
131
-
<FormDivider className={FormClasses.dividerDefault} />
132
<FormSwitch
133
className={Margins.marginTop20}
134
value={MoonbaseSettingsStore.getConfigOption("patchAll") ?? false}
···
16
Clickable
17
} from "@moonlight-mod/wp/discord/components/common/index";
18
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
19
+
import { CircleXIcon } from "@moonlight-mod/wp/discord/components/common/index";
20
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
21
+
import FormSwitchClasses from "@moonlight-mod/wp/discord/components/common/FormSwitch.css";
22
23
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
24
25
+
let GuildSettingsRoleEditClasses: any;
26
spacepack
27
.lazyLoad(
28
"renderArtisanalHack",
29
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"GuildSettings"/,
30
/webpackId:(\d+),name:"GuildSettings"/
31
)
32
+
.then(
33
+
() =>
34
+
(GuildSettingsRoleEditClasses = spacepack.require(
35
+
"discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"
36
+
))
37
+
);
38
39
function RemoveEntryButton({ onClick }: { onClick: () => void }) {
40
return (
41
+
<div className={GuildSettingsRoleEditClasses.removeButtonContainer}>
42
<Tooltip text="Remove entry" position="top">
43
{(props: any) => (
44
+
<Clickable {...props} className={GuildSettingsRoleEditClasses.removeButton} onClick={onClick}>
45
<CircleXIcon width={24} height={24} />
46
</Clickable>
47
)}
···
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}
+176
-86
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
+176
-86
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
···
1
import { ExtensionState } from "../../../types";
2
-
import { constants, ExtensionLoadSource } from "@moonlight-mod/types";
3
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
4
-
5
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
6
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
7
import React from "@moonlight-mod/wp/react";
8
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
9
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
10
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
11
-
import IntegrationCard from "@moonlight-mod/wp/discord/modules/guild_settings/IntegrationCard.css";
12
-
13
import ExtensionInfo from "./info";
14
import Settings from "./settings";
15
-
import { doBuiltinExtensionPopup, doMissingExtensionPopup } from "./popup";
16
17
export enum ExtensionPage {
18
Info,
19
Description,
20
Settings
21
}
22
23
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
24
-
25
-
const { BeakerIcon, DownloadIcon, TrashIcon, AngleBracketsIcon, CircleWarningIcon, Tooltip } = Components;
26
-
27
-
const PanelButton = spacepack.findByCode("Masks.PANEL_BUTTON")[0].exports.Z;
28
-
const TabBarClasses = spacepack.findByExports("tabBar", "tabBarItem", "headerContentWrapper")[0].exports;
29
-
const MarkupClasses = spacepack.findByExports("markup", "inlineFormat")[0].exports;
30
-
31
-
const BuildOverrideClasses = spacepack.findByExports("disabledButtonOverride")[0].exports;
32
-
33
const COMPAT_TEXT_MAP: Record<ExtensionCompat, string> = {
34
[ExtensionCompat.Compatible]: "huh?",
35
[ExtensionCompat.InvalidApiLevel]: "Incompatible API level",
36
[ExtensionCompat.InvalidEnvironment]: "Incompatible platform"
37
};
38
39
-
export default function ExtensionCard({ uniqueId }: { uniqueId: number }) {
40
-
const [tab, setTab] = React.useState(ExtensionPage.Info);
41
-
const [restartNeeded, setRestartNeeded] = React.useState(false);
42
43
const { ext, enabled, busy, update, conflicting } = useStateFromStores([MoonbaseSettingsStore], () => {
44
return {
45
ext: MoonbaseSettingsStore.getExtension(uniqueId),
···
50
};
51
});
52
53
-
// Why it work like that :sob:
54
-
if (ext == null) return <></>;
55
-
56
-
const { Card, Text, FormSwitch, TabBar, Button } = Components;
57
58
const tagline = ext.manifest?.meta?.tagline;
59
-
const settings = ext.manifest?.settings;
60
const description = ext.manifest?.meta?.description;
61
const enabledDependants = useStateFromStores([MoonbaseSettingsStore], () =>
62
Object.keys(MoonbaseSettingsStore.extensions)
63
.filter((uniqueId) => {
64
const potentialDependant = MoonbaseSettingsStore.getExtension(parseInt(uniqueId));
65
66
return (
67
-
potentialDependant.manifest.dependencies?.includes(ext.id) &&
68
MoonbaseSettingsStore.getExtensionEnabled(parseInt(uniqueId))
69
);
70
})
···
72
);
73
const implicitlyEnabled = enabledDependants.length > 0;
74
75
-
return (
76
-
<Card editable={true} className={IntegrationCard.card}>
77
-
<div className={IntegrationCard.cardHeader}>
78
<Flex direction={Flex.Direction.VERTICAL}>
79
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER}>
80
<Text variant="text-md/semibold">{ext.manifest?.meta?.name ?? ext.id}</Text>
81
{ext.source.type === ExtensionLoadSource.Developer && (
82
<Tooltip text="This is a local extension" position="top">
83
-
{(props: any) => <BeakerIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
84
</Tooltip>
85
)}
86
</Flex>
···
97
gap: "1rem"
98
}}
99
>
100
-
{ext.manifest.meta?.source != null && (
101
-
<PanelButton
102
-
icon={AngleBracketsIcon}
103
-
tooltipText="View source"
104
-
onClick={() => {
105
-
window.open(ext.manifest.meta!.source);
106
-
}}
107
-
/>
108
-
)}
109
-
110
{ext.state === ExtensionState.NotDownloaded ? (
111
-
<Tooltip text={COMPAT_TEXT_MAP[ext.compat]} shouldShow={ext.compat !== ExtensionCompat.Compatible}>
112
{(props: any) => (
113
<Button
114
{...props}
···
120
const deps = await MoonbaseSettingsStore.getDependencies(uniqueId);
121
if (deps != null) {
122
await doMissingExtensionPopup(deps);
123
}
124
}}
125
>
···
145
tooltipText="Update"
146
onClick={() => {
147
MoonbaseSettingsStore.installExtension(uniqueId);
148
-
setRestartNeeded(true);
149
}}
150
-
/>
151
-
)}
152
-
153
-
{restartNeeded && (
154
-
<PanelButton
155
-
icon={() => <CircleWarningIcon color={Components.tokens.colors.STATUS_DANGER} />}
156
-
onClick={() => window.location.reload()}
157
-
tooltipText="You will need to reload/restart your client for this extension to work properly."
158
/>
159
)}
160
···
163
disabled={implicitlyEnabled || ext.compat !== ExtensionCompat.Compatible}
164
hideBorder={true}
165
style={{ marginBottom: "0px" }}
166
tooltipNote={
167
-
ext.compat !== ExtensionCompat.Compatible
168
-
? COMPAT_TEXT_MAP[ext.compat]
169
-
: implicitlyEnabled
170
-
? `This extension is a dependency of the following enabled extension${
171
-
enabledDependants.length > 1 ? "s" : ""
172
-
}: ${enabledDependants.map((a) => a.manifest.meta?.name ?? a.id).join(", ")}`
173
-
: undefined
174
}
175
onChange={() => {
176
const toggle = () => {
177
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, !enabled);
178
-
setRestartNeeded(true);
179
};
180
181
if (enabled && constants.builtinExtensions.includes(ext.id)) {
182
-
doBuiltinExtensionPopup(uniqueId, toggle);
183
} else {
184
toggle();
185
}
···
192
</div>
193
194
<div>
195
-
{(description != null || settings != null) && (
196
-
<TabBar
197
-
selectedItem={tab}
198
-
type="top"
199
-
onItemSelect={setTab}
200
-
className={TabBarClasses.tabBar}
201
-
style={{
202
-
padding: "0 20px"
203
-
}}
204
-
>
205
-
<TabBar.Item className={TabBarClasses.tabBarItem} id={ExtensionPage.Info}>
206
-
Info
207
-
</TabBar.Item>
208
209
-
{description != null && (
210
-
<TabBar.Item className={TabBarClasses.tabBarItem} id={ExtensionPage.Description}>
211
-
Description
212
-
</TabBar.Item>
213
-
)}
214
215
-
{settings != null && (
216
-
<TabBar.Item className={TabBarClasses.tabBarItem} id={ExtensionPage.Settings}>
217
-
Settings
218
-
</TabBar.Item>
219
-
)}
220
-
</TabBar>
221
)}
222
223
<Flex
224
justify={Flex.Justify.START}
225
wrap={Flex.Wrap.WRAP}
226
style={{
227
-
padding: "16px 16px"
228
}}
229
>
230
-
{tab === ExtensionPage.Info && <ExtensionInfo ext={ext} />}
231
{tab === ExtensionPage.Description && (
232
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ width: "100%" }}>
233
{MarkupUtils.parse(description ?? "*No description*", true, {
···
237
})}
238
</Text>
239
)}
240
-
{tab === ExtensionPage.Settings && <Settings ext={ext} />}
241
</Flex>
242
</div>
243
</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),
···
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
})
···
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>
···
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}
···
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
>
···
194
tooltipText="Update"
195
onClick={() => {
196
MoonbaseSettingsStore.installExtension(uniqueId);
197
}}
198
/>
199
)}
200
···
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
}
···
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, {
···
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>
+91
-45
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/filterBar.tsx
+91
-45
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 * as React from "@moonlight-mod/wp/react";
5
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
···
11
Popout,
12
Dialog,
13
Menu,
14
-
MenuGroup,
15
-
MenuCheckboxItem,
16
-
MenuItem
17
} from "@moonlight-mod/wp/discord/components/common/index";
18
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
19
20
export enum Filter {
21
Core = 1 << 0,
···
25
Disabled = 1 << 4,
26
Installed = 1 << 5,
27
Repository = 1 << 6,
28
-
Incompatible = 1 << 7
29
}
30
export const defaultFilter = 127 as Filter;
31
32
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
33
-
const SortMenuClasses = spacepack.findByCode("container:", "clearText:")[0].exports;
34
-
35
-
let FilterDialogClasses: any;
36
-
let FilterBarClasses: any;
37
spacepack
38
.lazyLoad('"Missing channel in Channel.openChannelContextMenu"', /e\("(\d+)"\)/g, /webpackId:(\d+?),/)
39
.then(() => {
40
-
FilterBarClasses = spacepack.findByCode("tagsButtonWithCount:")[0].exports;
41
-
FilterDialogClasses = spacepack.findByCode("countContainer:", "tagContainer:")[0].exports;
42
});
43
44
-
const TagItem = spacepack.findByCode('"forum-tag-"')[0].exports.Z;
45
-
46
-
// FIXME: type component keys
47
-
const { ChevronSmallDownIcon, ChevronSmallUpIcon, ArrowsUpDownIcon } = Components;
48
-
49
function toggleTag(selectedTags: Set<string>, setSelectedTags: (tags: Set<string>) => void, tag: string) {
50
const newState = new Set(selectedTags);
51
if (newState.has(tag)) newState.delete(tag);
···
122
checked={(filter & Filter.Incompatible) === Filter.Incompatible}
123
action={() => toggleFilter(Filter.Incompatible)}
124
/>
125
<MenuItem
126
id="reset-all"
127
className={SortMenuClasses.clearText}
···
139
140
function TagButtonPopout({ selectedTags, setSelectedTags, setPopoutRef, closePopout }: any) {
141
return (
142
-
<Dialog ref={setPopoutRef} className={FilterDialogClasses.container}>
143
-
<div className={FilterDialogClasses.header}>
144
-
<div className={FilterDialogClasses.headerLeft}>
145
-
<Heading color="interactive-normal" variant="text-xs/bold" className={FilterDialogClasses.headerText}>
146
Select tags
147
</Heading>
148
-
<div className={FilterDialogClasses.countContainer}>
149
-
<Text className={FilterDialogClasses.countText} color="none" variant="text-xs/medium">
150
{selectedTags.size}
151
</Text>
152
</div>
153
</div>
154
</div>
155
-
<div className={FilterDialogClasses.tagContainer}>
156
{Object.keys(tagNames).map((tag) => (
157
<TagItem
158
key={tag}
159
-
className={FilterDialogClasses.tag}
160
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
161
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
162
selected={selectedTags.has(tag)}
163
/>
164
))}
165
</div>
166
-
<div className={FilterDialogClasses.separator} />
167
<Button
168
look={Button.Looks.LINK}
169
size={Button.Sizes.MIN}
170
color={Button.Colors.CUSTOM}
171
-
className={FilterDialogClasses.clear}
172
onClick={() => {
173
setSelectedTags(new Set());
174
closePopout();
···
198
const tagsContainer = React.useRef<HTMLDivElement>(null);
199
const tagListInner = React.useRef<HTMLDivElement>(null);
200
const [tagsButtonOffset, setTagsButtonOffset] = React.useState(0);
201
React.useLayoutEffect(() => {
202
if (tagsContainer.current === null || tagListInner.current === null) return;
203
const { left: containerX, top: containerY } = tagsContainer.current.getBoundingClientRect();
···
211
}
212
}
213
setTagsButtonOffset(offset);
214
-
}, [windowSize]);
215
216
return (
217
<div
···
219
style={{
220
paddingTop: "12px"
221
}}
222
-
className={`${FilterBarClasses.tagsContainer} ${Margins.marginBottom8}`}
223
>
224
<Popout
225
renderPopout={({ closePopout }: any) => (
226
<FilterButtonPopout filter={filter} setFilter={setFilter} closePopout={closePopout} />
···
233
{...props}
234
size={Button.Sizes.MIN}
235
color={Button.Colors.CUSTOM}
236
-
className={FilterBarClasses.sortDropdown}
237
-
innerClassName={FilterBarClasses.sortDropdownInner}
238
>
239
<ArrowsUpDownIcon size="xs" />
240
-
<Text className={FilterBarClasses.sortDropdownText} variant="text-sm/medium" color="interactive-normal">
241
Sort & filter
242
</Text>
243
{isShown ? (
···
248
</Button>
249
)}
250
</Popout>
251
-
<div className={FilterBarClasses.divider} />
252
-
<div className={FilterBarClasses.tagList}>
253
-
<div ref={tagListInner} className={FilterBarClasses.tagListInner}>
254
{Object.keys(tagNames).map((tag) => (
255
<TagItem
256
key={tag}
257
-
className={FilterBarClasses.tag}
258
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
259
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
260
selected={selectedTags.has(tag)}
261
/>
···
283
left: tagsButtonOffset
284
}}
285
// TODO: Use Discord's class name utility
286
-
className={`${FilterBarClasses.tagsButton} ${
287
-
selectedTags.size > 0 ? FilterBarClasses.tagsButtonWithCount : ""
288
-
}`}
289
-
innerClassName={FilterBarClasses.tagsButtonInner}
290
>
291
{selectedTags.size > 0 ? (
292
-
<div style={{ boxSizing: "content-box" }} className={FilterBarClasses.countContainer}>
293
-
<Text className={FilterBarClasses.countText} color="none" variant="text-xs/medium">
294
{selectedTags.size}
295
</Text>
296
</div>
···
305
</Button>
306
)}
307
</Popout>
308
</div>
309
);
310
}
···
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";
···
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,
···
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);
···
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}
···
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();
···
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();
···
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} />
···
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
}
+74
-9
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
+74
-9
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
···
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
10
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
11
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
12
13
-
const SearchBar: any = Object.values(spacepack.findByCode("hideSearchIcon")[0].exports)[0];
14
15
export default function ExtensionsPage() {
16
const { extensions, savedFilter } = useStateFromStoresObject([MoonbaseSettingsStore], () => {
···
21
});
22
23
const [query, setQuery] = React.useState("");
24
25
const filterState = React.useState(defaultFilter);
26
···
32
filter = filterState[0];
33
setFilter = filterState[1];
34
}
35
const [selectedTags, setSelectedTags] = React.useState(new Set<string>());
36
const sorted = Object.values(extensions).sort((a, b) => {
37
const aName = a.manifest.meta?.name ?? a.id;
38
const bName = b.manifest.meta?.name ?? b.id;
···
49
Object.entries(ext.manifest.settings).some(([key, setting]) =>
50
(setting.displayName ?? key).toLowerCase().includes(query)
51
)) ||
52
ext.manifest.meta?.description?.toLowerCase().includes(query)) &&
53
[...selectedTags.values()].every((tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag)) &&
54
// This seems very bad, sorry
···
63
) &&
64
(filter & Filter.Incompatible ||
65
ext.compat === ExtensionCompat.Compatible ||
66
-
(ext.compat === ExtensionCompat.InvalidApiLevel && ext.hasUpdate))
67
);
68
69
// Prioritize extensions with updates
70
-
filtered.sort((a, b) => {
71
-
if (a.hasUpdate && !b.hasUpdate) return -1;
72
-
if (!a.hasUpdate && b.hasUpdate) return 1;
73
-
return 0;
74
-
});
75
76
return (
77
<>
···
89
}}
90
/>
91
<FilterBar filter={filter} setFilter={setFilter} selectedTags={selectedTags} setSelectedTags={setSelectedTags} />
92
-
{filtered.map((ext) => (
93
-
<ExtensionCard uniqueId={ext.uniqueId} key={ext.uniqueId} />
94
))}
95
</>
96
);
···
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], () => {
···
32
});
33
34
const [query, setQuery] = React.useState("");
35
+
const [hitUpdateAll, setHitUpdateAll] = React.useState(false);
36
37
const filterState = React.useState(defaultFilter);
38
···
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;
···
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
···
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
<>
···
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
-26
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/info.tsx
+55
-26
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/info.tsx
···
2
import { MoonbaseExtension } from "../../../types";
3
4
import React from "@moonlight-mod/wp/react";
5
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
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("infoScroller", "userInfoSection", "userInfoSectionHeader")[0].exports;
38
-
39
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
40
-
41
-
// FIXME: type component keys
42
-
const { Text } = Components;
43
44
function InfoSection({ title, children }: { title: string; children: React.ReactNode }) {
45
return (
···
48
marginRight: "1em"
49
}}
50
>
51
-
<Text variant="eyebrow" className={UserInfoClasses.userInfoSectionHeader}>
52
{title}
53
</Text>
54
···
57
);
58
}
59
60
-
function Badge({ color, children }: { color: string; children: React.ReactNode }) {
61
return (
62
<span
63
-
style={{
64
-
borderRadius: ".1875rem",
65
-
padding: "0 0.275rem",
66
-
marginRight: "0.4em",
67
-
backgroundColor: color,
68
-
color: "#fff"
69
-
}}
70
>
71
{children}
72
</span>
73
);
74
}
75
76
-
export default function ExtensionInfo({ ext }: { ext: MoonbaseExtension }) {
77
const authors = ext.manifest?.meta?.authors;
78
const tags = ext.manifest?.meta?.tags;
79
const version = ext.manifest?.version;
80
81
const dependencies: Dependency[] = [];
82
if (ext.manifest.dependencies != null) {
83
dependencies.push(
84
...ext.manifest.dependencies.map((dep) => ({
···
98
}
99
100
if (ext.manifest.incompatible != null) {
101
-
dependencies.push(
102
...ext.manifest.incompatible.map((dep) => ({
103
id: dep,
104
type: DependencyType.Incompatible
···
136
<InfoSection title="Tags">
137
{tags.map((tag, i) => {
138
const name = tagNames[tag];
139
140
return (
141
-
<Badge key={i} color={tag === ExtensionTag.DangerZone ? "var(--red-400)" : "var(--brand-500)"}>
142
{name}
143
</Badge>
144
);
···
149
{dependencies.length > 0 && (
150
<InfoSection title="Dependencies">
151
{dependencies.map((dep) => {
152
-
const colors = {
153
-
[DependencyType.Dependency]: "var(--brand-500)",
154
-
[DependencyType.Optional]: "var(--orange-400)",
155
-
[DependencyType.Incompatible]: "var(--red-400)"
156
-
};
157
-
const color = colors[dep.type];
158
const name = MoonbaseSettingsStore.tryGetExtensionName(dep.id);
159
160
return (
161
-
<Badge color={color} key={dep.id}>
162
{name}
163
</Badge>
164
);
···
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 (
···
42
marginRight: "1em"
43
}}
44
>
45
+
<Text variant="eyebrow" className="moonlight-card-info-header">
46
{title}
47
</Text>
48
···
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
);
+33
-20
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/popup.tsx
+33
-20
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/popup.tsx
···
1
// TODO: clean up the styling here
2
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
3
import React from "@moonlight-mod/wp/react";
4
import { MoonbaseExtension } from "core-extensions/src/moonbase/types";
5
-
import * as Components 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
10
-
const { openModalLazy, useModalsStore, closeModal } = Components;
11
-
const Popup = spacepack.findByCode(".minorContainer", "secondaryAction")[0].exports.default;
12
13
function close() {
14
const ModalStore = useModalsStore.getState();
15
closeModal(ModalStore.default[0].key);
16
}
17
18
const presentableLoadSources: Record<ExtensionLoadSource, string> = {
19
[ExtensionLoadSource.Developer]: "Local extension", // should never show up
20
[ExtensionLoadSource.Core]: "Core extension",
···
32
option: string | undefined;
33
setOption: (pick: string | undefined) => void;
34
}) {
35
-
const { SingleSelect } = Components;
36
-
37
return (
38
<SingleSelect
39
key={id}
···
61
deps: Record<string, MoonbaseExtension[]>;
62
transitionState: number | null;
63
}) {
64
-
const { Text } = Components;
65
-
66
const amountNotAvailable = Object.values(deps).filter((candidates) => candidates.length === 0).length;
67
68
const [options, setOptions] = React.useState<Record<string, string | undefined>>(
···
75
);
76
77
return (
78
-
<Popup
79
body={
80
<Flex
81
style={{
···
155
});
156
}
157
158
-
function BuiltinExtensionPopup({
159
transitionState,
160
uniqueId,
161
cb
162
}: {
163
transitionState: number | null;
164
uniqueId: number;
165
cb: () => void;
166
}) {
167
-
const { Text } = Components;
168
169
return (
170
-
<Popup
171
body={
172
<Flex>
173
-
<Text variant="text-md/normal">
174
-
This extension is enabled by default. Disabling it might have consequences. Are you sure you want to disable
175
-
it?
176
-
</Text>
177
</Flex>
178
}
179
cancelText="No"
180
-
confirmText="Yes"
181
onCancel={close}
182
onConfirm={() => {
183
close();
184
cb();
185
}}
186
-
title="Built in extension"
187
transitionState={transitionState}
188
/>
189
);
190
}
191
192
-
export async function doBuiltinExtensionPopup(uniqueId: number, cb: () => void) {
193
await openModalLazy(async () => {
194
return ({ transitionState }: { transitionState: number | null }) => {
195
-
return <BuiltinExtensionPopup transitionState={transitionState} uniqueId={uniqueId} cb={cb} />;
196
};
197
});
198
}
···
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",
···
39
option: string | undefined;
40
setOption: (pick: string | undefined) => void;
41
}) {
42
return (
43
<SingleSelect
44
key={id}
···
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>>(
···
79
);
80
81
return (
82
+
<ConfirmModal
83
body={
84
<Flex
85
style={{
···
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
}
+85
-51
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
+85
-51
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
···
11
12
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
13
import React from "@moonlight-mod/wp/react";
14
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
15
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
16
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
17
18
type SettingsProps = {
19
ext: MoonbaseExtension;
···
21
setting: ExtensionSettingsManifest;
22
disabled: boolean;
23
};
24
-
25
type SettingsComponent = React.ComponentType<SettingsProps>;
26
27
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
28
const Margins = spacepack.require("discord/styles/shared/Margins.css");
29
30
function useConfigEntry<T>(uniqueId: number, name: string) {
31
return useStateFromStores(
32
[MoonbaseSettingsStore],
···
42
}
43
44
function Boolean({ ext, name, setting, disabled }: SettingsProps) {
45
-
const { FormSwitch } = Components;
46
const { value, displayName, description } = useConfigEntry<boolean>(ext.uniqueId, name);
47
48
return (
···
53
onChange={(value: boolean) => {
54
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
55
}}
56
-
note={description}
57
className={`${Margins.marginReset} ${Margins.marginTop20}`}
58
>
59
{displayName}
···
62
}
63
64
function Number({ ext, name, setting, disabled }: SettingsProps) {
65
-
const { FormItem, FormText, Slider } = Components;
66
const { value, displayName, description } = useConfigEntry<number>(ext.uniqueId, name);
67
68
const castedSetting = setting as NumberSettingType;
69
-
const min = castedSetting.min ?? 0;
70
-
const max = castedSetting.max ?? 100;
71
72
return (
73
<FormItem className={Margins.marginTop20} title={displayName}>
74
-
{description && <FormText>{description}</FormText>}
75
-
<Slider
76
-
initialValue={value ?? 0}
77
-
disabled={disabled}
78
-
minValue={castedSetting.min ?? 0}
79
-
maxValue={castedSetting.max ?? 100}
80
-
onValueChange={(value: number) => {
81
-
const rounded = Math.max(min, Math.min(max, Math.round(value)));
82
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, rounded);
83
-
}}
84
-
/>
85
</FormItem>
86
);
87
}
88
89
function String({ ext, name, setting, disabled }: SettingsProps) {
90
-
const { FormItem, FormText, TextInput } = Components;
91
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
92
93
return (
94
<FormItem className={Margins.marginTop20} title={displayName}>
95
-
{description && <FormText className={Margins.marginBottom8}>{description}</FormText>}
96
<TextInput
97
value={value ?? ""}
98
-
onChange={(value: string) => {
99
-
if (disabled) return;
100
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
101
-
}}
102
/>
103
</FormItem>
104
);
105
}
106
107
function MultilineString({ ext, name, setting, disabled }: SettingsProps) {
108
-
const { FormItem, FormText, TextArea } = Components;
109
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
110
111
return (
112
<FormItem className={Margins.marginTop20} title={displayName}>
113
-
{description && <FormText className={Margins.marginBottom8}>{description}</FormText>}
114
<TextArea
115
rows={5}
116
value={value ?? ""}
117
className={"moonbase-resizeable"}
118
-
onChange={(value: string) => {
119
-
if (disabled) return;
120
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
121
-
}}
122
/>
123
</FormItem>
124
);
125
}
126
127
function Select({ ext, name, setting, disabled }: SettingsProps) {
128
-
const { FormItem, FormText, SingleSelect } = Components;
129
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
130
131
const castedSetting = setting as SelectSettingType;
···
133
134
return (
135
<FormItem className={Margins.marginTop20} title={displayName}>
136
-
{description && <FormText className={Margins.marginBottom8}>{description}</FormText>}
137
<SingleSelect
138
autofocus={false}
139
clearable={false}
···
149
}
150
151
function MultiSelect({ ext, name, setting, disabled }: SettingsProps) {
152
-
const { FormItem, FormText, Select, useVariableSelect, multiSelect } = Components;
153
const { value, displayName, description } = useConfigEntry<string | string[]>(ext.uniqueId, name);
154
155
const castedSetting = setting as MultiSelectSettingType;
···
157
158
return (
159
<FormItem className={Margins.marginTop20} title={displayName}>
160
-
{description && <FormText className={Margins.marginBottom8}>{description}</FormText>}
161
-
<Select
162
autofocus={false}
163
clearable={false}
164
closeOnSelect={false}
···
176
);
177
}
178
179
-
const RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0].exports;
180
-
181
-
// FIXME: type component keys
182
-
const { CircleXIcon } = Components;
183
-
184
function RemoveEntryButton({ onClick, disabled }: { onClick: () => void; disabled: boolean }) {
185
-
const { Tooltip, Clickable } = Components;
186
return (
187
-
<div className={RemoveButtonClasses.removeButtonContainer}>
188
<Tooltip text="Remove entry" position="top">
189
{(props: any) => (
190
-
<Clickable {...props} className={RemoveButtonClasses.removeButton} onClick={onClick}>
191
<CircleXIcon width={16} height={16} />
192
</Clickable>
193
)}
···
197
}
198
199
function List({ ext, name, setting, disabled }: SettingsProps) {
200
-
const { FormItem, FormText, TextInput, Button } = Components;
201
const { value, displayName, description } = useConfigEntry<string[]>(ext.uniqueId, name);
202
203
const entries = value ?? [];
···
205
206
return (
207
<FormItem className={Margins.marginTop20} title={displayName}>
208
-
{description && <FormText className={Margins.marginBottom4}>{description}</FormText>}
209
<Flex direction={Flex.Direction.VERTICAL}>
210
{entries.map((val, i) => (
211
// FIXME: stylesheets
···
257
}
258
259
function Dictionary({ ext, name, setting, disabled }: SettingsProps) {
260
-
const { FormItem, FormText, TextInput, Button } = Components;
261
const { value, displayName, description } = useConfigEntry<Record<string, string>>(ext.uniqueId, name);
262
263
const entries = Object.entries(value ?? {});
···
265
266
return (
267
<FormItem className={Margins.marginTop20} title={displayName}>
268
-
{description && <FormText className={Margins.marginBottom4}>{description}</FormText>}
269
<Flex direction={Flex.Direction.VERTICAL}>
270
{entries.map(([key, val], i) => (
271
// FIXME: stylesheets
···
339
);
340
341
if (Component == null) {
342
-
const { Text } = Components;
343
return (
344
<Text variant="text-md/normal">{`Custom setting "${displayName}" is missing a component. Perhaps the extension is not installed?`}</Text>
345
);
346
}
347
348
return (
349
-
<Component value={value} setValue={(value) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)} />
350
);
351
}
352
···
370
export default function Settings({ ext }: { ext: MoonbaseExtension }) {
371
return (
372
<Flex className="moonbase-settings" direction={Flex.Direction.VERTICAL}>
373
-
{Object.entries(ext.manifest.settings!).map(([name, setting]) => (
374
<Setting
375
ext={ext}
376
key={name}
···
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],
···
81
}
82
83
function Boolean({ ext, name, setting, disabled }: SettingsProps) {
84
const { value, displayName, description } = useConfigEntry<boolean>(ext.uniqueId, name);
85
86
return (
···
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;
···
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}
···
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;
···
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}
···
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 ?? [];
···
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 ?? {});
···
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
···
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
···
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}
+26
-13
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
+26
-13
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
···
1
import React from "@moonlight-mod/wp/react";
2
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
3
import { Text, TabBar } from "@moonlight-mod/wp/discord/components/common/index";
4
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
5
import { UserSettingsModalStore } from "@moonlight-mod/wp/common_stores";
6
7
import ExtensionsPage from "./extensions";
8
import ConfigPage from "./config";
9
import Update from "./update";
10
-
11
-
const { Divider } = spacepack.findByCode(".forumOrHome]:")[0].exports.Z;
12
-
const TitleBarClasses = spacepack.findByCode("iconWrapper:", "children:")[0].exports;
13
-
const TabBarClasses = spacepack.findByCode("nowPlayingColumn:")[0].exports;
14
-
const { setSection, clearSubsection } = spacepack.findByExports("setSection", "clearSubsection")[0].exports.Z;
15
-
const Margins = spacepack.require("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
···
35
const subsection = useStateFromStores([UserSettingsModalStore], () => UserSettingsModalStore.getSubsection() ?? 0);
36
const setSubsection = React.useCallback(
37
(to: string) => {
38
-
if (subsection !== to) setSection("moonbase", to);
39
},
40
[subsection]
41
);
···
43
React.useEffect(
44
() => () => {
45
// 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
46
-
clearSubsection("moonbase");
47
},
48
[]
49
);
50
51
return (
52
<>
53
-
<div className={`${TitleBarClasses.children} ${Margins.marginBottom20}`}>
54
-
<Text className={TitleBarClasses.titleWrapper} variant="heading-lg/semibold" tag="h2">
55
Moonbase
56
</Text>
57
<Divider />
58
-
<TabBar selectedItem={subsection} onItemSelect={setSubsection} type="top-pill" className={TabBarClasses.tabBar}>
59
{pages.map((page, i) => (
60
-
<TabBar.Item key={page.id} id={i} className={TabBarClasses.item}>
61
{page.name}
62
</TabBar.Item>
63
))}
64
</TabBar>
65
</div>
66
67
<Update />
68
69
{React.createElement(pages[subsection].element)}
70
</>
71
);
72
}
···
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
···
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 />
63
+
<TabBar
64
+
selectedItem={subsection}
65
+
onItemSelect={setSubsection}
66
+
type="top-pill"
67
+
className={PeoplePageClasses.tabBar}
68
+
>
69
{pages.map((page, i) => (
70
+
<TabBar.Item key={page.id} id={i} className={PeoplePageClasses.item}>
71
{page.name}
72
</TabBar.Item>
73
))}
74
</TabBar>
75
</div>
76
77
+
<RestartAdviceMessage />
78
<Update />
79
80
{React.createElement(pages[subsection].element)}
81
</>
82
);
83
}
84
+
85
+
export { RestartAdviceMessage, Update };
+83
-40
packages/core-extensions/src/moonbase/webpackModules/ui/update.tsx
+83
-40
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 * as Components from "@moonlight-mod/wp/discord/components/common/index";
4
import React from "@moonlight-mod/wp/react";
5
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
6
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
7
-
import { UpdateState } from "../../types";
8
-
9
-
const { ThemeDarkIcon, Text, Button } = Components;
10
-
const Margins = spacepack.require("discord/styles/shared/Margins.css");
11
-
const HelpMessageClasses = spacepack.findByExports("positive", "iconDiv")[0].exports;
12
-
13
-
const logger = moonlight.getLogger("moonbase/ui/update");
14
15
const strings: Record<UpdateState, string> = {
16
[UpdateState.Ready]: "A new version of moonlight is available.",
···
19
[UpdateState.Failed]: "Failed to update moonlight. Please use the installer instead."
20
};
21
22
export default function Update() {
23
-
const [state, setState] = React.useState(UpdateState.Ready);
24
-
const newVersion = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.newVersion);
25
26
if (newVersion == null) return null;
27
28
-
// reimpl of HelpMessage but with a custom icon
29
return (
30
-
<div
31
-
className={`${Margins.marginBottom20} ${HelpMessageClasses.info} ${HelpMessageClasses.container} moonbase-update-section`}
32
-
>
33
-
<Flex direction={Flex.Direction.HORIZONTAL}>
34
-
<div
35
-
className={HelpMessageClasses.iconDiv}
36
-
style={{
37
-
alignItems: "center"
38
-
}}
39
-
>
40
-
<ThemeDarkIcon size="sm" color="currentColor" className={HelpMessageClasses.icon} />
41
-
</div>
42
43
-
<Text variant="text-sm/medium" color="currentColor" className={HelpMessageClasses.text}>
44
-
{strings[state]}
45
-
</Text>
46
-
</Flex>
47
-
48
-
<div className="moonbase-update-section-buttons">
49
{state === UpdateState.Installed && (
50
<Button
51
look={Button.Looks.OUTLINED}
52
color={Button.Colors.CUSTOM}
53
size={Button.Sizes.TINY}
54
-
onClick={() => window.location.reload()}
55
>
56
Restart Discord
57
</Button>
···
63
size={Button.Sizes.TINY}
64
disabled={state !== UpdateState.Ready}
65
onClick={() => {
66
-
setState(UpdateState.Working);
67
-
68
-
MoonbaseSettingsStore.updateMoonlight()
69
-
.then(() => setState(UpdateState.Installed))
70
-
.catch((e) => {
71
-
logger.error(e);
72
-
setState(UpdateState.Failed);
73
-
});
74
}}
75
>
76
Update
77
</Button>
78
</div>
79
-
</div>
80
);
81
}
···
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.",
···
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>
···
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
}
+7
-20
packages/core-extensions/src/moonbase/webpackModules/updates.tsx
+7
-20
packages/core-extensions/src/moonbase/webpackModules/updates.tsx
···
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 * as Components from "@moonlight-mod/wp/discord/components/common/index";
7
-
8
-
// FIXME: not indexed as importable
9
-
const Constants = spacepack.require("discord/Constants");
10
-
const UserSettingsSections = spacepack.findObjectFromKey(Constants, "APPEARANCE_THEME_PICKER");
11
-
12
-
const { ThemeDarkIcon } = Components;
13
14
function plural(str: string, num: number) {
15
return `${str}${num > 1 ? "s" : ""}`;
···
63
{
64
name: "Open Moonbase",
65
onClick: () => {
66
-
const { open } = spacepack.findByExports("setSection", "clearSubsection")[0].exports.Z;
67
-
68
-
// settings is lazy loaded thus lazily patched
69
-
// FIXME: figure out a way to detect if settings has been opened
70
-
// alreadyjust so the transition isnt as jarring
71
-
open(UserSettingsSections.ACCOUNT);
72
-
setTimeout(() => {
73
-
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
74
-
open("moonbase-extensions");
75
-
} else {
76
-
open("moonbase", 0);
77
-
}
78
-
}, 0);
79
return true;
80
}
81
}
···
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" : ""}`;
···
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
}
+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
+
}
+141
-1
packages/core-extensions/src/nativeFixes/host.ts
+141
-1
packages/core-extensions/src/nativeFixes/host.ts
···
1
import { app, nativeTheme } from "electron";
2
3
const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(",");
4
5
moonlightHost.events.on("window-created", function (browserWindow) {
···
22
23
// already added on Windows, but not on other operating systems
24
app.commandLine.appendSwitch("disable-background-timer-throttling");
25
}
26
27
if (process.platform === "linux") {
···
32
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxSpeechDispatcher") ?? true) {
33
app.commandLine.appendSwitch("enable-speech-dispatcher");
34
}
35
}
36
37
// NOTE: Only tested if this appears on Windows, it should appear on all when
38
// hardware acceleration is disabled
39
const noAccel = app.commandLine.hasSwitch("disable-gpu-compositing");
40
if ((moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapi") ?? true) && !noAccel) {
41
-
if (process.platform === "linux")
42
// These will eventually be renamed https://source.chromium.org/chromium/chromium/src/+/5482210941a94d70406b8da962426e4faca7fce4
43
enabledFeatures.push("VaapiVideoEncoder", "VaapiVideoDecoder", "VaapiVideoDecodeLinuxGL");
44
}
45
46
app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].join(","));
···
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) {
···
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") {
···
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
+
}
+36
-1
packages/core-extensions/src/nativeFixes/manifest.json
+36
-1
packages/core-extensions/src/nativeFixes/manifest.json
···
1
{
2
"id": "nativeFixes",
3
"meta": {
4
"name": "Native Fixes",
5
"tagline": "Various configurable fixes for Discord and Electron",
6
-
"authors": ["Cynosphere", "adryd"],
7
"tags": ["fixes"]
8
},
9
"settings": {
10
"devtoolsThemeFix": {
11
"displayName": "Devtools Theme Fix",
12
"description": "Temporary workaround for devtools defaulting to light theme on Electron 32",
13
"type": "boolean",
14
"default": true
15
},
16
"disableRendererBackgrounding": {
17
"displayName": "Disable Renderer Backgrounding",
18
"description": "This is enabled by default as a power saving measure, but it breaks screensharing and websocket connections fairly often",
19
"type": "boolean",
20
"default": true
21
},
22
"linuxAutoscroll": {
23
"displayName": "Enable middle click autoscroll on Linux",
24
"description": "Requires manual configuration of your system to disable middle click paste, has no effect on other operating systems",
25
"type": "boolean",
26
"default": false
27
},
28
"linuxSpeechDispatcher": {
29
"displayName": "Enable speech-dispatcher for TTS on Linux",
30
"description": "Fixes text-to-speech. Has no effect on other operating systems",
31
"type": "boolean",
32
"default": true
33
},
34
"vaapi": {
35
"displayName": "Enable VAAPI features on Linux",
36
"description": "Provides hardware accelerated video encode and decode. Has no effect on other operating systems",
37
"type": "boolean",
38
"default": true
39
}
···
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
}
+3
-3
packages/core-extensions/src/noHideToken/index.ts
+3
-3
packages/core-extensions/src/noHideToken/index.ts
+1
packages/core-extensions/src/noHideToken/manifest.json
+1
packages/core-extensions/src/noHideToken/manifest.json
+3
-3
packages/core-extensions/src/noTrack/index.ts
+3
-3
packages/core-extensions/src/noTrack/index.ts
+4
-1
packages/core-extensions/src/noTrack/manifest.json
+4
-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": {
···
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
}
+1
packages/core-extensions/src/notices/manifest.json
+1
packages/core-extensions/src/notices/manifest.json
+3
-6
packages/core-extensions/src/notices/webpackModules/component.tsx
+3
-6
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 * as Components 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 } from "@moonlight-mod/types/coreExtensions/notices";
7
8
-
// FIXME: types
9
-
const { Notice, NoticeCloseButton, PrimaryCTANoticeButton } = Components;
10
-
11
-
function popAndDismiss(notice: Notice) {
12
NoticesStore.popNotice();
13
if (notice?.onDismiss) {
14
notice.onDismiss();
···
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();
+103
-15
packages/core-extensions/src/quietLoggers/index.ts
+103
-15
packages/core-extensions/src/quietLoggers/index.ts
···
3
const notXssDefensesOnly = () =>
4
(moonlight.getConfigOption<boolean>("quietLoggers", "xssDefensesOnly") ?? false) === false;
5
6
// These patches MUST run before the simple patches, these are to remove loggers
7
// that end up causing syntax errors by the normal patch
8
const loggerFixes: Patch[] = [
···
16
{
17
find: '("GatewaySocket")',
18
replace: {
19
-
match: /.\.(info|log)(\(.+?\))(;|,)/g,
20
-
replacement: (_, type, body, trail) => `(()=>{})${body}${trail}`
21
}
22
}
23
];
···
28
// Patches to simply remove a logger call
29
const stubPatches = [
30
// "sh" is not a valid locale.
31
-
["is not a valid locale", /(.)\.error\(""\.concat\((.)," is not a valid locale\."\)\)/g],
32
-
['"[BUILD INFO] Release Channel: "', /new .{1,2}\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?\)\),/],
33
-
['.APP_NATIVE_CRASH,"Storage"', /console\.log\("AppCrashedFatalReport lastCrash:",.,.\);/],
34
-
['.APP_NATIVE_CRASH,"Storage"', 'console.log("AppCrashedFatalReport: getLastCrash not supported.");'],
35
-
['"[NATIVE INFO] ', /new .{1,2}\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/],
36
-
['"Spellchecker"', /.\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
37
-
['throw Error("Messages are still loading.");', /console\.warn\("Unsupported Locale",.\),/],
38
-
["}_dispatchWithDevtools(", /.\.totalTime>.{1,2}&&.\.verbose\(.+?\);/],
39
-
['"NativeDispatchUtils"', /null==.&&.\.warn\("Tried getting Dispatch instance before instantiated"\),/],
40
-
['("DatabaseManager")', /.\.log\("removing database \(user: ".+?\)\),/],
41
[
42
'"Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. Action: "',
43
-
/.\.has\(.\.type\)&&.\.log\(.+?\.type\)\),/
44
],
45
-
['console.warn("Window state not initialized"', /console\.warn\("Window state not initialized",.\),/]
46
];
47
48
const simplePatches = [
···
54
{
55
find: ".Messages.SELF_XSS_HEADER",
56
replace: {
57
-
match: /\(null!=.{1,2}&&"0\.0\.0"===.{1,2}\.remoteApp\.getVersion\(\)\)/,
58
replacement: "(true)"
59
}
60
},
61
...loggerFixes,
62
...stubPatches.map((patch) => ({
63
find: patch[0],
···
72
replace: {
73
match: patch[0],
74
replacement: patch[1]
75
},
76
prerequisite: notXssDefensesOnly
77
}))
···
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[] = [
···
18
{
19
find: '("GatewaySocket")',
20
replace: {
21
+
match: /\i\.(log|info)\(/g,
22
+
replacement: "(()=>{})("
23
+
}
24
+
},
25
+
{
26
+
find: '"_connect called with already existing websocket"',
27
+
replace: {
28
+
match: /\i\.(log|info|verbose)\(/g,
29
+
replacement: "(()=>{})("
30
}
31
}
32
];
···
37
// Patches to simply remove a logger call
38
const stubPatches = [
39
// "sh" is not a valid locale.
40
+
["is not a valid locale", /void \i\.error\(""\.concat\(\i," is not a valid locale\."\)\)/g],
41
+
['"[BUILD INFO] Release Channel: "', /new \i\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?\)\),/],
42
+
['.APP_NATIVE_CRASH,"Storage"', /console\.log\("AppCrashedFatalReport lastCrash:",\i,\i\);/],
43
+
['.APP_NATIVE_CRASH,"Storage"', 'void console.log("AppCrashedFatalReport: getLastCrash not supported.")'],
44
+
['"[NATIVE INFO] ', /new \i\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/],
45
+
['"Spellchecker"', /\i\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
46
+
['throw Error("Messages are still loading.");', /console\.warn\("Unsupported Locale",\i\),/],
47
+
["}_dispatchWithDevtools(", /\i\.totalTime>\i&&\i\.verbose\(.+?\);/],
48
+
['"NativeDispatchUtils"', /null==\i&&\i\.warn\("Tried getting Dispatch instance before instantiated"\),/],
49
[
50
'"Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. Action: "',
51
+
/\i\.has\(\i\.type\)&&\i\.log\(.+?\.type\)\),/
52
+
],
53
+
['console.warn("Window state not initialized"', /console\.warn\("Window state not initialized",\i\),/],
54
+
['.name="MaxListenersExceededWarning",', /(?<=\.length),\i\(\i\)/],
55
+
[
56
+
'"The answer for life the universe and everything is:"',
57
+
/\i\.info\("The answer for life the universe and everything is:",\i\),/
58
+
],
59
+
[
60
+
'"isLibdiscoreBlockedDomainsEnabled called but libdiscore is not loaded"',
61
+
/,\i\.verbose\("isLibdiscoreBlockedDomainsEnabledThisSession: ".concat\(\i\)\)/
62
+
],
63
+
[
64
+
'"Unable to determine render window for element"',
65
+
/console\.warn\("Unable to determine render window for element",\i\),/
66
],
67
+
[
68
+
'"Unable to determine render window for element"',
69
+
/console\.warn\('Unable to find element constructor "'\.concat\(\i,'" in'\),\i\),/
70
+
],
71
+
[
72
+
'"[PostMessageTransport] Protocol error: event data should be an Array!"',
73
+
/void console\.warn\("\[PostMessageTransport] Protocol error: event data should be an Array!"\)/
74
+
],
75
+
[
76
+
'("ComponentDispatchUtils")',
77
+
/new \i\.Z\("ComponentDispatchUtils"\)\.warn\("ComponentDispatch\.resubscribe: Resubscribe without existing subscription",\i\),/
78
+
]
79
+
];
80
+
81
+
const stripLoggers = [
82
+
'("OverlayRenderStore")',
83
+
'("FetchBlockedDomain")',
84
+
'="UserSettingsProtoLastWriteTimes",',
85
+
'("MessageActionCreators")',
86
+
'("Routing/Utils")',
87
+
'("DatabaseManager")',
88
+
'("KeyboardLayoutMapUtils")',
89
+
'("ChannelMessages")',
90
+
'("MessageQueue")',
91
+
'("RTCLatencyTestManager")',
92
+
'("OverlayStoreV3")',
93
+
'("OverlayBridgeStore")',
94
+
'("AuthenticationStore")',
95
+
'("ConnectionStore")',
96
+
'"Dispatched INITIAL_GUILD "',
97
+
'"handleIdentify called"',
98
+
'("Spotify")'
99
];
100
101
const simplePatches = [
···
107
{
108
find: ".Messages.SELF_XSS_HEADER",
109
replace: {
110
+
match: /\(null!=\i&&"0\.0\.0"===\i\.remoteApp\.getVersion\(\)\)/,
111
replacement: "(true)"
112
}
113
},
114
+
{
115
+
find: '("ComponentDispatchUtils")',
116
+
replace: {
117
+
match:
118
+
/new \i\.Z\("ComponentDispatchUtils"\)\.warn\("ComponentDispatch\.subscribe: Attempting to add a duplicate listener",\i\)/,
119
+
replacement: "void 0"
120
+
},
121
+
prerequisite: notXssDefensesOnly
122
+
},
123
+
// Highlight.js deprecation warnings
124
+
{
125
+
find: "Deprecated as of",
126
+
replace: {
127
+
match: /console\./g,
128
+
replacement: "false&&console."
129
+
},
130
+
prerequisite: notXssDefensesOnly
131
+
},
132
+
// Discord's logger
133
+
{
134
+
find: "ฮฃ:",
135
+
replace: {
136
+
match: "for",
137
+
replacement: "return;for"
138
+
},
139
+
prerequisite: () => silenceDiscordLogger && notXssDefensesOnly()
140
+
},
141
...loggerFixes,
142
...stubPatches.map((patch) => ({
143
find: patch[0],
···
152
replace: {
153
match: patch[0],
154
replacement: patch[1]
155
+
},
156
+
prerequisite: notXssDefensesOnly
157
+
})),
158
+
...stripLoggers.map((find) => ({
159
+
find,
160
+
replace: {
161
+
match: /(\i|this\.logger)\.(log|warn|error|info|verbose)\(/g,
162
+
replacement: "(()=>{})("
163
},
164
prerequisite: notXssDefensesOnly
165
}))
+9
packages/core-extensions/src/quietLoggers/manifest.json
+9
packages/core-extensions/src/quietLoggers/manifest.json
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "quietLoggers",
4
"apiLevel": 2,
5
"meta": {
···
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
}
+4
-1
packages/core-extensions/src/rocketship/manifest.json
+4
-1
packages/core-extensions/src/rocketship/manifest.json
···
1
{
2
"id": "rocketship",
3
"apiLevel": 2,
4
"meta": {
5
"name": "Rocketship",
6
"tagline": "Adds new features when using rocketship",
7
"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.",
8
-
"authors": ["NotNite", "Cynosphere", "adryd"]
9
}
10
}
···
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
}
+1
-1
packages/core-extensions/src/settings/index.ts
+1
-1
packages/core-extensions/src/settings/index.ts
+1
packages/core-extensions/src/settings/manifest.json
+1
packages/core-extensions/src/settings/manifest.json
+8
-2
packages/core-extensions/src/settings/webpackModules/settings.ts
+8
-2
packages/core-extensions/src/settings/webpackModules/settings.ts
···
1
import { SettingsSection, Settings as SettingsType } from "@moonlight-mod/types/coreExtensions/settings";
2
3
export const Settings: SettingsType = {
4
ourSections: [],
5
sectionNames: [],
6
sectionMenuItems: {},
7
8
-
addSection: (section, label, element, color = null, pos, notice) => {
9
const data: SettingsSection = {
10
section,
11
label,
12
color,
13
element,
14
pos: pos ?? -4,
15
-
notice: notice
16
};
17
18
Settings.ourSections.push(data);
···
43
44
_mutateSections: (sections) => {
45
for (const section of Settings.ourSections) {
46
sections.splice(section.pos < 0 ? sections.length + section.pos : section.pos, 0, section);
47
}
48
···
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);
···
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
+2
packages/core-extensions/src/spacepack/manifest.json
+2
packages/core-extensions/src/spacepack/manifest.json
···
1
{
2
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
3
"id": "spacepack",
4
"apiLevel": 2,
5
"meta": {
···
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",
+76
-26
packages/core-extensions/src/spacepack/webpackModules/spacepack.ts
+76
-26
packages/core-extensions/src/spacepack/webpackModules/spacepack.ts
···
1
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "@moonlight-mod/types";
2
import { Spacepack } from "@moonlight-mod/types/coreExtensions/spacepack";
3
4
const webpackRequire = require as unknown as WebpackRequireType;
5
const cache = webpackRequire.c;
···
36
"module",
37
"exports",
38
"require",
39
-
`(${funcStr}).apply(this, arguments)\n` + `//# sourceURL=Webpack-Module-${module}`
40
) as WebpackModuleFunc;
41
},
42
43
findByCode: (...args: (string | RegExp)[]) => {
44
-
return Object.entries(modules)
45
-
.filter(
46
-
([id, mod]) =>
47
-
!args.some(
48
-
(item) => !(item instanceof RegExp ? item.test(mod.toString()) : mod.toString().indexOf(item) !== -1)
49
-
)
50
-
)
51
.map(([id]) => {
52
//if (!(id in cache)) require(id);
53
//return cache[id];
···
56
try {
57
exports = require(id);
58
} catch (e) {
59
-
logger.error(`Error requiring module "${id}": `, e);
60
}
61
62
return {
···
65
};
66
})
67
.filter((item) => item !== null);
68
},
69
70
findByExports: (...args: string[]) => {
···
88
},
89
90
findObjectFromKey: (exports: Record<string, any>, key: string) => {
91
let subKey;
92
if (key.indexOf(".") > -1) {
93
const splitKey = key.split(".");
···
98
const obj = exports[exportKey];
99
if (obj && obj[key] !== undefined) {
100
if (subKey) {
101
-
if (obj[key][subKey]) return obj;
102
} else {
103
-
return obj;
104
}
105
}
106
}
107
-
return null;
108
},
109
110
findObjectFromValue: (exports: Record<string, any>, value: any) => {
111
for (const exportKey in exports) {
112
const obj = exports[exportKey];
113
// eslint-disable-next-line eqeqeq
114
-
if (obj == value) return obj;
115
for (const subKey in obj) {
116
// eslint-disable-next-line eqeqeq
117
if (obj && obj[subKey] == value) {
118
-
return obj;
119
}
120
}
121
}
122
-
return null;
123
},
124
125
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => {
126
for (const exportKey in exports) {
127
const obj = exports[exportKey];
128
// eslint-disable-next-line eqeqeq
129
if (obj && obj[key] == value) {
130
-
return obj;
131
}
132
}
133
return null;
134
},
135
136
findFunctionByStrings: (exports: Record<string, any>, ...strings: (string | RegExp)[]) => {
137
-
return (
138
Object.entries(exports).filter(
139
([index, func]) =>
140
-
typeof func === "function" &&
141
-
!strings.some(
142
-
(query) => !(query instanceof RegExp ? func.toString().match(query) : func.toString().includes(query))
143
-
)
144
-
)?.[0]?.[1] ?? null
145
-
);
146
},
147
148
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => {
149
const mod = Array.isArray(find) ? spacepack.findByCode(...find) : spacepack.findByCode(find);
150
-
if (mod.length < 1) return Promise.reject("Module find failed");
151
152
const findId = mod[0].id;
153
const findCode = webpackRequire.m[findId].toString().replace(/\n/g, "");
···
160
if (match) chunkIds = [...match[0].matchAll(/"(\d+)"/g)].map(([, id]) => id);
161
}
162
163
-
if (!chunkIds || chunkIds.length === 0) return Promise.reject("Chunk ID match failed");
164
165
const moduleId = findCode.match(module)?.[1];
166
-
if (!moduleId) return Promise.reject("Module ID match failed");
167
168
return Promise.all(chunkIds.map((c) => webpackRequire.e(c))).then(() => webpackRequire(moduleId));
169
},
···
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;
···
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[]) => {
···
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, "");
···
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
},
+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
}
+119
-93
packages/injector/src/index.ts
+119
-93
packages/injector/src/index.ts
···
6
} from "electron";
7
import Module from "node:module";
8
import { constants, MoonlightBranch } from "@moonlight-mod/types";
9
-
import { readConfig } 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 { join, resolve } from "node:path";
15
import persist from "@moonlight-mod/core/persist";
16
import createFS from "@moonlight-mod/core/fs";
17
18
const logger = new Logger("injector");
19
20
let oldPreloadPath: string | undefined;
21
let corsAllow: string[] = [];
22
let blockedUrls: RegExp[] = [];
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);
···
72
blockedUrls = compiled;
73
});
74
75
-
function patchCsp(headers: Record<string, string[]>) {
76
-
const directives = ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src", "prefetch-src"];
77
-
const values = ["*", "blob:", "data:", "'unsafe-inline'", "disclip:"];
78
79
const csp = "content-security-policy";
80
if (headers[csp] == null) return;
···
93
parts[directive] = values;
94
}
95
96
const stringified = Object.entries<string[]>(parts)
97
.map(([key, value]) => {
98
return `${key} ${value.join(" ")}`;
···
101
headers[csp] = [stringified];
102
}
103
104
-
function removeOpenAsarEventIfPresent(eventHandler: (...args: any[]) => void) {
105
-
const code = eventHandler.toString();
106
-
if (code.indexOf("bw.webContents.on('dom-ready'") > -1) {
107
-
electron.app.off("browser-window-created", eventHandler);
108
-
}
109
-
}
110
-
111
class BrowserWindow extends ElectronBrowserWindow {
112
constructor(opts: BrowserWindowConstructorOptions) {
113
-
oldPreloadPath = opts.webPreferences!.preload;
114
-
115
const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;
116
117
-
if (isMainWindow) opts.webPreferences!.preload = require.resolve("./node-preload.js");
118
119
// Event for modifying window options
120
moonlightHost.events.emit("window-options", opts, isMainWindow);
···
124
// Event for when a window is created
125
moonlightHost.events.emit("window-created", this, isMainWindow);
126
127
this.webContents.session.webRequest.onHeadersReceived((details, cb) => {
128
if (details.responseHeaders != null) {
129
// Patch CSP so things can use externally hosted assets
130
if (details.resourceType === "mainFrame") {
131
-
patchCsp(details.responseHeaders);
132
}
133
134
// Allow plugins to bypass CORS for specific URLs
135
if (corsAllow.some((x) => details.url.startsWith(x))) {
136
-
details.responseHeaders["access-control-allow-origin"] = ["*"];
137
}
138
139
cb({ cancel: false, responseHeaders: details.responseHeaders });
140
}
141
});
142
143
-
// Allow plugins to block some URLs,
144
-
// this is needed because multiple webRequest handlers cannot be registered at once
145
this.webContents.session.webRequest.onBeforeRequest((details, cb) => {
146
-
cb({ cancel: blockedUrls.some((u) => u.test(details.url)) });
147
-
});
148
149
-
if (hasOpenAsar) {
150
-
// Remove DOM injections
151
-
// Settings can still be opened via:
152
-
// `DiscordNative.ipc.send("DISCORD_UPDATED_QUOTES","o")`
153
-
// @ts-expect-error Electron internals
154
-
const events = electron.app._events["browser-window-created"];
155
-
if (Array.isArray(events)) {
156
-
for (const event of events) {
157
-
removeOpenAsarEventIfPresent(event);
158
}
159
-
} else if (events != null) {
160
-
removeOpenAsarEventIfPresent(events);
161
-
}
162
163
-
// Config screen fails to context bridge properly
164
-
// Less than ideal, but better than disabling it everywhere
165
-
if (opts.webPreferences!.preload === openAsarConfigPreload) {
166
-
opts.webPreferences!.sandbox = false;
167
}
168
-
}
169
}
170
}
171
···
186
writable: false
187
});
188
189
-
export async function inject(asarPath: string) {
190
-
isMoonlightDesktop = asarPath === "moonlightDesktop";
191
global.moonlightNodeSandboxed = {
192
fs: createFS(),
193
// These aren't supposed to be used from host
194
-
addCors(url) {},
195
-
addBlocked(url) {}
196
};
197
198
try {
199
-
const config = await readConfig();
200
initLogger(config);
201
const extensions = await getExtensions();
202
203
// Duplicated in node-preload... oops
204
function getConfig(ext: string) {
···
206
if (val == null || typeof val === "boolean") return undefined;
207
return val.config;
208
}
209
-
210
global.moonlightHost = {
211
asarPath,
212
-
config,
213
events: new EventEmitter(),
214
-
extensions,
215
-
processedExtensions: {
216
-
extensions: [],
217
-
dependencyGraph: new Map()
218
-
},
219
220
version: MOONLIGHT_VERSION,
221
branch: MOONLIGHT_BRANCH as MoonlightBranch,
222
223
getConfig,
224
-
getConfigOption: <T>(ext: string, name: string) => {
225
-
const config = getConfig(ext);
226
-
if (config == null) return undefined;
227
-
const option = config[name];
228
-
if (option == null) return undefined;
229
-
return option as T;
230
},
231
-
getLogger: (id: string) => {
232
return new Logger(id);
233
}
234
};
235
236
-
// Check if we're running with OpenAsar
237
-
try {
238
-
require.resolve(join(asarPath, "updater", "updater.js"));
239
-
hasOpenAsar = true;
240
-
openAsarConfigPreload = resolve(asarPath, "config", "preload.js");
241
-
// eslint-disable-next-line no-empty
242
-
} catch {}
243
-
244
-
if (hasOpenAsar) {
245
-
// Disable command line switch injection
246
-
// I personally think that the command line switches should be vetted by
247
-
// the user and not just "trust that these are sane defaults that work
248
-
// always". I'm not hating on Ducko or anything, I'm just opinionated.
249
-
// Someone can always make a command line modifier plugin, thats the point
250
-
// of having host modules.
251
-
try {
252
-
const cmdSwitchesPath = require.resolve(join(asarPath, "cmdSwitches.js"));
253
-
require.cache[cmdSwitchesPath] = new Module(cmdSwitchesPath, require.cache[require.resolve(asarPath)]);
254
-
require.cache[cmdSwitchesPath]!.exports = () => {};
255
-
} catch (error) {
256
-
logger.error("Failed to disable OpenAsar's command line flags:", error);
257
-
}
258
-
}
259
-
260
patchElectron();
261
262
-
global.moonlightHost.processedExtensions = await loadExtensions(extensions);
263
await loadProcessedExtensions(global.moonlightHost.processedExtensions);
264
} catch (error) {
265
logger.error("Failed to inject:", error);
266
}
267
268
-
if (isMoonlightDesktop) return;
269
-
270
-
if (!hasOpenAsar && !isMoonlightDesktop) {
271
persist(asarPath);
272
}
273
274
-
// Need to do this instead of require() or it breaks require.main
275
-
// @ts-expect-error Module internals
276
-
Module._load(asarPath, Module, true);
277
}
278
279
function patchElectron() {
···
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);
···
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;
···
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)
106
.map(([key, value]) => {
107
return `${key} ${value.join(" ")}`;
···
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);
···
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) {
···
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() {
+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
}
+72
-13
packages/node-preload/src/index.ts
+72
-13
packages/node-preload/src/index.ts
···
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
15
let initialized = false;
16
17
function setCors() {
18
const data = getDynamicCors();
···
35
36
let config = await readConfig();
37
initLogger(config);
38
const extensions = await getExtensions();
39
const processedExtensions = await loadExtensions(extensions);
40
const moonlightDir = await getMoonlightDir();
···
48
processedExtensions,
49
nativesCache: {},
50
isBrowser: false,
51
52
version: MOONLIGHT_VERSION,
53
branch: MOONLIGHT_BRANCH as MoonlightBranch,
···
57
},
58
getConfigOption(ext, name) {
59
const manifest = getManifest(extensions, ext);
60
-
return getConfigOption(ext, name, config, manifest);
61
},
62
-
setConfigOption(ext, name, value) {
63
setConfigOption(config, ext, name, value);
64
-
this.writeConfig(config);
65
},
66
67
getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
68
getLogger: (id: string) => {
69
return new Logger(id);
70
},
71
-
72
getMoonlightDir() {
73
return moonlightDir;
74
},
75
getExtensionDir: (ext: string) => {
76
return path.join(extensionsPath, ext);
77
-
},
78
-
async writeConfig(newConfig) {
79
-
await writeConfig(newConfig);
80
-
config = newConfig;
81
}
82
};
83
···
109
const webPreloadPath = path.join(__dirname, "web-preload.js");
110
const webPreload = fs.readFileSync(webPreloadPath, "utf8");
111
await webFrame.executeJavaScript(webPreload);
112
}
113
114
-
async function init(oldPreloadPath: string) {
115
try {
116
await injectGlobals();
117
await loadPreload();
···
122
message: message
123
});
124
}
125
-
126
-
// Let Discord start even if we fail
127
-
if (oldPreloadPath) require(oldPreloadPath);
128
}
129
130
const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
131
-
init(oldPreloadPath);
···
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();
···
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();
···
53
processedExtensions,
54
nativesCache: {},
55
isBrowser: false,
56
+
events: createEventEmitter<NodeEventType, NodeEventPayloads>(),
57
58
version: MOONLIGHT_VERSION,
59
branch: MOONLIGHT_BRANCH as MoonlightBranch,
···
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
···
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
+14
-7
packages/types/package.json
+14
-7
packages/types/package.json
···
1
{
2
"name": "@moonlight-mod/types",
3
-
"version": "1.3.3",
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
-
"@moonlight-mod/lunast": "^1.0.0",
13
-
"@moonlight-mod/mappings": "^1.0.10",
14
-
"@moonlight-mod/moonmap": "^1.0.3",
15
"@types/react": "^18.3.10",
16
-
"csstype": "^3.1.2",
17
"standalone-electron-types": "^1.0.0"
18
}
19
}
···
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
}
+48
packages/types/src/config.ts
+48
packages/types/src/config.ts
···
33
};
34
35
export type BooleanSettingType = {
36
type: ExtensionSettingType.Boolean;
37
default?: boolean;
38
};
39
40
export type NumberSettingType = {
41
type: ExtensionSettingType.Number;
42
default?: number;
43
min?: number;
···
45
};
46
47
export type StringSettingType = {
48
type: ExtensionSettingType.String;
49
default?: string;
50
};
51
52
export type MultilineTextInputSettingType = {
53
type: ExtensionSettingType.MultilineString;
54
default?: string;
55
};
56
57
export type SelectSettingType = {
58
type: ExtensionSettingType.Select;
59
options: SelectOption[];
60
default?: string;
61
};
62
63
export type MultiSelectSettingType = {
64
type: ExtensionSettingType.MultiSelect;
65
options: string[];
66
default?: string[];
67
};
68
69
export type ListSettingType = {
70
type: ExtensionSettingType.List;
71
default?: string[];
72
};
73
74
export type DictionarySettingType = {
75
type: ExtensionSettingType.Dictionary;
76
default?: Record<string, string>;
77
};
78
79
export type CustomSettingType = {
80
type: ExtensionSettingType.Custom;
81
default?: any;
82
};
83
84
export type ExtensionSettingsManifest = {
85
displayName?: string;
86
description?: string;
87
} & (
88
| BooleanSettingType
89
| NumberSettingType
···
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
+3
-1
packages/types/src/constants.ts
+3
-1
packages/types/src/constants.ts
···
4
export const repoUrlFile = ".moonlight-repo-url";
5
export const installedVersionFile = ".moonlight-installed-version";
6
7
export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";
8
export const ipcGetAppData = "_moonlight_getAppData";
9
-
export const ipcGetIsMoonlightDesktop = "_moonlight_getIsMoonlightDesktop";
10
export const ipcMessageBox = "_moonlight_messageBox";
11
export const ipcSetCorsList = "_moonlight_setCorsList";
12
export const ipcSetBlockedList = "_moonlight_setBlockedList";
···
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";
+13
-4
packages/types/src/core/event.ts
+13
-4
packages/types/src/core/event.ts
···
1
import { WebpackModuleFunc, WebpackRequireType } from "../discord";
2
3
export interface MoonlightEventEmitter<EventId extends string = string, EventData = Record<EventId, any>> {
···
6
removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => void;
7
}
8
9
-
export enum EventType {
10
ChunkLoad = "chunkLoad",
11
ExtensionLoad = "extensionLoad"
12
}
13
14
-
export type EventPayloads = {
15
-
[EventType.ChunkLoad]: {
16
chunkId?: number[];
17
modules: { [id: string]: WebpackModuleFunc };
18
require?: (require: WebpackRequireType) => any;
19
};
20
-
[EventType.ExtensionLoad]: string;
21
};
···
1
+
import { Config } from "../config";
2
import { WebpackModuleFunc, WebpackRequireType } from "../discord";
3
4
export interface MoonlightEventEmitter<EventId extends string = string, EventData = Record<EventId, any>> {
···
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
};
+9
packages/types/src/coreExtensions/appPanels.ts
+9
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
+8
-1
packages/types/src/coreExtensions/contextMenu.ts
+8
-1
packages/types/src/coreExtensions/contextMenu.ts
···
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;
+15
packages/types/src/coreExtensions/markdown.ts
+15
packages/types/src/coreExtensions/markdown.ts
···
82
slateDecorators: Record<string, string>;
83
ruleBlacklists: Record<Ruleset, Record<string, boolean>>;
84
85
addRule: (
86
name: string,
87
markdown: (rules: Record<string, MarkdownRule>) => MarkdownRule,
88
slate: (rules: Record<string, SlateRule>) => SlateRule,
89
decorator?: string | undefined
90
) => void;
91
blacklistFromRuleset: (ruleset: Ruleset, name: string) => void;
92
};
···
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
};
+11
-2
packages/types/src/coreExtensions/moonbase.ts
+11
-2
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
};
+16
-1
packages/types/src/coreExtensions/notices.ts
+16
-1
packages/types/src/coreExtensions/notices.ts
···
1
-
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux";
2
3
export type NoticeButton = {
4
name: string;
···
14
};
15
16
export type Notices = Store<any> & {
17
addNotice: (notice: Notice) => void;
18
popNotice: () => void;
19
getCurrentNotice: () => Notice | null;
20
shouldShowNotice: () => boolean;
21
};
···
1
+
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux/Store";
2
3
export type NoticeButton = {
4
name: string;
···
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
};
+39
-8
packages/types/src/coreExtensions/settings.ts
+39
-8
packages/types/src/coreExtensions/settings.ts
···
1
import React, { ReactElement } from "react";
2
-
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux";
3
4
export type NoticeProps = {
5
stores: Store<any>[];
···
7
};
8
9
export type SettingsSection =
10
-
| { section: "DIVIDER"; pos: number }
11
-
| { section: "HEADER"; label: string; pos: number }
12
| {
13
section: string;
14
label: string;
15
color: string | null;
16
element: React.FunctionComponent;
17
-
pos: number;
18
notice?: NoticeProps;
19
_moonlight_submenu?: () => ReactElement | ReactElement[];
20
};
21
···
24
sectionNames: string[];
25
sectionMenuItems: Record<string, ReactElement[]>;
26
27
addSection: (
28
section: string,
29
label: string,
30
element: React.FunctionComponent,
31
color?: string | null,
32
-
pos?: number,
33
-
notice?: NoticeProps
34
) => void;
35
addSectionMenuItems: (section: string, ...items: ReactElement[]) => void;
36
37
-
addDivider: (pos: number | null) => void;
38
-
addHeader: (label: string, pos: number | null) => void;
39
_mutateSections: (sections: SettingsSection[]) => SettingsSection[];
40
};
···
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>[];
···
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
···
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
};
+58
-5
packages/types/src/coreExtensions/spacepack.ts
+58
-5
packages/types/src/coreExtensions/spacepack.ts
···
1
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "../discord";
2
3
-
// Only bothered TSDoc'ing the hard-to-understand functions
4
-
5
export type Spacepack = {
6
inspect: (module: number | string) => WebpackModuleFunc | null;
7
findByCode: (...args: (string | RegExp)[]) => WebpackModule[];
8
findByExports: (...args: string[]) => WebpackModule[];
9
-
// re-export of require
10
require: WebpackRequireType;
11
-
// re-export of require.m
12
modules: Record<string, WebpackModuleFunc>;
13
-
// re-export of require.c
14
cache: Record<string, any>;
15
findObjectFromKey: (exports: Record<string, any>, key: string) => any | null;
16
findObjectFromValue: (exports: Record<string, any>, value: any) => any | null;
17
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => any | null;
18
/**
19
* Finds a function from a module's exports using the given source find.
20
* This behaves like findByCode but localized to the exported function.
···
27
...strings: (string | RegExp)[]
28
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
29
) => Function | null;
30
/**
31
* Lazy load a Webpack module.
32
* @param find A list of finds to discover a target module with
···
35
* @returns The target Webpack module
36
*/
37
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => Promise<any>;
38
/**
39
* Filter a list of Webpack modules to "real" ones from the Discord client.
40
* @param modules A list of Webpack modules
···
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.
···
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
···
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
+3
packages/types/src/coreExtensions.ts
+3
packages/types/src/coreExtensions.ts
···
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";
+12
packages/types/src/discord/require.ts
+12
packages/types/src/discord/require.ts
···
1
import { AppPanels } from "../coreExtensions/appPanels";
2
import { ContextMenu, EvilItemParser } from "../coreExtensions/contextMenu";
3
import { Markdown } from "../coreExtensions/markdown";
4
import { Moonbase } from "../coreExtensions/moonbase";
···
9
declare function WebpackRequire(id: string): any;
10
11
declare function WebpackRequire(id: "appPanels_appPanels"): AppPanels;
12
13
declare function WebpackRequire(id: "contextMenu_evilMenu"): EvilItemParser;
14
declare function WebpackRequire(id: "contextMenu_contextMenu"): ContextMenu;
···
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";
···
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;
+103
-1
packages/types/src/extension.ts
+103
-1
packages/types/src/extension.ts
···
28
};
29
30
export type ExtensionManifest = {
31
id: string;
32
version?: string;
33
apiLevel?: number;
34
environment?: ExtensionEnvironment;
35
36
meta?: {
37
name?: string;
38
tagline?: string;
39
description?: string;
40
authors?: ExtensionAuthor[];
41
-
deprecated?: boolean;
42
tags?: ExtensionTag[];
43
source?: string;
44
};
45
46
dependencies?: string[];
47
suggested?: string[];
48
incompatible?: string[];
49
50
settings?: Record<string, ExtensionSettingsManifest>;
51
52
cors?: string[];
53
blocked?: string[];
54
};
55
56
export enum ExtensionEnvironment {
57
Both = "both",
58
Desktop = "desktop",
59
Web = "web"
60
}
61
···
107
export type Patch = {
108
find: PatchMatch;
109
replace: PatchReplace | PatchReplace[];
110
prerequisite?: () => boolean;
111
};
112
···
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
···
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
+1
packages/types/src/fs.ts
+1
packages/types/src/fs.ts
+24
-9
packages/types/src/globals.ts
+24
-9
packages/types/src/globals.ts
···
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 { EventPayloads, EventType, MoonlightEventEmitter } from "./core/event";
8
-
import { MoonlightFS } from "./fs";
9
10
export type MoonlightHost = {
11
-
asarPath: string;
12
config: Config;
13
-
events: EventEmitter;
14
extensions: DetectedExtension[];
15
processedExtensions: ProcessedExtensions;
16
17
version: string;
18
branch: MoonlightBranch;
19
20
getConfig: (ext: string) => ConfigExtension["config"];
21
getConfigOption: <T>(ext: string, name: string) => T | undefined;
22
getLogger: (id: string) => Logger;
23
};
24
25
export type MoonlightNode = {
···
28
processedExtensions: ProcessedExtensions;
29
nativesCache: Record<string, any>;
30
isBrowser: boolean;
31
32
version: string;
33
branch: MoonlightBranch;
34
35
getConfig: (ext: string) => ConfigExtension["config"];
36
getConfigOption: <T>(ext: string, name: string) => T | undefined;
37
-
setConfigOption: <T>(ext: string, name: string, value: T) => void;
38
39
getNatives: (ext: string) => any | undefined;
40
getLogger: (id: string) => Logger;
41
-
42
getMoonlightDir: () => string;
43
getExtensionDir: (ext: string) => string;
44
-
writeConfig: (config: Config) => Promise<void>;
45
};
46
47
export type MoonlightNodeSandboxed = {
···
51
};
52
53
export type MoonlightWeb = {
54
unpatched: Set<IdentifiedPatch>;
55
pendingModules: Set<IdentifiedWebpackModule>;
56
enabledExtensions: Set<string>;
57
-
apiLevel: number;
58
-
events: MoonlightEventEmitter<EventType, EventPayloads>;
59
patchingInternals: {
60
onModuleLoad: (moduleId: string | string[], callback: (moduleId: string) => void) => void;
61
registerPatch: (patch: IdentifiedPatch) => void;
···
65
66
version: string;
67
branch: MoonlightBranch;
68
69
// Re-exports for ease of use
70
getConfig: MoonlightNode["getConfig"];
71
getConfigOption: MoonlightNode["getConfigOption"];
72
setConfigOption: MoonlightNode["setConfigOption"];
73
74
getNatives: (ext: string) => any | undefined;
75
getLogger: (id: string) => Logger;
76
lunast: LunAST;
77
moonmap: Moonmap;
78
};
···
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 = {
···
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 = {
···
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;
···
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
};
+32
packages/types/src/import.d.ts
+32
packages/types/src/import.d.ts
···
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";
+3
-2
packages/types/src/index.ts
+3
-2
packages/types/src/index.ts
···
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
}
+841
-25
packages/types/src/mappings.d.ts
+841
-25
packages/types/src/mappings.d.ts
···
1
// auto-generated
2
declare module "@moonlight-mod/wp/discord/Dispatcher" {
3
import { MappedModules } from "@moonlight-mod/mappings";
4
-
const _: MappedModules["discord/Dispatcher"];
5
-
export = _;
6
}
7
8
declare module "@moonlight-mod/wp/discord/actions/ContextMenuActionCreators" {
9
import { MappedModules } from "@moonlight-mod/mappings";
10
-
const _: MappedModules["discord/actions/ContextMenuActionCreators"];
11
-
export = _;
12
}
13
14
declare module "@moonlight-mod/wp/discord/components/common/index" {
15
import { MappedModules } from "@moonlight-mod/mappings";
16
-
const _: MappedModules["discord/components/common/index"];
17
-
export = _;
18
}
19
20
-
declare module "@moonlight-mod/wp/discord/modules/guild_settings/IntegrationCard.css" {
21
import { MappedModules } from "@moonlight-mod/mappings";
22
-
const _: MappedModules["discord/modules/guild_settings/IntegrationCard.css"];
23
-
export = _;
24
}
25
26
declare module "@moonlight-mod/wp/discord/modules/markup/MarkupUtils" {
27
import { MappedModules } from "@moonlight-mod/mappings";
28
-
const _: MappedModules["discord/modules/markup/MarkupUtils"];
29
-
export = _;
30
}
31
32
-
declare module "@moonlight-mod/wp/discord/modules/user_settings/web/openUserSettings" {
33
import { MappedModules } from "@moonlight-mod/mappings";
34
-
const _: MappedModules["discord/modules/user_settings/web/openUserSettings"];
35
-
export = _;
36
}
37
38
declare module "@moonlight-mod/wp/discord/packages/flux" {
39
import { MappedModules } from "@moonlight-mod/mappings";
40
-
const _: MappedModules["discord/packages/flux"];
41
-
export = _;
42
}
43
44
declare module "@moonlight-mod/wp/discord/uikit/Flex" {
45
import { MappedModules } from "@moonlight-mod/mappings";
46
-
const _: MappedModules["discord/uikit/Flex"];
47
-
export = _;
48
}
49
50
declare module "@moonlight-mod/wp/discord/utils/ClipboardUtils" {
51
import { MappedModules } from "@moonlight-mod/mappings";
52
-
const _: MappedModules["discord/utils/ClipboardUtils"];
53
-
export = _;
54
}
55
56
declare module "@moonlight-mod/wp/discord/utils/HTTPUtils" {
57
import { MappedModules } from "@moonlight-mod/mappings";
58
-
const _: MappedModules["discord/utils/HTTPUtils"];
59
-
export = _;
60
}
61
62
declare module "@moonlight-mod/wp/discord/utils/NativeUtils" {
63
import { MappedModules } from "@moonlight-mod/mappings";
64
-
const _: MappedModules["discord/utils/NativeUtils"];
65
-
export = _;
66
}
67
68
declare module "@moonlight-mod/wp/react" {
69
import { MappedModules } from "@moonlight-mod/mappings";
70
-
const _: MappedModules["react"];
71
export = _;
72
}
···
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
}
+10
-3
packages/web-preload/package.json
+10
-3
packages/web-preload/package.json
···
2
"name": "@moonlight-mod/web-preload",
3
"private": true,
4
"main": "src/index.ts",
5
"dependencies": {
6
"@moonlight-mod/core": "workspace:*",
7
-
"@moonlight-mod/lunast": "^1.0.0",
8
-
"@moonlight-mod/mappings": "^1.0.10",
9
-
"@moonlight-mod/moonmap": "^1.0.3",
10
"@moonlight-mod/types": "workspace:*"
11
}
12
}
···
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
}
+12
-13
packages/web-preload/src/index.ts
+12
-13
packages/web-preload/src/index.ts
···
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 { EventPayloads, EventType } from "@moonlight-mod/types/core/event";
11
12
async function load() {
13
initLogger(moonlightNode.config);
14
const logger = new Logger("web-preload");
15
16
window.moonlight = {
17
-
apiLevel: constants.apiLevel,
18
unpatched: new Set(),
19
pendingModules: new Set(),
20
enabledExtensions: new Set(),
21
-
events: createEventEmitter<EventType, EventPayloads>(),
22
patchingInternals: {
23
onModuleLoad,
24
registerPatch,
···
28
29
version: MOONLIGHT_VERSION,
30
branch: MOONLIGHT_BRANCH as MoonlightBranch,
31
32
getConfig: moonlightNode.getConfig.bind(moonlightNode),
33
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
34
setConfigOption: moonlightNode.setConfigOption.bind(moonlightNode),
35
36
getNatives: moonlightNode.getNatives.bind(moonlightNode),
37
getLogger(id) {
38
return new Logger(id);
39
},
40
lunast: new LunAST(),
41
moonmap: new Moonmap()
42
};
···
49
logger.error("Error setting up web-preload", e);
50
}
51
52
-
if (MOONLIGHT_ENV === "web-preload") {
53
-
window.addEventListener("DOMContentLoaded", () => {
54
-
installStyles();
55
-
});
56
} else {
57
-
installStyles();
58
}
59
}
60
61
-
if (MOONLIGHT_ENV === "web-preload") {
62
-
load();
63
-
} else {
64
-
window._moonlightBrowserLoad = load;
65
-
}
···
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,
···
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
};
···
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
+1253
-731
pnpm-lock.yaml
+1253
-731
pnpm-lock.yaml
···
4
autoInstallPeers: true
5
excludeLinksFromLockfile: false
6
7
importers:
8
9
.:
10
devDependencies:
11
'@moonlight-mod/eslint-config':
12
-
specifier: github:moonlight-mod/eslint-config
13
-
version: https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/7eb7bd7c51fe0e3ee9d2a0baf149212d2bb893af(eslint@9.12.0)(prettier@3.1.0)(typescript@5.3.2)
14
esbuild:
15
-
specifier: ^0.19.3
16
version: 0.19.3
17
esbuild-copy-static-files:
18
-
specifier: ^0.1.0
19
version: 0.1.0
20
eslint:
21
-
specifier: ^9.12.0
22
-
version: 9.12.0
23
husky:
24
-
specifier: ^8.0.3
25
version: 8.0.3
26
prettier:
27
-
specifier: ^3.1.0
28
version: 3.1.0
29
typescript:
30
-
specifier: ^5.3.2
31
-
version: 5.3.2
32
33
packages/browser:
34
dependencies:
···
42
specifier: workspace:*
43
version: link:../web-preload
44
'@zenfs/core':
45
-
specifier: ^1.0.2
46
-
version: 1.0.2
47
'@zenfs/dom':
48
-
specifier: ^0.2.16
49
-
version: 0.2.16(@zenfs/core@1.0.2)
50
51
packages/core:
52
dependencies:
···
62
'@moonlight-mod/types':
63
specifier: workspace:*
64
version: link:../types
65
nanotar:
66
-
specifier: ^0.1.1
67
version: 0.1.1
68
69
packages/injector:
···
87
packages/types:
88
dependencies:
89
'@moonlight-mod/lunast':
90
-
specifier: ^1.0.0
91
-
version: 1.0.0
92
'@moonlight-mod/mappings':
93
-
specifier: ^1.0.10
94
-
version: 1.0.10(@moonlight-mod/lunast@1.0.0)(@moonlight-mod/moonmap@1.0.3)
95
'@moonlight-mod/moonmap':
96
-
specifier: ^1.0.3
97
-
version: 1.0.3
98
'@types/react':
99
specifier: ^18.3.10
100
-
version: 18.3.10
101
csstype:
102
-
specifier: ^3.1.2
103
-
version: 3.1.2
104
standalone-electron-types:
105
specifier: ^1.0.0
106
version: 1.0.0
···
111
specifier: workspace:*
112
version: link:../core
113
'@moonlight-mod/lunast':
114
-
specifier: ^1.0.0
115
-
version: 1.0.0
116
'@moonlight-mod/mappings':
117
-
specifier: ^1.0.10
118
-
version: 1.0.10(@moonlight-mod/lunast@1.0.0)(@moonlight-mod/moonmap@1.0.3)
119
'@moonlight-mod/moonmap':
120
-
specifier: ^1.0.3
121
-
version: 1.0.3
122
'@moonlight-mod/types':
123
specifier: workspace:*
124
version: link:../types
···
128
'@aashutoshrathi/word-wrap@1.2.6':
129
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
130
engines: {node: '>=0.10.0'}
131
132
'@esbuild/android-arm64@0.19.3':
133
resolution: {integrity: sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==}
···
261
cpu: [x64]
262
os: [win32]
263
264
-
'@eslint-community/eslint-utils@4.4.0':
265
-
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
266
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
267
peerDependencies:
268
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
269
270
-
'@eslint-community/regexpp@4.11.1':
271
-
resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
272
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
273
274
-
'@eslint/config-array@0.18.0':
275
-
resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==}
276
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
277
278
-
'@eslint/core@0.6.0':
279
-
resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==}
280
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
281
282
-
'@eslint/eslintrc@3.1.0':
283
-
resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
284
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
285
286
-
'@eslint/js@9.12.0':
287
-
resolution: {integrity: sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==}
288
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
289
290
-
'@eslint/object-schema@2.1.4':
291
-
resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
292
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
293
294
-
'@eslint/plugin-kit@0.2.0':
295
-
resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==}
296
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
297
298
-
'@humanfs/core@0.19.0':
299
-
resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==}
300
engines: {node: '>=18.18.0'}
301
302
-
'@humanfs/node@0.16.5':
303
-
resolution: {integrity: sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==}
304
engines: {node: '>=18.18.0'}
305
306
'@humanwhocodes/module-importer@1.0.1':
···
311
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
312
engines: {node: '>=18.18'}
313
314
-
'@moonlight-mod/eslint-config@https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/7eb7bd7c51fe0e3ee9d2a0baf149212d2bb893af':
315
-
resolution: {tarball: https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/7eb7bd7c51fe0e3ee9d2a0baf149212d2bb893af}
316
-
version: 1.0.0
317
peerDependencies:
318
eslint: '>= 9'
319
typescript: '>= 5.3'
320
321
-
'@moonlight-mod/lunast@1.0.0':
322
-
resolution: {integrity: sha512-kJgf41K12i6/2LbXK97CNO+pNO7ADGh9N4bCQcOPwosocKMcwKHDEZUgPqeihNshY3c3AEW1LiyXjlsl24PdDw==}
323
324
-
'@moonlight-mod/mappings@1.0.10':
325
-
resolution: {integrity: sha512-L04To4MhlxOWxvvfVIRwj7d8H5qHthjUfikSx9WMk60qt67+6dFzN9CoAG9uG5l1B27IIEG3voVXBr0oSkooYw==}
326
peerDependencies:
327
-
'@moonlight-mod/lunast': ^1.0.0
328
-
'@moonlight-mod/moonmap': ^1.0.0
329
330
-
'@moonlight-mod/moonmap@1.0.3':
331
-
resolution: {integrity: sha512-G7pwvrcVDimc388IX6VZFzBXpbuyvqbJ+w9/v+MUIc8P7dADJXQ9YkBWvobtRc6eaBBl1FWUwTeU8oobbxLVag==}
332
333
'@nodelib/fs.scandir@2.1.5':
334
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
···
342
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
343
engines: {node: '>= 8'}
344
345
-
'@pkgr/core@0.1.1':
346
-
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
347
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
348
349
'@types/estree-jsx@1.0.5':
350
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
351
352
'@types/estree@1.0.6':
353
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
354
355
'@types/fbemitter@2.0.35':
356
resolution: {integrity: sha512-Xem6d7qUfmouCHntCrRYgDBwbf+WWRd6G+7WEFlEZFZ67LZXiYRvT2LV8wcZa6mIaAil95+ABQdKgB6hPIsnng==}
357
358
'@types/flux@3.1.14':
359
resolution: {integrity: sha512-WRXN0kQPCnqxN0/PgNgc7WBF6c8rbSHsEep3/qBLpsQ824RONdOmTs0TV7XhIW2GDNRAHO2CqCgAFLR5PChosw==}
360
361
'@types/json-schema@7.0.15':
362
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
363
364
'@types/node@18.17.17':
365
resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==}
366
367
-
'@types/node@20.16.10':
368
-
resolution: {integrity: sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==}
369
370
'@types/prop-types@15.7.13':
371
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
372
373
-
'@types/react@18.3.10':
374
-
resolution: {integrity: sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg==}
375
-
376
-
'@types/readable-stream@4.0.15':
377
-
resolution: {integrity: sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==}
378
379
-
'@typescript-eslint/eslint-plugin@8.8.1':
380
-
resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==}
381
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
382
peerDependencies:
383
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
384
eslint: ^8.57.0 || ^9.0.0
385
-
typescript: '*'
386
-
peerDependenciesMeta:
387
-
typescript:
388
-
optional: true
389
390
-
'@typescript-eslint/parser@8.8.1':
391
-
resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==}
392
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
393
peerDependencies:
394
eslint: ^8.57.0 || ^9.0.0
395
-
typescript: '*'
396
-
peerDependenciesMeta:
397
-
typescript:
398
-
optional: true
399
400
-
'@typescript-eslint/scope-manager@8.8.1':
401
-
resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==}
402
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
403
404
-
'@typescript-eslint/type-utils@8.8.1':
405
-
resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==}
406
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
407
peerDependencies:
408
-
typescript: '*'
409
-
peerDependenciesMeta:
410
-
typescript:
411
-
optional: true
412
413
-
'@typescript-eslint/types@8.8.1':
414
-
resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==}
415
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
416
417
-
'@typescript-eslint/typescript-estree@8.8.1':
418
-
resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==}
419
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
420
peerDependencies:
421
-
typescript: '*'
422
-
peerDependenciesMeta:
423
-
typescript:
424
-
optional: true
425
426
-
'@typescript-eslint/utils@8.8.1':
427
-
resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==}
428
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
429
peerDependencies:
430
eslint: ^8.57.0 || ^9.0.0
431
432
-
'@typescript-eslint/visitor-keys@8.8.1':
433
-
resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==}
434
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
435
436
-
'@zenfs/core@1.0.2':
437
-
resolution: {integrity: sha512-LMTD4ntn6Ag1y+IeOSVykDDvYC12dsGFtsX8M/54OQrLs7v+YnX4bpo0o2osbm8XFmU2MTNMX/G3PLsvzgWzrg==}
438
-
engines: {node: '>= 16'}
439
hasBin: true
440
441
-
'@zenfs/dom@0.2.16':
442
-
resolution: {integrity: sha512-6Ev+ol9hZIgQECNZR+xxjQ/a99EhhrWeiQttm/+U7YJK3HdTjiKfU39DsfGeH64vSqhpa5Vj+LWRx75SHkjw0Q==}
443
engines: {node: '>= 18'}
444
peerDependencies:
445
-
'@zenfs/core': ^1.0.0
446
447
abort-controller@3.0.0:
448
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
···
453
peerDependencies:
454
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
455
456
-
acorn@8.12.1:
457
-
resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
458
engines: {node: '>=0.4.0'}
459
hasBin: true
460
···
465
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
466
engines: {node: '>=8'}
467
468
argparse@2.0.1:
469
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
470
471
-
array-buffer-byte-length@1.0.1:
472
-
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
473
engines: {node: '>= 0.4'}
474
475
array-includes@3.1.8:
···
480
resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
481
engines: {node: '>= 0.4'}
482
483
-
array.prototype.flat@1.3.2:
484
-
resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
485
engines: {node: '>= 0.4'}
486
487
-
array.prototype.flatmap@1.3.2:
488
-
resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
489
engines: {node: '>= 0.4'}
490
491
array.prototype.tosorted@1.1.4:
492
resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
493
engines: {node: '>= 0.4'}
494
495
-
arraybuffer.prototype.slice@1.0.3:
496
-
resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
497
engines: {node: '>= 0.4'}
498
499
astring@1.9.0:
500
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
501
hasBin: true
502
503
available-typed-arrays@1.0.7:
504
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
···
523
buffer@6.0.3:
524
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
525
526
-
call-bind@1.0.7:
527
-
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
528
engines: {node: '>= 0.4'}
529
530
callsites@3.1.0:
···
545
concat-map@0.0.1:
546
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
547
548
-
cross-spawn@7.0.3:
549
-
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
550
engines: {node: '>= 8'}
551
552
-
csstype@3.1.2:
553
-
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
554
-
555
csstype@3.1.3:
556
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
557
558
-
data-view-buffer@1.0.1:
559
-
resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
560
engines: {node: '>= 0.4'}
561
562
-
data-view-byte-length@1.0.1:
563
-
resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==}
564
engines: {node: '>= 0.4'}
565
566
-
data-view-byte-offset@1.0.0:
567
-
resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
568
engines: {node: '>= 0.4'}
569
570
-
debug@4.3.4:
571
-
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
572
engines: {node: '>=6.0'}
573
peerDependencies:
574
supports-color: '*'
···
587
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
588
engines: {node: '>= 0.4'}
589
590
doctrine@2.1.0:
591
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
592
engines: {node: '>=0.10.0'}
593
594
-
es-abstract@1.23.3:
595
-
resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==}
596
engines: {node: '>= 0.4'}
597
598
-
es-define-property@1.0.0:
599
-
resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
600
engines: {node: '>= 0.4'}
601
602
es-errors@1.3.0:
603
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
604
engines: {node: '>= 0.4'}
605
606
-
es-iterator-helpers@1.1.0:
607
-
resolution: {integrity: sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==}
608
engines: {node: '>= 0.4'}
609
610
-
es-object-atoms@1.0.0:
611
-
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
612
engines: {node: '>= 0.4'}
613
614
-
es-set-tostringtag@2.0.3:
615
-
resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
616
engines: {node: '>= 0.4'}
617
618
-
es-shim-unscopables@1.0.2:
619
-
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
620
621
-
es-to-primitive@1.2.1:
622
-
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
623
engines: {node: '>= 0.4'}
624
625
esbuild-copy-static-files@0.1.0:
···
640
peerDependencies:
641
eslint: '>=7.0.0'
642
643
-
eslint-plugin-prettier@5.2.1:
644
-
resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==}
645
engines: {node: ^14.18.0 || >=16.0.0}
646
peerDependencies:
647
'@types/eslint': '>=8.0.0'
648
eslint: '>=8.0.0'
649
-
eslint-config-prettier: '*'
650
prettier: '>=3.0.0'
651
peerDependenciesMeta:
652
'@types/eslint':
···
654
eslint-config-prettier:
655
optional: true
656
657
-
eslint-plugin-react@7.37.1:
658
-
resolution: {integrity: sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==}
659
engines: {node: '>=4'}
660
peerDependencies:
661
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
662
663
-
eslint-scope@8.1.0:
664
-
resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==}
665
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
666
667
eslint-visitor-keys@3.4.3:
668
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
669
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
670
671
-
eslint-visitor-keys@4.1.0:
672
-
resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==}
673
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
674
675
-
eslint@9.12.0:
676
-
resolution: {integrity: sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==}
677
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
678
hasBin: true
679
peerDependencies:
···
682
jiti:
683
optional: true
684
685
-
espree@10.2.0:
686
-
resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==}
687
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
688
689
-
esquery@1.5.0:
690
-
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
691
engines: {node: '>=0.10'}
692
693
esrecurse@4.3.0:
···
735
fastq@1.17.1:
736
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
737
738
file-entry-cache@8.0.0:
739
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
740
engines: {node: '>=16.0.0'}
···
743
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
744
engines: {node: '>=8'}
745
746
find-up@5.0.0:
747
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
748
engines: {node: '>=10'}
···
754
flatted@3.2.9:
755
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
756
757
-
for-each@0.3.3:
758
-
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
759
760
function-bind@1.1.2:
761
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
762
763
-
function.prototype.name@1.1.6:
764
-
resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
765
engines: {node: '>= 0.4'}
766
767
functions-have-names@1.2.3:
768
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
769
770
-
get-intrinsic@1.2.4:
771
-
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
772
engines: {node: '>= 0.4'}
773
774
-
get-symbol-description@1.0.2:
775
-
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
776
engines: {node: '>= 0.4'}
777
778
glob-parent@5.1.2:
···
791
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
792
engines: {node: '>= 0.4'}
793
794
-
gopd@1.0.1:
795
-
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
796
797
graphemer@1.4.0:
798
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
799
800
-
has-bigints@1.0.2:
801
-
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
802
803
has-flag@4.0.0:
804
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
···
807
has-property-descriptors@1.0.2:
808
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
809
810
-
has-proto@1.0.3:
811
-
resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
812
engines: {node: '>= 0.4'}
813
814
-
has-symbols@1.0.3:
815
-
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
816
engines: {node: '>= 0.4'}
817
818
has-tostringtag@1.0.2:
···
831
ieee754@1.2.1:
832
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
833
834
-
ignore@5.3.0:
835
-
resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==}
836
-
engines: {node: '>= 4'}
837
-
838
ignore@5.3.2:
839
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
840
engines: {node: '>= 4'}
···
847
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
848
engines: {node: '>=0.8.19'}
849
850
-
internal-slot@1.0.7:
851
-
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
852
engines: {node: '>= 0.4'}
853
854
-
is-array-buffer@3.0.4:
855
-
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
856
engines: {node: '>= 0.4'}
857
858
-
is-async-function@2.0.0:
859
-
resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==}
860
engines: {node: '>= 0.4'}
861
862
-
is-bigint@1.0.4:
863
-
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
864
865
-
is-boolean-object@1.1.2:
866
-
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
867
engines: {node: '>= 0.4'}
868
869
is-callable@1.2.7:
870
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
871
engines: {node: '>= 0.4'}
872
873
-
is-core-module@2.15.1:
874
-
resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
875
engines: {node: '>= 0.4'}
876
877
-
is-data-view@1.0.1:
878
-
resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==}
879
engines: {node: '>= 0.4'}
880
881
-
is-date-object@1.0.5:
882
-
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
883
engines: {node: '>= 0.4'}
884
885
is-extglob@2.1.1:
886
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
887
engines: {node: '>=0.10.0'}
888
889
-
is-finalizationregistry@1.0.2:
890
-
resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==}
891
892
-
is-generator-function@1.0.10:
893
-
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
894
engines: {node: '>= 0.4'}
895
896
is-glob@4.0.3:
···
901
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
902
engines: {node: '>= 0.4'}
903
904
-
is-negative-zero@2.0.3:
905
-
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
906
-
engines: {node: '>= 0.4'}
907
-
908
-
is-number-object@1.0.7:
909
-
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
910
engines: {node: '>= 0.4'}
911
912
is-number@7.0.0:
913
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
914
engines: {node: '>=0.12.0'}
915
916
-
is-regex@1.1.4:
917
-
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
918
engines: {node: '>= 0.4'}
919
920
is-set@2.0.3:
921
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
922
engines: {node: '>= 0.4'}
923
924
-
is-shared-array-buffer@1.0.3:
925
-
resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
926
engines: {node: '>= 0.4'}
927
928
-
is-string@1.0.7:
929
-
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
930
engines: {node: '>= 0.4'}
931
932
-
is-symbol@1.0.4:
933
-
resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
934
engines: {node: '>= 0.4'}
935
936
-
is-typed-array@1.1.13:
937
-
resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
938
engines: {node: '>= 0.4'}
939
940
is-weakmap@2.0.2:
941
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
942
engines: {node: '>= 0.4'}
943
944
-
is-weakref@1.0.2:
945
-
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
946
947
-
is-weakset@2.0.3:
948
-
resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==}
949
engines: {node: '>= 0.4'}
950
951
isarray@2.0.5:
···
954
isexe@2.0.0:
955
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
956
957
-
iterator.prototype@1.1.3:
958
-
resolution: {integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==}
959
engines: {node: '>= 0.4'}
960
961
js-tokens@4.0.0:
962
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
···
996
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
997
hasBin: true
998
999
merge2@1.4.1:
1000
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
1001
engines: {node: '>= 8'}
···
1004
resolution: {integrity: sha512-OyvYIOgpzXREySYJ1cqEb2pOKdeQMTfF9M8dRU6nC4hi/GXMmNpe9ssZCrSoTHazu05BSAoRBN/uYeco+ymfOg==}
1005
engines: {node: '>=18.0.0'}
1006
1007
micromatch@4.0.8:
1008
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
1009
engines: {node: '>=8.6'}
1010
1011
minimatch@3.1.2:
1012
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
1013
···
1015
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
1016
engines: {node: '>=16 || 14 >=14.17'}
1017
1018
-
ms@2.1.2:
1019
-
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
1020
1021
nanotar@0.1.1:
1022
resolution: {integrity: sha512-AiJsGsSF3O0havL1BydvI4+wR76sKT+okKRwWIaK96cZUnXqH0uNBOsHlbwZq3+m2BR1VKqHDVudl3gO4mYjpQ==}
1023
1024
natural-compare@1.4.0:
1025
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
1026
1027
object-assign@4.1.1:
1028
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1029
engines: {node: '>=0.10.0'}
1030
1031
-
object-inspect@1.13.2:
1032
-
resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
1033
engines: {node: '>= 0.4'}
1034
1035
object-keys@1.1.1:
1036
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
1037
engines: {node: '>= 0.4'}
1038
1039
-
object.assign@4.1.5:
1040
-
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
1041
engines: {node: '>= 0.4'}
1042
1043
-
object.entries@1.1.8:
1044
-
resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==}
1045
engines: {node: '>= 0.4'}
1046
1047
object.fromentries@2.0.8:
1048
resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
1049
engines: {node: '>= 0.4'}
1050
1051
-
object.values@1.2.0:
1052
-
resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
1053
engines: {node: '>= 0.4'}
1054
1055
optionator@0.9.3:
1056
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
1057
engines: {node: '>= 0.8.0'}
1058
1059
p-limit@3.1.0:
1060
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
1061
engines: {node: '>=10'}
···
1063
p-locate@5.0.0:
1064
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
1065
engines: {node: '>=10'}
1066
1067
parent-module@1.0.1:
1068
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
···
1079
path-parse@1.0.7:
1080
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
1081
1082
picomatch@2.3.1:
1083
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
1084
engines: {node: '>=8.6'}
1085
1086
-
possible-typed-array-names@1.0.0:
1087
-
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
1088
engines: {node: '>= 0.4'}
1089
1090
prelude-ls@1.2.1:
···
1111
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
1112
engines: {node: '>=6'}
1113
1114
queue-microtask@1.2.3:
1115
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
1116
···
1121
resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==}
1122
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
1123
1124
-
reflect.getprototypeof@1.0.6:
1125
-
resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
1126
engines: {node: '>= 0.4'}
1127
1128
-
regexp.prototype.flags@1.5.3:
1129
-
resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==}
1130
engines: {node: '>= 0.4'}
1131
1132
resolve-from@4.0.0:
···
1137
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
1138
hasBin: true
1139
1140
reusify@1.0.4:
1141
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
1142
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
···
1144
run-parallel@1.2.0:
1145
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
1146
1147
-
safe-array-concat@1.1.2:
1148
-
resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
1149
engines: {node: '>=0.4'}
1150
-
1151
-
safe-buffer@5.1.2:
1152
-
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
1153
1154
safe-buffer@5.2.1:
1155
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
1156
1157
-
safe-regex-test@1.0.3:
1158
-
resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
1159
engines: {node: '>= 0.4'}
1160
1161
semver@6.3.1:
1162
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
1163
hasBin: true
1164
1165
-
semver@7.6.3:
1166
-
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
1167
engines: {node: '>=10'}
1168
hasBin: true
1169
···
1175
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
1176
engines: {node: '>= 0.4'}
1177
1178
shebang-command@2.0.0:
1179
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
1180
engines: {node: '>=8'}
···
1183
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
1184
engines: {node: '>=8'}
1185
1186
-
side-channel@1.0.6:
1187
-
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
1188
engines: {node: '>= 0.4'}
1189
1190
standalone-electron-types@1.0.0:
1191
resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==}
1192
1193
-
string.prototype.matchall@4.0.11:
1194
-
resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==}
1195
engines: {node: '>= 0.4'}
1196
1197
string.prototype.repeat@1.0.0:
1198
resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
1199
1200
-
string.prototype.trim@1.2.9:
1201
-
resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
1202
engines: {node: '>= 0.4'}
1203
1204
-
string.prototype.trimend@1.0.8:
1205
-
resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
1206
1207
string.prototype.trimstart@1.0.8:
1208
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
···
1223
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
1224
engines: {node: '>= 0.4'}
1225
1226
-
synckit@0.9.2:
1227
-
resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==}
1228
engines: {node: ^14.18.0 || >=16.0.0}
1229
1230
-
text-table@0.2.0:
1231
-
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
1232
1233
to-regex-range@5.0.1:
1234
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
1235
engines: {node: '>=8.0'}
1236
1237
-
ts-api-utils@1.3.0:
1238
-
resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
1239
-
engines: {node: '>=16'}
1240
peerDependencies:
1241
-
typescript: '>=4.2.0'
1242
1243
-
tslib@2.7.0:
1244
-
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
1245
1246
type-check@0.4.0:
1247
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
1248
engines: {node: '>= 0.8.0'}
1249
1250
-
typed-array-buffer@1.0.2:
1251
-
resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
1252
engines: {node: '>= 0.4'}
1253
1254
-
typed-array-byte-length@1.0.1:
1255
-
resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
1256
engines: {node: '>= 0.4'}
1257
1258
-
typed-array-byte-offset@1.0.2:
1259
-
resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
1260
engines: {node: '>= 0.4'}
1261
1262
-
typed-array-length@1.0.6:
1263
-
resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
1264
engines: {node: '>= 0.4'}
1265
1266
-
typescript-eslint@8.8.1:
1267
-
resolution: {integrity: sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==}
1268
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1269
peerDependencies:
1270
-
typescript: '*'
1271
-
peerDependenciesMeta:
1272
-
typescript:
1273
-
optional: true
1274
1275
-
typescript@5.3.2:
1276
-
resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==}
1277
engines: {node: '>=14.17'}
1278
hasBin: true
1279
1280
-
unbox-primitive@1.0.2:
1281
-
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
1282
1283
-
undici-types@6.19.8:
1284
-
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
1285
1286
uri-js@4.4.1:
1287
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
1288
1289
-
utilium@0.7.1:
1290
-
resolution: {integrity: sha512-2ocvTkI7U8LERmwxL0LhFUvEfN66UqcjF6tMiURvUwSyU7U1QC9gST+3iSUSiGccFfnP3f2EXwHNXOnOzx+lAg==}
1291
1292
-
which-boxed-primitive@1.0.2:
1293
-
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
1294
1295
-
which-builtin-type@1.1.4:
1296
-
resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==}
1297
engines: {node: '>= 0.4'}
1298
1299
which-collection@1.0.2:
1300
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
1301
engines: {node: '>= 0.4'}
1302
1303
-
which-typed-array@1.1.15:
1304
-
resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
1305
engines: {node: '>= 0.4'}
1306
1307
which@2.0.2:
···
1309
engines: {node: '>= 8'}
1310
hasBin: true
1311
1312
yocto-queue@0.1.0:
1313
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
1314
engines: {node: '>=10'}
1315
1316
snapshots:
1317
1318
'@aashutoshrathi/word-wrap@1.2.6': {}
1319
1320
'@esbuild/android-arm64@0.19.3':
1321
optional: true
···
1383
'@esbuild/win32-x64@0.19.3':
1384
optional: true
1385
1386
-
'@eslint-community/eslint-utils@4.4.0(eslint@9.12.0)':
1387
dependencies:
1388
-
eslint: 9.12.0
1389
eslint-visitor-keys: 3.4.3
1390
1391
-
'@eslint-community/regexpp@4.11.1': {}
1392
1393
-
'@eslint/config-array@0.18.0':
1394
dependencies:
1395
-
'@eslint/object-schema': 2.1.4
1396
-
debug: 4.3.4
1397
minimatch: 3.1.2
1398
transitivePeerDependencies:
1399
- supports-color
1400
1401
-
'@eslint/core@0.6.0': {}
1402
1403
-
'@eslint/eslintrc@3.1.0':
1404
dependencies:
1405
ajv: 6.12.6
1406
-
debug: 4.3.4
1407
-
espree: 10.2.0
1408
globals: 14.0.0
1409
-
ignore: 5.3.0
1410
import-fresh: 3.3.0
1411
js-yaml: 4.1.0
1412
minimatch: 3.1.2
···
1414
transitivePeerDependencies:
1415
- supports-color
1416
1417
-
'@eslint/js@9.12.0': {}
1418
1419
-
'@eslint/object-schema@2.1.4': {}
1420
1421
-
'@eslint/plugin-kit@0.2.0':
1422
dependencies:
1423
levn: 0.4.1
1424
1425
-
'@humanfs/core@0.19.0': {}
1426
1427
-
'@humanfs/node@0.16.5':
1428
dependencies:
1429
-
'@humanfs/core': 0.19.0
1430
'@humanwhocodes/retry': 0.3.1
1431
1432
'@humanwhocodes/module-importer@1.0.1': {}
1433
1434
'@humanwhocodes/retry@0.3.1': {}
1435
1436
-
'@moonlight-mod/eslint-config@https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/7eb7bd7c51fe0e3ee9d2a0baf149212d2bb893af(eslint@9.12.0)(prettier@3.1.0)(typescript@5.3.2)':
1437
dependencies:
1438
-
'@eslint/js': 9.12.0
1439
-
eslint: 9.12.0
1440
-
eslint-config-prettier: 9.1.0(eslint@9.12.0)
1441
-
eslint-plugin-prettier: 5.2.1(eslint-config-prettier@9.1.0(eslint@9.12.0))(eslint@9.12.0)(prettier@3.1.0)
1442
-
eslint-plugin-react: 7.37.1(eslint@9.12.0)
1443
-
typescript: 5.3.2
1444
-
typescript-eslint: 8.8.1(eslint@9.12.0)(typescript@5.3.2)
1445
transitivePeerDependencies:
1446
- '@types/eslint'
1447
- prettier
1448
- supports-color
1449
1450
-
'@moonlight-mod/lunast@1.0.0':
1451
dependencies:
1452
astring: 1.9.0
1453
estree-toolkit: 1.7.8
1454
meriyah: 6.0.1
1455
1456
-
'@moonlight-mod/mappings@1.0.10(@moonlight-mod/lunast@1.0.0)(@moonlight-mod/moonmap@1.0.3)':
1457
dependencies:
1458
-
'@moonlight-mod/lunast': 1.0.0
1459
-
'@moonlight-mod/moonmap': 1.0.3
1460
'@types/flux': 3.1.14
1461
-
'@types/react': 18.3.10
1462
csstype: 3.1.3
1463
1464
-
'@moonlight-mod/moonmap@1.0.3': {}
1465
1466
'@nodelib/fs.scandir@2.1.5':
1467
dependencies:
···
1475
'@nodelib/fs.scandir': 2.1.5
1476
fastq: 1.17.1
1477
1478
-
'@pkgr/core@0.1.1': {}
1479
1480
'@types/estree-jsx@1.0.5':
1481
dependencies:
···
1483
1484
'@types/estree@1.0.6': {}
1485
1486
'@types/fbemitter@2.0.35': {}
1487
1488
'@types/flux@3.1.14':
1489
dependencies:
1490
'@types/fbemitter': 2.0.35
1491
-
'@types/react': 18.3.10
1492
1493
'@types/json-schema@7.0.15': {}
1494
1495
'@types/node@18.17.17': {}
1496
1497
-
'@types/node@20.16.10':
1498
dependencies:
1499
-
undici-types: 6.19.8
1500
1501
'@types/prop-types@15.7.13': {}
1502
1503
-
'@types/react@18.3.10':
1504
dependencies:
1505
'@types/prop-types': 15.7.13
1506
csstype: 3.1.3
1507
1508
-
'@types/readable-stream@4.0.15':
1509
dependencies:
1510
-
'@types/node': 20.16.10
1511
-
safe-buffer: 5.1.2
1512
-
1513
-
'@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.3.2))(eslint@9.12.0)(typescript@5.3.2)':
1514
-
dependencies:
1515
-
'@eslint-community/regexpp': 4.11.1
1516
-
'@typescript-eslint/parser': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
1517
-
'@typescript-eslint/scope-manager': 8.8.1
1518
-
'@typescript-eslint/type-utils': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
1519
-
'@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
1520
-
'@typescript-eslint/visitor-keys': 8.8.1
1521
-
eslint: 9.12.0
1522
graphemer: 1.4.0
1523
ignore: 5.3.2
1524
natural-compare: 1.4.0
1525
-
ts-api-utils: 1.3.0(typescript@5.3.2)
1526
-
optionalDependencies:
1527
-
typescript: 5.3.2
1528
transitivePeerDependencies:
1529
- supports-color
1530
1531
-
'@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.3.2)':
1532
dependencies:
1533
-
'@typescript-eslint/scope-manager': 8.8.1
1534
-
'@typescript-eslint/types': 8.8.1
1535
-
'@typescript-eslint/typescript-estree': 8.8.1(typescript@5.3.2)
1536
-
'@typescript-eslint/visitor-keys': 8.8.1
1537
-
debug: 4.3.4
1538
-
eslint: 9.12.0
1539
-
optionalDependencies:
1540
-
typescript: 5.3.2
1541
transitivePeerDependencies:
1542
- supports-color
1543
1544
-
'@typescript-eslint/scope-manager@8.8.1':
1545
dependencies:
1546
-
'@typescript-eslint/types': 8.8.1
1547
-
'@typescript-eslint/visitor-keys': 8.8.1
1548
1549
-
'@typescript-eslint/type-utils@8.8.1(eslint@9.12.0)(typescript@5.3.2)':
1550
dependencies:
1551
-
'@typescript-eslint/typescript-estree': 8.8.1(typescript@5.3.2)
1552
-
'@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
1553
-
debug: 4.3.4
1554
-
ts-api-utils: 1.3.0(typescript@5.3.2)
1555
-
optionalDependencies:
1556
-
typescript: 5.3.2
1557
transitivePeerDependencies:
1558
-
- eslint
1559
- supports-color
1560
1561
-
'@typescript-eslint/types@8.8.1': {}
1562
1563
-
'@typescript-eslint/typescript-estree@8.8.1(typescript@5.3.2)':
1564
dependencies:
1565
-
'@typescript-eslint/types': 8.8.1
1566
-
'@typescript-eslint/visitor-keys': 8.8.1
1567
-
debug: 4.3.4
1568
fast-glob: 3.3.2
1569
is-glob: 4.0.3
1570
minimatch: 9.0.5
1571
-
semver: 7.6.3
1572
-
ts-api-utils: 1.3.0(typescript@5.3.2)
1573
-
optionalDependencies:
1574
-
typescript: 5.3.2
1575
transitivePeerDependencies:
1576
- supports-color
1577
1578
-
'@typescript-eslint/utils@8.8.1(eslint@9.12.0)(typescript@5.3.2)':
1579
dependencies:
1580
-
'@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0)
1581
-
'@typescript-eslint/scope-manager': 8.8.1
1582
-
'@typescript-eslint/types': 8.8.1
1583
-
'@typescript-eslint/typescript-estree': 8.8.1(typescript@5.3.2)
1584
-
eslint: 9.12.0
1585
transitivePeerDependencies:
1586
- supports-color
1587
-
- typescript
1588
1589
-
'@typescript-eslint/visitor-keys@8.8.1':
1590
dependencies:
1591
-
'@typescript-eslint/types': 8.8.1
1592
-
eslint-visitor-keys: 3.4.3
1593
1594
-
'@zenfs/core@1.0.2':
1595
dependencies:
1596
-
'@types/node': 20.16.10
1597
-
'@types/readable-stream': 4.0.15
1598
buffer: 6.0.3
1599
eventemitter3: 5.0.1
1600
-
minimatch: 9.0.5
1601
readable-stream: 4.5.2
1602
-
utilium: 0.7.1
1603
1604
-
'@zenfs/dom@0.2.16(@zenfs/core@1.0.2)':
1605
dependencies:
1606
-
'@zenfs/core': 1.0.2
1607
1608
abort-controller@3.0.0:
1609
dependencies:
1610
event-target-shim: 5.0.1
1611
1612
-
acorn-jsx@5.3.2(acorn@8.12.1):
1613
dependencies:
1614
-
acorn: 8.12.1
1615
1616
-
acorn@8.12.1: {}
1617
1618
ajv@6.12.6:
1619
dependencies:
···
1626
dependencies:
1627
color-convert: 2.0.1
1628
1629
argparse@2.0.1: {}
1630
1631
-
array-buffer-byte-length@1.0.1:
1632
dependencies:
1633
-
call-bind: 1.0.7
1634
-
is-array-buffer: 3.0.4
1635
1636
array-includes@3.1.8:
1637
dependencies:
1638
-
call-bind: 1.0.7
1639
define-properties: 1.2.1
1640
-
es-abstract: 1.23.3
1641
-
es-object-atoms: 1.0.0
1642
-
get-intrinsic: 1.2.4
1643
-
is-string: 1.0.7
1644
1645
array.prototype.findlast@1.2.5:
1646
dependencies:
1647
-
call-bind: 1.0.7
1648
define-properties: 1.2.1
1649
-
es-abstract: 1.23.3
1650
es-errors: 1.3.0
1651
-
es-object-atoms: 1.0.0
1652
-
es-shim-unscopables: 1.0.2
1653
1654
-
array.prototype.flat@1.3.2:
1655
dependencies:
1656
-
call-bind: 1.0.7
1657
define-properties: 1.2.1
1658
-
es-abstract: 1.23.3
1659
-
es-shim-unscopables: 1.0.2
1660
1661
-
array.prototype.flatmap@1.3.2:
1662
dependencies:
1663
-
call-bind: 1.0.7
1664
define-properties: 1.2.1
1665
-
es-abstract: 1.23.3
1666
-
es-shim-unscopables: 1.0.2
1667
1668
array.prototype.tosorted@1.1.4:
1669
dependencies:
1670
-
call-bind: 1.0.7
1671
define-properties: 1.2.1
1672
-
es-abstract: 1.23.3
1673
es-errors: 1.3.0
1674
-
es-shim-unscopables: 1.0.2
1675
1676
-
arraybuffer.prototype.slice@1.0.3:
1677
dependencies:
1678
-
array-buffer-byte-length: 1.0.1
1679
-
call-bind: 1.0.7
1680
define-properties: 1.2.1
1681
-
es-abstract: 1.23.3
1682
es-errors: 1.3.0
1683
-
get-intrinsic: 1.2.4
1684
-
is-array-buffer: 3.0.4
1685
-
is-shared-array-buffer: 1.0.3
1686
1687
astring@1.9.0: {}
1688
1689
available-typed-arrays@1.0.7:
1690
dependencies:
1691
-
possible-typed-array-names: 1.0.0
1692
1693
balanced-match@1.0.2: {}
1694
···
1712
base64-js: 1.5.1
1713
ieee754: 1.2.1
1714
1715
-
call-bind@1.0.7:
1716
dependencies:
1717
-
es-define-property: 1.0.0
1718
es-errors: 1.3.0
1719
function-bind: 1.1.2
1720
-
get-intrinsic: 1.2.4
1721
set-function-length: 1.2.2
1722
1723
callsites@3.1.0: {}
1724
···
1735
1736
concat-map@0.0.1: {}
1737
1738
-
cross-spawn@7.0.3:
1739
dependencies:
1740
path-key: 3.1.1
1741
shebang-command: 2.0.0
1742
which: 2.0.2
1743
1744
-
csstype@3.1.2: {}
1745
-
1746
csstype@3.1.3: {}
1747
1748
-
data-view-buffer@1.0.1:
1749
dependencies:
1750
-
call-bind: 1.0.7
1751
es-errors: 1.3.0
1752
-
is-data-view: 1.0.1
1753
1754
-
data-view-byte-length@1.0.1:
1755
dependencies:
1756
-
call-bind: 1.0.7
1757
es-errors: 1.3.0
1758
-
is-data-view: 1.0.1
1759
1760
-
data-view-byte-offset@1.0.0:
1761
dependencies:
1762
-
call-bind: 1.0.7
1763
es-errors: 1.3.0
1764
-
is-data-view: 1.0.1
1765
1766
-
debug@4.3.4:
1767
dependencies:
1768
-
ms: 2.1.2
1769
1770
deep-is@0.1.4: {}
1771
1772
define-data-property@1.1.4:
1773
dependencies:
1774
-
es-define-property: 1.0.0
1775
es-errors: 1.3.0
1776
-
gopd: 1.0.1
1777
1778
define-properties@1.2.1:
1779
dependencies:
···
1781
has-property-descriptors: 1.0.2
1782
object-keys: 1.1.1
1783
1784
doctrine@2.1.0:
1785
dependencies:
1786
esutils: 2.0.3
1787
1788
-
es-abstract@1.23.3:
1789
dependencies:
1790
-
array-buffer-byte-length: 1.0.1
1791
-
arraybuffer.prototype.slice: 1.0.3
1792
available-typed-arrays: 1.0.7
1793
-
call-bind: 1.0.7
1794
-
data-view-buffer: 1.0.1
1795
-
data-view-byte-length: 1.0.1
1796
-
data-view-byte-offset: 1.0.0
1797
-
es-define-property: 1.0.0
1798
es-errors: 1.3.0
1799
-
es-object-atoms: 1.0.0
1800
-
es-set-tostringtag: 2.0.3
1801
-
es-to-primitive: 1.2.1
1802
-
function.prototype.name: 1.1.6
1803
-
get-intrinsic: 1.2.4
1804
-
get-symbol-description: 1.0.2
1805
globalthis: 1.0.4
1806
-
gopd: 1.0.1
1807
has-property-descriptors: 1.0.2
1808
-
has-proto: 1.0.3
1809
-
has-symbols: 1.0.3
1810
hasown: 2.0.2
1811
-
internal-slot: 1.0.7
1812
-
is-array-buffer: 3.0.4
1813
is-callable: 1.2.7
1814
-
is-data-view: 1.0.1
1815
-
is-negative-zero: 2.0.3
1816
-
is-regex: 1.1.4
1817
-
is-shared-array-buffer: 1.0.3
1818
-
is-string: 1.0.7
1819
-
is-typed-array: 1.1.13
1820
-
is-weakref: 1.0.2
1821
-
object-inspect: 1.13.2
1822
object-keys: 1.1.1
1823
-
object.assign: 4.1.5
1824
-
regexp.prototype.flags: 1.5.3
1825
-
safe-array-concat: 1.1.2
1826
-
safe-regex-test: 1.0.3
1827
-
string.prototype.trim: 1.2.9
1828
-
string.prototype.trimend: 1.0.8
1829
string.prototype.trimstart: 1.0.8
1830
-
typed-array-buffer: 1.0.2
1831
-
typed-array-byte-length: 1.0.1
1832
-
typed-array-byte-offset: 1.0.2
1833
-
typed-array-length: 1.0.6
1834
-
unbox-primitive: 1.0.2
1835
-
which-typed-array: 1.1.15
1836
1837
-
es-define-property@1.0.0:
1838
-
dependencies:
1839
-
get-intrinsic: 1.2.4
1840
1841
es-errors@1.3.0: {}
1842
1843
-
es-iterator-helpers@1.1.0:
1844
dependencies:
1845
-
call-bind: 1.0.7
1846
define-properties: 1.2.1
1847
-
es-abstract: 1.23.3
1848
es-errors: 1.3.0
1849
-
es-set-tostringtag: 2.0.3
1850
function-bind: 1.1.2
1851
-
get-intrinsic: 1.2.4
1852
globalthis: 1.0.4
1853
has-property-descriptors: 1.0.2
1854
-
has-proto: 1.0.3
1855
-
has-symbols: 1.0.3
1856
-
internal-slot: 1.0.7
1857
-
iterator.prototype: 1.1.3
1858
-
safe-array-concat: 1.1.2
1859
1860
-
es-object-atoms@1.0.0:
1861
dependencies:
1862
es-errors: 1.3.0
1863
1864
-
es-set-tostringtag@2.0.3:
1865
dependencies:
1866
-
get-intrinsic: 1.2.4
1867
has-tostringtag: 1.0.2
1868
hasown: 2.0.2
1869
1870
-
es-shim-unscopables@1.0.2:
1871
dependencies:
1872
hasown: 2.0.2
1873
1874
-
es-to-primitive@1.2.1:
1875
dependencies:
1876
is-callable: 1.2.7
1877
-
is-date-object: 1.0.5
1878
-
is-symbol: 1.0.4
1879
1880
esbuild-copy-static-files@0.1.0: {}
1881
···
1906
1907
escape-string-regexp@4.0.0: {}
1908
1909
-
eslint-config-prettier@9.1.0(eslint@9.12.0):
1910
dependencies:
1911
-
eslint: 9.12.0
1912
1913
-
eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@9.12.0))(eslint@9.12.0)(prettier@3.1.0):
1914
dependencies:
1915
-
eslint: 9.12.0
1916
prettier: 3.1.0
1917
prettier-linter-helpers: 1.0.0
1918
-
synckit: 0.9.2
1919
optionalDependencies:
1920
-
eslint-config-prettier: 9.1.0(eslint@9.12.0)
1921
1922
-
eslint-plugin-react@7.37.1(eslint@9.12.0):
1923
dependencies:
1924
array-includes: 3.1.8
1925
array.prototype.findlast: 1.2.5
1926
-
array.prototype.flatmap: 1.3.2
1927
array.prototype.tosorted: 1.1.4
1928
doctrine: 2.1.0
1929
-
es-iterator-helpers: 1.1.0
1930
-
eslint: 9.12.0
1931
estraverse: 5.3.0
1932
hasown: 2.0.2
1933
jsx-ast-utils: 3.3.5
1934
minimatch: 3.1.2
1935
-
object.entries: 1.1.8
1936
object.fromentries: 2.0.8
1937
-
object.values: 1.2.0
1938
prop-types: 15.8.1
1939
resolve: 2.0.0-next.5
1940
semver: 6.3.1
1941
-
string.prototype.matchall: 4.0.11
1942
string.prototype.repeat: 1.0.0
1943
1944
-
eslint-scope@8.1.0:
1945
dependencies:
1946
esrecurse: 4.3.0
1947
estraverse: 5.3.0
1948
1949
eslint-visitor-keys@3.4.3: {}
1950
1951
-
eslint-visitor-keys@4.1.0: {}
1952
1953
-
eslint@9.12.0:
1954
dependencies:
1955
-
'@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0)
1956
-
'@eslint-community/regexpp': 4.11.1
1957
-
'@eslint/config-array': 0.18.0
1958
-
'@eslint/core': 0.6.0
1959
-
'@eslint/eslintrc': 3.1.0
1960
-
'@eslint/js': 9.12.0
1961
-
'@eslint/plugin-kit': 0.2.0
1962
-
'@humanfs/node': 0.16.5
1963
'@humanwhocodes/module-importer': 1.0.1
1964
-
'@humanwhocodes/retry': 0.3.1
1965
'@types/estree': 1.0.6
1966
'@types/json-schema': 7.0.15
1967
ajv: 6.12.6
1968
chalk: 4.1.2
1969
-
cross-spawn: 7.0.3
1970
-
debug: 4.3.4
1971
escape-string-regexp: 4.0.0
1972
-
eslint-scope: 8.1.0
1973
-
eslint-visitor-keys: 4.1.0
1974
-
espree: 10.2.0
1975
-
esquery: 1.5.0
1976
esutils: 2.0.3
1977
fast-deep-equal: 3.1.3
1978
file-entry-cache: 8.0.0
1979
find-up: 5.0.0
1980
glob-parent: 6.0.2
1981
-
ignore: 5.3.0
1982
imurmurhash: 0.1.4
1983
is-glob: 4.0.3
1984
json-stable-stringify-without-jsonify: 1.0.1
···
1986
minimatch: 3.1.2
1987
natural-compare: 1.4.0
1988
optionator: 0.9.3
1989
-
text-table: 0.2.0
1990
transitivePeerDependencies:
1991
- supports-color
1992
1993
-
espree@10.2.0:
1994
dependencies:
1995
-
acorn: 8.12.1
1996
-
acorn-jsx: 5.3.2(acorn@8.12.1)
1997
-
eslint-visitor-keys: 4.1.0
1998
1999
-
esquery@1.5.0:
2000
dependencies:
2001
estraverse: 5.3.0
2002
···
2039
dependencies:
2040
reusify: 1.0.4
2041
2042
file-entry-cache@8.0.0:
2043
dependencies:
2044
flat-cache: 4.0.1
···
2047
dependencies:
2048
to-regex-range: 5.0.1
2049
2050
find-up@5.0.0:
2051
dependencies:
2052
locate-path: 6.0.0
···
2059
2060
flatted@3.2.9: {}
2061
2062
-
for-each@0.3.3:
2063
dependencies:
2064
is-callable: 1.2.7
2065
2066
function-bind@1.1.2: {}
2067
2068
-
function.prototype.name@1.1.6:
2069
dependencies:
2070
-
call-bind: 1.0.7
2071
define-properties: 1.2.1
2072
-
es-abstract: 1.23.3
2073
functions-have-names: 1.2.3
2074
2075
functions-have-names@1.2.3: {}
2076
2077
-
get-intrinsic@1.2.4:
2078
dependencies:
2079
es-errors: 1.3.0
2080
function-bind: 1.1.2
2081
-
has-proto: 1.0.3
2082
-
has-symbols: 1.0.3
2083
hasown: 2.0.2
2084
2085
-
get-symbol-description@1.0.2:
2086
dependencies:
2087
-
call-bind: 1.0.7
2088
es-errors: 1.3.0
2089
-
get-intrinsic: 1.2.4
2090
2091
glob-parent@5.1.2:
2092
dependencies:
···
2101
globalthis@1.0.4:
2102
dependencies:
2103
define-properties: 1.2.1
2104
-
gopd: 1.0.1
2105
2106
-
gopd@1.0.1:
2107
-
dependencies:
2108
-
get-intrinsic: 1.2.4
2109
2110
graphemer@1.4.0: {}
2111
2112
-
has-bigints@1.0.2: {}
2113
2114
has-flag@4.0.0: {}
2115
2116
has-property-descriptors@1.0.2:
2117
dependencies:
2118
-
es-define-property: 1.0.0
2119
2120
-
has-proto@1.0.3: {}
2121
2122
-
has-symbols@1.0.3: {}
2123
2124
has-tostringtag@1.0.2:
2125
dependencies:
2126
-
has-symbols: 1.0.3
2127
2128
hasown@2.0.2:
2129
dependencies:
···
2132
husky@8.0.3: {}
2133
2134
ieee754@1.2.1: {}
2135
-
2136
-
ignore@5.3.0: {}
2137
2138
ignore@5.3.2: {}
2139
···
2144
2145
imurmurhash@0.1.4: {}
2146
2147
-
internal-slot@1.0.7:
2148
dependencies:
2149
es-errors: 1.3.0
2150
hasown: 2.0.2
2151
-
side-channel: 1.0.6
2152
2153
-
is-array-buffer@3.0.4:
2154
dependencies:
2155
-
call-bind: 1.0.7
2156
-
get-intrinsic: 1.2.4
2157
2158
-
is-async-function@2.0.0:
2159
dependencies:
2160
has-tostringtag: 1.0.2
2161
2162
-
is-bigint@1.0.4:
2163
dependencies:
2164
-
has-bigints: 1.0.2
2165
2166
-
is-boolean-object@1.1.2:
2167
dependencies:
2168
-
call-bind: 1.0.7
2169
has-tostringtag: 1.0.2
2170
2171
is-callable@1.2.7: {}
2172
2173
-
is-core-module@2.15.1:
2174
dependencies:
2175
hasown: 2.0.2
2176
2177
-
is-data-view@1.0.1:
2178
dependencies:
2179
-
is-typed-array: 1.1.13
2180
2181
-
is-date-object@1.0.5:
2182
dependencies:
2183
has-tostringtag: 1.0.2
2184
2185
is-extglob@2.1.1: {}
2186
2187
-
is-finalizationregistry@1.0.2:
2188
dependencies:
2189
-
call-bind: 1.0.7
2190
2191
-
is-generator-function@1.0.10:
2192
dependencies:
2193
has-tostringtag: 1.0.2
2194
2195
is-glob@4.0.3:
2196
dependencies:
···
2198
2199
is-map@2.0.3: {}
2200
2201
-
is-negative-zero@2.0.3: {}
2202
-
2203
-
is-number-object@1.0.7:
2204
dependencies:
2205
has-tostringtag: 1.0.2
2206
2207
is-number@7.0.0: {}
2208
2209
-
is-regex@1.1.4:
2210
dependencies:
2211
-
call-bind: 1.0.7
2212
has-tostringtag: 1.0.2
2213
2214
is-set@2.0.3: {}
2215
2216
-
is-shared-array-buffer@1.0.3:
2217
dependencies:
2218
-
call-bind: 1.0.7
2219
2220
-
is-string@1.0.7:
2221
dependencies:
2222
has-tostringtag: 1.0.2
2223
2224
-
is-symbol@1.0.4:
2225
dependencies:
2226
-
has-symbols: 1.0.3
2227
2228
-
is-typed-array@1.1.13:
2229
dependencies:
2230
-
which-typed-array: 1.1.15
2231
2232
is-weakmap@2.0.2: {}
2233
2234
-
is-weakref@1.0.2:
2235
dependencies:
2236
-
call-bind: 1.0.7
2237
2238
-
is-weakset@2.0.3:
2239
dependencies:
2240
-
call-bind: 1.0.7
2241
-
get-intrinsic: 1.2.4
2242
2243
isarray@2.0.5: {}
2244
2245
isexe@2.0.0: {}
2246
2247
-
iterator.prototype@1.1.3:
2248
dependencies:
2249
-
define-properties: 1.2.1
2250
-
get-intrinsic: 1.2.4
2251
-
has-symbols: 1.0.3
2252
-
reflect.getprototypeof: 1.0.6
2253
set-function-name: 2.0.2
2254
2255
js-tokens@4.0.0: {}
2256
···
2267
jsx-ast-utils@3.3.5:
2268
dependencies:
2269
array-includes: 3.1.8
2270
-
array.prototype.flat: 1.3.2
2271
-
object.assign: 4.1.5
2272
-
object.values: 1.2.0
2273
2274
keyv@4.5.4:
2275
dependencies:
···
2290
dependencies:
2291
js-tokens: 4.0.0
2292
2293
merge2@1.4.1: {}
2294
2295
meriyah@6.0.1: {}
2296
2297
micromatch@4.0.8:
2298
dependencies:
2299
braces: 3.0.3
2300
picomatch: 2.3.1
2301
2302
minimatch@3.1.2:
2303
dependencies:
···
2307
dependencies:
2308
brace-expansion: 2.0.1
2309
2310
-
ms@2.1.2: {}
2311
2312
nanotar@0.1.1: {}
2313
2314
natural-compare@1.4.0: {}
2315
2316
object-assign@4.1.1: {}
2317
2318
-
object-inspect@1.13.2: {}
2319
2320
object-keys@1.1.1: {}
2321
2322
-
object.assign@4.1.5:
2323
dependencies:
2324
-
call-bind: 1.0.7
2325
define-properties: 1.2.1
2326
-
has-symbols: 1.0.3
2327
object-keys: 1.1.1
2328
2329
-
object.entries@1.1.8:
2330
dependencies:
2331
-
call-bind: 1.0.7
2332
define-properties: 1.2.1
2333
-
es-object-atoms: 1.0.0
2334
2335
object.fromentries@2.0.8:
2336
dependencies:
2337
-
call-bind: 1.0.7
2338
define-properties: 1.2.1
2339
-
es-abstract: 1.23.3
2340
-
es-object-atoms: 1.0.0
2341
2342
-
object.values@1.2.0:
2343
dependencies:
2344
-
call-bind: 1.0.7
2345
define-properties: 1.2.1
2346
-
es-object-atoms: 1.0.0
2347
2348
optionator@0.9.3:
2349
dependencies:
···
2354
prelude-ls: 1.2.1
2355
type-check: 0.4.0
2356
2357
p-limit@3.1.0:
2358
dependencies:
2359
yocto-queue: 0.1.0
···
2362
dependencies:
2363
p-limit: 3.1.0
2364
2365
parent-module@1.0.1:
2366
dependencies:
2367
callsites: 3.1.0
···
2372
2373
path-parse@1.0.7: {}
2374
2375
picomatch@2.3.1: {}
2376
2377
-
possible-typed-array-names@1.0.0: {}
2378
2379
prelude-ls@1.2.1: {}
2380
···
2394
2395
punycode@2.3.1: {}
2396
2397
queue-microtask@1.2.3: {}
2398
2399
react-is@16.13.1: {}
···
2406
process: 0.11.10
2407
string_decoder: 1.3.0
2408
2409
-
reflect.getprototypeof@1.0.6:
2410
dependencies:
2411
-
call-bind: 1.0.7
2412
define-properties: 1.2.1
2413
-
es-abstract: 1.23.3
2414
es-errors: 1.3.0
2415
-
get-intrinsic: 1.2.4
2416
-
globalthis: 1.0.4
2417
-
which-builtin-type: 1.1.4
2418
2419
-
regexp.prototype.flags@1.5.3:
2420
dependencies:
2421
-
call-bind: 1.0.7
2422
define-properties: 1.2.1
2423
es-errors: 1.3.0
2424
set-function-name: 2.0.2
2425
2426
resolve-from@4.0.0: {}
2427
2428
resolve@2.0.0-next.5:
2429
dependencies:
2430
-
is-core-module: 2.15.1
2431
path-parse: 1.0.7
2432
supports-preserve-symlinks-flag: 1.0.0
2433
2434
reusify@1.0.4: {}
2435
2436
run-parallel@1.2.0:
2437
dependencies:
2438
queue-microtask: 1.2.3
2439
2440
-
safe-array-concat@1.1.2:
2441
dependencies:
2442
-
call-bind: 1.0.7
2443
-
get-intrinsic: 1.2.4
2444
-
has-symbols: 1.0.3
2445
isarray: 2.0.5
2446
-
2447
-
safe-buffer@5.1.2: {}
2448
2449
safe-buffer@5.2.1: {}
2450
2451
-
safe-regex-test@1.0.3:
2452
dependencies:
2453
-
call-bind: 1.0.7
2454
es-errors: 1.3.0
2455
-
is-regex: 1.1.4
2456
2457
semver@6.3.1: {}
2458
2459
-
semver@7.6.3: {}
2460
2461
set-function-length@1.2.2:
2462
dependencies:
2463
define-data-property: 1.1.4
2464
es-errors: 1.3.0
2465
function-bind: 1.1.2
2466
-
get-intrinsic: 1.2.4
2467
-
gopd: 1.0.1
2468
has-property-descriptors: 1.0.2
2469
2470
set-function-name@2.0.2:
···
2473
es-errors: 1.3.0
2474
functions-have-names: 1.2.3
2475
has-property-descriptors: 1.0.2
2476
2477
shebang-command@2.0.0:
2478
dependencies:
···
2480
2481
shebang-regex@3.0.0: {}
2482
2483
-
side-channel@1.0.6:
2484
dependencies:
2485
-
call-bind: 1.0.7
2486
es-errors: 1.3.0
2487
-
get-intrinsic: 1.2.4
2488
-
object-inspect: 1.13.2
2489
2490
standalone-electron-types@1.0.0:
2491
dependencies:
2492
'@types/node': 18.17.17
2493
2494
-
string.prototype.matchall@4.0.11:
2495
dependencies:
2496
-
call-bind: 1.0.7
2497
define-properties: 1.2.1
2498
-
es-abstract: 1.23.3
2499
es-errors: 1.3.0
2500
-
es-object-atoms: 1.0.0
2501
-
get-intrinsic: 1.2.4
2502
-
gopd: 1.0.1
2503
-
has-symbols: 1.0.3
2504
-
internal-slot: 1.0.7
2505
-
regexp.prototype.flags: 1.5.3
2506
set-function-name: 2.0.2
2507
-
side-channel: 1.0.6
2508
2509
string.prototype.repeat@1.0.0:
2510
dependencies:
2511
define-properties: 1.2.1
2512
-
es-abstract: 1.23.3
2513
2514
-
string.prototype.trim@1.2.9:
2515
dependencies:
2516
-
call-bind: 1.0.7
2517
define-properties: 1.2.1
2518
-
es-abstract: 1.23.3
2519
-
es-object-atoms: 1.0.0
2520
2521
-
string.prototype.trimend@1.0.8:
2522
dependencies:
2523
-
call-bind: 1.0.7
2524
define-properties: 1.2.1
2525
-
es-object-atoms: 1.0.0
2526
2527
string.prototype.trimstart@1.0.8:
2528
dependencies:
2529
-
call-bind: 1.0.7
2530
define-properties: 1.2.1
2531
-
es-object-atoms: 1.0.0
2532
2533
string_decoder@1.3.0:
2534
dependencies:
···
2542
2543
supports-preserve-symlinks-flag@1.0.0: {}
2544
2545
-
synckit@0.9.2:
2546
dependencies:
2547
-
'@pkgr/core': 0.1.1
2548
-
tslib: 2.7.0
2549
2550
-
text-table@0.2.0: {}
2551
2552
to-regex-range@5.0.1:
2553
dependencies:
2554
is-number: 7.0.0
2555
2556
-
ts-api-utils@1.3.0(typescript@5.3.2):
2557
dependencies:
2558
-
typescript: 5.3.2
2559
2560
-
tslib@2.7.0: {}
2561
2562
type-check@0.4.0:
2563
dependencies:
2564
prelude-ls: 1.2.1
2565
2566
-
typed-array-buffer@1.0.2:
2567
dependencies:
2568
-
call-bind: 1.0.7
2569
es-errors: 1.3.0
2570
-
is-typed-array: 1.1.13
2571
2572
-
typed-array-byte-length@1.0.1:
2573
dependencies:
2574
-
call-bind: 1.0.7
2575
-
for-each: 0.3.3
2576
-
gopd: 1.0.1
2577
-
has-proto: 1.0.3
2578
-
is-typed-array: 1.1.13
2579
2580
-
typed-array-byte-offset@1.0.2:
2581
dependencies:
2582
available-typed-arrays: 1.0.7
2583
-
call-bind: 1.0.7
2584
-
for-each: 0.3.3
2585
-
gopd: 1.0.1
2586
-
has-proto: 1.0.3
2587
-
is-typed-array: 1.1.13
2588
2589
-
typed-array-length@1.0.6:
2590
dependencies:
2591
-
call-bind: 1.0.7
2592
-
for-each: 0.3.3
2593
-
gopd: 1.0.1
2594
-
has-proto: 1.0.3
2595
-
is-typed-array: 1.1.13
2596
-
possible-typed-array-names: 1.0.0
2597
2598
-
typescript-eslint@8.8.1(eslint@9.12.0)(typescript@5.3.2):
2599
dependencies:
2600
-
'@typescript-eslint/eslint-plugin': 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.3.2))(eslint@9.12.0)(typescript@5.3.2)
2601
-
'@typescript-eslint/parser': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
2602
-
'@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
2603
-
optionalDependencies:
2604
-
typescript: 5.3.2
2605
transitivePeerDependencies:
2606
-
- eslint
2607
- supports-color
2608
2609
-
typescript@5.3.2: {}
2610
2611
-
unbox-primitive@1.0.2:
2612
dependencies:
2613
-
call-bind: 1.0.7
2614
-
has-bigints: 1.0.2
2615
-
has-symbols: 1.0.3
2616
-
which-boxed-primitive: 1.0.2
2617
2618
-
undici-types@6.19.8: {}
2619
2620
uri-js@4.4.1:
2621
dependencies:
2622
punycode: 2.3.1
2623
2624
-
utilium@0.7.1:
2625
dependencies:
2626
eventemitter3: 5.0.1
2627
2628
-
which-boxed-primitive@1.0.2:
2629
dependencies:
2630
-
is-bigint: 1.0.4
2631
-
is-boolean-object: 1.1.2
2632
-
is-number-object: 1.0.7
2633
-
is-string: 1.0.7
2634
-
is-symbol: 1.0.4
2635
2636
-
which-builtin-type@1.1.4:
2637
dependencies:
2638
-
function.prototype.name: 1.1.6
2639
has-tostringtag: 1.0.2
2640
-
is-async-function: 2.0.0
2641
-
is-date-object: 1.0.5
2642
-
is-finalizationregistry: 1.0.2
2643
-
is-generator-function: 1.0.10
2644
-
is-regex: 1.1.4
2645
-
is-weakref: 1.0.2
2646
isarray: 2.0.5
2647
-
which-boxed-primitive: 1.0.2
2648
which-collection: 1.0.2
2649
-
which-typed-array: 1.1.15
2650
2651
which-collection@1.0.2:
2652
dependencies:
2653
is-map: 2.0.3
2654
is-set: 2.0.3
2655
is-weakmap: 2.0.2
2656
-
is-weakset: 2.0.3
2657
2658
-
which-typed-array@1.1.15:
2659
dependencies:
2660
available-typed-arrays: 1.0.7
2661
-
call-bind: 1.0.7
2662
-
for-each: 0.3.3
2663
-
gopd: 1.0.1
2664
has-tostringtag: 1.0.2
2665
2666
which@2.0.2:
2667
dependencies:
2668
isexe: 2.0.0
2669
2670
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:
···
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:
···
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:
···
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
···
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
···
196
'@aashutoshrathi/word-wrap@1.2.6':
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':
205
resolution: {integrity: sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==}
···
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':
···
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==}
···
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
···
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:
···
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==}
···
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:
···
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: '*'
···
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:
···
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:
···
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'}
···
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'}
···
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:
···
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==}
···
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:
···
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'}
···
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:
···
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:
···
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'}
···
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
···
1188
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
1189
engines: {node: '>=16 || 14 >=14.17'}
1190
1191
+
ms@2.1.3:
1192
+
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
1193
1194
nanotar@0.1.1:
1195
resolution: {integrity: sha512-AiJsGsSF3O0havL1BydvI4+wR76sKT+okKRwWIaK96cZUnXqH0uNBOsHlbwZq3+m2BR1VKqHDVudl3gO4mYjpQ==}
1196
1197
natural-compare@1.4.0:
1198
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
1199
+
1200
+
node-fetch-native@1.6.6:
1201
+
resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==}
1202
1203
object-assign@4.1.1:
1204
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1205
engines: {node: '>=0.10.0'}
1206
1207
+
object-inspect@1.13.4:
1208
+
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
1209
engines: {node: '>= 0.4'}
1210
1211
object-keys@1.1.1:
1212
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
1213
engines: {node: '>= 0.4'}
1214
1215
+
object.assign@4.1.7:
1216
+
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
1217
engines: {node: '>= 0.4'}
1218
1219
+
object.entries@1.1.9:
1220
+
resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
1221
engines: {node: '>= 0.4'}
1222
1223
object.fromentries@2.0.8:
1224
resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
1225
engines: {node: '>= 0.4'}
1226
1227
+
object.values@1.2.1:
1228
+
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
1229
engines: {node: '>= 0.4'}
1230
+
1231
+
ofetch@1.4.1:
1232
+
resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==}
1233
+
1234
+
onetime@7.0.0:
1235
+
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
1236
+
engines: {node: '>=18'}
1237
1238
optionator@0.9.3:
1239
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
1240
engines: {node: '>= 0.8.0'}
1241
1242
+
own-keys@1.0.1:
1243
+
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
1244
+
engines: {node: '>= 0.4'}
1245
+
1246
p-limit@3.1.0:
1247
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
1248
engines: {node: '>=10'}
···
1250
p-locate@5.0.0:
1251
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
1252
engines: {node: '>=10'}
1253
+
1254
+
package-manager-detector@1.1.0:
1255
+
resolution: {integrity: sha512-Y8f9qUlBzW8qauJjd/eu6jlpJZsuPJm2ZAV0cDVd420o4EdpH5RPdoCv+60/TdJflGatr4sDfpAL6ArWZbM5tA==}
1256
1257
parent-module@1.0.1:
1258
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
···
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:
···
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
···
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'}
···
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
···
1383
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
1384
engines: {node: '>= 0.4'}
1385
1386
+
set-proto@1.0.0:
1387
+
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
1388
+
engines: {node: '>= 0.4'}
1389
+
1390
shebang-command@2.0.0:
1391
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
1392
engines: {node: '>=8'}
···
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==}
···
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:
···
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:
···
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:
···
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
···
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
···
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:
···
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
···
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
···
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
···
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
···
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:
···
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:
···
2510
husky@8.0.3: {}
2511
2512
ieee754@1.2.1: {}
2513
2514
ignore@5.3.2: {}
2515
···
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:
···
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
···
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:
···
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
···
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:
2791
callsites: 3.1.0
···
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
···
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: {}
···
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:
···
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:
···
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.js
-78
scripts/link.js
···
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
-
}
···
+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
+
}
-29
scripts/update.js
-29
scripts/update.js
···
1
-
// Update dependencies in all packages
2
-
/* eslint-disable no-console */
3
-
const fs = require("fs");
4
-
const path = require("path");
5
-
const child_process = require("child_process");
6
-
7
-
const packageToUpdate = process.argv[2];
8
-
9
-
function getDeps(packageJSON) {
10
-
const ret = {};
11
-
Object.assign(ret, packageJSON.dependencies || {});
12
-
Object.assign(ret, packageJSON.devDependencies || {});
13
-
Object.assign(ret, packageJSON.peerDependencies || {});
14
-
return ret;
15
-
}
16
-
17
-
function exec(cmd, dir) {
18
-
child_process.execSync(cmd, { cwd: dir, stdio: "inherit" });
19
-
}
20
-
21
-
for (const package of fs.readdirSync("./packages")) {
22
-
const packageJSON = JSON.parse(fs.readFileSync(path.join("./packages", package, "package.json"), "utf8"));
23
-
24
-
const deps = getDeps(packageJSON);
25
-
if (Object.keys(deps).includes(packageToUpdate)) {
26
-
console.log(`Updating ${packageToUpdate} in ${package}`);
27
-
exec(`pnpm update ${packageToUpdate}`, path.join("./packages", package));
28
-
}
29
-
}
···
+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
-16
tsconfig.json
+7
-16
tsconfig.json
···
1
{
2
"compilerOptions": {
3
-
"target": "es2022",
4
-
"module": "es2020",
5
-
"esModuleInterop": true,
6
-
"forceConsistentCasingInFileNames": true,
7
-
"strict": true,
8
-
"moduleResolution": "bundler",
9
"baseUrl": "./packages/",
10
-
"jsx": "react",
11
-
"noEmit": true,
12
-
13
-
// meriyah has a broken import lol
14
-
"skipLibCheck": true,
15
-
16
-
// disable unreachable code detection because it breaks with esbuild labels
17
-
"allowUnreachableCode": true
18
},
19
-
"include": ["./packages/**/*", "./env.d.ts"],
20
-
"exclude": ["node_modules"]
21
}