this repo has no description

Compare changes

Choose any two refs to compare.

Changed files
+6374 -2100
.github
.vscode
nix
packages
browser
core
core-extensions
injector
node-preload
types
web-preload
scripts
+4 -8
.github/workflows/browser.yml
···
name: Browser extension builds
runs-on: ubuntu-latest
steps:
-
- uses: actions/checkout@v3
-
-
- uses: pnpm/action-setup@v2
-
with:
-
version: 9
-
run_install: false
-
- uses: actions/setup-node@v3
with:
-
node-version: 18
cache: pnpm
- name: Install dependencies
···
name: Browser extension builds
runs-on: ubuntu-latest
steps:
+
- uses: actions/checkout@v4
+
- uses: pnpm/action-setup@v4
+
- uses: actions/setup-node@v4
with:
+
node-version: 22
cache: pnpm
- name: Install dependencies
+4 -8
.github/workflows/lint.yml
···
name: Lint commits
runs-on: ubuntu-latest
steps:
-
- uses: actions/checkout@v3
-
-
- uses: pnpm/action-setup@v2
-
with:
-
version: 9
-
run_install: false
-
- uses: actions/setup-node@v3
with:
-
node-version: 18
cache: pnpm
- name: Install dependencies
···
name: Lint commits
runs-on: ubuntu-latest
steps:
+
- uses: actions/checkout@v4
+
- uses: pnpm/action-setup@v4
+
- uses: actions/setup-node@v4
with:
+
node-version: 22
cache: pnpm
- name: Install dependencies
+7 -11
.github/workflows/nightly.yml
···
name: Nightly builds on GitHub Pages
runs-on: ubuntu-latest
steps:
-
- uses: actions/checkout@v3
-
-
- uses: pnpm/action-setup@v2
-
with:
-
version: 9
-
run_install: false
-
- uses: actions/setup-node@v3
with:
-
node-version: 18
cache: pnpm
- name: Install dependencies
···
echo "$(date +%s)" >> ./dist/ref
- name: Setup GitHub Pages
-
uses: actions/configure-pages@v3
- name: Upload artifact
-
uses: actions/upload-pages-artifact@v1
with:
path: ./dist
- name: Deploy to GitHub Pages
-
uses: actions/deploy-pages@v2
···
name: Nightly builds on GitHub Pages
runs-on: ubuntu-latest
steps:
+
- uses: actions/checkout@v4
+
- uses: pnpm/action-setup@v4
+
- uses: actions/setup-node@v4
with:
+
node-version: 22
cache: pnpm
- name: Install dependencies
···
echo "$(date +%s)" >> ./dist/ref
- name: Setup GitHub Pages
+
uses: actions/configure-pages@v5
- name: Upload artifact
+
uses: actions/upload-pages-artifact@v3
with:
path: ./dist
- name: Deploy to GitHub Pages
+
uses: actions/deploy-pages@v4
+16
.github/workflows/nix.yml
···
···
+
name: Check Nix flake
+
on: [push, pull_request]
+
+
permissions:
+
checks: write
+
+
jobs:
+
nix:
+
name: Check Nix flake
+
runs-on: ubuntu-latest
+
steps:
+
- uses: actions/checkout@v4
+
- uses: DeterminateSystems/nix-installer-action@main
+
+
- name: Build default flake output
+
run: nix build
+4 -8
.github/workflows/release.yml
···
name: Release builds to GitHub Releases
runs-on: ubuntu-latest
steps:
-
- uses: actions/checkout@v3
-
-
- uses: pnpm/action-setup@v2
-
with:
-
version: 9
-
run_install: false
-
- uses: actions/setup-node@v3
with:
-
node-version: 18
cache: pnpm
- name: Install dependencies
···
name: Release builds to GitHub Releases
runs-on: ubuntu-latest
steps:
+
- uses: actions/checkout@v4
+
- uses: pnpm/action-setup@v4
+
- uses: actions/setup-node@v4
with:
+
node-version: 22
cache: pnpm
- name: Install dependencies
+5 -11
.github/workflows/types.yml
···
name: Publish types on npm
runs-on: ubuntu-latest
steps:
-
- uses: actions/checkout@v3
-
-
- uses: pnpm/action-setup@v2
-
with:
-
version: 9
-
run_install: false
-
- uses: actions/setup-node@v3
with:
-
node-version: 18
cache: pnpm
registry-url: https://registry.npmjs.org
···
run: pnpm run build
- name: Publish types
-
run: |
-
cd packages/types
-
pnpm publish --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
···
name: Publish types on npm
runs-on: ubuntu-latest
steps:
+
- uses: actions/checkout@v4
+
- uses: pnpm/action-setup@v4
+
- uses: actions/setup-node@v4
with:
+
node-version: 22
cache: pnpm
registry-url: https://registry.npmjs.org
···
run: pnpm run build
- name: Publish types
+
run: pnpm publish --filter=./packages/types --access public --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+5 -1
.gitignore
···
dist.tar.gz
.DS_Store
eslint_report.json
-
# Nix
/result
*.drv
···
dist.tar.gz
.DS_Store
eslint_report.json
+
.eslintcache
# Nix
/result
*.drv
+
+
# IDEs
+
.vscode/
+
.idea/
+4 -4
.prettierrc
···
{
-
"printWidth": 120,
-
"trailingComma": "none",
-
"tabWidth": 2,
-
"singleQuote": false
}
···
{
+
"printWidth": 120,
+
"trailingComma": "none",
+
"tabWidth": 2,
+
"singleQuote": false
}
-14
.vscode/tasks.json
···
-
{
-
"version": "2.0.0",
-
"tasks": [
-
{
-
"label": "build",
-
"type": "shell",
-
"command": "pnpm run build",
-
"group": {
-
"kind": "build",
-
"isDefault": true
-
}
-
}
-
]
-
}
···
+4 -5
CHANGELOG.md
···
-
- Fixes for latest Discord
-
- Added a warning when disabling built in extensions in Moonbase
-
- Added a prompt to restart when updating an extension in Moonbase
-
- Added a button to view extension source in Moonbase
-
- Searching for settings of extensions now works properly in Moonbase
···
+
## Core
+
+
- Updated mappings
+
- Fixed using remapped paths as patch finds not working
+10 -3
README.md
···
<img src="./img/wordmark.png" alt="moonlight" />
</picture>
-
<a href="https://discord.gg/FdZBTFCP6F">Discord server</a>
\- <a href="https://github.com/moonlight-mod/moonlight">GitHub</a>
-
\- <a href="https://moonlight-mod.github.io/">Docs</a>
<hr />
</h3>
**moonlight** is yet another Discord client mod, focused on providing a decent user and developer experience.
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.
-
**_This is an experimental passion project._** Anything and everything is subject to change, but it is stable enough for developers to experiment with.
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.
···
<img src="./img/wordmark.png" alt="moonlight" />
</picture>
+
<a href="https://moonlight-mod.github.io/using/install">Install</a>
+
\- <a href="https://moonlight-mod.github.io/ext-dev/getting-started">Docs</a>
+
\- <a href="https://discord.gg/FdZBTFCP6F">Discord server</a>
\- <a href="https://github.com/moonlight-mod/moonlight">GitHub</a>
<hr />
+
+
<picture>
+
<source media="(prefers-color-scheme: dark)" srcset="https://moonlight-mod.github.io/moonbase.png">
+
<source media="(prefers-color-scheme: light)" srcset="https://moonlight-mod.github.io/moonbase-light.png">
+
<img src="https://moonlight-mod.github.io/moonbase.png" alt="A screenshot of Moonbase, the moonlight UI" />
+
</picture>
</h3>
**moonlight** is yet another Discord client mod, focused on providing a decent user and developer experience.
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.
+
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.
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
···
"type": "github"
}
},
-
"flake-utils_2": {
-
"inputs": {
-
"systems": "systems_2"
-
},
-
"locked": {
-
"lastModified": 1701680307,
-
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
-
"owner": "numtide",
-
"repo": "flake-utils",
-
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
-
"type": "github"
-
},
-
"original": {
-
"owner": "numtide",
-
"repo": "flake-utils",
-
"type": "github"
-
}
-
},
"nixpkgs": {
"locked": {
-
"lastModified": 1728067476,
-
"narHash": "sha256-/uJcVXuBt+VFCPQIX+4YnYrHaubJSx4HoNsJVNRgANM=",
"owner": "NixOS",
"repo": "nixpkgs",
-
"rev": "6e6b3dd395c3b1eb9be9f2d096383a8d05add030",
"type": "github"
},
"original": {
"owner": "NixOS",
-
"ref": "nixos-24.05",
-
"repo": "nixpkgs",
-
"type": "github"
-
}
-
},
-
"nixpkgs_2": {
-
"locked": {
-
"lastModified": 1727802920,
-
"narHash": "sha256-HP89HZOT0ReIbI7IJZJQoJgxvB2Tn28V6XS3MNKnfLs=",
-
"owner": "nixos",
-
"repo": "nixpkgs",
-
"rev": "27e30d177e57d912d614c88c622dcfdb2e6e6515",
-
"type": "github"
-
},
-
"original": {
-
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
-
"pnpm2nix": {
-
"inputs": {
-
"flake-utils": "flake-utils_2",
-
"nixpkgs": "nixpkgs_2"
-
},
-
"locked": {
-
"lastModified": 1728137762,
-
"narHash": "sha256-iEFvPR3BopGyI5KjQ1DK+gEZ1dKDugq838tKdet2moQ=",
-
"owner": "NotNite",
-
"repo": "pnpm2nix-nzbr",
-
"rev": "b7a60d3c7d106b601665e3f05dba6cdc6f59f959",
-
"type": "github"
-
},
-
"original": {
-
"owner": "NotNite",
-
"repo": "pnpm2nix-nzbr",
-
"type": "github"
-
}
-
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
-
"nixpkgs": "nixpkgs",
-
"pnpm2nix": "pnpm2nix"
}
},
"systems": {
-
"locked": {
-
"lastModified": 1681028828,
-
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
-
"owner": "nix-systems",
-
"repo": "default",
-
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
-
"type": "github"
-
},
-
"original": {
-
"owner": "nix-systems",
-
"repo": "default",
-
"type": "github"
-
}
-
},
-
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
···
"type": "github"
}
},
"nixpkgs": {
"locked": {
+
"lastModified": 1744232761,
+
"narHash": "sha256-gbl9hE39nQRpZaLjhWKmEu5ejtQsgI5TWYrIVVJn30U=",
"owner": "NixOS",
"repo": "nixpkgs",
+
"rev": "f675531bc7e6657c10a18b565cfebd8aa9e24c14",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
+
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+3 -4
flake.nix
···
description = "Yet another Discord mod";
inputs = {
-
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
flake-utils.url = "github:numtide/flake-utils";
-
pnpm2nix.url = "github:NotNite/pnpm2nix-nzbr";
};
-
outputs = { self, nixpkgs, flake-utils, pnpm2nix }:
-
let overlay = import ./nix/overlay.nix { inherit pnpm2nix; };
in flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
···
description = "Yet another Discord mod";
inputs = {
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
+
outputs = { self, nixpkgs, flake-utils }:
+
let overlay = import ./nix/overlay.nix { };
in flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
+40 -17
nix/default.nix
···
-
{ pkgs, mkPnpmPackage }:
-
mkPnpmPackage rec {
-
workspace = ./..;
src = ./..;
-
# Work around a bug with how it expects dist
-
components = [
-
"packages/core"
-
"packages/core-extensions"
-
"packages/injector"
-
"packages/node-preload"
-
"packages/types"
-
"packages/web-preload"
];
-
distDirs = [ "dist" ];
-
copyNodeModules = true;
-
buildPhase = "pnpm run build";
-
installPhase = "cp -r dist $out";
-
meta = with pkgs.lib; {
description = "Yet another Discord mod";
homepage = "https://moonlight-mod.github.io/";
license = licenses.lgpl3;
maintainers = with maintainers; [ notnite ];
};
-
}
···
+
{
+
lib,
+
stdenv,
+
nodejs_22,
+
pnpm_10,
+
}:
+
+
stdenv.mkDerivation (finalAttrs: {
+
pname = "moonlight";
+
version = (builtins.fromJSON (builtins.readFile ./../package.json)).version;
src = ./..;
+
nativeBuildInputs = [
+
nodejs_22
+
pnpm_10.configHook
];
+
+
pnpmDeps = pnpm_10.fetchDeps {
+
inherit (finalAttrs) pname version src;
+
hash = "sha256-I+zRCUqJabpGJRFBGW0NrM9xzyzeCjioF54zlCpynBU=";
+
};
+
env = {
+
NODE_ENV = "production";
+
MOONLIGHT_VERSION = "v${finalAttrs.version}";
+
};
+
+
buildPhase = ''
+
runHook preBuild
+
pnpm run build
+
+
runHook postBuild
+
'';
+
+
installPhase = ''
+
runHook preInstall
+
+
cp -r dist $out
+
+
runHook postInstall
+
'';
+
+
meta = with lib; {
description = "Yet another Discord mod";
homepage = "https://moonlight-mod.github.io/";
license = licenses.lgpl3;
maintainers = with maintainers; [ notnite ];
};
+
})
+3 -6
nix/overlay.nix
···
-
{ pnpm2nix }:
let
nameTable = {
···
'';
packageJson = ''
-
{"name":"discord","main":"./injector.js","private":true}
'';
in old.installPhase + "\n" + ''
···
'';
});
in final: prev: rec {
-
moonlight-mod = final.callPackage ./default.nix {
-
pkgs = final;
-
mkPnpmPackage = pnpm2nix.packages.${final.system}.mkPnpmPackage;
-
};
discord = mkOverride prev moonlight-mod "discord";
discord-ptb = mkOverride prev moonlight-mod "discord-ptb";
discord-canary = mkOverride prev moonlight-mod "discord-canary";
···
+
{ ... }:
let
nameTable = {
···
'';
packageJson = ''
+
{"name":"${name}","main":"./injector.js","private":true}
'';
in old.installPhase + "\n" + ''
···
'';
});
in final: prev: rec {
+
moonlight-mod = final.callPackage ./default.nix { };
discord = mkOverride prev moonlight-mod "discord";
discord-ptb = mkOverride prev moonlight-mod "discord-ptb";
discord-canary = mkOverride prev moonlight-mod "discord-canary";
+23 -12
package.json
···
{
"name": "moonlight",
-
"version": "1.2.4",
"description": "Yet another Discord mod",
-
"homepage": "https://moonlight-mod.github.io/",
"license": "LGPL-3.0-or-later",
"repository": {
"type": "git",
"url": "git+https://github.com/moonlight-mod/moonlight.git"
···
"bugs": {
"url": "https://github.com/moonlight-mod/moonlight/issues"
},
"scripts": {
"build": "node build.mjs",
"dev": "node build.mjs --watch",
···
"browser": "node build.mjs --browser",
"browser-mv2": "node build.mjs --browser --mv2",
"lint": "eslint packages",
-
"lint:fix": "eslint packages --fix",
-
"lint:report": "eslint --output-file eslint_report.json --format json packages",
"typecheck": "tsc --noEmit",
"check": "pnpm run lint && pnpm run typecheck",
-
"prepare": "husky install"
},
"devDependencies": {
-
"esbuild": "^0.19.3",
-
"esbuild-copy-static-files": "^0.1.0",
-
"eslint": "^9.12.0",
-
"@moonlight-mod/eslint-config": "github:moonlight-mod/eslint-config",
-
"husky": "^8.0.3",
-
"prettier": "^3.1.0",
-
"typescript": "^5.3.2"
}
}
···
{
"name": "moonlight",
+
"version": "1.3.14",
+
"packageManager": "pnpm@10.7.1",
"description": "Yet another Discord mod",
"license": "LGPL-3.0-or-later",
+
"homepage": "https://moonlight-mod.github.io/",
"repository": {
"type": "git",
"url": "git+https://github.com/moonlight-mod/moonlight.git"
···
"bugs": {
"url": "https://github.com/moonlight-mod/moonlight/issues"
},
+
"engineStrict": true,
+
"engines": {
+
"node": ">=22",
+
"pnpm": ">=10",
+
"npm": "pnpm",
+
"yarn": "pnpm"
+
},
"scripts": {
"build": "node build.mjs",
"dev": "node build.mjs --watch",
···
"browser": "node build.mjs --browser",
"browser-mv2": "node build.mjs --browser --mv2",
"lint": "eslint packages",
+
"lint:fix": "pnpm lint --fix",
+
"lint:report": "pnpm lint --output-file eslint_report.json --format json",
"typecheck": "tsc --noEmit",
"check": "pnpm run lint && pnpm run typecheck",
+
"prepare": "husky install",
+
"updates": "pnpm taze -r"
},
"devDependencies": {
+
"@moonlight-mod/eslint-config": "catalog:dev",
+
"@types/node": "catalog:dev",
+
"esbuild": "catalog:dev",
+
"esbuild-copy-static-files": "catalog:dev",
+
"eslint": "catalog:dev",
+
"husky": "catalog:dev",
+
"prettier": "catalog:dev",
+
"taze": "catalog:dev",
+
"typescript": "catalog:dev"
}
}
+2 -1
packages/browser/blockLoading.json
···
"type": "block"
},
"condition": {
-
"urlFilter": "*://discord.com/assets/*.js",
"resourceTypes": ["script"]
}
}
···
"type": "block"
},
"condition": {
+
"requestDomains": ["discord.com", "discordapp.com"],
+
"urlFilter": "*/assets/*.js",
"resourceTypes": ["script"]
}
}
+10 -4
packages/browser/manifest.json
···
{
"manifest_version": 3,
"name": "moonlight",
"description": "Yet another Discord mod",
-
"version": "1.2.4",
"permissions": ["declarativeNetRequestWithHostAccess", "webRequest", "scripting", "webNavigation"],
-
"host_permissions": ["https://moonlight-mod.github.io/*", "https://api.github.com/*", "https://*.discord.com/*"],
"content_scripts": [
{
"js": ["index.js"],
-
"matches": ["https://*.discord.com/*"],
"run_at": "document_start",
"world": "MAIN"
}
···
"web_accessible_resources": [
{
"resources": ["index.js"],
-
"matches": ["https://*.discord.com/*"]
}
]
}
···
{
+
"$schema": "https://json.schemastore.org/chrome-manifest",
"manifest_version": 3,
"name": "moonlight",
"description": "Yet another Discord mod",
+
"version": "1.3.14",
"permissions": ["declarativeNetRequestWithHostAccess", "webRequest", "scripting", "webNavigation"],
+
"host_permissions": [
+
"https://moonlight-mod.github.io/*",
+
"https://api.github.com/*",
+
"https://*.discord.com/*",
+
"https://*.discordapp.com/*"
+
],
"content_scripts": [
{
"js": ["index.js"],
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"],
"run_at": "document_start",
"world": "MAIN"
}
···
"web_accessible_resources": [
{
"resources": ["index.js"],
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"]
}
]
}
+6 -5
packages/browser/manifestv2.json
···
{
"manifest_version": 2,
"name": "moonlight",
"description": "Yet another Discord mod",
-
"version": "1.2.4",
"permissions": [
"webRequest",
"webRequestBlocking",
"scripting",
"webNavigation",
-
"https://*.discord.com/assets/*.js",
"https://moonlight-mod.github.io/*",
-
"https://api.github.com/*",
-
"https://*.discord.com/*"
],
"background": {
"scripts": ["background.js"]
···
"content_scripts": [
{
"js": ["index.js"],
-
"matches": ["https://*.discord.com/*"],
"run_at": "document_start",
"world": "MAIN"
}
···
{
+
"$schema": "https://json.schemastore.org/chrome-manifest",
"manifest_version": 2,
"name": "moonlight",
"description": "Yet another Discord mod",
+
"version": "1.3.14",
"permissions": [
"webRequest",
"webRequestBlocking",
"scripting",
"webNavigation",
+
"https://*.discord.com/*",
+
"https://*.discordapp.com/*",
"https://moonlight-mod.github.io/*",
+
"https://api.github.com/*"
],
"background": {
"scripts": ["background.js"]
···
"content_scripts": [
{
"js": ["index.js"],
+
"matches": ["https://*.discord.com/*", "https://*.discordapp.com/*"],
"run_at": "document_start",
"world": "MAIN"
}
+1 -1
packages/browser/modifyResponseHeaders.json
···
},
"condition": {
"resourceTypes": ["main_frame"],
-
"initiatorDomains": ["discord.com"]
}
}
]
···
},
"condition": {
"resourceTypes": ["main_frame"],
+
"requestDomains": ["discord.com"]
}
}
]
+12 -2
packages/browser/package.json
···
{
"name": "@moonlight-mod/browser",
"private": true,
"dependencies": {
"@moonlight-mod/core": "workspace:*",
"@moonlight-mod/types": "workspace:*",
"@moonlight-mod/web-preload": "workspace:*",
-
"@zenfs/core": "^1.0.2",
-
"@zenfs/dom": "^0.2.16"
}
}
···
{
"name": "@moonlight-mod/browser",
"private": true,
+
"engines": {
+
"node": ">=22",
+
"pnpm": ">=10",
+
"npm": "pnpm",
+
"yarn": "pnpm"
+
},
"dependencies": {
"@moonlight-mod/core": "workspace:*",
"@moonlight-mod/types": "workspace:*",
"@moonlight-mod/web-preload": "workspace:*",
+
"@zenfs/core": "catalog:prod",
+
"@zenfs/dom": "catalog:prod"
+
},
+
"engineStrict": true,
+
"devDependencies": {
+
"@types/chrome": "catalog:dev"
}
}
+55 -70
packages/browser/src/background-mv2.js
···
/* eslint-disable no-console */
/* eslint-disable no-undef */
-
const starterUrls = ["web.", "sentry."];
-
let blockLoading = true;
-
let doing = false;
-
let collectedUrls = new Set();
-
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
-
const url = new URL(details.url);
-
if (!blockLoading && url.hostname.endsWith("discord.com")) {
-
console.log("Blocking", details.url);
-
blockLoading = true;
-
collectedUrls.clear();
-
}
-
});
-
async function doTheThing(urls, tabId) {
-
console.log("Doing", urls, tabId);
-
blockLoading = false;
-
try {
-
await chrome.scripting.executeScript({
-
target: { tabId },
-
world: "MAIN",
-
args: [urls],
-
func: async (urls) => {
-
try {
-
await window._moonlightBrowserInit();
-
} catch (e) {
-
console.log(e);
-
}
-
const scripts = [...document.querySelectorAll("script")].filter(
-
(script) => script.src && urls.some((url) => url.includes(script.src))
-
);
-
// backwards
-
urls.reverse();
-
for (const url of urls) {
-
const script = scripts.find((script) => url.includes(script.src));
-
console.log("adding new script", script);
-
const newScript = document.createElement("script");
-
for (const { name, value } of script.attributes) {
-
newScript.setAttribute(name, value);
}
-
-
script.remove();
-
document.documentElement.appendChild(newScript);
-
}
-
}
-
});
-
} catch (e) {
-
console.log(e);
-
}
-
-
doing = false;
-
collectedUrls.clear();
-
}
-
-
chrome.webRequest.onBeforeRequest.addListener(
-
async (details) => {
-
if (starterUrls.some((url) => details.url.includes(url))) {
-
console.log("Adding", details.url);
-
collectedUrls.add(details.url);
}
-
if (collectedUrls.size === starterUrls.length) {
-
if (doing) return;
-
if (!blockLoading) return;
-
doing = true;
-
const urls = [...collectedUrls];
-
const tabId = details.tabId;
-
-
// yes this is a load-bearing sleep
-
setTimeout(() => doTheThing(urls, tabId), 0);
-
}
-
-
if (blockLoading) return { cancel: true };
},
{
-
urls: ["https://*.discord.com/assets/*.js"]
},
["blocking"]
);
···
)
};
},
-
{ urls: ["https://*.discord.com/*"] },
["blocking", "responseHeaders"]
);
···
/* eslint-disable no-console */
/* eslint-disable no-undef */
+
const scriptUrls = ["web.", "sentry."];
+
let blockedScripts = new Set();
+
chrome.webRequest.onBeforeRequest.addListener(
+
async (details) => {
+
if (details.tabId === -1) return;
+
const url = new URL(details.url);
+
const hasUrl = scriptUrls.some((scriptUrl) => {
+
return (
+
details.url.includes(scriptUrl) &&
+
!url.searchParams.has("inj") &&
+
(url.host.endsWith("discord.com") || url.host.endsWith("discordapp.com"))
+
);
+
});
+
if (hasUrl) blockedScripts.add(details.url);
+
if (blockedScripts.size === scriptUrls.length) {
+
const blockedScriptsCopy = Array.from(blockedScripts);
+
blockedScripts.clear();
+
setTimeout(async () => {
+
console.log("Starting moonlight");
+
await chrome.scripting.executeScript({
+
target: { tabId: details.tabId },
+
world: "MAIN",
+
args: [blockedScriptsCopy],
+
func: async (blockedScripts) => {
+
console.log("Initializing moonlight");
+
try {
+
await window._moonlightBrowserInit();
+
} catch (e) {
+
console.error(e);
+
}
+
console.log("Readding scripts");
+
try {
+
const scripts = [...document.querySelectorAll("script")].filter(
+
(script) => script.src && blockedScripts.some((url) => url.includes(script.src))
+
);
+
blockedScripts.reverse();
+
for (const url of blockedScripts) {
+
if (url.includes("/sentry.")) continue;
+
const script = scripts.find((script) => url.includes(script.src));
+
const newScript = document.createElement("script");
+
for (const attr of script.attributes) {
+
if (attr.name === "src") attr.value += "?inj";
+
newScript.setAttribute(attr.name, attr.value);
+
}
+
script.remove();
+
document.documentElement.appendChild(newScript);
+
}
+
} catch (e) {
+
console.error(e);
+
}
}
+
});
+
}, 0);
}
+
if (hasUrl) return { cancel: true };
},
{
+
urls: ["https://*.discord.com/assets/*.js", "https://*.discordapp.com/assets/*.js"]
},
["blocking"]
);
···
)
};
},
+
{ urls: ["https://*.discord.com/*", "https://*.discordapp.com/*"] },
["blocking", "responseHeaders"]
);
+37 -39
packages/browser/src/background.js
···
/* eslint-disable no-console */
/* eslint-disable no-undef */
-
const starterUrls = ["web.", "sentry."];
-
let blockLoading = true;
-
let doing = false;
-
let collectedUrls = new Set();
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
const url = new URL(details.url);
-
if (!blockLoading && url.hostname.endsWith("discord.com")) {
await chrome.declarativeNetRequest.updateEnabledRulesets({
enableRulesetIds: ["modifyResponseHeaders", "blockLoading"]
});
-
blockLoading = true;
-
collectedUrls.clear();
}
});
chrome.webRequest.onBeforeRequest.addListener(
async (details) => {
if (details.tabId === -1) return;
-
if (starterUrls.some((url) => details.url.includes(url))) {
-
console.log("Adding", details.url);
-
collectedUrls.add(details.url);
-
}
-
if (collectedUrls.size === starterUrls.length) {
-
if (doing) return;
-
if (!blockLoading) return;
-
doing = true;
-
const urls = [...collectedUrls];
-
console.log("Doing", urls);
console.log("Running moonlight script");
try {
···
files: ["index.js"]
});
} catch (e) {
-
console.log(e);
}
console.log("Initializing moonlight");
···
try {
await window._moonlightBrowserInit();
} catch (e) {
-
console.log(e);
}
}
});
···
console.log(e);
}
-
console.log("Updating rulesets");
try {
-
blockLoading = false;
await chrome.declarativeNetRequest.updateEnabledRulesets({
disableRulesetIds: ["blockLoading"],
enableRulesetIds: ["modifyResponseHeaders"]
});
} catch (e) {
-
console.log(e);
}
console.log("Readding scripts");
···
await chrome.scripting.executeScript({
target: { tabId: details.tabId },
world: "MAIN",
-
args: [urls],
-
func: async (urls) => {
const scripts = [...document.querySelectorAll("script")].filter(
-
(script) => script.src && urls.some((url) => url.includes(script.src))
);
-
// backwards
-
urls.reverse();
-
for (const url of urls) {
-
const script = scripts.find((script) => url.includes(script.src));
-
console.log("adding new script", script);
const newScript = document.createElement("script");
-
for (const { name, value } of script.attributes) {
-
newScript.setAttribute(name, value);
}
-
script.remove();
document.documentElement.appendChild(newScript);
}
}
});
} catch (e) {
-
console.log(e);
}
-
-
console.log("Done");
-
doing = false;
-
collectedUrls.clear();
}
},
{
-
urls: ["*://*.discord.com/assets/*.js"]
}
);
···
/* eslint-disable no-console */
/* eslint-disable no-undef */
+
const scriptUrls = ["web.", "sentry."];
+
let blockedScripts = new Set();
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
const url = new URL(details.url);
+
if (
+
!url.searchParams.has("inj") &&
+
(url.hostname.endsWith("discord.com") || url.hostname.endsWith("discordapp.com"))
+
) {
+
console.log("Enabling block ruleset");
await chrome.declarativeNetRequest.updateEnabledRulesets({
enableRulesetIds: ["modifyResponseHeaders", "blockLoading"]
});
}
});
chrome.webRequest.onBeforeRequest.addListener(
async (details) => {
if (details.tabId === -1) return;
+
const url = new URL(details.url);
+
const hasUrl = scriptUrls.some((scriptUrl) => {
+
return (
+
details.url.includes(scriptUrl) &&
+
!url.searchParams.has("inj") &&
+
(url.hostname.endsWith("discord.com") || url.hostname.endsWith("discordapp.com"))
+
);
+
});
+
+
if (hasUrl) blockedScripts.add(details.url);
+
+
if (blockedScripts.size === scriptUrls.length) {
+
const blockedScriptsCopy = Array.from(blockedScripts);
+
blockedScripts.clear();
console.log("Running moonlight script");
try {
···
files: ["index.js"]
});
} catch (e) {
+
console.error(e);
}
console.log("Initializing moonlight");
···
try {
await window._moonlightBrowserInit();
} catch (e) {
+
console.error(e);
}
}
});
···
console.log(e);
}
+
console.log("Disabling block ruleset");
try {
await chrome.declarativeNetRequest.updateEnabledRulesets({
disableRulesetIds: ["blockLoading"],
enableRulesetIds: ["modifyResponseHeaders"]
});
} catch (e) {
+
console.error(e);
}
console.log("Readding scripts");
···
await chrome.scripting.executeScript({
target: { tabId: details.tabId },
world: "MAIN",
+
args: [blockedScriptsCopy],
+
func: async (blockedScripts) => {
const scripts = [...document.querySelectorAll("script")].filter(
+
(script) => script.src && blockedScripts.some((url) => url.includes(script.src))
);
+
blockedScripts.reverse();
+
for (const url of blockedScripts) {
+
if (url.includes("/sentry.")) continue;
+
const script = scripts.find((script) => url.includes(script.src));
const newScript = document.createElement("script");
+
for (const attr of script.attributes) {
+
if (attr.name === "src") attr.value += "?inj";
+
newScript.setAttribute(attr.name, attr.value);
}
script.remove();
document.documentElement.appendChild(newScript);
}
}
});
} catch (e) {
+
console.error(e);
}
}
},
{
+
urls: ["*://*.discord.com/assets/*.js", "*://*.discordapp.com/assets/*.js"]
}
);
+34 -27
packages/browser/src/index.ts
···
import { getExtensions } from "@moonlight-mod/core/extension";
import { loadExtensions } from "@moonlight-mod/core/extension/loader";
import { MoonlightBranch, MoonlightNode } from "@moonlight-mod/types";
import { IndexedDB } from "@zenfs/dom";
-
import { configure } from "@zenfs/core";
import * as fs from "@zenfs/core/promises";
function getParts(path: string) {
if (path.startsWith("/")) path = path.substring(1);
···
}
window._moonlightBrowserInit = async () => {
// Set up a virtual filesystem with IndexedDB
-
await configure({
-
mounts: {
-
"/": {
-
backend: IndexedDB,
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-
// @ts-ignore tsc tweaking
-
storeName: "moonlight-fs"
-
}
-
}
});
window.moonlightNodeSandboxed = {
···
async isFile(path) {
return (await fs.stat(path)).isFile();
},
join(...parts) {
let str = parts.join("/");
···
};
// Actual loading begins here
-
const config = await readConfig();
initLogger(config);
const extensions = await getExtensions();
const processedExtensions = await loadExtensions(extensions);
-
function getConfig(ext: string) {
-
const val = config.extensions[ext];
-
if (val == null || typeof val === "boolean") return undefined;
-
return val.config;
-
}
-
const moonlightNode: MoonlightNode = {
-
config,
extensions,
processedExtensions,
nativesCache: {},
isBrowser: true,
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
-
getConfig,
-
getConfigOption: <T>(ext: string, name: string) => {
-
const config = getConfig(ext);
-
if (config == null) return undefined;
-
const option = config[name];
-
if (option == null) return undefined;
-
return option as T;
},
getNatives: () => {},
getLogger: (id: string) => {
return new Logger(id);
···
return `/extensions/${ext}`;
},
-
writeConfig
};
Object.assign(window, {
···
});
// This is set by web-preload for us
-
await window._moonlightBrowserLoad();
};
···
import { getExtensions } from "@moonlight-mod/core/extension";
import { loadExtensions } from "@moonlight-mod/core/extension/loader";
import { MoonlightBranch, MoonlightNode } from "@moonlight-mod/types";
+
import { getConfig, getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
import { IndexedDB } from "@zenfs/dom";
+
import { configureSingle } from "@zenfs/core";
import * as fs from "@zenfs/core/promises";
+
import { NodeEventPayloads, NodeEventType } from "@moonlight-mod/types/core/event";
+
import { createEventEmitter } from "@moonlight-mod/core/util/event";
function getParts(path: string) {
if (path.startsWith("/")) path = path.substring(1);
···
}
window._moonlightBrowserInit = async () => {
+
delete window._moonlightBrowserInit;
+
// Set up a virtual filesystem with IndexedDB
+
await configureSingle({
+
backend: IndexedDB,
+
storeName: "moonlight-fs"
});
window.moonlightNodeSandboxed = {
···
async isFile(path) {
return (await fs.stat(path)).isFile();
},
+
async isDir(path) {
+
return (await fs.stat(path)).isDirectory();
+
},
join(...parts) {
let str = parts.join("/");
···
};
// Actual loading begins here
+
let config = await readConfig();
initLogger(config);
const extensions = await getExtensions();
const processedExtensions = await loadExtensions(extensions);
const moonlightNode: MoonlightNode = {
+
get config() {
+
return config;
+
},
extensions,
processedExtensions,
nativesCache: {},
isBrowser: true,
+
events: createEventEmitter<NodeEventType, NodeEventPayloads>(),
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
+
getConfig(ext) {
+
return getConfig(ext, config);
},
+
getConfigOption(ext, name) {
+
const manifest = getManifest(extensions, ext);
+
return getConfigOption(ext, name, config, manifest?.settings);
+
},
+
async setConfigOption(ext, name, value) {
+
setConfigOption(config, ext, name, value);
+
await this.writeConfig(config);
+
},
+
getNatives: () => {},
getLogger: (id: string) => {
return new Logger(id);
···
return `/extensions/${ext}`;
},
+
async writeConfig(newConfig) {
+
await writeConfig(newConfig);
+
config = newConfig;
+
this.events.dispatchEvent(NodeEventType.ConfigSaved, newConfig);
+
}
};
Object.assign(window, {
···
});
// This is set by web-preload for us
+
await window._moonlightWebLoad!();
};
+1
packages/browser/tsconfig.json
···
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "ES2022"
}
}
···
{
"extends": "../../tsconfig.json",
"compilerOptions": {
+
"lib": ["DOM", "ESNext", "ESNext.AsyncIterable"],
"module": "ES2022"
}
}
+7
packages/core/package.json
···
"exports": {
"./*": "./src/*.ts"
},
"dependencies": {
"@moonlight-mod/types": "workspace:*"
}
···
"exports": {
"./*": "./src/*.ts"
},
+
"engineStrict": true,
+
"engines": {
+
"node": ">=22",
+
"pnpm": ">=10",
+
"npm": "pnpm",
+
"yarn": "pnpm"
+
},
"dependencies": {
"@moonlight-mod/types": "workspace:*"
}
+44 -27
packages/core/src/extension/loader.ts
···
import calculateDependencies from "../util/dependency";
import { createEventEmitter } from "../util/event";
import { registerStyles } from "../styles";
-
import { EventPayloads, EventType } from "@moonlight-mod/types/core/event";
const logger = new Logger("core/extension/loader");
-
function loadExtWeb(ext: DetectedExtension) {
if (ext.scripts.web != null) {
const source = ext.scripts.web + `\n//# sourceURL=${ext.id}/web.js`;
-
const fn = new Function("require", "module", "exports", source);
-
const module = { id: ext.id, exports: {} };
-
fn.apply(window, [
-
() => {
-
logger.warn("Attempted to require() from web");
-
},
-
module,
-
module.exports
-
]);
-
const exports: ExtensionWebExports = module.exports;
if (exports.patches != null) {
let idx = 0;
for (const patch of exports.patches) {
if (Array.isArray(patch.replace)) {
-
for (const replacement of patch.replace) {
-
const newPatch = Object.assign({}, patch, {
-
replace: replacement
-
});
-
-
registerPatch({ ...newPatch, ext: ext.id, id: idx });
-
idx++;
-
}
} else {
-
registerPatch({ ...patch, ext: ext.id, id: idx });
-
idx++;
}
}
}
···
async function loadExt(ext: DetectedExtension) {
webTarget: {
try {
-
loadExtWeb(ext);
} catch (e) {
logger.error(`Failed to load extension "${ext.id}"`, e);
}
···
}
export async function loadProcessedExtensions({ extensions, dependencyGraph }: ProcessedExtensions) {
-
const eventEmitter = createEventEmitter<EventType, EventPayloads>();
const finished: Set<string> = new Set();
logger.trace(
···
}
function done() {
-
eventEmitter.removeEventListener(EventType.ExtensionLoad, cb);
r();
}
-
eventEmitter.addEventListener(EventType.ExtensionLoad, cb);
if (finished.has(dep)) done();
})
);
···
await loadExt(ext);
finished.add(ext.id);
-
eventEmitter.dispatchEvent(EventType.ExtensionLoad, ext.id);
logger.debug(`Loaded "${ext.id}"`);
}
···
import calculateDependencies from "../util/dependency";
import { createEventEmitter } from "../util/event";
import { registerStyles } from "../styles";
+
import { WebEventPayloads, WebEventType } from "@moonlight-mod/types/core/event";
const logger = new Logger("core/extension/loader");
+
function evalIIFE(id: string, source: string): ExtensionWebExports {
+
const fn = new Function("require", "module", "exports", source);
+
+
const module = { id, exports: {} };
+
fn.apply(window, [
+
() => {
+
logger.warn("Attempted to require() from web");
+
},
+
module,
+
module.exports
+
]);
+
+
return module.exports;
+
}
+
+
async function evalEsm(source: string): Promise<ExtensionWebExports> {
+
// Data URLs (`data:`) don't seem to work under the CSP, but object URLs do
+
const url = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
+
+
const module = await import(url);
+
+
URL.revokeObjectURL(url);
+
+
return module;
+
}
+
+
async function loadExtWeb(ext: DetectedExtension) {
if (ext.scripts.web != null) {
const source = ext.scripts.web + `\n//# sourceURL=${ext.id}/web.js`;
+
+
let exports: ExtensionWebExports;
+
try {
+
exports = evalIIFE(ext.id, source);
+
} catch {
+
logger.trace(`Failed to load IIFE for extension ${ext.id}, trying ESM loading`);
+
exports = await evalEsm(source);
+
}
if (exports.patches != null) {
let idx = 0;
for (const patch of exports.patches) {
if (Array.isArray(patch.replace)) {
+
registerPatch({ ...patch, ext: ext.id, id: idx });
} else {
+
registerPatch({ ...patch, replace: [patch.replace], ext: ext.id, id: idx });
}
+
idx++;
}
}
···
async function loadExt(ext: DetectedExtension) {
webTarget: {
try {
+
await loadExtWeb(ext);
} catch (e) {
logger.error(`Failed to load extension "${ext.id}"`, e);
}
···
}
export async function loadProcessedExtensions({ extensions, dependencyGraph }: ProcessedExtensions) {
+
const eventEmitter = createEventEmitter<WebEventType, WebEventPayloads>();
const finished: Set<string> = new Set();
logger.trace(
···
}
function done() {
+
eventEmitter.removeEventListener(WebEventType.ExtensionLoad, cb);
r();
}
+
eventEmitter.addEventListener(WebEventType.ExtensionLoad, cb);
if (finished.has(dep)) done();
})
);
···
await loadExt(ext);
finished.add(ext.id);
+
eventEmitter.dispatchEvent(WebEventType.ExtensionLoad, ext.id);
logger.debug(`Loaded "${ext.id}"`);
}
+3 -6
packages/core/src/extension.ts
···
res.push(...(await loadDetectedExtensions(getCoreExtensionsPath(), ExtensionLoadSource.Core, seen)));
-
res.push(...(await loadDetectedExtensions(await getExtensionsPath(), ExtensionLoadSource.Normal, seen)));
-
for (const devSearchPath of config.devSearchPaths ?? []) {
res.push(...(await loadDetectedExtensions(devSearchPath, ExtensionLoadSource.Developer, seen)));
}
return res;
}
···
const ret: DetectedExtension[] = [];
const seen = new Set<string>();
-
const coreExtensionsFs: Record<string, string> = JSON.parse(
-
// @ts-expect-error shut up
-
_moonlight_coreExtensionsStr
-
);
const coreExtensions = Array.from(new Set(Object.keys(coreExtensionsFs).map((x) => x.split("/")[0])));
for (const ext of coreExtensions) {
···
res.push(...(await loadDetectedExtensions(getCoreExtensionsPath(), ExtensionLoadSource.Core, seen)));
for (const devSearchPath of config.devSearchPaths ?? []) {
res.push(...(await loadDetectedExtensions(devSearchPath, ExtensionLoadSource.Developer, seen)));
}
+
+
res.push(...(await loadDetectedExtensions(await getExtensionsPath(), ExtensionLoadSource.Normal, seen)));
return res;
}
···
const ret: DetectedExtension[] = [];
const seen = new Set<string>();
+
const coreExtensionsFs: Record<string, string> = JSON.parse(_moonlight_coreExtensionsStr);
const coreExtensions = Array.from(new Set(Object.keys(coreExtensionsFs).map((x) => x.split("/")[0])));
for (const ext of coreExtensions) {
+3
packages/core/src/fs.ts
···
async isFile(path) {
return fs.statSync(path).isFile();
},
join(...parts) {
return path.join(...parts);
···
async isFile(path) {
return fs.statSync(path).isFile();
},
+
async isDir(path) {
+
return fs.statSync(path).isDirectory();
+
},
join(...parts) {
return path.join(...parts);
+123 -69
packages/core/src/patch.ts
···
} from "@moonlight-mod/types";
import Logger from "./util/logger";
import calculateDependencies, { Dependency } from "./util/dependency";
-
import WebpackRequire from "@moonlight-mod/types/discord/require";
-
import { EventType } from "@moonlight-mod/types/core/event";
const logger = new Logger("core/patch");
···
const moduleLoadSubscriptions: Map<string, ((moduleId: string) => void)[]> = new Map();
export function registerPatch(patch: IdentifiedPatch) {
patches.push(patch);
moonlight.unpatched.add(patch);
}
···
const moduleCache: Record<string, string> = {};
const patched: Record<string, Array<string>> = {};
-
function patchModules(entry: WebpackJsonpEntry[1]) {
-
function patchModule(id: string, patchId: string, replaced: string) {
-
// Store what extensions patched what modules for easier debugging
-
patched[id] = patched[id] || [];
-
patched[id].push(patchId);
-
// Webpack module arguments are minified, so we replace them with consistent names
-
// We have to wrap it so things don't break, though
-
const patchedStr = patched[id].sort().join(", ");
-
const wrapped =
-
`(${replaced}).apply(this, arguments)\n` +
-
`// Patched by moonlight: ${patchedStr}\n` +
-
`//# sourceURL=Webpack-Module-${id}`;
-
try {
-
const func = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
-
entry[id] = func;
-
entry[id].__moonlight = true;
-
return true;
-
} catch (e) {
-
logger.warn("Error constructing function for patch", patchId, e);
-
patched[id].pop();
-
return false;
-
}
}
// Populate the module cache
for (const [id, func] of Object.entries(entry)) {
if (!Object.hasOwn(moduleCache, id) && func.__moonlight !== true) {
···
for (const [id, func] of Object.entries(entry)) {
if (func.__moonlight === true) continue;
-
let moduleString = moduleCache[id];
for (let i = 0; i < patches.length; i++) {
const patch = patches[i];
if (patch.prerequisite != null && !patch.prerequisite()) {
continue;
}
···
patch.find.lastIndex = 0;
}
-
// indexOf is faster than includes by 0.25% lmao
-
const match =
-
typeof patch.find === "string" ? moduleString.indexOf(patch.find) !== -1 : patch.find.test(moduleString);
// Global regexes apply to all modules
const shouldRemove = typeof patch.find === "string" ? true : !patch.find.global;
if (match) {
-
// We ensured all arrays get turned into normal PatchReplace objects on register
-
const replace = patch.replace as PatchReplace;
-
if (replace.type === undefined || replace.type === PatchReplaceType.Normal) {
-
// Add support for \i to match rspack's minified names
-
if (typeof replace.match !== "string") {
-
replace.match = new RegExp(replace.match.source.replace(/\\i/g, "[A-Za-z_$][\\w$]*"), replace.match.flags);
-
}
-
// tsc fails to detect the overloads for this, so I'll just do this
-
// Verbose, but it works
-
let replaced;
-
if (typeof replace.replacement === "string") {
-
replaced = moduleString.replace(replace.match, replace.replacement);
-
} else {
-
replaced = moduleString.replace(replace.match, replace.replacement);
-
}
-
if (replaced === moduleString) {
-
logger.warn("Patch replacement failed", id, patch);
-
continue;
-
}
-
if (patchModule(id, `${patch.ext}#${patch.id}`, replaced)) {
-
moduleString = replaced;
}
-
} else if (replace.type === PatchReplaceType.Module) {
-
// Directly replace the module with a new one
-
const newModule = replace.replacement(moduleString);
-
entry[id] = newModule;
-
entry[id].__moonlight = true;
-
moduleString = newModule.toString().replace(/\n/g, "") + `//# sourceURL=Webpack-Module-${id}`;
}
-
moonlight.unpatched.delete(patch);
-
-
if (shouldRemove) {
-
patches.splice(i--, 1);
}
}
}
-
moduleCache[id] = moduleString;
try {
const parsed = moonlight.lunast.parseScript(id, moduleString);
if (parsed != null) {
for (const [parsedId, parsedScript] of Object.entries(parsed)) {
-
if (patchModule(parsedId, "lunast", parsedScript)) {
moduleCache[parsedId] = parsedScript;
}
}
···
if (moonlightNode.config.patchAll === true) {
if ((typeof id !== "string" || !id.includes("_")) && !entry[id].__moonlight) {
-
const wrapped = `(${moduleCache[id]}).apply(this, arguments)\n` + `//# sourceURL=Webpack-Module-${id}`;
entry[id] = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
entry[id].__moonlight = true;
}
···
}
}
if (deps.size !== 0) {
-
wpModule.dependencies = Array.from(deps);
continue;
}
-
-
wpModule.dependencies = Array.from(deps);
}
}
···
if (wpModule.run) {
modules[id] = wpModule.run;
wpModule.run.__moonlight = true;
}
-
if (wpModule.entrypoint) entrypoints.push(id);
}
if (!webpackModules.size) break;
}
for (const [name, func] of Object.entries(moonlight.moonmap.getWebpackModules("window.moonlight.moonmap"))) {
injectedWpModules.push({ id: name, run: func });
modules[name] = func;
inject = true;
···
window.webpackChunkdiscord_app.push([
[--chunkId],
modules,
-
(require: typeof WebpackRequire) => entrypoints.map(require)
]);
}
}
···
const realPush = jsonp.push;
if (jsonp.push.__moonlight !== true) {
jsonp.push = (items) => {
-
moonlight.events.dispatchEvent(EventType.ChunkLoad, {
chunkId: items[0],
modules: items[1],
require: items[2]
···
set(modules: any) {
const { stack } = new Error();
if (stack!.includes("/assets/") && !Array.isArray(modules)) {
-
moonlight.events.dispatchEvent(EventType.ChunkLoad, {
modules: modules
});
patchModules(modules);
···
} from "@moonlight-mod/types";
import Logger from "./util/logger";
import calculateDependencies, { Dependency } from "./util/dependency";
+
import { WebEventType } from "@moonlight-mod/types/core/event";
+
import { processFind, processReplace, testFind } from "./util/patch";
const logger = new Logger("core/patch");
···
const moduleLoadSubscriptions: Map<string, ((moduleId: string) => void)[]> = new Map();
export function registerPatch(patch: IdentifiedPatch) {
+
patch.find = processFind(patch.find);
+
processReplace(patch.replace);
+
patches.push(patch);
moonlight.unpatched.add(patch);
}
···
const moduleCache: Record<string, string> = {};
const patched: Record<string, Array<string>> = {};
+
function createSourceURL(id: string) {
+
const remapped = Object.entries(moonlight.moonmap.modules).find((m) => m[1] === id)?.[0];
+
+
if (remapped) {
+
return `// Webpack Module: ${id}\n//# sourceURL=${remapped}`;
+
}
+
+
return `//# sourceURL=Webpack-Module/${id.slice(0, 3)}/${id}`;
+
}
+
+
function patchModule(id: string, patchId: string, replaced: string, entry: WebpackJsonpEntry[1]) {
+
// Store what extensions patched what modules for easier debugging
+
patched[id] = patched[id] ?? [];
+
patched[id].push(patchId);
+
// Webpack module arguments are minified, so we replace them with consistent names
+
// We have to wrap it so things don't break, though
+
const patchedStr = patched[id].sort().join(", ");
+
const wrapped =
+
`(${replaced}).apply(this, arguments)\n` + `// Patched by moonlight: ${patchedStr}\n` + createSourceURL(id);
+
try {
+
const func = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
+
entry[id] = func;
+
entry[id].__moonlight = true;
+
return true;
+
} catch (e) {
+
logger.warn("Error constructing function for patch", patchId, e);
+
patched[id].pop();
+
return false;
}
+
}
+
function patchModules(entry: WebpackJsonpEntry[1]) {
// Populate the module cache
for (const [id, func] of Object.entries(entry)) {
if (!Object.hasOwn(moduleCache, id) && func.__moonlight !== true) {
···
for (const [id, func] of Object.entries(entry)) {
if (func.__moonlight === true) continue;
+
+
// Clone the module string so finds don't get messed up by other extensions
+
const origModuleString = moduleCache[id];
+
let moduleString = origModuleString;
+
const patchedStr = [];
+
const mappedName = Object.entries(moonlight.moonmap.modules).find((m) => m[1] === id)?.[0];
+
let modified = false;
+
let swappedModule = false;
+
+
const exts = new Set<string>();
for (let i = 0; i < patches.length; i++) {
const patch = patches[i];
if (patch.prerequisite != null && !patch.prerequisite()) {
+
moonlight.unpatched.delete(patch);
continue;
}
···
patch.find.lastIndex = 0;
}
+
const match = testFind(origModuleString, patch.find) || patch.find === mappedName;
// Global regexes apply to all modules
const shouldRemove = typeof patch.find === "string" ? true : !patch.find.global;
+
let replaced = moduleString;
+
let hardFailed = false;
if (match) {
+
// We ensured normal PatchReplace objects get turned into arrays on register
+
const replaces = patch.replace as PatchReplace[];
+
let isPatched = true;
+
for (let i = 0; i < replaces.length; i++) {
+
const replace = replaces[i];
+
let patchId = `${patch.ext}#${patch.id}`;
+
if (replaces.length > 1) patchId += `#${i}`;
+
patchedStr.push(patchId);
+
if (replace.type === undefined || replace.type === PatchReplaceType.Normal) {
+
// tsc fails to detect the overloads for this, so I'll just do this
+
// Verbose, but it works
+
if (typeof replace.replacement === "string") {
+
replaced = replaced.replace(replace.match, replace.replacement);
+
} else {
+
replaced = replaced.replace(replace.match, replace.replacement);
+
}
+
if (replaced === moduleString) {
+
logger.warn("Patch replacement failed", id, patchId, patch);
+
isPatched = false;
+
if (patch.hardFail) {
+
hardFailed = true;
+
break;
+
} else {
+
continue;
+
}
+
}
+
} else if (replace.type === PatchReplaceType.Module) {
+
// Directly replace the module with a new one
+
const newModule = replace.replacement(replaced);
+
entry[id] = newModule;
+
entry[id].__moonlight = true;
+
replaced = newModule.toString().replace(/\n/g, "");
+
swappedModule = true;
}
}
+
if (!hardFailed) {
+
moduleString = replaced;
+
modified = true;
+
exts.add(patch.ext);
}
+
+
if (isPatched) moonlight.unpatched.delete(patch);
+
if (shouldRemove) patches.splice(i--, 1);
}
}
+
if (modified) {
+
let shouldCache = true;
+
if (!swappedModule) shouldCache = patchModule(id, patchedStr.join(", "), moduleString, entry);
+
if (shouldCache) moduleCache[id] = moduleString;
+
moonlight.patched.set(id, exts);
+
}
try {
const parsed = moonlight.lunast.parseScript(id, moduleString);
if (parsed != null) {
for (const [parsedId, parsedScript] of Object.entries(parsed)) {
+
if (patchModule(parsedId, "lunast", parsedScript, entry)) {
moduleCache[parsedId] = parsedScript;
}
}
···
if (moonlightNode.config.patchAll === true) {
if ((typeof id !== "string" || !id.includes("_")) && !entry[id].__moonlight) {
+
const wrapped = `(${moduleCache[id]}).apply(this, arguments)\n` + createSourceURL(id);
entry[id] = new Function("module", "exports", "require", wrapped) as WebpackModuleFunc;
entry[id].__moonlight = true;
}
···
}
}
+
wpModule.dependencies = Array.from(deps);
if (deps.size !== 0) {
continue;
}
}
}
···
if (wpModule.run) {
modules[id] = wpModule.run;
wpModule.run.__moonlight = true;
+
// @ts-expect-error hacks
+
wpModule.run.call = function (self, module, exports, require) {
+
try {
+
wpModule.run!.apply(self, [module, exports, require]);
+
} catch (err) {
+
logger.error(`Failed to run module "${id}":`, err);
+
}
+
};
+
if (wpModule.entrypoint) entrypoints.push(id);
}
}
if (!webpackModules.size) break;
}
for (const [name, func] of Object.entries(moonlight.moonmap.getWebpackModules("window.moonlight.moonmap"))) {
+
// @ts-expect-error probably should fix the type on this idk
+
func.__moonlight = true;
injectedWpModules.push({ id: name, run: func });
modules[name] = func;
inject = true;
···
window.webpackChunkdiscord_app.push([
[--chunkId],
modules,
+
(require: WebpackRequireType) =>
+
entrypoints.map((id) => {
+
try {
+
if (require.m[id] == null) {
+
logger.error(`Failing to load entrypoint module "${id}" because it's not found in Webpack.`);
+
} else {
+
require(id);
+
}
+
} catch (err) {
+
logger.error(`Failed to load entrypoint module "${id}":`, err);
+
}
+
})
]);
}
}
···
const realPush = jsonp.push;
if (jsonp.push.__moonlight !== true) {
jsonp.push = (items) => {
+
moonlight.events.dispatchEvent(WebEventType.ChunkLoad, {
chunkId: items[0],
modules: items[1],
require: items[2]
···
set(modules: any) {
const { stack } = new Error();
if (stack!.includes("/assets/") && !Array.isArray(modules)) {
+
moonlight.events.dispatchEvent(WebEventType.ChunkLoad, {
modules: modules
});
patchModules(modules);
+39
packages/core/src/util/config.ts
···
···
+
import type { Config, DetectedExtension, ExtensionManifest } from "@moonlight-mod/types";
+
+
export function getManifest(extensions: DetectedExtension[], ext: string) {
+
return extensions.find((x) => x.id === ext)?.manifest;
+
}
+
+
export function getConfig(ext: string, config: Config) {
+
const val = config.extensions[ext];
+
if (val == null || typeof val === "boolean") return undefined;
+
return val.config;
+
}
+
+
export function getConfigOption<T>(
+
ext: string,
+
key: string,
+
config: Config,
+
settings?: ExtensionManifest["settings"]
+
): T | undefined {
+
const defaultValue: T | undefined = structuredClone(settings?.[key]?.default);
+
const cfg = getConfig(ext, config);
+
if (cfg == null || typeof cfg === "boolean") return defaultValue;
+
return cfg?.[key] ?? defaultValue;
+
}
+
+
export function setConfigOption<T>(config: Config, ext: string, key: string, value: T) {
+
const oldConfig = config.extensions[ext];
+
const newConfig =
+
typeof oldConfig === "boolean"
+
? {
+
enabled: oldConfig,
+
config: { [key]: value }
+
}
+
: {
+
...oldConfig,
+
config: { ...(oldConfig?.config ?? {}), [key]: value }
+
};
+
+
config.extensions[ext] = newConfig;
+
}
+30
packages/core/src/util/patch.ts
···
···
+
import { PatchReplace, PatchReplaceType } from "@moonlight-mod/types";
+
+
type SingleFind = string | RegExp;
+
type Find = SingleFind | SingleFind[];
+
+
export function processFind<T extends Find>(find: T): T {
+
if (Array.isArray(find)) {
+
return find.map(processFind) as T;
+
} else if (find instanceof RegExp) {
+
// Add support for \i to match rspack's minified names
+
return new RegExp(find.source.replace(/\\i/g, "[A-Za-z_$][\\w$]*"), find.flags) as T;
+
} else {
+
return find;
+
}
+
}
+
+
export function processReplace(replace: PatchReplace | PatchReplace[]) {
+
if (Array.isArray(replace)) {
+
replace.forEach(processReplace);
+
} else {
+
if (replace.type === undefined || replace.type === PatchReplaceType.Normal) {
+
replace.match = processFind(replace.match);
+
}
+
}
+
}
+
+
export function testFind(src: string, find: SingleFind) {
+
// indexOf is faster than includes by 0.25% lmao
+
return typeof find === "string" ? src.indexOf(find) !== -1 : find.test(src);
+
}
+4 -1
packages/core/tsconfig.json
···
{
-
"extends": "../../tsconfig.json"
}
···
{
+
"extends": "../../tsconfig.json",
+
"compilerOptions": {
+
"lib": ["ESNext", "DOM"]
+
}
}
+9 -1
packages/core-extensions/package.json
···
{
"name": "@moonlight-mod/core-extensions",
"private": true,
"dependencies": {
"@moonlight-mod/core": "workspace:*",
"@moonlight-mod/types": "workspace:*",
-
"nanotar": "^0.1.1"
}
}
···
{
"name": "@moonlight-mod/core-extensions",
"private": true,
+
"engineStrict": true,
+
"engines": {
+
"node": ">=22",
+
"pnpm": ">=10",
+
"npm": "pnpm",
+
"yarn": "pnpm"
+
},
"dependencies": {
"@moonlight-mod/core": "workspace:*",
"@moonlight-mod/types": "workspace:*",
+
"microdiff": "catalog:prod",
+
"nanotar": "catalog:prod"
}
}
+1
packages/core-extensions/src/appPanels/manifest.json
···
{
"id": "appPanels",
"apiLevel": 2,
"meta": {
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "appPanels",
"apiLevel": 2,
"meta": {
+85
packages/core-extensions/src/commands/index.ts
···
···
+
import { Patch, ExtensionWebpackModule } from "@moonlight-mod/types";
+
import { APPLICATION_ID } from "@moonlight-mod/types/coreExtensions/commands";
+
+
export const patches: Patch[] = [
+
{
+
find: ".fI5MTU)", // COMMAND_SECTION_BUILT_IN_NAME
+
replace: [
+
// inject commands
+
{
+
match: /return (\i)\.filter/,
+
replacement: (orig, commands) =>
+
`return [...${commands},...require("commands_commands").default._getCommands()].filter`
+
},
+
+
// section
+
{
+
match: /(?<=\i={)(?=\[\i\.\i\.BUILT_IN]:{id:\i\.\i\.BUILT_IN,type:(\i.\i\.BUILT_IN))/,
+
replacement: (_, type) =>
+
`"${APPLICATION_ID}":{id:"${APPLICATION_ID}",type:${type},get name(){return "moonlight"}},`
+
}
+
]
+
},
+
+
// index our section
+
{
+
find: '"ApplicationCommandIndexStore"',
+
replace: {
+
match: /(?<=let \i=(\i)\((\i\.\i)\[\i\.\i\.BUILT_IN\],(\i),!0,!0,(\i)\);)null!=(\i)&&(\i)\.push\(\i\)/,
+
replacement: (_, createSection, sections, deny, props, section, commands) =>
+
`null!=${section}&&(${section}.data=${section}.data.filter(c=>c.applicationId=="-1"));
+
null!=${section}&&${commands}.push(${section});
+
const moonlightCommands=${createSection}(${sections}["${APPLICATION_ID}"],${deny},!0,!0,${props});
+
null!=moonlightCommands&&(moonlightCommands.data=moonlightCommands.data.filter(c=>c.applicationId=="${APPLICATION_ID}"));
+
null!=moonlightCommands&&${commands}.push(moonlightCommands)`
+
}
+
},
+
+
// grab legacy commands (needed for adding actions that act like sed/plus reacting)
+
{
+
find: "={tts:{action:",
+
replace: {
+
match: /Object\.setPrototypeOf\((\i),null\)/,
+
replacement: (_, legacyCommands) => `require("commands_commands")._getLegacyCommands(${legacyCommands})`
+
}
+
},
+
+
// add icon
+
{
+
find: ",hasSpaceTerminator:",
+
replace: {
+
match: /(\i)\.type===/,
+
replacement: (orig, section) => `${section}.id!=="${APPLICATION_ID}"&&${orig}`
+
}
+
},
+
{
+
find: ".icon,bot:null==",
+
replace: {
+
match: /(\.useMemo\(\(\)=>{(var \i;)?)((return |if\()(\i)\.type)/,
+
replacement: (_, before, beforeVar, after, afterIf, section) => `${before}
+
if (${section}.id==="${APPLICATION_ID}") return "https://moonlight-mod.github.io/favicon.png";
+
${after}`
+
}
+
},
+
// fix icon sizing because they expect built in to be 24 and others to be 32
+
{
+
find: ".builtInSeparator}):null]",
+
replace: {
+
match: /(\i)\.type===\i\.\i\.BUILT_IN/,
+
replacement: (orig, section) => `${section}.id!=="${APPLICATION_ID}"&&${orig}`
+
}
+
},
+
+
// tell it this app id is authorized
+
{
+
find: /let{customInstallUrl:\i,installParams:\i,integrationTypesConfig:\i}/,
+
replace: {
+
match: /\|\|(\i)===\i\.\i\.BUILT_IN/,
+
replacement: (orig, id) => `${orig}||${id}==="${APPLICATION_ID}"`
+
}
+
}
+
];
+
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
+
commands: {}
+
};
+11
packages/core-extensions/src/commands/manifest.json
···
···
+
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
+
"id": "commands",
+
"apiLevel": 2,
+
"meta": {
+
"name": "Commands",
+
"tagline": "A library to add commands",
+
"authors": ["Cynosphere", "NotNite"],
+
"tags": ["library"]
+
}
+
}
+71
packages/core-extensions/src/commands/webpackModules/commands.ts
···
···
+
import {
+
APPLICATION_ID,
+
Commands,
+
LegacyCommand,
+
RegisteredCommand
+
} from "@moonlight-mod/types/coreExtensions/commands";
+
+
type LegacyCommands = Record<string, LegacyCommand>;
+
let legacyCommands: LegacyCommands | undefined;
+
let queuedLegacyCommands: Record<string, LegacyCommand> | null = {};
+
+
const registeredCommands: RegisteredCommand[] = [];
+
+
export function _getLegacyCommands(commands: LegacyCommands) {
+
legacyCommands = commands;
+
if (queuedLegacyCommands != null) {
+
for (const [key, value] of Object.entries(queuedLegacyCommands)) {
+
legacyCommands[key] = value;
+
}
+
queuedLegacyCommands = null;
+
}
+
}
+
+
export const commands: Commands = {
+
registerCommand(command) {
+
const registered: RegisteredCommand = {
+
...command,
+
untranslatedName: command.id,
+
displayName: command.id,
+
applicationId: APPLICATION_ID,
+
untranslatedDescription: command.description,
+
displayDescription: command.description,
+
options: command.options?.map((o) => ({
+
...o,
+
displayName: o.name,
+
displayDescription: o.description
+
}))
+
};
+
registeredCommands.push(registered);
+
},
+
+
registerLegacyCommand(id, command) {
+
if (command.match) {
+
if (command.match instanceof RegExp) {
+
command.match = this.anyScopeRegex(command.match);
+
} else if (command.match.regex && typeof command.match !== "function") {
+
command.match = this.anyScopeRegex(command.match.regex);
+
}
+
}
+
+
if (!legacyCommands) {
+
queuedLegacyCommands![id] = command;
+
} else {
+
legacyCommands[id] = command;
+
}
+
},
+
+
anyScopeRegex(regex) {
+
const out = function (str: string) {
+
return regex.exec(str);
+
};
+
out.regex = regex;
+
return out;
+
},
+
+
_getCommands() {
+
return [...registeredCommands];
+
}
+
};
+
+
export default commands;
+7 -5
packages/core-extensions/src/common/index.ts
···
export const webpackModules: ExtensionWebExports["webpackModules"] = {
stores: {
-
dependencies: [
-
{
-
id: "discord/packages/flux"
-
}
-
]
}
};
···
export const webpackModules: ExtensionWebExports["webpackModules"] = {
stores: {
+
dependencies: [{ id: "discord/packages/flux" }]
+
},
+
ErrorBoundary: {
+
dependencies: [{ id: "react" }]
+
},
+
icons: {
+
dependencies: [{ id: "react" }, { id: "discord/components/common/index" }]
}
};
+2 -1
packages/core-extensions/src/common/manifest.json
···
{
"id": "common",
"apiLevel": 2,
"meta": {
"name": "Common",
-
"tagline": "A *lot* of common clientmodding utilities from the Discord client",
"authors": ["Cynosphere", "NotNite"],
"tags": ["library"]
},
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "common",
"apiLevel": 2,
"meta": {
"name": "Common",
+
"tagline": "Common client modding utilities for the Discord client",
"authors": ["Cynosphere", "NotNite"],
"tags": ["library"]
},
+27
packages/core-extensions/src/common/style.css
···
···
+
.moonlight-error-boundary {
+
margin: 0 0 15px;
+
padding: 10px;
+
border-radius: 5px;
+
font-size: 1rem;
+
font-weight: 300;
+
line-height: 22px;
+
color: var(--text-normal, white);
+
background: hsl(var(--red-400-hsl) / 0.1);
+
border: 2px solid hsl(var(--red-400-hsl) / 0.5);
+
+
.theme-light & {
+
color: var(--text-normal, black) !important;
+
}
+
+
& > h3 {
+
margin-bottom: 0.25rem;
+
}
+
+
& > .hljs {
+
background: var(--background-secondary);
+
border: 1px solid var(--background-tertiary);
+
white-space: pre-wrap;
+
font-family: var(--font-code);
+
user-select: text;
+
}
+
}
+47
packages/core-extensions/src/common/webpackModules/ErrorBoundary.tsx
···
···
+
import React from "@moonlight-mod/wp/react";
+
import { ErrorBoundaryProps, ErrorBoundaryState } from "@moonlight-mod/types/coreExtensions/common";
+
+
const logger = moonlight.getLogger("ErrorBoundary");
+
+
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
+
constructor(props: ErrorBoundaryProps) {
+
super(props);
+
this.state = {
+
errored: false,
+
error: undefined,
+
componentStack: undefined
+
};
+
}
+
+
static getDerivedStateFromError(error: Error) {
+
return {
+
errored: true,
+
error
+
};
+
}
+
+
componentDidCatch(error: Error, { componentStack }: { componentStack: string }) {
+
logger.error(`${error}\n\nComponent stack:\n${componentStack}`);
+
this.setState({ error, componentStack });
+
}
+
+
render() {
+
const { noop, fallback: FallbackComponent, children, message } = this.props;
+
const { errored, error, componentStack } = this.state;
+
+
if (FallbackComponent) return <FallbackComponent children={children} {...this.state} />;
+
+
if (errored) {
+
return noop ? null : (
+
<div className={`moonlight-error-boundary`}>
+
<h3>{message ?? "An error occurred rendering this component:"}</h3>
+
<code className="hljs">{`${error}\n\nComponent stack:\n${componentStack}`}</code>
+
</div>
+
);
+
}
+
+
return children;
+
}
+
}
+
+
export default ErrorBoundary;
+31
packages/core-extensions/src/common/webpackModules/icons.ts
···
···
+
import { Icons, IconSize } from "@moonlight-mod/types/coreExtensions/common";
+
import { tokens } from "@moonlight-mod/wp/discord/components/common/index";
+
+
// This is defined in a Webpack module but we copy it here to be less breakage-prone
+
const sizes: Partial<Record<IconSize, number>> = {
+
xxs: 12,
+
xs: 16,
+
sm: 18,
+
md: 24,
+
lg: 32,
+
refresh_sm: 20
+
};
+
+
export const icons: Icons = {
+
parseProps(props) {
+
// NOTE: var() fallback is non-standard behavior, just for safety reasons
+
const color = props?.color ?? tokens?.colors?.["INTERACTIVE_NORMAL"] ?? "var(--interactive-normal)";
+
+
const size = sizes[props?.size ?? "md"];
+
+
return {
+
// note: this default size is also non-standard behavior, just for safety
+
width: size ?? props?.width ?? sizes.md!,
+
height: size ?? props?.width ?? sizes.md!,
+
+
fill: typeof color === "string" ? color : color.css,
+
className: props?.colorClass ?? ""
+
};
+
}
+
};
+
export default icons;
+83
packages/core-extensions/src/componentEditor/index.ts
···
···
+
import { ExtensionWebpackModule, Patch } from "@moonlight-mod/types";
+
+
export const patches: Patch[] = [
+
// dm list
+
{
+
find: ".interactiveSystemDM]:",
+
replace: [
+
{
+
match: /decorators:(\i\.isSystemDM\(\)\?\(0,\i\.jsx\)\(.+?verified:!0}\):null)/,
+
replacement: (_, decorators) =>
+
`decorators:require("componentEditor_dmList").default._patchDecorators([${decorators}],arguments[0])`
+
},
+
{
+
match: /(?<=selected:\i,)children:\[/,
+
replacement: 'children:require("componentEditor_dmList").default._patchItems(['
+
},
+
{
+
match: /(?<=(onMouseDown|nameplate):\i}\))]/,
+
replacement: "],arguments[0])"
+
}
+
],
+
hardFail: true
+
},
+
+
// member list
+
{
+
find: ".lostPermission",
+
replace: [
+
{
+
match: /(?<=\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[\i\(\),.+?\i\(\)])/,
+
replacement: (_, decorators) =>
+
`children:require("componentEditor_memberList").default._patchDecorators(${decorators},arguments[0])`
+
},
+
{
+
match: /name:null==\i\?\(0,\i\.jsx\)\("span"/,
+
replacement: (orig: string) =>
+
`children:require("componentEditor_memberList").default._patchItems([],arguments[0]),${orig}`
+
}
+
]
+
},
+
+
// messages
+
{
+
find: '},"new-member")),',
+
replace: [
+
{
+
match: /(?<=\.BADGES](=|:))(\i)(;|})/,
+
replacement: (_, leading, badges, trailing) =>
+
`require("componentEditor_messages").default._patchUsernameBadges(${badges},arguments[0])${trailing}`
+
},
+
{
+
match: /(?<=className:\i,)badges:(\i)/,
+
replacement: (_, badges) =>
+
`badges:require("componentEditor_messages").default._patchBadges(${badges},arguments[0])`
+
},
+
{
+
match: /(?<=username:\(0,\i\.jsxs\)\(\i\.Fragment,{)children:(\[.+?])}\),usernameSpanId:/,
+
replacement: (_, elements) =>
+
`children:require("componentEditor_messages").default._patchUsername(${elements},arguments[0])}),usernameSpanId:`
+
}
+
]
+
},
+
{
+
find: '.provider&&"Discord"===',
+
replace: {
+
match: /(?<=\.container\),)children:(\[.+?this\.renderSuppressConfirmModal\(\),.+?\])}\)/,
+
replacement: (_, elements) =>
+
`children:require("componentEditor_messages").default._patchAccessories(${elements},this.props)})`
+
}
+
}
+
];
+
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
+
dmList: {
+
dependencies: [{ id: "react" }]
+
},
+
memberList: {
+
dependencies: [{ id: "react" }]
+
},
+
messages: {
+
dependencies: [{ id: "react" }]
+
}
+
};
+11
packages/core-extensions/src/componentEditor/manifest.json
···
···
+
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
+
"id": "componentEditor",
+
"apiLevel": 2,
+
"meta": {
+
"name": "Component Editor",
+
"tagline": "A library to add to commonly patched components",
+
"authors": ["Cynosphere"],
+
"tags": ["library"]
+
}
+
}
+61
packages/core-extensions/src/componentEditor/webpackModules/dmList.tsx
···
···
+
import {
+
DMList,
+
DMListItem,
+
DMListDecorator,
+
DMListAnchorIndicies,
+
DMListDecoratorAnchorIndicies
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
+
import React from "@moonlight-mod/wp/react";
+
+
const items: Record<string, DMListItem> = {};
+
const decorators: Record<string, DMListDecorator> = {};
+
+
function addEntries(
+
elements: React.ReactNode[],
+
entries: Record<string, DMListItem | DMListDecorator>,
+
indicies: Partial<Record<keyof typeof DMListAnchorIndicies | keyof typeof DMListDecoratorAnchorIndicies, number>>,
+
props: any
+
) {
+
const originalElements = [...elements];
+
for (const [id, entry] of Object.entries(entries)) {
+
const component = <entry.component {...props} key={id} />;
+
+
if (entry.anchor === undefined) {
+
if (entry.before) {
+
elements.splice(0, 0, component);
+
} else {
+
elements.push(component);
+
}
+
} else {
+
const index = elements.indexOf(originalElements[indicies[entry.anchor]!]);
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
+
}
+
}
+
}
+
+
export const dmList: DMList = {
+
addItem(id, component, anchor, before = false) {
+
items[id] = {
+
component,
+
anchor,
+
before
+
};
+
},
+
addDecorator(id, component, anchor, before = false) {
+
decorators[id] = {
+
component,
+
anchor,
+
before
+
};
+
},
+
_patchItems(elements, props) {
+
addEntries(elements, items, DMListAnchorIndicies, props);
+
return elements;
+
},
+
_patchDecorators(elements, props) {
+
addEntries(elements, decorators, DMListDecoratorAnchorIndicies, props);
+
return elements;
+
}
+
};
+
+
export default dmList;
+50
packages/core-extensions/src/componentEditor/webpackModules/memberList.tsx
···
···
+
import {
+
MemberList,
+
MemberListDecorator,
+
MemberListDecoratorAnchorIndicies
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
+
import React from "@moonlight-mod/wp/react";
+
+
const items: Record<string, React.FC<any>> = {};
+
const decorators: Record<string, MemberListDecorator> = {};
+
+
export const memberList: MemberList = {
+
addItem(id, component) {
+
items[id] = component;
+
},
+
addDecorator(id, component, anchor, before = false) {
+
decorators[id] = {
+
component,
+
anchor,
+
before
+
};
+
},
+
_patchItems(elements, props) {
+
for (const [id, Component] of Object.entries(items)) {
+
elements.push(<Component {...props} key={id} />);
+
}
+
+
return elements;
+
},
+
_patchDecorators(elements, props) {
+
const originalElements = [...elements];
+
for (const [id, entry] of Object.entries(decorators)) {
+
const component = <entry.component {...props} key={id} />;
+
+
if (entry.anchor === undefined) {
+
if (entry.before) {
+
elements.splice(0, 0, component);
+
} else {
+
elements.push(component);
+
}
+
} else {
+
const index = elements.indexOf(originalElements[MemberListDecoratorAnchorIndicies[entry.anchor]!]);
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
+
}
+
}
+
+
return elements;
+
}
+
};
+
+
export default memberList;
+97
packages/core-extensions/src/componentEditor/webpackModules/messages.tsx
···
···
+
import {
+
MessageBadge,
+
MessageBadgeIndicies,
+
Messages,
+
MessageUsername,
+
MessageUsernameBadge,
+
MessageUsernameBadgeIndicies,
+
MessageUsernameIndicies
+
} from "@moonlight-mod/types/coreExtensions/componentEditor";
+
import React from "@moonlight-mod/wp/react";
+
+
const username: Record<string, MessageUsername> = {};
+
const usernameBadges: Record<string, MessageUsernameBadge> = {};
+
const badges: Record<string, MessageBadge> = {};
+
const accessories: Record<string, React.FC<any>> = {};
+
+
function addEntries(
+
elements: React.ReactNode[],
+
entries: Record<string, MessageUsername | MessageUsernameBadge | MessageBadge>,
+
indicies: Partial<
+
Record<
+
| keyof typeof MessageUsernameIndicies
+
| keyof typeof MessageUsernameBadgeIndicies
+
| keyof typeof MessageBadgeIndicies,
+
number
+
>
+
>,
+
props: any
+
) {
+
const originalElements = [...elements];
+
for (const [id, entry] of Object.entries(entries)) {
+
const component = <entry.component {...props} key={id} />;
+
+
if (entry.anchor === undefined) {
+
if (entry.before) {
+
elements.splice(0, 0, component);
+
} else {
+
elements.push(component);
+
}
+
} else {
+
const index = elements.indexOf(originalElements[indicies[entry.anchor]!]);
+
elements.splice(index! + (entry.before ? 0 : 1), 0, component);
+
}
+
}
+
}
+
+
function addComponents(elements: React.ReactNode[], components: Record<string, React.FC<any>>, props: any) {
+
for (const [id, Component] of Object.entries(components)) {
+
const component = <Component {...props} key={id} />;
+
elements.push(component);
+
}
+
}
+
+
export const messages: Messages = {
+
addToUsername(id, component, anchor, before = false) {
+
username[id] = {
+
component,
+
anchor,
+
before
+
};
+
},
+
addUsernameBadge(id, component, anchor, before = false) {
+
usernameBadges[id] = {
+
component,
+
anchor,
+
before
+
};
+
},
+
addBadge(id, component, anchor, before = false) {
+
badges[id] = {
+
component,
+
anchor,
+
before
+
};
+
},
+
addAccessory(id, component) {
+
accessories[id] = component;
+
},
+
_patchUsername(elements, props) {
+
addEntries(elements, username, MessageUsernameIndicies, props);
+
return elements;
+
},
+
_patchUsernameBadges(elements, props) {
+
addEntries(elements, usernameBadges, MessageUsernameBadgeIndicies, props);
+
return elements;
+
},
+
_patchBadges(elements, props) {
+
addEntries(elements, badges, MessageBadgeIndicies, props);
+
return elements;
+
},
+
_patchAccessories(elements, props) {
+
addComponents(elements, accessories, props);
+
return elements;
+
}
+
};
+
+
export default messages;
+1
packages/core-extensions/src/contextMenu/manifest.json
···
{
"id": "contextMenu",
"apiLevel": 2,
"meta": {
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "contextMenu",
"apiLevel": 2,
"meta": {
+17 -6
packages/core-extensions/src/contextMenu/webpackModules/contextMenu.ts
···
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import parser from "@moonlight-mod/wp/contextMenu_evilMenu";
type Patch = {
navId: string;
-
item: (props: any) => MenuElement | MenuElement[];
-
anchorId: string;
before: boolean;
};
-
function addItem<T>(navId: string, item: (props: T) => MenuElement | MenuElement[], anchorId: string, before = false) {
-
patches.push({ navId, item, anchorId, before });
}
const patches: Patch[] = [];
···
if (!matches.length) return items;
for (const patch of matches) {
-
const idx = items.findIndex((i) => i.key === patch.anchorId);
if (idx === -1) continue;
-
items.splice(idx + 1 - +patch.before, 0, ...parser(patch.item(menuProps)));
}
return items;
···
};
// Unmangle Menu elements
const code =
spacepack.require.m[
spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id
···
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import parser from "@moonlight-mod/wp/contextMenu_evilMenu";
+
// NOTE: We originally had item as a function that returned this, but it didn't
+
// quite know how to work out the type and thought it was a JSX element (it
+
// *technically* was). This has less type safety, but a @ts-expect-error has
+
// zero, so it's better than nothing.
+
type ReturnType = MenuElement | MenuElement[];
+
type Patch = {
navId: string;
+
item: React.FC<any>;
+
anchor: string | RegExp;
before: boolean;
};
+
function addItem<T = any>(navId: string, item: React.FC<T>, anchor: string | RegExp, before = false) {
+
if (anchor instanceof RegExp && anchor.flags.includes("g"))
+
throw new Error("anchor regular expression should not be global");
+
patches.push({ navId, item, anchor, before });
}
const patches: Patch[] = [];
···
if (!matches.length) return items;
for (const patch of matches) {
+
const idx = items.findIndex((i) =>
+
typeof patch.anchor === "string" ? i.key === patch.anchor : patch.anchor.test(i.key!)
+
);
if (idx === -1) continue;
+
items.splice(idx + 1 - +patch.before, 0, ...parser(patch.item(menuProps) as ReturnType));
}
return items;
···
};
// Unmangle Menu elements
+
// spacepack.require.m[moonlight.moonmap.modules["discord/modules/menus/web/Menu"]].toString();
const code =
spacepack.require.m[
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
···
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
let code =
spacepack.require.m[
spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id
···
const parserSym = code.match(/(?<=_patchMenu\(.,).+?(?=\()/)![0];
-
code = code.replace(/(?<=function\(\){return ).(?=})/, parserSym);
const mod = new Function("module", "exports", "require", `(${code}).apply(this, arguments)`);
const exp: any = {};
···
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
+
// spacepack.require.m[moonlight.moonmap.modules["discord/modules/menus/web/Menu"]].toString();
let code =
spacepack.require.m[
spacepack.findByCode("Menu API only allows Items and groups of Items as children.")[0].id
···
const parserSym = code.match(/(?<=_patchMenu\(.,).+?(?=\()/)![0];
+
code = code.replace(/{(.):\(\)=>./, (orig, e) => `{${e}:()=>${parserSym}`);
const mod = new Function("module", "exports", "require", `(${code}).apply(this, arguments)`);
const exp: any = {};
+1
packages/core-extensions/src/devToolsExtensions/manifest.json
···
},
"settings": {
"paths": {
"displayName": "Extension Paths",
"type": "list"
}
···
},
"settings": {
"paths": {
+
"advice": "restart",
"displayName": "Extension Paths",
"type": "list"
}
+2 -2
packages/core-extensions/src/disableSentry/index.ts
···
find: "profiledRootComponent:",
replace: {
type: PatchReplaceType.Normal,
-
match: /(?<=\.Z=){.+?}}/,
-
replacement: 'require("disableSentry_stub").proxy()'
}
},
{
···
find: "profiledRootComponent:",
replace: {
type: PatchReplaceType.Normal,
+
match: /Z:\(\)=>\i/,
+
replacement: 'Z:()=>require("disableSentry_stub").proxy()'
}
},
{
+5 -1
packages/core-extensions/src/disableSentry/manifest.json
···
{
"id": "disableSentry",
"apiLevel": 2,
"meta": {
···
"https://*.sentry.io/*",
"https://*.discord.com/error-reporting-proxy/*",
"https://discord.com/assets/sentry.*.js",
-
"https://*.discord.com/assets/sentry.*.js"
]
}
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "disableSentry",
"apiLevel": 2,
"meta": {
···
"https://*.sentry.io/*",
"https://*.discord.com/error-reporting-proxy/*",
"https://discord.com/assets/sentry.*.js",
+
"https://*.discord.com/assets/sentry.*.js",
+
"https://*.discordapp.com/error-reporting-proxy/*",
+
"https://discordapp.com/assets/sentry.*.js",
+
"https://*.discordapp.com/assets/sentry.*.js"
]
}
+13 -15
packages/core-extensions/src/disableSentry/node.ts
···
const logger = moonlightNode.getLogger("disableSentry");
-
if (!ipcRenderer.sendSync(constants.ipcGetIsMoonlightDesktop)) {
-
const preloadPath = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
-
try {
-
const sentryPath = require.resolve(resolve(preloadPath, "..", "node_modules", "@sentry", "electron"));
-
require.cache[sentryPath] = new Module(sentryPath, require.cache[require.resolve(preloadPath)]);
-
require.cache[sentryPath]!.exports = {
-
init: () => {},
-
setTag: () => {},
-
setUser: () => {},
-
captureMessage: () => {}
-
};
-
logger.debug("Stubbed Sentry node side!");
-
} catch (err) {
-
logger.error("Failed to stub Sentry:", err);
-
}
}
···
const logger = moonlightNode.getLogger("disableSentry");
+
const preloadPath = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
+
try {
+
const sentryPath = require.resolve(resolve(preloadPath, "..", "node_modules", "@sentry", "electron"));
+
require.cache[sentryPath] = new Module(sentryPath, require.cache[require.resolve(preloadPath)]);
+
require.cache[sentryPath]!.exports = {
+
init: () => {},
+
setTag: () => {},
+
setUser: () => {},
+
captureMessage: () => {}
+
};
+
logger.debug("Stubbed Sentry node side!");
+
} catch (err) {
+
logger.error("Failed to stub Sentry:", err);
}
+19 -5
packages/core-extensions/src/experiments/index.ts
···
{
find: '"scientist:triggered"', // Scientist? Triggered.
replace: {
-
match: /(?<=personal_connection_id\|\|)!1/,
-
replacement: "!0"
}
},
// Enable staff help menu
-
// FIXME: either make this actually work live (needs a state hook) or just
-
// wait for #122
{
find: ".HEADER_BAR)",
replace: {
···
`&&(((moonlight.getConfigOption("experiments","devtools")??false)?true:${isStaff})?(0,`
}
},
// Enable further staff-locked options
-
// FIXME: #122, this doesn't work live
{
find: "shouldShowLurkerModeUpsellPopout:",
replace: {
···
{
find: '"scientist:triggered"', // Scientist? Triggered.
replace: {
+
match: ".personal_connection_id",
+
replacement: ".personal_connection_id || true"
}
},
// Enable staff help menu
{
find: ".HEADER_BAR)",
replace: {
···
`&&(((moonlight.getConfigOption("experiments","devtools")??false)?true:${isStaff})?(0,`
}
},
+
// staff help menu - visual refresh
+
{
+
find: '("AppTitleBar")',
+
replace: {
+
match: /{hasBugReporterAccess:(\i)}=\i\.\i\.useExperiment\({location:"HeaderBar"},{autoTrackExposure:!1}\);/,
+
replacement: (orig, isStaff) =>
+
`${orig}if(moonlight.getConfigOption("experiments","devtools")??false)${isStaff}=true;`
+
}
+
},
+
{
+
find: 'navId:"staff-help-popout",',
+
replace: {
+
match: /isDiscordDeveloper:(\i)}\),/,
+
replacement: (_, isStaff) =>
+
`isDiscordDeveloper:(moonlight.getConfigOption("experiments","devtools")??false)||${isStaff}}),`
+
}
+
},
// Enable further staff-locked options
{
find: "shouldShowLurkerModeUpsellPopout:",
replace: {
+3
packages/core-extensions/src/experiments/manifest.json
···
{
"id": "experiments",
"apiLevel": 2,
"meta": {
···
},
"settings": {
"devtools": {
"displayName": "Enable staff help menu (DevTools)",
"type": "boolean",
"default": false
},
"staffSettings": {
"displayName": "Allow access to other staff settings elsewhere",
"type": "boolean",
"default": false
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "experiments",
"apiLevel": 2,
"meta": {
···
},
"settings": {
"devtools": {
+
"advice": "reload",
"displayName": "Enable staff help menu (DevTools)",
"type": "boolean",
"default": false
},
"staffSettings": {
+
"advice": "reload",
"displayName": "Allow access to other staff settings elsewhere",
"type": "boolean",
"default": false
+1
packages/core-extensions/src/markdown/manifest.json
···
{
"id": "markdown",
"apiLevel": 2,
"meta": {
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "markdown",
"apiLevel": 2,
"meta": {
+108
packages/core-extensions/src/moonbase/host.ts
···
···
+
import * as electron from "electron";
+
import * as fs from "node:fs/promises";
+
import * as path from "node:path";
+
import getNatives from "./native";
+
import { MoonlightBranch } from "@moonlight-mod/types";
+
+
const natives = getNatives();
+
+
const confirm = (action: string) =>
+
electron.dialog
+
.showMessageBox({
+
title: "Are you sure?",
+
message: `Are you sure? This will ${action} and restart Discord.`,
+
type: "warning",
+
buttons: ["OK", "Cancel"]
+
})
+
.then((r) => r.response === 0);
+
+
async function updateAndRestart() {
+
if (!(await confirm("update moonlight"))) return;
+
const newVersion = await natives.checkForMoonlightUpdate();
+
+
if (newVersion === null) {
+
electron.dialog.showMessageBox({ message: "You are already on the latest version of moonlight." });
+
return;
+
}
+
+
try {
+
await natives.updateMoonlight();
+
await electron.dialog.showMessageBox({ message: "Update successful, restarting Discord." });
+
electron.app.relaunch();
+
electron.app.exit(0);
+
} catch {
+
await electron.dialog.showMessageBox({
+
message: "Failed to update moonlight. Please use the installer instead.",
+
type: "error"
+
});
+
}
+
}
+
+
async function resetConfig() {
+
if (!(await confirm("reset your configuration"))) return;
+
+
const config = await moonlightHost.getConfigPath();
+
const dir = path.dirname(config);
+
const branch = path.basename(config, ".json");
+
await fs.rename(config, path.join(dir, `${branch}-backup-${Math.floor(Date.now() / 1000)}.json`));
+
+
await electron.dialog.showMessageBox({ message: "Configuration reset, restarting Discord." });
+
electron.app.relaunch();
+
electron.app.exit(0);
+
}
+
+
async function changeBranch(branch: MoonlightBranch) {
+
if (moonlightHost.branch === branch) return;
+
if (!(await confirm("switch branches"))) return;
+
try {
+
await natives.updateMoonlight(branch);
+
await electron.dialog.showMessageBox({ message: "Branch switch successful, restarting Discord." });
+
electron.app.relaunch();
+
electron.app.exit(0);
+
} catch (e) {
+
await electron.dialog.showMessageBox({ message: "Failed to switch branches:\n" + e, type: "error" });
+
}
+
}
+
+
function showAbout() {
+
electron.dialog.showMessageBox({
+
title: "About moonlight",
+
message: `moonlight ${moonlightHost.branch} ${moonlightHost.version}`
+
});
+
}
+
+
electron.app.whenReady().then(() => {
+
const original = electron.Menu.buildFromTemplate;
+
electron.Menu.buildFromTemplate = function (entries) {
+
const i = entries.findIndex((e) => e.label === "Check for Updates...");
+
if (i === -1) return original.call(this, entries);
+
+
if (!entries.find((e) => e.label === "moonlight")) {
+
const options: Electron.MenuItemConstructorOptions[] = [
+
{ label: "Update and restart", click: updateAndRestart },
+
{ label: "Reset config", click: resetConfig }
+
];
+
+
if (moonlightHost.branch !== MoonlightBranch.DEV) {
+
options.push({
+
label: "Switch branch",
+
submenu: [MoonlightBranch.STABLE, MoonlightBranch.NIGHTLY].map((branch) => ({
+
label: branch,
+
type: "radio",
+
checked: moonlightHost.branch === branch,
+
click: () => changeBranch(branch)
+
}))
+
});
+
}
+
+
options.push({ label: "About", click: showAbout });
+
+
entries.splice(i + 1, 0, {
+
label: "moonlight",
+
submenu: options
+
});
+
}
+
+
return original.call(this, entries);
+
};
+
});
+18 -6
packages/core-extensions/src/moonbase/index.tsx
···
{
// CvQlAA mapped to ERRORS_ACTION_TO_TAKE
// FIXME: Better patch find?
-
match: /,(\(0,(.)\.jsx\))\("p",{children:.\.intl\.string\(.\..\.CvQlAA\)}\)/,
replacement: (_, createElement, ReactJSX) =>
`,${createElement}(require("moonbase_crashScreen")?.UpdateText??${ReactJSX}.Fragment,{state:this.state,setState:this.setState.bind(this)})`
},
// wrap actions field to display error details
{
-
match: /(?<=return(\(0,(.)\.jsx\))\(.+?,)action:(.),className:/,
replacement: (_, createElement, ReactJSX, action) =>
`action:require("moonbase_crashScreen")?.wrapAction?${createElement}(require("moonbase_crashScreen").wrapAction,{action:${action},state:this.state}):${action},className:`
},
···
// add update button
// +hivLS -> ERRORS_RELOAD
{
-
match: /(?<=\["\+hivLS"\]\)}\),(\(0,(.)\.jsx\))\(.,{}\))/,
replacement: (_, createElement, ReactJSX) =>
`,${createElement}(require("moonbase_crashScreen")?.UpdateButton??${ReactJSX}.Fragment,{state:this.state,setState:this.setState.bind(this)})`
}
···
{ id: "react" },
{ id: "discord/components/common/index" },
{ ext: "moonbase", id: "stores" },
-
{ id: "discord/modules/guild_settings/IntegrationCard.css" },
"Masks.PANEL_BUTTON",
'"Missing channel in Channel.openChannelContextMenu"',
".forumOrHome]:"
]
},
settings: {
dependencies: [
{ ext: "spacepack", id: "spacepack" },
{ ext: "settings", id: "settings" },
{ id: "react" },
-
{ ext: "moonbase", id: "ui" }
],
entrypoint: true
},
···
dependencies: [
{ id: "react" },
{ ext: "moonbase", id: "stores" },
{ ext: "notices", id: "notices" },
{
ext: "spacepack",
id: "spacepack"
-
}
],
entrypoint: true
},
···
{
// CvQlAA mapped to ERRORS_ACTION_TO_TAKE
// FIXME: Better patch find?
+
match: /,(\(0,(\i)\.jsx\))\("p",{children:\i\.\i\.string\(\i\.\i\.CvQlAA\)}\)/,
replacement: (_, createElement, ReactJSX) =>
`,${createElement}(require("moonbase_crashScreen")?.UpdateText??${ReactJSX}.Fragment,{state:this.state,setState:this.setState.bind(this)})`
},
// wrap actions field to display error details
{
+
match: /(?<=return(\(0,(\i)\.jsx\))\(.+?,)action:(\i),className:/,
replacement: (_, createElement, ReactJSX, action) =>
`action:require("moonbase_crashScreen")?.wrapAction?${createElement}(require("moonbase_crashScreen").wrapAction,{action:${action},state:this.state}):${action},className:`
},
···
// add update button
// +hivLS -> ERRORS_RELOAD
{
+
match: /(?<=\["\+hivLS"\]\)}\),(\(0,(\i)\.jsx\))\(\i,{}\))/,
replacement: (_, createElement, ReactJSX) =>
`,${createElement}(require("moonbase_crashScreen")?.UpdateButton??${ReactJSX}.Fragment,{state:this.state,setState:this.setState.bind(this)})`
}
···
{ id: "react" },
{ id: "discord/components/common/index" },
{ ext: "moonbase", id: "stores" },
+
{ ext: "moonbase", id: "ThemeDarkIcon" },
+
{ id: "discord/modules/guild_settings/web/AppCard.css" },
+
{ ext: "contextMenu", id: "contextMenu" },
+
{ id: "discord/modules/modals/Modals" },
"Masks.PANEL_BUTTON",
'"Missing channel in Channel.openChannelContextMenu"',
".forumOrHome]:"
]
},
+
ThemeDarkIcon: {
+
dependencies: [{ ext: "common", id: "icons" }, { id: "react" }]
+
},
+
settings: {
dependencies: [
{ ext: "spacepack", id: "spacepack" },
{ ext: "settings", id: "settings" },
{ id: "react" },
+
{ ext: "moonbase", id: "ui" },
+
{ ext: "contextMenu", id: "contextMenu" },
+
':"USER_SETTINGS_MODAL_SET_SECTION"'
],
entrypoint: true
},
···
dependencies: [
{ id: "react" },
{ ext: "moonbase", id: "stores" },
+
{ ext: "moonbase", id: "ThemeDarkIcon" },
{ ext: "notices", id: "notices" },
{
ext: "spacepack",
id: "spacepack"
+
},
+
{ id: "discord/Constants" },
+
{ id: "discord/components/common/index" }
],
entrypoint: true
},
+18 -5
packages/core-extensions/src/moonbase/manifest.json
···
{
"id": "moonbase",
"apiLevel": 2,
"meta": {
···
"tagline": "The official settings UI for moonlight",
"authors": ["Cynosphere", "NotNite", "redstonekasi"]
},
-
"dependencies": ["spacepack", "settings", "common", "notices"],
"settings": {
"sections": {
"displayName": "Split into sections",
"description": "Show the Moonbase tabs as separate sections",
-
"type": "boolean"
},
"saveFilter": {
"displayName": "Persist filter",
"description": "Save extension filter in config",
-
"type": "boolean"
},
"updateChecking": {
"displayName": "Automatic update checking",
"description": "Checks for updates to moonlight",
"type": "boolean",
-
"default": "true"
},
"updateBanner": {
"displayName": "Show update banner",
"description": "Shows a banner for moonlight and extension updates",
"type": "boolean",
-
"default": "true"
}
},
"cors": [
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "moonbase",
"apiLevel": 2,
"meta": {
···
"tagline": "The official settings UI for moonlight",
"authors": ["Cynosphere", "NotNite", "redstonekasi"]
},
+
"dependencies": ["spacepack", "settings", "common", "notices", "contextMenu"],
"settings": {
"sections": {
+
"advice": "reload",
"displayName": "Split into sections",
"description": "Show the Moonbase tabs as separate sections",
+
"type": "boolean",
+
"default": false
+
},
+
"oldLocation": {
+
"advice": "reload",
+
"displayName": "Put Moonbase back at the bottom",
+
"type": "boolean",
+
"default": false
},
"saveFilter": {
+
"advice": "none",
"displayName": "Persist filter",
"description": "Save extension filter in config",
+
"type": "boolean",
+
"default": false
},
"updateChecking": {
+
"advice": "none",
"displayName": "Automatic update checking",
"description": "Checks for updates to moonlight",
"type": "boolean",
+
"default": true
},
"updateBanner": {
+
"advice": "none",
"displayName": "Show update banner",
"description": "Shows a banner for moonlight and extension updates",
"type": "boolean",
+
"default": true
}
},
"cors": [
+29 -43
packages/core-extensions/src/moonbase/native.ts
···
import { distDir, repoUrlFile, installedVersionFile } from "@moonlight-mod/types/constants";
import { parseTarGzip } from "nanotar";
const githubRepo = "moonlight-mod/moonlight";
const githubApiUrl = `https://api.github.com/repos/${githubRepo}/releases/latest`;
const artifactName = "dist.tar.gz";
···
const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref";
const nightlyZipUrl = "https://moonlight-mod.github.io/moonlight/dist.tar.gz";
-
export const userAgent = `moonlight/${moonlightNode.version} (https://github.com/moonlight-mod/moonlight)`;
async function getStableRelease(): Promise<{
name: string;
···
}> {
const req = await fetch(githubApiUrl, {
cache: "no-store",
-
headers: {
-
"User-Agent": userAgent
-
}
});
return await req.json();
}
export default function getNatives(): MoonbaseNatives {
-
const logger = moonlightNode.getLogger("moonbase/natives");
return {
async checkForMoonlightUpdate() {
try {
-
if (moonlightNode.branch === MoonlightBranch.STABLE) {
const json = await getStableRelease();
-
return json.name !== moonlightNode.version ? json.name : null;
-
} else if (moonlightNode.branch === MoonlightBranch.NIGHTLY) {
const req = await fetch(nightlyRefUrl, {
cache: "no-store",
-
headers: {
-
"User-Agent": userAgent
-
}
});
const ref = (await req.text()).split("\n")[0];
-
return ref !== moonlightNode.version ? ref : null;
}
return null;
···
}
},
-
async updateMoonlight() {
// Note: this won't do anything on browser, we should probably disable it
// entirely when running in browser.
async function downloadStable(): Promise<[ArrayBuffer, string]> {
···
logger.debug(`Downloading ${asset.browser_download_url}`);
const req = await fetch(asset.browser_download_url, {
cache: "no-store",
-
headers: {
-
"User-Agent": userAgent
-
}
});
return [await req.arrayBuffer(), json.name];
···
logger.debug(`Downloading ${nightlyZipUrl}`);
const zipReq = await fetch(nightlyZipUrl, {
cache: "no-store",
-
headers: {
-
"User-Agent": userAgent
-
}
});
const refReq = await fetch(nightlyRefUrl, {
cache: "no-store",
-
headers: {
-
"User-Agent": userAgent
-
}
});
const ref = (await refReq.text()).split("\n")[0];
···
}
const [tar, ref] =
-
moonlightNode.branch === MoonlightBranch.STABLE
? await downloadStable()
-
: moonlightNode.branch === MoonlightBranch.NIGHTLY
? await downloadNightly()
: [null, null];
if (!tar || !ref) return;
-
const dist = moonlightNodeSandboxed.fs.join(moonlightNode.getMoonlightDir(), distDir);
if (await moonlightNodeSandboxed.fs.exists(dist)) await moonlightNodeSandboxed.fs.rmdir(dist);
await moonlightNodeSandboxed.fs.mkdir(dist);
···
}
logger.debug("Writing version file:", ref);
-
const versionFile = moonlightNodeSandboxed.fs.join(moonlightNode.getMoonlightDir(), installedVersionFile);
await moonlightNodeSandboxed.fs.writeFileString(versionFile, ref.trim());
logger.debug("Update extracted");
···
try {
const req = await fetch(repo, {
cache: "no-store",
-
headers: {
-
"User-Agent": userAgent
-
}
});
const json = await req.json();
ret[repo] = json;
···
async installExtension(manifest, url, repo) {
const req = await fetch(url, {
cache: "no-store",
-
headers: {
-
"User-Agent": userAgent
-
}
});
-
const dir = moonlightNode.getExtensionDir(manifest.id);
// remake it in case of updates
if (await moonlightNodeSandboxed.fs.exists(dir)) await moonlightNodeSandboxed.fs.rmdir(dir);
await moonlightNodeSandboxed.fs.mkdir(dir);
···
},
async deleteExtension(id) {
-
const dir = moonlightNode.getExtensionDir(id);
await moonlightNodeSandboxed.fs.rmdir(dir);
-
},
-
-
getExtensionConfig(id, key) {
-
const config = moonlightNode.config.extensions[id];
-
if (typeof config === "object") {
-
return config.config?.[key];
-
}
-
-
return undefined;
}
};
}
···
import { distDir, repoUrlFile, installedVersionFile } from "@moonlight-mod/types/constants";
import { parseTarGzip } from "nanotar";
+
const moonlightGlobal = globalThis.moonlightHost ?? globalThis.moonlightNode;
+
const githubRepo = "moonlight-mod/moonlight";
const githubApiUrl = `https://api.github.com/repos/${githubRepo}/releases/latest`;
const artifactName = "dist.tar.gz";
···
const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref";
const nightlyZipUrl = "https://moonlight-mod.github.io/moonlight/dist.tar.gz";
+
export const userAgent = `moonlight/${moonlightGlobal.version} (https://github.com/moonlight-mod/moonlight)`;
+
+
// User-Agent header causes trouble on Firefox
+
const isBrowser = globalThis.moonlightNode != null && globalThis.moonlightNode.isBrowser;
+
const sharedHeaders: Record<string, string> = {};
+
if (!isBrowser) sharedHeaders["User-Agent"] = userAgent;
async function getStableRelease(): Promise<{
name: string;
···
}> {
const req = await fetch(githubApiUrl, {
cache: "no-store",
+
headers: sharedHeaders
});
return await req.json();
}
export default function getNatives(): MoonbaseNatives {
+
const logger = moonlightGlobal.getLogger("moonbase/natives");
return {
async checkForMoonlightUpdate() {
try {
+
if (moonlightGlobal.branch === MoonlightBranch.STABLE) {
const json = await getStableRelease();
+
return json.name !== moonlightGlobal.version ? json.name : null;
+
} else if (moonlightGlobal.branch === MoonlightBranch.NIGHTLY) {
const req = await fetch(nightlyRefUrl, {
cache: "no-store",
+
headers: sharedHeaders
});
const ref = (await req.text()).split("\n")[0];
+
return ref !== moonlightGlobal.version ? ref : null;
}
return null;
···
}
},
+
async updateMoonlight(overrideBranch?: MoonlightBranch) {
+
const branch = overrideBranch ?? moonlightGlobal.branch;
+
// Note: this won't do anything on browser, we should probably disable it
// entirely when running in browser.
async function downloadStable(): Promise<[ArrayBuffer, string]> {
···
logger.debug(`Downloading ${asset.browser_download_url}`);
const req = await fetch(asset.browser_download_url, {
cache: "no-store",
+
headers: sharedHeaders
});
return [await req.arrayBuffer(), json.name];
···
logger.debug(`Downloading ${nightlyZipUrl}`);
const zipReq = await fetch(nightlyZipUrl, {
cache: "no-store",
+
headers: sharedHeaders
});
const refReq = await fetch(nightlyRefUrl, {
cache: "no-store",
+
headers: sharedHeaders
});
const ref = (await refReq.text()).split("\n")[0];
···
}
const [tar, ref] =
+
branch === MoonlightBranch.STABLE
? await downloadStable()
+
: branch === MoonlightBranch.NIGHTLY
? await downloadNightly()
: [null, null];
if (!tar || !ref) return;
+
const dist = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), distDir);
if (await moonlightNodeSandboxed.fs.exists(dist)) await moonlightNodeSandboxed.fs.rmdir(dist);
await moonlightNodeSandboxed.fs.mkdir(dist);
···
}
logger.debug("Writing version file:", ref);
+
const versionFile = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), installedVersionFile);
await moonlightNodeSandboxed.fs.writeFileString(versionFile, ref.trim());
logger.debug("Update extracted");
···
try {
const req = await fetch(repo, {
cache: "no-store",
+
headers: sharedHeaders
});
const json = await req.json();
ret[repo] = json;
···
async installExtension(manifest, url, repo) {
const req = await fetch(url, {
cache: "no-store",
+
headers: sharedHeaders
});
+
const dir = moonlightGlobal.getExtensionDir(manifest.id);
// remake it in case of updates
if (await moonlightNodeSandboxed.fs.exists(dir)) await moonlightNodeSandboxed.fs.rmdir(dir);
await moonlightNodeSandboxed.fs.mkdir(dir);
···
},
async deleteExtension(id) {
+
const dir = moonlightGlobal.getExtensionDir(id);
await moonlightNodeSandboxed.fs.rmdir(dir);
}
};
}
+99 -11
packages/core-extensions/src/moonbase/style.css
···
--moonbase-fg: #fffba6;
}
-
.moonbase-settings> :first-child {
margin-top: 0px;
}
textarea.moonbase-resizeable {
resize: vertical;
}
.moonbase-updates-notice {
background-color: var(--moonbase-bg);
color: var(--moonbase-fg);
line-height: unset;
height: 36px;
}
···
gap: 2px;
}
.moonbase-update-section {
background-color: var(--moonbase-bg);
--info-help-foreground: var(--moonbase-fg);
border: none !important;
color: var(--moonbase-fg);
-
-
display: flex;
-
flex-direction: row;
-
justify-content: space-between;
}
.moonbase-update-section button {
···
border-color: var(--moonbase-fg);
}
-
.moonbase-update-section-buttons {
display: flex;
flex-direction: row;
gap: 8px;
}
-
/* crash screen */
-
.moonbase-crash-wrapper>[class^="buttons_"] {
gap: 1rem;
}
···
box-sizing: border-box;
padding: 0;
font-family: var(--font-code);
-
font-size: .75rem;
line-height: 1rem;
margin: 6px;
white-space: pre-wrap;
background-clip: border-box;
-
&>code {
-
font-size: .875rem;
line-height: 1.125rem;
text-indent: 0;
white-space: pre-wrap;
···
line-height: 1.286;
font-weight: 400;
}
···
--moonbase-fg: #fffba6;
}
+
.moonbase-settings > :first-child {
margin-top: 0px;
+
}
+
+
.moonbase-retry-button {
+
padding: 8px;
+
margin-right: 8px;
}
textarea.moonbase-resizeable {
resize: vertical;
}
+
.moonbase-link-buttons {
+
border-bottom: 2px solid var(--background-modifier-accent);
+
margin-bottom: -2px;
+
margin-left: 0 !important;
+
padding-right: 20px;
+
gap: 1rem;
+
}
+
+
.moonbase-speen {
+
animation: moonbase-speen-animation 0.25s linear infinite;
+
}
+
+
@keyframes moonbase-speen-animation {
+
from {
+
transform: rotate(0deg);
+
}
+
to {
+
transform: rotate(360deg);
+
}
+
}
+
+
/* Update notice at the top of the client */
.moonbase-updates-notice {
background-color: var(--moonbase-bg);
color: var(--moonbase-fg);
+
--custom-notice-text: var(--moonbase-fg);
line-height: unset;
height: 36px;
}
···
gap: 2px;
}
+
/* Help messages in Moonbase UI */
+
.moonbase-help-message {
+
display: flex;
+
flex-direction: row;
+
justify-content: space-between;
+
}
+
+
.moonbase-help-message-sticky {
+
position: sticky;
+
top: 24px;
+
z-index: 10;
+
background-color: var(--background-primary);
+
}
+
+
.moonbase-extension-update-section {
+
margin-top: 15px;
+
}
+
.moonbase-update-section {
background-color: var(--moonbase-bg);
--info-help-foreground: var(--moonbase-fg);
border: none !important;
color: var(--moonbase-fg);
}
.moonbase-update-section button {
···
border-color: var(--moonbase-fg);
}
+
.moonbase-help-message-buttons {
display: flex;
flex-direction: row;
gap: 8px;
+
align-items: center;
}
+
.moonbase-update-divider {
+
margin: 32px 0;
+
}
+
+
.moonlight-card-info-header {
+
margin-bottom: 0.25rem;
+
}
+
+
.moonlight-card-badge {
+
border-radius: 0.1875rem;
+
padding: 0 0.275rem;
+
margin-right: 0.4em;
+
background-color: var(--badge-color, var(--bg-mod-strong));
+
}
+
+
/* Crash screen */
+
.moonbase-crash-wrapper > [class^="buttons_"] {
gap: 1rem;
}
···
box-sizing: border-box;
padding: 0;
font-family: var(--font-code);
+
font-size: 0.75rem;
line-height: 1rem;
margin: 6px;
white-space: pre-wrap;
background-clip: border-box;
+
& > code {
+
font-size: 0.875rem;
line-height: 1.125rem;
text-indent: 0;
white-space: pre-wrap;
···
line-height: 1.286;
font-weight: 400;
}
+
+
/* About page */
+
.moonbase-wordmark {
+
width: 100%;
+
}
+
+
.moonbase-devs {
+
width: 100%;
+
display: flex;
+
justify-content: center;
+
gap: 0rem 0.5rem;
+
padding-top: 0.5rem;
+
}
+
+
.moonbase-dev {
+
height: 4rem;
+
}
+
+
.moonbase-dev-avatar {
+
width: 2rem;
+
border-radius: 50%;
+
}
+
+
.moonbase-gap {
+
gap: 0.5rem;
+
}
+
+
.moonbase-about-page {
+
gap: 1rem;
+
}
+12 -3
packages/core-extensions/src/moonbase/types.ts
···
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
-
import { DetectedExtension, ExtensionManifest } from "@moonlight-mod/types";
export type MoonbaseNatives = {
checkForMoonlightUpdate(): Promise<string | null>;
-
updateMoonlight(): Promise<void>;
fetchRepositories(repos: string[]): Promise<Record<string, RepositoryManifest[]>>;
installExtension(manifest: RepositoryManifest, url: string, repo: string): Promise<void>;
deleteExtension(id: string): Promise<void>;
-
getExtensionConfig(id: string, key: string): any;
};
export type RepositoryManifest = ExtensionManifest & {
···
state: ExtensionState;
compat: ExtensionCompat;
hasUpdate: boolean;
};
export enum UpdateState {
···
Installed,
Failed
}
···
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
+
import { DetectedExtension, ExtensionManifest, MoonlightBranch } from "@moonlight-mod/types";
export type MoonbaseNatives = {
checkForMoonlightUpdate(): Promise<string | null>;
+
updateMoonlight(overrideBranch?: MoonlightBranch): Promise<void>;
fetchRepositories(repos: string[]): Promise<Record<string, RepositoryManifest[]>>;
installExtension(manifest: RepositoryManifest, url: string, repo: string): Promise<void>;
deleteExtension(id: string): Promise<void>;
};
export type RepositoryManifest = ExtensionManifest & {
···
state: ExtensionState;
compat: ExtensionCompat;
hasUpdate: boolean;
+
changelog?: string;
+
settingsOverride?: ExtensionManifest["settings"];
};
export enum UpdateState {
···
Installed,
Failed
}
+
+
// Ordered in terms of priority
+
export enum RestartAdvice {
+
NotNeeded, // No action is needed
+
ReloadSuggested, // A reload might be needed
+
ReloadNeeded, // A reload is needed
+
RestartNeeded // A restart is needed
+
}
+36
packages/core-extensions/src/moonbase/webpackModules/ThemeDarkIcon.tsx
···
···
+
// RIP to ThemeDarkIcon ????-2025
+
// <Cynthia> Failed to remap "ThemeDarkIcon" in "discord/components/common/index"
+
// <NotNite> bro are you fucking kidding me
+
// <NotNite> that's literally the icon we use for the update banner
+
+
import React from "@moonlight-mod/wp/react";
+
import icons from "@moonlight-mod/wp/common_icons";
+
import type { IconProps } from "@moonlight-mod/types/coreExtensions/common";
+
+
export default function ThemeDarkIcon(props?: IconProps) {
+
const parsed = icons.parseProps(props);
+
+
return (
+
<svg
+
aria-hidden="true"
+
role="img"
+
xmlns="http://www.w3.org/2000/svg"
+
width={parsed.width}
+
height={parsed.height}
+
fill="none"
+
viewBox="0 0 24 24"
+
>
+
<path
+
fill={parsed.fill}
+
className={parsed.className}
+
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"
+
/>
+
+
<path
+
fill={parsed.fill}
+
className={parsed.className}
+
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"
+
/>
+
</svg>
+
);
+
}
+90 -9
packages/core-extensions/src/moonbase/webpackModules/crashScreen.tsx
···
import React from "@moonlight-mod/wp/react";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
import { useStateFromStores, useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
import { RepositoryManifest, UpdateState } from "../types";
-
const { Button, TabBar } = Components;
-
const TabBarClasses = spacepack.findByCode(/tabBar:"tabBar_[a-z0-9]+",tabBarItem:"tabBarItem_[a-z0-9]+"/)[0].exports;
const logger = moonlight.getLogger("moonbase/crashScreen");
···
}}
>
{extensionButtonStrings[state]}
</Button>
</div>
</div>
···
};
});
return (
<div className="moonbase-crash-wrapper">
{action}
<TabBar
-
className={`${TabBarClasses.tabBar} moonbase-crash-tabs`}
type="top"
selectedItem={tab}
onItemSelect={(v) => setTab(v)}
>
-
<TabBar.Item className={TabBarClasses.tabBarItem} id="crash">
-
Crash Details
</TabBar.Item>
-
<TabBar.Item className={TabBarClasses.tabBarItem} id="extensions" disabled={updateCount === 0}>
-
{`Extension Updates (${updateCount})`}
</TabBar.Item>
</TabBar>
{tab === "crash" ? (
···
{updates.map(([id, ext]) => (
<ExtensionUpdateCard id={Number(id)} ext={ext} />
))}
</div>
) : null}
</div>
···
import React from "@moonlight-mod/wp/react";
+
import { Button, TabBar } from "@moonlight-mod/wp/discord/components/common/index";
import { useStateFromStores, useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
import { RepositoryManifest, UpdateState } from "../types";
+
import { ConfigExtension, DetectedExtension } from "@moonlight-mod/types";
+
import DiscoveryClasses from "@moonlight-mod/wp/discord/modules/discovery/web/Discovery.css";
+
const MODULE_REGEX = /Webpack-Module\/(\d+)\/(\d+)/g;
const logger = moonlight.getLogger("moonbase/crashScreen");
···
}}
>
{extensionButtonStrings[state]}
+
</Button>
+
</div>
+
</div>
+
);
+
}
+
+
function ExtensionDisableCard({ ext }: { ext: DetectedExtension }) {
+
function disableWithDependents() {
+
const disable = new Set<string>();
+
disable.add(ext.id);
+
for (const [id, dependencies] of moonlightNode.processedExtensions.dependencyGraph) {
+
if (dependencies?.has(ext.id)) disable.add(id);
+
}
+
+
const config = structuredClone(moonlightNode.config);
+
for (const id in config.extensions) {
+
if (!disable.has(id)) continue;
+
if (typeof config.extensions[id] === "boolean") config.extensions[id] = false;
+
else (config.extensions[id] as ConfigExtension).enabled = false;
+
}
+
+
let msg = `Are you sure you want to disable "${ext.manifest.meta?.name ?? ext.id}"`;
+
if (disable.size > 1) {
+
msg += ` and its ${disable.size - 1} dependent${disable.size - 1 === 1 ? "" : "s"}`;
+
}
+
msg += "?";
+
+
if (confirm(msg)) {
+
moonlightNode.writeConfig(config);
+
window.location.reload();
+
}
+
}
+
+
return (
+
<div className="moonbase-crash-extensionCard">
+
<div className="moonbase-crash-extensionCard-meta">
+
<div className="moonbase-crash-extensionCard-title">{ext.manifest.meta?.name ?? ext.id}</div>
+
<div className="moonbase-crash-extensionCard-version">{`v${ext.manifest.version ?? "???"}`}</div>
+
</div>
+
<div className="moonbase-crash-extensionCard-button">
+
<Button color={Button.Colors.RED} onClick={disableWithDependents}>
+
Disable
</Button>
</div>
</div>
···
};
});
+
const causes = React.useMemo(() => {
+
const causes = new Set<string>();
+
if (state.error.stack) {
+
for (const [, , id] of state.error.stack.matchAll(MODULE_REGEX))
+
for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
+
}
+
for (const [, , id] of state.info.componentStack.matchAll(MODULE_REGEX))
+
for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
+
+
for (const [path, id] of Object.entries(moonlight.moonmap.modules)) {
+
const MAPPING_REGEX = new RegExp(
+
// @ts-expect-error Only Firefox has RegExp.escape
+
`(${RegExp.escape ? RegExp.escape(path) : path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`,
+
"g"
+
);
+
+
if (state.error.stack) {
+
for (const match of state.error.stack.matchAll(MAPPING_REGEX))
+
if (match) for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
+
}
+
for (const match of state.info.componentStack.matchAll(MAPPING_REGEX))
+
if (match) for (const ext of moonlight.patched.get(id) ?? []) causes.add(ext);
+
}
+
+
return [...causes];
+
}, []);
+
return (
<div className="moonbase-crash-wrapper">
{action}
<TabBar
+
className={`${DiscoveryClasses.tabBar} moonbase-crash-tabs`}
type="top"
selectedItem={tab}
onItemSelect={(v) => setTab(v)}
>
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id="crash">
+
Crash details
</TabBar.Item>
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id="extensions" disabled={updateCount === 0}>
+
{`Extension updates (${updateCount})`}
+
</TabBar.Item>
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id="causes" disabled={causes.length === 0}>
+
{`Possible causes (${causes.length})`}
</TabBar.Item>
</TabBar>
{tab === "crash" ? (
···
{updates.map(([id, ext]) => (
<ExtensionUpdateCard id={Number(id)} ext={ext} />
))}
+
</div>
+
) : null}
+
{tab === "causes" ? (
+
<div className="moonbase-crash-extensions">
+
{causes
+
.map((ext) => moonlightNode.extensions.find((e) => e.id === ext)!)
+
.map((ext) => (
+
<ExtensionDisableCard ext={ext} />
+
))}
</div>
) : null}
</div>
+27 -17
packages/core-extensions/src/moonbase/webpackModules/settings.tsx
···
import settings from "@moonlight-mod/wp/settings_settings";
import React from "@moonlight-mod/wp/react";
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
-
import { Moonbase, pages } from "@moonlight-mod/wp/moonbase_ui";
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
-
-
import Update from "./ui/update";
-
-
const { MenuItem, Text, Breadcrumbs } = Components;
-
-
const Margins = spacepack.require("discord/styles/shared/Margins.css");
-
-
const { open } = spacepack.findByExports("setSection", "clearSubsection")[0].exports.Z;
const notice = {
stores: [MoonbaseSettingsStore],
element: () => {
// Require it here because lazy loading SUX
-
const SettingsNotice = spacepack.findByCode("onSaveButtonColor", "FocusRingScope")[0].exports.Z;
return (
<SettingsNotice
submitting={MoonbaseSettingsStore.submitting}
···
}
};
function addSection(id: string, name: string, element: React.FunctionComponent) {
-
settings.addSection(`moonbase-${id}`, name, element, null, -2, notice);
}
// FIXME: move to component types
···
);
}
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
-
settings.addHeader("Moonbase", -2);
-
for (const page of pages) {
addSection(page.id, page.name, () => {
const breadcrumbs = [
{ id: "moonbase", label: "Moonbase" },
···
{page.name}
</Breadcrumbs>
<Update />
<page.element />
···
);
});
}
} else {
-
settings.addSection("moonbase", "Moonbase", Moonbase, null, -2, notice);
settings.addSectionMenuItems(
"moonbase",
...pages.map((page, i) => (
-
<MenuItem key={page.id} id={`moonbase-${page.id}`} label={page.name} action={() => open("moonbase", i)} />
))
);
}
···
import settings from "@moonlight-mod/wp/settings_settings";
import React from "@moonlight-mod/wp/react";
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
+
import { Moonbase, pages, RestartAdviceMessage, Update } from "@moonlight-mod/wp/moonbase_ui";
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
+
import { Text, Breadcrumbs } from "@moonlight-mod/wp/discord/components/common/index";
+
import { MenuItem } from "@moonlight-mod/wp/contextMenu_contextMenu";
const notice = {
stores: [MoonbaseSettingsStore],
element: () => {
// Require it here because lazy loading SUX
+
const SettingsNotice = spacepack.require("discord/components/common/SettingsNotice").default;
return (
<SettingsNotice
submitting={MoonbaseSettingsStore.submitting}
···
}
};
+
const oldLocation = MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "oldLocation", false);
+
const position = oldLocation ? -2 : -9999;
+
function addSection(id: string, name: string, element: React.FunctionComponent) {
+
settings.addSection(`moonbase-${id}`, name, element, null, position, notice);
}
// FIXME: move to component types
···
);
}
+
if (!oldLocation) {
+
settings.addDivider(position);
+
}
+
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
+
if (oldLocation) settings.addHeader("Moonbase", position);
+
const _pages = oldLocation ? pages : pages.reverse();
+
for (const page of _pages) {
addSection(page.id, page.name, () => {
const breadcrumbs = [
{ id: "moonbase", label: "Moonbase" },
···
{page.name}
</Breadcrumbs>
+
<RestartAdviceMessage />
<Update />
<page.element />
···
);
});
}
+
+
if (!oldLocation) settings.addHeader("Moonbase", position);
} else {
+
settings.addSection("moonbase", "Moonbase", Moonbase, null, position, notice);
settings.addSectionMenuItems(
"moonbase",
...pages.map((page, i) => (
+
<MenuItem
+
key={page.id}
+
id={`moonbase-${page.id}`}
+
label={page.name}
+
action={() => UserSettingsModalActionCreators.open("moonbase", i.toString())}
+
/>
))
);
}
+239 -93
packages/core-extensions/src/moonbase/webpackModules/stores.ts
···
-
import { Config, ExtensionLoadSource } from "@moonlight-mod/types";
-
import { ExtensionState, MoonbaseExtension, MoonbaseNatives, RepositoryManifest } from "../types";
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
import getNatives from "../native";
import { mainRepo } from "@moonlight-mod/types/constants";
import { checkExtensionCompat, ExtensionCompat } from "@moonlight-mod/core/extension/loader";
import { CustomComponent } from "@moonlight-mod/types/coreExtensions/moonbase";
const logger = moonlight.getLogger("moonbase");
···
if (moonlightNode.isBrowser) natives = getNatives();
class MoonbaseSettingsStore extends Store<any> {
-
private origConfig: Config;
private config: Config;
private extensionIndex: number;
private configComponents: Record<string, Record<string, CustomComponent>> = {};
···
submitting: boolean;
installing: boolean;
newVersion: string | null;
shouldShowNotice: boolean;
extensions: { [id: number]: MoonbaseExtension };
updates: {
[id: number]: {
···
constructor() {
super(Dispatcher);
-
this.origConfig = moonlightNode.config;
-
this.config = this.clone(this.origConfig);
this.extensionIndex = 0;
this.modified = false;
···
};
}
-
natives!
-
.fetchRepositories(this.config.repositories)
-
.then((ret) => {
-
for (const [repo, exts] of Object.entries(ret)) {
-
try {
-
for (const ext of exts) {
-
const uniqueId = this.extensionIndex++;
-
const extensionData = {
-
id: ext.id,
-
uniqueId,
-
manifest: ext,
-
source: { type: ExtensionLoadSource.Normal, url: repo },
-
state: ExtensionState.NotDownloaded,
-
compat: ExtensionCompat.Compatible,
-
hasUpdate: false
-
};
-
// Don't present incompatible updates
-
if (checkExtensionCompat(ext) !== ExtensionCompat.Compatible) continue;
-
const existing = this.getExisting(extensionData);
-
if (existing != null) {
-
// Make sure the download URL is properly updated
-
for (const [id, e] of Object.entries(this.extensions)) {
-
if (e.id === ext.id && e.source.url === repo) {
-
this.extensions[parseInt(id)].manifest = {
-
...e.manifest,
-
download: ext.download
-
};
-
break;
-
}
-
}
-
if (this.hasUpdate(extensionData)) {
-
this.updates[existing.uniqueId] = {
-
version: ext.version!,
-
download: ext.download,
-
updateManifest: ext
-
};
-
existing.hasUpdate = true;
-
}
-
continue;
-
}
-
this.extensions[uniqueId] = extensionData;
-
}
-
} catch (e) {
-
logger.error(`Error processing repository ${repo}`, e);
}
}
-
this.emitChange();
-
})
-
.then(() =>
-
this.getExtensionConfigRaw("moonbase", "updateChecking", true)
-
? natives!.checkForMoonlightUpdate()
-
: new Promise<null>((resolve) => resolve(null))
-
)
-
.then((version) => {
-
this.newVersion = version;
-
this.emitChange();
-
})
-
.then(() => {
-
this.shouldShowNotice = this.newVersion != null || Object.keys(this.updates).length > 0;
-
this.emitChange();
-
});
}
private getExisting(ext: MoonbaseExtension) {
···
// Jank
private isModified() {
-
const orig = JSON.stringify(this.origConfig);
const curr = JSON.stringify(this.config);
return orig !== curr;
}
···
return this.submitting || this.installing;
}
showNotice() {
return this.modified;
}
···
getExtensionConfig<T>(uniqueId: number, key: string): T | undefined {
const ext = this.getExtension(uniqueId);
-
const defaultValue = ext.manifest.settings?.[key]?.default;
-
const clonedDefaultValue = this.clone(defaultValue);
-
const cfg = this.config.extensions[ext.id];
-
-
if (cfg == null || typeof cfg === "boolean") return clonedDefaultValue;
-
return cfg.config?.[key] ?? clonedDefaultValue;
}
getExtensionConfigRaw<T>(id: string, key: string, defaultValue: T | undefined): T | undefined {
const cfg = this.config.extensions[id];
-
if (cfg == null || typeof cfg === "boolean") return defaultValue;
return cfg.config?.[key] ?? defaultValue;
}
getExtensionConfigName(uniqueId: number, key: string) {
const ext = this.getExtension(uniqueId);
-
return ext.manifest.settings?.[key]?.displayName ?? key;
}
getExtensionConfigDescription(uniqueId: number, key: string) {
const ext = this.getExtension(uniqueId);
-
return ext.manifest.settings?.[key]?.description;
}
setExtensionConfig(id: string, key: string, value: any) {
-
const oldConfig = this.config.extensions[id];
-
const newConfig =
-
typeof oldConfig === "boolean"
-
? {
-
enabled: oldConfig,
-
config: { [key]: value }
-
}
-
: {
-
...oldConfig,
-
config: { ...(oldConfig?.config ?? {}), [key]: value }
-
};
-
-
this.config.extensions[id] = newConfig;
this.modified = this.isModified();
this.emitChange();
}
···
this.emitChange();
}
async installExtension(uniqueId: number) {
const ext = this.getExtension(uniqueId);
if (!("download" in ext.manifest)) {
···
this.extensions[uniqueId].state = ExtensionState.Disabled;
}
-
if (update != null) this.extensions[uniqueId].compat = checkExtensionCompat(update.updateManifest);
delete this.updates[uniqueId];
} catch (e) {
···
}
this.installing = false;
this.emitChange();
}
···
const aRank = this.getRank(a);
const bRank = this.getRank(b);
if (aRank === bRank) {
-
const repoIndex = this.config.repositories.indexOf(a.source.url!);
-
const otherRepoIndex = this.config.repositories.indexOf(b.source.url!);
return repoIndex - otherRepoIndex;
} else {
return bRank - aRank;
···
}
this.installing = false;
this.emitChange();
}
async updateMoonlight() {
-
await natives.updateMoonlight();
}
getConfigOption<K extends keyof Config>(key: K): Config[K] {
···
return this.configComponents[ext]?.[name];
}
writeConfig() {
this.submitting = true;
moonlightNode.writeConfig(this.config);
-
this.origConfig = this.clone(this.config);
this.submitting = false;
this.modified = false;
this.emitChange();
}
reset() {
this.submitting = false;
this.modified = false;
-
this.config = this.clone(this.origConfig);
this.emitChange();
}
// Required because electron likes to make it immutable sometimes.
···
+
import { Config, ExtensionEnvironment, ExtensionLoadSource, ExtensionSettingsAdvice } from "@moonlight-mod/types";
+
import {
+
ExtensionState,
+
MoonbaseExtension,
+
MoonbaseNatives,
+
RepositoryManifest,
+
RestartAdvice,
+
UpdateState
+
} from "../types";
import { Store } from "@moonlight-mod/wp/discord/packages/flux";
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
import getNatives from "../native";
import { mainRepo } from "@moonlight-mod/types/constants";
import { checkExtensionCompat, ExtensionCompat } from "@moonlight-mod/core/extension/loader";
import { CustomComponent } from "@moonlight-mod/types/coreExtensions/moonbase";
+
import { getConfigOption, setConfigOption } from "@moonlight-mod/core/util/config";
+
import diff from "microdiff";
const logger = moonlight.getLogger("moonbase");
···
if (moonlightNode.isBrowser) natives = getNatives();
class MoonbaseSettingsStore extends Store<any> {
+
private initialConfig: Config;
+
private savedConfig: Config;
private config: Config;
private extensionIndex: number;
private configComponents: Record<string, Record<string, CustomComponent>> = {};
···
submitting: boolean;
installing: boolean;
+
#updateState = UpdateState.Ready;
+
get updateState() {
+
return this.#updateState;
+
}
newVersion: string | null;
shouldShowNotice: boolean;
+
restartAdvice = RestartAdvice.NotNeeded;
+
extensions: { [id: number]: MoonbaseExtension };
updates: {
[id: number]: {
···
constructor() {
super(Dispatcher);
+
this.initialConfig = moonlightNode.config;
+
this.savedConfig = moonlightNode.config;
+
this.config = this.clone(this.savedConfig);
this.extensionIndex = 0;
this.modified = false;
···
};
}
+
this.checkUpdates();
+
}
+
async checkUpdates() {
+
await Promise.all([this.checkExtensionUpdates(), this.checkMoonlightUpdates()]);
+
this.shouldShowNotice = this.newVersion != null || Object.keys(this.updates).length > 0;
+
this.emitChange();
+
}
+
private async checkExtensionUpdates() {
+
const repositories = await natives!.fetchRepositories(this.savedConfig.repositories);
+
// Reset update state
+
for (const id in this.extensions) {
+
const ext = this.extensions[id];
+
ext.hasUpdate = false;
+
ext.changelog = undefined;
+
}
+
this.updates = {};
+
for (const [repo, exts] of Object.entries(repositories)) {
+
for (const ext of exts) {
+
const uniqueId = this.extensionIndex++;
+
const extensionData = {
+
id: ext.id,
+
uniqueId,
+
manifest: ext,
+
source: { type: ExtensionLoadSource.Normal, url: repo },
+
state: ExtensionState.NotDownloaded,
+
compat: ExtensionCompat.Compatible,
+
hasUpdate: false
+
};
+
// Don't present incompatible updates
+
if (checkExtensionCompat(ext) !== ExtensionCompat.Compatible) continue;
+
+
const existing = this.getExisting(extensionData);
+
if (existing != null) {
+
// Make sure the download URL is properly updated
+
existing.manifest = {
+
...existing.manifest,
+
download: ext.download
+
};
+
+
if (this.hasUpdate(extensionData)) {
+
this.updates[existing.uniqueId] = {
+
version: ext.version!,
+
download: ext.download,
+
updateManifest: ext
+
};
+
existing.hasUpdate = true;
+
existing.changelog = ext.meta?.changelog;
}
+
} else {
+
this.extensions[uniqueId] = extensionData;
}
+
}
+
}
+
}
+
private async checkMoonlightUpdates() {
+
this.newVersion = this.getExtensionConfigRaw("moonbase", "updateChecking", true)
+
? await natives!.checkForMoonlightUpdate()
+
: null;
}
private getExisting(ext: MoonbaseExtension) {
···
// Jank
private isModified() {
+
const orig = JSON.stringify(this.savedConfig);
const curr = JSON.stringify(this.config);
return orig !== curr;
}
···
return this.submitting || this.installing;
}
+
// Required for the settings store contract
showNotice() {
return this.modified;
}
···
getExtensionConfig<T>(uniqueId: number, key: string): T | undefined {
const ext = this.getExtension(uniqueId);
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
+
return getConfigOption(ext.id, key, this.config, settings);
}
getExtensionConfigRaw<T>(id: string, key: string, defaultValue: T | undefined): T | undefined {
const cfg = this.config.extensions[id];
if (cfg == null || typeof cfg === "boolean") return defaultValue;
return cfg.config?.[key] ?? defaultValue;
}
getExtensionConfigName(uniqueId: number, key: string) {
const ext = this.getExtension(uniqueId);
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
+
return settings?.[key]?.displayName ?? key;
}
getExtensionConfigDescription(uniqueId: number, key: string) {
const ext = this.getExtension(uniqueId);
+
const settings = ext.settingsOverride ?? ext.manifest.settings;
+
return settings?.[key]?.description;
}
setExtensionConfig(id: string, key: string, value: any) {
+
setConfigOption(this.config, id, key, value);
this.modified = this.isModified();
this.emitChange();
}
···
this.emitChange();
}
+
dismissAllExtensionUpdates() {
+
for (const id in this.extensions) {
+
this.extensions[id].hasUpdate = false;
+
}
+
this.emitChange();
+
}
+
+
async updateAllExtensions() {
+
for (const id of Object.keys(this.updates)) {
+
try {
+
await this.installExtension(parseInt(id));
+
} catch (e) {
+
logger.error("Error bulk updating extension", id, e);
+
}
+
}
+
}
+
async installExtension(uniqueId: number) {
const ext = this.getExtension(uniqueId);
if (!("download" in ext.manifest)) {
···
this.extensions[uniqueId].state = ExtensionState.Disabled;
}
+
if (update != null) {
+
const existing = this.extensions[uniqueId];
+
existing.settingsOverride = update.updateManifest.settings;
+
existing.compat = checkExtensionCompat(update.updateManifest);
+
existing.manifest = update.updateManifest;
+
existing.changelog = update.updateManifest.meta?.changelog;
+
}
delete this.updates[uniqueId];
} catch (e) {
···
}
this.installing = false;
+
this.restartAdvice = this.#computeRestartAdvice();
this.emitChange();
}
···
const aRank = this.getRank(a);
const bRank = this.getRank(b);
if (aRank === bRank) {
+
const repoIndex = this.savedConfig.repositories.indexOf(a.source.url!);
+
const otherRepoIndex = this.savedConfig.repositories.indexOf(b.source.url!);
return repoIndex - otherRepoIndex;
} else {
return bRank - aRank;
···
}
this.installing = false;
+
this.restartAdvice = this.#computeRestartAdvice();
this.emitChange();
}
async updateMoonlight() {
+
this.#updateState = UpdateState.Working;
+
this.emitChange();
+
+
await natives
+
.updateMoonlight()
+
.then(() => (this.#updateState = UpdateState.Installed))
+
.catch((e) => {
+
logger.error(e);
+
this.#updateState = UpdateState.Failed;
+
});
+
+
this.emitChange();
}
getConfigOption<K extends keyof Config>(key: K): Config[K] {
···
return this.configComponents[ext]?.[name];
}
+
#computeRestartAdvice() {
+
// If moonlight update needs a restart, always hide advice.
+
if (this.#updateState === UpdateState.Installed) return RestartAdvice.NotNeeded;
+
+
const i = this.initialConfig; // Initial config, from startup
+
const n = this.config; // New config about to be saved
+
+
let returnedAdvice = RestartAdvice.NotNeeded;
+
const updateAdvice = (r: RestartAdvice) => (returnedAdvice < r ? (returnedAdvice = r) : returnedAdvice);
+
+
// Top-level keys, repositories is not needed here because Moonbase handles it.
+
if (i.patchAll !== n.patchAll) updateAdvice(RestartAdvice.ReloadNeeded);
+
if (i.loggerLevel !== n.loggerLevel) updateAdvice(RestartAdvice.ReloadNeeded);
+
if (diff(i.devSearchPaths ?? [], n.devSearchPaths ?? [], { cyclesFix: false }).length !== 0)
+
return updateAdvice(RestartAdvice.RestartNeeded);
+
+
// Extension specific logic
+
for (const id in n.extensions) {
+
// Installed extension (might not be detected yet)
+
const ext = Object.values(this.extensions).find((e) => e.id === id && e.state !== ExtensionState.NotDownloaded);
+
// Installed and detected extension
+
const detected = moonlightNode.extensions.find((e) => e.id === id);
+
+
// If it's not installed at all, we don't care
+
if (!ext) continue;
+
+
const initState = i.extensions[id];
+
const newState = n.extensions[id];
+
+
const newEnabled = typeof newState === "boolean" ? newState : newState.enabled;
+
// If it's enabled but not detected yet, restart.
+
if (newEnabled && !detected) {
+
return updateAdvice(RestartAdvice.RestartNeeded);
+
}
+
+
// Toggling extensions specifically wants to rely on the initial state,
+
// that's what was considered when loading extensions.
+
const initEnabled = initState && (typeof initState === "boolean" ? initState : initState.enabled);
+
if (initEnabled !== newEnabled || detected?.manifest.version !== ext.manifest.version) {
+
// If we have the extension locally, we confidently know if it has host/preload scripts.
+
// If not, we have to respect the environment specified in the manifest.
+
// If that is the default, we can't know what's needed.
+
+
if (detected?.scripts.hostPath || detected?.scripts.nodePath) {
+
return updateAdvice(RestartAdvice.RestartNeeded);
+
}
+
+
switch (ext.manifest.environment) {
+
case ExtensionEnvironment.Both:
+
case ExtensionEnvironment.Web:
+
updateAdvice(RestartAdvice.ReloadNeeded);
+
continue;
+
case ExtensionEnvironment.Desktop:
+
return updateAdvice(RestartAdvice.RestartNeeded);
+
default:
+
updateAdvice(RestartAdvice.ReloadNeeded);
+
continue;
+
}
+
}
+
+
const initConfig = typeof initState === "boolean" ? {} : { ...initState?.config };
+
const newConfig = typeof newState === "boolean" ? {} : { ...newState?.config };
+
+
const def = ext.manifest.settings;
+
if (!def) continue;
+
+
for (const key in def) {
+
const defaultValue = def[key].default;
+
+
initConfig[key] ??= defaultValue;
+
newConfig[key] ??= defaultValue;
+
}
+
+
const changedKeys = diff(initConfig, newConfig, { cyclesFix: false }).map((c) => c.path[0]);
+
for (const key in def) {
+
if (!changedKeys.includes(key)) continue;
+
+
const advice = def[key].advice;
+
switch (advice) {
+
case ExtensionSettingsAdvice.None:
+
updateAdvice(RestartAdvice.NotNeeded);
+
continue;
+
case ExtensionSettingsAdvice.Reload:
+
updateAdvice(RestartAdvice.ReloadNeeded);
+
continue;
+
case ExtensionSettingsAdvice.Restart:
+
updateAdvice(RestartAdvice.RestartNeeded);
+
continue;
+
default:
+
updateAdvice(RestartAdvice.ReloadSuggested);
+
}
+
}
+
}
+
+
return returnedAdvice;
+
}
+
writeConfig() {
this.submitting = true;
+
this.restartAdvice = this.#computeRestartAdvice();
+
const modifiedRepos = diff(this.savedConfig.repositories, this.config.repositories);
moonlightNode.writeConfig(this.config);
+
this.savedConfig = this.clone(this.config);
this.submitting = false;
this.modified = false;
this.emitChange();
+
+
if (modifiedRepos.length !== 0) this.checkUpdates();
}
reset() {
this.submitting = false;
this.modified = false;
+
this.config = this.clone(this.savedConfig);
this.emitChange();
+
}
+
+
restartDiscord() {
+
if (moonlightNode.isBrowser) {
+
window.location.reload();
+
} else {
+
// @ts-expect-error TODO: DiscordNative
+
window.DiscordNative.app.relaunch();
+
}
}
// Required because electron likes to make it immutable sometimes.
+47
packages/core-extensions/src/moonbase/webpackModules/ui/HelpMessage.tsx
···
···
+
import React from "@moonlight-mod/wp/react";
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
+
import { Text } from "@moonlight-mod/wp/discord/components/common/index";
+
import HelpMessageClasses from "@moonlight-mod/wp/discord/components/common/HelpMessage.css";
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
+
+
// reimpl of HelpMessage but with a custom icon
+
export default function HelpMessage({
+
className,
+
text,
+
icon,
+
children,
+
type = "info"
+
}: {
+
className?: string;
+
text: string;
+
icon: React.ComponentType<any>;
+
type?: "warning" | "positive" | "error" | "info";
+
children?: React.ReactNode;
+
}) {
+
return (
+
<div
+
className={`${Margins.marginBottom20} ${HelpMessageClasses[type]} ${HelpMessageClasses.container} moonbase-help-message ${className}`}
+
>
+
<Flex direction={Flex.Direction.HORIZONTAL}>
+
<div
+
className={HelpMessageClasses.iconDiv}
+
style={{
+
alignItems: "center"
+
}}
+
>
+
{React.createElement(icon, {
+
size: "sm",
+
color: "currentColor",
+
className: HelpMessageClasses.icon
+
})}
+
</div>
+
+
<Text variant="text-sm/medium" color="currentColor" className={HelpMessageClasses.text}>
+
{text}
+
</Text>
+
+
{children}
+
</Flex>
+
</div>
+
);
+
}
+43
packages/core-extensions/src/moonbase/webpackModules/ui/RestartAdvice.tsx
···
···
+
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
+
import { Button, CircleWarningIcon } from "@moonlight-mod/wp/discord/components/common/index";
+
import React from "@moonlight-mod/wp/react";
+
import { RestartAdvice } from "../../types";
+
import HelpMessage from "./HelpMessage";
+
+
const strings: Record<RestartAdvice, string> = {
+
[RestartAdvice.NotNeeded]: "how did you even",
+
[RestartAdvice.ReloadSuggested]: "A reload might be needed to apply some of the changed options.",
+
[RestartAdvice.ReloadNeeded]: "A reload is needed to apply some of the changed options.",
+
[RestartAdvice.RestartNeeded]: "A restart is needed to apply some of the changed options."
+
};
+
+
const buttonStrings: Record<RestartAdvice, string> = {
+
[RestartAdvice.NotNeeded]: "huh?",
+
[RestartAdvice.ReloadSuggested]: "Reload",
+
[RestartAdvice.ReloadNeeded]: "Reload",
+
[RestartAdvice.RestartNeeded]: "Restart"
+
};
+
+
const actions: Record<RestartAdvice, () => void> = {
+
[RestartAdvice.NotNeeded]: () => {},
+
[RestartAdvice.ReloadSuggested]: () => window.location.reload(),
+
[RestartAdvice.ReloadNeeded]: () => window.location.reload(),
+
[RestartAdvice.RestartNeeded]: () => MoonbaseSettingsStore.restartDiscord()
+
};
+
+
export default function RestartAdviceMessage() {
+
const restartAdvice = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.restartAdvice);
+
+
if (restartAdvice === RestartAdvice.NotNeeded) return null;
+
+
return (
+
<div className="moonbase-help-message-sticky">
+
<HelpMessage text={strings[restartAdvice]} icon={CircleWarningIcon} type="warning">
+
<Button color={Button.Colors.YELLOW} size={Button.Sizes.TINY} onClick={actions[restartAdvice]}>
+
{buttonStrings[restartAdvice]}
+
</Button>
+
</HelpMessage>
+
</div>
+
);
+
}
+110
packages/core-extensions/src/moonbase/webpackModules/ui/about.tsx
···
···
+
import {
+
Text,
+
useThemeContext,
+
Button,
+
AngleBracketsIcon,
+
BookCheckIcon,
+
ClydeIcon
+
} from "@moonlight-mod/wp/discord/components/common/index";
+
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
+
import React from "@moonlight-mod/wp/react";
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
+
+
const wordmark = "https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/heads/main/img/wordmark.png";
+
const wordmarkLight =
+
"https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/heads/main/img/wordmark-light.png";
+
+
function parse(str: string) {
+
return MarkupUtils.parse(str, true, {
+
allowHeading: true,
+
allowLinks: true,
+
allowList: true
+
});
+
}
+
+
function Dev({ name, picture, link }: { name: string; picture: string; link: string }) {
+
return (
+
<Button onClick={() => window.open(link)} color={Button.Colors.PRIMARY} className="moonbase-dev">
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
+
<img src={picture} alt={name} className="moonbase-dev-avatar" />
+
+
<Text variant="text-md/semibold">{name}</Text>
+
</Flex>
+
</Button>
+
);
+
}
+
+
function IconButton({
+
text,
+
link,
+
icon,
+
openInClient
+
}: {
+
text: string;
+
link: string;
+
icon: React.FC<any>;
+
openInClient?: boolean;
+
}) {
+
return (
+
<Button
+
onClick={() => {
+
if (openInClient) {
+
try {
+
const { handleClick } = spacepack.require("discord/utils/MaskedLinkUtils");
+
handleClick({ href: link });
+
} catch {
+
window.open(link);
+
}
+
} else {
+
// Will open externally in the user's browser
+
window.open(link);
+
}
+
}}
+
>
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
+
{React.createElement(icon, {
+
size: "sm",
+
color: "currentColor"
+
})}
+
{text}
+
</Flex>
+
</Button>
+
);
+
}
+
+
export default function AboutPage() {
+
const darkTheme = useThemeContext()?.theme !== "light";
+
+
return (
+
<Flex direction={Flex.Direction.VERTICAL} align={Flex.Align.CENTER} className="moonbase-about-page">
+
<img src={darkTheme ? wordmarkLight : wordmark} alt="moonlight wordmark" className="moonbase-wordmark" />
+
+
<Text variant="heading-lg/medium">created by:</Text>
+
<div className="moonbase-devs">
+
<Dev name="Cynosphere" picture="https://github.com/Cynosphere.png" link="https://github.com/Cynosphere" />
+
<Dev name="NotNite" picture="https://github.com/NotNite.png" link="https://github.com/NotNite" />
+
<Dev name="adryd" picture="https://github.com/adryd325.png" link="https://github.com/adryd325" />
+
<Dev name="redstonekasi" picture="https://github.com/redstonekasi.png" link="https://github.com/redstonekasi" />
+
</div>
+
+
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER} className="moonbase-gap">
+
<IconButton text="View source" icon={AngleBracketsIcon} link="https://github.com/moonlight-mod/moonlight" />
+
<IconButton text="Open the docs" icon={BookCheckIcon} link="https://moonlight-mod.github.io/" />
+
<IconButton text="Join the server" icon={ClydeIcon} link="https://discord.gg/FdZBTFCP6F" openInClient={true} />
+
</Flex>
+
+
<Flex direction={Flex.Direction.VERTICAL} align={Flex.Align.START}>
+
<Text variant="text-sm/normal">
+
{parse(`moonlight \`${window.moonlight.version}\` on \`${window.moonlight.branch}\``)}
+
</Text>
+
+
<Text variant="text-sm/normal">
+
{parse(
+
"moonlight is licensed under the [GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.html) (`LGPL-3.0-or-later`)."
+
)}
+
</Text>
+
</Flex>
+
</Flex>
+
);
+
}
+14 -13
packages/core-extensions/src/moonbase/webpackModules/ui/config/index.tsx
···
Clickable
} from "@moonlight-mod/wp/discord/components/common/index";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
-
const FormClasses = spacepack.findByCode("dividerDefault:")[0].exports;
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
-
-
let RemoveButtonClasses: any;
spacepack
.lazyLoad(
"renderArtisanalHack",
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"GuildSettings"/,
/webpackId:(\d+),name:"GuildSettings"/
)
-
.then(() => (RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0].exports));
-
-
// FIXME: type component keys
-
const { CircleXIcon } = Components;
function RemoveEntryButton({ onClick }: { onClick: () => void }) {
return (
-
<div className={RemoveButtonClasses.removeButtonContainer}>
<Tooltip text="Remove entry" position="top">
{(props: any) => (
-
<Clickable {...props} className={RemoveButtonClasses.removeButton} onClick={onClick}>
<CircleXIcon width={24} height={24} />
</Clickable>
)}
···
<FormText className={Margins.marginBottom4}>A list of remote repositories to display extensions from</FormText>
<ArrayFormItem config="repositories" />
</FormItem>
-
<FormDivider className={FormClasses.dividerDefault} />
<FormItem title="Extension search paths" className={Margins.marginTop20}>
<FormText className={Margins.marginBottom4}>
A list of local directories to search for built extensions
</FormText>
<ArrayFormItem config="devSearchPaths" />
</FormItem>
-
<FormDivider className={FormClasses.dividerDefault} />
<FormSwitch
className={Margins.marginTop20}
value={MoonbaseSettingsStore.getConfigOption("patchAll") ?? false}
···
Clickable
} from "@moonlight-mod/wp/discord/components/common/index";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
+
import { CircleXIcon } from "@moonlight-mod/wp/discord/components/common/index";
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
+
import FormSwitchClasses from "@moonlight-mod/wp/discord/components/common/FormSwitch.css";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
+
let GuildSettingsRoleEditClasses: any;
spacepack
.lazyLoad(
"renderArtisanalHack",
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"GuildSettings"/,
/webpackId:(\d+),name:"GuildSettings"/
)
+
.then(
+
() =>
+
(GuildSettingsRoleEditClasses = spacepack.require(
+
"discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"
+
))
+
);
function RemoveEntryButton({ onClick }: { onClick: () => void }) {
return (
+
<div className={GuildSettingsRoleEditClasses.removeButtonContainer}>
<Tooltip text="Remove entry" position="top">
{(props: any) => (
+
<Clickable {...props} className={GuildSettingsRoleEditClasses.removeButton} onClick={onClick}>
<CircleXIcon width={24} height={24} />
</Clickable>
)}
···
<FormText className={Margins.marginBottom4}>A list of remote repositories to display extensions from</FormText>
<ArrayFormItem config="repositories" />
</FormItem>
+
<FormDivider className={FormSwitchClasses.dividerDefault} />
<FormItem title="Extension search paths" className={Margins.marginTop20}>
<FormText className={Margins.marginBottom4}>
A list of local directories to search for built extensions
</FormText>
<ArrayFormItem config="devSearchPaths" />
</FormItem>
+
<FormDivider className={FormSwitchClasses.dividerDefault} />
<FormSwitch
className={Margins.marginTop20}
value={MoonbaseSettingsStore.getConfigOption("patchAll") ?? false}
+176 -86
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/card.tsx
···
import { ExtensionState } from "../../../types";
-
import { constants, ExtensionLoadSource } from "@moonlight-mod/types";
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
-
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
import React from "@moonlight-mod/wp/react";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
-
import IntegrationCard from "@moonlight-mod/wp/discord/modules/guild_settings/IntegrationCard.css";
-
import ExtensionInfo from "./info";
import Settings from "./settings";
-
import { doBuiltinExtensionPopup, doMissingExtensionPopup } from "./popup";
export enum ExtensionPage {
Info,
Description,
Settings
}
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
-
-
const { BeakerIcon, DownloadIcon, TrashIcon, AngleBracketsIcon, CircleWarningIcon, Tooltip } = Components;
-
-
const PanelButton = spacepack.findByCode("Masks.PANEL_BUTTON")[0].exports.Z;
-
const TabBarClasses = spacepack.findByExports("tabBar", "tabBarItem", "headerContentWrapper")[0].exports;
-
const MarkupClasses = spacepack.findByExports("markup", "inlineFormat")[0].exports;
-
-
const BuildOverrideClasses = spacepack.findByExports("disabledButtonOverride")[0].exports;
-
const COMPAT_TEXT_MAP: Record<ExtensionCompat, string> = {
[ExtensionCompat.Compatible]: "huh?",
[ExtensionCompat.InvalidApiLevel]: "Incompatible API level",
[ExtensionCompat.InvalidEnvironment]: "Incompatible platform"
};
-
export default function ExtensionCard({ uniqueId }: { uniqueId: number }) {
-
const [tab, setTab] = React.useState(ExtensionPage.Info);
-
const [restartNeeded, setRestartNeeded] = React.useState(false);
const { ext, enabled, busy, update, conflicting } = useStateFromStores([MoonbaseSettingsStore], () => {
return {
ext: MoonbaseSettingsStore.getExtension(uniqueId),
···
};
});
-
// Why it work like that :sob:
-
if (ext == null) return <></>;
-
-
const { Card, Text, FormSwitch, TabBar, Button } = Components;
const tagline = ext.manifest?.meta?.tagline;
-
const settings = ext.manifest?.settings;
const description = ext.manifest?.meta?.description;
const enabledDependants = useStateFromStores([MoonbaseSettingsStore], () =>
Object.keys(MoonbaseSettingsStore.extensions)
.filter((uniqueId) => {
const potentialDependant = MoonbaseSettingsStore.getExtension(parseInt(uniqueId));
return (
-
potentialDependant.manifest.dependencies?.includes(ext.id) &&
MoonbaseSettingsStore.getExtensionEnabled(parseInt(uniqueId))
);
})
···
);
const implicitlyEnabled = enabledDependants.length > 0;
-
return (
-
<Card editable={true} className={IntegrationCard.card}>
-
<div className={IntegrationCard.cardHeader}>
<Flex direction={Flex.Direction.VERTICAL}>
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER}>
<Text variant="text-md/semibold">{ext.manifest?.meta?.name ?? ext.id}</Text>
{ext.source.type === ExtensionLoadSource.Developer && (
<Tooltip text="This is a local extension" position="top">
-
{(props: any) => <BeakerIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
</Tooltip>
)}
</Flex>
···
gap: "1rem"
}}
>
-
{ext.manifest.meta?.source != null && (
-
<PanelButton
-
icon={AngleBracketsIcon}
-
tooltipText="View source"
-
onClick={() => {
-
window.open(ext.manifest.meta!.source);
-
}}
-
/>
-
)}
-
{ext.state === ExtensionState.NotDownloaded ? (
-
<Tooltip text={COMPAT_TEXT_MAP[ext.compat]} shouldShow={ext.compat !== ExtensionCompat.Compatible}>
{(props: any) => (
<Button
{...props}
···
const deps = await MoonbaseSettingsStore.getDependencies(uniqueId);
if (deps != null) {
await doMissingExtensionPopup(deps);
}
}}
>
···
tooltipText="Update"
onClick={() => {
MoonbaseSettingsStore.installExtension(uniqueId);
-
setRestartNeeded(true);
}}
-
/>
-
)}
-
-
{restartNeeded && (
-
<PanelButton
-
icon={() => <CircleWarningIcon color={Components.tokens.colors.STATUS_DANGER} />}
-
onClick={() => window.location.reload()}
-
tooltipText="You will need to reload/restart your client for this extension to work properly."
/>
)}
···
disabled={implicitlyEnabled || ext.compat !== ExtensionCompat.Compatible}
hideBorder={true}
style={{ marginBottom: "0px" }}
tooltipNote={
-
ext.compat !== ExtensionCompat.Compatible
-
? COMPAT_TEXT_MAP[ext.compat]
-
: implicitlyEnabled
-
? `This extension is a dependency of the following enabled extension${
-
enabledDependants.length > 1 ? "s" : ""
-
}: ${enabledDependants.map((a) => a.manifest.meta?.name ?? a.id).join(", ")}`
-
: undefined
}
onChange={() => {
const toggle = () => {
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, !enabled);
-
setRestartNeeded(true);
};
if (enabled && constants.builtinExtensions.includes(ext.id)) {
-
doBuiltinExtensionPopup(uniqueId, toggle);
} else {
toggle();
}
···
</div>
<div>
-
{(description != null || settings != null) && (
-
<TabBar
-
selectedItem={tab}
-
type="top"
-
onItemSelect={setTab}
-
className={TabBarClasses.tabBar}
-
style={{
-
padding: "0 20px"
-
}}
-
>
-
<TabBar.Item className={TabBarClasses.tabBarItem} id={ExtensionPage.Info}>
-
Info
-
</TabBar.Item>
-
{description != null && (
-
<TabBar.Item className={TabBarClasses.tabBarItem} id={ExtensionPage.Description}>
-
Description
-
</TabBar.Item>
-
)}
-
{settings != null && (
-
<TabBar.Item className={TabBarClasses.tabBarItem} id={ExtensionPage.Settings}>
-
Settings
-
</TabBar.Item>
-
)}
-
</TabBar>
)}
<Flex
justify={Flex.Justify.START}
wrap={Flex.Wrap.WRAP}
style={{
-
padding: "16px 16px"
}}
>
-
{tab === ExtensionPage.Info && <ExtensionInfo ext={ext} />}
{tab === ExtensionPage.Description && (
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ width: "100%" }}>
{MarkupUtils.parse(description ?? "*No description*", true, {
···
})}
</Text>
)}
-
{tab === ExtensionPage.Settings && <Settings ext={ext} />}
</Flex>
</div>
</Card>
···
import { ExtensionState } from "../../../types";
+
import { constants, ExtensionLoadSource, ExtensionTag } from "@moonlight-mod/types";
+
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
+
import {
+
ScienceIcon,
+
DownloadIcon,
+
TrashIcon,
+
AngleBracketsIcon,
+
Tooltip,
+
Card,
+
Text,
+
FormSwitch,
+
TabBar,
+
Button,
+
ChannelListIcon,
+
HeartIcon,
+
WindowTopOutlineIcon,
+
WarningIcon
+
} from "@moonlight-mod/wp/discord/components/common/index";
import React from "@moonlight-mod/wp/react";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
+
import AppCardClasses from "@moonlight-mod/wp/discord/modules/guild_settings/web/AppCard.css";
+
import PanelButton from "@moonlight-mod/wp/discord/components/common/PanelButton";
+
import DiscoveryClasses from "@moonlight-mod/wp/discord/modules/discovery/web/Discovery.css";
+
import MarkupClasses from "@moonlight-mod/wp/discord/modules/messages/web/Markup.css";
+
import BuildOverrideClasses from "@moonlight-mod/wp/discord/modules/build_overrides/web/BuildOverride.css";
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
+
import ErrorBoundary from "@moonlight-mod/wp/common_ErrorBoundary";
import ExtensionInfo from "./info";
import Settings from "./settings";
+
import { doGenericExtensionPopup, doMissingExtensionPopup } from "./popup";
export enum ExtensionPage {
Info,
Description,
+
Changelog,
Settings
}
const COMPAT_TEXT_MAP: Record<ExtensionCompat, string> = {
[ExtensionCompat.Compatible]: "huh?",
[ExtensionCompat.InvalidApiLevel]: "Incompatible API level",
[ExtensionCompat.InvalidEnvironment]: "Incompatible platform"
};
+
const CONFLICTING_TEXT = "This extension is already installed from another source.";
+
function PanelLinkButton({ icon, tooltip, link }: { icon: React.ReactNode; tooltip: string; link: string }) {
+
return (
+
<PanelButton
+
icon={icon}
+
tooltipText={tooltip}
+
onClick={() => {
+
window.open(link);
+
}}
+
/>
+
);
+
}
+
export default function ExtensionCard({ uniqueId, selectTag }: { uniqueId: number; selectTag: (tag: string) => void }) {
const { ext, enabled, busy, update, conflicting } = useStateFromStores([MoonbaseSettingsStore], () => {
return {
ext: MoonbaseSettingsStore.getExtension(uniqueId),
···
};
});
+
const [tab, setTab] = React.useState(
+
update != null && ext?.changelog != null ? ExtensionPage.Changelog : ExtensionPage.Info
+
);
const tagline = ext.manifest?.meta?.tagline;
+
const settings = ext.settingsOverride ?? ext.manifest?.settings;
const description = ext.manifest?.meta?.description;
+
const changelog = ext.changelog;
+
const linkButtons = [
+
ext?.manifest?.meta?.source && (
+
<PanelLinkButton icon={<AngleBracketsIcon />} tooltip="View source" link={ext.manifest.meta.source} />
+
),
+
ext?.source?.url && <PanelLinkButton icon={<ChannelListIcon />} tooltip="View repository" link={ext.source.url} />,
+
ext?.manifest?.meta?.donate && (
+
<PanelLinkButton icon={<HeartIcon />} tooltip="Donate" link={ext.manifest.meta.donate} />
+
)
+
].filter((x) => x != null);
+
const enabledDependants = useStateFromStores([MoonbaseSettingsStore], () =>
Object.keys(MoonbaseSettingsStore.extensions)
.filter((uniqueId) => {
const potentialDependant = MoonbaseSettingsStore.getExtension(parseInt(uniqueId));
return (
+
potentialDependant.manifest.dependencies?.includes(ext?.id) &&
MoonbaseSettingsStore.getExtensionEnabled(parseInt(uniqueId))
);
})
···
);
const implicitlyEnabled = enabledDependants.length > 0;
+
const hasDuplicateEntry = useStateFromStores([MoonbaseSettingsStore], () =>
+
Object.entries(MoonbaseSettingsStore.extensions).some(
+
([otherUniqueId, otherExt]) =>
+
otherExt != null && otherExt?.id === ext?.id && parseInt(otherUniqueId) !== uniqueId
+
)
+
);
+
+
return ext == null ? (
+
<></>
+
) : (
+
<Card editable={true} className={AppCardClasses.card}>
+
<div className={AppCardClasses.cardHeader}>
<Flex direction={Flex.Direction.VERTICAL}>
<Flex direction={Flex.Direction.HORIZONTAL} align={Flex.Align.CENTER}>
<Text variant="text-md/semibold">{ext.manifest?.meta?.name ?? ext.id}</Text>
{ext.source.type === ExtensionLoadSource.Developer && (
<Tooltip text="This is a local extension" position="top">
+
{(props: any) => <ScienceIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
+
</Tooltip>
+
)}
+
+
{hasDuplicateEntry && ext?.source?.url && (
+
<Tooltip text={`This extension is from the following repository: ${ext.source.url}`} position="top">
+
{(props: any) => <WindowTopOutlineIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
+
</Tooltip>
+
)}
+
+
{ext.manifest?.meta?.deprecated && (
+
<Tooltip text="This extension is deprecated" position="top">
+
{(props: any) => <WarningIcon {...props} class={BuildOverrideClasses.infoIcon} size="xs" />}
</Tooltip>
)}
</Flex>
···
gap: "1rem"
}}
>
{ext.state === ExtensionState.NotDownloaded ? (
+
<Tooltip
+
text={conflicting ? CONFLICTING_TEXT : COMPAT_TEXT_MAP[ext.compat]}
+
shouldShow={conflicting || ext.compat !== ExtensionCompat.Compatible}
+
>
{(props: any) => (
<Button
{...props}
···
const deps = await MoonbaseSettingsStore.getDependencies(uniqueId);
if (deps != null) {
await doMissingExtensionPopup(deps);
+
}
+
+
// Don't auto enable dangerous extensions
+
if (!ext.manifest?.meta?.tags?.includes(ExtensionTag.DangerZone)) {
+
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, true);
}
}}
>
···
tooltipText="Update"
onClick={() => {
MoonbaseSettingsStore.installExtension(uniqueId);
}}
/>
)}
···
disabled={implicitlyEnabled || ext.compat !== ExtensionCompat.Compatible}
hideBorder={true}
style={{ marginBottom: "0px" }}
+
// @ts-expect-error fix type later
tooltipNote={
+
ext.compat !== ExtensionCompat.Compatible ? (
+
COMPAT_TEXT_MAP[ext.compat]
+
) : implicitlyEnabled ? (
+
<div style={{ display: "flex", flexDirection: "column" }}>
+
<div>{`This extension is a dependency of the following enabled extension${
+
enabledDependants.length > 1 ? "s" : ""
+
}:`}</div>
+
{enabledDependants.map((dep) => (
+
<div>{"โ€ข " + (dep.manifest.meta?.name ?? dep.id)}</div>
+
))}
+
</div>
+
) : undefined
}
onChange={() => {
const toggle = () => {
MoonbaseSettingsStore.setExtensionEnabled(uniqueId, !enabled);
};
if (enabled && constants.builtinExtensions.includes(ext.id)) {
+
doGenericExtensionPopup(
+
"Built in extension",
+
"This extension is enabled by default. Disabling it might have consequences. Are you sure you want to disable it?",
+
uniqueId,
+
toggle
+
);
+
} else if (!enabled && ext.manifest?.meta?.tags?.includes(ExtensionTag.DangerZone)) {
+
doGenericExtensionPopup(
+
"Dangerous extension",
+
"This extension is marked as dangerous. Enabling it might have consequences. Are you sure you want to enable it?",
+
uniqueId,
+
toggle
+
);
} else {
toggle();
}
···
</div>
<div>
+
{(description != null || changelog != null || settings != null || linkButtons.length > 0) && (
+
<Flex>
+
<TabBar
+
selectedItem={tab}
+
type="top"
+
onItemSelect={setTab}
+
className={DiscoveryClasses.tabBar}
+
style={{
+
padding: "0 20px"
+
}}
+
>
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Info}>
+
Info
+
</TabBar.Item>
+
+
{description != null && (
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Description}>
+
Description
+
</TabBar.Item>
+
)}
+
+
{changelog != null && (
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Changelog}>
+
Changelog
+
</TabBar.Item>
+
)}
+
{settings != null && (
+
<TabBar.Item className={DiscoveryClasses.tabBarItem} id={ExtensionPage.Settings}>
+
Settings
+
</TabBar.Item>
+
)}
+
</TabBar>
+
<Flex
+
align={Flex.Align.CENTER}
+
justify={Flex.Justify.END}
+
direction={Flex.Direction.HORIZONTAL}
+
grow={1}
+
className="moonbase-link-buttons"
+
>
+
{linkButtons.length > 0 && linkButtons}
+
</Flex>
+
</Flex>
)}
<Flex
justify={Flex.Justify.START}
wrap={Flex.Wrap.WRAP}
style={{
+
padding: "16px 16px",
+
// This looks wonky in the settings tab
+
rowGap: tab === ExtensionPage.Info ? "16px" : undefined
}}
>
+
{tab === ExtensionPage.Info && <ExtensionInfo ext={ext} selectTag={selectTag} />}
{tab === ExtensionPage.Description && (
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ width: "100%" }}>
{MarkupUtils.parse(description ?? "*No description*", true, {
···
})}
</Text>
)}
+
{tab === ExtensionPage.Changelog && (
+
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ width: "100%" }}>
+
{MarkupUtils.parse(changelog ?? "*No changelog*", true, {
+
allowHeading: true,
+
allowLinks: true,
+
allowList: true
+
})}
+
</Text>
+
)}
+
{tab === ExtensionPage.Settings && (
+
<ErrorBoundary>
+
<Settings ext={ext} />
+
</ErrorBoundary>
+
)}
</Flex>
</div>
</Card>
+91 -45
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/filterBar.tsx
···
import { tagNames } from "./info";
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import * as React from "@moonlight-mod/wp/react";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
···
Popout,
Dialog,
Menu,
-
MenuGroup,
-
MenuCheckboxItem,
-
MenuItem
} from "@moonlight-mod/wp/discord/components/common/index";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
export enum Filter {
Core = 1 << 0,
···
Disabled = 1 << 4,
Installed = 1 << 5,
Repository = 1 << 6,
-
Incompatible = 1 << 7
}
export const defaultFilter = 127 as Filter;
-
const Margins = spacepack.findByCode("marginCenterHorz:")[0].exports;
-
const SortMenuClasses = spacepack.findByCode("container:", "clearText:")[0].exports;
-
-
let FilterDialogClasses: any;
-
let FilterBarClasses: any;
spacepack
.lazyLoad('"Missing channel in Channel.openChannelContextMenu"', /e\("(\d+)"\)/g, /webpackId:(\d+?),/)
.then(() => {
-
FilterBarClasses = spacepack.findByCode("tagsButtonWithCount:")[0].exports;
-
FilterDialogClasses = spacepack.findByCode("countContainer:", "tagContainer:")[0].exports;
});
-
const TagItem = spacepack.findByCode('"forum-tag-"')[0].exports.Z;
-
-
// FIXME: type component keys
-
const { ChevronSmallDownIcon, ChevronSmallUpIcon, ArrowsUpDownIcon } = Components;
-
function toggleTag(selectedTags: Set<string>, setSelectedTags: (tags: Set<string>) => void, tag: string) {
const newState = new Set(selectedTags);
if (newState.has(tag)) newState.delete(tag);
···
checked={(filter & Filter.Incompatible) === Filter.Incompatible}
action={() => toggleFilter(Filter.Incompatible)}
/>
<MenuItem
id="reset-all"
className={SortMenuClasses.clearText}
···
function TagButtonPopout({ selectedTags, setSelectedTags, setPopoutRef, closePopout }: any) {
return (
-
<Dialog ref={setPopoutRef} className={FilterDialogClasses.container}>
-
<div className={FilterDialogClasses.header}>
-
<div className={FilterDialogClasses.headerLeft}>
-
<Heading color="interactive-normal" variant="text-xs/bold" className={FilterDialogClasses.headerText}>
Select tags
</Heading>
-
<div className={FilterDialogClasses.countContainer}>
-
<Text className={FilterDialogClasses.countText} color="none" variant="text-xs/medium">
{selectedTags.size}
</Text>
</div>
</div>
</div>
-
<div className={FilterDialogClasses.tagContainer}>
{Object.keys(tagNames).map((tag) => (
<TagItem
key={tag}
-
className={FilterDialogClasses.tag}
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
selected={selectedTags.has(tag)}
/>
))}
</div>
-
<div className={FilterDialogClasses.separator} />
<Button
look={Button.Looks.LINK}
size={Button.Sizes.MIN}
color={Button.Colors.CUSTOM}
-
className={FilterDialogClasses.clear}
onClick={() => {
setSelectedTags(new Set());
closePopout();
···
const tagsContainer = React.useRef<HTMLDivElement>(null);
const tagListInner = React.useRef<HTMLDivElement>(null);
const [tagsButtonOffset, setTagsButtonOffset] = React.useState(0);
React.useLayoutEffect(() => {
if (tagsContainer.current === null || tagListInner.current === null) return;
const { left: containerX, top: containerY } = tagsContainer.current.getBoundingClientRect();
···
}
}
setTagsButtonOffset(offset);
-
}, [windowSize]);
return (
<div
···
style={{
paddingTop: "12px"
}}
-
className={`${FilterBarClasses.tagsContainer} ${Margins.marginBottom8}`}
>
<Popout
renderPopout={({ closePopout }: any) => (
<FilterButtonPopout filter={filter} setFilter={setFilter} closePopout={closePopout} />
···
{...props}
size={Button.Sizes.MIN}
color={Button.Colors.CUSTOM}
-
className={FilterBarClasses.sortDropdown}
-
innerClassName={FilterBarClasses.sortDropdownInner}
>
<ArrowsUpDownIcon size="xs" />
-
<Text className={FilterBarClasses.sortDropdownText} variant="text-sm/medium" color="interactive-normal">
Sort & filter
</Text>
{isShown ? (
···
</Button>
)}
</Popout>
-
<div className={FilterBarClasses.divider} />
-
<div className={FilterBarClasses.tagList}>
-
<div ref={tagListInner} className={FilterBarClasses.tagListInner}>
{Object.keys(tagNames).map((tag) => (
<TagItem
key={tag}
-
className={FilterBarClasses.tag}
-
tag={{ name: tagNames[tag as keyof typeof tagNames] }}
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
selected={selectedTags.has(tag)}
/>
···
left: tagsButtonOffset
}}
// TODO: Use Discord's class name utility
-
className={`${FilterBarClasses.tagsButton} ${
-
selectedTags.size > 0 ? FilterBarClasses.tagsButtonWithCount : ""
-
}`}
-
innerClassName={FilterBarClasses.tagsButtonInner}
>
{selectedTags.size > 0 ? (
-
<div style={{ boxSizing: "content-box" }} className={FilterBarClasses.countContainer}>
-
<Text className={FilterBarClasses.countText} color="none" variant="text-xs/medium">
{selectedTags.size}
</Text>
</div>
···
</Button>
)}
</Popout>
</div>
);
}
···
import { tagNames } from "./info";
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import * as React from "@moonlight-mod/wp/react";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
···
Popout,
Dialog,
Menu,
+
ChevronSmallDownIcon,
+
ChevronSmallUpIcon,
+
ArrowsUpDownIcon,
+
RetryIcon,
+
Tooltip
} from "@moonlight-mod/wp/discord/components/common/index";
+
import { MenuGroup, MenuCheckboxItem, MenuItem } from "@moonlight-mod/wp/contextMenu_contextMenu";
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
+
import TagItem from "@moonlight-mod/wp/discord/modules/forums/web/Tag";
export enum Filter {
Core = 1 << 0,
···
Disabled = 1 << 4,
Installed = 1 << 5,
Repository = 1 << 6,
+
Incompatible = 1 << 7,
+
Deprecated = 1 << 8
}
export const defaultFilter = 127 as Filter;
+
let HeaderClasses: any;
+
let ForumsClasses: any;
+
let SortMenuClasses: any;
spacepack
.lazyLoad('"Missing channel in Channel.openChannelContextMenu"', /e\("(\d+)"\)/g, /webpackId:(\d+?),/)
.then(() => {
+
ForumsClasses = spacepack.require("discord/modules/forums/web/Forums.css");
+
HeaderClasses = spacepack.require("discord/modules/forums/web/Header.css");
+
SortMenuClasses = spacepack.require("discord/modules/forums/web/SortMenu.css");
});
function toggleTag(selectedTags: Set<string>, setSelectedTags: (tags: Set<string>) => void, tag: string) {
const newState = new Set(selectedTags);
if (newState.has(tag)) newState.delete(tag);
···
checked={(filter & Filter.Incompatible) === Filter.Incompatible}
action={() => toggleFilter(Filter.Incompatible)}
/>
+
<MenuCheckboxItem
+
id="l-deprecated"
+
label="Show deprecated"
+
checked={(filter & Filter.Deprecated) === Filter.Deprecated}
+
action={() => toggleFilter(Filter.Deprecated)}
+
/>
<MenuItem
id="reset-all"
className={SortMenuClasses.clearText}
···
function TagButtonPopout({ selectedTags, setSelectedTags, setPopoutRef, closePopout }: any) {
return (
+
<Dialog ref={setPopoutRef} className={HeaderClasses.container}>
+
<div className={HeaderClasses.header}>
+
<div className={HeaderClasses.headerLeft}>
+
<Heading color="interactive-normal" variant="text-xs/bold" className={HeaderClasses.headerText}>
Select tags
</Heading>
+
<div className={HeaderClasses.countContainer}>
+
<Text className={HeaderClasses.countText} color="none" variant="text-xs/medium">
{selectedTags.size}
</Text>
</div>
</div>
</div>
+
<div className={HeaderClasses.tagContainer}>
{Object.keys(tagNames).map((tag) => (
<TagItem
key={tag}
+
className={HeaderClasses.tag}
+
tag={{ name: tagNames[tag as keyof typeof tagNames], id: tagNames[tag as keyof typeof tagNames] }}
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
selected={selectedTags.has(tag)}
/>
))}
</div>
+
<div className={HeaderClasses.separator} />
<Button
look={Button.Looks.LINK}
size={Button.Sizes.MIN}
color={Button.Colors.CUSTOM}
+
className={HeaderClasses.clear}
onClick={() => {
setSelectedTags(new Set());
closePopout();
···
const tagsContainer = React.useRef<HTMLDivElement>(null);
const tagListInner = React.useRef<HTMLDivElement>(null);
const [tagsButtonOffset, setTagsButtonOffset] = React.useState(0);
+
const [checkingUpdates, setCheckingUpdates] = React.useState(false);
+
React.useLayoutEffect(() => {
if (tagsContainer.current === null || tagListInner.current === null) return;
const { left: containerX, top: containerY } = tagsContainer.current.getBoundingClientRect();
···
}
}
setTagsButtonOffset(offset);
+
}, [windowSize, tagsContainer.current, tagListInner.current, tagListInner.current?.getBoundingClientRect()?.width]);
return (
<div
···
style={{
paddingTop: "12px"
}}
+
className={`${ForumsClasses.tagsContainer} ${Margins.marginBottom8}`}
>
+
<Tooltip text="Refresh updates" position="top">
+
{(props: any) => (
+
<Button
+
{...props}
+
size={Button.Sizes.MIN}
+
color={Button.Colors.CUSTOM}
+
className={`${ForumsClasses.sortDropdown} moonbase-retry-button`}
+
innerClassName={ForumsClasses.sortDropdownInner}
+
onClick={() => {
+
(async () => {
+
try {
+
setCheckingUpdates(true);
+
await MoonbaseSettingsStore.checkUpdates();
+
} finally {
+
// artificial delay because the spin is fun
+
await new Promise((r) => setTimeout(r, 500));
+
setCheckingUpdates(false);
+
}
+
})();
+
}}
+
>
+
<RetryIcon size={"custom"} width={16} className={checkingUpdates ? "moonbase-speen" : ""} />
+
</Button>
+
)}
+
</Tooltip>
<Popout
renderPopout={({ closePopout }: any) => (
<FilterButtonPopout filter={filter} setFilter={setFilter} closePopout={closePopout} />
···
{...props}
size={Button.Sizes.MIN}
color={Button.Colors.CUSTOM}
+
className={ForumsClasses.sortDropdown}
+
innerClassName={ForumsClasses.sortDropdownInner}
>
<ArrowsUpDownIcon size="xs" />
+
<Text className={ForumsClasses.sortDropdownText} variant="text-sm/medium" color="interactive-normal">
Sort & filter
</Text>
{isShown ? (
···
</Button>
)}
</Popout>
+
<div className={ForumsClasses.divider} />
+
<div className={ForumsClasses.tagList}>
+
<div ref={tagListInner} className={ForumsClasses.tagListInner}>
{Object.keys(tagNames).map((tag) => (
<TagItem
key={tag}
+
className={ForumsClasses.tag}
+
tag={{ name: tagNames[tag as keyof typeof tagNames], id: tag }}
onClick={() => toggleTag(selectedTags, setSelectedTags, tag)}
selected={selectedTags.has(tag)}
/>
···
left: tagsButtonOffset
}}
// TODO: Use Discord's class name utility
+
className={`${ForumsClasses.tagsButton} ${selectedTags.size > 0 ? ForumsClasses.tagsButtonWithCount : ""}`}
+
innerClassName={ForumsClasses.tagsButtonInner}
>
{selectedTags.size > 0 ? (
+
<div style={{ boxSizing: "content-box" }} className={ForumsClasses.countContainer}>
+
<Text className={ForumsClasses.countText} color="none" variant="text-xs/medium">
{selectedTags.size}
</Text>
</div>
···
</Button>
)}
</Popout>
+
<Button
+
size={Button.Sizes.MIN}
+
color={Button.Colors.CUSTOM}
+
className={`${ForumsClasses.tagsButton} ${ForumsClasses.tagsButtonPlaceholder}`}
+
innerClassName={ForumsClasses.tagsButtonInner}
+
>
+
{selectedTags.size > 0 ? (
+
<div style={{ boxSizing: "content-box" }} className={ForumsClasses.countContainer}>
+
<Text className={ForumsClasses.countText} color="none" variant="text-xs/medium">
+
{selectedTags.size}
+
</Text>
+
</div>
+
) : null}
+
+
<ChevronSmallUpIcon size={"custom"} width={20} />
+
</Button>
</div>
);
}
+74 -9
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/index.tsx
···
import React from "@moonlight-mod/wp/react";
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
-
const SearchBar: any = Object.values(spacepack.findByCode("hideSearchIcon")[0].exports)[0];
export default function ExtensionsPage() {
const { extensions, savedFilter } = useStateFromStoresObject([MoonbaseSettingsStore], () => {
···
});
const [query, setQuery] = React.useState("");
const filterState = React.useState(defaultFilter);
···
filter = filterState[0];
setFilter = filterState[1];
}
const [selectedTags, setSelectedTags] = React.useState(new Set<string>());
const sorted = Object.values(extensions).sort((a, b) => {
const aName = a.manifest.meta?.name ?? a.id;
const bName = b.manifest.meta?.name ?? b.id;
···
Object.entries(ext.manifest.settings).some(([key, setting]) =>
(setting.displayName ?? key).toLowerCase().includes(query)
)) ||
ext.manifest.meta?.description?.toLowerCase().includes(query)) &&
[...selectedTags.values()].every((tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag)) &&
// This seems very bad, sorry
···
) &&
(filter & Filter.Incompatible ||
ext.compat === ExtensionCompat.Compatible ||
-
(ext.compat === ExtensionCompat.InvalidApiLevel && ext.hasUpdate))
);
// Prioritize extensions with updates
-
filtered.sort((a, b) => {
-
if (a.hasUpdate && !b.hasUpdate) return -1;
-
if (!a.hasUpdate && b.hasUpdate) return 1;
-
return 0;
-
});
return (
<>
···
}}
/>
<FilterBar filter={filter} setFilter={setFilter} selectedTags={selectedTags} setSelectedTags={setSelectedTags} />
-
{filtered.map((ext) => (
-
<ExtensionCard uniqueId={ext.uniqueId} key={ext.uniqueId} />
))}
</>
);
···
import React from "@moonlight-mod/wp/react";
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
+
import {
+
FormDivider,
+
CircleInformationIcon,
+
XSmallIcon,
+
Button
+
} from "@moonlight-mod/wp/discord/components/common/index";
+
import PanelButton from "@moonlight-mod/wp/discord/components/common/PanelButton";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
+
import ErrorBoundary from "@moonlight-mod/wp/common_ErrorBoundary";
import { ExtensionCompat } from "@moonlight-mod/core/extension/loader";
+
import HelpMessage from "../HelpMessage";
+
+
const SearchBar = spacepack.require("discord/uikit/search/SearchBar").default;
+
const validTags: string[] = Object.values(ExtensionTag);
export default function ExtensionsPage() {
const { extensions, savedFilter } = useStateFromStoresObject([MoonbaseSettingsStore], () => {
···
});
const [query, setQuery] = React.useState("");
+
const [hitUpdateAll, setHitUpdateAll] = React.useState(false);
const filterState = React.useState(defaultFilter);
···
filter = filterState[0];
setFilter = filterState[1];
}
+
const [selectedTags, setSelectedTags] = React.useState(new Set<string>());
+
const selectTag = React.useCallback(
+
(tag: string) => {
+
const newState = new Set(selectedTags);
+
if (validTags.includes(tag)) newState.add(tag);
+
setSelectedTags(newState);
+
},
+
[selectedTags]
+
);
+
const sorted = Object.values(extensions).sort((a, b) => {
const aName = a.manifest.meta?.name ?? a.id;
const bName = b.manifest.meta?.name ?? b.id;
···
Object.entries(ext.manifest.settings).some(([key, setting]) =>
(setting.displayName ?? key).toLowerCase().includes(query)
)) ||
+
(ext.manifest?.meta?.authors != null &&
+
ext.manifest.meta.authors.some((author) =>
+
(typeof author === "string" ? author : author.name).toLowerCase().includes(query)
+
)) ||
ext.manifest.meta?.description?.toLowerCase().includes(query)) &&
[...selectedTags.values()].every((tag) => ext.manifest.meta?.tags?.includes(tag as ExtensionTag)) &&
// This seems very bad, sorry
···
) &&
(filter & Filter.Incompatible ||
ext.compat === ExtensionCompat.Compatible ||
+
(ext.compat === ExtensionCompat.InvalidApiLevel && ext.hasUpdate)) &&
+
(filter & Filter.Deprecated ||
+
ext.manifest?.meta?.deprecated !== true ||
+
ext.state !== ExtensionState.NotDownloaded)
);
// Prioritize extensions with updates
+
const filteredWithUpdates = filtered.filter((ext) => ext!.hasUpdate);
+
const filteredWithoutUpdates = filtered.filter((ext) => !ext!.hasUpdate);
return (
<>
···
}}
/>
<FilterBar filter={filter} setFilter={setFilter} selectedTags={selectedTags} setSelectedTags={setSelectedTags} />
+
+
{filteredWithUpdates.length > 0 && (
+
<HelpMessage
+
icon={CircleInformationIcon}
+
text="Extension updates are available"
+
className="moonbase-extension-update-section"
+
>
+
<div className="moonbase-help-message-buttons">
+
<Button
+
color={Button.Colors.BRAND}
+
size={Button.Sizes.TINY}
+
disabled={hitUpdateAll}
+
onClick={() => {
+
setHitUpdateAll(true);
+
MoonbaseSettingsStore.updateAllExtensions();
+
}}
+
>
+
Update all
+
</Button>
+
<PanelButton
+
icon={XSmallIcon}
+
onClick={() => {
+
MoonbaseSettingsStore.dismissAllExtensionUpdates();
+
}}
+
/>
+
</div>
+
</HelpMessage>
+
)}
+
+
{filteredWithUpdates.map((ext) => (
+
<ErrorBoundary>
+
<ExtensionCard uniqueId={ext.uniqueId} key={ext.uniqueId} selectTag={selectTag} />
+
</ErrorBoundary>
+
))}
+
{filteredWithUpdates.length > 0 && filteredWithoutUpdates.length > 0 && (
+
<FormDivider className="moonbase-update-divider" />
+
)}
+
{filteredWithoutUpdates.map((ext) => (
+
<ErrorBoundary>
+
<ExtensionCard uniqueId={ext.uniqueId} key={ext.uniqueId} selectTag={selectTag} />
+
</ErrorBoundary>
))}
</>
);
+55 -26
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/info.tsx
···
import { MoonbaseExtension } from "../../../types";
import React from "@moonlight-mod/wp/react";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
type Dependency = {
id: string;
···
[ExtensionTag.Library]: "Library"
};
-
const UserInfoClasses = spacepack.findByCode("infoScroller", "userInfoSection", "userInfoSectionHeader")[0].exports;
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
-
-
// FIXME: type component keys
-
const { Text } = Components;
function InfoSection({ title, children }: { title: string; children: React.ReactNode }) {
return (
···
marginRight: "1em"
}}
>
-
<Text variant="eyebrow" className={UserInfoClasses.userInfoSectionHeader}>
{title}
</Text>
···
);
}
-
function Badge({ color, children }: { color: string; children: React.ReactNode }) {
return (
<span
-
style={{
-
borderRadius: ".1875rem",
-
padding: "0 0.275rem",
-
marginRight: "0.4em",
-
backgroundColor: color,
-
color: "#fff"
-
}}
>
{children}
</span>
);
}
-
export default function ExtensionInfo({ ext }: { ext: MoonbaseExtension }) {
const authors = ext.manifest?.meta?.authors;
const tags = ext.manifest?.meta?.tags;
const version = ext.manifest?.version;
const dependencies: Dependency[] = [];
if (ext.manifest.dependencies != null) {
dependencies.push(
...ext.manifest.dependencies.map((dep) => ({
···
}
if (ext.manifest.incompatible != null) {
-
dependencies.push(
...ext.manifest.incompatible.map((dep) => ({
id: dep,
type: DependencyType.Incompatible
···
<InfoSection title="Tags">
{tags.map((tag, i) => {
const name = tagNames[tag];
return (
-
<Badge key={i} color={tag === ExtensionTag.DangerZone ? "var(--red-400)" : "var(--brand-500)"}>
{name}
</Badge>
);
···
{dependencies.length > 0 && (
<InfoSection title="Dependencies">
{dependencies.map((dep) => {
-
const colors = {
-
[DependencyType.Dependency]: "var(--brand-500)",
-
[DependencyType.Optional]: "var(--orange-400)",
-
[DependencyType.Incompatible]: "var(--red-400)"
-
};
-
const color = colors[dep.type];
const name = MoonbaseSettingsStore.tryGetExtensionName(dep.id);
return (
-
<Badge color={color} key={dep.id}>
{name}
</Badge>
);
···
import { MoonbaseExtension } from "../../../types";
import React from "@moonlight-mod/wp/react";
+
import { Text } from "@moonlight-mod/wp/discord/components/common/index";
type Dependency = {
id: string;
···
[ExtensionTag.Library]: "Library"
};
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
function InfoSection({ title, children }: { title: string; children: React.ReactNode }) {
return (
···
marginRight: "1em"
}}
>
+
<Text variant="eyebrow" className="moonlight-card-info-header">
{title}
</Text>
···
);
}
+
function Badge({
+
color,
+
children,
+
style = {},
+
onClick
+
}: {
+
color: string;
+
children: React.ReactNode;
+
style?: React.CSSProperties;
+
onClick?: () => void;
+
}) {
+
if (onClick) style.cursor ??= "pointer";
return (
<span
+
className="moonlight-card-badge"
+
style={
+
{
+
"--badge-color": color,
+
...style
+
} as React.CSSProperties
+
}
+
onClick={onClick}
>
{children}
</span>
);
}
+
export default function ExtensionInfo({
+
ext,
+
selectTag
+
}: {
+
ext: MoonbaseExtension;
+
selectTag: (tag: string) => void;
+
}) {
const authors = ext.manifest?.meta?.authors;
const tags = ext.manifest?.meta?.tags;
const version = ext.manifest?.version;
const dependencies: Dependency[] = [];
+
const incompatible: Dependency[] = [];
+
if (ext.manifest.dependencies != null) {
dependencies.push(
...ext.manifest.dependencies.map((dep) => ({
···
}
if (ext.manifest.incompatible != null) {
+
incompatible.push(
...ext.manifest.incompatible.map((dep) => ({
id: dep,
type: DependencyType.Incompatible
···
<InfoSection title="Tags">
{tags.map((tag, i) => {
const name = tagNames[tag];
+
let color = "var(--bg-mod-strong)";
+
let style;
+
if (tag === ExtensionTag.DangerZone) {
+
color = "var(--red-460)";
+
style = { color: "var(--primary-230)" };
+
}
return (
+
<Badge key={i} color={color} style={style} onClick={() => selectTag(tag)}>
{name}
</Badge>
);
···
{dependencies.length > 0 && (
<InfoSection title="Dependencies">
{dependencies.map((dep) => {
const name = MoonbaseSettingsStore.tryGetExtensionName(dep.id);
+
// TODO: figure out a decent way to distinguish suggested
return (
+
<Badge color="var(--bg-mod-strong)" key={dep.id}>
+
{name}
+
</Badge>
+
);
+
})}
+
</InfoSection>
+
)}
+
+
{incompatible.length > 0 && (
+
<InfoSection title="Incompatible">
+
{incompatible.map((dep) => {
+
const name = MoonbaseSettingsStore.tryGetExtensionName(dep.id);
+
+
return (
+
<Badge color="var(--bg-mod-strong)" key={dep.id}>
{name}
</Badge>
);
+33 -21
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/popup.tsx
···
// TODO: clean up the styling here
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import React from "@moonlight-mod/wp/react";
import { MoonbaseExtension } from "core-extensions/src/moonbase/types";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
import { ExtensionLoadSource } from "@moonlight-mod/types";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
-
const { openModalLazy, useModalsStore, closeModal } = Components;
-
const Popup = spacepack.findByCode(".minorContainer", "secondaryAction")[0].exports.default;
function close() {
const ModalStore = useModalsStore.getState();
closeModal(ModalStore.default[0].key);
}
const presentableLoadSources: Record<ExtensionLoadSource, string> = {
[ExtensionLoadSource.Developer]: "Local extension", // should never show up
[ExtensionLoadSource.Core]: "Core extension",
···
option: string | undefined;
setOption: (pick: string | undefined) => void;
}) {
-
const { SingleSelect } = Components;
-
return (
<SingleSelect
key={id}
···
onChange={(value: string) => {
setOption(value);
}}
-
// @ts-expect-error no thanks
placeholder="Missing extension"
/>
);
···
deps: Record<string, MoonbaseExtension[]>;
transitionState: number | null;
}) {
-
const { Text } = Components;
-
const amountNotAvailable = Object.values(deps).filter((candidates) => candidates.length === 0).length;
const [options, setOptions] = React.useState<Record<string, string | undefined>>(
···
);
return (
-
<Popup
body={
<Flex
style={{
···
});
}
-
function BuiltinExtensionPopup({
transitionState,
uniqueId,
cb
}: {
transitionState: number | null;
uniqueId: number;
cb: () => void;
}) {
-
const { Text } = Components;
return (
-
<Popup
body={
<Flex>
-
<Text variant="text-md/normal">
-
This extension is enabled by default. Disabling it might have consequences. Are you sure you want to disable
-
it?
-
</Text>
</Flex>
}
cancelText="No"
-
confirmText="Yes"
onCancel={close}
onConfirm={() => {
close();
cb();
}}
-
title="Built in extension"
transitionState={transitionState}
/>
);
}
-
export async function doBuiltinExtensionPopup(uniqueId: number, cb: () => void) {
await openModalLazy(async () => {
return ({ transitionState }: { transitionState: number | null }) => {
-
return <BuiltinExtensionPopup transitionState={transitionState} uniqueId={uniqueId} cb={cb} />;
};
});
}
···
// TODO: clean up the styling here
import React from "@moonlight-mod/wp/react";
import { MoonbaseExtension } from "core-extensions/src/moonbase/types";
+
import { openModalLazy, useModalsStore, closeModal } from "@moonlight-mod/wp/discord/modules/modals/Modals";
+
import { SingleSelect, Text } from "@moonlight-mod/wp/discord/components/common/index";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
import { ExtensionLoadSource } from "@moonlight-mod/types";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
+
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
+
let ConfirmModal: typeof import("@moonlight-mod/wp/discord/components/modals/ConfirmModal").default;
function close() {
const ModalStore = useModalsStore.getState();
closeModal(ModalStore.default[0].key);
}
+
// do this to avoid a hard dependency
+
function lazyLoad() {
+
if (!ConfirmModal) {
+
ConfirmModal = spacepack.require("discord/components/modals/ConfirmModal").default;
+
}
+
}
+
const presentableLoadSources: Record<ExtensionLoadSource, string> = {
[ExtensionLoadSource.Developer]: "Local extension", // should never show up
[ExtensionLoadSource.Core]: "Core extension",
···
option: string | undefined;
setOption: (pick: string | undefined) => void;
}) {
return (
<SingleSelect
key={id}
···
onChange={(value: string) => {
setOption(value);
}}
placeholder="Missing extension"
/>
);
···
deps: Record<string, MoonbaseExtension[]>;
transitionState: number | null;
}) {
+
lazyLoad();
const amountNotAvailable = Object.values(deps).filter((candidates) => candidates.length === 0).length;
const [options, setOptions] = React.useState<Record<string, string | undefined>>(
···
);
return (
+
<ConfirmModal
body={
<Flex
style={{
···
});
}
+
function GenericExtensionPopup({
+
title,
+
content,
transitionState,
uniqueId,
cb
}: {
+
title: string;
+
content: string;
transitionState: number | null;
uniqueId: number;
cb: () => void;
}) {
+
lazyLoad();
return (
+
<ConfirmModal
+
title={title}
body={
<Flex>
+
<Text variant="text-md/normal">{content}</Text>
</Flex>
}
+
confirmText="Yes"
cancelText="No"
onCancel={close}
onConfirm={() => {
close();
cb();
}}
transitionState={transitionState}
/>
);
}
+
export async function doGenericExtensionPopup(title: string, content: string, uniqueId: number, cb: () => void) {
await openModalLazy(async () => {
return ({ transitionState }: { transitionState: number | null }) => {
+
return (
+
<GenericExtensionPopup
+
title={title}
+
content={content}
+
transitionState={transitionState}
+
uniqueId={uniqueId}
+
cb={cb}
+
/>
+
);
};
});
}
+85 -51
packages/core-extensions/src/moonbase/webpackModules/ui/extensions/settings.tsx
···
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import React from "@moonlight-mod/wp/react";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
type SettingsProps = {
ext: MoonbaseExtension;
···
setting: ExtensionSettingsManifest;
disabled: boolean;
};
-
type SettingsComponent = React.ComponentType<SettingsProps>;
-
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
const Margins = spacepack.require("discord/styles/shared/Margins.css");
function useConfigEntry<T>(uniqueId: number, name: string) {
return useStateFromStores(
[MoonbaseSettingsStore],
···
}
function Boolean({ ext, name, setting, disabled }: SettingsProps) {
-
const { FormSwitch } = Components;
const { value, displayName, description } = useConfigEntry<boolean>(ext.uniqueId, name);
return (
···
onChange={(value: boolean) => {
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
}}
-
note={description}
className={`${Margins.marginReset} ${Margins.marginTop20}`}
>
{displayName}
···
}
function Number({ ext, name, setting, disabled }: SettingsProps) {
-
const { FormItem, FormText, Slider } = Components;
const { value, displayName, description } = useConfigEntry<number>(ext.uniqueId, name);
const castedSetting = setting as NumberSettingType;
-
const min = castedSetting.min ?? 0;
-
const max = castedSetting.max ?? 100;
return (
<FormItem className={Margins.marginTop20} title={displayName}>
-
{description && <FormText>{description}</FormText>}
-
<Slider
-
initialValue={value ?? 0}
-
disabled={disabled}
-
minValue={castedSetting.min ?? 0}
-
maxValue={castedSetting.max ?? 100}
-
onValueChange={(value: number) => {
-
const rounded = Math.max(min, Math.min(max, Math.round(value)));
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, rounded);
-
}}
-
/>
</FormItem>
);
}
function String({ ext, name, setting, disabled }: SettingsProps) {
-
const { FormItem, FormText, TextInput } = Components;
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
return (
<FormItem className={Margins.marginTop20} title={displayName}>
-
{description && <FormText className={Margins.marginBottom8}>{description}</FormText>}
<TextInput
value={value ?? ""}
-
onChange={(value: string) => {
-
if (disabled) return;
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
-
}}
/>
</FormItem>
);
}
function MultilineString({ ext, name, setting, disabled }: SettingsProps) {
-
const { FormItem, FormText, TextArea } = Components;
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
return (
<FormItem className={Margins.marginTop20} title={displayName}>
-
{description && <FormText className={Margins.marginBottom8}>{description}</FormText>}
<TextArea
rows={5}
value={value ?? ""}
className={"moonbase-resizeable"}
-
onChange={(value: string) => {
-
if (disabled) return;
-
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
-
}}
/>
</FormItem>
);
}
function Select({ ext, name, setting, disabled }: SettingsProps) {
-
const { FormItem, FormText, SingleSelect } = Components;
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
const castedSetting = setting as SelectSettingType;
···
return (
<FormItem className={Margins.marginTop20} title={displayName}>
-
{description && <FormText className={Margins.marginBottom8}>{description}</FormText>}
<SingleSelect
autofocus={false}
clearable={false}
···
}
function MultiSelect({ ext, name, setting, disabled }: SettingsProps) {
-
const { FormItem, FormText, Select, useVariableSelect, multiSelect } = Components;
const { value, displayName, description } = useConfigEntry<string | string[]>(ext.uniqueId, name);
const castedSetting = setting as MultiSelectSettingType;
···
return (
<FormItem className={Margins.marginTop20} title={displayName}>
-
{description && <FormText className={Margins.marginBottom8}>{description}</FormText>}
-
<Select
autofocus={false}
clearable={false}
closeOnSelect={false}
···
);
}
-
const RemoveButtonClasses = spacepack.findByCode("removeButtonContainer")[0].exports;
-
-
// FIXME: type component keys
-
const { CircleXIcon } = Components;
-
function RemoveEntryButton({ onClick, disabled }: { onClick: () => void; disabled: boolean }) {
-
const { Tooltip, Clickable } = Components;
return (
-
<div className={RemoveButtonClasses.removeButtonContainer}>
<Tooltip text="Remove entry" position="top">
{(props: any) => (
-
<Clickable {...props} className={RemoveButtonClasses.removeButton} onClick={onClick}>
<CircleXIcon width={16} height={16} />
</Clickable>
)}
···
}
function List({ ext, name, setting, disabled }: SettingsProps) {
-
const { FormItem, FormText, TextInput, Button } = Components;
const { value, displayName, description } = useConfigEntry<string[]>(ext.uniqueId, name);
const entries = value ?? [];
···
return (
<FormItem className={Margins.marginTop20} title={displayName}>
-
{description && <FormText className={Margins.marginBottom4}>{description}</FormText>}
<Flex direction={Flex.Direction.VERTICAL}>
{entries.map((val, i) => (
// FIXME: stylesheets
···
}
function Dictionary({ ext, name, setting, disabled }: SettingsProps) {
-
const { FormItem, FormText, TextInput, Button } = Components;
const { value, displayName, description } = useConfigEntry<Record<string, string>>(ext.uniqueId, name);
const entries = Object.entries(value ?? {});
···
return (
<FormItem className={Margins.marginTop20} title={displayName}>
-
{description && <FormText className={Margins.marginBottom4}>{description}</FormText>}
<Flex direction={Flex.Direction.VERTICAL}>
{entries.map(([key, val], i) => (
// FIXME: stylesheets
···
);
if (Component == null) {
-
const { Text } = Components;
return (
<Text variant="text-md/normal">{`Custom setting "${displayName}" is missing a component. Perhaps the extension is not installed?`}</Text>
);
}
return (
-
<Component value={value} setValue={(value) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)} />
);
}
···
export default function Settings({ ext }: { ext: MoonbaseExtension }) {
return (
<Flex className="moonbase-settings" direction={Flex.Direction.VERTICAL}>
-
{Object.entries(ext.manifest.settings!).map(([name, setting]) => (
<Setting
ext={ext}
key={name}
···
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import React from "@moonlight-mod/wp/react";
+
import {
+
FormSwitch,
+
FormItem,
+
FormText,
+
TextInput,
+
Slider,
+
TextArea,
+
Tooltip,
+
Clickable,
+
CircleXIcon,
+
Text,
+
SingleSelect,
+
Button,
+
useVariableSelect,
+
multiSelect,
+
Select as DiscordSelect,
+
NumberInputStepper
+
} from "@moonlight-mod/wp/discord/components/common/index";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
+
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
+
import ErrorBoundary from "@moonlight-mod/wp/common_ErrorBoundary";
+
+
let GuildSettingsRoleEditClasses: any;
+
spacepack
+
.lazyLoad(
+
"renderArtisanalHack",
+
/\[(?:.\.e\("\d+?"\),?)+\][^}]+?webpackId:\d+,name:"GuildSettings"/,
+
/webpackId:(\d+),name:"GuildSettings"/
+
)
+
.then(
+
() =>
+
(GuildSettingsRoleEditClasses = spacepack.require(
+
"discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"
+
))
+
);
type SettingsProps = {
ext: MoonbaseExtension;
···
setting: ExtensionSettingsManifest;
disabled: boolean;
};
type SettingsComponent = React.ComponentType<SettingsProps>;
const Margins = spacepack.require("discord/styles/shared/Margins.css");
+
function markdownify(str: string) {
+
return MarkupUtils.parse(str, true, {
+
hideSimpleEmbedContent: true,
+
allowLinks: true
+
});
+
}
+
function useConfigEntry<T>(uniqueId: number, name: string) {
return useStateFromStores(
[MoonbaseSettingsStore],
···
}
function Boolean({ ext, name, setting, disabled }: SettingsProps) {
const { value, displayName, description } = useConfigEntry<boolean>(ext.uniqueId, name);
return (
···
onChange={(value: boolean) => {
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value);
}}
+
note={description != null ? markdownify(description) : undefined}
className={`${Margins.marginReset} ${Margins.marginTop20}`}
>
{displayName}
···
}
function Number({ ext, name, setting, disabled }: SettingsProps) {
const { value, displayName, description } = useConfigEntry<number>(ext.uniqueId, name);
const castedSetting = setting as NumberSettingType;
+
const min = castedSetting.min;
+
const max = castedSetting.max;
+
+
const onChange = (value: number) => {
+
const rounded = min == null || max == null ? Math.round(value) : Math.max(min, Math.min(max, Math.round(value)));
+
MoonbaseSettingsStore.setExtensionConfig(ext.id, name, rounded);
+
};
return (
<FormItem className={Margins.marginTop20} title={displayName}>
+
{min == null || max == null ? (
+
<Flex justify={Flex.Justify.BETWEEN} direction={Flex.Direction.HORIZONTAL}>
+
{description && <FormText>{markdownify(description)}</FormText>}
+
<NumberInputStepper value={value ?? 0} onChange={onChange} />
+
</Flex>
+
) : (
+
<>
+
{description && <FormText>{markdownify(description)}</FormText>}
+
<Slider
+
initialValue={value ?? 0}
+
disabled={disabled}
+
minValue={min}
+
maxValue={max}
+
onValueChange={onChange}
+
onValueRender={(value: number) => `${Math.round(value)}`}
+
/>
+
</>
+
)}
</FormItem>
);
}
function String({ ext, name, setting, disabled }: SettingsProps) {
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
return (
<FormItem className={Margins.marginTop20} title={displayName}>
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
<TextInput
value={value ?? ""}
+
disabled={disabled}
+
onChange={(value: string) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)}
/>
</FormItem>
);
}
function MultilineString({ ext, name, setting, disabled }: SettingsProps) {
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
return (
<FormItem className={Margins.marginTop20} title={displayName}>
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
<TextArea
rows={5}
value={value ?? ""}
+
disabled={disabled}
className={"moonbase-resizeable"}
+
onChange={(value: string) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)}
/>
</FormItem>
);
}
function Select({ ext, name, setting, disabled }: SettingsProps) {
const { value, displayName, description } = useConfigEntry<string>(ext.uniqueId, name);
const castedSetting = setting as SelectSettingType;
···
return (
<FormItem className={Margins.marginTop20} title={displayName}>
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
<SingleSelect
autofocus={false}
clearable={false}
···
}
function MultiSelect({ ext, name, setting, disabled }: SettingsProps) {
const { value, displayName, description } = useConfigEntry<string | string[]>(ext.uniqueId, name);
const castedSetting = setting as MultiSelectSettingType;
···
return (
<FormItem className={Margins.marginTop20} title={displayName}>
+
{description && <FormText className={Margins.marginBottom8}>{markdownify(description)}</FormText>}
+
<DiscordSelect
autofocus={false}
clearable={false}
closeOnSelect={false}
···
);
}
function RemoveEntryButton({ onClick, disabled }: { onClick: () => void; disabled: boolean }) {
return (
+
<div className={GuildSettingsRoleEditClasses.removeButtonContainer}>
<Tooltip text="Remove entry" position="top">
{(props: any) => (
+
<Clickable {...props} className={GuildSettingsRoleEditClasses.removeButton} onClick={onClick}>
<CircleXIcon width={16} height={16} />
</Clickable>
)}
···
}
function List({ ext, name, setting, disabled }: SettingsProps) {
const { value, displayName, description } = useConfigEntry<string[]>(ext.uniqueId, name);
const entries = value ?? [];
···
return (
<FormItem className={Margins.marginTop20} title={displayName}>
+
{description && <FormText className={Margins.marginBottom4}>{markdownify(description)}</FormText>}
<Flex direction={Flex.Direction.VERTICAL}>
{entries.map((val, i) => (
// FIXME: stylesheets
···
}
function Dictionary({ ext, name, setting, disabled }: SettingsProps) {
const { value, displayName, description } = useConfigEntry<Record<string, string>>(ext.uniqueId, name);
const entries = Object.entries(value ?? {});
···
return (
<FormItem className={Margins.marginTop20} title={displayName}>
+
{description && <FormText className={Margins.marginBottom4}>{markdownify(description)}</FormText>}
<Flex direction={Flex.Direction.VERTICAL}>
{entries.map(([key, val], i) => (
// FIXME: stylesheets
···
);
if (Component == null) {
return (
<Text variant="text-md/normal">{`Custom setting "${displayName}" is missing a component. Perhaps the extension is not installed?`}</Text>
);
}
return (
+
<ErrorBoundary>
+
<Component value={value} setValue={(value) => MoonbaseSettingsStore.setExtensionConfig(ext.id, name, value)} />
+
</ErrorBoundary>
);
}
···
export default function Settings({ ext }: { ext: MoonbaseExtension }) {
return (
<Flex className="moonbase-settings" direction={Flex.Direction.VERTICAL}>
+
{Object.entries(ext.settingsOverride ?? ext.manifest.settings!).map(([name, setting]) => (
<Setting
ext={ext}
key={name}
+26 -13
packages/core-extensions/src/moonbase/webpackModules/ui/index.tsx
···
import React from "@moonlight-mod/wp/react";
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import { Text, TabBar } from "@moonlight-mod/wp/discord/components/common/index";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
import { UserSettingsModalStore } from "@moonlight-mod/wp/common_stores";
import ExtensionsPage from "./extensions";
import ConfigPage from "./config";
import Update from "./update";
-
-
const { Divider } = spacepack.findByCode(".forumOrHome]:")[0].exports.Z;
-
const TitleBarClasses = spacepack.findByCode("iconWrapper:", "children:")[0].exports;
-
const TabBarClasses = spacepack.findByCode("nowPlayingColumn:")[0].exports;
-
const { setSection, clearSubsection } = spacepack.findByExports("setSection", "clearSubsection")[0].exports.Z;
-
const Margins = spacepack.require("discord/styles/shared/Margins.css");
export const pages: {
id: string;
···
id: "config",
name: "Config",
element: ConfigPage
}
];
···
const subsection = useStateFromStores([UserSettingsModalStore], () => UserSettingsModalStore.getSubsection() ?? 0);
const setSubsection = React.useCallback(
(to: string) => {
-
if (subsection !== to) setSection("moonbase", to);
},
[subsection]
);
···
React.useEffect(
() => () => {
// 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
-
clearSubsection("moonbase");
},
[]
);
return (
<>
-
<div className={`${TitleBarClasses.children} ${Margins.marginBottom20}`}>
-
<Text className={TitleBarClasses.titleWrapper} variant="heading-lg/semibold" tag="h2">
Moonbase
</Text>
<Divider />
-
<TabBar selectedItem={subsection} onItemSelect={setSubsection} type="top-pill" className={TabBarClasses.tabBar}>
{pages.map((page, i) => (
-
<TabBar.Item key={page.id} id={i} className={TabBarClasses.item}>
{page.name}
</TabBar.Item>
))}
</TabBar>
</div>
<Update />
{React.createElement(pages[subsection].element)}
</>
);
}
···
import React from "@moonlight-mod/wp/react";
import { Text, TabBar } from "@moonlight-mod/wp/discord/components/common/index";
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
import { UserSettingsModalStore } from "@moonlight-mod/wp/common_stores";
import ExtensionsPage from "./extensions";
import ConfigPage from "./config";
+
import AboutPage from "./about";
import Update from "./update";
+
import RestartAdviceMessage from "./RestartAdvice";
+
import { Divider } from "@moonlight-mod/wp/discord/components/common/BaseHeaderBar";
+
import HeaderBarClasses from "@moonlight-mod/wp/discord/components/common/HeaderBar.css";
+
import PeoplePageClasses from "@moonlight-mod/wp/discord/modules/people/web/PeoplePage.css";
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
+
import Margins from "@moonlight-mod/wp/discord/styles/shared/Margins.css";
export const pages: {
id: string;
···
id: "config",
name: "Config",
element: ConfigPage
+
},
+
{
+
id: "about",
+
name: "About",
+
element: AboutPage
}
];
···
const subsection = useStateFromStores([UserSettingsModalStore], () => UserSettingsModalStore.getSubsection() ?? 0);
const setSubsection = React.useCallback(
(to: string) => {
+
if (subsection !== to) UserSettingsModalActionCreators.setSection("moonbase", to);
},
[subsection]
);
···
React.useEffect(
() => () => {
// 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
+
UserSettingsModalActionCreators.clearSubsection("moonbase");
},
[]
);
return (
<>
+
<div className={`${HeaderBarClasses.children} ${Margins.marginBottom20}`}>
+
<Text className={HeaderBarClasses.titleWrapper} variant="heading-lg/semibold" tag="h2">
Moonbase
</Text>
<Divider />
+
<TabBar
+
selectedItem={subsection}
+
onItemSelect={setSubsection}
+
type="top-pill"
+
className={PeoplePageClasses.tabBar}
+
>
{pages.map((page, i) => (
+
<TabBar.Item key={page.id} id={i} className={PeoplePageClasses.item}>
{page.name}
</TabBar.Item>
))}
</TabBar>
</div>
+
<RestartAdviceMessage />
<Update />
{React.createElement(pages[subsection].element)}
</>
);
}
+
+
export { RestartAdviceMessage, Update };
+83 -40
packages/core-extensions/src/moonbase/webpackModules/ui/update.tsx
···
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
import React from "@moonlight-mod/wp/react";
-
import spacepack from "@moonlight-mod/wp/spacepack_spacepack";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
-
import { UpdateState } from "../../types";
-
-
const { ThemeDarkIcon, Text, Button } = Components;
-
const Margins = spacepack.require("discord/styles/shared/Margins.css");
-
const HelpMessageClasses = spacepack.findByExports("positive", "iconDiv")[0].exports;
-
-
const logger = moonlight.getLogger("moonbase/ui/update");
const strings: Record<UpdateState, string> = {
[UpdateState.Ready]: "A new version of moonlight is available.",
···
[UpdateState.Failed]: "Failed to update moonlight. Please use the installer instead."
};
export default function Update() {
-
const [state, setState] = React.useState(UpdateState.Ready);
-
const newVersion = useStateFromStores([MoonbaseSettingsStore], () => MoonbaseSettingsStore.newVersion);
if (newVersion == null) return null;
-
// reimpl of HelpMessage but with a custom icon
return (
-
<div
-
className={`${Margins.marginBottom20} ${HelpMessageClasses.info} ${HelpMessageClasses.container} moonbase-update-section`}
-
>
-
<Flex direction={Flex.Direction.HORIZONTAL}>
-
<div
-
className={HelpMessageClasses.iconDiv}
-
style={{
-
alignItems: "center"
-
}}
-
>
-
<ThemeDarkIcon size="sm" color="currentColor" className={HelpMessageClasses.icon} />
-
</div>
-
<Text variant="text-sm/medium" color="currentColor" className={HelpMessageClasses.text}>
-
{strings[state]}
-
</Text>
-
</Flex>
-
-
<div className="moonbase-update-section-buttons">
{state === UpdateState.Installed && (
<Button
look={Button.Looks.OUTLINED}
color={Button.Colors.CUSTOM}
size={Button.Sizes.TINY}
-
onClick={() => window.location.reload()}
>
Restart Discord
</Button>
···
size={Button.Sizes.TINY}
disabled={state !== UpdateState.Ready}
onClick={() => {
-
setState(UpdateState.Working);
-
-
MoonbaseSettingsStore.updateMoonlight()
-
.then(() => setState(UpdateState.Installed))
-
.catch((e) => {
-
logger.error(e);
-
setState(UpdateState.Failed);
-
});
}}
>
Update
</Button>
</div>
-
</div>
);
}
···
import { useStateFromStores } from "@moonlight-mod/wp/discord/packages/flux";
import { MoonbaseSettingsStore } from "@moonlight-mod/wp/moonbase_stores";
import React from "@moonlight-mod/wp/react";
+
import { UpdateState } from "../../types";
+
import HelpMessage from "./HelpMessage";
+
import { MoonlightBranch } from "@moonlight-mod/types";
+
import MarkupUtils from "@moonlight-mod/wp/discord/modules/markup/MarkupUtils";
import Flex from "@moonlight-mod/wp/discord/uikit/Flex";
+
import {
+
Button,
+
Text,
+
ModalRoot,
+
ModalSize,
+
ModalContent,
+
ModalHeader,
+
Heading,
+
ModalCloseButton,
+
openModal
+
} from "@moonlight-mod/wp/discord/components/common/index";
+
import MarkupClasses from "@moonlight-mod/wp/discord/modules/messages/web/Markup.css";
+
import ThemeDarkIcon from "@moonlight-mod/wp/moonbase_ThemeDarkIcon";
const strings: Record<UpdateState, string> = {
[UpdateState.Ready]: "A new version of moonlight is available.",
···
[UpdateState.Failed]: "Failed to update moonlight. Please use the installer instead."
};
+
function MoonlightChangelog({
+
changelog,
+
version,
+
transitionState,
+
onClose
+
}: {
+
changelog: string;
+
version: string;
+
transitionState: number | null;
+
onClose: () => void;
+
}) {
+
return (
+
<ModalRoot transitionState={transitionState} size={ModalSize.DYNAMIC}>
+
<ModalHeader>
+
<Flex.Child grow={1} shrink={1}>
+
<Heading variant="heading-lg/semibold">moonlight</Heading>
+
<Text variant="text-xs/normal">{version}</Text>
+
</Flex.Child>
+
+
<Flex.Child grow={0}>
+
<ModalCloseButton onClick={onClose} />
+
</Flex.Child>
+
</ModalHeader>
+
+
<ModalContent>
+
<Text variant="text-md/normal" className={MarkupClasses.markup} style={{ padding: "1rem" }}>
+
{MarkupUtils.parse(changelog, true, {
+
allowHeading: true,
+
allowList: true,
+
allowLinks: true
+
})}
+
</Text>
+
</ModalContent>
+
</ModalRoot>
+
);
+
}
+
export default function Update() {
+
const [newVersion, state] = useStateFromStores([MoonbaseSettingsStore], () => [
+
MoonbaseSettingsStore.newVersion,
+
MoonbaseSettingsStore.updateState
+
]);
if (newVersion == null) return null;
return (
+
<HelpMessage text={strings[state]} className="moonbase-update-section" icon={ThemeDarkIcon}>
+
<div className="moonbase-help-message-buttons">
+
{moonlight.branch === MoonlightBranch.STABLE && (
+
<Button
+
look={Button.Looks.OUTLINED}
+
color={Button.Colors.CUSTOM}
+
size={Button.Sizes.TINY}
+
onClick={() => {
+
fetch(`https://raw.githubusercontent.com/moonlight-mod/moonlight/refs/tags/${newVersion}/CHANGELOG.md`)
+
.then((r) => r.text())
+
.then((changelog) =>
+
openModal((modalProps) => {
+
return <MoonlightChangelog {...modalProps} changelog={changelog} version={newVersion} />;
+
})
+
);
+
}}
+
>
+
View changelog
+
</Button>
+
)}
{state === UpdateState.Installed && (
<Button
look={Button.Looks.OUTLINED}
color={Button.Colors.CUSTOM}
size={Button.Sizes.TINY}
+
onClick={() => {
+
MoonbaseSettingsStore.restartDiscord();
+
}}
>
Restart Discord
</Button>
···
size={Button.Sizes.TINY}
disabled={state !== UpdateState.Ready}
onClick={() => {
+
MoonbaseSettingsStore.updateMoonlight();
}}
>
Update
</Button>
</div>
+
</HelpMessage>
);
}
+7 -20
packages/core-extensions/src/moonbase/webpackModules/updates.tsx
···
import Notices from "@moonlight-mod/wp/notices_notices";
import { MoonlightBranch } from "@moonlight-mod/types";
import React from "@moonlight-mod/wp/react";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
-
-
// FIXME: not indexed as importable
-
const Constants = spacepack.require("discord/Constants");
-
const UserSettingsSections = spacepack.findObjectFromKey(Constants, "APPEARANCE_THEME_PICKER");
-
-
const { ThemeDarkIcon } = Components;
function plural(str: string, num: number) {
return `${str}${num > 1 ? "s" : ""}`;
···
{
name: "Open Moonbase",
onClick: () => {
-
const { open } = spacepack.findByExports("setSection", "clearSubsection")[0].exports.Z;
-
-
// settings is lazy loaded thus lazily patched
-
// FIXME: figure out a way to detect if settings has been opened
-
// alreadyjust so the transition isnt as jarring
-
open(UserSettingsSections.ACCOUNT);
-
setTimeout(() => {
-
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
-
open("moonbase-extensions");
-
} else {
-
open("moonbase", 0);
-
}
-
}, 0);
return true;
}
}
···
import Notices from "@moonlight-mod/wp/notices_notices";
import { MoonlightBranch } from "@moonlight-mod/types";
import React from "@moonlight-mod/wp/react";
+
import ThemeDarkIcon from "@moonlight-mod/wp/moonbase_ThemeDarkIcon";
function plural(str: string, num: number) {
return `${str}${num > 1 ? "s" : ""}`;
···
{
name: "Open Moonbase",
onClick: () => {
+
const { open } = spacepack.require("discord/actions/UserSettingsModalActionCreators").default;
+
if (MoonbaseSettingsStore.getExtensionConfigRaw<boolean>("moonbase", "sections", false)) {
+
open("moonbase-extensions");
+
} else {
+
open("moonbase", "0");
+
}
return true;
}
}
+5
packages/core-extensions/src/moonbase/wp.d.ts
···
declare module "@moonlight-mod/wp/moonbase_stores" {
export * from "core-extensions/src/moonbase/webpackModules/stores";
}
···
declare module "@moonlight-mod/wp/moonbase_stores" {
export * from "core-extensions/src/moonbase/webpackModules/stores";
}
+
+
declare module "@moonlight-mod/wp/moonbase_ThemeDarkIcon" {
+
import ThemeDarkIcon from "core-extensions/src/moonbase/webpackModules/ThemeDarkIcon";
+
export = ThemeDarkIcon;
+
}
+141 -1
packages/core-extensions/src/nativeFixes/host.ts
···
import { app, nativeTheme } from "electron";
const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(",");
moonlightHost.events.on("window-created", function (browserWindow) {
···
// already added on Windows, but not on other operating systems
app.commandLine.appendSwitch("disable-background-timer-throttling");
}
if (process.platform === "linux") {
···
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxSpeechDispatcher") ?? true) {
app.commandLine.appendSwitch("enable-speech-dispatcher");
}
}
// NOTE: Only tested if this appears on Windows, it should appear on all when
// hardware acceleration is disabled
const noAccel = app.commandLine.hasSwitch("disable-gpu-compositing");
if ((moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapi") ?? true) && !noAccel) {
-
if (process.platform === "linux")
// These will eventually be renamed https://source.chromium.org/chromium/chromium/src/+/5482210941a94d70406b8da962426e4faca7fce4
enabledFeatures.push("VaapiVideoEncoder", "VaapiVideoDecoder", "VaapiVideoDecodeLinuxGL");
}
app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].join(","));
···
import { app, nativeTheme } from "electron";
+
import * as path from "node:path";
+
import * as fs from "node:fs/promises";
+
import * as fsSync from "node:fs";
+
import { parseTarGzip } from "nanotar";
+
const logger = moonlightHost.getLogger("nativeFixes/host");
const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(",");
moonlightHost.events.on("window-created", function (browserWindow) {
···
// already added on Windows, but not on other operating systems
app.commandLine.appendSwitch("disable-background-timer-throttling");
+
}
+
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "vulkan") ?? false) {
+
enabledFeatures.push("Vulkan", "DefaultANGLEVulkan", "VulkanFromANGLE");
}
if (process.platform === "linux") {
···
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxSpeechDispatcher") ?? true) {
app.commandLine.appendSwitch("enable-speech-dispatcher");
}
+
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxHevcSupport") ?? true) {
+
enabledFeatures.push("PlatformHEVCDecoderSupport");
+
}
}
// NOTE: Only tested if this appears on Windows, it should appear on all when
// hardware acceleration is disabled
const noAccel = app.commandLine.hasSwitch("disable-gpu-compositing");
if ((moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapi") ?? true) && !noAccel) {
+
if (process.platform === "linux") {
// These will eventually be renamed https://source.chromium.org/chromium/chromium/src/+/5482210941a94d70406b8da962426e4faca7fce4
enabledFeatures.push("VaapiVideoEncoder", "VaapiVideoDecoder", "VaapiVideoDecodeLinuxGL");
+
+
if (moonlightHost.getConfigOption<boolean>("nativeFixes", "vaapiIgnoreDriverChecks") ?? false)
+
enabledFeatures.push("VaapiIgnoreDriverChecks");
+
}
}
app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].join(","));
+
+
if (process.platform === "linux" && moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxUpdater")) {
+
const exePath = app.getPath("exe");
+
const appName = path.basename(exePath);
+
const targetDir = path.dirname(exePath);
+
const { releaseChannel }: { releaseChannel: string } = JSON.parse(
+
fsSync.readFileSync(path.join(targetDir, "resources", "build_info.json"), "utf8")
+
);
+
+
const updaterModule = require(path.join(moonlightHost.asarPath, "app_bootstrap", "hostUpdater.js"));
+
const updater = updaterModule.constructor;
+
+
async function doUpdate(cb: (percent: number) => void) {
+
logger.debug("Extracting to", targetDir);
+
+
const exists = (path: string) =>
+
fs
+
.stat(path)
+
.then(() => true)
+
.catch(() => false);
+
+
const url = `https://discord.com/api/download/${releaseChannel}?platform=linux&format=tar.gz`;
+
const resp = await fetch(url, {
+
cache: "no-store"
+
});
+
+
const reader = resp.body!.getReader();
+
const contentLength = parseInt(resp.headers.get("Content-Length") ?? "0");
+
logger.info(`Expecting ${contentLength} bytes for the update`);
+
const bytes = new Uint8Array(contentLength);
+
let pos = 0;
+
let lastPercent = 0;
+
+
while (true) {
+
const { done, value } = await reader.read();
+
if (done) {
+
break;
+
} else {
+
bytes.set(value, pos);
+
pos += value.length;
+
+
const newPercent = Math.floor((pos / contentLength) * 100);
+
if (lastPercent !== newPercent) {
+
lastPercent = newPercent;
+
cb(newPercent);
+
}
+
}
+
}
+
+
const files = await parseTarGzip(bytes);
+
+
for (const file of files) {
+
if (!file.data) continue;
+
// @ts-expect-error What do you mean their own types are wrong
+
if (file.type !== "file") continue;
+
+
// Discord update files are inside of a main "Discord(PTB|Canary)" folder
+
const filePath = file.name.replace(`${appName}/`, "");
+
logger.info("Extracting", filePath);
+
+
let targetFilePath = path.join(targetDir, filePath);
+
if (filePath === "resources/app.asar") {
+
// You tried
+
targetFilePath = path.join(targetDir, "resources", "_app.asar");
+
} else if (filePath === appName || filePath === "chrome_crashpad_handler") {
+
// Can't write over the executable? Just move it! 4head
+
if (await exists(targetFilePath)) {
+
await fs.rename(targetFilePath, targetFilePath + ".bak");
+
await fs.unlink(targetFilePath + ".bak");
+
}
+
}
+
const targetFileDir = path.dirname(targetFilePath);
+
+
if (!(await exists(targetFileDir))) await fs.mkdir(targetFileDir, { recursive: true });
+
await fs.writeFile(targetFilePath, file.data);
+
+
const mode = file.attrs?.mode;
+
if (mode != null) {
+
// Not sure why this slice is needed
+
await fs.chmod(targetFilePath, mode.slice(-3));
+
}
+
}
+
+
logger.debug("Done updating");
+
}
+
+
const realEmit = updater.prototype.emit;
+
updater.prototype.emit = function (event: string, ...args: any[]) {
+
// Arrow functions don't bind `this` :D
+
const call = (event: string, ...args: any[]) => realEmit.call(this, event, ...args);
+
+
if (event === "update-manually") {
+
const latestVerStr: string = args[0];
+
logger.debug("update-manually called, intercepting", latestVerStr);
+
call("update-available");
+
+
(async () => {
+
try {
+
await doUpdate((progress) => {
+
call("update-progress", progress);
+
});
+
// Copied from the win32 updater
+
this.updateVersion = latestVerStr;
+
call(
+
"update-downloaded",
+
{},
+
releaseChannel,
+
latestVerStr,
+
new Date(),
+
this.updateUrl,
+
this.quitAndInstall.bind(this)
+
);
+
} catch (e) {
+
logger.error("Error updating", e);
+
}
+
})();
+
+
return this;
+
} else {
+
return realEmit.call(this, event, ...args);
+
}
+
};
+
}
+36 -1
packages/core-extensions/src/nativeFixes/manifest.json
···
{
"id": "nativeFixes",
"meta": {
"name": "Native Fixes",
"tagline": "Various configurable fixes for Discord and Electron",
-
"authors": ["Cynosphere", "adryd"],
"tags": ["fixes"]
},
"settings": {
"devtoolsThemeFix": {
"displayName": "Devtools Theme Fix",
"description": "Temporary workaround for devtools defaulting to light theme on Electron 32",
"type": "boolean",
"default": true
},
"disableRendererBackgrounding": {
"displayName": "Disable Renderer Backgrounding",
"description": "This is enabled by default as a power saving measure, but it breaks screensharing and websocket connections fairly often",
"type": "boolean",
"default": true
},
"linuxAutoscroll": {
"displayName": "Enable middle click autoscroll on Linux",
"description": "Requires manual configuration of your system to disable middle click paste, has no effect on other operating systems",
"type": "boolean",
"default": false
},
"linuxSpeechDispatcher": {
"displayName": "Enable speech-dispatcher for TTS on Linux",
"description": "Fixes text-to-speech. Has no effect on other operating systems",
"type": "boolean",
"default": true
},
"vaapi": {
"displayName": "Enable VAAPI features on Linux",
"description": "Provides hardware accelerated video encode and decode. Has no effect on other operating systems",
"type": "boolean",
"default": true
}
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "nativeFixes",
"meta": {
"name": "Native Fixes",
"tagline": "Various configurable fixes for Discord and Electron",
+
"authors": ["Cynosphere", "adryd", "NotNite"],
"tags": ["fixes"]
},
+
"environment": "desktop",
"settings": {
"devtoolsThemeFix": {
+
"advice": "restart",
"displayName": "Devtools Theme Fix",
"description": "Temporary workaround for devtools defaulting to light theme on Electron 32",
"type": "boolean",
"default": true
},
"disableRendererBackgrounding": {
+
"advice": "restart",
"displayName": "Disable Renderer Backgrounding",
"description": "This is enabled by default as a power saving measure, but it breaks screensharing and websocket connections fairly often",
"type": "boolean",
"default": true
},
+
"vulkan": {
+
"advice": "restart",
+
"displayName": "Enable Vulkan renderer",
+
"description": "Uses the Vulkan backend for rendering",
+
"type": "boolean",
+
"default": false
+
},
"linuxAutoscroll": {
+
"advice": "restart",
"displayName": "Enable middle click autoscroll on Linux",
"description": "Requires manual configuration of your system to disable middle click paste, has no effect on other operating systems",
"type": "boolean",
"default": false
},
"linuxSpeechDispatcher": {
+
"advice": "restart",
"displayName": "Enable speech-dispatcher for TTS on Linux",
"description": "Fixes text-to-speech. Has no effect on other operating systems",
"type": "boolean",
"default": true
},
"vaapi": {
+
"advice": "restart",
"displayName": "Enable VAAPI features on Linux",
"description": "Provides hardware accelerated video encode and decode. Has no effect on other operating systems",
+
"type": "boolean",
+
"default": true
+
},
+
"vaapiIgnoreDriverChecks": {
+
"advice": "restart",
+
"displayName": "Ignore VAAPI driver checks on Linux",
+
"description": "Forces hardware video acceleration on some graphics drivers at the cost of stability. Has no effect on other operating systems",
+
"type": "boolean",
+
"default": false
+
},
+
"linuxUpdater": {
+
"advice": "restart",
+
"displayName": "Linux Updater",
+
"description": "Actually implements updating Discord on Linux. Has no effect on other operating systems",
+
"type": "boolean",
+
"default": false
+
},
+
"linuxHevcSupport": {
+
"advice": "restart",
+
"displayName": "HEVC support on Linux",
+
"description": "You might also need to enable Vulkan renderer. Has no effect on other operating systems",
"type": "boolean",
"default": true
}
+3 -3
packages/core-extensions/src/noHideToken/index.ts
···
export const patches: Patch[] = [
{
-
find: "hideToken:function",
replace: {
-
match: /(?<=hideToken:function\(\){)/,
-
replacement: `return()=>{};`
}
}
];
···
export const patches: Patch[] = [
{
+
find: "hideToken:()=>",
replace: {
+
match: /hideToken:\(\)=>.+?,/,
+
replacement: `hideToken:()=>{},`
}
}
];
+1
packages/core-extensions/src/noHideToken/manifest.json
···
{
"id": "noHideToken",
"apiLevel": 2,
"meta": {
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "noHideToken",
"apiLevel": 2,
"meta": {
+3 -3
packages/core-extensions/src/noTrack/index.ts
···
export const patches: Patch[] = [
{
-
find: "analyticsTrackingStoreMaker:function",
replace: {
-
match: /analyticsTrackingStoreMaker:function\(\){return .+?}/,
-
replacement: "analyticsTrackingStoreMaker:function(){return ()=>{}}"
}
},
{
···
export const patches: Patch[] = [
{
+
find: "analyticsTrackingStoreMaker:()=>",
replace: {
+
match: /analyticsTrackingStoreMaker:\(\)=>.+?,/,
+
replacement: "analyticsTrackingStoreMaker:()=>()=>{},"
}
},
{
+4 -1
packages/core-extensions/src/noTrack/manifest.json
···
{
"id": "noTrack",
"apiLevel": 2,
"meta": {
···
},
"blocked": [
"https://*.discord.com/api/v*/science",
-
"https://*.discord.com/api/v*/metrics"
]
}
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "noTrack",
"apiLevel": 2,
"meta": {
···
},
"blocked": [
"https://*.discord.com/api/v*/science",
+
"https://*.discord.com/api/v*/metrics",
+
"https://*.discordapp.com/api/v*/science",
+
"https://*.discordapp.com/api/v*/metrics"
]
}
+1
packages/core-extensions/src/notices/manifest.json
···
{
"id": "notices",
"apiLevel": 2,
"meta": {
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "notices",
"apiLevel": 2,
"meta": {
+3 -6
packages/core-extensions/src/notices/webpackModules/component.tsx
···
import React from "@moonlight-mod/wp/react";
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
-
import * as Components from "@moonlight-mod/wp/discord/components/common/index";
import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
import NoticesStore from "@moonlight-mod/wp/notices_notices";
-
import type { Notice } from "@moonlight-mod/types/coreExtensions/notices";
-
// FIXME: types
-
const { Notice, NoticeCloseButton, PrimaryCTANoticeButton } = Components;
-
-
function popAndDismiss(notice: Notice) {
NoticesStore.popNotice();
if (notice?.onDismiss) {
notice.onDismiss();
···
import React from "@moonlight-mod/wp/react";
import Dispatcher from "@moonlight-mod/wp/discord/Dispatcher";
+
import { Notice, NoticeCloseButton, PrimaryCTANoticeButton } from "@moonlight-mod/wp/discord/components/common/index";
import { useStateFromStoresObject } from "@moonlight-mod/wp/discord/packages/flux";
import NoticesStore from "@moonlight-mod/wp/notices_notices";
+
import type { Notice as NoticeType } from "@moonlight-mod/types/coreExtensions/notices";
+
function popAndDismiss(notice: NoticeType) {
NoticesStore.popNotice();
if (notice?.onDismiss) {
notice.onDismiss();
+22 -2
packages/core-extensions/src/quietLoggers/index.ts
···
const notXssDefensesOnly = () =>
(moonlight.getConfigOption<boolean>("quietLoggers", "xssDefensesOnly") ?? false) === false;
// These patches MUST run before the simple patches, these are to remove loggers
// that end up causing syntax errors by the normal patch
const loggerFixes: Patch[] = [
···
// Patches to simply remove a logger call
const stubPatches = [
// "sh" is not a valid locale.
-
["is not a valid locale", /(.)\.error\(""\.concat\((.)," is not a valid locale\."\)\)/g],
['"[BUILD INFO] Release Channel: "', /new .{1,2}\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?\)\),/],
['.APP_NATIVE_CRASH,"Storage"', /console\.log\("AppCrashedFatalReport lastCrash:",.,.\);/],
-
['.APP_NATIVE_CRASH,"Storage"', 'console.log("AppCrashedFatalReport: getLastCrash not supported.");'],
['"[NATIVE INFO] ', /new .{1,2}\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/],
['"Spellchecker"', /.\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
['throw Error("Messages are still loading.");', /console\.warn\("Unsupported Locale",.\),/],
···
match: /\(null!=.{1,2}&&"0\.0\.0"===.{1,2}\.remoteApp\.getVersion\(\)\)/,
replacement: "(true)"
}
},
...loggerFixes,
...stubPatches.map((patch) => ({
···
const notXssDefensesOnly = () =>
(moonlight.getConfigOption<boolean>("quietLoggers", "xssDefensesOnly") ?? false) === false;
+
const silenceDiscordLogger = moonlight.getConfigOption<boolean>("quietLoggers", "silenceDiscordLogger") ?? false;
+
// These patches MUST run before the simple patches, these are to remove loggers
// that end up causing syntax errors by the normal patch
const loggerFixes: Patch[] = [
···
// Patches to simply remove a logger call
const stubPatches = [
// "sh" is not a valid locale.
+
["is not a valid locale", /void (.)\.error\(""\.concat\((.)," is not a valid locale\."\)\)/g],
['"[BUILD INFO] Release Channel: "', /new .{1,2}\.Z\(\)\.log\("\[BUILD INFO\] Release Channel: ".+?\)\),/],
['.APP_NATIVE_CRASH,"Storage"', /console\.log\("AppCrashedFatalReport lastCrash:",.,.\);/],
+
['.APP_NATIVE_CRASH,"Storage"', 'void console.log("AppCrashedFatalReport: getLastCrash not supported.")'],
['"[NATIVE INFO] ', /new .{1,2}\.Z\(\)\.log\("\[NATIVE INFO] .+?\)\);/],
['"Spellchecker"', /.\.info\("Switching to ".+?"\(unavailable\)"\);?/g],
['throw Error("Messages are still loading.");', /console\.warn\("Unsupported Locale",.\),/],
···
match: /\(null!=.{1,2}&&"0\.0\.0"===.{1,2}\.remoteApp\.getVersion\(\)\)/,
replacement: "(true)"
}
+
},
+
// Highlight.js deprecation warnings
+
{
+
find: "Deprecated as of",
+
replace: {
+
match: /console\./g,
+
replacement: "false&&console."
+
},
+
prerequisite: notXssDefensesOnly
+
},
+
// Discord's logger
+
{
+
find: "ฮฃ:",
+
replace: {
+
match: "for",
+
replacement: "return;for"
+
},
+
prerequisite: () => silenceDiscordLogger && notXssDefensesOnly()
},
...loggerFixes,
...stubPatches.map((patch) => ({
+9
packages/core-extensions/src/quietLoggers/manifest.json
···
{
"id": "quietLoggers",
"apiLevel": 2,
"meta": {
···
},
"settings": {
"xssDefensesOnly": {
"displayName": "Only hide self-XSS",
"description": "Only disable self XSS prevention log",
"type": "boolean",
"default": false
}
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "quietLoggers",
"apiLevel": 2,
"meta": {
···
},
"settings": {
"xssDefensesOnly": {
+
"advice": "reload",
"displayName": "Only hide self-XSS",
"description": "Only disable self XSS prevention log",
+
"type": "boolean",
+
"default": false
+
},
+
"silenceDiscordLogger": {
+
"advice": "reload",
+
"displayName": "Silence Discord logger",
+
"description": "Hides all messages from Discord's logger (the logs that start with purple text in brackets)",
"type": "boolean",
"default": false
}
+4 -1
packages/core-extensions/src/rocketship/manifest.json
···
{
"id": "rocketship",
"apiLevel": 2,
"meta": {
"name": "Rocketship",
"tagline": "Adds new features when using rocketship",
"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.",
-
"authors": ["NotNite", "Cynosphere", "adryd"]
}
}
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "rocketship",
"apiLevel": 2,
+
"environment": "desktop",
"meta": {
"name": "Rocketship",
"tagline": "Adds new features when using rocketship",
"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.",
+
"authors": ["NotNite", "Cynosphere", "adryd"],
+
"deprecated": true
}
}
+1 -1
packages/core-extensions/src/settings/index.ts
···
{
find: 'navId:"user-settings-cog",',
replace: {
-
match: /children:\[(.)\.map\(.+?\),children:.\((.)\)/,
replacement: (orig, sections, section) =>
`${orig.replace(
/Object\.values\(.\..+?\)/,
···
{
find: 'navId:"user-settings-cog",',
replace: {
+
match: /children:\[(\i)\.map\(.+?\),.*?children:\i\((\i)\)/,
replacement: (orig, sections, section) =>
`${orig.replace(
/Object\.values\(.\..+?\)/,
+1
packages/core-extensions/src/settings/manifest.json
···
{
"id": "settings",
"apiLevel": 2,
"meta": {
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "settings",
"apiLevel": 2,
"meta": {
+8 -2
packages/core-extensions/src/settings/webpackModules/settings.ts
···
import { SettingsSection, Settings as SettingsType } from "@moonlight-mod/types/coreExtensions/settings";
export const Settings: SettingsType = {
ourSections: [],
sectionNames: [],
sectionMenuItems: {},
-
addSection: (section, label, element, color = null, pos, notice) => {
const data: SettingsSection = {
section,
label,
color,
element,
pos: pos ?? -4,
-
notice: notice
};
Settings.ourSections.push(data);
···
_mutateSections: (sections) => {
for (const section of Settings.ourSections) {
sections.splice(section.pos < 0 ? sections.length + section.pos : section.pos, 0, section);
}
···
import { SettingsSection, Settings as SettingsType } from "@moonlight-mod/types/coreExtensions/settings";
+
import UserSettingsModalActionCreators from "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators";
export const Settings: SettingsType = {
ourSections: [],
sectionNames: [],
sectionMenuItems: {},
+
addSection: (section, label, element, color = null, pos, notice, onClick) => {
const data: SettingsSection = {
section,
label,
color,
element,
pos: pos ?? -4,
+
notice: notice,
+
onClick: onClick ?? (() => UserSettingsModalActionCreators.open(section))
};
Settings.ourSections.push(data);
···
_mutateSections: (sections) => {
for (const section of Settings.ourSections) {
+
// Discord's `pos` only supports numbers, so lets call the function to get the position.
+
if (typeof section.pos === "function") {
+
section.pos = section.pos(sections);
+
}
sections.splice(section.pos < 0 ? sections.length + section.pos : section.pos, 0, section);
}
+2
packages/core-extensions/src/spacepack/manifest.json
···
{
"id": "spacepack",
"apiLevel": 2,
"meta": {
···
},
"settings": {
"addToGlobalScope": {
"displayName": "Add to global scope",
"description": "Populates window.spacepack for easier usage in DevTools",
"type": "boolean",
···
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
"id": "spacepack",
"apiLevel": 2,
"meta": {
···
},
"settings": {
"addToGlobalScope": {
+
"advice": "reload",
"displayName": "Add to global scope",
"description": "Populates window.spacepack for easier usage in DevTools",
"type": "boolean",
+76 -26
packages/core-extensions/src/spacepack/webpackModules/spacepack.ts
···
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "@moonlight-mod/types";
import { Spacepack } from "@moonlight-mod/types/coreExtensions/spacepack";
const webpackRequire = require as unknown as WebpackRequireType;
const cache = webpackRequire.c;
···
"module",
"exports",
"require",
-
`(${funcStr}).apply(this, arguments)\n` + `//# sourceURL=Webpack-Module-${module}`
) as WebpackModuleFunc;
},
findByCode: (...args: (string | RegExp)[]) => {
-
return Object.entries(modules)
-
.filter(
-
([id, mod]) =>
-
!args.some(
-
(item) => !(item instanceof RegExp ? item.test(mod.toString()) : mod.toString().indexOf(item) !== -1)
-
)
-
)
.map(([id]) => {
//if (!(id in cache)) require(id);
//return cache[id];
···
try {
exports = require(id);
} catch (e) {
-
logger.error(`Error requiring module "${id}": `, e);
}
return {
···
};
})
.filter((item) => item !== null);
},
findByExports: (...args: string[]) => {
···
},
findObjectFromKey: (exports: Record<string, any>, key: string) => {
let subKey;
if (key.indexOf(".") > -1) {
const splitKey = key.split(".");
···
const obj = exports[exportKey];
if (obj && obj[key] !== undefined) {
if (subKey) {
-
if (obj[key][subKey]) return obj;
} else {
-
return obj;
}
}
}
-
return null;
},
findObjectFromValue: (exports: Record<string, any>, value: any) => {
for (const exportKey in exports) {
const obj = exports[exportKey];
// eslint-disable-next-line eqeqeq
-
if (obj == value) return obj;
for (const subKey in obj) {
// eslint-disable-next-line eqeqeq
if (obj && obj[subKey] == value) {
-
return obj;
}
}
}
-
return null;
},
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => {
for (const exportKey in exports) {
const obj = exports[exportKey];
// eslint-disable-next-line eqeqeq
if (obj && obj[key] == value) {
-
return obj;
}
}
return null;
},
findFunctionByStrings: (exports: Record<string, any>, ...strings: (string | RegExp)[]) => {
-
return (
Object.entries(exports).filter(
([index, func]) =>
-
typeof func === "function" &&
-
!strings.some(
-
(query) => !(query instanceof RegExp ? func.toString().match(query) : func.toString().includes(query))
-
)
-
)?.[0]?.[1] ?? null
-
);
},
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => {
const mod = Array.isArray(find) ? spacepack.findByCode(...find) : spacepack.findByCode(find);
-
if (mod.length < 1) return Promise.reject("Module find failed");
const findId = mod[0].id;
const findCode = webpackRequire.m[findId].toString().replace(/\n/g, "");
···
if (match) chunkIds = [...match[0].matchAll(/"(\d+)"/g)].map(([, id]) => id);
}
-
if (!chunkIds || chunkIds.length === 0) return Promise.reject("Chunk ID match failed");
const moduleId = findCode.match(module)?.[1];
-
if (!moduleId) return Promise.reject("Module ID match failed");
return Promise.all(chunkIds.map((c) => webpackRequire.e(c))).then(() => webpackRequire(moduleId));
},
···
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "@moonlight-mod/types";
import { Spacepack } from "@moonlight-mod/types/coreExtensions/spacepack";
+
import { processFind, testFind } from "@moonlight-mod/core/util/patch";
const webpackRequire = require as unknown as WebpackRequireType;
const cache = webpackRequire.c;
···
"module",
"exports",
"require",
+
`(${funcStr}).apply(this, arguments)\n` + `//# sourceURL=Webpack-Module/${module.slice(0, 3)}/${module}`
) as WebpackModuleFunc;
},
findByCode: (...args: (string | RegExp)[]) => {
+
const ret = Object.entries(modules)
+
.filter(([id, mod]) => !args.some((item) => !testFind(mod.toString(), processFind(item))))
.map(([id]) => {
//if (!(id in cache)) require(id);
//return cache[id];
···
try {
exports = require(id);
} catch (e) {
+
logger.error(`findByCode: Error requiring module "${id}": `, args, e);
}
return {
···
};
})
.filter((item) => item !== null);
+
+
if (ret.length === 0) {
+
logger.warn("findByCode: Got zero results for", args, new Error().stack!.substring(5));
+
}
+
+
return ret;
},
findByExports: (...args: string[]) => {
···
},
findObjectFromKey: (exports: Record<string, any>, key: string) => {
+
let ret = null;
let subKey;
if (key.indexOf(".") > -1) {
const splitKey = key.split(".");
···
const obj = exports[exportKey];
if (obj && obj[key] !== undefined) {
if (subKey) {
+
if (obj[key][subKey]) {
+
ret = obj;
+
break;
+
}
} else {
+
ret = obj;
+
break;
}
}
}
+
+
if (ret == null) {
+
logger.warn("Failed to find object by key", key, "in", exports, new Error().stack!.substring(5));
+
}
+
+
return ret;
},
findObjectFromValue: (exports: Record<string, any>, value: any) => {
+
let ret = null;
for (const exportKey in exports) {
const obj = exports[exportKey];
// eslint-disable-next-line eqeqeq
+
if (obj == value) {
+
ret = obj;
+
break;
+
}
for (const subKey in obj) {
// eslint-disable-next-line eqeqeq
if (obj && obj[subKey] == value) {
+
ret = obj;
+
break;
}
}
}
+
+
if (ret == null) {
+
logger.warn("Failed to find object by value", value, "in", exports, new Error().stack!.substring(5));
+
}
+
+
return ret;
},
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => {
+
let ret = null;
for (const exportKey in exports) {
const obj = exports[exportKey];
// eslint-disable-next-line eqeqeq
if (obj && obj[key] == value) {
+
ret = obj;
+
break;
}
}
+
+
if (ret == null) {
+
logger.warn(
+
"Failed to find object by key value pair",
+
key,
+
value,
+
"in",
+
exports,
+
new Error().stack!.substring(5)
+
);
+
}
+
return null;
},
findFunctionByStrings: (exports: Record<string, any>, ...strings: (string | RegExp)[]) => {
+
const ret =
Object.entries(exports).filter(
([index, func]) =>
+
typeof func === "function" && !strings.some((query) => !testFind(func.toString(), processFind(query)))
+
)?.[0]?.[1] ?? null;
+
+
if (ret == null) {
+
logger.warn("Failed to find function by strings", strings, "in", exports, new Error().stack!.substring(5));
+
}
+
+
return ret;
},
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => {
+
chunk = processFind(chunk);
+
module = processFind(module);
+
const mod = Array.isArray(find) ? spacepack.findByCode(...find) : spacepack.findByCode(find);
+
if (mod.length < 1) {
+
logger.warn("lazyLoad: Module find failed", find, chunk, module, new Error().stack!.substring(5));
+
return Promise.reject("Module find failed");
+
}
const findId = mod[0].id;
const findCode = webpackRequire.m[findId].toString().replace(/\n/g, "");
···
if (match) chunkIds = [...match[0].matchAll(/"(\d+)"/g)].map(([, id]) => id);
}
+
if (!chunkIds || chunkIds.length === 0) {
+
logger.warn("lazyLoad: Chunk ID match failed", find, chunk, module, new Error().stack!.substring(5));
+
return Promise.reject("Chunk ID match failed");
+
}
const moduleId = findCode.match(module)?.[1];
+
if (!moduleId) {
+
logger.warn("lazyLoad: Module ID match failed", find, chunk, module, new Error().stack!.substring(5));
+
return Promise.reject("Module ID match failed");
+
}
return Promise.all(chunkIds.map((c) => webpackRequire.e(c))).then(() => webpackRequire(moduleId));
},
+4 -1
packages/core-extensions/tsconfig.json
···
{
-
"extends": "../../tsconfig.json"
}
···
{
+
"extends": "../../tsconfig.json",
+
"compilerOptions": {
+
"lib": ["ESNext", "DOM", "DOM.Iterable"]
+
}
}
+10 -3
packages/injector/package.json
···
{
"name": "@moonlight-mod/injector",
"private": true,
"dependencies": {
-
"@moonlight-mod/types": "workspace:*",
-
"@moonlight-mod/core": "workspace:*"
-
}
}
···
{
"name": "@moonlight-mod/injector",
"private": true,
+
"engines": {
+
"node": ">=22",
+
"pnpm": ">=10",
+
"npm": "pnpm",
+
"yarn": "pnpm"
+
},
"dependencies": {
+
"@moonlight-mod/core": "workspace:*",
+
"@moonlight-mod/types": "workspace:*"
+
},
+
"engineStrict": true
}
+119 -93
packages/injector/src/index.ts
···
} from "electron";
import Module from "node:module";
import { constants, MoonlightBranch } from "@moonlight-mod/types";
-
import { readConfig } from "@moonlight-mod/core/config";
import { getExtensions } from "@moonlight-mod/core/extension";
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
import EventEmitter from "node:events";
-
import { join, resolve } from "node:path";
import persist from "@moonlight-mod/core/persist";
import createFS from "@moonlight-mod/core/fs";
const logger = new Logger("injector");
let oldPreloadPath: string | undefined;
let corsAllow: string[] = [];
let blockedUrls: RegExp[] = [];
-
let isMoonlightDesktop = false;
-
let hasOpenAsar = false;
-
let openAsarConfigPreload: string | undefined;
ipcMain.on(constants.ipcGetOldPreloadPath, (e) => {
e.returnValue = oldPreloadPath;
});
ipcMain.on(constants.ipcGetAppData, (e) => {
e.returnValue = app.getPath("appData");
});
-
ipcMain.on(constants.ipcGetIsMoonlightDesktop, (e) => {
-
e.returnValue = isMoonlightDesktop;
});
ipcMain.handle(constants.ipcMessageBox, (_, opts) => {
electron.dialog.showMessageBoxSync(opts);
···
blockedUrls = compiled;
});
-
function patchCsp(headers: Record<string, string[]>) {
-
const directives = ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src", "prefetch-src"];
-
const values = ["*", "blob:", "data:", "'unsafe-inline'", "disclip:"];
const csp = "content-security-policy";
if (headers[csp] == null) return;
···
parts[directive] = values;
}
const stringified = Object.entries<string[]>(parts)
.map(([key, value]) => {
return `${key} ${value.join(" ")}`;
···
headers[csp] = [stringified];
}
-
function removeOpenAsarEventIfPresent(eventHandler: (...args: any[]) => void) {
-
const code = eventHandler.toString();
-
if (code.indexOf("bw.webContents.on('dom-ready'") > -1) {
-
electron.app.off("browser-window-created", eventHandler);
-
}
-
}
-
class BrowserWindow extends ElectronBrowserWindow {
constructor(opts: BrowserWindowConstructorOptions) {
-
oldPreloadPath = opts.webPreferences!.preload;
-
const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;
-
if (isMainWindow) opts.webPreferences!.preload = require.resolve("./node-preload.js");
// Event for modifying window options
moonlightHost.events.emit("window-options", opts, isMainWindow);
···
// Event for when a window is created
moonlightHost.events.emit("window-created", this, isMainWindow);
this.webContents.session.webRequest.onHeadersReceived((details, cb) => {
if (details.responseHeaders != null) {
// Patch CSP so things can use externally hosted assets
if (details.resourceType === "mainFrame") {
-
patchCsp(details.responseHeaders);
}
// Allow plugins to bypass CORS for specific URLs
if (corsAllow.some((x) => details.url.startsWith(x))) {
-
details.responseHeaders["access-control-allow-origin"] = ["*"];
}
cb({ cancel: false, responseHeaders: details.responseHeaders });
}
});
-
// Allow plugins to block some URLs,
-
// this is needed because multiple webRequest handlers cannot be registered at once
this.webContents.session.webRequest.onBeforeRequest((details, cb) => {
-
cb({ cancel: blockedUrls.some((u) => u.test(details.url)) });
-
});
-
if (hasOpenAsar) {
-
// Remove DOM injections
-
// Settings can still be opened via:
-
// `DiscordNative.ipc.send("DISCORD_UPDATED_QUOTES","o")`
-
// @ts-expect-error Electron internals
-
const events = electron.app._events["browser-window-created"];
-
if (Array.isArray(events)) {
-
for (const event of events) {
-
removeOpenAsarEventIfPresent(event);
}
-
} else if (events != null) {
-
removeOpenAsarEventIfPresent(events);
-
}
-
// Config screen fails to context bridge properly
-
// Less than ideal, but better than disabling it everywhere
-
if (opts.webPreferences!.preload === openAsarConfigPreload) {
-
opts.webPreferences!.sandbox = false;
}
-
}
}
}
···
writable: false
});
-
export async function inject(asarPath: string) {
-
isMoonlightDesktop = asarPath === "moonlightDesktop";
global.moonlightNodeSandboxed = {
fs: createFS(),
// These aren't supposed to be used from host
-
addCors(url) {},
-
addBlocked(url) {}
};
try {
-
const config = await readConfig();
initLogger(config);
const extensions = await getExtensions();
// Duplicated in node-preload... oops
function getConfig(ext: string) {
···
if (val == null || typeof val === "boolean") return undefined;
return val.config;
}
-
global.moonlightHost = {
asarPath,
-
config,
events: new EventEmitter(),
-
extensions,
-
processedExtensions: {
-
extensions: [],
-
dependencyGraph: new Map()
-
},
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
getConfig,
-
getConfigOption: <T>(ext: string, name: string) => {
-
const config = getConfig(ext);
-
if (config == null) return undefined;
-
const option = config[name];
-
if (option == null) return undefined;
-
return option as T;
},
-
getLogger: (id: string) => {
return new Logger(id);
}
};
-
// Check if we're running with OpenAsar
-
try {
-
require.resolve(join(asarPath, "updater", "updater.js"));
-
hasOpenAsar = true;
-
openAsarConfigPreload = resolve(asarPath, "config", "preload.js");
-
// eslint-disable-next-line no-empty
-
} catch {}
-
-
if (hasOpenAsar) {
-
// Disable command line switch injection
-
// I personally think that the command line switches should be vetted by
-
// the user and not just "trust that these are sane defaults that work
-
// always". I'm not hating on Ducko or anything, I'm just opinionated.
-
// Someone can always make a command line modifier plugin, thats the point
-
// of having host modules.
-
try {
-
const cmdSwitchesPath = require.resolve(join(asarPath, "cmdSwitches.js"));
-
require.cache[cmdSwitchesPath] = new Module(cmdSwitchesPath, require.cache[require.resolve(asarPath)]);
-
require.cache[cmdSwitchesPath]!.exports = () => {};
-
} catch (error) {
-
logger.error("Failed to disable OpenAsar's command line flags:", error);
-
}
-
}
-
patchElectron();
-
global.moonlightHost.processedExtensions = await loadExtensions(extensions);
await loadProcessedExtensions(global.moonlightHost.processedExtensions);
} catch (error) {
logger.error("Failed to inject:", error);
}
-
if (isMoonlightDesktop) return;
-
-
if (!hasOpenAsar && !isMoonlightDesktop) {
persist(asarPath);
}
-
// Need to do this instead of require() or it breaks require.main
-
// @ts-expect-error Module internals
-
Module._load(asarPath, Module, true);
}
function patchElectron() {
···
} from "electron";
import Module from "node:module";
import { constants, MoonlightBranch } from "@moonlight-mod/types";
+
import { readConfig, writeConfig } from "@moonlight-mod/core/config";
import { getExtensions } from "@moonlight-mod/core/extension";
import Logger, { initLogger } from "@moonlight-mod/core/util/logger";
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
import EventEmitter from "node:events";
+
import path from "node:path";
import persist from "@moonlight-mod/core/persist";
import createFS from "@moonlight-mod/core/fs";
+
import { getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
+
import { getConfigPath, getExtensionsPath, getMoonlightDir } from "@moonlight-mod/core/util/data";
const logger = new Logger("injector");
let oldPreloadPath: string | undefined;
let corsAllow: string[] = [];
let blockedUrls: RegExp[] = [];
+
let injectorConfig: InjectorConfig | undefined;
+
+
const scriptUrls = ["web.", "sentry."];
+
const blockedScripts = new Set<string>();
ipcMain.on(constants.ipcGetOldPreloadPath, (e) => {
e.returnValue = oldPreloadPath;
});
+
ipcMain.on(constants.ipcGetAppData, (e) => {
e.returnValue = app.getPath("appData");
});
+
ipcMain.on(constants.ipcGetInjectorConfig, (e) => {
+
e.returnValue = injectorConfig;
});
ipcMain.handle(constants.ipcMessageBox, (_, opts) => {
electron.dialog.showMessageBoxSync(opts);
···
blockedUrls = compiled;
});
+
function patchCsp(headers: Record<string, string[]>, extensionCspOverrides: Record<string, string[]>) {
+
const directives = ["script-src", "style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"];
+
const values = ["*", "blob:", "data:", "'unsafe-inline'", "'unsafe-eval'", "disclip:"];
const csp = "content-security-policy";
if (headers[csp] == null) return;
···
parts[directive] = values;
}
+
for (const [directive, urls] of Object.entries(extensionCspOverrides)) {
+
parts[directive] ??= [];
+
parts[directive].push(...urls);
+
}
+
const stringified = Object.entries<string[]>(parts)
.map(([key, value]) => {
return `${key} ${value.join(" ")}`;
···
headers[csp] = [stringified];
}
class BrowserWindow extends ElectronBrowserWindow {
constructor(opts: BrowserWindowConstructorOptions) {
const isMainWindow = opts.webPreferences!.preload!.indexOf("discord_desktop_core") > -1;
+
if (isMainWindow) {
+
if (!oldPreloadPath) oldPreloadPath = opts.webPreferences!.preload;
+
opts.webPreferences!.preload = require.resolve("./node-preload.js");
+
}
// Event for modifying window options
moonlightHost.events.emit("window-options", opts, isMainWindow);
···
// Event for when a window is created
moonlightHost.events.emit("window-created", this, isMainWindow);
+
const extensionCspOverrides: Record<string, string[]> = {};
+
+
{
+
const extCsps = moonlightHost.processedExtensions.extensions.map((x) => x.manifest.csp ?? {});
+
for (const csp of extCsps) {
+
for (const [directive, urls] of Object.entries(csp)) {
+
extensionCspOverrides[directive] ??= [];
+
extensionCspOverrides[directive].push(...urls);
+
}
+
}
+
}
+
this.webContents.session.webRequest.onHeadersReceived((details, cb) => {
if (details.responseHeaders != null) {
// Patch CSP so things can use externally hosted assets
if (details.resourceType === "mainFrame") {
+
patchCsp(details.responseHeaders, extensionCspOverrides);
}
// Allow plugins to bypass CORS for specific URLs
if (corsAllow.some((x) => details.url.startsWith(x))) {
+
if (!details.responseHeaders) details.responseHeaders = {};
+
+
// Work around HTTP header case sensitivity by reusing the header name if it exists
+
// https://github.com/moonlight-mod/moonlight/issues/201
+
const fallback = "access-control-allow-origin";
+
const key = Object.keys(details.responseHeaders).find((h) => h.toLowerCase() === fallback) ?? fallback;
+
details.responseHeaders[key] = ["*"];
}
+
+
moonlightHost.events.emit("headers-received", details, isMainWindow);
cb({ cancel: false, responseHeaders: details.responseHeaders });
}
});
this.webContents.session.webRequest.onBeforeRequest((details, cb) => {
+
/*
+
In order to get moonlight loading to be truly async, we prevent Discord
+
from loading their scripts immediately. We block the requests, keep note
+
of their URLs, and then send them off to node-preload when we get all of
+
them. node-preload then loads node side, web side, and then recreates
+
the script elements to cause them to re-fetch.
+
+
The browser extension also does this, but in a background script (see
+
packages/browser/src/background.js - we should probably get this working
+
with esbuild someday).
+
*/
+
if (details.resourceType === "script" && isMainWindow) {
+
const url = new URL(details.url);
+
const hasUrl = scriptUrls.some((scriptUrl) => {
+
return (
+
details.url.includes(scriptUrl) &&
+
!url.searchParams.has("inj") &&
+
(url.host.endsWith("discord.com") || url.host.endsWith("discordapp.com"))
+
);
+
});
+
if (hasUrl) blockedScripts.add(details.url);
+
if (blockedScripts.size === scriptUrls.length) {
+
setTimeout(() => {
+
logger.debug("Kicking off node-preload");
+
this.webContents.send(constants.ipcNodePreloadKickoff, Array.from(blockedScripts));
+
blockedScripts.clear();
+
}, 0);
}
+
if (hasUrl) return cb({ cancel: true });
}
+
+
// Allow plugins to block some URLs,
+
// this is needed because multiple webRequest handlers cannot be registered at once
+
cb({ cancel: blockedUrls.some((u) => u.test(details.url)) });
+
});
}
}
···
writable: false
});
+
type InjectorConfig = { disablePersist?: boolean; disableLoad?: boolean };
+
export async function inject(asarPath: string, _injectorConfig?: InjectorConfig) {
+
injectorConfig = _injectorConfig;
+
global.moonlightNodeSandboxed = {
fs: createFS(),
// These aren't supposed to be used from host
+
addCors() {},
+
addBlocked() {}
};
try {
+
let config = await readConfig();
initLogger(config);
const extensions = await getExtensions();
+
const processedExtensions = await loadExtensions(extensions);
+
const moonlightDir = await getMoonlightDir();
+
const extensionsPath = await getExtensionsPath();
// Duplicated in node-preload... oops
function getConfig(ext: string) {
···
if (val == null || typeof val === "boolean") return undefined;
return val.config;
}
global.moonlightHost = {
+
get config() {
+
return config;
+
},
+
extensions,
+
processedExtensions,
asarPath,
events: new EventEmitter(),
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
getConfig,
+
getConfigPath,
+
getConfigOption(ext, name) {
+
const manifest = getManifest(extensions, ext);
+
return getConfigOption(ext, name, config, manifest?.settings);
},
+
setConfigOption(ext, name, value) {
+
setConfigOption(config, ext, name, value);
+
this.writeConfig(config);
+
},
+
async writeConfig(newConfig) {
+
await writeConfig(newConfig);
+
config = newConfig;
+
},
+
+
getLogger(id) {
return new Logger(id);
+
},
+
getMoonlightDir() {
+
return moonlightDir;
+
},
+
getExtensionDir: (ext: string) => {
+
return path.join(extensionsPath, ext);
}
};
patchElectron();
await loadProcessedExtensions(global.moonlightHost.processedExtensions);
} catch (error) {
logger.error("Failed to inject:", error);
}
+
if (injectorConfig?.disablePersist !== true) {
persist(asarPath);
}
+
if (injectorConfig?.disableLoad !== true) {
+
// Need to do this instead of require() or it breaks require.main
+
// @ts-expect-error Module internals
+
Module._load(asarPath, Module, true);
+
}
}
function patchElectron() {
+8 -1
packages/node-preload/package.json
···
{
"name": "@moonlight-mod/node-preload",
"private": true,
"dependencies": {
"@moonlight-mod/core": "workspace:*",
"@moonlight-mod/types": "workspace:*"
-
}
}
···
{
"name": "@moonlight-mod/node-preload",
"private": true,
+
"engines": {
+
"node": ">=22",
+
"pnpm": ">=10",
+
"npm": "pnpm",
+
"yarn": "pnpm"
+
},
"dependencies": {
"@moonlight-mod/core": "workspace:*",
"@moonlight-mod/types": "workspace:*"
+
},
+
"engineStrict": true
}
+86 -23
packages/node-preload/src/index.ts
···
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
import createFS from "@moonlight-mod/core/fs";
import { registerCors, registerBlocked, getDynamicCors } from "@moonlight-mod/core/cors";
let initialized = false;
function setCors() {
const data = getDynamicCors();
···
}
};
-
const config = await readConfig();
initLogger(config);
const extensions = await getExtensions();
const processedExtensions = await loadExtensions(extensions);
const moonlightDir = await getMoonlightDir();
const extensionsPath = await getExtensionsPath();
-
-
function getConfig(ext: string) {
-
const val = config.extensions[ext];
-
if (val == null || typeof val === "boolean") return undefined;
-
return val.config;
-
}
global.moonlightNode = {
-
config,
extensions,
processedExtensions,
nativesCache: {},
isBrowser: false,
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
-
getConfig,
-
getConfigOption: <T>(ext: string, name: string) => {
-
const config = getConfig(ext);
-
if (config == null) return undefined;
-
const option = config[name];
-
if (option == null) return undefined;
-
return option as T;
},
getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
getLogger: (id: string) => {
return new Logger(id);
},
-
getMoonlightDir() {
return moonlightDir;
},
getExtensionDir: (ext: string) => {
return path.join(extensionsPath, ext);
-
},
-
writeConfig
};
await loadProcessedExtensions(processedExtensions);
···
const webPreloadPath = path.join(__dirname, "web-preload.js");
const webPreload = fs.readFileSync(webPreloadPath, "utf8");
await webFrame.executeJavaScript(webPreload);
}
-
async function init(oldPreloadPath: string) {
try {
await injectGlobals();
await loadPreload();
···
message: message
});
}
-
-
// Let Discord start even if we fail
-
if (oldPreloadPath) require(oldPreloadPath);
}
const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
-
init(oldPreloadPath);
···
import { loadExtensions, loadProcessedExtensions } from "@moonlight-mod/core/extension/loader";
import createFS from "@moonlight-mod/core/fs";
import { registerCors, registerBlocked, getDynamicCors } from "@moonlight-mod/core/cors";
+
import { getConfig, getConfigOption, getManifest, setConfigOption } from "@moonlight-mod/core/util/config";
+
import { NodeEventPayloads, NodeEventType } from "@moonlight-mod/types/core/event";
+
import { createEventEmitter } from "@moonlight-mod/core/util/event";
let initialized = false;
+
let logger: Logger;
function setCors() {
const data = getDynamicCors();
···
}
};
+
let config = await readConfig();
initLogger(config);
+
logger = new Logger("node-preload");
+
const extensions = await getExtensions();
const processedExtensions = await loadExtensions(extensions);
const moonlightDir = await getMoonlightDir();
const extensionsPath = await getExtensionsPath();
global.moonlightNode = {
+
get config() {
+
return config;
+
},
extensions,
processedExtensions,
nativesCache: {},
isBrowser: false,
+
events: createEventEmitter<NodeEventType, NodeEventPayloads>(),
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
+
getConfig(ext) {
+
return getConfig(ext, config);
+
},
+
getConfigOption(ext, name) {
+
const manifest = getManifest(extensions, ext);
+
return getConfigOption(ext, name, config, manifest?.settings);
+
},
+
async setConfigOption(ext, name, value) {
+
setConfigOption(config, ext, name, value);
+
await this.writeConfig(config);
},
+
async writeConfig(newConfig) {
+
await writeConfig(newConfig);
+
config = newConfig;
+
this.events.dispatchEvent(NodeEventType.ConfigSaved, newConfig);
+
},
+
getNatives: (ext: string) => global.moonlightNode.nativesCache[ext],
getLogger: (id: string) => {
return new Logger(id);
},
getMoonlightDir() {
return moonlightDir;
},
getExtensionDir: (ext: string) => {
return path.join(extensionsPath, ext);
+
}
};
await loadProcessedExtensions(processedExtensions);
···
const webPreloadPath = path.join(__dirname, "web-preload.js");
const webPreload = fs.readFileSync(webPreloadPath, "utf8");
await webFrame.executeJavaScript(webPreload);
+
+
const func = await webFrame.executeJavaScript("async () => { await window._moonlightWebLoad(); }");
+
await func();
}
+
async function init() {
try {
await injectGlobals();
await loadPreload();
···
message: message
});
}
}
const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
+
const isOverlay = window.location.href.indexOf("discord_overlay") > -1;
+
+
if (isOverlay) {
+
// The overlay has an inline script tag to call to DiscordNative, so we'll
+
// just load it immediately. Somehow moonlight still loads in this env, I
+
// have no idea why - so I suspect it's just forwarding render calls or
+
// something from the original process
+
require(oldPreloadPath);
+
} else {
+
ipcRenderer.on(constants.ipcNodePreloadKickoff, (_, blockedScripts: string[]) => {
+
(async () => {
+
try {
+
await init();
+
logger.debug("Blocked scripts:", blockedScripts);
+
+
const oldPreloadPath: string = ipcRenderer.sendSync(constants.ipcGetOldPreloadPath);
+
logger.debug("Old preload path:", oldPreloadPath);
+
if (oldPreloadPath) require(oldPreloadPath);
+
+
// Do this to get global.DiscordNative assigned
+
// @ts-expect-error Lying to discord_desktop_core
+
process.emit("loaded");
+
+
function replayScripts() {
+
const scripts = [...document.querySelectorAll("script")].filter(
+
(script) => script.src && blockedScripts.some((url) => url.includes(script.src))
+
);
+
+
blockedScripts.reverse();
+
for (const url of blockedScripts) {
+
if (url.includes("/sentry.")) continue;
+
+
const script = scripts.find((script) => url.includes(script.src))!;
+
const newScript = document.createElement("script");
+
for (const attr of script.attributes) {
+
if (attr.name === "src") attr.value += "?inj";
+
newScript.setAttribute(attr.name, attr.value);
+
}
+
script.remove();
+
document.documentElement.appendChild(newScript);
+
}
+
}
+
+
if (document.readyState === "complete") {
+
replayScripts();
+
} else {
+
window.addEventListener("load", replayScripts);
+
}
+
} catch (e) {
+
logger.error("Error restoring original scripts:", e);
+
}
+
})();
+
});
+
}
+4 -1
packages/node-preload/tsconfig.json
···
{
-
"extends": "../../tsconfig.json"
}
···
{
+
"extends": "../../tsconfig.json",
+
"compilerOptions": {
+
"lib": ["DOM", "ESNext", "DOM.Iterable"]
+
}
}
+14 -7
packages/types/package.json
···
{
"name": "@moonlight-mod/types",
-
"version": "1.3.2",
-
"main": "./src/index.ts",
-
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./import": "./src/import.d.ts",
"./*": "./src/*.ts"
},
"dependencies": {
-
"@moonlight-mod/lunast": "^1.0.0",
-
"@moonlight-mod/mappings": "^1.0.9",
-
"@moonlight-mod/moonmap": "^1.0.3",
"@types/react": "^18.3.10",
-
"csstype": "^3.1.2",
"standalone-electron-types": "^1.0.0"
}
}
···
{
"name": "@moonlight-mod/types",
+
"version": "1.3.17",
"exports": {
".": "./src/index.ts",
"./import": "./src/import.d.ts",
"./*": "./src/*.ts"
},
+
"main": "./src/index.ts",
+
"types": "./src/index.ts",
+
"engineStrict": false,
+
"engines": {
+
"node": ">=22",
+
"pnpm": ">=10",
+
"npm": "pnpm",
+
"yarn": "pnpm"
+
},
"dependencies": {
+
"@moonlight-mod/lunast": "^1.0.1",
+
"@moonlight-mod/mappings": "^1.1.25",
+
"@moonlight-mod/moonmap": "^1.0.5",
"@types/react": "^18.3.10",
+
"csstype": "^3.1.3",
"standalone-electron-types": "^1.0.0"
}
}
+48
packages/types/src/config.ts
···
};
export type BooleanSettingType = {
type: ExtensionSettingType.Boolean;
default?: boolean;
};
export type NumberSettingType = {
type: ExtensionSettingType.Number;
default?: number;
min?: number;
···
};
export type StringSettingType = {
type: ExtensionSettingType.String;
default?: string;
};
export type MultilineTextInputSettingType = {
type: ExtensionSettingType.MultilineString;
default?: string;
};
export type SelectSettingType = {
type: ExtensionSettingType.Select;
options: SelectOption[];
default?: string;
};
export type MultiSelectSettingType = {
type: ExtensionSettingType.MultiSelect;
options: string[];
default?: string[];
};
export type ListSettingType = {
type: ExtensionSettingType.List;
default?: string[];
};
export type DictionarySettingType = {
type: ExtensionSettingType.Dictionary;
default?: Record<string, string>;
};
export type CustomSettingType = {
type: ExtensionSettingType.Custom;
default?: any;
};
export type ExtensionSettingsManifest = {
displayName?: string;
description?: string;
} & (
| BooleanSettingType
| NumberSettingType
···
};
export type BooleanSettingType = {
+
/**
+
* Displays as a simple switch.
+
*/
type: ExtensionSettingType.Boolean;
default?: boolean;
};
export type NumberSettingType = {
+
/**
+
* Displays as a simple slider.
+
*/
type: ExtensionSettingType.Number;
default?: number;
min?: number;
···
};
export type StringSettingType = {
+
/**
+
* Displays as a single line string input.
+
*/
type: ExtensionSettingType.String;
default?: string;
};
export type MultilineTextInputSettingType = {
+
/**
+
* Displays as a multiple line string input.
+
*/
type: ExtensionSettingType.MultilineString;
default?: string;
};
export type SelectSettingType = {
+
/**
+
* A dropdown to pick between one of many values.
+
*/
type: ExtensionSettingType.Select;
options: SelectOption[];
default?: string;
};
export type MultiSelectSettingType = {
+
/**
+
* A dropdown to pick multiple values.
+
*/
type: ExtensionSettingType.MultiSelect;
options: string[];
default?: string[];
};
export type ListSettingType = {
+
/**
+
* A list of strings that the user can add or remove from.
+
*/
type: ExtensionSettingType.List;
default?: string[];
};
export type DictionarySettingType = {
+
/**
+
* A dictionary (key-value pair) that the user can add or remove from.
+
*/
type: ExtensionSettingType.Dictionary;
default?: Record<string, string>;
};
export type CustomSettingType = {
+
/**
+
* A custom component.
+
* You can use the registerConfigComponent function in the Moonbase API to register a React component to render here.
+
*/
type: ExtensionSettingType.Custom;
default?: any;
};
+
export enum ExtensionSettingsAdvice {
+
None = "none",
+
Reload = "reload",
+
Restart = "restart"
+
}
+
export type ExtensionSettingsManifest = {
+
/**
+
* A human friendly name for the setting.
+
*/
displayName?: string;
+
+
/**
+
* A longer description for the setting.
+
* Markdown is not supported.
+
*/
description?: string;
+
+
/**
+
* The "advice" to give upon changing this setting.
+
* Can be configured to reload the client, restart the client, or do nothing.
+
*/
+
advice?: ExtensionSettingsAdvice;
} & (
| BooleanSettingType
| NumberSettingType
+3 -1
packages/types/src/constants.ts
···
export const repoUrlFile = ".moonlight-repo-url";
export const installedVersionFile = ".moonlight-installed-version";
export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";
export const ipcGetAppData = "_moonlight_getAppData";
-
export const ipcGetIsMoonlightDesktop = "_moonlight_getIsMoonlightDesktop";
export const ipcMessageBox = "_moonlight_messageBox";
export const ipcSetCorsList = "_moonlight_setCorsList";
export const ipcSetBlockedList = "_moonlight_setBlockedList";
···
export const repoUrlFile = ".moonlight-repo-url";
export const installedVersionFile = ".moonlight-installed-version";
+
export const ipcNodePreloadKickoff = "_moonlight_nodePreloadKickoff";
export const ipcGetOldPreloadPath = "_moonlight_getOldPreloadPath";
+
export const ipcGetAppData = "_moonlight_getAppData";
+
export const ipcGetInjectorConfig = "_moonlight_getInjectorConfig";
export const ipcMessageBox = "_moonlight_messageBox";
export const ipcSetCorsList = "_moonlight_setCorsList";
export const ipcSetBlockedList = "_moonlight_setBlockedList";
+13 -4
packages/types/src/core/event.ts
···
import { WebpackModuleFunc, WebpackRequireType } from "../discord";
export interface MoonlightEventEmitter<EventId extends string = string, EventData = Record<EventId, any>> {
···
removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => void;
}
-
export enum EventType {
ChunkLoad = "chunkLoad",
ExtensionLoad = "extensionLoad"
}
-
export type EventPayloads = {
-
[EventType.ChunkLoad]: {
chunkId?: number[];
modules: { [id: string]: WebpackModuleFunc };
require?: (require: WebpackRequireType) => any;
};
-
[EventType.ExtensionLoad]: string;
};
···
+
import { Config } from "../config";
import { WebpackModuleFunc, WebpackRequireType } from "../discord";
export interface MoonlightEventEmitter<EventId extends string = string, EventData = Record<EventId, any>> {
···
removeEventListener: <Id extends keyof EventData>(id: Id, cb: (data: EventData[Id]) => void) => void;
}
+
export enum WebEventType {
ChunkLoad = "chunkLoad",
ExtensionLoad = "extensionLoad"
}
+
export type WebEventPayloads = {
+
[WebEventType.ChunkLoad]: {
chunkId?: number[];
modules: { [id: string]: WebpackModuleFunc };
require?: (require: WebpackRequireType) => any;
};
+
[WebEventType.ExtensionLoad]: string;
+
};
+
+
export enum NodeEventType {
+
ConfigSaved = "configSaved"
+
}
+
+
export type NodeEventPayloads = {
+
[NodeEventType.ConfigSaved]: Config;
};
+9
packages/types/src/coreExtensions/appPanels.ts
···
export type AppPanels = {
addPanel: (section: string, element: React.FC<any>) => void;
getPanels: (el: React.FC<any>) => React.ReactNode;
};
···
export type AppPanels = {
+
/**
+
* Registers a new panel to be displayed around the user/voice controls.
+
* @param section A unique name for your section
+
* @param element A React component
+
*/
addPanel: (section: string, element: React.FC<any>) => void;
+
+
/**
+
* @private
+
*/
getPanels: (el: React.FC<any>) => React.ReactNode;
};
+204
packages/types/src/coreExtensions/commands.ts
···
···
+
export const APPLICATION_ID = "-3";
+
+
export enum CommandType {
+
CHAT = 1,
+
MESSAGE = 3,
+
PRIMARY_ENTRY_POINT = 4,
+
USER = 2
+
}
+
+
export enum InputType {
+
BOT = 3,
+
BUILT_IN = 0,
+
BUILT_IN_INTEGRATION = 2,
+
BUILT_IN_TEXT = 1,
+
PLACEHOLDER = 4
+
}
+
+
export enum OptionType {
+
SUB_COMMAND = 1,
+
SUB_COMMAND_GROUP = 2,
+
STRING = 3,
+
INTEGER = 4,
+
BOOLEAN = 5,
+
USER = 6,
+
CHANNEL = 7,
+
ROLE = 8,
+
MENTIONABLE = 9,
+
NUMBER = 10,
+
ATTACHMENT = 11
+
}
+
+
export enum ChannelType {
+
GUILD_TEXT = 0,
+
DM = 1,
+
GUILD_VOICE = 2,
+
GROUP_DM = 3,
+
GUILD_CATEGORY = 4,
+
GUILD_ANNOUNCEMENT = 5,
+
GUILD_STORE = 6,
+
ANNOUNCEMENT_THREAD = 10,
+
PUBLIC_THREAD = 11,
+
PRIVATE_THREAD = 12,
+
GUILD_STAGE_VOICE = 13,
+
GUILD_DIRECTORY = 14,
+
GUILD_FORUM = 15,
+
GUILD_MEDIA = 16,
+
LOBBY = 17,
+
DM_SDK = 18
+
}
+
+
export type RegisteredCommandOption = MoonlightCommandOption & {
+
displayName: string;
+
displayDescription: string;
+
};
+
+
export type CommandOptionChoice<T> = {
+
name: string;
+
value: T;
+
};
+
+
type CommandOptionBase<T> = {
+
type: T;
+
name: string;
+
description: string;
+
required?: T extends OptionType.SUB_COMMAND
+
? never
+
: T extends OptionType.SUB_COMMAND_GROUP
+
? never
+
: boolean | undefined;
+
choices?: T extends OptionType.STRING
+
? CommandOptionChoice<string>[]
+
: T extends OptionType.INTEGER
+
? CommandOptionChoice<number>[]
+
: T extends OptionType.NUMBER
+
? CommandOptionChoice<number>[]
+
: never;
+
options?: T extends OptionType.SUB_COMMAND
+
? MoonlightCommandOption[]
+
: T extends OptionType.SUB_COMMAND_GROUP
+
? MoonlightCommandOption[]
+
: never;
+
channelTypes?: T extends OptionType.CHANNEL ? ChannelType[] : never;
+
minValue?: T extends OptionType.INTEGER ? number : T extends OptionType.NUMBER ? number : never;
+
maxValue?: T extends OptionType.INTEGER ? number : T extends OptionType.NUMBER ? number : never;
+
minLength?: T extends OptionType.STRING ? number : never;
+
maxLength?: T extends OptionType.STRING ? number : never;
+
};
+
+
// This is bad lol
+
export type MoonlightCommandOption =
+
| CommandOptionBase<OptionType.SUB_COMMAND>
+
| CommandOptionBase<OptionType.SUB_COMMAND_GROUP>
+
| CommandOptionBase<OptionType.STRING>
+
| CommandOptionBase<OptionType.INTEGER>
+
| CommandOptionBase<OptionType.BOOLEAN>
+
| CommandOptionBase<OptionType.USER>
+
| CommandOptionBase<OptionType.CHANNEL>
+
| CommandOptionBase<OptionType.ROLE>
+
| CommandOptionBase<OptionType.MENTIONABLE>
+
| CommandOptionBase<OptionType.NUMBER>
+
| CommandOptionBase<OptionType.ATTACHMENT>;
+
+
// TODO: types
+
export type CommandPredicateState = {
+
channel: any;
+
guild: any;
+
};
+
+
export type RegisteredCommand = {
+
id: string;
+
untranslatedName: string;
+
displayName: string;
+
type: CommandType;
+
inputType: InputType;
+
applicationId: string; // set to -3!
+
untranslatedDescription: string;
+
displayDescription: string;
+
options?: RegisteredCommandOption[];
+
predicate?: (state: CommandPredicateState) => boolean;
+
execute: (options: CommandOption[]) => void;
+
};
+
+
export type MoonlightCommand = {
+
id: string;
+
description: string;
+
+
/**
+
* You likely want CHAT
+
*/
+
type: CommandType;
+
+
/**
+
* You likely want BUILT_IN (or BUILT_IN_TEXT if usable with replies)
+
*/
+
inputType: InputType;
+
options?: MoonlightCommandOption[];
+
predicate?: (state: CommandPredicateState) => boolean;
+
execute: (options: CommandOption[]) => void;
+
};
+
+
export type CommandOption = {
+
name: string;
+
} & ( // TODO: more of these
+
| {
+
type: Exclude<OptionType, OptionType.STRING>;
+
value: any;
+
}
+
| {
+
type: OptionType.STRING;
+
value: string;
+
}
+
| {
+
type: OptionType.NUMBER | OptionType.INTEGER;
+
value: number;
+
}
+
| {
+
type: OptionType.BOOLEAN;
+
value: boolean;
+
}
+
| {
+
type: OptionType.SUB_COMMAND | OptionType.SUB_COMMAND_GROUP;
+
options: CommandOption[];
+
}
+
);
+
+
export type AnyScopeRegex = RegExp["exec"] & {
+
regex: RegExp;
+
};
+
+
export type Commands = {
+
/**
+
* Register a command in the internal slash command system
+
*/
+
registerCommand: (command: MoonlightCommand) => void;
+
+
/**
+
* Register a legacy command that works via regex
+
*/
+
registerLegacyCommand: (id: string, command: LegacyCommand) => void;
+
+
/**
+
* Creates a regular expression that legacy commands can understand
+
*/
+
anyScopeRegex: (regex: RegExp) => AnyScopeRegex;
+
+
/**
+
* @private
+
*/
+
_getCommands: () => RegisteredCommand[];
+
};
+
+
export type LegacyContext = {
+
channel: any;
+
isEdit: boolean;
+
};
+
+
export type LegacyReturn = {
+
content: string;
+
};
+
+
export type LegacyCommand = {
+
match?: RegExp | { regex: RegExp } | AnyScopeRegex;
+
action: (content: string, context: LegacyContext) => LegacyReturn;
+
};
+33
packages/types/src/coreExtensions/common.ts
···
···
+
import type { IconProps, IconSize } from "@moonlight-mod/mappings/discord/components/common/index";
+
+
export type ErrorBoundaryProps = React.PropsWithChildren<{
+
noop?: boolean;
+
fallback?: React.FC<any>;
+
message?: string;
+
}>;
+
+
export type ErrorBoundaryState = {
+
errored: boolean;
+
error?: Error;
+
componentStack?: string;
+
};
+
+
export type ErrorBoundary = React.ComponentClass<ErrorBoundaryProps, ErrorBoundaryState>;
+
+
export type ParsedIconProps = {
+
width: number;
+
height: number;
+
fill: string;
+
className: string;
+
};
+
+
export interface Icons {
+
/**
+
* Parse icon props into their actual width/height.
+
* @param props The icon props
+
*/
+
parseProps(props?: IconProps): ParsedIconProps;
+
}
+
+
// Re-export so extension developers don't need to depend on mappings
+
export type { IconProps, IconSize };
+162
packages/types/src/coreExtensions/componentEditor.ts
···
···
+
type Patcher<T> = (elements: React.ReactNode[], props: T) => React.ReactNode[];
+
+
//#region DM List
+
export type DMListAnchors =
+
| "content"
+
| "favorite-server-indicator"
+
| "ignored-indicator"
+
| "blocked-indicator"
+
| "close-button"
+
| undefined;
+
export type DMListDecoratorAnchors = "system-tag" | undefined;
+
+
export enum DMListAnchorIndicies {
+
content = 0,
+
"favorite-server-indicator",
+
"ignored-indicator",
+
"blocked-indicator",
+
"close-button"
+
}
+
export enum DMListDecoratorAnchorIndicies {
+
"system-tag" = 0
+
}
+
+
export type DMListItem = {
+
component: React.FC<any>;
+
anchor: DMListAnchors;
+
before: boolean;
+
};
+
export type DMListDecorator = {
+
component: React.FC<any>;
+
anchor: DMListDecoratorAnchors;
+
before: boolean;
+
};
+
+
export type DMList = {
+
addItem: (id: string, component: React.FC<any>, anchor?: DMListAnchors, before?: boolean) => void;
+
addDecorator: (id: string, component: React.FC<any>, anchor?: DMListDecoratorAnchors, before?: boolean) => void;
+
//TODO: fix props type
+
/**
+
* @private
+
*/
+
_patchItems: Patcher<any>;
+
/**
+
* @private
+
*/
+
_patchDecorators: Patcher<any>;
+
};
+
//#endregion
+
+
//#region Member List
+
export type MemberListDecoratorAnchors = "bot-tag" | "owner-crown" | "boost-icon" | undefined;
+
+
export enum MemberListDecoratorAnchorIndicies {
+
"bot-tag" = 0,
+
"owner-crown",
+
"boost-icon"
+
}
+
+
export type MemberListDecorator = {
+
component: React.FC<any>;
+
anchor: MemberListDecoratorAnchors;
+
before: boolean;
+
};
+
+
export type MemberList = {
+
addItem: (id: string, component: React.FC<any>) => void;
+
addDecorator: (id: string, component: React.FC<any>, anchor?: MemberListDecoratorAnchors, before?: boolean) => void;
+
//TODO: fix props type
+
/**
+
* @private
+
*/
+
_patchItems: Patcher<any>;
+
/**
+
* @private
+
*/
+
_patchDecorators: Patcher<any>;
+
};
+
//#endregion
+
+
//#region Messages
+
export type MessageUsernameAnchors = "communication-disabled" | "username" | undefined;
+
export type MessageUsernameBadgeAnchors =
+
| "nitro-author"
+
| "role-icon"
+
| "new-member"
+
| "leaderboard-champion"
+
| "connections"
+
| undefined;
+
export type MessageBadgeAnchors = "silent" | "potion" | undefined;
+
+
export type MessageUsername = {
+
component: React.FC<any>;
+
anchor: MessageUsernameAnchors;
+
before: boolean;
+
};
+
export type MessageUsernameBadge = {
+
component: React.FC<any>;
+
anchor: MessageUsernameBadgeAnchors;
+
before: boolean;
+
};
+
export type MessageBadge = {
+
component: React.FC<any>;
+
anchor: MessageBadgeAnchors;
+
before: boolean;
+
};
+
+
export enum MessageUsernameIndicies {
+
"communication-disabled" = 0,
+
username
+
}
+
export enum MessageUsernameBadgeIndicies {
+
"nitro-author" = 0,
+
"role-icon",
+
"new-member",
+
"leaderboard-champion",
+
connections
+
}
+
export enum MessageBadgeIndicies {
+
silent = 0,
+
potion
+
}
+
+
export type Messages = {
+
/**
+
* Adds a component to the username of a message
+
*/
+
addToUsername: (id: string, component: React.FC<any>, anchor?: MessageUsernameAnchors, before?: boolean) => void;
+
/**
+
* Adds a component to the username badge area of a message (e.g. where role icons/new member badge is)
+
*/
+
addUsernameBadge: (
+
id: string,
+
component: React.FC<any>,
+
anchor?: MessageUsernameBadgeAnchors,
+
before?: boolean
+
) => void;
+
/**
+
* Adds a component to the end of a message header (e.g. silent indicator)
+
*/
+
addBadge: (id: string, component: React.FC<any>, anchor?: MessageBadgeAnchors, before?: boolean) => void;
+
/**
+
* Adds a component to message accessories (e.g. embeds)
+
*/
+
addAccessory: (id: string, component: React.FC<any>) => void;
+
/**
+
* @private
+
*/
+
_patchUsername: Patcher<any>;
+
/**
+
* @private
+
*/
+
_patchUsernameBadges: Patcher<any>;
+
/**
+
* @private
+
*/
+
_patchBadges: Patcher<any>;
+
/**
+
* @private
+
*/
+
_patchAccessories: Patcher<any>;
+
};
+
//#endregion
+8 -1
packages/types/src/coreExtensions/contextMenu.ts
···
} from "@moonlight-mod/mappings/discord/components/common/index";
export type ContextMenu = {
-
addItem: (navId: string, item: (props: any) => MenuElement, anchorId: string, before?: boolean) => void;
MenuCheckboxItem: MenuCheckboxItem;
MenuControlItem: MenuControlItem;
···
} from "@moonlight-mod/mappings/discord/components/common/index";
export type ContextMenu = {
+
/**
+
* Registers a new context menu item for a given context menu type.
+
* @param navId The navigation ID for the target context menu (e.g. "user-context", "message")
+
* @param item A React component
+
* @param anchor An existing item's ID to anchor the new item to
+
* @param before Whether to insert the new item before the anchor item
+
*/
+
addItem: (navId: string, item: React.FC<any>, anchor: string | RegExp, before?: boolean) => void;
MenuCheckboxItem: MenuCheckboxItem;
MenuControlItem: MenuControlItem;
+15
packages/types/src/coreExtensions/markdown.ts
···
slateDecorators: Record<string, string>;
ruleBlacklists: Record<Ruleset, Record<string, boolean>>;
addRule: (
name: string,
markdown: (rules: Record<string, MarkdownRule>) => MarkdownRule,
slate: (rules: Record<string, SlateRule>) => SlateRule,
decorator?: string | undefined
) => void;
blacklistFromRuleset: (ruleset: Ruleset, name: string) => void;
};
···
slateDecorators: Record<string, string>;
ruleBlacklists: Record<Ruleset, Record<string, boolean>>;
+
/**
+
* Registers a new Markdown rule with simple-markdown.
+
* @param name The name of the rule
+
* @param markdown A function that returns simple-markdown rules
+
* @param slate A function that returns Slate rules
+
* @param decorator A decorator name for Slate
+
* @see https://www.npmjs.com/package/simple-markdown#adding-a-simple-extension
+
* @see https://docs.slatejs.org/
+
*/
addRule: (
name: string,
markdown: (rules: Record<string, MarkdownRule>) => MarkdownRule,
slate: (rules: Record<string, SlateRule>) => SlateRule,
decorator?: string | undefined
) => void;
+
+
/**
+
* Blacklist a rule from a ruleset.
+
* @param ruleset The ruleset name
+
* @param name The rule name
+
*/
blacklistFromRuleset: (ruleset: Ruleset, name: string) => void;
};
+11 -2
packages/types/src/coreExtensions/moonbase.ts
···
-
export type CustomComponent = React.FC<{
value: any;
setValue: (value: any) => void;
-
}>;
export type Moonbase = {
registerConfigComponent: (ext: string, option: string, component: CustomComponent) => void;
};
···
+
export type CustomComponentProps = {
value: any;
setValue: (value: any) => void;
+
};
+
+
export type CustomComponent = React.FC<CustomComponentProps>;
export type Moonbase = {
+
/**
+
* Registers a custom component for an extension setting.
+
* The extension setting must be of type "custom".
+
* @param ext The extension ID
+
* @param option The setting ID
+
* @param component A React component
+
*/
registerConfigComponent: (ext: string, option: string, component: CustomComponent) => void;
};
+16 -1
packages/types/src/coreExtensions/notices.ts
···
-
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux";
export type NoticeButton = {
name: string;
···
};
export type Notices = Store<any> & {
addNotice: (notice: Notice) => void;
popNotice: () => void;
getCurrentNotice: () => Notice | null;
shouldShowNotice: () => boolean;
};
···
+
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux/Store";
export type NoticeButton = {
name: string;
···
};
export type Notices = Store<any> & {
+
/**
+
* Adds a custom notice to the top of the screen.
+
*/
addNotice: (notice: Notice) => void;
+
+
/**
+
* Removes the current notice from the top of the screen.
+
*/
popNotice: () => void;
+
+
/**
+
* @private
+
*/
getCurrentNotice: () => Notice | null;
+
+
/**
+
* @private
+
*/
shouldShowNotice: () => boolean;
};
+39 -8
packages/types/src/coreExtensions/settings.ts
···
import React, { ReactElement } from "react";
-
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux";
export type NoticeProps = {
stores: Store<any>[];
···
};
export type SettingsSection =
-
| { section: "DIVIDER"; pos: number }
-
| { section: "HEADER"; label: string; pos: number }
| {
section: string;
label: string;
color: string | null;
element: React.FunctionComponent;
-
pos: number;
notice?: NoticeProps;
_moonlight_submenu?: () => ReactElement | ReactElement[];
};
···
sectionNames: string[];
sectionMenuItems: Record<string, ReactElement[]>;
addSection: (
section: string,
label: string,
element: React.FunctionComponent,
color?: string | null,
-
pos?: number,
-
notice?: NoticeProps
) => void;
addSectionMenuItems: (section: string, ...items: ReactElement[]) => void;
-
addDivider: (pos: number | null) => void;
-
addHeader: (label: string, pos: number | null) => void;
_mutateSections: (sections: SettingsSection[]) => SettingsSection[];
};
···
import React, { ReactElement } from "react";
+
import type { Store } from "@moonlight-mod/mappings/discord/packages/flux/Store";
export type NoticeProps = {
stores: Store<any>[];
···
};
export type SettingsSection =
+
| { section: "DIVIDER"; pos: number | ((sections: SettingsSection[]) => number) }
+
| { section: "HEADER"; label: string; pos: number | ((sections: SettingsSection[]) => number) }
| {
section: string;
label: string;
color: string | null;
element: React.FunctionComponent;
+
pos: number | ((sections: SettingsSection[]) => number);
notice?: NoticeProps;
+
onClick?: () => void;
_moonlight_submenu?: () => ReactElement | ReactElement[];
};
···
sectionNames: string[];
sectionMenuItems: Record<string, ReactElement[]>;
+
/**
+
* Registers a new section in the settings menu.
+
* @param section The section ID
+
* @param label The label for the section
+
* @param element The React component to render
+
* @param color A color to use for the section
+
* @param pos The position in the settings menu to place the section
+
* @param notice A notice to display when in the section
+
* @param onClick A custom action to execute when clicked from the context menu
+
*/
addSection: (
section: string,
label: string,
element: React.FunctionComponent,
color?: string | null,
+
pos?: number | ((sections: SettingsSection[]) => number),
+
notice?: NoticeProps,
+
onClick?: () => void
) => void;
+
+
/**
+
* Adds new items to a section in the settings menu.
+
* @param section The section ID
+
* @param items The React components to render
+
*/
addSectionMenuItems: (section: string, ...items: ReactElement[]) => void;
+
/**
+
* Places a divider in the settings menu.
+
* @param pos The position in the settings menu to place the divider
+
*/
+
addDivider: (pos: number | ((sections: SettingsSection[]) => number) | null) => void;
+
+
/**
+
* Places a header in the settings menu.
+
* @param pos The position in the settings menu to place the header
+
*/
+
addHeader: (label: string, pos: number | ((sections: SettingsSection[]) => number) | null) => void;
+
+
/**
+
* @private
+
*/
_mutateSections: (sections: SettingsSection[]) => SettingsSection[];
};
+58 -5
packages/types/src/coreExtensions/spacepack.ts
···
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "../discord";
-
// Only bothered TSDoc'ing the hard-to-understand functions
-
export type Spacepack = {
inspect: (module: number | string) => WebpackModuleFunc | null;
findByCode: (...args: (string | RegExp)[]) => WebpackModule[];
findByExports: (...args: string[]) => WebpackModule[];
-
// re-export of require
require: WebpackRequireType;
-
// re-export of require.m
modules: Record<string, WebpackModuleFunc>;
-
// re-export of require.c
cache: Record<string, any>;
findObjectFromKey: (exports: Record<string, any>, key: string) => any | null;
findObjectFromValue: (exports: Record<string, any>, value: any) => any | null;
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => any | null;
/**
* Finds a function from a module's exports using the given source find.
* This behaves like findByCode but localized to the exported function.
···
...strings: (string | RegExp)[]
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
) => Function | null;
/**
* Lazy load a Webpack module.
* @param find A list of finds to discover a target module with
···
* @returns The target Webpack module
*/
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => Promise<any>;
/**
* Filter a list of Webpack modules to "real" ones from the Discord client.
* @param modules A list of Webpack modules
···
import { WebpackModule, WebpackModuleFunc, WebpackRequireType } from "../discord";
export type Spacepack = {
+
/**
+
* Given a Webpack module ID, returns the function for the Webpack module.
+
* Can be double clicked to inspect in DevTools.
+
* @param module The module ID
+
* @returns The Webpack module, if found
+
*/
inspect: (module: number | string) => WebpackModuleFunc | null;
+
+
/**
+
* Find Webpack modules based on matches in code.
+
* @param args A list of finds to match against
+
* @returns The Webpack modules, if found
+
*/
findByCode: (...args: (string | RegExp)[]) => WebpackModule[];
+
+
/**
+
* Find Webpack modules based on their exports.
+
* @deprecated This has race conditions. Consider using findByCode instead.
+
* @param args A list of finds to match exports against
+
* @returns The Webpack modules, if found
+
*/
findByExports: (...args: string[]) => WebpackModule[];
+
+
/**
+
* The Webpack require function.
+
*/
require: WebpackRequireType;
+
+
/**
+
* The Webpack module list.
+
* Re-export of require.m.
+
*/
modules: Record<string, WebpackModuleFunc>;
+
+
/**
+
* The Webpack module cache.
+
* Re-export of require.c.
+
*/
cache: Record<string, any>;
+
+
/**
+
* Finds an object from a module's exports using the given key.
+
* @param exports Exports from a Webpack module
+
* @param key The key to find with
+
* @returns The object, if found
+
*/
findObjectFromKey: (exports: Record<string, any>, key: string) => any | null;
+
+
/**
+
* Finds an object from a module's exports using the given value.
+
* @param exports Exports from a Webpack module
+
* @param value The value to find with
+
* @returns The object, if found
+
*/
findObjectFromValue: (exports: Record<string, any>, value: any) => any | null;
+
+
/**
+
* Finds an object from a module's exports using the given key-value pair.
+
* @param exports Exports from a Webpack module
+
* @param key The key to find with
+
* @param value The value to find with
+
* @returns The object, if found
+
*/
findObjectFromKeyValuePair: (exports: Record<string, any>, key: string, value: any) => any | null;
+
/**
* Finds a function from a module's exports using the given source find.
* This behaves like findByCode but localized to the exported function.
···
...strings: (string | RegExp)[]
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
) => Function | null;
+
/**
* Lazy load a Webpack module.
* @param find A list of finds to discover a target module with
···
* @returns The target Webpack module
*/
lazyLoad: (find: string | RegExp | (string | RegExp)[], chunk: RegExp, module: RegExp) => Promise<any>;
+
/**
* Filter a list of Webpack modules to "real" ones from the Discord client.
* @param modules A list of Webpack modules
+3
packages/types/src/coreExtensions.ts
···
export * as Notices from "./coreExtensions/notices";
export * as Moonbase from "./coreExtensions/moonbase";
export * as AppPanels from "./coreExtensions/appPanels";
···
export * as Notices from "./coreExtensions/notices";
export * as Moonbase from "./coreExtensions/moonbase";
export * as AppPanels from "./coreExtensions/appPanels";
+
export * as Commands from "./coreExtensions/commands";
+
export * as ComponentEditor from "./coreExtensions/componentEditor";
+
export * as Common from "./coreExtensions/common";
+12
packages/types/src/discord/require.ts
···
import { AppPanels } from "../coreExtensions/appPanels";
import { ContextMenu, EvilItemParser } from "../coreExtensions/contextMenu";
import { Markdown } from "../coreExtensions/markdown";
import { Moonbase } from "../coreExtensions/moonbase";
···
declare function WebpackRequire(id: string): any;
declare function WebpackRequire(id: "appPanels_appPanels"): AppPanels;
declare function WebpackRequire(id: "contextMenu_evilMenu"): EvilItemParser;
declare function WebpackRequire(id: "contextMenu_contextMenu"): ContextMenu;
···
import { AppPanels } from "../coreExtensions/appPanels";
+
import { Commands } from "../coreExtensions/commands";
+
import { ErrorBoundary, Icons } from "../coreExtensions/common";
+
import { DMList, MemberList, Messages } from "../coreExtensions/componentEditor";
import { ContextMenu, EvilItemParser } from "../coreExtensions/contextMenu";
import { Markdown } from "../coreExtensions/markdown";
import { Moonbase } from "../coreExtensions/moonbase";
···
declare function WebpackRequire(id: string): any;
declare function WebpackRequire(id: "appPanels_appPanels"): AppPanels;
+
+
declare function WebpackRequire(id: "commands_commands"): Commands;
+
+
declare function WebpackRequire(id: "common_ErrorBoundary"): ErrorBoundary;
+
declare function WebpackRequire(id: "common_icons"): Icons;
+
+
declare function WebpackRequire(id: "componentEditor_dmList"): DMList;
+
declare function WebpackRequire(id: "componentEditor_memberList"): MemberList;
+
declare function WebpackRequire(id: "componentEditor_messages"): Messages;
declare function WebpackRequire(id: "contextMenu_evilMenu"): EvilItemParser;
declare function WebpackRequire(id: "contextMenu_contextMenu"): ContextMenu;
+103 -1
packages/types/src/extension.ts
···
};
export type ExtensionManifest = {
id: string;
version?: string;
apiLevel?: number;
environment?: ExtensionEnvironment;
meta?: {
name?: string;
tagline?: string;
description?: string;
authors?: ExtensionAuthor[];
-
deprecated?: boolean;
tags?: ExtensionTag[];
source?: string;
};
dependencies?: string[];
suggested?: string[];
incompatible?: string[];
settings?: Record<string, ExtensionSettingsManifest>;
cors?: string[];
blocked?: string[];
};
export enum ExtensionEnvironment {
Both = "both",
Desktop = "desktop",
Web = "web"
}
···
export type Patch = {
find: PatchMatch;
replace: PatchReplace | PatchReplace[];
prerequisite?: () => boolean;
};
···
};
export type ExtensionManifest = {
+
$schema?: string;
+
+
/**
+
* A unique identifier for your extension.
+
*/
id: string;
+
+
/**
+
* A version string for your extension - doesn't need to follow a specific format. Required for publishing.
+
*/
version?: string;
+
+
/**
+
* The API level this extension targets. If it does not match the current version, the extension will not be loaded.
+
*/
apiLevel?: number;
+
+
/**
+
* Which environment this extension is capable of running in.
+
*/
environment?: ExtensionEnvironment;
+
/**
+
* Metadata about your extension for use in Moonbase.
+
*/
meta?: {
+
/**
+
* A human friendly name for your extension as a proper noun.
+
*/
name?: string;
+
+
/**
+
* A short tagline that appears below the name.
+
*/
tagline?: string;
+
+
/**
+
* A longer description that can use Markdown.
+
*/
description?: string;
+
+
/**
+
* List of authors that worked on this extension - accepts string or object with ID.
+
*/
authors?: ExtensionAuthor[];
+
+
/**
+
* A list of tags that are relevant to the extension.
+
*/
tags?: ExtensionTag[];
+
+
/**
+
* The URL to the source repository.
+
*/
source?: string;
+
+
/**
+
* A donation link (or other method of support). If you don't want financial contributions, consider putting your favorite charity here!
+
*/
+
donate?: string;
+
+
/**
+
* A changelog to show in Moonbase.
+
* Moonbase will show the changelog for the latest version, even if it is not installed.
+
*/
+
changelog?: string;
+
+
/**
+
* Whether the extension is deprecated and no longer receiving updates.
+
*/
+
deprecated?: boolean;
};
+
/**
+
* A list of extension IDs that are required for the extension to load.
+
*/
dependencies?: string[];
+
+
/**
+
* A list of extension IDs that the user may want to install.
+
*/
suggested?: string[];
+
+
/**
+
* A list of extension IDs that the extension is incompatible with.
+
* If two incompatible extensions are enabled, one of them will not load.
+
*/
incompatible?: string[];
+
/**
+
* A list of settings for your extension, where the key is the settings ID.
+
*/
settings?: Record<string, ExtensionSettingsManifest>;
+
/**
+
* A list of URLs to bypass CORS for.
+
* This is implemented by checking if the start of the URL matches.
+
* @example https://moonlight-mod.github.io/
+
*/
cors?: string[];
+
+
/**
+
* A list of URLs to block all requests to.
+
* This is implemented by checking if the start of the URL matches.
+
* @example https://moonlight-mod.github.io/
+
*/
blocked?: string[];
+
+
/**
+
* A mapping from CSP directives to URLs to allow.
+
* @example { "script-src": ["https://example.com"] }
+
*/
+
csp?: Record<string, string[]>;
};
export enum ExtensionEnvironment {
+
/**
+
* The extension will run on both platforms, the host/native modules MAY be loaded
+
*/
Both = "both",
+
+
/**
+
* Extension will run on desktop only, the host/native modules are guaranteed to load
+
*/
Desktop = "desktop",
+
+
/**
+
* Currently equivalent to Both
+
*/
Web = "web"
}
···
export type Patch = {
find: PatchMatch;
replace: PatchReplace | PatchReplace[];
+
hardFail?: boolean; // if any patches fail, all fail
prerequisite?: () => boolean;
};
+1
packages/types/src/fs.ts
···
exists: (path: string) => Promise<boolean>;
isFile: (path: string) => Promise<boolean>;
join: (...parts: string[]) => string;
dirname: (path: string) => string;
···
exists: (path: string) => Promise<boolean>;
isFile: (path: string) => Promise<boolean>;
+
isDir: (path: string) => Promise<boolean>;
join: (...parts: string[]) => string;
dirname: (path: string) => string;
+31 -10
packages/types/src/globals.ts
···
import type EventEmitter from "events";
import type LunAST from "@moonlight-mod/lunast";
import type Moonmap from "@moonlight-mod/moonmap";
-
import type { EventPayloads, EventType, MoonlightEventEmitter } from "./core/event";
-
import { MoonlightFS } from "./fs";
export type MoonlightHost = {
-
asarPath: string;
config: Config;
-
events: EventEmitter;
extensions: DetectedExtension[];
processedExtensions: ProcessedExtensions;
version: string;
branch: MoonlightBranch;
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
getLogger: (id: string) => Logger;
};
export type MoonlightNode = {
···
processedExtensions: ProcessedExtensions;
nativesCache: Record<string, any>;
isBrowser: boolean;
version: string;
branch: MoonlightBranch;
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
getNatives: (ext: string) => any | undefined;
getLogger: (id: string) => Logger;
-
getMoonlightDir: () => string;
getExtensionDir: (ext: string) => string;
-
writeConfig: (config: Config) => Promise<void>;
};
export type MoonlightNodeSandboxed = {
···
};
export type MoonlightWeb = {
unpatched: Set<IdentifiedPatch>;
pendingModules: Set<IdentifiedWebpackModule>;
enabledExtensions: Set<string>;
-
apiLevel: number;
-
events: MoonlightEventEmitter<EventType, EventPayloads>;
patchingInternals: {
onModuleLoad: (moduleId: string | string[], callback: (moduleId: string) => void) => void;
registerPatch: (patch: IdentifiedPatch) => void;
registerWebpackModule: (module: IdentifiedWebpackModule) => void;
};
version: string;
branch: MoonlightBranch;
-
getConfig: (ext: string) => ConfigExtension["config"];
-
getConfigOption: <T>(ext: string, name: string) => T | undefined;
getNatives: (ext: string) => any | undefined;
getLogger: (id: string) => Logger;
lunast: LunAST;
moonmap: Moonmap;
};
···
import type EventEmitter from "events";
import type LunAST from "@moonlight-mod/lunast";
import type Moonmap from "@moonlight-mod/moonmap";
+
import type {
+
WebEventPayloads,
+
WebEventType,
+
MoonlightEventEmitter,
+
NodeEventType,
+
NodeEventPayloads
+
} from "./core/event";
+
import type { MoonlightFS } from "./fs";
export type MoonlightHost = {
config: Config;
extensions: DetectedExtension[];
processedExtensions: ProcessedExtensions;
+
asarPath: string;
+
events: EventEmitter;
version: string;
branch: MoonlightBranch;
getConfig: (ext: string) => ConfigExtension["config"];
+
getConfigPath: () => Promise<string>;
getConfigOption: <T>(ext: string, name: string) => T | undefined;
+
setConfigOption: <T>(ext: string, name: string, value: T) => void;
+
writeConfig: (config: Config) => Promise<void>;
+
getLogger: (id: string) => Logger;
+
getMoonlightDir: () => string;
+
getExtensionDir: (ext: string) => string;
};
export type MoonlightNode = {
···
processedExtensions: ProcessedExtensions;
nativesCache: Record<string, any>;
isBrowser: boolean;
+
events: MoonlightEventEmitter<NodeEventType, NodeEventPayloads>;
version: string;
branch: MoonlightBranch;
getConfig: (ext: string) => ConfigExtension["config"];
getConfigOption: <T>(ext: string, name: string) => T | undefined;
+
setConfigOption: <T>(ext: string, name: string, value: T) => Promise<void>;
+
writeConfig: (config: Config) => Promise<void>;
+
getNatives: (ext: string) => any | undefined;
getLogger: (id: string) => Logger;
getMoonlightDir: () => string;
getExtensionDir: (ext: string) => string;
};
export type MoonlightNodeSandboxed = {
···
};
export type MoonlightWeb = {
+
patched: Map<string, Set<string>>;
unpatched: Set<IdentifiedPatch>;
pendingModules: Set<IdentifiedWebpackModule>;
enabledExtensions: Set<string>;
+
events: MoonlightEventEmitter<WebEventType, WebEventPayloads>;
patchingInternals: {
onModuleLoad: (moduleId: string | string[], callback: (moduleId: string) => void) => void;
registerPatch: (patch: IdentifiedPatch) => void;
registerWebpackModule: (module: IdentifiedWebpackModule) => void;
};
+
localStorage: Storage;
version: string;
branch: MoonlightBranch;
+
apiLevel: number;
+
// Re-exports for ease of use
+
getConfig: MoonlightNode["getConfig"];
+
getConfigOption: MoonlightNode["getConfigOption"];
+
setConfigOption: MoonlightNode["setConfigOption"];
+
writeConfig: MoonlightNode["writeConfig"];
+
getNatives: (ext: string) => any | undefined;
getLogger: (id: string) => Logger;
+
lunast: LunAST;
moonmap: Moonmap;
};
+32
packages/types/src/import.d.ts
···
export = AppPanels;
}
declare module "@moonlight-mod/wp/common_stores";
declare module "@moonlight-mod/wp/contextMenu_evilMenu" {
import { CoreExtensions } from "@moonlight-mod/types";
···
export = AppPanels;
}
+
declare module "@moonlight-mod/wp/commands_commands" {
+
import { CoreExtensions } from "@moonlight-mod/types";
+
export const commands: CoreExtensions.Commands.Commands;
+
export default commands;
+
}
+
+
declare module "@moonlight-mod/wp/common_ErrorBoundary" {
+
import { CoreExtensions } from "@moonlight-mod/types";
+
const ErrorBoundary: CoreExtensions.Common.ErrorBoundary;
+
export = ErrorBoundary;
+
}
+
declare module "@moonlight-mod/wp/common_icons" {
+
import { CoreExtensions } from "@moonlight-mod/types";
+
export const icons: CoreExtensions.Common.Icons;
+
export default icons;
+
}
declare module "@moonlight-mod/wp/common_stores";
+
+
declare module "@moonlight-mod/wp/componentEditor_dmList" {
+
import { CoreExtensions } from "@moonlight-mod/types";
+
export const dmList: CoreExtensions.ComponentEditor.DMList;
+
export default dmList;
+
}
+
declare module "@moonlight-mod/wp/componentEditor_memberList" {
+
import { CoreExtensions } from "@moonlight-mod/types";
+
export const memberList: CoreExtensions.ComponentEditor.MemberList;
+
export default memberList;
+
}
+
declare module "@moonlight-mod/wp/componentEditor_messages" {
+
import { CoreExtensions } from "@moonlight-mod/types";
+
export const message: CoreExtensions.ComponentEditor.Messages;
+
export default message;
+
}
declare module "@moonlight-mod/wp/contextMenu_evilMenu" {
import { CoreExtensions } from "@moonlight-mod/types";
+3 -2
packages/types/src/index.ts
···
var moonlightNode: MoonlightNode;
var moonlightNodeSandboxed: MoonlightNodeSandboxed;
var moonlight: MoonlightWeb;
-
var _moonlightBrowserInit: () => Promise<void>;
-
var _moonlightBrowserLoad: () => Promise<void>;
}
···
var moonlightNode: MoonlightNode;
var moonlightNodeSandboxed: MoonlightNodeSandboxed;
var moonlight: MoonlightWeb;
+
var _moonlight_coreExtensionsStr: string;
+
var _moonlightBrowserInit: undefined | (() => Promise<void>);
+
var _moonlightWebLoad: undefined | (() => Promise<void>);
}
+841 -25
packages/types/src/mappings.d.ts
···
// auto-generated
declare module "@moonlight-mod/wp/discord/Dispatcher" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/Dispatcher"];
-
export = _;
}
declare module "@moonlight-mod/wp/discord/actions/ContextMenuActionCreators" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/actions/ContextMenuActionCreators"];
-
export = _;
}
declare module "@moonlight-mod/wp/discord/components/common/index" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/components/common/index"];
-
export = _;
}
-
declare module "@moonlight-mod/wp/discord/modules/guild_settings/IntegrationCard.css" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/modules/guild_settings/IntegrationCard.css"];
-
export = _;
}
declare module "@moonlight-mod/wp/discord/modules/markup/MarkupUtils" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/modules/markup/MarkupUtils"];
-
export = _;
}
-
declare module "@moonlight-mod/wp/discord/modules/user_settings/web/openUserSettings" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/modules/user_settings/web/openUserSettings"];
-
export = _;
}
declare module "@moonlight-mod/wp/discord/packages/flux" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/packages/flux"];
-
export = _;
}
declare module "@moonlight-mod/wp/discord/uikit/Flex" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/uikit/Flex"];
-
export = _;
}
declare module "@moonlight-mod/wp/discord/utils/ClipboardUtils" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/utils/ClipboardUtils"];
-
export = _;
}
declare module "@moonlight-mod/wp/discord/utils/HTTPUtils" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/utils/HTTPUtils"];
-
export = _;
}
declare module "@moonlight-mod/wp/discord/utils/NativeUtils" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["discord/utils/NativeUtils"];
-
export = _;
}
declare module "@moonlight-mod/wp/react" {
import { MappedModules } from "@moonlight-mod/mappings";
-
const _: MappedModules["react"];
export = _;
}
···
// auto-generated
+
declare module "@moonlight-mod/wp/chroma-js" {}
+
+
declare module "@moonlight-mod/wp/classnames" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["classnames"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/dependency-graph" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const DepGraph: MappedModules["dependency-graph"]["DepGraph"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/Constants" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const ActivityFlags: MappedModules["discord/Constants"]["ActivityFlags"];
+
export const ActivityTypes: MappedModules["discord/Constants"]["ActivityTypes"];
+
export const AnalyticsLocations: MappedModules["discord/Constants"]["AnalyticsLocations"];
+
export const ChannelLayouts: MappedModules["discord/Constants"]["ChannelLayouts"];
+
export const ChannelModes: MappedModules["discord/Constants"]["ChannelModes"];
+
export const ChannelTypes: MappedModules["discord/Constants"]["ChannelTypes"];
+
export const ChannelStreamTypes: MappedModules["discord/Constants"]["ChannelStreamTypes"];
+
export const ComponentActions: MappedModules["discord/Constants"]["ComponentActions"];
+
export const DEFAULT_ROLE_COLOR: MappedModules["discord/Constants"]["DEFAULT_ROLE_COLOR"];
+
export const Endpoints: MappedModules["discord/Constants"]["Endpoints"];
+
export const MessageFlags: MappedModules["discord/Constants"]["MessageFlags"];
+
export const MessageTypes: MappedModules["discord/Constants"]["MessageTypes"];
+
export const Permissions: MappedModules["discord/Constants"]["Permissions"];
+
export const PlatformTypes: MappedModules["discord/Constants"]["PlatformTypes"];
+
export const RelationshipTypes: MappedModules["discord/Constants"]["RelationshipTypes"];
+
export const Routes: MappedModules["discord/Constants"]["Routes"];
+
export const StatusTypes: MappedModules["discord/Constants"]["StatusTypes"];
+
export const Themes: MappedModules["discord/Constants"]["Themes"];
+
export const UserSettingsSections: MappedModules["discord/Constants"]["UserSettingsSections"];
+
export const UserFlags: MappedModules["discord/Constants"]["UserFlags"];
+
}
+
declare module "@moonlight-mod/wp/discord/Dispatcher" {
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/Dispatcher"]["default"];
+
export default _default;
}
declare module "@moonlight-mod/wp/discord/actions/ContextMenuActionCreators" {
import { MappedModules } from "@moonlight-mod/mappings";
+
export const closeContextMenu: MappedModules["discord/actions/ContextMenuActionCreators"]["closeContextMenu"];
+
export const openContextMenu: MappedModules["discord/actions/ContextMenuActionCreators"]["openContextMenu"];
+
export const openContextMenuLazy: MappedModules["discord/actions/ContextMenuActionCreators"]["openContextMenuLazy"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/actions/UserSettingsModalActionCreators" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/actions/UserSettingsModalActionCreators"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/common/AppStartPerformance" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/common/AppStartPerformance"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/components/common/Alerts" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/components/common/Alerts"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/components/common/BaseHeaderBar" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const Icon: MappedModules["discord/components/common/BaseHeaderBar"]["Icon"];
+
export const Divider: MappedModules["discord/components/common/BaseHeaderBar"]["Divider"];
+
const _default: MappedModules["discord/components/common/BaseHeaderBar"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/components/common/Card" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/components/common/Card"]["default"];
+
export default _default;
+
export const Types: MappedModules["discord/components/common/Card"]["Types"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/components/common/FileUpload" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/components/common/FileUpload"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/components/common/FormSwitch.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const container: MappedModules["discord/components/common/FormSwitch.css"]["container"];
+
export const labelRow: MappedModules["discord/components/common/FormSwitch.css"]["labelRow"];
+
export const control: MappedModules["discord/components/common/FormSwitch.css"]["control"];
+
export const disabled: MappedModules["discord/components/common/FormSwitch.css"]["disabled"];
+
export const title: MappedModules["discord/components/common/FormSwitch.css"]["title"];
+
export const note: MappedModules["discord/components/common/FormSwitch.css"]["note"];
+
export const disabledText: MappedModules["discord/components/common/FormSwitch.css"]["disabledText"];
+
export const dividerDefault: MappedModules["discord/components/common/FormSwitch.css"]["dividerDefault"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/components/common/HeaderBar.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const caret: MappedModules["discord/components/common/HeaderBar.css"]["caret"];
+
export const children: MappedModules["discord/components/common/HeaderBar.css"]["children"];
+
export const clickable: MappedModules["discord/components/common/HeaderBar.css"]["clickable"];
+
export const container: MappedModules["discord/components/common/HeaderBar.css"]["container"];
+
export const divider: MappedModules["discord/components/common/HeaderBar.css"]["divider"];
+
export const dot: MappedModules["discord/components/common/HeaderBar.css"]["dot"];
+
export const hamburger: MappedModules["discord/components/common/HeaderBar.css"]["hamburger"];
+
export const icon: MappedModules["discord/components/common/HeaderBar.css"]["icon"];
+
export const iconBadge: MappedModules["discord/components/common/HeaderBar.css"]["iconBadge"];
+
export const iconBadgeBottom: MappedModules["discord/components/common/HeaderBar.css"]["iconBadgeBottom"];
+
export const iconBadgeTop: MappedModules["discord/components/common/HeaderBar.css"]["iconBadgeTop"];
+
export const iconWrapper: MappedModules["discord/components/common/HeaderBar.css"]["iconWrapper"];
+
export const scrollable: MappedModules["discord/components/common/HeaderBar.css"]["scrollable"];
+
export const selected: MappedModules["discord/components/common/HeaderBar.css"]["selected"];
+
export const themed: MappedModules["discord/components/common/HeaderBar.css"]["themed"];
+
export const themedMobile: MappedModules["discord/components/common/HeaderBar.css"]["themedMobile"];
+
export const title: MappedModules["discord/components/common/HeaderBar.css"]["title"];
+
export const titleWrapper: MappedModules["discord/components/common/HeaderBar.css"]["titleWrapper"];
+
export const toolbar: MappedModules["discord/components/common/HeaderBar.css"]["toolbar"];
+
export const transparent: MappedModules["discord/components/common/HeaderBar.css"]["transparent"];
+
export const upperContainer: MappedModules["discord/components/common/HeaderBar.css"]["upperContainer"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/components/common/HelpMessage.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const container: MappedModules["discord/components/common/HelpMessage.css"]["container"];
+
export const icon: MappedModules["discord/components/common/HelpMessage.css"]["icon"];
+
export const iconDiv: MappedModules["discord/components/common/HelpMessage.css"]["iconDiv"];
+
export const text: MappedModules["discord/components/common/HelpMessage.css"]["text"];
+
export const positive: MappedModules["discord/components/common/HelpMessage.css"]["positive"];
+
export const warning: MappedModules["discord/components/common/HelpMessage.css"]["warning"];
+
export const info: MappedModules["discord/components/common/HelpMessage.css"]["info"];
+
export const error: MappedModules["discord/components/common/HelpMessage.css"]["error"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/components/common/Image" {}
+
+
declare module "@moonlight-mod/wp/discord/components/common/PanelButton" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/components/common/PanelButton"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/components/common/Scroller.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const auto: MappedModules["discord/components/common/Scroller.css"]["auto"];
+
export const content: MappedModules["discord/components/common/Scroller.css"]["content"];
+
export const customTheme: MappedModules["discord/components/common/Scroller.css"]["customTheme"];
+
export const disableScrollAnchor: MappedModules["discord/components/common/Scroller.css"]["disableScrollAnchor"];
+
export const fade: MappedModules["discord/components/common/Scroller.css"]["fade"];
+
export const managedReactiveScroller: MappedModules["discord/components/common/Scroller.css"]["managedReactiveScroller"];
+
export const none: MappedModules["discord/components/common/Scroller.css"]["none"];
+
export const pointerCover: MappedModules["discord/components/common/Scroller.css"]["pointerCover"];
+
export const scrolling: MappedModules["discord/components/common/Scroller.css"]["scrolling"];
+
export const thin: MappedModules["discord/components/common/Scroller.css"]["thin"];
}
declare module "@moonlight-mod/wp/discord/components/common/index" {
import { MappedModules } from "@moonlight-mod/mappings";
+
export const Clickable: MappedModules["discord/components/common/index"]["Clickable"];
+
export const TextInput: MappedModules["discord/components/common/index"]["TextInput"];
+
export const TextArea: MappedModules["discord/components/common/index"]["TextArea"];
+
export const FormDivider: MappedModules["discord/components/common/index"]["FormDivider"];
+
export const FormSection: MappedModules["discord/components/common/index"]["FormSection"];
+
export const FormText: MappedModules["discord/components/common/index"]["FormText"];
+
export const FormTitle: MappedModules["discord/components/common/index"]["FormTitle"];
+
export const FormSwitch: MappedModules["discord/components/common/index"]["FormSwitch"];
+
export const FormItem: MappedModules["discord/components/common/index"]["FormItem"];
+
export const Slider: MappedModules["discord/components/common/index"]["Slider"];
+
export const Switch: MappedModules["discord/components/common/index"]["Switch"];
+
export const Button: MappedModules["discord/components/common/index"]["Button"];
+
export const Tooltip: MappedModules["discord/components/common/index"]["Tooltip"];
+
export const Avatar: MappedModules["discord/components/common/index"]["Avatar"];
+
export const AvatarSizes: MappedModules["discord/components/common/index"]["AvatarSizes"];
+
export const AvatarSizeSpecs: MappedModules["discord/components/common/index"]["AvatarSizeSpecs"];
+
export const Scroller: MappedModules["discord/components/common/index"]["Scroller"];
+
export const Text: MappedModules["discord/components/common/index"]["Text"];
+
export const Heading: MappedModules["discord/components/common/index"]["Heading"];
+
export const Card: MappedModules["discord/components/common/index"]["Card"];
+
export const Popout: MappedModules["discord/components/common/index"]["Popout"];
+
export const Dialog: MappedModules["discord/components/common/index"]["Dialog"];
+
export const Menu: MappedModules["discord/components/common/index"]["Menu"];
+
export const TabBar: MappedModules["discord/components/common/index"]["TabBar"];
+
export const SingleSelect: MappedModules["discord/components/common/index"]["SingleSelect"];
+
export const Select: MappedModules["discord/components/common/index"]["Select"];
+
export const NoticeColors: MappedModules["discord/components/common/index"]["NoticeColors"];
+
export const Notice: MappedModules["discord/components/common/index"]["Notice"];
+
export const NoticeCloseButton: MappedModules["discord/components/common/index"]["NoticeCloseButton"];
+
export const PrimaryCTANoticeButton: MappedModules["discord/components/common/index"]["PrimaryCTANoticeButton"];
+
export const Breadcrumbs: MappedModules["discord/components/common/index"]["Breadcrumbs"];
+
export const Image: MappedModules["discord/components/common/index"]["Image"];
+
export const tokens: MappedModules["discord/components/common/index"]["tokens"];
+
export const useVariableSelect: MappedModules["discord/components/common/index"]["useVariableSelect"];
+
export const useMultiSelect: MappedModules["discord/components/common/index"]["useMultiSelect"];
+
export const multiSelect: MappedModules["discord/components/common/index"]["multiSelect"];
+
export const openModal: MappedModules["discord/components/common/index"]["openModal"];
+
export const openModalLazy: MappedModules["discord/components/common/index"]["openModalLazy"];
+
export const closeModal: MappedModules["discord/components/common/index"]["closeModal"];
+
export const AngleBracketsIcon: MappedModules["discord/components/common/index"]["AngleBracketsIcon"];
+
export const ArrowAngleLeftUpIcon: MappedModules["discord/components/common/index"]["ArrowAngleLeftUpIcon"];
+
export const ArrowAngleRightUpIcon: MappedModules["discord/components/common/index"]["ArrowAngleRightUpIcon"];
+
export const ArrowsUpDownIcon: MappedModules["discord/components/common/index"]["ArrowsUpDownIcon"];
+
export const BookCheckIcon: MappedModules["discord/components/common/index"]["BookCheckIcon"];
+
export const ChannelListIcon: MappedModules["discord/components/common/index"]["ChannelListIcon"];
+
export const ChevronSmallDownIcon: MappedModules["discord/components/common/index"]["ChevronSmallDownIcon"];
+
export const ChevronSmallUpIcon: MappedModules["discord/components/common/index"]["ChevronSmallUpIcon"];
+
export const CircleInformationIcon: MappedModules["discord/components/common/index"]["CircleInformationIcon"];
+
export const CircleWarningIcon: MappedModules["discord/components/common/index"]["CircleWarningIcon"];
+
export const CircleXIcon: MappedModules["discord/components/common/index"]["CircleXIcon"];
+
export const ClydeIcon: MappedModules["discord/components/common/index"]["ClydeIcon"];
+
export const CopyIcon: MappedModules["discord/components/common/index"]["CopyIcon"];
+
export const DownloadIcon: MappedModules["discord/components/common/index"]["DownloadIcon"];
+
export const FullscreenEnterIcon: MappedModules["discord/components/common/index"]["FullscreenEnterIcon"];
+
export const GameControllerIcon: MappedModules["discord/components/common/index"]["GameControllerIcon"];
+
export const GlobeEarthIcon: MappedModules["discord/components/common/index"]["GlobeEarthIcon"];
+
export const HeartIcon: MappedModules["discord/components/common/index"]["HeartIcon"];
+
export const LinkIcon: MappedModules["discord/components/common/index"]["LinkIcon"];
+
export const MaximizeIcon: MappedModules["discord/components/common/index"]["MaximizeIcon"];
+
export const MinusIcon: MappedModules["discord/components/common/index"]["MinusIcon"];
+
export const MobilePhoneIcon: MappedModules["discord/components/common/index"]["MobilePhoneIcon"];
+
export const PauseIcon: MappedModules["discord/components/common/index"]["PauseIcon"];
+
export const PlayIcon: MappedModules["discord/components/common/index"]["PlayIcon"];
+
export const PlusLargeIcon: MappedModules["discord/components/common/index"]["PlusLargeIcon"];
+
export const RetryIcon: MappedModules["discord/components/common/index"]["RetryIcon"];
+
export const ScienceIcon: MappedModules["discord/components/common/index"]["ScienceIcon"];
+
export const ScreenIcon: MappedModules["discord/components/common/index"]["ScreenIcon"];
+
export const StarIcon: MappedModules["discord/components/common/index"]["StarIcon"];
+
export const TrashIcon: MappedModules["discord/components/common/index"]["TrashIcon"];
+
export const WarningIcon: MappedModules["discord/components/common/index"]["WarningIcon"];
+
export const WindowLaunchIcon: MappedModules["discord/components/common/index"]["WindowLaunchIcon"];
+
export const WindowTopOutlineIcon: MappedModules["discord/components/common/index"]["WindowTopOutlineIcon"];
+
export const XLargeIcon: MappedModules["discord/components/common/index"]["XLargeIcon"];
+
export const XSmallIcon: MappedModules["discord/components/common/index"]["XSmallIcon"];
+
export const ConfirmModal: MappedModules["discord/components/common/index"]["ConfirmModal"];
+
export const H: MappedModules["discord/components/common/index"]["H"];
+
export const HelpMessage: MappedModules["discord/components/common/index"]["HelpMessage"];
+
export const ModalCloseButton: MappedModules["discord/components/common/index"]["ModalCloseButton"];
+
export const ModalContent: MappedModules["discord/components/common/index"]["ModalContent"];
+
export const ModalFooter: MappedModules["discord/components/common/index"]["ModalFooter"];
+
export const ModalHeader: MappedModules["discord/components/common/index"]["ModalHeader"];
+
export const ModalRoot: MappedModules["discord/components/common/index"]["ModalRoot"];
+
export const NumberInputStepper: MappedModules["discord/components/common/index"]["NumberInputStepper"];
+
export const SearchableSelect: MappedModules["discord/components/common/index"]["SearchableSelect"];
+
export const createToast: MappedModules["discord/components/common/index"]["createToast"];
+
export const popToast: MappedModules["discord/components/common/index"]["popToast"];
+
export const showToast: MappedModules["discord/components/common/index"]["showToast"];
+
export const useThemeContext: MappedModules["discord/components/common/index"]["useThemeContext"];
+
export const AccessibilityAnnouncer: MappedModules["discord/components/common/index"]["AccessibilityAnnouncer"];
+
export const BackdropStyles: MappedModules["discord/components/common/index"]["BackdropStyles"];
+
export const BadgeShapes: MappedModules["discord/components/common/index"]["BadgeShapes"];
+
export const CardTypes: MappedModules["discord/components/common/index"]["CardTypes"];
+
export const CircleIconButtonColors: MappedModules["discord/components/common/index"]["CircleIconButtonColors"];
+
export const CircleIconButtonSizes: MappedModules["discord/components/common/index"]["CircleIconButtonSizes"];
+
export const FormErrorBlockColors: MappedModules["discord/components/common/index"]["FormErrorBlockColors"];
+
export const FormNoticeImagePositions: MappedModules["discord/components/common/index"]["FormNoticeImagePositions"];
+
export const FormTitleTags: MappedModules["discord/components/common/index"]["FormTitleTags"];
+
export const HelpMessageTypes: MappedModules["discord/components/common/index"]["HelpMessageTypes"];
+
export const ModalSize: MappedModules["discord/components/common/index"]["ModalSize"];
+
export const ModalTransitionState: MappedModules["discord/components/common/index"]["ModalTransitionState"];
+
export const PRETTY_KEYS: MappedModules["discord/components/common/index"]["PRETTY_KEYS"];
+
export const SelectLooks: MappedModules["discord/components/common/index"]["SelectLooks"];
+
export const SpinnerTypes: MappedModules["discord/components/common/index"]["SpinnerTypes"];
+
export const StatusTypes: MappedModules["discord/components/common/index"]["StatusTypes"];
+
export const ToastPosition: MappedModules["discord/components/common/index"]["ToastPosition"];
+
export const ToastType: MappedModules["discord/components/common/index"]["ToastType"];
+
export const TransitionStates: MappedModules["discord/components/common/index"]["TransitionStates"];
+
export const DEFAULT_MODAL_CONTEXT: MappedModules["discord/components/common/index"]["DEFAULT_MODAL_CONTEXT"];
+
export const LOW_SATURATION_THRESHOLD: MappedModules["discord/components/common/index"]["LOW_SATURATION_THRESHOLD"];
+
export const LayerClassName: MappedModules["discord/components/common/index"]["LayerClassName"];
+
export const POPOUT_MODAL_CONTEXT: MappedModules["discord/components/common/index"]["POPOUT_MODAL_CONTEXT"];
}
+
declare module "@moonlight-mod/wp/discord/components/modals/ConfirmModal" {
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/components/modals/ConfirmModal"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/lib/BaseRecord" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/lib/BaseRecord"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/lib/web/Storage" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const ObjectStorage: MappedModules["discord/lib/web/Storage"]["ObjectStorage"];
+
export const impl: MappedModules["discord/lib/web/Storage"]["impl"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/build_overrides/web/BuildOverride.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const wrapper: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["wrapper"];
+
export const titleRegion: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["titleRegion"];
+
export const title: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["title"];
+
export const infoIcon: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["infoIcon"];
+
export const copyLink: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["copyLink"];
+
export const copied: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["copied"];
+
export const copyLinkIcon: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["copyLinkIcon"];
+
export const content: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["content"];
+
export const infoLink: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["infoLink"];
+
export const buildInfo: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buildInfo"];
+
export const button: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["button"];
+
export const buttonSize: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buttonSize"];
+
export const subHead: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["subHead"];
+
export const icon: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["icon"];
+
export const buildDetails: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buildDetails"];
+
export const barLoader: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["barLoader"];
+
export const barTitle: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["barTitle"];
+
export const buttonLoader: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["buttonLoader"];
+
export const disabledButtonOverride: MappedModules["discord/modules/build_overrides/web/BuildOverride.css"]["disabledButtonOverride"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/discovery/web/Discovery.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const header: MappedModules["discord/modules/discovery/web/Discovery.css"]["header"];
+
export const headerImage: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerImage"];
+
export const headerImageSimple: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerImageSimple"];
+
export const headerImageBG: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerImageBG"];
+
export const searchTitle: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchTitle"];
+
export const searchSubtitle: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchSubtitle"];
+
export const headerContentWrapper: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerContentWrapper"];
+
export const headerContent: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerContent"];
+
export const headerContentSmall: MappedModules["discord/modules/discovery/web/Discovery.css"]["headerContentSmall"];
+
export const searchBox: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchBox"];
+
export const searchBoxInput: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchBoxInput"];
+
export const closeIcon: MappedModules["discord/modules/discovery/web/Discovery.css"]["closeIcon"];
+
export const searchIcon: MappedModules["discord/modules/discovery/web/Discovery.css"]["searchIcon"];
+
export const tabBar: MappedModules["discord/modules/discovery/web/Discovery.css"]["tabBar"];
+
export const tabBarItem: MappedModules["discord/modules/discovery/web/Discovery.css"]["tabBarItem"];
+
export const sectionHeader: MappedModules["discord/modules/discovery/web/Discovery.css"]["sectionHeader"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/Forums.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const container: MappedModules["discord/modules/forums/web/Forums.css"]["container"];
+
export const uploadArea: MappedModules["discord/modules/forums/web/Forums.css"]["uploadArea"];
+
export const label: MappedModules["discord/modules/forums/web/Forums.css"]["label"];
+
export const content: MappedModules["discord/modules/forums/web/Forums.css"]["content"];
+
export const noListContainer: MappedModules["discord/modules/forums/web/Forums.css"]["noListContainer"];
+
export const list: MappedModules["discord/modules/forums/web/Forums.css"]["list"];
+
export const grid: MappedModules["discord/modules/forums/web/Forums.css"]["grid"];
+
export const headerRow: MappedModules["discord/modules/forums/web/Forums.css"]["headerRow"];
+
export const card: MappedModules["discord/modules/forums/web/Forums.css"]["card"];
+
export const columnsSpan: MappedModules["discord/modules/forums/web/Forums.css"]["columnsSpan"];
+
export const emptyStateRow: MappedModules["discord/modules/forums/web/Forums.css"]["emptyStateRow"];
+
export const newMemberBanner: MappedModules["discord/modules/forums/web/Forums.css"]["newMemberBanner"];
+
export const gridViewBanner: MappedModules["discord/modules/forums/web/Forums.css"]["gridViewBanner"];
+
export const placeholder: MappedModules["discord/modules/forums/web/Forums.css"]["placeholder"];
+
export const mainCard: MappedModules["discord/modules/forums/web/Forums.css"]["mainCard"];
+
export const emptyMainCard: MappedModules["discord/modules/forums/web/Forums.css"]["emptyMainCard"];
+
export const outOfDate: MappedModules["discord/modules/forums/web/Forums.css"]["outOfDate"];
+
export const header: MappedModules["discord/modules/forums/web/Forums.css"]["header"];
+
export const matchingPostsRow: MappedModules["discord/modules/forums/web/Forums.css"]["matchingPostsRow"];
+
export const headerWithMatchingPosts: MappedModules["discord/modules/forums/web/Forums.css"]["headerWithMatchingPosts"];
+
export const noForm: MappedModules["discord/modules/forums/web/Forums.css"]["noForm"];
+
export const sortContainer: MappedModules["discord/modules/forums/web/Forums.css"]["sortContainer"];
+
export const sort: MappedModules["discord/modules/forums/web/Forums.css"]["sort"];
+
export const sortPopout: MappedModules["discord/modules/forums/web/Forums.css"]["sortPopout"];
+
export const archivedDividerRow: MappedModules["discord/modules/forums/web/Forums.css"]["archivedDividerRow"];
+
export const archivedDivider: MappedModules["discord/modules/forums/web/Forums.css"]["archivedDivider"];
+
export const newPostsButton: MappedModules["discord/modules/forums/web/Forums.css"]["newPostsButton"];
+
export const loadingCard: MappedModules["discord/modules/forums/web/Forums.css"]["loadingCard"];
+
export const enterIcon: MappedModules["discord/modules/forums/web/Forums.css"]["enterIcon"];
+
export const warnIcon: MappedModules["discord/modules/forums/web/Forums.css"]["warnIcon"];
+
export const searchIcon: MappedModules["discord/modules/forums/web/Forums.css"]["searchIcon"];
+
export const missingReadHistoryPermission: MappedModules["discord/modules/forums/web/Forums.css"]["missingReadHistoryPermission"];
+
export const divider: MappedModules["discord/modules/forums/web/Forums.css"]["divider"];
+
export const tagsContainer: MappedModules["discord/modules/forums/web/Forums.css"]["tagsContainer"];
+
export const filterIcon: MappedModules["discord/modules/forums/web/Forums.css"]["filterIcon"];
+
export const tagList: MappedModules["discord/modules/forums/web/Forums.css"]["tagList"];
+
export const tagListInner: MappedModules["discord/modules/forums/web/Forums.css"]["tagListInner"];
+
export const tag: MappedModules["discord/modules/forums/web/Forums.css"]["tag"];
+
export const tagsButton: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButton"];
+
export const tagsButtonInner: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButtonInner"];
+
export const tagsButtonPlaceholder: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButtonPlaceholder"];
+
export const tagsButtonWithCount: MappedModules["discord/modules/forums/web/Forums.css"]["tagsButtonWithCount"];
+
export const sortDropdown: MappedModules["discord/modules/forums/web/Forums.css"]["sortDropdown"];
+
export const sortDropdownInner: MappedModules["discord/modules/forums/web/Forums.css"]["sortDropdownInner"];
+
export const sortDropdownText: MappedModules["discord/modules/forums/web/Forums.css"]["sortDropdownText"];
+
export const clear: MappedModules["discord/modules/forums/web/Forums.css"]["clear"];
+
export const matchingPosts: MappedModules["discord/modules/forums/web/Forums.css"]["matchingPosts"];
+
export const startPostHelp: MappedModules["discord/modules/forums/web/Forums.css"]["startPostHelp"];
+
export const tagsSpacer: MappedModules["discord/modules/forums/web/Forums.css"]["tagsSpacer"];
+
export const keyboardShortcut: MappedModules["discord/modules/forums/web/Forums.css"]["keyboardShortcut"];
+
export const key: MappedModules["discord/modules/forums/web/Forums.css"]["key"];
+
export const countContainer: MappedModules["discord/modules/forums/web/Forums.css"]["countContainer"];
+
export const countText: MappedModules["discord/modules/forums/web/Forums.css"]["countText"];
+
export const optInNotice: MappedModules["discord/modules/forums/web/Forums.css"]["optInNotice"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/Header.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const container: MappedModules["discord/modules/forums/web/Header.css"]["container"];
+
export const header: MappedModules["discord/modules/forums/web/Header.css"]["header"];
+
export const headerLeft: MappedModules["discord/modules/forums/web/Header.css"]["headerLeft"];
+
export const headerText: MappedModules["discord/modules/forums/web/Header.css"]["headerText"];
+
export const countContainer: MappedModules["discord/modules/forums/web/Header.css"]["countContainer"];
+
export const countText: MappedModules["discord/modules/forums/web/Header.css"]["countText"];
+
export const tagContainer: MappedModules["discord/modules/forums/web/Header.css"]["tagContainer"];
+
export const tag: MappedModules["discord/modules/forums/web/Header.css"]["tag"];
+
export const clear: MappedModules["discord/modules/forums/web/Header.css"]["clear"];
+
export const row: MappedModules["discord/modules/forums/web/Header.css"]["row"];
+
export const separator: MappedModules["discord/modules/forums/web/Header.css"]["separator"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/SortMenu.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const container: MappedModules["discord/modules/forums/web/SortMenu.css"]["container"];
+
export const clearText: MappedModules["discord/modules/forums/web/SortMenu.css"]["clearText"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/forums/web/Tag" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/modules/forums/web/Tag"]["default"];
+
export default _default;
+
export const TagBar: MappedModules["discord/modules/forums/web/Tag"]["TagBar"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const addButton: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["addButton"];
+
export const container: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["container"];
+
export const emptyRowContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["emptyRowContainer"];
+
export const emptyRowText: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["emptyRowText"];
+
export const headerContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["headerContainer"];
+
export const list: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["list"];
+
export const memberDetails: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["memberDetails"];
+
export const memberRow: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["memberRow"];
+
export const removeButton: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeButton"];
+
export const removeButtonContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeButtonContainer"];
+
export const removeButtonDisabled: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeButtonDisabled"];
+
export const removeTip: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["removeTip"];
+
export const searchContainer: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["searchContainer"];
+
export const searchWarning: MappedModules["discord/modules/guild_settings/roles/web/GuildSettingsRoleEdit.css"]["searchWarning"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/web/AppCard.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const card: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["card"];
+
export const inModal: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["inModal"];
+
export const cardHeader: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["cardHeader"];
+
export const title: MappedModules["discord/modules/guild_settings/web/AppCard.css"]["title"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/web/AppCardItem.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const icon: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["icon"];
+
export const identifier: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["identifier"];
+
export const item: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["item"];
+
export const statusContainer: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["statusContainer"];
+
export const statusLine: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["statusLine"];
+
export const statusIcon: MappedModules["discord/modules/guild_settings/web/AppCardItem.css"]["statusIcon"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/guild_settings/web/SearchSection.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const container: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["container"];
+
export const headerContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["headerContainer"];
+
export const searchContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["searchContainer"];
+
export const searchWarning: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["searchWarning"];
+
export const addButton: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["addButton"];
+
export const memberRow: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["memberRow"];
+
export const emptyRowContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["emptyRowContainer"];
+
export const emptyRowText: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["emptyRowText"];
+
export const memberDetails: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["memberDetails"];
+
export const list: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["list"];
+
export const removeButtonContainer: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeButtonContainer"];
+
export const removeButton: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeButton"];
+
export const removeButtonDisabled: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeButtonDisabled"];
+
export const removeTip: MappedModules["discord/modules/guild_settings/web/SearchSection.css"]["removeTip"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/guild_sidebar/web/CategoryChannel.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const containerDefault: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["containerDefault"];
+
export const containerDragBefore: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["containerDragBefore"];
+
export const containerDragAfter: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["containerDragAfter"];
+
export const addButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["addButton"];
+
export const forceVisible: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["forceVisible"];
+
export const iconVisibility: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["iconVisibility"];
+
export const addButtonIcon: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["addButtonIcon"];
+
export const wrapper: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["wrapper"];
+
export const wrapperStatic: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["wrapperStatic"];
+
export const clickable: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["clickable"];
+
export const children: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["children"];
+
export const mainContent: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["mainContent"];
+
export const icon: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["icon"];
+
export const collapsed: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["collapsed"];
+
export const muted: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["muted"];
+
export const name: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["name"];
+
export const dismissWrapper: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["dismissWrapper"];
+
export const dismissButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["dismissButton"];
+
export const dismiss: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["dismiss"];
+
export const voiceChannelsButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["voiceChannelsButton"];
+
export const voiceChannelsToggleIcon: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["voiceChannelsToggleIcon"];
+
export const refreshVoiceChannelsButton: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["refreshVoiceChannelsButton"];
+
export const refreshVoiceChannelsButtonInner: MappedModules["discord/modules/guild_sidebar/web/CategoryChannel.css"]["refreshVoiceChannelsButtonInner"];
}
declare module "@moonlight-mod/wp/discord/modules/markup/MarkupUtils" {
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/modules/markup/MarkupUtils"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/menus/web/Menu" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const MenuSpinner: MappedModules["discord/modules/menus/web/Menu"]["MenuSpinner"];
+
export const Menu: MappedModules["discord/modules/menus/web/Menu"]["Menu"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/messages/web/Markup.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const markup: MappedModules["discord/modules/messages/web/Markup.css"]["markup"];
+
export const inlineFormat: MappedModules["discord/modules/messages/web/Markup.css"]["inlineFormat"];
+
export const codeContainer: MappedModules["discord/modules/messages/web/Markup.css"]["codeContainer"];
+
export const codeActions: MappedModules["discord/modules/messages/web/Markup.css"]["codeActions"];
+
export const blockquoteContainer: MappedModules["discord/modules/messages/web/Markup.css"]["blockquoteContainer"];
+
export const blockquoteDivider: MappedModules["discord/modules/messages/web/Markup.css"]["blockquoteDivider"];
+
export const slateBlockquoteContainer: MappedModules["discord/modules/messages/web/Markup.css"]["slateBlockquoteContainer"];
+
export const roleMention: MappedModules["discord/modules/messages/web/Markup.css"]["roleMention"];
+
export const rolePopout: MappedModules["discord/modules/messages/web/Markup.css"]["rolePopout"];
+
export const roleHeader: MappedModules["discord/modules/messages/web/Markup.css"]["roleHeader"];
+
export const roleScroller: MappedModules["discord/modules/messages/web/Markup.css"]["roleScroller"];
+
export const timestamp: MappedModules["discord/modules/messages/web/Markup.css"]["timestamp"];
+
export const timestampTooltip: MappedModules["discord/modules/messages/web/Markup.css"]["timestampTooltip"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/messages/web/Message.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const wrapper: MappedModules["discord/modules/messages/web/Message.css"]["wrapper"];
+
export const compact: MappedModules["discord/modules/messages/web/Message.css"]["compact"];
+
export const cozy: MappedModules["discord/modules/messages/web/Message.css"]["cozy"];
+
export const contentOnly: MappedModules["discord/modules/messages/web/Message.css"]["contentOnly"];
+
export const repliedMessage: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessage"];
+
export const threadMessageAccessory: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessory"];
+
export const executedCommand: MappedModules["discord/modules/messages/web/Message.css"]["executedCommand"];
+
export const latin12CompactTimeStamp: MappedModules["discord/modules/messages/web/Message.css"]["latin12CompactTimeStamp"];
+
export const latin24CompactTimeStamp: MappedModules["discord/modules/messages/web/Message.css"]["latin24CompactTimeStamp"];
+
export const asianCompactTimeStamp: MappedModules["discord/modules/messages/web/Message.css"]["asianCompactTimeStamp"];
+
export const contextCommandMessage: MappedModules["discord/modules/messages/web/Message.css"]["contextCommandMessage"];
+
export const messageSpine: MappedModules["discord/modules/messages/web/Message.css"]["messageSpine"];
+
export const repliedMessageClickableSpine: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessageClickableSpine"];
+
export const repliedMessageContentHovered: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessageContentHovered"];
+
export const threadMessageAccessoryAvatar: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryAvatar"];
+
export const replyAvatar: MappedModules["discord/modules/messages/web/Message.css"]["replyAvatar"];
+
export const replyBadge: MappedModules["discord/modules/messages/web/Message.css"]["replyBadge"];
+
export const executedCommandAvatar: MappedModules["discord/modules/messages/web/Message.css"]["executedCommandAvatar"];
+
export const replyChatIconContainer: MappedModules["discord/modules/messages/web/Message.css"]["replyChatIconContainer"];
+
export const replyIcon: MappedModules["discord/modules/messages/web/Message.css"]["replyIcon"];
+
export const clanTagChiplet: MappedModules["discord/modules/messages/web/Message.css"]["clanTagChiplet"];
+
export const userJoinSystemMessageIcon: MappedModules["discord/modules/messages/web/Message.css"]["userJoinSystemMessageIcon"];
+
export const ticketIcon: MappedModules["discord/modules/messages/web/Message.css"]["ticketIcon"];
+
export const commandIcon: MappedModules["discord/modules/messages/web/Message.css"]["commandIcon"];
+
export const username: MappedModules["discord/modules/messages/web/Message.css"]["username"];
+
export const roleDot: MappedModules["discord/modules/messages/web/Message.css"]["roleDot"];
+
export const commandName: MappedModules["discord/modules/messages/web/Message.css"]["commandName"];
+
export const appsIcon: MappedModules["discord/modules/messages/web/Message.css"]["appsIcon"];
+
export const appLauncherOnboardingCommandName: MappedModules["discord/modules/messages/web/Message.css"]["appLauncherOnboardingCommandName"];
+
export const targetUsername: MappedModules["discord/modules/messages/web/Message.css"]["targetUsername"];
+
export const executedCommandSeparator: MappedModules["discord/modules/messages/web/Message.css"]["executedCommandSeparator"];
+
export const repliedTextPreview: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextPreview"];
+
export const threadMessageAccessoryPreview: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryPreview"];
+
export const repliedTextContent: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextContent"];
+
export const clickable: MappedModules["discord/modules/messages/web/Message.css"]["clickable"];
+
export const repliedMessageClickableSpineHovered: MappedModules["discord/modules/messages/web/Message.css"]["repliedMessageClickableSpineHovered"];
+
export const threadMessageAccessoryContent: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryContent"];
+
export const repliedTextPlaceholder: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextPlaceholder"];
+
export const threadMessageAccessoryPlaceholder: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryPlaceholder"];
+
export const repliedTextContentTrailingIcon: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextContentTrailingIcon"];
+
export const threadMessageAccessoryContentTrailingIcon: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryContentTrailingIcon"];
+
export const repliedTextContentLeadingIcon: MappedModules["discord/modules/messages/web/Message.css"]["repliedTextContentLeadingIcon"];
+
export const threadMessageAccessoryContentLeadingIcon: MappedModules["discord/modules/messages/web/Message.css"]["threadMessageAccessoryContentLeadingIcon"];
+
export const contents: MappedModules["discord/modules/messages/web/Message.css"]["contents"];
+
export const zalgo: MappedModules["discord/modules/messages/web/Message.css"]["zalgo"];
+
export const messageContent: MappedModules["discord/modules/messages/web/Message.css"]["messageContent"];
+
export const header: MappedModules["discord/modules/messages/web/Message.css"]["header"];
+
export const buttonContainer: MappedModules["discord/modules/messages/web/Message.css"]["buttonContainer"];
+
export const avatar: MappedModules["discord/modules/messages/web/Message.css"]["avatar"];
+
export const avatarDecoration: MappedModules["discord/modules/messages/web/Message.css"]["avatarDecoration"];
+
export const roleIcon: MappedModules["discord/modules/messages/web/Message.css"]["roleIcon"];
+
export const timestamp: MappedModules["discord/modules/messages/web/Message.css"]["timestamp"];
+
export const timestampInline: MappedModules["discord/modules/messages/web/Message.css"]["timestampInline"];
+
export const alt: MappedModules["discord/modules/messages/web/Message.css"]["alt"];
+
export const timestampTooltip: MappedModules["discord/modules/messages/web/Message.css"]["timestampTooltip"];
+
export const timestampVisibleOnHover: MappedModules["discord/modules/messages/web/Message.css"]["timestampVisibleOnHover"];
+
export const nitroAuthorBadgeTootip: MappedModules["discord/modules/messages/web/Message.css"]["nitroAuthorBadgeTootip"];
+
export const headerText: MappedModules["discord/modules/messages/web/Message.css"]["headerText"];
+
export const hasRoleIcon: MappedModules["discord/modules/messages/web/Message.css"]["hasRoleIcon"];
+
export const hasBadges: MappedModules["discord/modules/messages/web/Message.css"]["hasBadges"];
+
export const botTagCompact: MappedModules["discord/modules/messages/web/Message.css"]["botTagCompact"];
+
export const botTagCozy: MappedModules["discord/modules/messages/web/Message.css"]["botTagCozy"];
+
export const nitroBadgeSvg: MappedModules["discord/modules/messages/web/Message.css"]["nitroBadgeSvg"];
+
export const nitroAuthorBadgeContainer: MappedModules["discord/modules/messages/web/Message.css"]["nitroAuthorBadgeContainer"];
+
export const separator: MappedModules["discord/modules/messages/web/Message.css"]["separator"];
+
export const hasThread: MappedModules["discord/modules/messages/web/Message.css"]["hasThread"];
+
export const isSystemMessage: MappedModules["discord/modules/messages/web/Message.css"]["isSystemMessage"];
+
export const hasReply: MappedModules["discord/modules/messages/web/Message.css"]["hasReply"];
+
export const markupRtl: MappedModules["discord/modules/messages/web/Message.css"]["markupRtl"];
+
export const isSending: MappedModules["discord/modules/messages/web/Message.css"]["isSending"];
+
export const isFailed: MappedModules["discord/modules/messages/web/Message.css"]["isFailed"];
+
export const isUnsupported: MappedModules["discord/modules/messages/web/Message.css"]["isUnsupported"];
+
export const edited: MappedModules["discord/modules/messages/web/Message.css"]["edited"];
+
export const communicationDisabled: MappedModules["discord/modules/messages/web/Message.css"]["communicationDisabled"];
+
export const compactCommunicationDisabled: MappedModules["discord/modules/messages/web/Message.css"]["compactCommunicationDisabled"];
+
export const communicationDisabledOpacity: MappedModules["discord/modules/messages/web/Message.css"]["communicationDisabledOpacity"];
+
export const badgesContainer: MappedModules["discord/modules/messages/web/Message.css"]["badgesContainer"];
}
+
declare module "@moonlight-mod/wp/discord/modules/modals/Modals" {
import { MappedModules } from "@moonlight-mod/mappings";
+
export const closeAllModals: MappedModules["discord/modules/modals/Modals"]["closeAllModals"];
+
export const closeAllModalsForContext: MappedModules["discord/modules/modals/Modals"]["closeAllModalsForContext"];
+
export const closeModal: MappedModules["discord/modules/modals/Modals"]["closeModal"];
+
export const getInteractingModalContext: MappedModules["discord/modules/modals/Modals"]["getInteractingModalContext"];
+
export const hasAnyModalOpen: MappedModules["discord/modules/modals/Modals"]["hasAnyModalOpen"];
+
export const hasAnyModalOpenSelector: MappedModules["discord/modules/modals/Modals"]["hasAnyModalOpenSelector"];
+
export const hasModalOpen: MappedModules["discord/modules/modals/Modals"]["hasModalOpen"];
+
export const hasModalOpenSelector: MappedModules["discord/modules/modals/Modals"]["hasModalOpenSelector"];
+
export const openModal: MappedModules["discord/modules/modals/Modals"]["openModal"];
+
export const openModalLazy: MappedModules["discord/modules/modals/Modals"]["openModalLazy"];
+
export const updateModal: MappedModules["discord/modules/modals/Modals"]["updateModal"];
+
export const useHasAnyModalOpen: MappedModules["discord/modules/modals/Modals"]["useHasAnyModalOpen"];
+
export const useIsModalAtTop: MappedModules["discord/modules/modals/Modals"]["useIsModalAtTop"];
+
export const useModalsStore: MappedModules["discord/modules/modals/Modals"]["useModalsStore"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/oauth2/index" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const OAuth2AuthorizeModal: MappedModules["discord/modules/oauth2/index"]["OAuth2AuthorizeModal"];
+
export const OAuth2AuthorizePage: MappedModules["discord/modules/oauth2/index"]["OAuth2AuthorizePage"];
+
export const getOAuth2AuthorizeProps: MappedModules["discord/modules/oauth2/index"]["getOAuth2AuthorizeProps"];
+
export const openOAuth2Modal: MappedModules["discord/modules/oauth2/index"]["openOAuth2Modal"];
+
export const openOAuth2ModalWithCreateGuildModal: MappedModules["discord/modules/oauth2/index"]["openOAuth2ModalWithCreateGuildModal"];
+
export const useOAuth2AuthorizeForm: MappedModules["discord/modules/oauth2/index"]["useOAuth2AuthorizeForm"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/people/web/PeoplePage.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const addFriend: MappedModules["discord/modules/people/web/PeoplePage.css"]["addFriend"];
+
export const badge: MappedModules["discord/modules/people/web/PeoplePage.css"]["badge"];
+
export const container: MappedModules["discord/modules/people/web/PeoplePage.css"]["container"];
+
export const inviteToolbar: MappedModules["discord/modules/people/web/PeoplePage.css"]["inviteToolbar"];
+
export const item: MappedModules["discord/modules/people/web/PeoplePage.css"]["item"];
+
export const nowPlayingColumn: MappedModules["discord/modules/people/web/PeoplePage.css"]["nowPlayingColumn"];
+
export const peopleColumn: MappedModules["discord/modules/people/web/PeoplePage.css"]["peopleColumn"];
+
export const tabBar: MappedModules["discord/modules/people/web/PeoplePage.css"]["tabBar"];
+
export const tabBody: MappedModules["discord/modules/people/web/PeoplePage.css"]["tabBody"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/modules/user_profile/web/BiteSizeActivity.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const header: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["header"];
+
export const headerTag: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["headerTag"];
+
export const body: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["body"];
+
export const footer: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["footer"];
+
export const backdrop: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["backdrop"];
+
export const toast: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["toast"];
+
export const activity: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["activity"];
+
export const upsell: MappedModules["discord/modules/user_profile/web/BiteSizeActivity.css"]["upsell"];
}
declare module "@moonlight-mod/wp/discord/packages/flux" {
import { MappedModules } from "@moonlight-mod/mappings";
+
export const BatchedStoreListener: MappedModules["discord/packages/flux"]["BatchedStoreListener"];
+
export const Dispatcher: MappedModules["discord/packages/flux"]["Dispatcher"];
+
export const Store: MappedModules["discord/packages/flux"]["Store"];
+
const _default: MappedModules["discord/packages/flux"]["default"];
+
export default _default;
+
export const statesWillNeverBeEqual: MappedModules["discord/packages/flux"]["statesWillNeverBeEqual"];
+
export const useStateFromStores: MappedModules["discord/packages/flux"]["useStateFromStores"];
+
export const useStateFromStoresArray: MappedModules["discord/packages/flux"]["useStateFromStoresArray"];
+
export const useStateFromStoresObject: MappedModules["discord/packages/flux"]["useStateFromStoresObject"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/packages/flux/BatchedStoreListener" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/packages/flux/BatchedStoreListener"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/packages/flux/ChangeListeners" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/packages/flux/ChangeListeners"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/packages/flux/Dispatcher" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const Dispatcher: MappedModules["discord/packages/flux/Dispatcher"]["Dispatcher"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/packages/flux/Emitter" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/packages/flux/Emitter"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/packages/flux/LoggingUtils" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/packages/flux/LoggingUtils"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/packages/flux/PersistedStore" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const PersistedStore: MappedModules["discord/packages/flux/PersistedStore"]["PersistedStore"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/packages/flux/Store" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const Store: MappedModules["discord/packages/flux/Store"]["Store"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/packages/flux/connectStores" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/packages/flux/connectStores"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/records/UserRecord" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/records/UserRecord"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/discord/styles/shared/Margins.css" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const marginReset: MappedModules["discord/styles/shared/Margins.css"]["marginReset"];
+
export const marginTop4: MappedModules["discord/styles/shared/Margins.css"]["marginTop4"];
+
export const marginBottom4: MappedModules["discord/styles/shared/Margins.css"]["marginBottom4"];
+
export const marginTop8: MappedModules["discord/styles/shared/Margins.css"]["marginTop8"];
+
export const marginBottom8: MappedModules["discord/styles/shared/Margins.css"]["marginBottom8"];
+
export const marginTop20: MappedModules["discord/styles/shared/Margins.css"]["marginTop20"];
+
export const marginBottom20: MappedModules["discord/styles/shared/Margins.css"]["marginBottom20"];
+
export const marginTop40: MappedModules["discord/styles/shared/Margins.css"]["marginTop40"];
+
export const marginBottom40: MappedModules["discord/styles/shared/Margins.css"]["marginBottom40"];
+
export const marginTop60: MappedModules["discord/styles/shared/Margins.css"]["marginTop60"];
+
export const marginBottom60: MappedModules["discord/styles/shared/Margins.css"]["marginBottom60"];
+
export const marginCenterHorz: MappedModules["discord/styles/shared/Margins.css"]["marginCenterHorz"];
+
export const marginLeft8: MappedModules["discord/styles/shared/Margins.css"]["marginLeft8"];
}
declare module "@moonlight-mod/wp/discord/uikit/Flex" {
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/uikit/Flex"]["default"];
+
export default _default;
}
declare module "@moonlight-mod/wp/discord/utils/ClipboardUtils" {
import { MappedModules } from "@moonlight-mod/mappings";
+
export const SUPPORTS_COPY: MappedModules["discord/utils/ClipboardUtils"]["SUPPORTS_COPY"];
+
export const copy: MappedModules["discord/utils/ClipboardUtils"]["copy"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/utils/ComponentDispatchUtils" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const ComponentDispatcher: MappedModules["discord/utils/ComponentDispatchUtils"]["ComponentDispatcher"];
+
export const ComponentDispatch: MappedModules["discord/utils/ComponentDispatchUtils"]["ComponentDispatch"];
}
declare module "@moonlight-mod/wp/discord/utils/HTTPUtils" {
import { MappedModules } from "@moonlight-mod/mappings";
+
export const HTTP: MappedModules["discord/utils/HTTPUtils"]["HTTP"];
+
}
+
+
declare module "@moonlight-mod/wp/discord/utils/MaskedLinkUtils" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const isLinkTrusted: MappedModules["discord/utils/MaskedLinkUtils"]["isLinkTrusted"];
+
export const handleClick: MappedModules["discord/utils/MaskedLinkUtils"]["handleClick"];
}
declare module "@moonlight-mod/wp/discord/utils/NativeUtils" {
import { MappedModules } from "@moonlight-mod/mappings";
+
const _default: MappedModules["discord/utils/NativeUtils"]["default"];
+
export default _default;
+
}
+
+
declare module "@moonlight-mod/wp/highlight.js" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const highlight: MappedModules["highlight.js"]["highlight"];
+
export const highlightAuto: MappedModules["highlight.js"]["highlightAuto"];
+
export const fixMarkup: MappedModules["highlight.js"]["fixMarkup"];
+
export const highlightBlock: MappedModules["highlight.js"]["highlightBlock"];
+
export const configure: MappedModules["highlight.js"]["configure"];
+
export const initHighlighting: MappedModules["highlight.js"]["initHighlighting"];
+
export const initHighlightingOnLoad: MappedModules["highlight.js"]["initHighlightingOnLoad"];
+
export const registerLanguage: MappedModules["highlight.js"]["registerLanguage"];
+
export const listLanguages: MappedModules["highlight.js"]["listLanguages"];
+
export const getLanguage: MappedModules["highlight.js"]["getLanguage"];
+
export const inherit: MappedModules["highlight.js"]["inherit"];
+
export const COMMENT: MappedModules["highlight.js"]["COMMENT"];
+
export const IDENT_RE: MappedModules["highlight.js"]["IDENT_RE"];
+
export const UNDERSCORE_IDENT_RE: MappedModules["highlight.js"]["UNDERSCORE_IDENT_RE"];
+
export const NUMBER_RE: MappedModules["highlight.js"]["NUMBER_RE"];
+
export const C_NUMBER_RE: MappedModules["highlight.js"]["C_NUMBER_RE"];
+
export const BINARY_NUMBER_RE: MappedModules["highlight.js"]["BINARY_NUMBER_RE"];
+
export const RE_STARTERS_RE: MappedModules["highlight.js"]["RE_STARTERS_RE"];
+
export const BACKSLASH_ESCAPE: MappedModules["highlight.js"]["BACKSLASH_ESCAPE"];
+
export const APOS_STRING_MODE: MappedModules["highlight.js"]["APOS_STRING_MODE"];
+
export const QUOTE_STRING_MODE: MappedModules["highlight.js"]["QUOTE_STRING_MODE"];
+
export const PHRASAL_WORDS_MODE: MappedModules["highlight.js"]["PHRASAL_WORDS_MODE"];
+
export const C_LINE_COMMENT_MODE: MappedModules["highlight.js"]["C_LINE_COMMENT_MODE"];
+
export const C_BLOCK_COMMENT_MODE: MappedModules["highlight.js"]["C_BLOCK_COMMENT_MODE"];
+
export const HASH_COMMENT_MODE: MappedModules["highlight.js"]["HASH_COMMENT_MODE"];
+
export const NUMBER_MODE: MappedModules["highlight.js"]["NUMBER_MODE"];
+
export const C_NUMBER_MODE: MappedModules["highlight.js"]["C_NUMBER_MODE"];
+
export const BINARY_NUMBER_MODE: MappedModules["highlight.js"]["BINARY_NUMBER_MODE"];
+
export const CSS_NUMBER_MODE: MappedModules["highlight.js"]["CSS_NUMBER_MODE"];
+
export const REGEX_MODE: MappedModules["highlight.js"]["REGEX_MODE"];
+
export const TITLE_MODE: MappedModules["highlight.js"]["TITLE_MODE"];
+
export const UNDERSCORE_TITLE_MODE: MappedModules["highlight.js"]["UNDERSCORE_TITLE_MODE"];
+
const _default: MappedModules["highlight.js"]["default"];
+
export default _default;
+
export const HighlightJS: MappedModules["highlight.js"]["HighlightJS"];
+
}
+
+
declare module "@moonlight-mod/wp/highlight.js/lib/core" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const highlight: MappedModules["highlight.js/lib/core"]["highlight"];
+
export const highlightAuto: MappedModules["highlight.js/lib/core"]["highlightAuto"];
+
export const fixMarkup: MappedModules["highlight.js/lib/core"]["fixMarkup"];
+
export const highlightBlock: MappedModules["highlight.js/lib/core"]["highlightBlock"];
+
export const configure: MappedModules["highlight.js/lib/core"]["configure"];
+
export const initHighlighting: MappedModules["highlight.js/lib/core"]["initHighlighting"];
+
export const initHighlightingOnLoad: MappedModules["highlight.js/lib/core"]["initHighlightingOnLoad"];
+
export const registerLanguage: MappedModules["highlight.js/lib/core"]["registerLanguage"];
+
export const listLanguages: MappedModules["highlight.js/lib/core"]["listLanguages"];
+
export const getLanguage: MappedModules["highlight.js/lib/core"]["getLanguage"];
+
export const inherit: MappedModules["highlight.js/lib/core"]["inherit"];
+
export const COMMENT: MappedModules["highlight.js/lib/core"]["COMMENT"];
+
export const IDENT_RE: MappedModules["highlight.js/lib/core"]["IDENT_RE"];
+
export const UNDERSCORE_IDENT_RE: MappedModules["highlight.js/lib/core"]["UNDERSCORE_IDENT_RE"];
+
export const NUMBER_RE: MappedModules["highlight.js/lib/core"]["NUMBER_RE"];
+
export const C_NUMBER_RE: MappedModules["highlight.js/lib/core"]["C_NUMBER_RE"];
+
export const BINARY_NUMBER_RE: MappedModules["highlight.js/lib/core"]["BINARY_NUMBER_RE"];
+
export const RE_STARTERS_RE: MappedModules["highlight.js/lib/core"]["RE_STARTERS_RE"];
+
export const BACKSLASH_ESCAPE: MappedModules["highlight.js/lib/core"]["BACKSLASH_ESCAPE"];
+
export const APOS_STRING_MODE: MappedModules["highlight.js/lib/core"]["APOS_STRING_MODE"];
+
export const QUOTE_STRING_MODE: MappedModules["highlight.js/lib/core"]["QUOTE_STRING_MODE"];
+
export const PHRASAL_WORDS_MODE: MappedModules["highlight.js/lib/core"]["PHRASAL_WORDS_MODE"];
+
export const C_LINE_COMMENT_MODE: MappedModules["highlight.js/lib/core"]["C_LINE_COMMENT_MODE"];
+
export const C_BLOCK_COMMENT_MODE: MappedModules["highlight.js/lib/core"]["C_BLOCK_COMMENT_MODE"];
+
export const HASH_COMMENT_MODE: MappedModules["highlight.js/lib/core"]["HASH_COMMENT_MODE"];
+
export const NUMBER_MODE: MappedModules["highlight.js/lib/core"]["NUMBER_MODE"];
+
export const C_NUMBER_MODE: MappedModules["highlight.js/lib/core"]["C_NUMBER_MODE"];
+
export const BINARY_NUMBER_MODE: MappedModules["highlight.js/lib/core"]["BINARY_NUMBER_MODE"];
+
export const CSS_NUMBER_MODE: MappedModules["highlight.js/lib/core"]["CSS_NUMBER_MODE"];
+
export const REGEX_MODE: MappedModules["highlight.js/lib/core"]["REGEX_MODE"];
+
export const TITLE_MODE: MappedModules["highlight.js/lib/core"]["TITLE_MODE"];
+
export const UNDERSCORE_TITLE_MODE: MappedModules["highlight.js/lib/core"]["UNDERSCORE_TITLE_MODE"];
+
}
+
+
declare module "@moonlight-mod/wp/lodash" {}
+
+
declare module "@moonlight-mod/wp/murmurhash" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const v2: MappedModules["murmurhash"]["v2"];
+
export const v3: MappedModules["murmurhash"]["v3"];
+
}
+
+
declare module "@moonlight-mod/wp/platform.js" {
+
import { MappedModules } from "@moonlight-mod/mappings";
+
export const description: MappedModules["platform.js"]["description"];
+
export const layout: MappedModules["platform.js"]["layout"];
+
export const manufacturer: MappedModules["platform.js"]["manufacturer"];
+
export const name: MappedModules["platform.js"]["name"];
+
export const prerelease: MappedModules["platform.js"]["prerelease"];
+
export const product: MappedModules["platform.js"]["product"];
+
export const ua: MappedModules["platform.js"]["ua"];
+
export const version: MappedModules["platform.js"]["version"];
+
export const os: MappedModules["platform.js"]["os"];
+
export const parse: MappedModules["platform.js"]["parse"];
+
export const toString: MappedModules["platform.js"]["toString"];
}
declare module "@moonlight-mod/wp/react" {
import { MappedModules } from "@moonlight-mod/mappings";
+
const _: Omit<MappedModules["react"], "__mappings_exportEquals">;
export = _;
}
+
+
declare module "@moonlight-mod/wp/uuid/v4" {}
+7 -7
packages/types/tsconfig.json
···
{
"compilerOptions": {
-
"target": "es2016",
-
"module": "es6",
-
"esModuleInterop": true,
-
"forceConsistentCasingInFileNames": true,
-
"strict": true,
-
"moduleResolution": "bundler",
"jsx": "react",
-
"declaration": true
},
"include": ["./src/**/*", "src/index.ts", "./src/import.d.ts"]
}
···
{
"compilerOptions": {
+
"target": "ES2016",
"jsx": "react",
+
"module": "ES6",
+
"moduleResolution": "bundler",
+
"strict": true,
+
"declaration": true,
+
"esModuleInterop": true,
+
"forceConsistentCasingInFileNames": true
},
"include": ["./src/**/*", "src/index.ts", "./src/import.d.ts"]
}
+10 -3
packages/web-preload/package.json
···
"name": "@moonlight-mod/web-preload",
"private": true,
"main": "src/index.ts",
"dependencies": {
"@moonlight-mod/core": "workspace:*",
-
"@moonlight-mod/lunast": "^1.0.0",
-
"@moonlight-mod/mappings": "^1.0.9",
-
"@moonlight-mod/moonmap": "^1.0.3",
"@moonlight-mod/types": "workspace:*"
}
}
···
"name": "@moonlight-mod/web-preload",
"private": true,
"main": "src/index.ts",
+
"engineStrict": true,
+
"engines": {
+
"node": ">=22",
+
"pnpm": ">=10",
+
"npm": "pnpm",
+
"yarn": "pnpm"
+
},
"dependencies": {
"@moonlight-mod/core": "workspace:*",
+
"@moonlight-mod/lunast": "catalog:prod",
+
"@moonlight-mod/mappings": "catalog:prod",
+
"@moonlight-mod/moonmap": "catalog:prod",
"@moonlight-mod/types": "workspace:*"
}
}
+15 -13
packages/web-preload/src/index.ts
···
import Moonmap from "@moonlight-mod/moonmap";
import loadMappings from "@moonlight-mod/mappings";
import { createEventEmitter } from "@moonlight-mod/core/util/event";
-
import { EventPayloads, EventType } from "@moonlight-mod/types/core/event";
async function load() {
initLogger(moonlightNode.config);
const logger = new Logger("web-preload");
window.moonlight = {
-
apiLevel: constants.apiLevel,
unpatched: new Set(),
pendingModules: new Set(),
enabledExtensions: new Set(),
-
events: createEventEmitter<EventType, EventPayloads>(),
patchingInternals: {
onModuleLoad,
registerPatch,
registerWebpackModule
},
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
getConfig: moonlightNode.getConfig.bind(moonlightNode),
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
getNatives: moonlightNode.getNatives.bind(moonlightNode),
getLogger(id) {
return new Logger(id);
},
lunast: new LunAST(),
moonmap: new Moonmap()
};
···
logger.error("Error setting up web-preload", e);
}
-
if (MOONLIGHT_ENV === "web-preload") {
-
window.addEventListener("DOMContentLoaded", () => {
-
installStyles();
-
});
} else {
-
installStyles();
}
}
-
if (MOONLIGHT_ENV === "web-preload") {
-
load();
-
} else {
-
window._moonlightBrowserLoad = load;
-
}
···
import Moonmap from "@moonlight-mod/moonmap";
import loadMappings from "@moonlight-mod/mappings";
import { createEventEmitter } from "@moonlight-mod/core/util/event";
+
import { WebEventPayloads, WebEventType } from "@moonlight-mod/types/core/event";
async function load() {
+
delete window._moonlightWebLoad;
initLogger(moonlightNode.config);
const logger = new Logger("web-preload");
window.moonlight = {
+
patched: new Map(),
unpatched: new Set(),
pendingModules: new Set(),
enabledExtensions: new Set(),
+
+
events: createEventEmitter<WebEventType, WebEventPayloads>(),
patchingInternals: {
onModuleLoad,
registerPatch,
registerWebpackModule
},
+
localStorage: window.localStorage,
version: MOONLIGHT_VERSION,
branch: MOONLIGHT_BRANCH as MoonlightBranch,
+
apiLevel: constants.apiLevel,
getConfig: moonlightNode.getConfig.bind(moonlightNode),
getConfigOption: moonlightNode.getConfigOption.bind(moonlightNode),
+
setConfigOption: moonlightNode.setConfigOption.bind(moonlightNode),
+
writeConfig: moonlightNode.writeConfig.bind(moonlightNode),
+
getNatives: moonlightNode.getNatives.bind(moonlightNode),
getLogger(id) {
return new Logger(id);
},
+
lunast: new LunAST(),
moonmap: new Moonmap()
};
···
logger.error("Error setting up web-preload", e);
}
+
if (document.readyState === "complete") {
+
installStyles();
} else {
+
window.addEventListener("load", installStyles);
}
}
+
window._moonlightWebLoad = load;
+4 -1
packages/web-preload/tsconfig.json
···
{
-
"extends": "../../tsconfig.json"
}
···
{
+
"extends": "../../tsconfig.json",
+
"compilerOptions": {
+
"lib": ["ESNext", "DOM"]
+
}
}
+1253 -731
pnpm-lock.yaml
···
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
'@moonlight-mod/eslint-config':
-
specifier: github:moonlight-mod/eslint-config
-
version: https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/7eb7bd7c51fe0e3ee9d2a0baf149212d2bb893af(eslint@9.12.0)(prettier@3.1.0)(typescript@5.3.2)
esbuild:
-
specifier: ^0.19.3
version: 0.19.3
esbuild-copy-static-files:
-
specifier: ^0.1.0
version: 0.1.0
eslint:
-
specifier: ^9.12.0
-
version: 9.12.0
husky:
-
specifier: ^8.0.3
version: 8.0.3
prettier:
-
specifier: ^3.1.0
version: 3.1.0
typescript:
-
specifier: ^5.3.2
-
version: 5.3.2
packages/browser:
dependencies:
···
specifier: workspace:*
version: link:../web-preload
'@zenfs/core':
-
specifier: ^1.0.2
-
version: 1.0.2
'@zenfs/dom':
-
specifier: ^0.2.16
-
version: 0.2.16(@zenfs/core@1.0.2)
packages/core:
dependencies:
···
'@moonlight-mod/types':
specifier: workspace:*
version: link:../types
nanotar:
-
specifier: ^0.1.1
version: 0.1.1
packages/injector:
···
packages/types:
dependencies:
'@moonlight-mod/lunast':
-
specifier: ^1.0.0
-
version: 1.0.0
'@moonlight-mod/mappings':
-
specifier: ^1.0.9
-
version: 1.0.9(@moonlight-mod/lunast@1.0.0)(@moonlight-mod/moonmap@1.0.3)
'@moonlight-mod/moonmap':
-
specifier: ^1.0.3
-
version: 1.0.3
'@types/react':
specifier: ^18.3.10
-
version: 18.3.10
csstype:
-
specifier: ^3.1.2
-
version: 3.1.2
standalone-electron-types:
specifier: ^1.0.0
version: 1.0.0
···
specifier: workspace:*
version: link:../core
'@moonlight-mod/lunast':
-
specifier: ^1.0.0
-
version: 1.0.0
'@moonlight-mod/mappings':
-
specifier: ^1.0.9
-
version: 1.0.9(@moonlight-mod/lunast@1.0.0)(@moonlight-mod/moonmap@1.0.3)
'@moonlight-mod/moonmap':
-
specifier: ^1.0.3
-
version: 1.0.3
'@moonlight-mod/types':
specifier: workspace:*
version: link:../types
···
'@aashutoshrathi/word-wrap@1.2.6':
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
engines: {node: '>=0.10.0'}
'@esbuild/android-arm64@0.19.3':
resolution: {integrity: sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==}
···
cpu: [x64]
os: [win32]
-
'@eslint-community/eslint-utils@4.4.0':
-
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
-
'@eslint-community/regexpp@4.11.1':
-
resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
-
'@eslint/config-array@0.18.0':
-
resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@eslint/core@0.6.0':
-
resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@eslint/eslintrc@3.1.0':
-
resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@eslint/js@9.12.0':
-
resolution: {integrity: sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@eslint/object-schema@2.1.4':
-
resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@eslint/plugin-kit@0.2.0':
-
resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@humanfs/core@0.19.0':
-
resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==}
engines: {node: '>=18.18.0'}
-
'@humanfs/node@0.16.5':
-
resolution: {integrity: sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==}
engines: {node: '>=18.18.0'}
'@humanwhocodes/module-importer@1.0.1':
···
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
engines: {node: '>=18.18'}
-
'@moonlight-mod/eslint-config@https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/7eb7bd7c51fe0e3ee9d2a0baf149212d2bb893af':
-
resolution: {tarball: https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/7eb7bd7c51fe0e3ee9d2a0baf149212d2bb893af}
-
version: 1.0.0
peerDependencies:
eslint: '>= 9'
typescript: '>= 5.3'
-
'@moonlight-mod/lunast@1.0.0':
-
resolution: {integrity: sha512-kJgf41K12i6/2LbXK97CNO+pNO7ADGh9N4bCQcOPwosocKMcwKHDEZUgPqeihNshY3c3AEW1LiyXjlsl24PdDw==}
-
'@moonlight-mod/mappings@1.0.9':
-
resolution: {integrity: sha512-isNonhD33MjWlryUvR/Ogk2AGzcFso2VHg2nQFp0FmpGOP0P61FF78KuLPHfhUzVwZbDDTfGBl0cnx5H7QvGMw==}
peerDependencies:
-
'@moonlight-mod/lunast': ^1.0.0
-
'@moonlight-mod/moonmap': ^1.0.0
-
'@moonlight-mod/moonmap@1.0.3':
-
resolution: {integrity: sha512-G7pwvrcVDimc388IX6VZFzBXpbuyvqbJ+w9/v+MUIc8P7dADJXQ9YkBWvobtRc6eaBBl1FWUwTeU8oobbxLVag==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
···
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
-
'@pkgr/core@0.1.1':
-
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
'@types/estree-jsx@1.0.5':
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
'@types/fbemitter@2.0.35':
resolution: {integrity: sha512-Xem6d7qUfmouCHntCrRYgDBwbf+WWRd6G+7WEFlEZFZ67LZXiYRvT2LV8wcZa6mIaAil95+ABQdKgB6hPIsnng==}
'@types/flux@3.1.14':
resolution: {integrity: sha512-WRXN0kQPCnqxN0/PgNgc7WBF6c8rbSHsEep3/qBLpsQ824RONdOmTs0TV7XhIW2GDNRAHO2CqCgAFLR5PChosw==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/node@18.17.17':
resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==}
-
'@types/node@20.16.10':
-
resolution: {integrity: sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==}
'@types/prop-types@15.7.13':
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
-
'@types/react@18.3.10':
-
resolution: {integrity: sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg==}
-
-
'@types/readable-stream@4.0.15':
-
resolution: {integrity: sha512-oAZ3kw+kJFkEqyh7xORZOku1YAKvsFTogRY8kVl4vHpEKiDkfnSA/My8haRE7fvmix5Zyy+1pwzOi7yycGLBJw==}
-
'@typescript-eslint/eslint-plugin@8.8.1':
-
resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
eslint: ^8.57.0 || ^9.0.0
-
typescript: '*'
-
peerDependenciesMeta:
-
typescript:
-
optional: true
-
'@typescript-eslint/parser@8.8.1':
-
resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
-
typescript: '*'
-
peerDependenciesMeta:
-
typescript:
-
optional: true
-
'@typescript-eslint/scope-manager@8.8.1':
-
resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@typescript-eslint/type-utils@8.8.1':
-
resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
-
typescript: '*'
-
peerDependenciesMeta:
-
typescript:
-
optional: true
-
'@typescript-eslint/types@8.8.1':
-
resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@typescript-eslint/typescript-estree@8.8.1':
-
resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
-
typescript: '*'
-
peerDependenciesMeta:
-
typescript:
-
optional: true
-
'@typescript-eslint/utils@8.8.1':
-
resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
-
'@typescript-eslint/visitor-keys@8.8.1':
-
resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
'@zenfs/core@1.0.2':
-
resolution: {integrity: sha512-LMTD4ntn6Ag1y+IeOSVykDDvYC12dsGFtsX8M/54OQrLs7v+YnX4bpo0o2osbm8XFmU2MTNMX/G3PLsvzgWzrg==}
-
engines: {node: '>= 16'}
hasBin: true
-
'@zenfs/dom@0.2.16':
-
resolution: {integrity: sha512-6Ev+ol9hZIgQECNZR+xxjQ/a99EhhrWeiQttm/+U7YJK3HdTjiKfU39DsfGeH64vSqhpa5Vj+LWRx75SHkjw0Q==}
engines: {node: '>= 18'}
peerDependencies:
-
'@zenfs/core': ^1.0.0
abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
···
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
-
acorn@8.12.1:
-
resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
engines: {node: '>=0.4.0'}
hasBin: true
···
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
-
array-buffer-byte-length@1.0.1:
-
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
engines: {node: '>= 0.4'}
array-includes@3.1.8:
···
resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
engines: {node: '>= 0.4'}
-
array.prototype.flat@1.3.2:
-
resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
engines: {node: '>= 0.4'}
-
array.prototype.flatmap@1.3.2:
-
resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
engines: {node: '>= 0.4'}
array.prototype.tosorted@1.1.4:
resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
engines: {node: '>= 0.4'}
-
arraybuffer.prototype.slice@1.0.3:
-
resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
engines: {node: '>= 0.4'}
astring@1.9.0:
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
hasBin: true
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
···
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
-
call-bind@1.0.7:
-
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
engines: {node: '>= 0.4'}
callsites@3.1.0:
···
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
-
cross-spawn@7.0.3:
-
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
-
csstype@3.1.2:
-
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
-
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
-
data-view-buffer@1.0.1:
-
resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
engines: {node: '>= 0.4'}
-
data-view-byte-length@1.0.1:
-
resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==}
engines: {node: '>= 0.4'}
-
data-view-byte-offset@1.0.0:
-
resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
engines: {node: '>= 0.4'}
-
debug@4.3.4:
-
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
···
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
doctrine@2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
-
es-abstract@1.23.3:
-
resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==}
engines: {node: '>= 0.4'}
-
es-define-property@1.0.0:
-
resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
-
es-iterator-helpers@1.1.0:
-
resolution: {integrity: sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==}
engines: {node: '>= 0.4'}
-
es-object-atoms@1.0.0:
-
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
engines: {node: '>= 0.4'}
-
es-set-tostringtag@2.0.3:
-
resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
engines: {node: '>= 0.4'}
-
es-shim-unscopables@1.0.2:
-
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
-
es-to-primitive@1.2.1:
-
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
engines: {node: '>= 0.4'}
esbuild-copy-static-files@0.1.0:
···
peerDependencies:
eslint: '>=7.0.0'
-
eslint-plugin-prettier@5.2.1:
-
resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
'@types/eslint': '>=8.0.0'
eslint: '>=8.0.0'
-
eslint-config-prettier: '*'
prettier: '>=3.0.0'
peerDependenciesMeta:
'@types/eslint':
···
eslint-config-prettier:
optional: true
-
eslint-plugin-react@7.37.1:
-
resolution: {integrity: sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==}
engines: {node: '>=4'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
-
eslint-scope@8.1.0:
-
resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-
eslint-visitor-keys@4.1.0:
-
resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
eslint@9.12.0:
-
resolution: {integrity: sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
···
jiti:
optional: true
-
espree@10.2.0:
-
resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
-
esquery@1.5.0:
-
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
engines: {node: '>=0.10'}
esrecurse@4.3.0:
···
fastq@1.17.1:
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
···
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
···
flatted@3.2.9:
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
-
for-each@0.3.3:
-
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
-
function.prototype.name@1.1.6:
-
resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
engines: {node: '>= 0.4'}
functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
-
get-intrinsic@1.2.4:
-
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
engines: {node: '>= 0.4'}
-
get-symbol-description@1.0.2:
-
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
engines: {node: '>= 0.4'}
glob-parent@5.1.2:
···
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
-
gopd@1.0.1:
-
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
-
has-bigints@1.0.2:
-
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
···
has-property-descriptors@1.0.2:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
-
has-proto@1.0.3:
-
resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
engines: {node: '>= 0.4'}
-
has-symbols@1.0.3:
-
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
···
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
-
ignore@5.3.0:
-
resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==}
-
engines: {node: '>= 4'}
-
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
···
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
-
internal-slot@1.0.7:
-
resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
engines: {node: '>= 0.4'}
-
is-array-buffer@3.0.4:
-
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
engines: {node: '>= 0.4'}
-
is-async-function@2.0.0:
-
resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==}
engines: {node: '>= 0.4'}
-
is-bigint@1.0.4:
-
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
-
is-boolean-object@1.1.2:
-
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
engines: {node: '>= 0.4'}
is-callable@1.2.7:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
-
is-core-module@2.15.1:
-
resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
engines: {node: '>= 0.4'}
-
is-data-view@1.0.1:
-
resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==}
engines: {node: '>= 0.4'}
-
is-date-object@1.0.5:
-
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
engines: {node: '>= 0.4'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
-
is-finalizationregistry@1.0.2:
-
resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==}
-
is-generator-function@1.0.10:
-
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
engines: {node: '>= 0.4'}
is-glob@4.0.3:
···
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'}
-
is-negative-zero@2.0.3:
-
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
-
engines: {node: '>= 0.4'}
-
-
is-number-object@1.0.7:
-
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
engines: {node: '>= 0.4'}
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
-
is-regex@1.1.4:
-
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
is-set@2.0.3:
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
engines: {node: '>= 0.4'}
-
is-shared-array-buffer@1.0.3:
-
resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
engines: {node: '>= 0.4'}
-
is-string@1.0.7:
-
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
engines: {node: '>= 0.4'}
-
is-symbol@1.0.4:
-
resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
engines: {node: '>= 0.4'}
-
is-typed-array@1.1.13:
-
resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
engines: {node: '>= 0.4'}
is-weakmap@2.0.2:
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
engines: {node: '>= 0.4'}
-
is-weakref@1.0.2:
-
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
-
is-weakset@2.0.3:
-
resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==}
engines: {node: '>= 0.4'}
isarray@2.0.5:
···
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
-
iterator.prototype@1.1.3:
-
resolution: {integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==}
engines: {node: '>= 0.4'}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
···
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
···
resolution: {integrity: sha512-OyvYIOgpzXREySYJ1cqEb2pOKdeQMTfF9M8dRU6nC4hi/GXMmNpe9ssZCrSoTHazu05BSAoRBN/uYeco+ymfOg==}
engines: {node: '>=18.0.0'}
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
···
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
-
ms@2.1.2:
-
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
nanotar@0.1.1:
resolution: {integrity: sha512-AiJsGsSF3O0havL1BydvI4+wR76sKT+okKRwWIaK96cZUnXqH0uNBOsHlbwZq3+m2BR1VKqHDVudl3gO4mYjpQ==}
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
-
object-inspect@1.13.2:
-
resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
engines: {node: '>= 0.4'}
object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
-
object.assign@4.1.5:
-
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
engines: {node: '>= 0.4'}
-
object.entries@1.1.8:
-
resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==}
engines: {node: '>= 0.4'}
object.fromentries@2.0.8:
resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
engines: {node: '>= 0.4'}
-
object.values@1.2.0:
-
resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
engines: {node: '>= 0.4'}
optionator@0.9.3:
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
engines: {node: '>= 0.8.0'}
p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
···
p-locate@5.0.0:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
···
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
-
possible-typed-array-names@1.0.0:
-
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
engines: {node: '>= 0.4'}
prelude-ls@1.2.1:
···
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
···
resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-
reflect.getprototypeof@1.0.6:
-
resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
engines: {node: '>= 0.4'}
-
regexp.prototype.flags@1.5.3:
-
resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==}
engines: {node: '>= 0.4'}
resolve-from@4.0.0:
···
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
hasBin: true
reusify@1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
···
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
-
safe-array-concat@1.1.2:
-
resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
engines: {node: '>=0.4'}
-
-
safe-buffer@5.1.2:
-
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
-
safe-regex-test@1.0.3:
-
resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
engines: {node: '>= 0.4'}
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
-
semver@7.6.3:
-
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
engines: {node: '>=10'}
hasBin: true
···
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
engines: {node: '>= 0.4'}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
···
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
-
side-channel@1.0.6:
-
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
engines: {node: '>= 0.4'}
standalone-electron-types@1.0.0:
resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==}
-
string.prototype.matchall@4.0.11:
-
resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==}
engines: {node: '>= 0.4'}
string.prototype.repeat@1.0.0:
resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
-
string.prototype.trim@1.2.9:
-
resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
engines: {node: '>= 0.4'}
-
string.prototype.trimend@1.0.8:
-
resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
string.prototype.trimstart@1.0.8:
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
···
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
-
synckit@0.9.2:
-
resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==}
engines: {node: ^14.18.0 || >=16.0.0}
-
text-table@0.2.0:
-
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
-
ts-api-utils@1.3.0:
-
resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
-
engines: {node: '>=16'}
peerDependencies:
-
typescript: '>=4.2.0'
-
tslib@2.7.0:
-
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
-
typed-array-buffer@1.0.2:
-
resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
engines: {node: '>= 0.4'}
-
typed-array-byte-length@1.0.1:
-
resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
engines: {node: '>= 0.4'}
-
typed-array-byte-offset@1.0.2:
-
resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
engines: {node: '>= 0.4'}
-
typed-array-length@1.0.6:
-
resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
engines: {node: '>= 0.4'}
-
typescript-eslint@8.8.1:
-
resolution: {integrity: sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
-
typescript: '*'
-
peerDependenciesMeta:
-
typescript:
-
optional: true
-
typescript@5.3.2:
-
resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==}
engines: {node: '>=14.17'}
hasBin: true
-
unbox-primitive@1.0.2:
-
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
-
undici-types@6.19.8:
-
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
-
utilium@0.7.1:
-
resolution: {integrity: sha512-2ocvTkI7U8LERmwxL0LhFUvEfN66UqcjF6tMiURvUwSyU7U1QC9gST+3iSUSiGccFfnP3f2EXwHNXOnOzx+lAg==}
-
which-boxed-primitive@1.0.2:
-
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
-
which-builtin-type@1.1.4:
-
resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==}
engines: {node: '>= 0.4'}
which-collection@1.0.2:
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
engines: {node: '>= 0.4'}
-
which-typed-array@1.1.15:
-
resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
engines: {node: '>= 0.4'}
which@2.0.2:
···
engines: {node: '>= 8'}
hasBin: true
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
snapshots:
'@aashutoshrathi/word-wrap@1.2.6': {}
'@esbuild/android-arm64@0.19.3':
optional: true
···
'@esbuild/win32-x64@0.19.3':
optional: true
-
'@eslint-community/eslint-utils@4.4.0(eslint@9.12.0)':
dependencies:
-
eslint: 9.12.0
eslint-visitor-keys: 3.4.3
-
'@eslint-community/regexpp@4.11.1': {}
-
'@eslint/config-array@0.18.0':
dependencies:
-
'@eslint/object-schema': 2.1.4
-
debug: 4.3.4
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
-
'@eslint/core@0.6.0': {}
-
'@eslint/eslintrc@3.1.0':
dependencies:
ajv: 6.12.6
-
debug: 4.3.4
-
espree: 10.2.0
globals: 14.0.0
-
ignore: 5.3.0
import-fresh: 3.3.0
js-yaml: 4.1.0
minimatch: 3.1.2
···
transitivePeerDependencies:
- supports-color
-
'@eslint/js@9.12.0': {}
-
'@eslint/object-schema@2.1.4': {}
-
'@eslint/plugin-kit@0.2.0':
dependencies:
levn: 0.4.1
-
'@humanfs/core@0.19.0': {}
-
'@humanfs/node@0.16.5':
dependencies:
-
'@humanfs/core': 0.19.0
'@humanwhocodes/retry': 0.3.1
'@humanwhocodes/module-importer@1.0.1': {}
'@humanwhocodes/retry@0.3.1': {}
-
'@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)':
dependencies:
-
'@eslint/js': 9.12.0
-
eslint: 9.12.0
-
eslint-config-prettier: 9.1.0(eslint@9.12.0)
-
eslint-plugin-prettier: 5.2.1(eslint-config-prettier@9.1.0(eslint@9.12.0))(eslint@9.12.0)(prettier@3.1.0)
-
eslint-plugin-react: 7.37.1(eslint@9.12.0)
-
typescript: 5.3.2
-
typescript-eslint: 8.8.1(eslint@9.12.0)(typescript@5.3.2)
transitivePeerDependencies:
- '@types/eslint'
- prettier
- supports-color
-
'@moonlight-mod/lunast@1.0.0':
dependencies:
astring: 1.9.0
estree-toolkit: 1.7.8
meriyah: 6.0.1
-
'@moonlight-mod/mappings@1.0.9(@moonlight-mod/lunast@1.0.0)(@moonlight-mod/moonmap@1.0.3)':
dependencies:
-
'@moonlight-mod/lunast': 1.0.0
-
'@moonlight-mod/moonmap': 1.0.3
'@types/flux': 3.1.14
-
'@types/react': 18.3.10
csstype: 3.1.3
-
'@moonlight-mod/moonmap@1.0.3': {}
'@nodelib/fs.scandir@2.1.5':
dependencies:
···
'@nodelib/fs.scandir': 2.1.5
fastq: 1.17.1
-
'@pkgr/core@0.1.1': {}
'@types/estree-jsx@1.0.5':
dependencies:
···
'@types/estree@1.0.6': {}
'@types/fbemitter@2.0.35': {}
'@types/flux@3.1.14':
dependencies:
'@types/fbemitter': 2.0.35
-
'@types/react': 18.3.10
'@types/json-schema@7.0.15': {}
'@types/node@18.17.17': {}
-
'@types/node@20.16.10':
dependencies:
-
undici-types: 6.19.8
'@types/prop-types@15.7.13': {}
-
'@types/react@18.3.10':
dependencies:
'@types/prop-types': 15.7.13
csstype: 3.1.3
-
'@types/readable-stream@4.0.15':
dependencies:
-
'@types/node': 20.16.10
-
safe-buffer: 5.1.2
-
-
'@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)':
-
dependencies:
-
'@eslint-community/regexpp': 4.11.1
-
'@typescript-eslint/parser': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
-
'@typescript-eslint/scope-manager': 8.8.1
-
'@typescript-eslint/type-utils': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
-
'@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
-
'@typescript-eslint/visitor-keys': 8.8.1
-
eslint: 9.12.0
graphemer: 1.4.0
ignore: 5.3.2
natural-compare: 1.4.0
-
ts-api-utils: 1.3.0(typescript@5.3.2)
-
optionalDependencies:
-
typescript: 5.3.2
transitivePeerDependencies:
- supports-color
-
'@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.3.2)':
dependencies:
-
'@typescript-eslint/scope-manager': 8.8.1
-
'@typescript-eslint/types': 8.8.1
-
'@typescript-eslint/typescript-estree': 8.8.1(typescript@5.3.2)
-
'@typescript-eslint/visitor-keys': 8.8.1
-
debug: 4.3.4
-
eslint: 9.12.0
-
optionalDependencies:
-
typescript: 5.3.2
transitivePeerDependencies:
- supports-color
-
'@typescript-eslint/scope-manager@8.8.1':
dependencies:
-
'@typescript-eslint/types': 8.8.1
-
'@typescript-eslint/visitor-keys': 8.8.1
-
'@typescript-eslint/type-utils@8.8.1(eslint@9.12.0)(typescript@5.3.2)':
dependencies:
-
'@typescript-eslint/typescript-estree': 8.8.1(typescript@5.3.2)
-
'@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
-
debug: 4.3.4
-
ts-api-utils: 1.3.0(typescript@5.3.2)
-
optionalDependencies:
-
typescript: 5.3.2
transitivePeerDependencies:
-
- eslint
- supports-color
-
'@typescript-eslint/types@8.8.1': {}
-
'@typescript-eslint/typescript-estree@8.8.1(typescript@5.3.2)':
dependencies:
-
'@typescript-eslint/types': 8.8.1
-
'@typescript-eslint/visitor-keys': 8.8.1
-
debug: 4.3.4
fast-glob: 3.3.2
is-glob: 4.0.3
minimatch: 9.0.5
-
semver: 7.6.3
-
ts-api-utils: 1.3.0(typescript@5.3.2)
-
optionalDependencies:
-
typescript: 5.3.2
transitivePeerDependencies:
- supports-color
-
'@typescript-eslint/utils@8.8.1(eslint@9.12.0)(typescript@5.3.2)':
dependencies:
-
'@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0)
-
'@typescript-eslint/scope-manager': 8.8.1
-
'@typescript-eslint/types': 8.8.1
-
'@typescript-eslint/typescript-estree': 8.8.1(typescript@5.3.2)
-
eslint: 9.12.0
transitivePeerDependencies:
- supports-color
-
- typescript
-
'@typescript-eslint/visitor-keys@8.8.1':
dependencies:
-
'@typescript-eslint/types': 8.8.1
-
eslint-visitor-keys: 3.4.3
-
'@zenfs/core@1.0.2':
dependencies:
-
'@types/node': 20.16.10
-
'@types/readable-stream': 4.0.15
buffer: 6.0.3
eventemitter3: 5.0.1
-
minimatch: 9.0.5
readable-stream: 4.5.2
-
utilium: 0.7.1
-
'@zenfs/dom@0.2.16(@zenfs/core@1.0.2)':
dependencies:
-
'@zenfs/core': 1.0.2
abort-controller@3.0.0:
dependencies:
event-target-shim: 5.0.1
-
acorn-jsx@5.3.2(acorn@8.12.1):
dependencies:
-
acorn: 8.12.1
-
acorn@8.12.1: {}
ajv@6.12.6:
dependencies:
···
dependencies:
color-convert: 2.0.1
argparse@2.0.1: {}
-
array-buffer-byte-length@1.0.1:
dependencies:
-
call-bind: 1.0.7
-
is-array-buffer: 3.0.4
array-includes@3.1.8:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
-
es-object-atoms: 1.0.0
-
get-intrinsic: 1.2.4
-
is-string: 1.0.7
array.prototype.findlast@1.2.5:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
es-errors: 1.3.0
-
es-object-atoms: 1.0.0
-
es-shim-unscopables: 1.0.2
-
array.prototype.flat@1.3.2:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
-
es-shim-unscopables: 1.0.2
-
array.prototype.flatmap@1.3.2:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
-
es-shim-unscopables: 1.0.2
array.prototype.tosorted@1.1.4:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
es-errors: 1.3.0
-
es-shim-unscopables: 1.0.2
-
arraybuffer.prototype.slice@1.0.3:
dependencies:
-
array-buffer-byte-length: 1.0.1
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
es-errors: 1.3.0
-
get-intrinsic: 1.2.4
-
is-array-buffer: 3.0.4
-
is-shared-array-buffer: 1.0.3
astring@1.9.0: {}
available-typed-arrays@1.0.7:
dependencies:
-
possible-typed-array-names: 1.0.0
balanced-match@1.0.2: {}
···
base64-js: 1.5.1
ieee754: 1.2.1
-
call-bind@1.0.7:
dependencies:
-
es-define-property: 1.0.0
es-errors: 1.3.0
function-bind: 1.1.2
-
get-intrinsic: 1.2.4
set-function-length: 1.2.2
callsites@3.1.0: {}
···
concat-map@0.0.1: {}
-
cross-spawn@7.0.3:
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
-
csstype@3.1.2: {}
-
csstype@3.1.3: {}
-
data-view-buffer@1.0.1:
dependencies:
-
call-bind: 1.0.7
es-errors: 1.3.0
-
is-data-view: 1.0.1
-
data-view-byte-length@1.0.1:
dependencies:
-
call-bind: 1.0.7
es-errors: 1.3.0
-
is-data-view: 1.0.1
-
data-view-byte-offset@1.0.0:
dependencies:
-
call-bind: 1.0.7
es-errors: 1.3.0
-
is-data-view: 1.0.1
-
debug@4.3.4:
dependencies:
-
ms: 2.1.2
deep-is@0.1.4: {}
define-data-property@1.1.4:
dependencies:
-
es-define-property: 1.0.0
es-errors: 1.3.0
-
gopd: 1.0.1
define-properties@1.2.1:
dependencies:
···
has-property-descriptors: 1.0.2
object-keys: 1.1.1
doctrine@2.1.0:
dependencies:
esutils: 2.0.3
-
es-abstract@1.23.3:
dependencies:
-
array-buffer-byte-length: 1.0.1
-
arraybuffer.prototype.slice: 1.0.3
available-typed-arrays: 1.0.7
-
call-bind: 1.0.7
-
data-view-buffer: 1.0.1
-
data-view-byte-length: 1.0.1
-
data-view-byte-offset: 1.0.0
-
es-define-property: 1.0.0
es-errors: 1.3.0
-
es-object-atoms: 1.0.0
-
es-set-tostringtag: 2.0.3
-
es-to-primitive: 1.2.1
-
function.prototype.name: 1.1.6
-
get-intrinsic: 1.2.4
-
get-symbol-description: 1.0.2
globalthis: 1.0.4
-
gopd: 1.0.1
has-property-descriptors: 1.0.2
-
has-proto: 1.0.3
-
has-symbols: 1.0.3
hasown: 2.0.2
-
internal-slot: 1.0.7
-
is-array-buffer: 3.0.4
is-callable: 1.2.7
-
is-data-view: 1.0.1
-
is-negative-zero: 2.0.3
-
is-regex: 1.1.4
-
is-shared-array-buffer: 1.0.3
-
is-string: 1.0.7
-
is-typed-array: 1.1.13
-
is-weakref: 1.0.2
-
object-inspect: 1.13.2
object-keys: 1.1.1
-
object.assign: 4.1.5
-
regexp.prototype.flags: 1.5.3
-
safe-array-concat: 1.1.2
-
safe-regex-test: 1.0.3
-
string.prototype.trim: 1.2.9
-
string.prototype.trimend: 1.0.8
string.prototype.trimstart: 1.0.8
-
typed-array-buffer: 1.0.2
-
typed-array-byte-length: 1.0.1
-
typed-array-byte-offset: 1.0.2
-
typed-array-length: 1.0.6
-
unbox-primitive: 1.0.2
-
which-typed-array: 1.1.15
-
es-define-property@1.0.0:
-
dependencies:
-
get-intrinsic: 1.2.4
es-errors@1.3.0: {}
-
es-iterator-helpers@1.1.0:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
es-errors: 1.3.0
-
es-set-tostringtag: 2.0.3
function-bind: 1.1.2
-
get-intrinsic: 1.2.4
globalthis: 1.0.4
has-property-descriptors: 1.0.2
-
has-proto: 1.0.3
-
has-symbols: 1.0.3
-
internal-slot: 1.0.7
-
iterator.prototype: 1.1.3
-
safe-array-concat: 1.1.2
-
es-object-atoms@1.0.0:
dependencies:
es-errors: 1.3.0
-
es-set-tostringtag@2.0.3:
dependencies:
-
get-intrinsic: 1.2.4
has-tostringtag: 1.0.2
hasown: 2.0.2
-
es-shim-unscopables@1.0.2:
dependencies:
hasown: 2.0.2
-
es-to-primitive@1.2.1:
dependencies:
is-callable: 1.2.7
-
is-date-object: 1.0.5
-
is-symbol: 1.0.4
esbuild-copy-static-files@0.1.0: {}
···
escape-string-regexp@4.0.0: {}
-
eslint-config-prettier@9.1.0(eslint@9.12.0):
dependencies:
-
eslint: 9.12.0
-
eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@9.12.0))(eslint@9.12.0)(prettier@3.1.0):
dependencies:
-
eslint: 9.12.0
prettier: 3.1.0
prettier-linter-helpers: 1.0.0
-
synckit: 0.9.2
optionalDependencies:
-
eslint-config-prettier: 9.1.0(eslint@9.12.0)
-
eslint-plugin-react@7.37.1(eslint@9.12.0):
dependencies:
array-includes: 3.1.8
array.prototype.findlast: 1.2.5
-
array.prototype.flatmap: 1.3.2
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
-
es-iterator-helpers: 1.1.0
-
eslint: 9.12.0
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.5
minimatch: 3.1.2
-
object.entries: 1.1.8
object.fromentries: 2.0.8
-
object.values: 1.2.0
prop-types: 15.8.1
resolve: 2.0.0-next.5
semver: 6.3.1
-
string.prototype.matchall: 4.0.11
string.prototype.repeat: 1.0.0
-
eslint-scope@8.1.0:
dependencies:
esrecurse: 4.3.0
estraverse: 5.3.0
eslint-visitor-keys@3.4.3: {}
-
eslint-visitor-keys@4.1.0: {}
-
eslint@9.12.0:
dependencies:
-
'@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0)
-
'@eslint-community/regexpp': 4.11.1
-
'@eslint/config-array': 0.18.0
-
'@eslint/core': 0.6.0
-
'@eslint/eslintrc': 3.1.0
-
'@eslint/js': 9.12.0
-
'@eslint/plugin-kit': 0.2.0
-
'@humanfs/node': 0.16.5
'@humanwhocodes/module-importer': 1.0.1
-
'@humanwhocodes/retry': 0.3.1
'@types/estree': 1.0.6
'@types/json-schema': 7.0.15
ajv: 6.12.6
chalk: 4.1.2
-
cross-spawn: 7.0.3
-
debug: 4.3.4
escape-string-regexp: 4.0.0
-
eslint-scope: 8.1.0
-
eslint-visitor-keys: 4.1.0
-
espree: 10.2.0
-
esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 8.0.0
find-up: 5.0.0
glob-parent: 6.0.2
-
ignore: 5.3.0
imurmurhash: 0.1.4
is-glob: 4.0.3
json-stable-stringify-without-jsonify: 1.0.1
···
minimatch: 3.1.2
natural-compare: 1.4.0
optionator: 0.9.3
-
text-table: 0.2.0
transitivePeerDependencies:
- supports-color
-
espree@10.2.0:
dependencies:
-
acorn: 8.12.1
-
acorn-jsx: 5.3.2(acorn@8.12.1)
-
eslint-visitor-keys: 4.1.0
-
esquery@1.5.0:
dependencies:
estraverse: 5.3.0
···
dependencies:
reusify: 1.0.4
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
···
dependencies:
to-regex-range: 5.0.1
find-up@5.0.0:
dependencies:
locate-path: 6.0.0
···
flatted@3.2.9: {}
-
for-each@0.3.3:
dependencies:
is-callable: 1.2.7
function-bind@1.1.2: {}
-
function.prototype.name@1.1.6:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
functions-have-names: 1.2.3
functions-have-names@1.2.3: {}
-
get-intrinsic@1.2.4:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
-
has-proto: 1.0.3
-
has-symbols: 1.0.3
hasown: 2.0.2
-
get-symbol-description@1.0.2:
dependencies:
-
call-bind: 1.0.7
es-errors: 1.3.0
-
get-intrinsic: 1.2.4
glob-parent@5.1.2:
dependencies:
···
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
-
gopd: 1.0.1
-
gopd@1.0.1:
-
dependencies:
-
get-intrinsic: 1.2.4
graphemer@1.4.0: {}
-
has-bigints@1.0.2: {}
has-flag@4.0.0: {}
has-property-descriptors@1.0.2:
dependencies:
-
es-define-property: 1.0.0
-
has-proto@1.0.3: {}
-
has-symbols@1.0.3: {}
has-tostringtag@1.0.2:
dependencies:
-
has-symbols: 1.0.3
hasown@2.0.2:
dependencies:
···
husky@8.0.3: {}
ieee754@1.2.1: {}
-
-
ignore@5.3.0: {}
ignore@5.3.2: {}
···
imurmurhash@0.1.4: {}
-
internal-slot@1.0.7:
dependencies:
es-errors: 1.3.0
hasown: 2.0.2
-
side-channel: 1.0.6
-
is-array-buffer@3.0.4:
dependencies:
-
call-bind: 1.0.7
-
get-intrinsic: 1.2.4
-
is-async-function@2.0.0:
dependencies:
has-tostringtag: 1.0.2
-
is-bigint@1.0.4:
dependencies:
-
has-bigints: 1.0.2
-
is-boolean-object@1.1.2:
dependencies:
-
call-bind: 1.0.7
has-tostringtag: 1.0.2
is-callable@1.2.7: {}
-
is-core-module@2.15.1:
dependencies:
hasown: 2.0.2
-
is-data-view@1.0.1:
dependencies:
-
is-typed-array: 1.1.13
-
is-date-object@1.0.5:
dependencies:
has-tostringtag: 1.0.2
is-extglob@2.1.1: {}
-
is-finalizationregistry@1.0.2:
dependencies:
-
call-bind: 1.0.7
-
is-generator-function@1.0.10:
dependencies:
has-tostringtag: 1.0.2
is-glob@4.0.3:
dependencies:
···
is-map@2.0.3: {}
-
is-negative-zero@2.0.3: {}
-
-
is-number-object@1.0.7:
dependencies:
has-tostringtag: 1.0.2
is-number@7.0.0: {}
-
is-regex@1.1.4:
dependencies:
-
call-bind: 1.0.7
has-tostringtag: 1.0.2
is-set@2.0.3: {}
-
is-shared-array-buffer@1.0.3:
dependencies:
-
call-bind: 1.0.7
-
is-string@1.0.7:
dependencies:
has-tostringtag: 1.0.2
-
is-symbol@1.0.4:
dependencies:
-
has-symbols: 1.0.3
-
is-typed-array@1.1.13:
dependencies:
-
which-typed-array: 1.1.15
is-weakmap@2.0.2: {}
-
is-weakref@1.0.2:
dependencies:
-
call-bind: 1.0.7
-
is-weakset@2.0.3:
dependencies:
-
call-bind: 1.0.7
-
get-intrinsic: 1.2.4
isarray@2.0.5: {}
isexe@2.0.0: {}
-
iterator.prototype@1.1.3:
dependencies:
-
define-properties: 1.2.1
-
get-intrinsic: 1.2.4
-
has-symbols: 1.0.3
-
reflect.getprototypeof: 1.0.6
set-function-name: 2.0.2
js-tokens@4.0.0: {}
···
jsx-ast-utils@3.3.5:
dependencies:
array-includes: 3.1.8
-
array.prototype.flat: 1.3.2
-
object.assign: 4.1.5
-
object.values: 1.2.0
keyv@4.5.4:
dependencies:
···
dependencies:
js-tokens: 4.0.0
merge2@1.4.1: {}
meriyah@6.0.1: {}
micromatch@4.0.8:
dependencies:
braces: 3.0.3
picomatch: 2.3.1
minimatch@3.1.2:
dependencies:
···
dependencies:
brace-expansion: 2.0.1
-
ms@2.1.2: {}
nanotar@0.1.1: {}
natural-compare@1.4.0: {}
object-assign@4.1.1: {}
-
object-inspect@1.13.2: {}
object-keys@1.1.1: {}
-
object.assign@4.1.5:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
has-symbols: 1.0.3
object-keys: 1.1.1
-
object.entries@1.1.8:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-object-atoms: 1.0.0
object.fromentries@2.0.8:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
-
es-object-atoms: 1.0.0
-
object.values@1.2.0:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-object-atoms: 1.0.0
optionator@0.9.3:
dependencies:
···
prelude-ls: 1.2.1
type-check: 0.4.0
p-limit@3.1.0:
dependencies:
yocto-queue: 0.1.0
···
dependencies:
p-limit: 3.1.0
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
···
path-parse@1.0.7: {}
picomatch@2.3.1: {}
-
possible-typed-array-names@1.0.0: {}
prelude-ls@1.2.1: {}
···
punycode@2.3.1: {}
queue-microtask@1.2.3: {}
react-is@16.13.1: {}
···
process: 0.11.10
string_decoder: 1.3.0
-
reflect.getprototypeof@1.0.6:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
es-errors: 1.3.0
-
get-intrinsic: 1.2.4
-
globalthis: 1.0.4
-
which-builtin-type: 1.1.4
-
regexp.prototype.flags@1.5.3:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
es-errors: 1.3.0
set-function-name: 2.0.2
resolve-from@4.0.0: {}
resolve@2.0.0-next.5:
dependencies:
-
is-core-module: 2.15.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
reusify@1.0.4: {}
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
-
safe-array-concat@1.1.2:
dependencies:
-
call-bind: 1.0.7
-
get-intrinsic: 1.2.4
-
has-symbols: 1.0.3
isarray: 2.0.5
-
-
safe-buffer@5.1.2: {}
safe-buffer@5.2.1: {}
-
safe-regex-test@1.0.3:
dependencies:
-
call-bind: 1.0.7
es-errors: 1.3.0
-
is-regex: 1.1.4
semver@6.3.1: {}
-
semver@7.6.3: {}
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
es-errors: 1.3.0
function-bind: 1.1.2
-
get-intrinsic: 1.2.4
-
gopd: 1.0.1
has-property-descriptors: 1.0.2
set-function-name@2.0.2:
···
es-errors: 1.3.0
functions-have-names: 1.2.3
has-property-descriptors: 1.0.2
shebang-command@2.0.0:
dependencies:
···
shebang-regex@3.0.0: {}
-
side-channel@1.0.6:
dependencies:
-
call-bind: 1.0.7
es-errors: 1.3.0
-
get-intrinsic: 1.2.4
-
object-inspect: 1.13.2
standalone-electron-types@1.0.0:
dependencies:
'@types/node': 18.17.17
-
string.prototype.matchall@4.0.11:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
es-errors: 1.3.0
-
es-object-atoms: 1.0.0
-
get-intrinsic: 1.2.4
-
gopd: 1.0.1
-
has-symbols: 1.0.3
-
internal-slot: 1.0.7
-
regexp.prototype.flags: 1.5.3
set-function-name: 2.0.2
-
side-channel: 1.0.6
string.prototype.repeat@1.0.0:
dependencies:
define-properties: 1.2.1
-
es-abstract: 1.23.3
-
string.prototype.trim@1.2.9:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-abstract: 1.23.3
-
es-object-atoms: 1.0.0
-
string.prototype.trimend@1.0.8:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-object-atoms: 1.0.0
string.prototype.trimstart@1.0.8:
dependencies:
-
call-bind: 1.0.7
define-properties: 1.2.1
-
es-object-atoms: 1.0.0
string_decoder@1.3.0:
dependencies:
···
supports-preserve-symlinks-flag@1.0.0: {}
-
synckit@0.9.2:
dependencies:
-
'@pkgr/core': 0.1.1
-
tslib: 2.7.0
-
text-table@0.2.0: {}
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
-
ts-api-utils@1.3.0(typescript@5.3.2):
dependencies:
-
typescript: 5.3.2
-
tslib@2.7.0: {}
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
-
typed-array-buffer@1.0.2:
dependencies:
-
call-bind: 1.0.7
es-errors: 1.3.0
-
is-typed-array: 1.1.13
-
typed-array-byte-length@1.0.1:
dependencies:
-
call-bind: 1.0.7
-
for-each: 0.3.3
-
gopd: 1.0.1
-
has-proto: 1.0.3
-
is-typed-array: 1.1.13
-
typed-array-byte-offset@1.0.2:
dependencies:
available-typed-arrays: 1.0.7
-
call-bind: 1.0.7
-
for-each: 0.3.3
-
gopd: 1.0.1
-
has-proto: 1.0.3
-
is-typed-array: 1.1.13
-
typed-array-length@1.0.6:
dependencies:
-
call-bind: 1.0.7
-
for-each: 0.3.3
-
gopd: 1.0.1
-
has-proto: 1.0.3
-
is-typed-array: 1.1.13
-
possible-typed-array-names: 1.0.0
-
typescript-eslint@8.8.1(eslint@9.12.0)(typescript@5.3.2):
dependencies:
-
'@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)
-
'@typescript-eslint/parser': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
-
'@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.3.2)
-
optionalDependencies:
-
typescript: 5.3.2
transitivePeerDependencies:
-
- eslint
- supports-color
-
typescript@5.3.2: {}
-
unbox-primitive@1.0.2:
dependencies:
-
call-bind: 1.0.7
-
has-bigints: 1.0.2
-
has-symbols: 1.0.3
-
which-boxed-primitive: 1.0.2
-
undici-types@6.19.8: {}
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
-
utilium@0.7.1:
dependencies:
eventemitter3: 5.0.1
-
which-boxed-primitive@1.0.2:
dependencies:
-
is-bigint: 1.0.4
-
is-boolean-object: 1.1.2
-
is-number-object: 1.0.7
-
is-string: 1.0.7
-
is-symbol: 1.0.4
-
which-builtin-type@1.1.4:
dependencies:
-
function.prototype.name: 1.1.6
has-tostringtag: 1.0.2
-
is-async-function: 2.0.0
-
is-date-object: 1.0.5
-
is-finalizationregistry: 1.0.2
-
is-generator-function: 1.0.10
-
is-regex: 1.1.4
-
is-weakref: 1.0.2
isarray: 2.0.5
-
which-boxed-primitive: 1.0.2
which-collection: 1.0.2
-
which-typed-array: 1.1.15
which-collection@1.0.2:
dependencies:
is-map: 2.0.3
is-set: 2.0.3
is-weakmap: 2.0.2
-
is-weakset: 2.0.3
-
which-typed-array@1.1.15:
dependencies:
available-typed-arrays: 1.0.7
-
call-bind: 1.0.7
-
for-each: 0.3.3
-
gopd: 1.0.1
has-tostringtag: 1.0.2
which@2.0.2:
dependencies:
isexe: 2.0.0
yocto-queue@0.1.0: {}
···
autoInstallPeers: true
excludeLinksFromLockfile: false
+
catalogs:
+
dev:
+
'@moonlight-mod/eslint-config':
+
specifier: github:moonlight-mod/eslint-config
+
version: 1.0.1
+
'@types/chrome':
+
specifier: ^0.0.313
+
version: 0.0.313
+
'@types/node':
+
specifier: ^22.14.0
+
version: 22.14.0
+
esbuild:
+
specifier: ^0.19.3
+
version: 0.19.3
+
esbuild-copy-static-files:
+
specifier: ^0.1.0
+
version: 0.1.0
+
eslint:
+
specifier: ^9.12.0
+
version: 9.23.0
+
husky:
+
specifier: ^8.0.3
+
version: 8.0.3
+
prettier:
+
specifier: ^3.1.0
+
version: 3.1.0
+
taze:
+
specifier: ^19.0.4
+
version: 19.0.4
+
typescript:
+
specifier: ^5.3.3
+
version: 5.8.2
+
prod:
+
'@moonlight-mod/lunast':
+
specifier: ^1.0.1
+
version: 1.0.1
+
'@moonlight-mod/mappings':
+
specifier: ^1.1.25
+
version: 1.1.25
+
'@moonlight-mod/moonmap':
+
specifier: ^1.0.5
+
version: 1.0.5
+
'@zenfs/core':
+
specifier: ^2.0.0
+
version: 2.0.0
+
'@zenfs/dom':
+
specifier: ^1.1.3
+
version: 1.1.6
+
microdiff:
+
specifier: ^1.5.0
+
version: 1.5.0
+
nanotar:
+
specifier: ^0.1.1
+
version: 0.1.1
+
importers:
.:
devDependencies:
'@moonlight-mod/eslint-config':
+
specifier: catalog:dev
+
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)
+
'@types/node':
+
specifier: catalog:dev
+
version: 22.14.0
esbuild:
+
specifier: catalog:dev
version: 0.19.3
esbuild-copy-static-files:
+
specifier: catalog:dev
version: 0.1.0
eslint:
+
specifier: catalog:dev
+
version: 9.23.0(jiti@2.4.2)
husky:
+
specifier: catalog:dev
version: 8.0.3
prettier:
+
specifier: catalog:dev
version: 3.1.0
+
taze:
+
specifier: catalog:dev
+
version: 19.0.4
typescript:
+
specifier: catalog:dev
+
version: 5.8.2
packages/browser:
dependencies:
···
specifier: workspace:*
version: link:../web-preload
'@zenfs/core':
+
specifier: catalog:prod
+
version: 2.0.0
'@zenfs/dom':
+
specifier: catalog:prod
+
version: 1.1.6(@zenfs/core@2.0.0)(utilium@1.10.1)
+
devDependencies:
+
'@types/chrome':
+
specifier: catalog:dev
+
version: 0.0.313
packages/core:
dependencies:
···
'@moonlight-mod/types':
specifier: workspace:*
version: link:../types
+
microdiff:
+
specifier: catalog:prod
+
version: 1.5.0
nanotar:
+
specifier: catalog:prod
version: 0.1.1
packages/injector:
···
packages/types:
dependencies:
'@moonlight-mod/lunast':
+
specifier: ^1.0.1
+
version: 1.0.1
'@moonlight-mod/mappings':
+
specifier: ^1.1.25
+
version: 1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)
'@moonlight-mod/moonmap':
+
specifier: ^1.0.5
+
version: 1.0.5
'@types/react':
specifier: ^18.3.10
+
version: 18.3.20
csstype:
+
specifier: ^3.1.3
+
version: 3.1.3
standalone-electron-types:
specifier: ^1.0.0
version: 1.0.0
···
specifier: workspace:*
version: link:../core
'@moonlight-mod/lunast':
+
specifier: catalog:prod
+
version: 1.0.1
'@moonlight-mod/mappings':
+
specifier: catalog:prod
+
version: 1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)
'@moonlight-mod/moonmap':
+
specifier: catalog:prod
+
version: 1.0.5
'@moonlight-mod/types':
specifier: workspace:*
version: link:../types
···
'@aashutoshrathi/word-wrap@1.2.6':
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
engines: {node: '>=0.10.0'}
+
+
'@antfu/ni@24.3.0':
+
resolution: {integrity: sha512-wBSav4mBxvHEW9RbdSo1SWLQ6MAlT0Dc423weC58yOWqW4OcMvtnNDdDrxOZeJ88fEIyPK93gDUWIelBxzSf8g==}
+
hasBin: true
'@esbuild/android-arm64@0.19.3':
resolution: {integrity: sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==}
···
cpu: [x64]
os: [win32]
+
'@eslint-community/eslint-utils@4.5.1':
+
resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
'@eslint-community/regexpp@4.12.1':
+
resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
'@eslint/config-array@0.19.2':
+
resolution: {integrity: sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@eslint/config-helpers@0.2.1':
+
resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@eslint/core@0.12.0':
+
resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@eslint/core@0.13.0':
+
resolution: {integrity: sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@eslint/eslintrc@3.3.1':
+
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@eslint/js@9.23.0':
+
resolution: {integrity: sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@eslint/object-schema@2.1.6':
+
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+
'@eslint/plugin-kit@0.2.8':
+
resolution: {integrity: sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==}
+
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+
'@humanfs/core@0.19.1':
+
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'}
+
'@humanfs/node@0.16.6':
+
resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
engines: {node: '>=18.18.0'}
'@humanwhocodes/module-importer@1.0.1':
···
resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
engines: {node: '>=18.18'}
+
'@humanwhocodes/retry@0.4.2':
+
resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
+
engines: {node: '>=18.18'}
+
+
'@moonlight-mod/eslint-config@https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9':
+
resolution: {tarball: https://codeload.github.com/moonlight-mod/eslint-config/tar.gz/e262ac24e1a0955a9b3e0d66da247a0a8c0446c9}
+
version: 1.0.1
peerDependencies:
eslint: '>= 9'
typescript: '>= 5.3'
+
'@moonlight-mod/lunast@1.0.1':
+
resolution: {integrity: sha512-K3vxzDlfFuYKjciIW2FMlcZ1qrrkAGDGpSBlNqYGtJ0sMt9bRCd2lpSpg6AX/giSljDtmAUXa/5mOfUoDQxjBA==}
+
'@moonlight-mod/mappings@1.1.25':
+
resolution: {integrity: sha512-bgnSN9H/IBdMGxGev6RQKXuzhQxwo1090NhIDHnflguZnjiu2pg/usPfh76bqyhxRuX4SS7tiZSNTwBoSflCLg==}
+
engines: {node: '>=22', npm: pnpm, pnpm: '>=10', yarn: pnpm}
peerDependencies:
+
'@moonlight-mod/lunast': ^1.0.1
+
'@moonlight-mod/moonmap': ^1.0.5
+
'@moonlight-mod/moonmap@1.0.5':
+
resolution: {integrity: sha512-Fdpxj8ghdulKB6TlTnchlCPey2YUKgEf1chuO1ofOIcvlqnVPBcQwSf2S80naOUQpXCDo4dQ+LWSE2fmhdDiiw==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
···
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
+
'@pkgr/core@0.2.0':
+
resolution: {integrity: sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
'@quansync/fs@0.1.2':
+
resolution: {integrity: sha512-ezIadUb1aFhwJLd++WVqVpi9rnlX8vnd4ju7saPhwLHJN1mJgOv0puePTGV+FbtSnWtwoHDT8lAm4kagDZmpCg==}
+
engines: {node: '>=20.0.0'}
+
+
'@types/chroma-js@3.1.0':
+
resolution: {integrity: sha512-Uwl3SOtUkbQ6Ye6ZYu4q4xdLGBzmY839sEHYtOT7i691neeyd+7fXWT5VIkcUSfNwIFrIjQutNYQn9h4q5HFvg==}
+
+
'@types/chrome@0.0.313':
+
resolution: {integrity: sha512-9R5T7gTaYZhkxlu+Ho4wk9FL+y/werWQY2yjGWSqCuiTsqS7nL/BE5UMTP6rU7J+oIG2FRKqrEycHhJATeltVA==}
+
+
'@types/eslint@9.6.1':
+
resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
+
'@types/estree-jsx@1.0.5':
resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
'@types/estree@1.0.7':
+
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+
'@types/fbemitter@2.0.35':
resolution: {integrity: sha512-Xem6d7qUfmouCHntCrRYgDBwbf+WWRd6G+7WEFlEZFZ67LZXiYRvT2LV8wcZa6mIaAil95+ABQdKgB6hPIsnng==}
+
+
'@types/filesystem@0.0.36':
+
resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==}
+
+
'@types/filewriter@0.0.33':
+
resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==}
'@types/flux@3.1.14':
resolution: {integrity: sha512-WRXN0kQPCnqxN0/PgNgc7WBF6c8rbSHsEep3/qBLpsQ824RONdOmTs0TV7XhIW2GDNRAHO2CqCgAFLR5PChosw==}
+
'@types/har-format@1.2.16':
+
resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==}
+
+
'@types/highlightjs@9.12.6':
+
resolution: {integrity: sha512-Qfd1DUrwE851Hc3tExADJY4qY8yeZMt06Xw9AJm/UtpneepJS3MZY29c33BY0wP899veaaHD4gZzYiSuQm84Fg==}
+
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
'@types/lodash@4.17.14':
+
resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==}
+
'@types/node@18.17.17':
resolution: {integrity: sha512-cOxcXsQ2sxiwkykdJqvyFS+MLQPLvIdwh5l6gNg8qF6s+C7XSkEWOZjK+XhUZd+mYvHV/180g2cnCcIl4l06Pw==}
+
'@types/node@22.13.6':
+
resolution: {integrity: sha512-GYmF65GI7417CpZXsEXMjT8goQQDnpRnJnDw6jIYa+le3V/lMazPZ4vZmK1B/9R17fh2VLr2zuy9d/h5xgrLAg==}
+
+
'@types/node@22.14.0':
+
resolution: {integrity: sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==}
+
+
'@types/platform@1.3.6':
+
resolution: {integrity: sha512-ZmSaqHuvzv+jC232cFoz2QqPUkaj6EvMmCrWcx3WRr7xTPVFCMUOTcOq8m2d+Zw1iKRc1kDiaA+jtNrV0hkVew==}
'@types/prop-types@15.7.13':
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
+
'@types/react@18.3.20':
+
resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==}
+
'@typescript-eslint/eslint-plugin@8.29.0':
+
resolution: {integrity: sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
eslint: ^8.57.0 || ^9.0.0
+
typescript: '>=4.8.4 <5.9.0'
+
'@typescript-eslint/parser@8.29.0':
+
resolution: {integrity: sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
+
typescript: '>=4.8.4 <5.9.0'
+
'@typescript-eslint/scope-manager@8.29.0':
+
resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@typescript-eslint/type-utils@8.29.0':
+
resolution: {integrity: sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
+
eslint: ^8.57.0 || ^9.0.0
+
typescript: '>=4.8.4 <5.9.0'
+
'@typescript-eslint/types@8.29.0':
+
resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@typescript-eslint/typescript-estree@8.29.0':
+
resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
+
typescript: '>=4.8.4 <5.9.0'
+
'@typescript-eslint/utils@8.29.0':
+
resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
+
typescript: '>=4.8.4 <5.9.0'
+
'@typescript-eslint/visitor-keys@8.29.0':
+
resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@xterm/xterm@5.5.0':
+
resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==}
+
+
'@zenfs/core@2.0.0':
+
resolution: {integrity: sha512-wOKNFTY1DJ1vdLqKdU7M8cRh0nVYZcDVu7WHuk/3u49hrSwTZVm4PzGxJUjFd8O9Wi3U5nYTbZoN7RX5mS2ldA==}
+
engines: {node: '>= 18'}
hasBin: true
+
'@zenfs/dom@1.1.6':
+
resolution: {integrity: sha512-7SBTWgA0esuEv/TE+N/xk6W/XJf8uBF+LhlPNHQdXds0H7aOy/UYsWv/8glvARe+meDMMidoeWFLzUWoMXfjlA==}
engines: {node: '>= 18'}
peerDependencies:
+
'@zenfs/core': ^2.0.0
+
utilium: ^1.9.0
abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
···
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
acorn@8.14.1:
+
resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
engines: {node: '>=0.4.0'}
hasBin: true
···
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
+
ansis@3.17.0:
+
resolution: {integrity: sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==}
+
engines: {node: '>=14'}
+
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
array-buffer-byte-length@1.0.2:
+
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
engines: {node: '>= 0.4'}
array-includes@3.1.8:
···
resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
engines: {node: '>= 0.4'}
+
array.prototype.flat@1.3.3:
+
resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
engines: {node: '>= 0.4'}
+
array.prototype.flatmap@1.3.3:
+
resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
engines: {node: '>= 0.4'}
array.prototype.tosorted@1.1.4:
resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
engines: {node: '>= 0.4'}
+
arraybuffer.prototype.slice@1.0.4:
+
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
engines: {node: '>= 0.4'}
astring@1.9.0:
resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
hasBin: true
+
+
async-function@1.0.0:
+
resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
+
engines: {node: '>= 0.4'}
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
···
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+
cac@6.7.14:
+
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+
engines: {node: '>=8'}
+
+
call-bind-apply-helpers@1.0.2:
+
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+
engines: {node: '>= 0.4'}
+
+
call-bind@1.0.8:
+
resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==}
+
engines: {node: '>= 0.4'}
+
+
call-bound@1.0.4:
+
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
callsites@3.1.0:
···
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
cross-spawn@7.0.6:
+
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
data-view-buffer@1.0.2:
+
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
engines: {node: '>= 0.4'}
+
data-view-byte-length@1.0.2:
+
resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
engines: {node: '>= 0.4'}
+
data-view-byte-offset@1.0.1:
+
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
engines: {node: '>= 0.4'}
+
debug@4.4.0:
+
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
···
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
+
defu@6.1.4:
+
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+
+
destr@2.0.4:
+
resolution: {integrity: sha512-FCAorltMy7QwX0QU38jOkhrv20LBpsHA8ogzvMhhPHCCKVCaN6GxrB0GGaWEWBUYI4eEjjfJ95RdP6dk9IdMQA==}
+
doctrine@2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
+
dunder-proto@1.0.1:
+
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
+
es-abstract@1.23.9:
+
resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==}
+
engines: {node: '>= 0.4'}
+
+
es-define-property@1.0.1:
+
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
+
es-iterator-helpers@1.2.1:
+
resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
engines: {node: '>= 0.4'}
+
es-object-atoms@1.1.1:
+
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
+
es-set-tostringtag@2.1.0:
+
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
+
es-shim-unscopables@1.1.0:
+
resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
+
engines: {node: '>= 0.4'}
+
es-to-primitive@1.3.0:
+
resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
engines: {node: '>= 0.4'}
esbuild-copy-static-files@0.1.0:
···
peerDependencies:
eslint: '>=7.0.0'
+
eslint-plugin-prettier@5.2.6:
+
resolution: {integrity: sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
'@types/eslint': '>=8.0.0'
eslint: '>=8.0.0'
+
eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
prettier: '>=3.0.0'
peerDependenciesMeta:
'@types/eslint':
···
eslint-config-prettier:
optional: true
+
eslint-plugin-react@7.37.5:
+
resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
engines: {node: '>=4'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
+
eslint-scope@8.3.0:
+
resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
eslint-visitor-keys@4.2.0:
+
resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
eslint@9.23.0:
+
resolution: {integrity: sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
···
jiti:
optional: true
+
espree@10.3.0:
+
resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
esquery@1.6.0:
+
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
engines: {node: '>=0.10'}
esrecurse@4.3.0:
···
fastq@1.17.1:
resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+
fdir@6.4.3:
+
resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==}
+
peerDependencies:
+
picomatch: ^3 || ^4
+
peerDependenciesMeta:
+
picomatch:
+
optional: true
+
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
···
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
+
find-up-simple@1.0.1:
+
resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==}
+
engines: {node: '>=18'}
+
find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
···
flatted@3.2.9:
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
+
for-each@0.3.5:
+
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
+
engines: {node: '>= 0.4'}
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
function.prototype.name@1.1.8:
+
resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==}
engines: {node: '>= 0.4'}
functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
fzf@0.5.2:
+
resolution: {integrity: sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==}
+
+
get-intrinsic@1.3.0:
+
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
+
get-proto@1.0.1:
+
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+
engines: {node: '>= 0.4'}
+
+
get-symbol-description@1.1.0:
+
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
engines: {node: '>= 0.4'}
glob-parent@5.1.2:
···
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
+
gopd@1.2.0:
+
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+
engines: {node: '>= 0.4'}
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
has-bigints@1.1.0:
+
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
+
engines: {node: '>= 0.4'}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
···
has-property-descriptors@1.0.2:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
has-proto@1.2.0:
+
resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
engines: {node: '>= 0.4'}
+
has-symbols@1.1.0:
+
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
···
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
···
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
engines: {node: '>=0.8.19'}
+
internal-slot@1.1.0:
+
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
+
is-array-buffer@3.0.5:
+
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'}
+
is-async-function@2.1.1:
+
resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
engines: {node: '>= 0.4'}
+
is-bigint@1.1.0:
+
resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
+
engines: {node: '>= 0.4'}
+
is-boolean-object@1.2.2:
+
resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
engines: {node: '>= 0.4'}
is-callable@1.2.7:
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
engines: {node: '>= 0.4'}
+
is-core-module@2.16.1:
+
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
engines: {node: '>= 0.4'}
+
is-data-view@1.0.2:
+
resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
engines: {node: '>= 0.4'}
+
is-date-object@1.1.0:
+
resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
engines: {node: '>= 0.4'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
+
is-finalizationregistry@1.1.1:
+
resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
+
engines: {node: '>= 0.4'}
+
is-generator-function@1.1.0:
+
resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
engines: {node: '>= 0.4'}
is-glob@4.0.3:
···
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
engines: {node: '>= 0.4'}
+
is-number-object@1.1.1:
+
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'}
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
+
is-regex@1.2.1:
+
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
is-set@2.0.3:
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
engines: {node: '>= 0.4'}
+
is-shared-array-buffer@1.0.4:
+
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
engines: {node: '>= 0.4'}
+
is-string@1.1.1:
+
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
engines: {node: '>= 0.4'}
+
is-symbol@1.1.1:
+
resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
engines: {node: '>= 0.4'}
+
is-typed-array@1.1.15:
+
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
engines: {node: '>= 0.4'}
is-weakmap@2.0.2:
resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
engines: {node: '>= 0.4'}
+
is-weakref@1.1.1:
+
resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
+
engines: {node: '>= 0.4'}
+
is-weakset@2.0.4:
+
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
engines: {node: '>= 0.4'}
isarray@2.0.5:
···
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
iterator.prototype@1.1.5:
+
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
engines: {node: '>= 0.4'}
+
+
jiti@2.4.2:
+
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
+
hasBin: true
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
···
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
+
math-intrinsics@1.1.0:
+
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+
engines: {node: '>= 0.4'}
+
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
···
resolution: {integrity: sha512-OyvYIOgpzXREySYJ1cqEb2pOKdeQMTfF9M8dRU6nC4hi/GXMmNpe9ssZCrSoTHazu05BSAoRBN/uYeco+ymfOg==}
engines: {node: '>=18.0.0'}
+
microdiff@1.5.0:
+
resolution: {integrity: sha512-Drq+/THMvDdzRYrK0oxJmOKiC24ayUV8ahrt8l3oRK51PWt6gdtrIGrlIH3pT/lFh1z93FbAcidtsHcWbnRz8Q==}
+
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
+
mimic-function@5.0.1:
+
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
+
engines: {node: '>=18'}
+
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
···
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
+
ms@2.1.3:
+
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nanotar@0.1.1:
resolution: {integrity: sha512-AiJsGsSF3O0havL1BydvI4+wR76sKT+okKRwWIaK96cZUnXqH0uNBOsHlbwZq3+m2BR1VKqHDVudl3gO4mYjpQ==}
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+
node-fetch-native@1.6.6:
+
resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
+
object-inspect@1.13.4:
+
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
engines: {node: '>= 0.4'}
object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
+
object.assign@4.1.7:
+
resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
engines: {node: '>= 0.4'}
+
object.entries@1.1.9:
+
resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
engines: {node: '>= 0.4'}
object.fromentries@2.0.8:
resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
engines: {node: '>= 0.4'}
+
object.values@1.2.1:
+
resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
engines: {node: '>= 0.4'}
+
+
ofetch@1.4.1:
+
resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==}
+
+
onetime@7.0.0:
+
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
+
engines: {node: '>=18'}
optionator@0.9.3:
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
engines: {node: '>= 0.8.0'}
+
own-keys@1.0.1:
+
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
+
engines: {node: '>= 0.4'}
+
p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
···
p-locate@5.0.0:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
+
+
package-manager-detector@1.1.0:
+
resolution: {integrity: sha512-Y8f9qUlBzW8qauJjd/eu6jlpJZsuPJm2ZAV0cDVd420o4EdpH5RPdoCv+60/TdJflGatr4sDfpAL6ArWZbM5tA==}
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
···
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
pathe@2.0.3:
+
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
+
picomatch@4.0.2:
+
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+
engines: {node: '>=12'}
+
+
pnpm-workspace-yaml@0.3.1:
+
resolution: {integrity: sha512-3nW5RLmREmZ8Pm8MbPsO2RM+99RRjYd25ynj3NV0cFsN7CcEl4sDFzgoFmSyduFwxFQ2Qbu3y2UdCh6HlyUOeA==}
+
+
possible-typed-array-names@1.1.0:
+
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
prelude-ls@1.2.1:
···
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
+
quansync@0.2.10:
+
resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==}
+
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
···
resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
reflect.getprototypeof@1.0.10:
+
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
+
regexp.prototype.flags@1.5.4:
+
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
resolve-from@4.0.0:
···
resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
hasBin: true
+
restore-cursor@5.1.0:
+
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
+
engines: {node: '>=18'}
+
reusify@1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
···
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
safe-array-concat@1.1.3:
+
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
engines: {node: '>=0.4'}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
safe-push-apply@1.0.0:
+
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
+
engines: {node: '>= 0.4'}
+
+
safe-regex-test@1.1.0:
+
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
+
semver@7.7.1:
+
resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
engines: {node: '>=10'}
hasBin: true
···
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
engines: {node: '>= 0.4'}
+
set-proto@1.0.0:
+
resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
+
engines: {node: '>= 0.4'}
+
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
···
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
+
side-channel-list@1.0.0:
+
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
engines: {node: '>= 0.4'}
+
side-channel-map@1.0.1:
+
resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+
engines: {node: '>= 0.4'}
+
+
side-channel-weakmap@1.0.2:
+
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+
engines: {node: '>= 0.4'}
+
+
side-channel@1.1.0:
+
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+
engines: {node: '>= 0.4'}
+
+
signal-exit@4.1.0:
+
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+
engines: {node: '>=14'}
+
standalone-electron-types@1.0.0:
resolution: {integrity: sha512-0HOi/tlTz3mjWhsAz4uRbpQcHMZ+ifj1JzWW9nugykOHClBBG77ps8QinrzX1eow4Iw2pnC+RFaSYRgufF4BOg==}
+
string.prototype.matchall@4.0.12:
+
resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
engines: {node: '>= 0.4'}
string.prototype.repeat@1.0.0:
resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
+
string.prototype.trim@1.2.10:
+
resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
engines: {node: '>= 0.4'}
+
string.prototype.trimend@1.0.9:
+
resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
+
engines: {node: '>= 0.4'}
string.prototype.trimstart@1.0.8:
resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
···
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+
synckit@0.11.1:
+
resolution: {integrity: sha512-fWZqNBZNNFp/7mTUy1fSsydhKsAKJ+u90Nk7kOK5Gcq9vObaqLBLjWFDBkyVU9Vvc6Y71VbOevMuGhqv02bT+Q==}
engines: {node: ^14.18.0 || >=16.0.0}
+
taze@19.0.4:
+
resolution: {integrity: sha512-bviyNotzqcIWpVBCC4QYVb2yupzKyUDGQi2m/8GERdiPaudVMtgAqaE98+x0cDDaByYRMJCyhQWM04ikUL6+kQ==}
+
hasBin: true
+
+
tinyexec@1.0.1:
+
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
+
+
tinyglobby@0.2.12:
+
resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==}
+
engines: {node: '>=12.0.0'}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+
ts-api-utils@2.1.0:
+
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
+
engines: {node: '>=18.12'}
peerDependencies:
+
typescript: '>=4.8.4'
+
tslib@2.8.1:
+
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
+
typed-array-buffer@1.0.3:
+
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
engines: {node: '>= 0.4'}
+
typed-array-byte-length@1.0.3:
+
resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
engines: {node: '>= 0.4'}
+
typed-array-byte-offset@1.0.4:
+
resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
engines: {node: '>= 0.4'}
+
typed-array-length@1.0.7:
+
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
+
typescript-eslint@8.29.0:
+
resolution: {integrity: sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
+
eslint: ^8.57.0 || ^9.0.0
+
typescript: '>=4.8.4 <5.9.0'
+
typescript@5.8.2:
+
resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
engines: {node: '>=14.17'}
hasBin: true
+
ufo@1.5.4:
+
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
+
unbox-primitive@1.1.0:
+
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
+
engines: {node: '>= 0.4'}
+
+
unconfig@7.3.1:
+
resolution: {integrity: sha512-LH5WL+un92tGAzWS87k7LkAfwpMdm7V0IXG2FxEjZz/QxiIW5J5LkcrKQThj0aRz6+h/lFmKI9EUXmK/T0bcrw==}
+
+
undici-types@6.20.0:
+
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
+
+
undici-types@6.21.0:
+
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
utilium@1.10.1:
+
resolution: {integrity: sha512-GQINDTb/ocyz4acQj3GXAe0wipYxws6L+9ouqaq10KlInTk9DGvW9TJd0pYa/Xu3cppNnZuB4T/sBuSXpcN2ng==}
+
which-boxed-primitive@1.1.1:
+
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
+
engines: {node: '>= 0.4'}
+
which-builtin-type@1.2.1:
+
resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
engines: {node: '>= 0.4'}
which-collection@1.0.2:
resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
engines: {node: '>= 0.4'}
+
which-typed-array@1.1.19:
+
resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==}
engines: {node: '>= 0.4'}
which@2.0.2:
···
engines: {node: '>= 8'}
hasBin: true
+
yaml@2.7.1:
+
resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==}
+
engines: {node: '>= 14'}
+
hasBin: true
+
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
+
zustand@5.0.3:
+
resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==}
+
engines: {node: '>=12.20.0'}
+
peerDependencies:
+
'@types/react': '>=18.0.0'
+
immer: '>=9.0.6'
+
react: '>=18.0.0'
+
use-sync-external-store: '>=1.2.0'
+
peerDependenciesMeta:
+
'@types/react':
+
optional: true
+
immer:
+
optional: true
+
react:
+
optional: true
+
use-sync-external-store:
+
optional: true
+
snapshots:
'@aashutoshrathi/word-wrap@1.2.6': {}
+
+
'@antfu/ni@24.3.0':
+
dependencies:
+
ansis: 3.17.0
+
fzf: 0.5.2
+
package-manager-detector: 1.1.0
+
tinyexec: 1.0.1
'@esbuild/android-arm64@0.19.3':
optional: true
···
'@esbuild/win32-x64@0.19.3':
optional: true
+
'@eslint-community/eslint-utils@4.5.1(eslint@9.23.0(jiti@2.4.2))':
dependencies:
+
eslint: 9.23.0(jiti@2.4.2)
eslint-visitor-keys: 3.4.3
+
'@eslint-community/regexpp@4.12.1': {}
+
'@eslint/config-array@0.19.2':
dependencies:
+
'@eslint/object-schema': 2.1.6
+
debug: 4.4.0
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
+
'@eslint/config-helpers@0.2.1': {}
+
+
'@eslint/core@0.12.0':
+
dependencies:
+
'@types/json-schema': 7.0.15
+
+
'@eslint/core@0.13.0':
+
dependencies:
+
'@types/json-schema': 7.0.15
+
'@eslint/eslintrc@3.3.1':
dependencies:
ajv: 6.12.6
+
debug: 4.4.0
+
espree: 10.3.0
globals: 14.0.0
+
ignore: 5.3.2
import-fresh: 3.3.0
js-yaml: 4.1.0
minimatch: 3.1.2
···
transitivePeerDependencies:
- supports-color
+
'@eslint/js@9.23.0': {}
+
'@eslint/object-schema@2.1.6': {}
+
'@eslint/plugin-kit@0.2.8':
dependencies:
+
'@eslint/core': 0.13.0
levn: 0.4.1
+
'@humanfs/core@0.19.1': {}
+
'@humanfs/node@0.16.6':
dependencies:
+
'@humanfs/core': 0.19.1
'@humanwhocodes/retry': 0.3.1
'@humanwhocodes/module-importer@1.0.1': {}
'@humanwhocodes/retry@0.3.1': {}
+
'@humanwhocodes/retry@0.4.2': {}
+
+
'@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)':
dependencies:
+
'@eslint/js': 9.23.0
+
eslint: 9.23.0(jiti@2.4.2)
+
eslint-config-prettier: 9.1.0(eslint@9.23.0(jiti@2.4.2))
+
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)
+
eslint-plugin-react: 7.37.5(eslint@9.23.0(jiti@2.4.2))
+
typescript: 5.8.2
+
typescript-eslint: 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
transitivePeerDependencies:
- '@types/eslint'
- prettier
- supports-color
+
'@moonlight-mod/lunast@1.0.1':
dependencies:
astring: 1.9.0
estree-toolkit: 1.7.8
meriyah: 6.0.1
+
'@moonlight-mod/mappings@1.1.25(@moonlight-mod/lunast@1.0.1)(@moonlight-mod/moonmap@1.0.5)':
dependencies:
+
'@moonlight-mod/lunast': 1.0.1
+
'@moonlight-mod/moonmap': 1.0.5
+
'@types/chroma-js': 3.1.0
'@types/flux': 3.1.14
+
'@types/highlightjs': 9.12.6
+
'@types/lodash': 4.17.14
+
'@types/platform': 1.3.6
+
'@types/react': 18.3.20
csstype: 3.1.3
+
zustand: 5.0.3(@types/react@18.3.20)
+
transitivePeerDependencies:
+
- immer
+
- react
+
- use-sync-external-store
+
'@moonlight-mod/moonmap@1.0.5': {}
'@nodelib/fs.scandir@2.1.5':
dependencies:
···
'@nodelib/fs.scandir': 2.1.5
fastq: 1.17.1
+
'@pkgr/core@0.2.0': {}
+
+
'@quansync/fs@0.1.2':
+
dependencies:
+
quansync: 0.2.10
+
+
'@types/chroma-js@3.1.0': {}
+
+
'@types/chrome@0.0.313':
+
dependencies:
+
'@types/filesystem': 0.0.36
+
'@types/har-format': 1.2.16
+
+
'@types/eslint@9.6.1':
+
dependencies:
+
'@types/estree': 1.0.7
+
'@types/json-schema': 7.0.15
+
optional: true
'@types/estree-jsx@1.0.5':
dependencies:
···
'@types/estree@1.0.6': {}
+
'@types/estree@1.0.7':
+
optional: true
+
'@types/fbemitter@2.0.35': {}
+
'@types/filesystem@0.0.36':
+
dependencies:
+
'@types/filewriter': 0.0.33
+
+
'@types/filewriter@0.0.33': {}
+
'@types/flux@3.1.14':
dependencies:
'@types/fbemitter': 2.0.35
+
'@types/react': 18.3.20
+
+
'@types/har-format@1.2.16': {}
+
+
'@types/highlightjs@9.12.6': {}
'@types/json-schema@7.0.15': {}
+
'@types/lodash@4.17.14': {}
+
'@types/node@18.17.17': {}
+
'@types/node@22.13.6':
+
dependencies:
+
undici-types: 6.20.0
+
+
'@types/node@22.14.0':
dependencies:
+
undici-types: 6.21.0
+
+
'@types/platform@1.3.6': {}
'@types/prop-types@15.7.13': {}
+
'@types/react@18.3.20':
dependencies:
'@types/prop-types': 15.7.13
csstype: 3.1.3
+
'@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)':
dependencies:
+
'@eslint-community/regexpp': 4.12.1
+
'@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+
'@typescript-eslint/scope-manager': 8.29.0
+
'@typescript-eslint/type-utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+
'@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+
'@typescript-eslint/visitor-keys': 8.29.0
+
eslint: 9.23.0(jiti@2.4.2)
graphemer: 1.4.0
ignore: 5.3.2
natural-compare: 1.4.0
+
ts-api-utils: 2.1.0(typescript@5.8.2)
+
typescript: 5.8.2
transitivePeerDependencies:
- supports-color
+
'@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
dependencies:
+
'@typescript-eslint/scope-manager': 8.29.0
+
'@typescript-eslint/types': 8.29.0
+
'@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
+
'@typescript-eslint/visitor-keys': 8.29.0
+
debug: 4.4.0
+
eslint: 9.23.0(jiti@2.4.2)
+
typescript: 5.8.2
transitivePeerDependencies:
- supports-color
+
'@typescript-eslint/scope-manager@8.29.0':
dependencies:
+
'@typescript-eslint/types': 8.29.0
+
'@typescript-eslint/visitor-keys': 8.29.0
+
'@typescript-eslint/type-utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
dependencies:
+
'@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
+
'@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+
debug: 4.4.0
+
eslint: 9.23.0(jiti@2.4.2)
+
ts-api-utils: 2.1.0(typescript@5.8.2)
+
typescript: 5.8.2
transitivePeerDependencies:
- supports-color
+
'@typescript-eslint/types@8.29.0': {}
+
'@typescript-eslint/typescript-estree@8.29.0(typescript@5.8.2)':
dependencies:
+
'@typescript-eslint/types': 8.29.0
+
'@typescript-eslint/visitor-keys': 8.29.0
+
debug: 4.4.0
fast-glob: 3.3.2
is-glob: 4.0.3
minimatch: 9.0.5
+
semver: 7.7.1
+
ts-api-utils: 2.1.0(typescript@5.8.2)
+
typescript: 5.8.2
transitivePeerDependencies:
- supports-color
+
'@typescript-eslint/utils@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
dependencies:
+
'@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
+
'@typescript-eslint/scope-manager': 8.29.0
+
'@typescript-eslint/types': 8.29.0
+
'@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2)
+
eslint: 9.23.0(jiti@2.4.2)
+
typescript: 5.8.2
transitivePeerDependencies:
- supports-color
+
'@typescript-eslint/visitor-keys@8.29.0':
dependencies:
+
'@typescript-eslint/types': 8.29.0
+
eslint-visitor-keys: 4.2.0
+
'@xterm/xterm@5.5.0':
+
optional: true
+
+
'@zenfs/core@2.0.0':
dependencies:
+
'@types/node': 22.13.6
buffer: 6.0.3
eventemitter3: 5.0.1
readable-stream: 4.5.2
+
utilium: 1.10.1
+
'@zenfs/dom@1.1.6(@zenfs/core@2.0.0)(utilium@1.10.1)':
dependencies:
+
'@zenfs/core': 2.0.0
+
utilium: 1.10.1
abort-controller@3.0.0:
dependencies:
event-target-shim: 5.0.1
+
acorn-jsx@5.3.2(acorn@8.14.1):
dependencies:
+
acorn: 8.14.1
+
acorn@8.14.1: {}
ajv@6.12.6:
dependencies:
···
dependencies:
color-convert: 2.0.1
+
ansis@3.17.0: {}
+
argparse@2.0.1: {}
+
array-buffer-byte-length@1.0.2:
dependencies:
+
call-bound: 1.0.4
+
is-array-buffer: 3.0.5
array-includes@3.1.8:
dependencies:
+
call-bind: 1.0.8
define-properties: 1.2.1
+
es-abstract: 1.23.9
+
es-object-atoms: 1.1.1
+
get-intrinsic: 1.3.0
+
is-string: 1.1.1
array.prototype.findlast@1.2.5:
dependencies:
+
call-bind: 1.0.8
define-properties: 1.2.1
+
es-abstract: 1.23.9
es-errors: 1.3.0
+
es-object-atoms: 1.1.1
+
es-shim-unscopables: 1.1.0
+
array.prototype.flat@1.3.3:
dependencies:
+
call-bind: 1.0.8
define-properties: 1.2.1
+
es-abstract: 1.23.9
+
es-shim-unscopables: 1.1.0
+
array.prototype.flatmap@1.3.3:
dependencies:
+
call-bind: 1.0.8
define-properties: 1.2.1
+
es-abstract: 1.23.9
+
es-shim-unscopables: 1.1.0
array.prototype.tosorted@1.1.4:
dependencies:
+
call-bind: 1.0.8
define-properties: 1.2.1
+
es-abstract: 1.23.9
es-errors: 1.3.0
+
es-shim-unscopables: 1.1.0
+
arraybuffer.prototype.slice@1.0.4:
dependencies:
+
array-buffer-byte-length: 1.0.2
+
call-bind: 1.0.8
define-properties: 1.2.1
+
es-abstract: 1.23.9
es-errors: 1.3.0
+
get-intrinsic: 1.3.0
+
is-array-buffer: 3.0.5
astring@1.9.0: {}
+
async-function@1.0.0: {}
+
available-typed-arrays@1.0.7:
dependencies:
+
possible-typed-array-names: 1.1.0
balanced-match@1.0.2: {}
···
base64-js: 1.5.1
ieee754: 1.2.1
+
cac@6.7.14: {}
+
+
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
+
+
call-bind@1.0.8:
+
dependencies:
+
call-bind-apply-helpers: 1.0.2
+
es-define-property: 1.0.1
+
get-intrinsic: 1.3.0
set-function-length: 1.2.2
+
+
call-bound@1.0.4:
+
dependencies:
+
call-bind-apply-helpers: 1.0.2
+
get-intrinsic: 1.3.0
callsites@3.1.0: {}
···
concat-map@0.0.1: {}
+
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
shebang-command: 2.0.0
which: 2.0.2
csstype@3.1.3: {}
+
data-view-buffer@1.0.2:
dependencies:
+
call-bound: 1.0.4
es-errors: 1.3.0
+
is-data-view: 1.0.2
+
data-view-byte-length@1.0.2:
dependencies:
+
call-bound: 1.0.4
es-errors: 1.3.0
+
is-data-view: 1.0.2
+
data-view-byte-offset@1.0.1:
dependencies:
+
call-bound: 1.0.4
es-errors: 1.3.0
+
is-data-view: 1.0.2
+
debug@4.4.0:
dependencies:
+
ms: 2.1.3
deep-is@0.1.4: {}
define-data-property@1.1.4:
dependencies:
+
es-define-property: 1.0.1
es-errors: 1.3.0
+
gopd: 1.2.0
define-properties@1.2.1:
dependencies:
···
has-property-descriptors: 1.0.2
object-keys: 1.1.1
+
defu@6.1.4: {}
+
+
destr@2.0.4: {}
+
doctrine@2.1.0:
dependencies:
esutils: 2.0.3
+
dunder-proto@1.0.1:
dependencies:
+
call-bind-apply-helpers: 1.0.2
+
es-errors: 1.3.0
+
gopd: 1.2.0
+
+
es-abstract@1.23.9:
+
dependencies:
+
array-buffer-byte-length: 1.0.2
+
arraybuffer.prototype.slice: 1.0.4
available-typed-arrays: 1.0.7
+
call-bind: 1.0.8
+
call-bound: 1.0.4
+
data-view-buffer: 1.0.2
+
data-view-byte-length: 1.0.2
+
data-view-byte-offset: 1.0.1
+
es-define-property: 1.0.1
es-errors: 1.3.0
+
es-object-atoms: 1.1.1
+
es-set-tostringtag: 2.1.0
+
es-to-primitive: 1.3.0
+
function.prototype.name: 1.1.8
+
get-intrinsic: 1.3.0
+
get-proto: 1.0.1
+
get-symbol-description: 1.1.0
globalthis: 1.0.4
+
gopd: 1.2.0
has-property-descriptors: 1.0.2
+
has-proto: 1.2.0
+
has-symbols: 1.1.0
hasown: 2.0.2
+
internal-slot: 1.1.0
+
is-array-buffer: 3.0.5
is-callable: 1.2.7
+
is-data-view: 1.0.2
+
is-regex: 1.2.1
+
is-shared-array-buffer: 1.0.4
+
is-string: 1.1.1
+
is-typed-array: 1.1.15
+
is-weakref: 1.1.1
+
math-intrinsics: 1.1.0
+
object-inspect: 1.13.4
object-keys: 1.1.1
+
object.assign: 4.1.7
+
own-keys: 1.0.1
+
regexp.prototype.flags: 1.5.4
+
safe-array-concat: 1.1.3
+
safe-push-apply: 1.0.0
+
safe-regex-test: 1.1.0
+
set-proto: 1.0.0
+
string.prototype.trim: 1.2.10
+
string.prototype.trimend: 1.0.9
string.prototype.trimstart: 1.0.8
+
typed-array-buffer: 1.0.3
+
typed-array-byte-length: 1.0.3
+
typed-array-byte-offset: 1.0.4
+
typed-array-length: 1.0.7
+
unbox-primitive: 1.1.0
+
which-typed-array: 1.1.19
+
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
+
es-iterator-helpers@1.2.1:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
define-properties: 1.2.1
+
es-abstract: 1.23.9
es-errors: 1.3.0
+
es-set-tostringtag: 2.1.0
function-bind: 1.1.2
+
get-intrinsic: 1.3.0
globalthis: 1.0.4
+
gopd: 1.2.0
has-property-descriptors: 1.0.2
+
has-proto: 1.2.0
+
has-symbols: 1.1.0
+
internal-slot: 1.1.0
+
iterator.prototype: 1.1.5
+
safe-array-concat: 1.1.3
+
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
+
es-set-tostringtag@2.1.0:
dependencies:
+
es-errors: 1.3.0
+
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
+
es-shim-unscopables@1.1.0:
dependencies:
hasown: 2.0.2
+
es-to-primitive@1.3.0:
dependencies:
is-callable: 1.2.7
+
is-date-object: 1.1.0
+
is-symbol: 1.1.1
esbuild-copy-static-files@0.1.0: {}
···
escape-string-regexp@4.0.0: {}
+
eslint-config-prettier@9.1.0(eslint@9.23.0(jiti@2.4.2)):
dependencies:
+
eslint: 9.23.0(jiti@2.4.2)
+
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):
dependencies:
+
eslint: 9.23.0(jiti@2.4.2)
prettier: 3.1.0
prettier-linter-helpers: 1.0.0
+
synckit: 0.11.1
optionalDependencies:
+
'@types/eslint': 9.6.1
+
eslint-config-prettier: 9.1.0(eslint@9.23.0(jiti@2.4.2))
+
eslint-plugin-react@7.37.5(eslint@9.23.0(jiti@2.4.2)):
dependencies:
array-includes: 3.1.8
array.prototype.findlast: 1.2.5
+
array.prototype.flatmap: 1.3.3
array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
+
es-iterator-helpers: 1.2.1
+
eslint: 9.23.0(jiti@2.4.2)
estraverse: 5.3.0
hasown: 2.0.2
jsx-ast-utils: 3.3.5
minimatch: 3.1.2
+
object.entries: 1.1.9
object.fromentries: 2.0.8
+
object.values: 1.2.1
prop-types: 15.8.1
resolve: 2.0.0-next.5
semver: 6.3.1
+
string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0
+
eslint-scope@8.3.0:
dependencies:
esrecurse: 4.3.0
estraverse: 5.3.0
eslint-visitor-keys@3.4.3: {}
+
eslint-visitor-keys@4.2.0: {}
+
eslint@9.23.0(jiti@2.4.2):
dependencies:
+
'@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
+
'@eslint-community/regexpp': 4.12.1
+
'@eslint/config-array': 0.19.2
+
'@eslint/config-helpers': 0.2.1
+
'@eslint/core': 0.12.0
+
'@eslint/eslintrc': 3.3.1
+
'@eslint/js': 9.23.0
+
'@eslint/plugin-kit': 0.2.8
+
'@humanfs/node': 0.16.6
'@humanwhocodes/module-importer': 1.0.1
+
'@humanwhocodes/retry': 0.4.2
'@types/estree': 1.0.6
'@types/json-schema': 7.0.15
ajv: 6.12.6
chalk: 4.1.2
+
cross-spawn: 7.0.6
+
debug: 4.4.0
escape-string-regexp: 4.0.0
+
eslint-scope: 8.3.0
+
eslint-visitor-keys: 4.2.0
+
espree: 10.3.0
+
esquery: 1.6.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 8.0.0
find-up: 5.0.0
glob-parent: 6.0.2
+
ignore: 5.3.2
imurmurhash: 0.1.4
is-glob: 4.0.3
json-stable-stringify-without-jsonify: 1.0.1
···
minimatch: 3.1.2
natural-compare: 1.4.0
optionator: 0.9.3
+
optionalDependencies:
+
jiti: 2.4.2
transitivePeerDependencies:
- supports-color
+
espree@10.3.0:
dependencies:
+
acorn: 8.14.1
+
acorn-jsx: 5.3.2(acorn@8.14.1)
+
eslint-visitor-keys: 4.2.0
+
esquery@1.6.0:
dependencies:
estraverse: 5.3.0
···
dependencies:
reusify: 1.0.4
+
fdir@6.4.3(picomatch@4.0.2):
+
optionalDependencies:
+
picomatch: 4.0.2
+
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
···
dependencies:
to-regex-range: 5.0.1
+
find-up-simple@1.0.1: {}
+
find-up@5.0.0:
dependencies:
locate-path: 6.0.0
···
flatted@3.2.9: {}
+
for-each@0.3.5:
dependencies:
is-callable: 1.2.7
function-bind@1.1.2: {}
+
function.prototype.name@1.1.8:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
define-properties: 1.2.1
functions-have-names: 1.2.3
+
hasown: 2.0.2
+
is-callable: 1.2.7
functions-have-names@1.2.3: {}
+
fzf@0.5.2: {}
+
+
get-intrinsic@1.3.0:
dependencies:
+
call-bind-apply-helpers: 1.0.2
+
es-define-property: 1.0.1
es-errors: 1.3.0
+
es-object-atoms: 1.1.1
function-bind: 1.1.2
+
get-proto: 1.0.1
+
gopd: 1.2.0
+
has-symbols: 1.1.0
hasown: 2.0.2
+
math-intrinsics: 1.1.0
+
get-proto@1.0.1:
+
dependencies:
+
dunder-proto: 1.0.1
+
es-object-atoms: 1.1.1
+
+
get-symbol-description@1.1.0:
dependencies:
+
call-bound: 1.0.4
es-errors: 1.3.0
+
get-intrinsic: 1.3.0
glob-parent@5.1.2:
dependencies:
···
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
+
gopd: 1.2.0
+
gopd@1.2.0: {}
graphemer@1.4.0: {}
+
has-bigints@1.1.0: {}
has-flag@4.0.0: {}
has-property-descriptors@1.0.2:
dependencies:
+
es-define-property: 1.0.1
+
has-proto@1.2.0:
+
dependencies:
+
dunder-proto: 1.0.1
+
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
+
has-symbols: 1.1.0
hasown@2.0.2:
dependencies:
···
husky@8.0.3: {}
ieee754@1.2.1: {}
ignore@5.3.2: {}
···
imurmurhash@0.1.4: {}
+
internal-slot@1.1.0:
dependencies:
es-errors: 1.3.0
hasown: 2.0.2
+
side-channel: 1.1.0
+
is-array-buffer@3.0.5:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
+
get-intrinsic: 1.3.0
+
is-async-function@2.1.1:
dependencies:
+
async-function: 1.0.0
+
call-bound: 1.0.4
+
get-proto: 1.0.1
has-tostringtag: 1.0.2
+
safe-regex-test: 1.1.0
+
is-bigint@1.1.0:
dependencies:
+
has-bigints: 1.1.0
+
is-boolean-object@1.2.2:
dependencies:
+
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-callable@1.2.7: {}
+
is-core-module@2.16.1:
dependencies:
hasown: 2.0.2
+
is-data-view@1.0.2:
dependencies:
+
call-bound: 1.0.4
+
get-intrinsic: 1.3.0
+
is-typed-array: 1.1.15
+
is-date-object@1.1.0:
dependencies:
+
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-extglob@2.1.1: {}
+
is-finalizationregistry@1.1.1:
dependencies:
+
call-bound: 1.0.4
+
is-generator-function@1.1.0:
dependencies:
+
call-bound: 1.0.4
+
get-proto: 1.0.1
has-tostringtag: 1.0.2
+
safe-regex-test: 1.1.0
is-glob@4.0.3:
dependencies:
···
is-map@2.0.3: {}
+
is-number-object@1.1.1:
dependencies:
+
call-bound: 1.0.4
has-tostringtag: 1.0.2
is-number@7.0.0: {}
+
is-regex@1.2.1:
dependencies:
+
call-bound: 1.0.4
+
gopd: 1.2.0
has-tostringtag: 1.0.2
+
hasown: 2.0.2
is-set@2.0.3: {}
+
is-shared-array-buffer@1.0.4:
dependencies:
+
call-bound: 1.0.4
+
is-string@1.1.1:
dependencies:
+
call-bound: 1.0.4
has-tostringtag: 1.0.2
+
is-symbol@1.1.1:
dependencies:
+
call-bound: 1.0.4
+
has-symbols: 1.1.0
+
safe-regex-test: 1.1.0
+
is-typed-array@1.1.15:
dependencies:
+
which-typed-array: 1.1.19
is-weakmap@2.0.2: {}
+
is-weakref@1.1.1:
dependencies:
+
call-bound: 1.0.4
+
is-weakset@2.0.4:
dependencies:
+
call-bound: 1.0.4
+
get-intrinsic: 1.3.0
isarray@2.0.5: {}
isexe@2.0.0: {}
+
iterator.prototype@1.1.5:
dependencies:
+
define-data-property: 1.1.4
+
es-object-atoms: 1.1.1
+
get-intrinsic: 1.3.0
+
get-proto: 1.0.1
+
has-symbols: 1.1.0
set-function-name: 2.0.2
+
+
jiti@2.4.2: {}
js-tokens@4.0.0: {}
···
jsx-ast-utils@3.3.5:
dependencies:
array-includes: 3.1.8
+
array.prototype.flat: 1.3.3
+
object.assign: 4.1.7
+
object.values: 1.2.1
keyv@4.5.4:
dependencies:
···
dependencies:
js-tokens: 4.0.0
+
math-intrinsics@1.1.0: {}
+
merge2@1.4.1: {}
meriyah@6.0.1: {}
+
microdiff@1.5.0: {}
+
micromatch@4.0.8:
dependencies:
braces: 3.0.3
picomatch: 2.3.1
+
+
mimic-function@5.0.1: {}
minimatch@3.1.2:
dependencies:
···
dependencies:
brace-expansion: 2.0.1
+
ms@2.1.3: {}
nanotar@0.1.1: {}
natural-compare@1.4.0: {}
+
+
node-fetch-native@1.6.6: {}
object-assign@4.1.1: {}
+
object-inspect@1.13.4: {}
object-keys@1.1.1: {}
+
object.assign@4.1.7:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
define-properties: 1.2.1
+
es-object-atoms: 1.1.1
+
has-symbols: 1.1.0
object-keys: 1.1.1
+
object.entries@1.1.9:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
define-properties: 1.2.1
+
es-object-atoms: 1.1.1
object.fromentries@2.0.8:
dependencies:
+
call-bind: 1.0.8
define-properties: 1.2.1
+
es-abstract: 1.23.9
+
es-object-atoms: 1.1.1
+
object.values@1.2.1:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
define-properties: 1.2.1
+
es-object-atoms: 1.1.1
+
+
ofetch@1.4.1:
+
dependencies:
+
destr: 2.0.4
+
node-fetch-native: 1.6.6
+
ufo: 1.5.4
+
+
onetime@7.0.0:
+
dependencies:
+
mimic-function: 5.0.1
optionator@0.9.3:
dependencies:
···
prelude-ls: 1.2.1
type-check: 0.4.0
+
own-keys@1.0.1:
+
dependencies:
+
get-intrinsic: 1.3.0
+
object-keys: 1.1.1
+
safe-push-apply: 1.0.0
+
p-limit@3.1.0:
dependencies:
yocto-queue: 0.1.0
···
dependencies:
p-limit: 3.1.0
+
package-manager-detector@1.1.0: {}
+
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
···
path-parse@1.0.7: {}
+
pathe@2.0.3: {}
+
picomatch@2.3.1: {}
+
picomatch@4.0.2: {}
+
+
pnpm-workspace-yaml@0.3.1:
+
dependencies:
+
yaml: 2.7.1
+
+
possible-typed-array-names@1.1.0: {}
prelude-ls@1.2.1: {}
···
punycode@2.3.1: {}
+
quansync@0.2.10: {}
+
queue-microtask@1.2.3: {}
react-is@16.13.1: {}
···
process: 0.11.10
string_decoder: 1.3.0
+
reflect.getprototypeof@1.0.10:
dependencies:
+
call-bind: 1.0.8
define-properties: 1.2.1
+
es-abstract: 1.23.9
es-errors: 1.3.0
+
es-object-atoms: 1.1.1
+
get-intrinsic: 1.3.0
+
get-proto: 1.0.1
+
which-builtin-type: 1.2.1
+
regexp.prototype.flags@1.5.4:
dependencies:
+
call-bind: 1.0.8
define-properties: 1.2.1
es-errors: 1.3.0
+
get-proto: 1.0.1
+
gopd: 1.2.0
set-function-name: 2.0.2
resolve-from@4.0.0: {}
resolve@2.0.0-next.5:
dependencies:
+
is-core-module: 2.16.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
+
restore-cursor@5.1.0:
+
dependencies:
+
onetime: 7.0.0
+
signal-exit: 4.1.0
+
reusify@1.0.4: {}
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
+
safe-array-concat@1.1.3:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
+
get-intrinsic: 1.3.0
+
has-symbols: 1.1.0
isarray: 2.0.5
safe-buffer@5.2.1: {}
+
safe-push-apply@1.0.0:
dependencies:
+
es-errors: 1.3.0
+
isarray: 2.0.5
+
+
safe-regex-test@1.1.0:
+
dependencies:
+
call-bound: 1.0.4
es-errors: 1.3.0
+
is-regex: 1.2.1
semver@6.3.1: {}
+
semver@7.7.1: {}
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
es-errors: 1.3.0
function-bind: 1.1.2
+
get-intrinsic: 1.3.0
+
gopd: 1.2.0
has-property-descriptors: 1.0.2
set-function-name@2.0.2:
···
es-errors: 1.3.0
functions-have-names: 1.2.3
has-property-descriptors: 1.0.2
+
+
set-proto@1.0.0:
+
dependencies:
+
dunder-proto: 1.0.1
+
es-errors: 1.3.0
+
es-object-atoms: 1.1.1
shebang-command@2.0.0:
dependencies:
···
shebang-regex@3.0.0: {}
+
side-channel-list@1.0.0:
+
dependencies:
+
es-errors: 1.3.0
+
object-inspect: 1.13.4
+
+
side-channel-map@1.0.1:
+
dependencies:
+
call-bound: 1.0.4
+
es-errors: 1.3.0
+
get-intrinsic: 1.3.0
+
object-inspect: 1.13.4
+
+
side-channel-weakmap@1.0.2:
+
dependencies:
+
call-bound: 1.0.4
+
es-errors: 1.3.0
+
get-intrinsic: 1.3.0
+
object-inspect: 1.13.4
+
side-channel-map: 1.0.1
+
+
side-channel@1.1.0:
dependencies:
es-errors: 1.3.0
+
object-inspect: 1.13.4
+
side-channel-list: 1.0.0
+
side-channel-map: 1.0.1
+
side-channel-weakmap: 1.0.2
+
+
signal-exit@4.1.0: {}
standalone-electron-types@1.0.0:
dependencies:
'@types/node': 18.17.17
+
string.prototype.matchall@4.0.12:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
define-properties: 1.2.1
+
es-abstract: 1.23.9
es-errors: 1.3.0
+
es-object-atoms: 1.1.1
+
get-intrinsic: 1.3.0
+
gopd: 1.2.0
+
has-symbols: 1.1.0
+
internal-slot: 1.1.0
+
regexp.prototype.flags: 1.5.4
set-function-name: 2.0.2
+
side-channel: 1.1.0
string.prototype.repeat@1.0.0:
dependencies:
define-properties: 1.2.1
+
es-abstract: 1.23.9
+
string.prototype.trim@1.2.10:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
+
define-data-property: 1.1.4
define-properties: 1.2.1
+
es-abstract: 1.23.9
+
es-object-atoms: 1.1.1
+
has-property-descriptors: 1.0.2
+
string.prototype.trimend@1.0.9:
dependencies:
+
call-bind: 1.0.8
+
call-bound: 1.0.4
define-properties: 1.2.1
+
es-object-atoms: 1.1.1
string.prototype.trimstart@1.0.8:
dependencies:
+
call-bind: 1.0.8
define-properties: 1.2.1
+
es-object-atoms: 1.1.1
string_decoder@1.3.0:
dependencies:
···
supports-preserve-symlinks-flag@1.0.0: {}
+
synckit@0.11.1:
dependencies:
+
'@pkgr/core': 0.2.0
+
tslib: 2.8.1
+
taze@19.0.4:
+
dependencies:
+
'@antfu/ni': 24.3.0
+
cac: 6.7.14
+
find-up-simple: 1.0.1
+
ofetch: 1.4.1
+
package-manager-detector: 1.1.0
+
pathe: 2.0.3
+
pnpm-workspace-yaml: 0.3.1
+
restore-cursor: 5.1.0
+
tinyexec: 1.0.1
+
tinyglobby: 0.2.12
+
unconfig: 7.3.1
+
yaml: 2.7.1
+
+
tinyexec@1.0.1: {}
+
+
tinyglobby@0.2.12:
+
dependencies:
+
fdir: 6.4.3(picomatch@4.0.2)
+
picomatch: 4.0.2
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
+
ts-api-utils@2.1.0(typescript@5.8.2):
dependencies:
+
typescript: 5.8.2
+
tslib@2.8.1: {}
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
+
typed-array-buffer@1.0.3:
dependencies:
+
call-bound: 1.0.4
es-errors: 1.3.0
+
is-typed-array: 1.1.15
+
typed-array-byte-length@1.0.3:
dependencies:
+
call-bind: 1.0.8
+
for-each: 0.3.5
+
gopd: 1.2.0
+
has-proto: 1.2.0
+
is-typed-array: 1.1.15
+
typed-array-byte-offset@1.0.4:
dependencies:
available-typed-arrays: 1.0.7
+
call-bind: 1.0.8
+
for-each: 0.3.5
+
gopd: 1.2.0
+
has-proto: 1.2.0
+
is-typed-array: 1.1.15
+
reflect.getprototypeof: 1.0.10
+
typed-array-length@1.0.7:
dependencies:
+
call-bind: 1.0.8
+
for-each: 0.3.5
+
gopd: 1.2.0
+
is-typed-array: 1.1.15
+
possible-typed-array-names: 1.1.0
+
reflect.getprototypeof: 1.0.10
+
typescript-eslint@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2):
dependencies:
+
'@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)
+
'@typescript-eslint/parser': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
+
'@typescript-eslint/utils': 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
transitivePeerDependencies:
- supports-color
+
typescript@5.8.2: {}
+
+
ufo@1.5.4: {}
+
unbox-primitive@1.1.0:
dependencies:
+
call-bound: 1.0.4
+
has-bigints: 1.1.0
+
has-symbols: 1.1.0
+
which-boxed-primitive: 1.1.1
+
unconfig@7.3.1:
+
dependencies:
+
'@quansync/fs': 0.1.2
+
defu: 6.1.4
+
jiti: 2.4.2
+
quansync: 0.2.10
+
+
undici-types@6.20.0: {}
+
+
undici-types@6.21.0: {}
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
+
utilium@1.10.1:
dependencies:
eventemitter3: 5.0.1
+
optionalDependencies:
+
'@xterm/xterm': 5.5.0
+
which-boxed-primitive@1.1.1:
dependencies:
+
is-bigint: 1.1.0
+
is-boolean-object: 1.2.2
+
is-number-object: 1.1.1
+
is-string: 1.1.1
+
is-symbol: 1.1.1
+
which-builtin-type@1.2.1:
dependencies:
+
call-bound: 1.0.4
+
function.prototype.name: 1.1.8
has-tostringtag: 1.0.2
+
is-async-function: 2.1.1
+
is-date-object: 1.1.0
+
is-finalizationregistry: 1.1.1
+
is-generator-function: 1.1.0
+
is-regex: 1.2.1
+
is-weakref: 1.1.1
isarray: 2.0.5
+
which-boxed-primitive: 1.1.1
which-collection: 1.0.2
+
which-typed-array: 1.1.19
which-collection@1.0.2:
dependencies:
is-map: 2.0.3
is-set: 2.0.3
is-weakmap: 2.0.2
+
is-weakset: 2.0.4
+
which-typed-array@1.1.19:
dependencies:
available-typed-arrays: 1.0.7
+
call-bind: 1.0.8
+
call-bound: 1.0.4
+
for-each: 0.3.5
+
get-proto: 1.0.1
+
gopd: 1.2.0
has-tostringtag: 1.0.2
which@2.0.2:
dependencies:
isexe: 2.0.0
+
yaml@2.7.1: {}
+
yocto-queue@0.1.0: {}
+
+
zustand@5.0.3(@types/react@18.3.20):
+
optionalDependencies:
+
'@types/react': 18.3.20
+31 -1
pnpm-workspace.yaml
···
packages:
-
- "packages/*"
···
packages:
+
- packages/*
+
+
catalogs:
+
dev:
+
esbuild: ^0.19.3
+
esbuild-copy-static-files: ^0.1.0
+
"@types/node": ^22.14.0
+
"@moonlight-mod/eslint-config": "github:moonlight-mod/eslint-config"
+
eslint: ^9.12.0
+
"@types/chrome": ^0.0.313
+
husky: ^8.0.3
+
prettier: ^3.1.0
+
typescript: ^5.3.3
+
taze: ^19.0.4
+
prod:
+
"@moonlight-mod/lunast": ^1.0.1
+
"@moonlight-mod/mappings": ^1.1.25
+
"@moonlight-mod/moonmap": ^1.0.5
+
microdiff: ^1.5.0
+
nanotar: ^0.1.1
+
"@zenfs/core": ^2.0.0
+
"@zenfs/dom": ^1.1.3
+
+
onlyBuiltDependencies:
+
- esbuild
+
+
engineStrict: true
+
strictSsl: true
+
strictDepBuilds: true
+
packageManagerStrict: true
+
registry: https://registry.npmjs.org/
-78
scripts/link.js
···
-
// Janky script to get around pnpm link issues
-
// Probably don't use this. Probably
-
/* eslint-disable no-console */
-
const fs = require("fs");
-
const path = require("path");
-
const child_process = require("child_process");
-
-
const cwd = process.cwd();
-
const onDisk = {
-
"@moonlight-mod/lunast": "../lunast",
-
"@moonlight-mod/moonmap": "../moonmap",
-
"@moonlight-mod/mappings": "../mappings"
-
};
-
-
function exec(cmd, dir) {
-
child_process.execSync(cmd, { cwd: dir, stdio: "inherit" });
-
}
-
-
function getDeps(packageJSON) {
-
const ret = {};
-
Object.assign(ret, packageJSON.dependencies || {});
-
Object.assign(ret, packageJSON.devDependencies || {});
-
Object.assign(ret, packageJSON.peerDependencies || {});
-
return ret;
-
}
-
-
function link(dir) {
-
const packageJSONPath = path.join(dir, "package.json");
-
if (!fs.existsSync(packageJSONPath)) return;
-
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, "utf8"));
-
const deps = getDeps(packageJSON);
-
-
for (const [dep, relativePath] of Object.entries(onDisk)) {
-
const fullPath = path.join(cwd, relativePath);
-
if (deps[dep]) {
-
exec(`pnpm link ${fullPath}`, dir);
-
}
-
}
-
}
-
-
function undo(dir) {
-
exec("pnpm unlink", dir);
-
try {
-
if (fs.existsSync(path.join(dir, "pnpm-lock.yaml"))) {
-
exec("git restore pnpm-lock.yaml", dir);
-
}
-
} catch {
-
// ignored
-
}
-
}
-
-
const shouldUndo = process.argv.includes("--undo");
-
const packages = fs.readdirSync("./packages");
-
-
for (const path of Object.values(onDisk)) {
-
console.log(path);
-
if (shouldUndo) {
-
undo(path);
-
} else {
-
link(path);
-
}
-
}
-
-
if (shouldUndo) {
-
console.log(cwd);
-
undo(cwd);
-
for (const pkg of packages) {
-
const dir = path.join(cwd, "packages", pkg);
-
console.log(dir);
-
undo(dir);
-
}
-
} else {
-
for (const pkg of packages) {
-
const dir = path.join(cwd, "packages", pkg);
-
console.log(dir);
-
link(dir);
-
}
-
}
···
+78
scripts/link.mjs
···
···
+
// Janky script to get around pnpm link issues
+
// Probably don't use this. Probably
+
/* eslint-disable no-console */
+
const fs = require("fs");
+
const path = require("path");
+
const child_process = require("child_process");
+
+
const cwd = process.cwd();
+
const onDisk = {
+
//"@moonlight-mod/lunast": "../lunast",
+
//"@moonlight-mod/moonmap": "../moonmap",
+
"@moonlight-mod/mappings": "../mappings"
+
};
+
+
function exec(cmd, dir) {
+
child_process.execSync(cmd, { cwd: dir, stdio: "inherit" });
+
}
+
+
function getDeps(packageJSON) {
+
const ret = {};
+
Object.assign(ret, packageJSON.dependencies || {});
+
Object.assign(ret, packageJSON.devDependencies || {});
+
Object.assign(ret, packageJSON.peerDependencies || {});
+
return ret;
+
}
+
+
function link(dir) {
+
const packageJSONPath = path.join(dir, "package.json");
+
if (!fs.existsSync(packageJSONPath)) return;
+
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, "utf8"));
+
const deps = getDeps(packageJSON);
+
+
for (const [dep, relativePath] of Object.entries(onDisk)) {
+
const fullPath = path.join(cwd, relativePath);
+
if (deps[dep]) {
+
exec(`pnpm link ${fullPath}`, dir);
+
}
+
}
+
}
+
+
function undo(dir) {
+
exec("pnpm unlink", dir);
+
try {
+
if (fs.existsSync(path.join(dir, "pnpm-lock.yaml"))) {
+
exec("git restore pnpm-lock.yaml", dir);
+
}
+
} catch {
+
// ignored
+
}
+
}
+
+
const shouldUndo = process.argv.includes("--undo");
+
const packages = fs.readdirSync("./packages");
+
+
for (const path of Object.values(onDisk)) {
+
console.log(path);
+
if (shouldUndo) {
+
undo(path);
+
} else {
+
link(path);
+
}
+
}
+
+
if (shouldUndo) {
+
console.log(cwd);
+
undo(cwd);
+
for (const pkg of packages) {
+
const dir = path.join(cwd, "packages", pkg);
+
console.log(dir);
+
undo(dir);
+
}
+
} else {
+
for (const pkg of packages) {
+
const dir = path.join(cwd, "packages", pkg);
+
console.log(dir);
+
link(dir);
+
}
+
}
-29
scripts/update.js
···
-
// Update dependencies in all packages
-
/* eslint-disable no-console */
-
const fs = require("fs");
-
const path = require("path");
-
const child_process = require("child_process");
-
-
const packageToUpdate = process.argv[2];
-
-
function getDeps(packageJSON) {
-
const ret = {};
-
Object.assign(ret, packageJSON.dependencies || {});
-
Object.assign(ret, packageJSON.devDependencies || {});
-
Object.assign(ret, packageJSON.peerDependencies || {});
-
return ret;
-
}
-
-
function exec(cmd, dir) {
-
child_process.execSync(cmd, { cwd: dir, stdio: "inherit" });
-
}
-
-
for (const package of fs.readdirSync("./packages")) {
-
const packageJSON = JSON.parse(fs.readFileSync(path.join("./packages", package, "package.json"), "utf8"));
-
-
const deps = getDeps(packageJSON);
-
if (Object.keys(deps).includes(packageToUpdate)) {
-
console.log(`Updating ${packageToUpdate} in ${package}`);
-
exec(`pnpm update ${packageToUpdate}`, path.join("./packages", package));
-
}
-
}
···
+35
tsconfig.base.json
···
···
+
{
+
"$schema": "https://json.schemastore.org/tsconfig.json",
+
"display": "Base",
+
"_version": "1.0.0",
+
"compilerOptions": {
+
"incremental": true,
+
"target": "ES2022",
+
"jsx": "react",
+
"lib": ["ESNext", "ESNext.Disposable", "DOM", "DOM.Iterable"],
+
"module": "ES2020",
+
"moduleResolution": "Bundler",
+
"resolveJsonModule": true,
+
"allowArbitraryExtensions": false,
+
"allowImportingTsExtensions": true,
+
"allowJs": true,
+
"strict": true,
+
"strictNullChecks": true,
+
+
// disable unreachable code detection because it breaks with esbuild labels
+
"allowUnreachableCode": true,
+
"noFallthroughCasesInSwitch": true,
+
"noImplicitReturns": true,
+
"declaration": true,
+
"declarationMap": true,
+
"outDir": "dist",
+
"sourceMap": true,
+
"stripInternal": true,
+
"esModuleInterop": true,
+
"forceConsistentCasingInFileNames": true,
+
"noErrorTruncation": true,
+
"verbatimModuleSyntax": false,
+
// meriyah has a broken import lol
+
"skipLibCheck": true
+
}
+
}
+7 -16
tsconfig.json
···
{
"compilerOptions": {
-
"target": "es2022",
-
"module": "es6",
-
"esModuleInterop": true,
-
"forceConsistentCasingInFileNames": true,
-
"strict": true,
-
"moduleResolution": "bundler",
"baseUrl": "./packages/",
-
"jsx": "react",
-
"noEmit": true,
-
-
// meriyah has a broken import lol
-
"skipLibCheck": true,
-
-
// disable unreachable code detection because it breaks with esbuild labels
-
"allowUnreachableCode": true
},
-
"include": ["./packages/**/*", "./env.d.ts"],
-
"exclude": ["node_modules"]
}
···
{
+
"extends": ["./tsconfig.base.json"],
"compilerOptions": {
"baseUrl": "./packages/",
+
"noEmit": true
},
+
"exclude": [
+
"**/node_modules/**",
+
"**/dist/**",
+
"**/build/**"
+
]
}