diff --git a/.editorconfig b/.editorconfig
index dcc46b748..1c7bcce3d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,5 +10,5 @@ max_line_length = 120
tab_width = 4
trim_trailing_whitespace = true
-[{*.json,*.xml}]
+[{*.json,*.xml,*.yml}]
indent_size = 2
diff --git a/.github/workflows/handbook.yml b/.github/workflows/handbook.yml
new file mode 100644
index 000000000..4497545fd
--- /dev/null
+++ b/.github/workflows/handbook.yml
@@ -0,0 +1,82 @@
+name: "Handbook"
+
+on:
+ workflow_dispatch: ~
+ push:
+ paths:
+ - "src/handbook/**.tsx"
+ branches:
+ - "development"
+ - "unstable"
+ pull_request:
+ paths:
+ - "src/handbook/**.tsx"
+ types:
+ - opened
+ - synchronize
+ - reopened
+
+jobs:
+ Lint-Code:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Setup Node
+ uses: actions/setup-node@v2
+ with:
+ node-version: '17'
+ - name: Cache node modules
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.npm
+ ~/.cache
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-
+ - name: Install dependencies
+ working-directory: src/handbook
+ run: npm install --force
+ - name: Run linter
+ working-directory: src/handbook
+ run: npm run lint
+
+ - run: git config --global user.name "github-actions"
+ - run: git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ - name: Commit changes
+ if: ${{ github.event_name == 'push' }}
+ run: git add -u && git commit -m "Lint Code [skip actions]" || true
+ - name: Push changes
+ if: ${{ github.event_name == 'push' }}
+ run: git push --set-upstream --force origin ${{ github.ref }}
+
+ Build-Handbook:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Setup Node
+ uses: actions/setup-node@v2
+ with:
+ node-version: '17'
+ - name: Cache node modules
+ uses: actions/cache@v2
+ with:
+ path: |
+ ~/.npm
+ ~/.cache
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-
+ - name: Install dependencies
+ working-directory: src/handbook
+ run: npm install --force
+ - name: Build handbook
+ working-directory: src/handbook
+ run: npm run build
+ - name: Upload build
+ uses: actions/upload-artifact@v3
+ with:
+ name: Handbook
+ path: src/handbook/dist/*.html
diff --git a/.gitignore b/.gitignore
index eaf307905..0a2881741 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,6 +73,9 @@ mongod.exe
gacha-mapping.js
mappings.js
BuildConfig.java
+data/hk4e/announcement/
+
+src/main/resources/handbook.html
# lombok
/.apt_generated/
@@ -80,7 +83,6 @@ BuildConfig.java
# macOS
.DS_Store
.directory
-data/hk4e/announcement/
# Hotswap Agent
hotswap-agent.properties
diff --git a/src/handbook/.gitignore b/src/handbook/.gitignore
new file mode 100644
index 000000000..c74de08b8
--- /dev/null
+++ b/src/handbook/.gitignore
@@ -0,0 +1,27 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+# Handbook data
+data/
diff --git a/src/handbook/.prettierrc b/src/handbook/.prettierrc
new file mode 100644
index 000000000..a370e477a
--- /dev/null
+++ b/src/handbook/.prettierrc
@@ -0,0 +1,12 @@
+{
+ "arrowParens": "always",
+ "bracketSpacing": true,
+ "endOfLine": "lf",
+ "jsxSingleQuote": false,
+ "jsxBracketSameLine": false,
+ "semi": true,
+ "singleQuote": false,
+ "tabWidth": 4,
+ "trailingComma": "none",
+ "useTabs": false
+}
\ No newline at end of file
diff --git a/src/handbook/cfg/postcss.config.js b/src/handbook/cfg/postcss.config.js
new file mode 100644
index 000000000..1962ca60c
--- /dev/null
+++ b/src/handbook/cfg/postcss.config.js
@@ -0,0 +1,25 @@
+import tailwind from "tailwindcss";
+import autoprefixer from "autoprefixer";
+import cssnanoPlugin from "cssnano";
+
+import tailwindConfig from "./tailwind.config.js";
+const mode = process.env.NODE_ENV;
+const dev = mode === "development";
+
+export default {
+ plugins: (() => {
+ let plugins = [
+ // Some plugins, like TailwindCSS/Nesting, need to run before Tailwind.
+ tailwind(tailwindConfig),
+
+ // But others, like autoprefixer, need to run after.
+ autoprefixer()
+ ];
+
+ !dev && cssnanoPlugin({
+ preset: "default"
+ });
+
+ return plugins;
+ })()
+}
diff --git a/src/handbook/cfg/tailwind.config.js b/src/handbook/cfg/tailwind.config.js
new file mode 100644
index 000000000..6a004f99f
--- /dev/null
+++ b/src/handbook/cfg/tailwind.config.js
@@ -0,0 +1,9 @@
+export default {
+ content: ["./src/**/*.{html,js,tsx,ts}"],
+ mode: "jit",
+ theme: {
+ extend: {}
+ },
+ darkMode: "class",
+ plugins: []
+};
diff --git a/src/handbook/data/README.md b/src/handbook/data/README.md
new file mode 100644
index 000000000..cb7adf9eb
--- /dev/null
+++ b/src/handbook/data/README.md
@@ -0,0 +1,32 @@
+# Handbook Data
+Use Grasscutter's dumpers to generate the data to put here.
+
+## Files Required
+- `commands.json`
+- `entities.csv`
+- `avatars.csv`
+- `scenes.csv`
+- `items.csv`
+
+# Item Icon Notes
+- Artifacts: `https://bbs.hoyolab.com/hoyowiki/picture/reliquary/(name)/(piece)_icon.png`
+ - Alternate source: `https://api.ambr.top/assets/UI/reliquary/UI_RelicIcon_(set)_(piece).png`
+ - `xxxx4` - `flower_of_life`
+ - `xxxx5` - `sands_of_eon`
+ - `xxxx3` - `circlet_of_logos`/`plume_of_death`
+ - Use `circlet_of_logos` with a complete set
+ - Use `plume_of_death` with part of a set.
+ - `xxxx2` - `plume_of_death`
+ - `xxxx1` - `goblet_of_eonothem`
+- Miscellaneous Items: `https://bbs.hoyolab.com/hoyowiki/picture/object/(name)_icon.png`
+ - Includes: materials, quest items, food, etc.
+ - Alternate source: `https://api.ambr.top/assets/UI/UI_ItemIcon_(id).png`
+- Avatars/Avatar Items: `https://bbs.hoyolab.com/hoyowiki/picture/character/(name)_icon.png`
+ - Avatar Items are between ranges `1001` and `1099`.
+- Weapons: `https://api.ambr.top/assets/UI/UI_EquipIcon_(type)_(name).png`
+- Furniture: `https://api.ambr.top/assets/UI/furniture/UI_Homeworld_(location)_(name).png`
+- Monsters: `https://api.ambr.top/assets/UI/monster/UI_MonsterIcon_(type)_(variant).png`
+
+# Credits
+- [`...List.json` files](https://raw.githubusercontent.com/Dituon/grasscutter-command-helper/main/data/en-US) - Grasscutter Command Helper
+- [Internal Asset API](https://ambr.top) - Project Amber
diff --git a/src/handbook/index.html b/src/handbook/index.html
new file mode 100644
index 000000000..4e84c88f2
--- /dev/null
+++ b/src/handbook/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ GM Handbook
+
+
+
+
+
+
diff --git a/src/handbook/package-lock.json b/src/handbook/package-lock.json
new file mode 100644
index 000000000..12186b8f7
--- /dev/null
+++ b/src/handbook/package-lock.json
@@ -0,0 +1,5847 @@
+{
+ "name": "handbook",
+ "version": "0.1.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "handbook",
+ "version": "0.1.0",
+ "hasInstallScript": true,
+ "dependencies": {
+ "events": "^3.3.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-virtualized": "^9.22.3"
+ },
+ "devDependencies": {
+ "@rollup/plugin-dsv": "^3.0.2",
+ "@types/events": "^3.0.0",
+ "@types/react": "^18.0.28",
+ "@types/react-dom": "^18.0.11",
+ "@types/react-virtualized": "^9.21.21",
+ "@vitejs/plugin-react-swc": "^3.0.0",
+ "autoprefixer": "^10.4.13",
+ "cssnano": "^5.1.15",
+ "patch-package": "^6.5.1",
+ "postcss": "^8.4.21",
+ "postcss-font-magician": "^3.0.0",
+ "postcss-load-config": "^4.0.1",
+ "prettier": "^2.8.7",
+ "sass": "^1.58.3",
+ "tailwindcss": "^3.2.7",
+ "typescript": "^4.9.3",
+ "vite": "^4.2.0",
+ "vite-plugin-singlefile": "^0.13.5",
+ "vite-plugin-svgr": "^2.4.0",
+ "vite-tsconfig-paths": "^4.0.7"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.1.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.21.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/highlight": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.21.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.21.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-compilation-targets": "^7.21.4",
+ "@babel/helper-module-transforms": "^7.21.2",
+ "@babel/helpers": "^7.21.0",
+ "@babel/parser": "^7.21.4",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.2",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.21.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.21.4",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.21.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.21.4",
+ "@babel/helper-validator-option": "^7.21.0",
+ "browserslist": "^4.21.3",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.18.9",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.21.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.20.7",
+ "@babel/types": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.18.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.21.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.21.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.21.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-simple-access": "^7.20.2",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.2",
+ "@babel/types": "^7.21.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.20.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.20.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.18.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.19.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.21.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.21.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.0",
+ "@babel/types": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.18.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.21.4",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
+ "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
+ "dependencies": {
+ "regenerator-runtime": "^0.13.11"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.20.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.21.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.21.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.21.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.19.4",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.17.15",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@rollup/plugin-dsv": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "@types/d3-dsv": "^3.0.0",
+ "d3-dsv": "2.0.0",
+ "tosource": "^2.0.0-alpha.3"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@svgr/babel-plugin-add-jsx-attribute": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-remove-jsx-attribute": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-svg-dynamic-title": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-svg-em-dimensions": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-transform-react-native-svg": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-plugin-transform-svg-component": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/babel-preset": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1",
+ "@svgr/babel-plugin-remove-jsx-attribute": "*",
+ "@svgr/babel-plugin-remove-jsx-empty-expression": "*",
+ "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1",
+ "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1",
+ "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1",
+ "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1",
+ "@svgr/babel-plugin-transform-svg-component": "^6.5.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@svgr/core": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.19.6",
+ "@svgr/babel-preset": "^6.5.1",
+ "@svgr/plugin-jsx": "^6.5.1",
+ "camelcase": "^6.2.0",
+ "cosmiconfig": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/@svgr/hast-util-to-babel-ast": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.20.0",
+ "entities": "^4.4.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ }
+ },
+ "node_modules/@svgr/hast-util-to-babel-ast/node_modules/entities": {
+ "version": "4.4.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/@svgr/plugin-jsx": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.19.6",
+ "@svgr/babel-preset": "^6.5.1",
+ "@svgr/hast-util-to-babel-ast": "^6.5.1",
+ "svg-parser": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/gregberge"
+ },
+ "peerDependencies": {
+ "@svgr/core": "^6.0.0"
+ }
+ },
+ "node_modules/@swc/core": {
+ "version": "1.3.44",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/swc"
+ },
+ "optionalDependencies": {
+ "@swc/core-darwin-arm64": "1.3.44",
+ "@swc/core-darwin-x64": "1.3.44",
+ "@swc/core-linux-arm-gnueabihf": "1.3.44",
+ "@swc/core-linux-arm64-gnu": "1.3.44",
+ "@swc/core-linux-arm64-musl": "1.3.44",
+ "@swc/core-linux-x64-gnu": "1.3.44",
+ "@swc/core-linux-x64-musl": "1.3.44",
+ "@swc/core-win32-arm64-msvc": "1.3.44",
+ "@swc/core-win32-ia32-msvc": "1.3.44",
+ "@swc/core-win32-x64-msvc": "1.3.44"
+ }
+ },
+ "node_modules/@swc/core-win32-x64-msvc": {
+ "version": "1.3.44",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@trysound/sax": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/@types/d3-dsv": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/events": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "18.15.11",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
+ "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "18.0.33",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.0.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-virtualized": {
+ "version": "9.21.21",
+ "resolved": "https://registry.npmjs.org/@types/react-virtualized/-/react-virtualized-9.21.21.tgz",
+ "integrity": "sha512-Exx6I7p4Qn+BBA1SRyj/UwQlZ0I0Pq7g7uhAp0QQ4JWzZunqEqNBGTmCmMmS/3N9wFgAGWuBD16ap7k8Y14VPA==",
+ "dev": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/react": "^17"
+ }
+ },
+ "node_modules/@types/react-virtualized/node_modules/@types/react": {
+ "version": "17.0.56",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.56.tgz",
+ "integrity": "sha512-Z13f9Qz7Hg8f2g2NsBjiJSVWmON2b3K8RIqFK8mMKCIgvD0CD0ZChTukz87H3lI28X3ukXoNFGzo3ZW1ICTtPA==",
+ "dev": true,
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/scheduler": {
+ "version": "0.16.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vitejs/plugin-react-swc": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@swc/core": "^1.3.35"
+ },
+ "peerDependencies": {
+ "vite": "^4"
+ }
+ },
+ "node_modules/@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+ "dev": true
+ },
+ "node_modules/amdefine": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "BSD-3-Clause OR MIT",
+ "engines": {
+ "node": ">=0.4.2"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.14",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.21.5",
+ "caniuse-lite": "^1.0.30001464",
+ "fraction.js": "^4.2.0",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/b3b": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/bootstrap-fonts-complete": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss": "^4.1.16"
+ }
+ },
+ "node_modules/bootstrap-fonts-complete/node_modules/postcss": {
+ "version": "4.1.16",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es6-promise": "~2.3.0",
+ "js-base64": "~2.1.8",
+ "source-map": "~0.4.2"
+ }
+ },
+ "node_modules/bootstrap-fonts-complete/node_modules/source-map": {
+ "version": "0.4.4",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "amdefine": ">=0.0.4"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brotli": {
+ "version": "1.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.1.2"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.21.5",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001449",
+ "electron-to-chromium": "^1.4.284",
+ "node-releases": "^2.0.8",
+ "update-browserslist-db": "^1.0.10"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/caniuse-api": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.0.0",
+ "caniuse-lite": "^1.0.0",
+ "lodash.memoize": "^4.1.2",
+ "lodash.uniq": "^4.5.0"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001473",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-convert/node_modules/color-name": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/colord": {
+ "version": "2.9.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "dependencies": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ },
+ "engines": {
+ "node": ">=4.8"
+ }
+ },
+ "node_modules/cross-spawn/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/css-declaration-sorter": {
+ "version": "6.4.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.9"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.0.1",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.0.14",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cssnano": {
+ "version": "5.1.15",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssnano-preset-default": "^5.2.14",
+ "lilconfig": "^2.0.3",
+ "yaml": "^1.10.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/cssnano"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/cssnano-preset-default": {
+ "version": "5.2.14",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "css-declaration-sorter": "^6.3.1",
+ "cssnano-utils": "^3.1.0",
+ "postcss-calc": "^8.2.3",
+ "postcss-colormin": "^5.3.1",
+ "postcss-convert-values": "^5.1.3",
+ "postcss-discard-comments": "^5.1.2",
+ "postcss-discard-duplicates": "^5.1.0",
+ "postcss-discard-empty": "^5.1.1",
+ "postcss-discard-overridden": "^5.1.0",
+ "postcss-merge-longhand": "^5.1.7",
+ "postcss-merge-rules": "^5.1.4",
+ "postcss-minify-font-values": "^5.1.0",
+ "postcss-minify-gradients": "^5.1.1",
+ "postcss-minify-params": "^5.1.4",
+ "postcss-minify-selectors": "^5.2.1",
+ "postcss-normalize-charset": "^5.1.0",
+ "postcss-normalize-display-values": "^5.1.0",
+ "postcss-normalize-positions": "^5.1.1",
+ "postcss-normalize-repeat-style": "^5.1.1",
+ "postcss-normalize-string": "^5.1.0",
+ "postcss-normalize-timing-functions": "^5.1.0",
+ "postcss-normalize-unicode": "^5.1.1",
+ "postcss-normalize-url": "^5.1.0",
+ "postcss-normalize-whitespace": "^5.1.1",
+ "postcss-ordered-values": "^5.1.3",
+ "postcss-reduce-initial": "^5.1.2",
+ "postcss-reduce-transforms": "^5.1.0",
+ "postcss-svgo": "^5.1.0",
+ "postcss-unique-selectors": "^5.1.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/cssnano-utils": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/csso": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "css-tree": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.2",
+ "license": "MIT"
+ },
+ "node_modules/d3-dsv": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "commander": "2",
+ "iconv-lite": "0.4",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json",
+ "csv2tsv": "bin/dsv2dsv",
+ "dsv2dsv": "bin/dsv2dsv",
+ "dsv2json": "bin/dsv2json",
+ "json2csv": "bin/json2dsv",
+ "json2dsv": "bin/json2dsv",
+ "json2tsv": "bin/json2dsv",
+ "tsv2csv": "bin/dsv2dsv",
+ "tsv2json": "bin/dsv2json"
+ }
+ },
+ "node_modules/d3-dsv/node_modules/commander": {
+ "version": "2.20.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/directory-fonts-complete": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "brotli": "^1.3.2",
+ "is-eot": "^1.0.0",
+ "is-otf": "^0.1.1",
+ "is-ttf": "^0.2.1",
+ "is-woff": "^1.0.1",
+ "is-woff2": "^1.0.0"
+ }
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "4.3.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.2.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "2.8.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.349",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/entities": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es6-promise": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.17.15",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.17.15",
+ "@esbuild/android-arm64": "0.17.15",
+ "@esbuild/android-x64": "0.17.15",
+ "@esbuild/darwin-arm64": "0.17.15",
+ "@esbuild/darwin-x64": "0.17.15",
+ "@esbuild/freebsd-arm64": "0.17.15",
+ "@esbuild/freebsd-x64": "0.17.15",
+ "@esbuild/linux-arm": "0.17.15",
+ "@esbuild/linux-arm64": "0.17.15",
+ "@esbuild/linux-ia32": "0.17.15",
+ "@esbuild/linux-loong64": "0.17.15",
+ "@esbuild/linux-mips64el": "0.17.15",
+ "@esbuild/linux-ppc64": "0.17.15",
+ "@esbuild/linux-riscv64": "0.17.15",
+ "@esbuild/linux-s390x": "0.17.15",
+ "@esbuild/linux-x64": "0.17.15",
+ "@esbuild/netbsd-x64": "0.17.15",
+ "@esbuild/openbsd-x64": "0.17.15",
+ "@esbuild/sunos-x64": "0.17.15",
+ "@esbuild/win32-arm64": "0.17.15",
+ "@esbuild/win32-ia32": "0.17.15",
+ "@esbuild/win32-x64": "0.17.15"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/fast-glob": {
+ "version": "3.2.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-yarn-workspace-root": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz",
+ "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==",
+ "dev": true,
+ "dependencies": {
+ "micromatch": "^4.0.2"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://www.patreon.com/infusion"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.1.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/globrex": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/google-fonts-complete": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss": "^7.0.18"
+ }
+ },
+ "node_modules/google-fonts-complete/node_modules/picocolors": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/google-fonts-complete/node_modules/postcss": {
+ "version": "7.0.39",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picocolors": "^0.2.1",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "dev": true,
+ "dependencies": {
+ "ci-info": "^2.0.0"
+ },
+ "bin": {
+ "is-ci": "bin.js"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.11.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-eot": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-otf": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b3b": "0.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-ttf": {
+ "version": "0.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b3b": "0.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-woff": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-woff2": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/jiti": {
+ "version": "1.18.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/js-base64": {
+ "version": "2.1.9",
+ "dev": true,
+ "license": "BSD"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/klaw-sync": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
+ "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.11"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.memoize": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.uniq": {
+ "version": "4.5.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.0.14",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.6",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.10",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-url": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/open": {
+ "version": "7.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
+ "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
+ "dev": true,
+ "dependencies": {
+ "is-docker": "^2.0.0",
+ "is-wsl": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/patch-package": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz",
+ "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==",
+ "dev": true,
+ "dependencies": {
+ "@yarnpkg/lockfile": "^1.1.0",
+ "chalk": "^4.1.2",
+ "cross-spawn": "^6.0.5",
+ "find-yarn-workspace-root": "^2.0.0",
+ "fs-extra": "^9.0.0",
+ "is-ci": "^2.0.0",
+ "klaw-sync": "^6.0.0",
+ "minimist": "^1.2.6",
+ "open": "^7.4.2",
+ "rimraf": "^2.6.3",
+ "semver": "^5.6.0",
+ "slash": "^2.0.0",
+ "tmp": "^0.0.33",
+ "yaml": "^1.10.2"
+ },
+ "bin": {
+ "patch-package": "index.js"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">5"
+ }
+ },
+ "node_modules/patch-package/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/patch-package/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/patch-package/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/patch-package/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/patch-package/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/patch-package/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.21",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-calc": {
+ "version": "8.2.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.2"
+ }
+ },
+ "node_modules/postcss-colormin": {
+ "version": "5.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.21.4",
+ "caniuse-api": "^3.0.0",
+ "colord": "^2.9.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-convert-values": {
+ "version": "5.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.21.4",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-discard-comments": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-discard-duplicates": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-discard-empty": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-discard-overridden": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-font-magician": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "bootstrap-fonts-complete": "^1.0.0",
+ "directory-fonts-complete": "^1.2.0",
+ "google-fonts-complete": "^2.1.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "14.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^2.0.5",
+ "yaml": "^2.1.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-load-config/node_modules/yaml": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/postcss-merge-longhand": {
+ "version": "5.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0",
+ "stylehacks": "^5.1.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-merge-rules": {
+ "version": "5.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.21.4",
+ "caniuse-api": "^3.0.0",
+ "cssnano-utils": "^3.1.0",
+ "postcss-selector-parser": "^6.0.5"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-minify-font-values": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-minify-gradients": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "colord": "^2.9.1",
+ "cssnano-utils": "^3.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-minify-params": {
+ "version": "5.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.21.4",
+ "cssnano-utils": "^3.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-minify-selectors": {
+ "version": "5.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.5"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-normalize-charset": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-normalize-display-values": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-normalize-positions": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-normalize-repeat-style": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-normalize-string": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-normalize-timing-functions": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-normalize-unicode": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.21.4",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-normalize-url": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "normalize-url": "^6.0.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-normalize-whitespace": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-ordered-values": {
+ "version": "5.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssnano-utils": "^3.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-reduce-initial": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.21.4",
+ "caniuse-api": "^3.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-reduce-transforms": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-svgo": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0",
+ "svgo": "^2.7.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-unique-selectors": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.5"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prettier": {
+ "version": "2.8.7",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ },
+ "peerDependencies": {
+ "react": "^18.2.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "license": "MIT"
+ },
+ "node_modules/react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "node_modules/react-virtualized": {
+ "version": "9.22.3",
+ "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.3.tgz",
+ "integrity": "sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==",
+ "dependencies": {
+ "@babel/runtime": "^7.7.2",
+ "clsx": "^1.0.4",
+ "dom-helpers": "^5.1.3",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.7.2",
+ "react-lifecycles-compat": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react": "^15.3.0 || ^16.0.0-alpha",
+ "react-dom": "^15.3.0 || ^16.0.0-alpha"
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+ },
+ "node_modules/resolve": {
+ "version": "1.22.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "3.20.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=14.18.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sass": {
+ "version": "1.60.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.0",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stable": {
+ "version": "0.1.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stylehacks": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.21.4",
+ "postcss-selector-parser": "^6.0.4"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.15"
+ }
+ },
+ "node_modules/sucrase": {
+ "version": "3.31.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^4.0.0",
+ "glob": "7.1.6",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/sucrase/node_modules/commander": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/svg-parser": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/svgo": {
+ "version": "2.8.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@trysound/sax": "0.2.0",
+ "commander": "^7.2.0",
+ "css-select": "^4.1.3",
+ "css-tree": "^1.1.3",
+ "csso": "^4.2.0",
+ "picocolors": "^1.0.0",
+ "stable": "^0.1.8"
+ },
+ "bin": {
+ "svgo": "bin/svgo"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "arg": "^5.0.2",
+ "chokidar": "^3.5.3",
+ "color-name": "^1.1.4",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.2.12",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.17.2",
+ "lilconfig": "^2.0.6",
+ "micromatch": "^4.0.5",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.0.9",
+ "postcss-import": "^14.1.0",
+ "postcss-js": "^4.0.0",
+ "postcss-load-config": "^3.1.4",
+ "postcss-nested": "6.0.0",
+ "postcss-selector-parser": "^6.0.11",
+ "postcss-value-parser": "^4.2.0",
+ "quick-lru": "^5.1.1",
+ "resolve": "^1.22.1",
+ "sucrase": "^3.29.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=12.13.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.9"
+ }
+ },
+ "node_modules/tailwindcss/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/tailwindcss/node_modules/postcss-load-config": {
+ "version": "3.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^2.0.5",
+ "yaml": "^1.10.2"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "dependencies": {
+ "os-tmpdir": "~1.0.2"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tosource": {
+ "version": "2.0.0-alpha.3",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/tsconfck": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tsconfck": "bin/tsconfck.js"
+ },
+ "engines": {
+ "node": "^14.13.1 || ^16 || >=18"
+ },
+ "peerDependencies": {
+ "typescript": "^4.3.5 || ^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/typescript": {
+ "version": "4.9.5",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.10",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "browserslist-lint": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "4.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.17.5",
+ "postcss": "^8.4.21",
+ "resolve": "^1.22.1",
+ "rollup": "^3.18.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ },
+ "peerDependencies": {
+ "@types/node": ">= 14",
+ "less": "*",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-singlefile": {
+ "version": "0.13.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "micromatch": "^4.0.5"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "rollup": ">=2.79.0",
+ "vite": ">=3.2.0"
+ }
+ },
+ "node_modules/vite-plugin-svgr": {
+ "version": "2.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.2",
+ "@svgr/core": "^6.5.1"
+ },
+ "peerDependencies": {
+ "vite": "^2.6.0 || 3 || 4"
+ }
+ },
+ "node_modules/vite-tsconfig-paths": {
+ "version": "4.0.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "globrex": "^0.1.2",
+ "tsconfck": "^2.1.0"
+ },
+ "peerDependencies": {
+ "vite": "*"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ }
+ },
+ "dependencies": {
+ "@ampproject/remapping": {
+ "version": "2.2.0",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.1.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.21.4",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.18.6"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.21.4",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.21.4",
+ "dev": true,
+ "requires": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-compilation-targets": "^7.21.4",
+ "@babel/helper-module-transforms": "^7.21.2",
+ "@babel/helpers": "^7.21.0",
+ "@babel/parser": "^7.21.4",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.2",
+ "semver": "^6.3.0"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.21.4",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.21.4",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ },
+ "dependencies": {
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ }
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.21.4",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.21.4",
+ "@babel/helper-validator-option": "^7.21.0",
+ "browserslist": "^4.21.3",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.0"
+ }
+ },
+ "@babel/helper-environment-visitor": {
+ "version": "7.18.9",
+ "dev": true
+ },
+ "@babel/helper-function-name": {
+ "version": "7.21.0",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.20.7",
+ "@babel/types": "^7.21.0"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.18.6",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.21.4",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.21.4"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.21.2",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-simple-access": "^7.20.2",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.2",
+ "@babel/types": "^7.21.2"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.20.2",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.2"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.18.6",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-string-parser": {
+ "version": "7.19.4",
+ "dev": true
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "dev": true
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.21.0",
+ "dev": true
+ },
+ "@babel/helpers": {
+ "version": "7.21.0",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.20.7",
+ "@babel/traverse": "^7.21.0",
+ "@babel/types": "^7.21.0"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.18.6",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.21.4",
+ "dev": true
+ },
+ "@babel/runtime": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz",
+ "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==",
+ "requires": {
+ "regenerator-runtime": "^0.13.11"
+ }
+ },
+ "@babel/template": {
+ "version": "7.20.7",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.21.4",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.21.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.21.4",
+ "@babel/types": "^7.21.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.21.4",
+ "dev": true,
+ "requires": {
+ "@babel/helper-string-parser": "^7.19.4",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@esbuild/win32-x64": {
+ "version": "0.17.15",
+ "dev": true,
+ "optional": true
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.1.1",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "dev": true
+ },
+ "@jridgewell/set-array": {
+ "version": "1.1.2",
+ "dev": true
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "dev": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@rollup/plugin-dsv": {
+ "version": "3.0.2",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^5.0.1",
+ "@types/d3-dsv": "^3.0.0",
+ "d3-dsv": "2.0.0",
+ "tosource": "^2.0.0-alpha.3"
+ }
+ },
+ "@rollup/pluginutils": {
+ "version": "5.0.2",
+ "dev": true,
+ "requires": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "@svgr/babel-plugin-add-jsx-attribute": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {}
+ },
+ "@svgr/babel-plugin-remove-jsx-attribute": {
+ "version": "7.0.0",
+ "dev": true,
+ "requires": {}
+ },
+ "@svgr/babel-plugin-remove-jsx-empty-expression": {
+ "version": "7.0.0",
+ "dev": true,
+ "requires": {}
+ },
+ "@svgr/babel-plugin-replace-jsx-attribute-value": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {}
+ },
+ "@svgr/babel-plugin-svg-dynamic-title": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {}
+ },
+ "@svgr/babel-plugin-svg-em-dimensions": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {}
+ },
+ "@svgr/babel-plugin-transform-react-native-svg": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {}
+ },
+ "@svgr/babel-plugin-transform-svg-component": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {}
+ },
+ "@svgr/babel-preset": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {
+ "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1",
+ "@svgr/babel-plugin-remove-jsx-attribute": "*",
+ "@svgr/babel-plugin-remove-jsx-empty-expression": "*",
+ "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1",
+ "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1",
+ "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1",
+ "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1",
+ "@svgr/babel-plugin-transform-svg-component": "^6.5.1"
+ }
+ },
+ "@svgr/core": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.19.6",
+ "@svgr/babel-preset": "^6.5.1",
+ "@svgr/plugin-jsx": "^6.5.1",
+ "camelcase": "^6.2.0",
+ "cosmiconfig": "^7.0.1"
+ }
+ },
+ "@svgr/hast-util-to-babel-ast": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.0",
+ "entities": "^4.4.0"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "4.4.0",
+ "dev": true
+ }
+ }
+ },
+ "@svgr/plugin-jsx": {
+ "version": "6.5.1",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.19.6",
+ "@svgr/babel-preset": "^6.5.1",
+ "@svgr/hast-util-to-babel-ast": "^6.5.1",
+ "svg-parser": "^2.0.4"
+ }
+ },
+ "@swc/core": {
+ "version": "1.3.44",
+ "dev": true,
+ "requires": {
+ "@swc/core-darwin-arm64": "1.3.44",
+ "@swc/core-darwin-x64": "1.3.44",
+ "@swc/core-linux-arm-gnueabihf": "1.3.44",
+ "@swc/core-linux-arm64-gnu": "1.3.44",
+ "@swc/core-linux-arm64-musl": "1.3.44",
+ "@swc/core-linux-x64-gnu": "1.3.44",
+ "@swc/core-linux-x64-musl": "1.3.44",
+ "@swc/core-win32-arm64-msvc": "1.3.44",
+ "@swc/core-win32-ia32-msvc": "1.3.44",
+ "@swc/core-win32-x64-msvc": "1.3.44"
+ }
+ },
+ "@swc/core-win32-x64-msvc": {
+ "version": "1.3.44",
+ "dev": true,
+ "optional": true
+ },
+ "@trysound/sax": {
+ "version": "0.2.0",
+ "dev": true
+ },
+ "@types/d3-dsv": {
+ "version": "3.0.1",
+ "dev": true
+ },
+ "@types/estree": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "@types/events": {
+ "version": "3.0.0",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "18.15.11",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
+ "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "@types/parse-json": {
+ "version": "4.0.0",
+ "dev": true
+ },
+ "@types/prop-types": {
+ "version": "15.7.5",
+ "dev": true
+ },
+ "@types/react": {
+ "version": "18.0.33",
+ "dev": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "@types/react-dom": {
+ "version": "18.0.11",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@types/react-virtualized": {
+ "version": "9.21.21",
+ "resolved": "https://registry.npmjs.org/@types/react-virtualized/-/react-virtualized-9.21.21.tgz",
+ "integrity": "sha512-Exx6I7p4Qn+BBA1SRyj/UwQlZ0I0Pq7g7uhAp0QQ4JWzZunqEqNBGTmCmMmS/3N9wFgAGWuBD16ap7k8Y14VPA==",
+ "dev": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "@types/react": "^17"
+ },
+ "dependencies": {
+ "@types/react": {
+ "version": "17.0.56",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.56.tgz",
+ "integrity": "sha512-Z13f9Qz7Hg8f2g2NsBjiJSVWmON2b3K8RIqFK8mMKCIgvD0CD0ZChTukz87H3lI28X3ukXoNFGzo3ZW1ICTtPA==",
+ "dev": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ }
+ }
+ },
+ "@types/scheduler": {
+ "version": "0.16.3",
+ "dev": true
+ },
+ "@vitejs/plugin-react-swc": {
+ "version": "3.2.0",
+ "dev": true,
+ "requires": {
+ "@swc/core": "^1.3.35"
+ }
+ },
+ "@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+ "dev": true
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "any-promise": {
+ "version": "1.3.0",
+ "dev": true
+ },
+ "anymatch": {
+ "version": "3.1.3",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "arg": {
+ "version": "5.0.2",
+ "dev": true
+ },
+ "at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true
+ },
+ "autoprefixer": {
+ "version": "10.4.14",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.5",
+ "caniuse-lite": "^1.0.30001464",
+ "fraction.js": "^4.2.0",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "b3b": {
+ "version": "0.0.1",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "1.5.1",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "dev": true
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "bootstrap-fonts-complete": {
+ "version": "1.0.0",
+ "dev": true,
+ "requires": {
+ "postcss": "^4.1.16"
+ },
+ "dependencies": {
+ "postcss": {
+ "version": "4.1.16",
+ "dev": true,
+ "requires": {
+ "es6-promise": "~2.3.0",
+ "js-base64": "~2.1.8",
+ "source-map": "~0.4.2"
+ }
+ },
+ "source-map": {
+ "version": "0.4.4",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "brotli": {
+ "version": "1.3.3",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.1.2"
+ }
+ },
+ "browserslist": {
+ "version": "4.21.5",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001449",
+ "electron-to-chromium": "^1.4.284",
+ "node-releases": "^2.0.8",
+ "update-browserslist-db": "^1.0.10"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "6.3.0",
+ "dev": true
+ },
+ "camelcase-css": {
+ "version": "2.0.1",
+ "dev": true
+ },
+ "caniuse-api": {
+ "version": "3.0.0",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.0.0",
+ "caniuse-lite": "^1.0.0",
+ "lodash.memoize": "^4.1.2",
+ "lodash.uniq": "^4.5.0"
+ }
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001473",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chokidar": {
+ "version": "3.5.3",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg=="
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ },
+ "dependencies": {
+ "color-name": {
+ "version": "1.1.3",
+ "dev": true
+ }
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "dev": true
+ },
+ "colord": {
+ "version": "2.9.3",
+ "dev": true
+ },
+ "commander": {
+ "version": "7.2.0",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.9.0",
+ "dev": true
+ },
+ "cosmiconfig": {
+ "version": "7.1.0",
+ "dev": true,
+ "requires": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "css-declaration-sorter": {
+ "version": "6.4.0",
+ "dev": true,
+ "requires": {}
+ },
+ "css-select": {
+ "version": "4.3.0",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.0.1",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ }
+ },
+ "css-tree": {
+ "version": "1.1.3",
+ "dev": true,
+ "requires": {
+ "mdn-data": "2.0.14",
+ "source-map": "^0.6.1"
+ }
+ },
+ "css-what": {
+ "version": "6.1.0",
+ "dev": true
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "dev": true
+ },
+ "cssnano": {
+ "version": "5.1.15",
+ "dev": true,
+ "requires": {
+ "cssnano-preset-default": "^5.2.14",
+ "lilconfig": "^2.0.3",
+ "yaml": "^1.10.2"
+ }
+ },
+ "cssnano-preset-default": {
+ "version": "5.2.14",
+ "dev": true,
+ "requires": {
+ "css-declaration-sorter": "^6.3.1",
+ "cssnano-utils": "^3.1.0",
+ "postcss-calc": "^8.2.3",
+ "postcss-colormin": "^5.3.1",
+ "postcss-convert-values": "^5.1.3",
+ "postcss-discard-comments": "^5.1.2",
+ "postcss-discard-duplicates": "^5.1.0",
+ "postcss-discard-empty": "^5.1.1",
+ "postcss-discard-overridden": "^5.1.0",
+ "postcss-merge-longhand": "^5.1.7",
+ "postcss-merge-rules": "^5.1.4",
+ "postcss-minify-font-values": "^5.1.0",
+ "postcss-minify-gradients": "^5.1.1",
+ "postcss-minify-params": "^5.1.4",
+ "postcss-minify-selectors": "^5.2.1",
+ "postcss-normalize-charset": "^5.1.0",
+ "postcss-normalize-display-values": "^5.1.0",
+ "postcss-normalize-positions": "^5.1.1",
+ "postcss-normalize-repeat-style": "^5.1.1",
+ "postcss-normalize-string": "^5.1.0",
+ "postcss-normalize-timing-functions": "^5.1.0",
+ "postcss-normalize-unicode": "^5.1.1",
+ "postcss-normalize-url": "^5.1.0",
+ "postcss-normalize-whitespace": "^5.1.1",
+ "postcss-ordered-values": "^5.1.3",
+ "postcss-reduce-initial": "^5.1.2",
+ "postcss-reduce-transforms": "^5.1.0",
+ "postcss-svgo": "^5.1.0",
+ "postcss-unique-selectors": "^5.1.1"
+ }
+ },
+ "cssnano-utils": {
+ "version": "3.1.0",
+ "dev": true,
+ "requires": {}
+ },
+ "csso": {
+ "version": "4.2.0",
+ "dev": true,
+ "requires": {
+ "css-tree": "^1.1.2"
+ }
+ },
+ "csstype": {
+ "version": "3.1.2"
+ },
+ "d3-dsv": {
+ "version": "2.0.0",
+ "dev": true,
+ "requires": {
+ "commander": "2",
+ "iconv-lite": "0.4",
+ "rw": "1"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.3",
+ "dev": true
+ }
+ }
+ },
+ "debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "didyoumean": {
+ "version": "1.2.2",
+ "dev": true
+ },
+ "directory-fonts-complete": {
+ "version": "1.2.0",
+ "dev": true,
+ "requires": {
+ "brotli": "^1.3.2",
+ "is-eot": "^1.0.0",
+ "is-otf": "^0.1.1",
+ "is-ttf": "^0.2.1",
+ "is-woff": "^1.0.1",
+ "is-woff2": "^1.0.0"
+ }
+ },
+ "dlv": {
+ "version": "1.1.3",
+ "dev": true
+ },
+ "dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "requires": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "dom-serializer": {
+ "version": "1.4.1",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ }
+ },
+ "domelementtype": {
+ "version": "2.3.0",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "4.3.1",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.2.0"
+ }
+ },
+ "domutils": {
+ "version": "2.8.0",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.4.349",
+ "dev": true
+ },
+ "entities": {
+ "version": "2.2.0",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es6-promise": {
+ "version": "2.3.0",
+ "dev": true
+ },
+ "esbuild": {
+ "version": "0.17.15",
+ "dev": true,
+ "requires": {
+ "@esbuild/android-arm": "0.17.15",
+ "@esbuild/android-arm64": "0.17.15",
+ "@esbuild/android-x64": "0.17.15",
+ "@esbuild/darwin-arm64": "0.17.15",
+ "@esbuild/darwin-x64": "0.17.15",
+ "@esbuild/freebsd-arm64": "0.17.15",
+ "@esbuild/freebsd-x64": "0.17.15",
+ "@esbuild/linux-arm": "0.17.15",
+ "@esbuild/linux-arm64": "0.17.15",
+ "@esbuild/linux-ia32": "0.17.15",
+ "@esbuild/linux-loong64": "0.17.15",
+ "@esbuild/linux-mips64el": "0.17.15",
+ "@esbuild/linux-ppc64": "0.17.15",
+ "@esbuild/linux-riscv64": "0.17.15",
+ "@esbuild/linux-s390x": "0.17.15",
+ "@esbuild/linux-x64": "0.17.15",
+ "@esbuild/netbsd-x64": "0.17.15",
+ "@esbuild/openbsd-x64": "0.17.15",
+ "@esbuild/sunos-x64": "0.17.15",
+ "@esbuild/win32-arm64": "0.17.15",
+ "@esbuild/win32-ia32": "0.17.15",
+ "@esbuild/win32-x64": "0.17.15"
+ }
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "2.0.2",
+ "dev": true
+ },
+ "events": {
+ "version": "3.3.0"
+ },
+ "fast-glob": {
+ "version": "3.2.12",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ }
+ },
+ "fastq": {
+ "version": "1.15.0",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-yarn-workspace-root": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz",
+ "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^4.0.2"
+ }
+ },
+ "fraction.js": {
+ "version": "4.2.0",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "requires": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "dev": true
+ },
+ "gensync": {
+ "version": "1.0.0-beta.2",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.6",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "dev": true
+ },
+ "globrex": {
+ "version": "0.1.2",
+ "dev": true
+ },
+ "google-fonts-complete": {
+ "version": "2.1.1",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.18"
+ },
+ "dependencies": {
+ "picocolors": {
+ "version": "0.2.1",
+ "dev": true
+ },
+ "postcss": {
+ "version": "7.0.39",
+ "dev": true,
+ "requires": {
+ "picocolors": "^0.2.1",
+ "source-map": "^0.6.1"
+ }
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "immutable": {
+ "version": "4.3.0",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.3.0",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "dev": true
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "dev": true,
+ "requires": {
+ "ci-info": "^2.0.0"
+ }
+ },
+ "is-core-module": {
+ "version": "2.11.0",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true
+ },
+ "is-eot": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "dev": true
+ },
+ "is-otf": {
+ "version": "0.1.2",
+ "dev": true,
+ "requires": {
+ "b3b": "0.0.1"
+ }
+ },
+ "is-ttf": {
+ "version": "0.2.2",
+ "dev": true,
+ "requires": {
+ "b3b": "0.0.1"
+ }
+ },
+ "is-woff": {
+ "version": "1.0.3",
+ "dev": true
+ },
+ "is-woff2": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "jiti": {
+ "version": "1.18.2",
+ "dev": true
+ },
+ "js-base64": {
+ "version": "2.1.9",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0"
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "dev": true
+ },
+ "json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.2.3",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "klaw-sync": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
+ "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11"
+ }
+ },
+ "lilconfig": {
+ "version": "2.1.0",
+ "dev": true
+ },
+ "lines-and-columns": {
+ "version": "1.2.4",
+ "dev": true
+ },
+ "lodash.memoize": {
+ "version": "4.1.2",
+ "dev": true
+ },
+ "lodash.uniq": {
+ "version": "4.5.0",
+ "dev": true
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "mdn-data": {
+ "version": "2.0.14",
+ "dev": true
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.5",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "dev": true
+ },
+ "mz": {
+ "version": "2.7.0",
+ "dev": true,
+ "requires": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "nanoid": {
+ "version": "3.3.6",
+ "dev": true
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node-releases": {
+ "version": "2.0.10",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "dev": true
+ },
+ "normalize-range": {
+ "version": "0.1.2",
+ "dev": true
+ },
+ "normalize-url": {
+ "version": "6.1.0",
+ "dev": true
+ },
+ "nth-check": {
+ "version": "2.1.1",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0"
+ }
+ },
+ "object-assign": {
+ "version": "4.1.1"
+ },
+ "object-hash": {
+ "version": "3.0.0",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "open": {
+ "version": "7.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
+ "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0",
+ "is-wsl": "^2.1.1"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "5.2.0",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
+ "patch-package": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz",
+ "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==",
+ "dev": true,
+ "requires": {
+ "@yarnpkg/lockfile": "^1.1.0",
+ "chalk": "^4.1.2",
+ "cross-spawn": "^6.0.5",
+ "find-yarn-workspace-root": "^2.0.0",
+ "fs-extra": "^9.0.0",
+ "is-ci": "^2.0.0",
+ "klaw-sync": "^6.0.0",
+ "minimist": "^1.2.6",
+ "open": "^7.4.2",
+ "rimraf": "^2.6.3",
+ "semver": "^5.6.0",
+ "slash": "^2.0.0",
+ "tmp": "^0.0.33",
+ "yaml": "^1.10.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "dev": true
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "dev": true
+ },
+ "pirates": {
+ "version": "4.0.5",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.4.21",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
+ "postcss-calc": {
+ "version": "8.2.4",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.9",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-colormin": {
+ "version": "5.3.1",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4",
+ "caniuse-api": "^3.0.0",
+ "colord": "^2.9.1",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-convert-values": {
+ "version": "5.1.3",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-discard-comments": {
+ "version": "5.1.2",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-discard-duplicates": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-discard-empty": {
+ "version": "5.1.1",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-discard-overridden": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-font-magician": {
+ "version": "3.0.0",
+ "dev": true,
+ "requires": {
+ "bootstrap-fonts-complete": "^1.0.0",
+ "directory-fonts-complete": "^1.2.0",
+ "google-fonts-complete": "^2.1.1"
+ }
+ },
+ "postcss-import": {
+ "version": "14.1.0",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ }
+ },
+ "postcss-js": {
+ "version": "4.0.1",
+ "dev": true,
+ "requires": {
+ "camelcase-css": "^2.0.1"
+ }
+ },
+ "postcss-load-config": {
+ "version": "4.0.1",
+ "dev": true,
+ "requires": {
+ "lilconfig": "^2.0.5",
+ "yaml": "^2.1.1"
+ },
+ "dependencies": {
+ "yaml": {
+ "version": "2.2.1",
+ "dev": true
+ }
+ }
+ },
+ "postcss-merge-longhand": {
+ "version": "5.1.7",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0",
+ "stylehacks": "^5.1.1"
+ }
+ },
+ "postcss-merge-rules": {
+ "version": "5.1.4",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4",
+ "caniuse-api": "^3.0.0",
+ "cssnano-utils": "^3.1.0",
+ "postcss-selector-parser": "^6.0.5"
+ }
+ },
+ "postcss-minify-font-values": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-minify-gradients": {
+ "version": "5.1.1",
+ "dev": true,
+ "requires": {
+ "colord": "^2.9.1",
+ "cssnano-utils": "^3.1.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-minify-params": {
+ "version": "5.1.4",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4",
+ "cssnano-utils": "^3.1.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-minify-selectors": {
+ "version": "5.2.1",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.5"
+ }
+ },
+ "postcss-nested": {
+ "version": "6.0.0",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "postcss-normalize-charset": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {}
+ },
+ "postcss-normalize-display-values": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-positions": {
+ "version": "5.1.1",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-repeat-style": {
+ "version": "5.1.1",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-string": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-timing-functions": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-unicode": {
+ "version": "5.1.1",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-url": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {
+ "normalize-url": "^6.0.1",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-normalize-whitespace": {
+ "version": "5.1.1",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-ordered-values": {
+ "version": "5.1.3",
+ "dev": true,
+ "requires": {
+ "cssnano-utils": "^3.1.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-reduce-initial": {
+ "version": "5.1.2",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4",
+ "caniuse-api": "^3.0.0"
+ }
+ },
+ "postcss-reduce-transforms": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "6.0.11",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "postcss-svgo": {
+ "version": "5.1.0",
+ "dev": true,
+ "requires": {
+ "postcss-value-parser": "^4.2.0",
+ "svgo": "^2.7.0"
+ }
+ },
+ "postcss-unique-selectors": {
+ "version": "5.1.1",
+ "dev": true,
+ "requires": {
+ "postcss-selector-parser": "^6.0.5"
+ }
+ },
+ "postcss-value-parser": {
+ "version": "4.2.0",
+ "dev": true
+ },
+ "prettier": {
+ "version": "2.8.7",
+ "dev": true
+ },
+ "prop-types": {
+ "version": "15.8.1",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "queue-microtask": {
+ "version": "1.2.3",
+ "dev": true
+ },
+ "quick-lru": {
+ "version": "5.1.1",
+ "dev": true
+ },
+ "react": {
+ "version": "18.2.0",
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "react-dom": {
+ "version": "18.2.0",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ }
+ },
+ "react-is": {
+ "version": "16.13.1"
+ },
+ "react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "react-virtualized": {
+ "version": "9.22.3",
+ "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.3.tgz",
+ "integrity": "sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==",
+ "requires": {
+ "@babel/runtime": "^7.7.2",
+ "clsx": "^1.0.4",
+ "dom-helpers": "^5.1.3",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.7.2",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "read-cache": {
+ "version": "1.0.0",
+ "dev": true,
+ "requires": {
+ "pify": "^2.3.0"
+ }
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+ },
+ "resolve": {
+ "version": "1.22.1",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "dev": true
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "rollup": {
+ "version": "3.20.2",
+ "dev": true,
+ "requires": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "run-parallel": {
+ "version": "1.2.0",
+ "dev": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "rw": {
+ "version": "1.3.3",
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "dev": true
+ },
+ "sass": {
+ "version": "1.60.0",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ }
+ },
+ "scheduler": {
+ "version": "0.23.0",
+ "requires": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
+ "dev": true
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "dev": true
+ },
+ "source-map-js": {
+ "version": "1.0.2",
+ "dev": true
+ },
+ "stable": {
+ "version": "0.1.8",
+ "dev": true
+ },
+ "stylehacks": {
+ "version": "5.1.1",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4",
+ "postcss-selector-parser": "^6.0.4"
+ }
+ },
+ "sucrase": {
+ "version": "3.31.0",
+ "dev": true,
+ "requires": {
+ "commander": "^4.0.0",
+ "glob": "7.1.6",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "4.1.1",
+ "dev": true
+ }
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "dev": true
+ },
+ "svg-parser": {
+ "version": "2.0.4",
+ "dev": true
+ },
+ "svgo": {
+ "version": "2.8.0",
+ "dev": true,
+ "requires": {
+ "@trysound/sax": "0.2.0",
+ "commander": "^7.2.0",
+ "css-select": "^4.1.3",
+ "css-tree": "^1.1.3",
+ "csso": "^4.2.0",
+ "picocolors": "^1.0.0",
+ "stable": "^0.1.8"
+ }
+ },
+ "tailwindcss": {
+ "version": "3.3.1",
+ "dev": true,
+ "requires": {
+ "arg": "^5.0.2",
+ "chokidar": "^3.5.3",
+ "color-name": "^1.1.4",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.2.12",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.17.2",
+ "lilconfig": "^2.0.6",
+ "micromatch": "^4.0.5",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.0.9",
+ "postcss-import": "^14.1.0",
+ "postcss-js": "^4.0.0",
+ "postcss-load-config": "^3.1.4",
+ "postcss-nested": "6.0.0",
+ "postcss-selector-parser": "^6.0.11",
+ "postcss-value-parser": "^4.2.0",
+ "quick-lru": "^5.1.1",
+ "resolve": "^1.22.1",
+ "sucrase": "^3.29.0"
+ },
+ "dependencies": {
+ "glob-parent": {
+ "version": "6.0.2",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.3"
+ }
+ },
+ "postcss-load-config": {
+ "version": "3.1.4",
+ "dev": true,
+ "requires": {
+ "lilconfig": "^2.0.5",
+ "yaml": "^1.10.2"
+ }
+ }
+ }
+ },
+ "thenify": {
+ "version": "3.3.1",
+ "dev": true,
+ "requires": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "thenify-all": {
+ "version": "1.6.0",
+ "dev": true,
+ "requires": {
+ "thenify": ">= 3.1.0 < 4"
+ }
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "dev": true
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "tosource": {
+ "version": "2.0.0-alpha.3",
+ "dev": true
+ },
+ "ts-interface-checker": {
+ "version": "0.1.13",
+ "dev": true
+ },
+ "tsconfck": {
+ "version": "2.1.1",
+ "dev": true,
+ "requires": {}
+ },
+ "typescript": {
+ "version": "4.9.5",
+ "dev": true
+ },
+ "universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true
+ },
+ "update-browserslist-db": {
+ "version": "1.0.10",
+ "dev": true,
+ "requires": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "dev": true
+ },
+ "vite": {
+ "version": "4.2.1",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.17.5",
+ "fsevents": "~2.3.2",
+ "postcss": "^8.4.21",
+ "resolve": "^1.22.1",
+ "rollup": "^3.18.0"
+ }
+ },
+ "vite-plugin-singlefile": {
+ "version": "0.13.5",
+ "dev": true,
+ "requires": {
+ "micromatch": "^4.0.5"
+ }
+ },
+ "vite-plugin-svgr": {
+ "version": "2.4.0",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^5.0.2",
+ "@svgr/core": "^6.5.1"
+ }
+ },
+ "vite-tsconfig-paths": {
+ "version": "4.0.8",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "globrex": "^0.1.2",
+ "tsconfck": "^2.1.0"
+ }
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "dev": true
+ },
+ "yaml": {
+ "version": "1.10.2",
+ "dev": true
+ }
+ }
+}
diff --git a/src/handbook/package.json b/src/handbook/package.json
new file mode 100644
index 000000000..94073bf07
--- /dev/null
+++ b/src/handbook/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "handbook",
+ "description": "The ultimate anime game handbook!",
+ "version": "0.1.0",
+ "private": true,
+
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview",
+
+ "postinstall": "npx patch-package",
+ "lint": "npx prettier --write \"src/**/*.{ts,tsx,js,jsx,json,md}\""
+ },
+
+ "dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-virtualized": "^9.22.3",
+
+ "events": "^3.3.0"
+ },
+ "devDependencies": {
+ "typescript": "^4.9.3",
+ "@types/react": "^18.0.28",
+ "@types/react-dom": "^18.0.11",
+ "@types/react-virtualized": "^9.21.21",
+ "@types/events": "^3.0.0",
+
+ "vite": "^4.2.0",
+ "vite-plugin-svgr": "^2.4.0",
+ "vite-tsconfig-paths": "^4.0.7",
+ "vite-plugin-singlefile": "^0.13.5",
+ "@vitejs/plugin-react-swc": "^3.0.0",
+ "@rollup/plugin-dsv": "^3.0.2",
+
+ "sass": "^1.58.3",
+ "cssnano": "^5.1.15",
+ "tailwindcss": "^3.2.7",
+ "autoprefixer": "^10.4.13",
+
+ "postcss": "^8.4.21",
+ "postcss-load-config": "^4.0.1",
+ "postcss-font-magician": "^3.0.0",
+
+ "prettier": "^2.8.7",
+ "patch-package": "^6.5.1"
+ }
+}
diff --git a/src/handbook/patches/react-virtualized+9.22.3.patch b/src/handbook/patches/react-virtualized+9.22.3.patch
new file mode 100644
index 000000000..d40d28db9
--- /dev/null
+++ b/src/handbook/patches/react-virtualized+9.22.3.patch
@@ -0,0 +1,10 @@
+diff --git a/node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js b/node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js
+index d00f0f1..42456dc 100644
+--- a/node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js
++++ b/node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js
+@@ -71,4 +71,3 @@ export function unregisterScrollListener(component, element) {
+ }
+ }
+ }
+-import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js";
+\ No newline at end of file
diff --git a/src/handbook/src/backend/data.ts b/src/handbook/src/backend/data.ts
new file mode 100644
index 000000000..72238d0b9
--- /dev/null
+++ b/src/handbook/src/backend/data.ts
@@ -0,0 +1,146 @@
+import commands from "@data/commands.json";
+import entities from "@data/entities.csv";
+import avatars from "@data/avatars.csv";
+import scenes from "@data/scenes.csv";
+import items from "@data/items.csv";
+
+import { Quality, ItemType, ItemCategory, SceneType } from "@backend/types";
+import type { Command, Avatar, Item, Scene, Entity } from "@backend/types";
+
+import { inRange } from "@app/utils";
+
+type AvatarDump = { [key: number]: Avatar };
+type CommandDump = { [key: string]: Command };
+type TaggedItems = { [key: number]: Item[] };
+
+/**
+ * @see {@file src/handbook/data/README.md}
+ */
+
+export const sortedItems: TaggedItems = {
+ [ItemCategory.Constellation]: [], // Range: 1102 - 11xx
+ [ItemCategory.Avatar]: [], // Range: 1002 - 10xx
+ [ItemCategory.Weapon]: [],
+ [ItemCategory.Artifact]: [],
+ [ItemCategory.Furniture]: [],
+ [ItemCategory.Material]: [],
+ [ItemCategory.Miscellaneous]: []
+};
+
+/**
+ * Setup function for this file.
+ * Sorts all items into their respective categories.
+ */
+export function setup(): void {
+ getItems().forEach((item) => {
+ switch (item.type) {
+ case ItemType.Weapon:
+ sortedItems[ItemCategory.Weapon].push(item);
+ break;
+ case ItemType.Material:
+ sortedItems[ItemCategory.Material].push(item);
+ break;
+ case ItemType.Furniture:
+ sortedItems[ItemCategory.Furniture].push(item);
+ break;
+ case ItemType.Reliquary:
+ sortedItems[ItemCategory.Artifact].push(item);
+ break;
+ }
+
+ // Sort constellations.
+ if (inRange(item.id, 1102, 1199)) {
+ sortedItems[ItemCategory.Constellation].push(item);
+ }
+ // Sort avatars.
+ if (inRange(item.id, 1002, 1099)) {
+ sortedItems[ItemCategory.Avatar].push(item);
+ }
+ });
+}
+
+/**
+ * Fetches and casts all commands in the file.
+ */
+export function getCommands(): CommandDump {
+ return commands as CommandDump;
+}
+
+/**
+ * Fetches and lists all the commands in the file.
+ */
+export function listCommands(): Command[] {
+ return Object.values(getCommands());
+}
+
+/**
+ * Fetches and casts all entities in the file.
+ */
+export function getEntities(): Entity[] {
+ return entities.map((entry) => {
+ const values = Object.values(entry) as string[];
+ const id = parseInt(values[0]);
+ return {
+ id,
+ name: values[1],
+ internal: values[2]
+ };
+ });
+}
+
+/**
+ * Fetches and casts all avatars in the file.
+ */
+export function getAvatars(): AvatarDump {
+ const map: AvatarDump = {};
+ avatars.forEach((avatar) => {
+ const values = Object.values(avatar) as [string, string, string];
+ const id = parseInt(values[0]);
+ map[id] = {
+ id,
+ name: values[1],
+ quality: values[2] as Quality
+ };
+ });
+
+ return map;
+}
+
+/**
+ * Fetches and lists all the avatars in the file.
+ */
+export function listAvatars(): Avatar[] {
+ return Object.values(getAvatars());
+}
+
+/**
+ * Fetches and casts all scenes in the file.
+ */
+export function getScenes(): Scene[] {
+ return scenes.map((entry) => {
+ const values = Object.values(entry) as string[];
+ const id = parseInt(values[0]);
+ return {
+ id,
+ identifier: values[1],
+ type: values[2] as SceneType
+ };
+ });
+}
+
+/**
+ * Fetches and casts all items in the file.
+ */
+export function getItems(): Item[] {
+ return items.map((entry) => {
+ const values = Object.values(entry) as string[];
+ const id = parseInt(values[0]);
+ return {
+ id,
+ name: values[1],
+ type: values[3] as ItemType,
+ quality: values[2] as Quality,
+ icon: values[4]
+ };
+ });
+}
diff --git a/src/handbook/src/backend/events.ts b/src/handbook/src/backend/events.ts
new file mode 100644
index 000000000..8904b8c2b
--- /dev/null
+++ b/src/handbook/src/backend/events.ts
@@ -0,0 +1,100 @@
+import { EventEmitter } from "events";
+
+import type { Page } from "@backend/types";
+import { isPage } from "@backend/types";
+
+const emitter = new EventEmitter();
+const navigation = new EventEmitter();
+
+let navStack: Page[] = [];
+let currentPage: number | null = -1;
+
+/**
+ * Sets up the event system.
+ */
+export function setup(): void {
+ window.onpopstate = (event) => {
+ navigate(event.state, false);
+ };
+
+ setTimeout(() => {
+ // Check if the window's href is a page.
+ const page = window.location.href.split("/").pop();
+ if (page == undefined || page == "") return;
+
+ // Convert the page to a Page type.
+ const pageName = page.charAt(0).toUpperCase() + page.slice(1);
+ const pageType = pageName as Page;
+
+ // Navigate to the page.
+ isPage(page) && navigate(pageType, false);
+ }, 3e2);
+}
+
+/**
+ * Adds a navigation listener.
+ *
+ * @param listener The listener to add.
+ */
+export function addNavListener(listener: (page: Page) => void) {
+ navigation.on("navigate", listener);
+}
+
+/**
+ * Removes a navigation listener.
+ *
+ * @param listener The listener to remove.
+ */
+export function removeNavListener(listener: (page: Page) => void) {
+ navigation.off("navigate", listener);
+}
+
+/**
+ * Navigates to a page.
+ * Returns the last page.
+ *
+ * @param page The page to navigate to.
+ * @param update Whether to update the state or not.
+ */
+export function navigate(page: Page, update: boolean = true): Page | null {
+ // Navigate to the new page.
+ const lastPage = currentPage;
+ navigation.emit("navigate", page);
+
+ if (update) {
+ // Set the current page.
+ navStack.push(page);
+ currentPage = navStack.length - 1;
+ // Add the page to the window history.
+ window.history.pushState(page, page, "/" + page.toLowerCase());
+ }
+
+ return lastPage ? navStack[lastPage] : null;
+}
+
+/**
+ * Goes back or forward in the navigation stack.
+ *
+ * @param forward Whether to go forward or not.
+ */
+export function go(forward: boolean): void {
+ if (currentPage == undefined) return;
+
+ // Get the new page.
+ const newPage = forward ? currentPage + 1 : currentPage - 1;
+ if (newPage < 0 || newPage >= navStack.length) return;
+
+ // Navigate to the new page.
+ currentPage = newPage;
+ navigation.emit("navigate", navStack[newPage]);
+
+ // Update the window history.
+ window.history.pushState(navStack[newPage], navStack[newPage], "/" + navStack[newPage].toLowerCase());
+}
+
+// This is the global event system.
+export default emitter;
+// @ts-ignore
+window["emitter"] = emitter;
+// @ts-ignore
+window["navigate"] = navigate;
diff --git a/src/handbook/src/backend/files.d.ts b/src/handbook/src/backend/files.d.ts
new file mode 100644
index 000000000..205989139
--- /dev/null
+++ b/src/handbook/src/backend/files.d.ts
@@ -0,0 +1,8 @@
+declare module "*.svg" {
+ export const ReactComponent: React.FunctionComponent>;
+}
+
+declare module "*.csv" {
+ const content: any[];
+ export default content;
+}
diff --git a/src/handbook/src/backend/server.ts b/src/handbook/src/backend/server.ts
new file mode 100644
index 000000000..f317e73b5
--- /dev/null
+++ b/src/handbook/src/backend/server.ts
@@ -0,0 +1,74 @@
+import type { CommandResponse } from "@backend/types";
+
+let targetPlayer = 0; // The UID of the target player.
+
+/**
+ * Sets the target player.
+ *
+ * @param player The UID of the target player.
+ */
+export function setTargetPlayer(player: number): void {
+ targetPlayer = player;
+ console.log(`Target Player is now: ${targetPlayer}`);
+}
+
+/**
+ * Validates a number.
+ *
+ * @param value The number to validate.
+ */
+function invalid(value: number): boolean {
+ return isNaN(value) || value < 0;
+}
+
+/**
+ * Grants an avatar to a player.
+ *
+ * @param avatar The avatar's ID.
+ * @param level The avatar's level.
+ * @param constellations The avatar's unlocked constellations.
+ * @param talents The level for the avatar's talents.
+ */
+export async function grantAvatar(
+ avatar: number,
+ level = 90,
+ constellations = 6,
+ talents = 6
+): Promise {
+ // Validate the numbers.
+ if (invalid(avatar) || invalid(level) || invalid(constellations) || invalid(talents))
+ return { status: -1, message: "Invalid arguments." };
+
+ return await fetch(`https://localhost:443/handbook/avatar`, {
+ method: "POST",
+ body: JSON.stringify({
+ player: targetPlayer.toString(),
+ avatar: avatar.toString(),
+ level,
+ constellations,
+ talentLevels: talents
+ })
+ }).then((res) => res.json());
+}
+
+/**
+ * Gives an item to the player.
+ * This does not support weapons.
+ * This does not support relics.
+ *
+ * @param item The item's ID.
+ * @param amount The amount of the item to give.
+ */
+export async function giveItem(item: number, amount = 1): Promise {
+ // Validate the number.
+ if (isNaN(amount) || amount < 1) return { status: -1, message: "Invalid amount." };
+
+ return await fetch(`https://localhost:443/handbook/item`, {
+ method: "POST",
+ body: JSON.stringify({
+ player: targetPlayer.toString(),
+ item: item.toString(),
+ amount
+ })
+ }).then((res) => res.json());
+}
diff --git a/src/handbook/src/backend/types.ts b/src/handbook/src/backend/types.ts
new file mode 100644
index 000000000..857f1aebb
--- /dev/null
+++ b/src/handbook/src/backend/types.ts
@@ -0,0 +1,159 @@
+export type Page = "Home" | "Commands" | "Avatars" | "Items" | "Entities" | "Scenes";
+export type Days = "Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday";
+
+export type Command = {
+ name: string[];
+ description: string;
+ usage: string[];
+ permission: string[];
+ target: Target;
+};
+
+export type Avatar = {
+ name: string;
+ quality: Quality;
+ id: number;
+};
+
+export type Scene = {
+ identifier: string;
+ type: SceneType;
+ id: number;
+};
+
+export type Item = {
+ id: number;
+ name: string;
+ quality: Quality;
+ type: ItemType;
+ icon: string;
+};
+
+export type Entity = {
+ id: number;
+ name: string;
+ internal: string;
+};
+
+// Exported from Project Amber.
+export type ItemInfo = {
+ response: number | 200 | 404;
+ data: {
+ name: string;
+ description: string;
+ type: string;
+ recipe: boolean;
+ mapMark: boolean;
+ source: {
+ name: string;
+ type: string | "domain";
+ days: Days;
+ }[];
+ icon: string;
+ rank: 1 | 2 | 3 | 4 | 5;
+ route: string;
+ };
+};
+
+// Exported from Project Amber.
+export type EntityInfo = {
+ response: number | 200 | 404;
+ data: {
+ id: number;
+ name: string;
+ type: string;
+ icon: string;
+ route: string;
+ title: string;
+ specialName: string;
+ description: string;
+ entries: any[];
+ tips: null;
+ };
+};
+
+export enum Target {
+ None = "NONE",
+ Offline = "OFFLINE",
+ Player = "PLAYER",
+ Online = "ONLINE"
+}
+
+export enum Quality {
+ Legendary = "LEGENDARY",
+ Epic = "EPIC",
+ Rare = "RARE",
+ Uncommon = "UNCOMMON",
+ Common = "COMMON",
+ Unknown = "UNKNOWN"
+}
+
+export enum ItemType {
+ None = "ITEM_NONE",
+ Virtual = "ITEM_VIRTUAL",
+ Material = "ITEM_MATERIAL",
+ Reliquary = "ITEM_RELIQUARY",
+ Weapon = "ITEM_WEAPON",
+ Display = "ITEM_DISPLAY",
+ Furniture = "ITEM_FURNITURE"
+}
+
+export enum SceneType {
+ None = "SCENE_NONE",
+ World = "SCENE_WORLD",
+ Dungeon = "SCENE_DUNGEON",
+ Room = "SCENE_ROOM",
+ HomeWorld = "SCENE_HOME_WORLD",
+ HomeRoom = "SCENE_HOME_ROOM",
+ Activity = "SCENE_ACTIVITY"
+}
+
+export enum ItemCategory {
+ Constellation,
+ Avatar,
+ Weapon,
+ Artifact,
+ Furniture,
+ Material,
+ Miscellaneous
+}
+
+export type CommandResponse = {
+ status: number | 200 | 500;
+ message: string;
+};
+
+/**
+ * Checks if a string is a page.
+ *
+ * @param page The string to check.
+ */
+export function isPage(page: string): page is Page {
+ return ["Home", "Commands", "Avatars", "Items", "Entities", "Scenes"].includes(page);
+}
+
+/**
+ * Converts an item type to a string.
+ *
+ * @param type The item type to convert.
+ */
+export function itemTypeToString(type: ItemType): string {
+ switch (type) {
+ default:
+ return "Unknown";
+ case ItemType.None:
+ return "None";
+ case ItemType.Virtual:
+ return "Virtual";
+ case ItemType.Material:
+ return "Material";
+ case ItemType.Reliquary:
+ return "Reliquary";
+ case ItemType.Weapon:
+ return "Weapon";
+ case ItemType.Display:
+ return "Display";
+ case ItemType.Furniture:
+ return "Furniture";
+ }
+}
diff --git a/src/handbook/src/css/App.scss b/src/handbook/src/css/App.scss
new file mode 100644
index 000000000..668df9cef
--- /dev/null
+++ b/src/handbook/src/css/App.scss
@@ -0,0 +1,54 @@
+html {
+ --background-color: #346b77;
+ --secondary-color: #418493;
+ --accent-color: #5abcb9;
+
+ --text-primary-color: #FFFFFF;
+
+ --legendary-color: #926d45;
+ --epic-color: #7b5c90;
+
+ overflow: hidden;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ height: 100vh;
+ width: 100%;
+ overflow: hidden;
+
+ #root {
+ height: 100%;
+ width: 100%;
+ }
+
+ * {
+ font-family: 'SDK_SC_Web', 'SDK_JP_Web', 'Poppins', sans-serif;
+ }
+
+ svg:focus {
+ outline: none;
+ }
+}
+
+.App {
+ display: flex;
+ flex-direction: row;
+
+ width: 100%;
+ height: 100%;
+}
+
+::-webkit-scrollbar {
+ width: 5px;
+}
+
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--accent-color);
+ border-radius: 10px;
+}
diff --git a/src/handbook/src/css/Text.scss b/src/handbook/src/css/Text.scss
new file mode 100644
index 000000000..5eae1e523
--- /dev/null
+++ b/src/handbook/src/css/Text.scss
@@ -0,0 +1,28 @@
+p {
+ color: var(--text-primary-color);
+ margin: 0;
+}
+
+h1 {
+ color: var(--text-primary-color);
+ font-style: normal;
+ font-weight: normal;
+ font-size: 48px;
+ margin: 0;
+}
+
+h2 {
+ color: var(--text-primary-color);
+ font-style: normal;
+ font-weight: 600;
+ font-size: 24px;
+ margin: 0;
+}
+
+h3 {
+ color: var(--text-primary-color);
+ font-style: normal;
+ font-weight: 600;
+ font-size: 18px;
+ margin: 0;
+}
diff --git a/src/handbook/src/css/components/VirtualizedGrid.scss b/src/handbook/src/css/components/VirtualizedGrid.scss
new file mode 100644
index 000000000..44b569499
--- /dev/null
+++ b/src/handbook/src/css/components/VirtualizedGrid.scss
@@ -0,0 +1,4 @@
+.GridRow {
+ display: flex;
+ flex-direction: row;
+}
diff --git a/src/handbook/src/css/pages/AvatarsPage.scss b/src/handbook/src/css/pages/AvatarsPage.scss
new file mode 100644
index 000000000..77ce01e18
--- /dev/null
+++ b/src/handbook/src/css/pages/AvatarsPage.scss
@@ -0,0 +1,33 @@
+.AvatarsPage {
+ display: flex;
+ height: 100%;
+ width: 100%;
+
+ background-color: var(--background-color);
+ flex-direction: column;
+
+ padding: 24px;
+}
+
+.AvatarsPage_Title {
+ max-width: 275px;
+ max-height: 60px;
+
+ font-size: 48px;
+ font-weight: bold;
+ text-align: center;
+
+ margin-bottom: 30px;
+}
+
+.AvatarsPage_List {
+ display: grid;
+ gap: 15px 15px;
+
+ max-width: 90%;
+
+ grid-template-columns: repeat(12, 100px);
+
+ margin-bottom: 28px;
+ overflow-y: scroll;
+}
diff --git a/src/handbook/src/css/pages/CommandsPage.scss b/src/handbook/src/css/pages/CommandsPage.scss
new file mode 100644
index 000000000..147a20b1a
--- /dev/null
+++ b/src/handbook/src/css/pages/CommandsPage.scss
@@ -0,0 +1,30 @@
+.CommandsPage {
+ display: flex;
+ height: 100%;
+ width: 100%;
+
+ background-color: var(--background-color);
+ flex-direction: column;
+
+ padding: 24px;
+}
+
+.CommandsPage_Title {
+ max-width: 275px;
+ max-height: 60px;
+
+ font-size: 48px;
+ font-weight: bold;
+ text-align: center;
+
+ margin-bottom: 30px;
+}
+
+.CommandsPage_List {
+ display: flex;
+ flex-direction: column;
+
+ gap: 15px;
+ margin-bottom: 28px;
+ overflow-y: scroll;
+}
diff --git a/src/handbook/src/css/pages/EntitiesPage.scss b/src/handbook/src/css/pages/EntitiesPage.scss
new file mode 100644
index 000000000..3da203659
--- /dev/null
+++ b/src/handbook/src/css/pages/EntitiesPage.scss
@@ -0,0 +1,93 @@
+.EntitiesPage {
+ display: flex;
+ height: 100%;
+ width: 100%;
+
+ flex-direction: row;
+ justify-content: space-between;
+ background-color: var(--background-color);
+
+ padding: 24px;
+}
+
+.EntitiesPage_Content {
+ display: flex;
+ flex-direction: column;
+
+ width: 80%;
+}
+
+.EntitiesPage_Header {
+ display: flex;
+ flex-direction: row;
+
+ gap: 30px;
+ align-content: center;
+
+ margin-bottom: 30px;
+}
+
+.EntitiesPage_Title {
+ max-width: 230px;
+ max-height: 60px;
+
+ font-size: 48px;
+ font-weight: bold;
+ text-align: center;
+ justify-content: center;
+}
+
+.EntitiesPage_Search {
+ display: flex;
+
+ width: 100%;
+ height: 100%;
+ max-width: 465px;
+ max-height: 60px;
+
+ box-sizing: border-box;
+ align-items: center;
+ border-radius: 10px;
+
+ background-color: var(--secondary-color);
+}
+
+.EntitiesPage_Input {
+ background-color: transparent;
+ border: none;
+
+ color: var(--text-primary-color);
+ font-size: 20px;
+ width: 100%;
+ padding: 11px;
+
+ &:focus, &:active {
+ outline: none;
+ }
+}
+
+.EntitiesPage_Input::placeholder {
+ color: var(--text-secondary-color);
+ opacity: 1;
+}
+
+.EntitiesPage_List {
+ display: grid;
+ gap: 15px 15px;
+
+ grid-template-columns: repeat(15, 100px);
+
+ margin-bottom: 28px;
+ overflow-y: scroll;
+}
+
+.EntitiesPage_Card {
+ display: flex;
+
+ width: 100%;
+ max-width: 300px;
+ min-height: 300px;
+ max-height: 700px;
+
+ align-self: center;
+}
diff --git a/src/handbook/src/css/pages/HomePage.scss b/src/handbook/src/css/pages/HomePage.scss
new file mode 100644
index 000000000..ee2afacdc
--- /dev/null
+++ b/src/handbook/src/css/pages/HomePage.scss
@@ -0,0 +1,151 @@
+.HomePage {
+ display: flex;
+ height: 100%;
+ width: 100%;
+
+ background-color: var(--background-color);
+ flex-direction: column;
+ justify-content: space-between;
+
+ div {
+ display: flex;
+ }
+}
+
+.HomePage_Top {
+ display: flex;
+ width: 100%;
+ height: 80%;
+
+ flex-direction: column;
+ align-items: center;
+
+ gap: 24px;
+}
+
+.HomePage_Title {
+ margin-top: 31px;
+ margin-bottom: 15px;
+}
+
+.HomePage_Buttons {
+ width: 100%;
+ height: 40%;
+
+ max-width: 1376px;
+ max-height: 256px;
+
+ gap: 24px;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+.HomePage_Bottom {
+ display: flex;
+
+ height: 50%;
+ max-height: 125px;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.HomePage_Box {
+ display: flex;
+ background-color: var(--secondary-color);
+}
+
+.HomePage_Disclaimer {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ background-color: var(--secondary-color);
+
+ width: 50%;
+ height: 100%;
+ max-width: 630px;
+ max-height: 93px;
+
+ margin: 0 0 0 60px;
+ border-radius: 10px;
+
+ box-sizing: border-box;
+ padding: 11px;
+
+ :nth-child(1) {
+ font-size: 24px;
+ max-height: 30px;
+
+ display: flex;
+ flex-direction: column;
+ }
+
+ p {
+ font-size: 18px;
+ max-height: 40px;
+ }
+}
+
+.HomePage_Discord {
+ max-height: 40px;
+ max-width: 150px;
+
+ gap: 8px;
+ align-self: center;
+ align-items: center;
+
+ svg {
+ width: 100%;
+ height: 100%;
+ max-width: 44px;
+ max-height: 30px;
+ }
+
+ p {
+ font-size: 16px;
+ }
+}
+
+.HomePage_Text {
+ display: flex;
+ flex-direction: column;
+ background-color: var(--secondary-color);
+
+ max-width: 300px;
+ max-height: 80px;
+
+ margin: 13px 60px 0 0;
+ border-radius: 10px;
+
+ box-sizing: border-box;
+ padding: 11px;
+}
+
+.HomePage_Credits {
+ display: flex;
+ flex-direction: row;
+ gap: 5px;
+
+ max-height: 18px;
+ padding-bottom: 5px;
+
+ :nth-child(1) {
+ font-size: 18px;
+ font-weight: bold;
+ }
+
+ :nth-child(2) {
+ font-size: 10px;
+ align-self: center;
+ }
+}
+
+.HomePage_Links {
+ display: flex;
+ flex-wrap: wrap;
+
+ a {
+ color: var(--text-primary-color);
+ text-decoration: none;
+ padding-right: 10px;
+ }
+}
diff --git a/src/handbook/src/css/pages/ItemsPage.scss b/src/handbook/src/css/pages/ItemsPage.scss
new file mode 100644
index 000000000..3ef443681
--- /dev/null
+++ b/src/handbook/src/css/pages/ItemsPage.scss
@@ -0,0 +1,93 @@
+.ItemsPage {
+ display: flex;
+ height: 100%;
+ width: 100%;
+
+ flex-direction: row;
+ justify-content: space-between;
+ background-color: var(--background-color);
+
+ padding: 24px;
+}
+
+.ItemsPage_Content {
+ display: flex;
+ flex-direction: column;
+
+ width: 80%;
+}
+
+.ItemsPage_Header {
+ display: flex;
+ flex-direction: row;
+
+ gap: 30px;
+ align-content: center;
+
+ margin-bottom: 30px;
+}
+
+.ItemsPage_Title {
+ max-width: 130px;
+ max-height: 60px;
+
+ font-size: 48px;
+ font-weight: bold;
+ text-align: center;
+ justify-content: center;
+}
+
+.ItemsPage_Search {
+ display: flex;
+
+ width: 100%;
+ height: 100%;
+ max-width: 465px;
+ max-height: 60px;
+
+ box-sizing: border-box;
+ align-items: center;
+ border-radius: 10px;
+
+ background-color: var(--secondary-color);
+}
+
+.ItemsPage_Input {
+ background-color: transparent;
+ border: none;
+
+ color: var(--text-primary-color);
+ font-size: 20px;
+ width: 100%;
+ padding: 11px;
+
+ &:focus, &:active {
+ outline: none;
+ }
+}
+
+.ItemsPage_Input::placeholder {
+ color: var(--text-secondary-color);
+ opacity: 1;
+}
+
+.ItemsPage_List {
+ display: grid;
+ gap: 15px 15px;
+
+ grid-template-columns: repeat(15, 100px);
+
+ margin-bottom: 28px;
+ overflow-y: scroll;
+}
+
+.ItemsPage_Card {
+ display: flex;
+
+ width: 100%;
+ max-width: 300px;
+ min-height: 300px;
+ max-height: 700px;
+
+ align-self: center;
+}
diff --git a/src/handbook/src/css/pages/ScenesPage.scss b/src/handbook/src/css/pages/ScenesPage.scss
new file mode 100644
index 000000000..8762741d4
--- /dev/null
+++ b/src/handbook/src/css/pages/ScenesPage.scss
@@ -0,0 +1,44 @@
+.ScenesPage {
+ display: flex;
+ height: 100%;
+ width: 100%;
+
+ background-color: var(--background-color);
+ flex-direction: column;
+
+ padding: 24px;
+}
+
+.ScenesPage_Title {
+ max-width: 180px;
+ max-height: 60px;
+
+ font-size: 48px;
+ font-weight: bold;
+ text-align: center;
+
+ margin-bottom: 30px;
+}
+
+.ScenesPage_List {
+ display: flex;
+ flex-direction: column;
+
+ gap: 15px;
+ margin-bottom: 28px;
+ overflow-y: scroll;
+}
+
+.ScenesPage_Button {
+ width: 94px;
+ height: 34px;
+
+ margin: 0;
+ border-radius: 10px;
+ border: transparent;
+
+ font-size: 20px;
+
+ color: var(--text-primary-color);
+ background-color: var(--background-color);
+}
diff --git a/src/handbook/src/css/views/Content.scss b/src/handbook/src/css/views/Content.scss
new file mode 100644
index 000000000..2a2abba8b
--- /dev/null
+++ b/src/handbook/src/css/views/Content.scss
@@ -0,0 +1,4 @@
+.Content {
+ width: 100%;
+ height: 100%;
+}
diff --git a/src/handbook/src/css/views/SideBar.scss b/src/handbook/src/css/views/SideBar.scss
new file mode 100644
index 000000000..52c2ef5ce
--- /dev/null
+++ b/src/handbook/src/css/views/SideBar.scss
@@ -0,0 +1,67 @@
+.SideBar {
+ display: flex;
+ flex-direction: column;
+
+ height: 100%;
+ width: 100%;
+ max-width: 300px;
+
+ background-color: var(--secondary-color);
+
+ gap: 40px;
+}
+
+.SideBar_Title {
+ margin-top: 42px;
+ line-height: 41px;
+ font-size: 34px;
+
+ max-width: 256px;
+ max-height: 128px;
+ text-align: center;
+ align-self: center;
+}
+
+.SideBar_Buttons {
+ display: flex;
+ flex-direction: column;
+
+ padding-left: 27px;
+ gap: 15px;
+}
+
+.SideBar_Enter {
+ display: flex;
+
+ width: 100%;
+ height: 100%;
+ max-width: 250px;
+ max-height: 50px;
+ margin-bottom: 24px;
+
+ box-sizing: border-box;
+ align-self: center;
+ align-items: center;
+ border-radius: 10px;
+
+ background-color: var(--background-color);
+}
+
+.SideBar_Input {
+ background-color: transparent;
+ border: none;
+
+ color: var(--text-primary-color);
+ font-size: 20px;
+ width: 100%;
+ padding: 11px;
+
+ &:focus, &:active {
+ outline: none;
+ }
+}
+
+.SideBar_Input::placeholder {
+ color: var(--text-secondary-color);
+ opacity: 1;
+}
diff --git a/src/handbook/src/css/widgets/Card.scss b/src/handbook/src/css/widgets/Card.scss
new file mode 100644
index 000000000..538b55f05
--- /dev/null
+++ b/src/handbook/src/css/widgets/Card.scss
@@ -0,0 +1,56 @@
+.Card {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+
+ width: 100%;
+ max-width: 1510px;
+ max-height: 100px;
+
+ border-radius: 15px;
+ padding: 10px;
+ box-sizing: border-box;
+
+ background-color: var(--secondary-color);
+}
+
+.Card_Content {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+}
+
+.Card_Header {
+ display: flex;
+ flex-direction: row;
+
+ width: 100%;
+ height: 100%;
+
+ gap: 15px;
+ align-items: center;
+}
+
+.Card_Title {
+ font-size: 32px;
+ font-weight: bold;
+}
+
+.Card_Alternate {
+ font-size: 24px;
+}
+
+.Card_Description {
+ color: var(--text-primary-color);
+
+ overflow-y: scroll;
+ max-height: 24px;
+}
+
+.Card_Button {
+ display: flex;
+ margin-right: 13px;
+
+ align-self: center;
+ justify-content: center;
+}
diff --git a/src/handbook/src/css/widgets/Character.scss b/src/handbook/src/css/widgets/Character.scss
new file mode 100644
index 000000000..5d161d3ee
--- /dev/null
+++ b/src/handbook/src/css/widgets/Character.scss
@@ -0,0 +1,36 @@
+.Character {
+ display: flex;
+ flex-direction: column;
+
+ max-width: 100px;
+ max-height: 125px;
+ border-radius: 15px;
+
+ height: 100%;
+
+ overflow: hidden;
+}
+
+.Character_Icon {
+ width: 100%;
+ height: 100%;
+
+ max-width: 96px;
+ max-height: 96px;
+}
+
+.Character_Label {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ background-color: var(--secondary-color);
+
+ max-width: 100px;
+ height: 30px;
+
+ p {
+ font-size: 18px;
+ text-align: center;
+ }
+}
diff --git a/src/handbook/src/css/widgets/HomeButton.scss b/src/handbook/src/css/widgets/HomeButton.scss
new file mode 100644
index 000000000..6f05ebe07
--- /dev/null
+++ b/src/handbook/src/css/widgets/HomeButton.scss
@@ -0,0 +1,29 @@
+.HomeButton {
+ display: flex;
+ flex-direction: column;
+
+ width: 100%;
+ height: 100%;
+ max-width: 256px;
+ max-height: 256px;
+
+ background-color: var(--secondary-color);
+
+ align-items: center;
+ justify-content: center;
+ gap: 20px;
+
+ border-radius: 10px;
+}
+
+.HomeButton_Icon {
+ max-width: 128px;
+ max-height: 128px;
+}
+
+.HomeButton_Label {
+ font-size: 34px;
+ line-height: 44px;
+ text-align: center;
+ font-style: normal;
+}
diff --git a/src/handbook/src/css/widgets/ItemCard.scss b/src/handbook/src/css/widgets/ItemCard.scss
new file mode 100644
index 000000000..3578447c5
--- /dev/null
+++ b/src/handbook/src/css/widgets/ItemCard.scss
@@ -0,0 +1,143 @@
+.ItemCard {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+
+ width: 100%;
+ height: 100%;
+ max-width: 300px;
+ min-height: 300px;
+ max-height: 700px;
+
+ padding: 20px;
+ box-sizing: border-box;
+
+ border-radius: 10px;
+ background-color: var(--accent-color);
+}
+
+.ItemCard_Content {
+ display: flex;
+ gap: 10px;
+
+ flex-direction: column;
+}
+
+.ItemCard_Header {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.ItemCard_Info {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+
+ :nth-child(1) {
+ font-weight: bold;
+ font-size: 20px;
+
+ max-width: 170px;
+ max-height: 60px;
+
+ color: var(--text-primary-color);
+ }
+
+ :nth-child(2) {
+ font-size: 16px;
+
+ color: var(--text-primary-color);
+ }
+}
+
+.ItemCard_Icon {
+ width: 64px;
+ height: 64px
+}
+
+.ItemCard_Description {
+ display: flex;
+ flex-direction: column;
+
+ max-width: 250px;
+ max-height: 460px;
+
+ p {
+ font-size: 14px;
+
+ color: var(--text-primary-color);
+ }
+}
+
+.ItemCard_Actions {
+ display: flex;
+ flex-direction: column;
+
+ gap: 5px;
+ padding-top: 10px;
+}
+
+.ItemCard_Counter {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+
+ width: 100%;
+ height: 100%;
+ max-width: 260px;
+ max-height: 46px;
+
+ border-radius: 10px;
+ padding: 0 13px 0 13px;
+ box-sizing: border-box;
+
+ align-items: center;
+ background-color: var(--secondary-color);
+}
+
+.ItemCard_Operation {
+ user-select: none;
+ display: flex;
+
+ width: 30px;
+ height: 20px;
+
+ font-size: 24px;
+ align-items: center;
+ justify-content: center;
+ color: var(--text-primary-color);
+
+ background-color: var(--background-color);
+}
+
+.ItemCard_Count {
+ max-width: 105px;
+ height: 48px;
+
+ font-size: 24px;
+ text-align: center;
+ background-color: transparent;
+ color: var(--text-primary-color);
+ border: transparent;
+}
+
+.ItemCard_Count:focus {
+ outline: none;
+}
+
+.ItemCard_Submit {
+ width: 100%;
+ height: 46px;
+ max-width: 260px;
+
+ border-radius: 10px;
+ text-align: center;
+ justify-content: center;
+
+ border: transparent;
+ font-size: 24px;
+
+ color: var(--text-primary-color);
+ background-color: var(--secondary-color);
+}
diff --git a/src/handbook/src/css/widgets/MiniCard.scss b/src/handbook/src/css/widgets/MiniCard.scss
new file mode 100644
index 000000000..80c6c6fe6
--- /dev/null
+++ b/src/handbook/src/css/widgets/MiniCard.scss
@@ -0,0 +1,40 @@
+.MiniCard {
+ display: flex;
+
+ width: 64px;
+ height: 64px;
+
+ overflow: hidden;
+ justify-content: center;
+}
+
+.MiniCard_Background {
+ display: flex;
+ align-items: center;
+
+ max-width: 64px;
+ max-height: 64px;
+
+ border-radius: 10px;
+ background-color: var(--secondary-color);
+}
+
+.MiniCard_Icon {
+ max-width: 64px;
+ max-height: 64px;
+ object-fit: scale-down;
+ border-radius: 10px;
+}
+
+.MiniCard_Label {
+ width: 64px;
+ max-height: 64px;
+ text-align: center;
+ font-size: 12px;
+ color: var(--text-primary-color);
+}
+
+.MiniCard_Info {
+ position: absolute;
+ display: flex;
+}
diff --git a/src/handbook/src/css/widgets/SideBarButton.scss b/src/handbook/src/css/widgets/SideBarButton.scss
new file mode 100644
index 000000000..805ad7513
--- /dev/null
+++ b/src/handbook/src/css/widgets/SideBarButton.scss
@@ -0,0 +1,26 @@
+.SideBarButton {
+ display: flex;
+ flex-direction: row;
+
+ gap: 15px;
+
+ width: 100%;
+ height: 64px;
+ max-width: 300px;
+ max-height: 64px;
+
+ align-items: center;
+}
+
+.SideBarButton_Icon {
+ max-width: 64px;
+ max-height: 64px;
+}
+
+.SideBarButton_Label {
+ font-size: 22px;
+ line-height: 29px;
+ font-style: normal;
+
+ max-width: 220px;
+}
diff --git a/src/handbook/src/icons/discord.svg b/src/handbook/src/icons/discord.svg
new file mode 100644
index 000000000..22ee27ba2
--- /dev/null
+++ b/src/handbook/src/icons/discord.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/handbook/src/main.tsx b/src/handbook/src/main.tsx
new file mode 100644
index 000000000..f80fd024a
--- /dev/null
+++ b/src/handbook/src/main.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import { createRoot } from "react-dom/client";
+
+import * as data from "@backend/data";
+import * as events from "@backend/events";
+
+import App from "@ui/App";
+
+// Call initial setup functions.
+data.setup();
+events.setup();
+
+// Render the application.
+createRoot(document.getElementById("root") as HTMLElement).render(
+
+
+
+);
diff --git a/src/handbook/src/ui/App.tsx b/src/handbook/src/ui/App.tsx
new file mode 100644
index 000000000..eba12dc40
--- /dev/null
+++ b/src/handbook/src/ui/App.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+
+import SideBar from "@views/SideBar";
+import Content from "@views/Content";
+
+import type { Page } from "@backend/types";
+import { isPage } from "@backend/types";
+
+import "@css/App.scss";
+import "@css/Text.scss";
+
+// Based on the design at: https://www.figma.com/file/PDeAVDkTDF5vvUGGdaIZ39/GM-Handbook.
+// Currently designed by: Magix.
+
+interface IState {
+ initial: Page | null;
+}
+
+class App extends React.Component<{}, IState> {
+ constructor(props: any) {
+ super(props);
+
+ // Check if the window's href is a page.
+ let targetPage = null;
+ const page = window.location.href.split("/").pop();
+ console.log(page);
+
+ if (page != undefined && page != "") {
+ // Convert the page to a Page type.
+ const pageName = page.charAt(0).toUpperCase() + page.slice(1);
+ // Check if the page is a valid page.
+ if (isPage(pageName)) targetPage = pageName as Page;
+ }
+
+ this.state = {
+ initial: targetPage as Page | null
+ };
+ }
+
+ render() {
+ return (
+
+
+
+
+ );
+ }
+}
+
+export default App;
diff --git a/src/handbook/src/ui/components/VirtualizedGrid.tsx b/src/handbook/src/ui/components/VirtualizedGrid.tsx
new file mode 100644
index 000000000..631c53fc9
--- /dev/null
+++ b/src/handbook/src/ui/components/VirtualizedGrid.tsx
@@ -0,0 +1,88 @@
+import React from "react";
+
+import { List as _List, ListProps, ListRowProps } from "react-virtualized/dist/es/List";
+import { AutoSizer as _AutoSizer, AutoSizerProps } from "react-virtualized/dist/es/AutoSizer";
+
+const List = _List as unknown as React.FC;
+const AutoSizer = _AutoSizer as unknown as React.FC;
+
+import "@css/components/VirtualizedGrid.scss";
+
+interface IProps {
+ list: T[];
+ render: (item: T) => React.ReactNode;
+
+ itemHeight: number;
+ itemsPerRow?: number;
+
+ gap?: number;
+ itemGap?: number;
+}
+
+interface IState {
+ scrollTop: number;
+}
+
+class VirtualizedGrid extends React.Component, IState> {
+ constructor(props: IProps) {
+ super(props);
+
+ this.state = {
+ scrollTop: 0
+ };
+ }
+
+ /**
+ * Renders a row of items.
+ */
+ private rowRender(props: ListRowProps): React.ReactNode {
+ const items: React.ReactNode[] = [];
+
+ // Calculate the items to render.
+ const perRow = this.props.itemsPerRow ?? 10;
+ for (let i = 0; i < perRow; i++) {
+ const itemIndex = props.index * perRow + i;
+ if (itemIndex < this.props.list.length) {
+ items.push(this.props.render(this.props.list[itemIndex]));
+ }
+ }
+
+ return (
+
+ {items.map((item, index) => (
+
{item}
+ ))}
+
+
+ );
+ }
+
+ render() {
+ const { list, itemHeight, itemsPerRow } = this.props;
+
+ return (
+
+ {({ height, width }) => (
+ this.setState({ scrollTop: e.scrollTop })}
+ />
+ )}
+
+ );
+ }
+}
+
+export default VirtualizedGrid;
diff --git a/src/handbook/src/ui/pages/AvatarsPage.tsx b/src/handbook/src/ui/pages/AvatarsPage.tsx
new file mode 100644
index 000000000..fe97e3a6d
--- /dev/null
+++ b/src/handbook/src/ui/pages/AvatarsPage.tsx
@@ -0,0 +1,39 @@
+import React from "react";
+
+import Character from "@app/ui/widgets/Character";
+
+import type { Avatar } from "@backend/types";
+import { listAvatars } from "@backend/data";
+import { grantAvatar } from "@backend/server";
+
+import "@css/pages/AvatarsPage.scss";
+
+class AvatarsPage extends React.PureComponent {
+ /**
+ * Grants the avatar to the user.
+ *
+ * @param avatar The avatar to grant.
+ * @private
+ */
+ private async grantAvatar(avatar: Avatar): Promise {
+ console.log(await grantAvatar(avatar.id));
+ }
+
+ render() {
+ return (
+
+
Characters
+
+
+ {listAvatars().map((avatar) =>
+ avatar.id > 11000000 ? undefined : (
+
+ )
+ )}
+
+
+ );
+ }
+}
+
+export default AvatarsPage;
diff --git a/src/handbook/src/ui/pages/CommandsPage.tsx b/src/handbook/src/ui/pages/CommandsPage.tsx
new file mode 100644
index 000000000..6c5416ace
--- /dev/null
+++ b/src/handbook/src/ui/pages/CommandsPage.tsx
@@ -0,0 +1,33 @@
+import React from "react";
+
+import Card from "@widgets/Card";
+
+import { listCommands } from "@backend/data";
+
+import "@css/pages/CommandsPage.scss";
+
+class CommandsPage extends React.PureComponent {
+ render() {
+ return (
+
+
Commands
+
+
+ {listCommands().map((command) => (
+
+ ))}
+
+
+ );
+ }
+}
+
+export default CommandsPage;
diff --git a/src/handbook/src/ui/pages/EntitiesPage.tsx b/src/handbook/src/ui/pages/EntitiesPage.tsx
new file mode 100644
index 000000000..f0dbe96da
--- /dev/null
+++ b/src/handbook/src/ui/pages/EntitiesPage.tsx
@@ -0,0 +1,153 @@
+import React, { ChangeEvent } from "react";
+
+import MiniCard from "@widgets/MiniCard";
+import VirtualizedGrid from "@components/VirtualizedGrid";
+
+import { Entity, ItemCategory } from "@backend/types";
+import type { Entity as EntityType, EntityInfo } from "@backend/types";
+import { getEntities } from "@backend/data";
+import { entityIcon, fetchEntityData } from "@app/utils";
+
+import "@css/pages/EntitiesPage.scss";
+import EntityCard from "@widgets/EntityCard";
+
+interface IState {
+ filters: ItemCategory[];
+ search: string;
+
+ selected: EntityType | null;
+ selectedInfo: EntityInfo | null;
+}
+
+class EntitiesPage extends React.Component<{}, IState> {
+ constructor(props: {}) {
+ super(props);
+
+ this.state = {
+ filters: [],
+ search: "",
+
+ selected: null,
+ selectedInfo: null
+ };
+ }
+
+ /**
+ * Should the entity be shown?
+ *
+ * @param entity The entity.
+ * @private
+ */
+ private showEntity(entity: Entity): boolean {
+ // Check if the entity's name starts with N/A.
+ if (entity.name.includes("[N/A]")) return false;
+
+ return entity.id > 0;
+ }
+
+ /**
+ * Gets the items to render.
+ * @private
+ */
+ private getEntities(): EntityType[] {
+ let entities: EntityType[] = [];
+
+ // Add items based on filters.
+ const filters = this.state.filters;
+ if (filters.length == 0) {
+ entities = getEntities();
+ } else {
+ for (const filter of filters) {
+ // Remove duplicate items.
+ entities = entities.filter((item, index) => {
+ return entities.indexOf(item) == index;
+ });
+ }
+ }
+
+ // Filter out items that don't match the search.
+ const search = this.state.search.toLowerCase();
+ if (search != "") {
+ entities = entities.filter((item) => {
+ return item.name.toLowerCase().includes(search);
+ });
+ }
+
+ return entities;
+ }
+
+ /**
+ * Invoked when the search input changes.
+ *
+ * @param event The event.
+ * @private
+ */
+ private onChange(event: ChangeEvent): void {
+ this.setState({ search: event.target.value });
+ }
+
+ /**
+ * Sets the selected entity.
+ *
+ * @param entity The entity.
+ * @private
+ */
+ private async setSelectedItem(entity: EntityType): Promise {
+ let data: EntityInfo | null = null;
+ try {
+ data = await fetchEntityData(entity);
+ } catch {}
+
+ this.setState({
+ selected: entity,
+ selectedInfo: data
+ });
+ }
+
+ render() {
+ const entities = this.getEntities();
+
+ return (
+
+
+
+
+ {entities.length > 0 ? (
+
this.showEntity(entity))}
+ itemHeight={64}
+ itemsPerRow={18}
+ gap={5}
+ itemGap={5}
+ render={(entity) => (
+ this.setSelectedItem(entity)}
+ />
+ )}
+ />
+ ) : undefined}
+
+
+
+
+
+
+ );
+ }
+}
+
+export default EntitiesPage;
diff --git a/src/handbook/src/ui/pages/HomePage.tsx b/src/handbook/src/ui/pages/HomePage.tsx
new file mode 100644
index 000000000..d25bfe4d6
--- /dev/null
+++ b/src/handbook/src/ui/pages/HomePage.tsx
@@ -0,0 +1,66 @@
+import React from "react";
+
+import HomeButton from "@widgets/HomeButton";
+
+import { ReactComponent as DiscordLogo } from "@icons/discord.svg";
+
+import "@css/pages/HomePage.scss";
+
+class HomePage extends React.Component {
+ constructor(props: any) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
+
Welcome back, Traveler~
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This tool is not affiliated with HoYoverse.
+
Genshin Impact, game content and materials are
+
trademarks and copyrights of HoYoverse.
+
+
+
+
+
Join the Community!
+
+
+
+
+
+
Credits
+
(hover to see info)
+
+
+
+
+
+
+ );
+ }
+}
+
+export default HomePage;
diff --git a/src/handbook/src/ui/pages/ItemsPage.tsx b/src/handbook/src/ui/pages/ItemsPage.tsx
new file mode 100644
index 000000000..1f4096737
--- /dev/null
+++ b/src/handbook/src/ui/pages/ItemsPage.tsx
@@ -0,0 +1,157 @@
+import React, { ChangeEvent } from "react";
+
+import MiniCard from "@widgets/MiniCard";
+import ItemCard from "@widgets/ItemCard";
+import VirtualizedGrid from "@components/VirtualizedGrid";
+
+import { ItemCategory } from "@backend/types";
+import type { Item as ItemType, ItemInfo } from "@backend/types";
+import { getItems, sortedItems } from "@backend/data";
+import { fetchItemData, itemIcon } from "@app/utils";
+
+import "@css/pages/ItemsPage.scss";
+
+interface IState {
+ filters: ItemCategory[];
+ search: string;
+
+ selected: ItemType | null;
+ selectedInfo: ItemInfo | null;
+}
+
+class ItemsPage extends React.Component<{}, IState> {
+ constructor(props: {}) {
+ super(props);
+
+ this.state = {
+ filters: [],
+ search: "",
+
+ selected: null,
+ selectedInfo: null
+ };
+ }
+
+ /**
+ * Gets the items to render.
+ * @private
+ */
+ private getItems(): ItemType[] {
+ let items: ItemType[] = [];
+
+ // Add items based on filters.
+ const filters = this.state.filters;
+ if (filters.length == 0) {
+ items = getItems();
+ } else {
+ for (const filter of filters) {
+ // Add items from the category.
+ items = items.concat(sortedItems[filter]);
+ // Remove duplicate items.
+ items = items.filter((item, index) => {
+ return items.indexOf(item) == index;
+ });
+ }
+ }
+
+ // Filter out items that don't match the search.
+ const search = this.state.search.toLowerCase();
+ if (search != "") {
+ items = items.filter((item) => {
+ return item.name.toLowerCase().includes(search);
+ });
+ }
+
+ return items;
+ }
+
+ /**
+ * Invoked when the search input changes.
+ *
+ * @param event The event.
+ * @private
+ */
+ private onChange(event: ChangeEvent): void {
+ this.setState({ search: event.target.value });
+ }
+
+ /**
+ * Should the item be showed?
+ *
+ * @param item The item.
+ * @private
+ */
+ private showItem(item: ItemType): boolean {
+ // Check if the item has an icon.
+ if (item.icon.length == 0) return false;
+ // Check if the item is a TCG card.
+ if (item.icon.includes("Gcg")) return false;
+
+ return item.id > 0;
+ }
+
+ /**
+ * Sets the selected item.
+ *
+ * @param item The item.
+ * @private
+ */
+ private async setSelectedItem(item: ItemType): Promise {
+ let data: ItemInfo | null = null;
+ try {
+ data = await fetchItemData(item);
+ } catch {}
+
+ this.setState({
+ selected: item,
+ selectedInfo: data
+ });
+ }
+
+ render() {
+ const items = this.getItems();
+
+ return (
+
+
+
+
+ {items.length > 0 ? (
+
this.showItem(item))}
+ itemHeight={64}
+ itemsPerRow={18}
+ gap={5}
+ itemGap={5}
+ render={(item) => (
+ this.setSelectedItem(item)}
+ />
+ )}
+ />
+ ) : undefined}
+
+
+
+
+
+
+ );
+ }
+}
+
+export default ItemsPage;
diff --git a/src/handbook/src/ui/pages/ScenesPage.tsx b/src/handbook/src/ui/pages/ScenesPage.tsx
new file mode 100644
index 000000000..4d8686128
--- /dev/null
+++ b/src/handbook/src/ui/pages/ScenesPage.tsx
@@ -0,0 +1,71 @@
+import React from "react";
+
+import Card from "@widgets/Card";
+
+import { SceneType } from "@backend/types";
+import { getScenes } from "@backend/data";
+
+import "@css/pages/ScenesPage.scss";
+
+/**
+ * Converts a scene type to a string.
+ *
+ * @param type The scene type.
+ */
+function sceneTypeToString(type: SceneType): string {
+ switch (type) {
+ default:
+ return "Unknown";
+ case SceneType.None:
+ return "None";
+ case SceneType.World:
+ return "World";
+ case SceneType.Activity:
+ return "Activity";
+ case SceneType.Dungeon:
+ return "Dungeon";
+ case SceneType.Room:
+ return "Room";
+ case SceneType.HomeRoom:
+ return "Home Room";
+ case SceneType.HomeWorld:
+ return "Home World";
+ }
+}
+
+class ScenesPage extends React.PureComponent {
+ /**
+ * Teleports the player to the specified scene.
+ * @private
+ */
+ private async teleport(): Promise {
+ // TODO: Implement teleporting.
+ }
+
+ render() {
+ return (
+
+
Scenes
+
+
+ {getScenes().map((command) => (
+
+ Teleport
+
+ }
+ rightOffset={13}
+ height={75}
+ />
+ ))}
+
+
+ );
+ }
+}
+
+export default ScenesPage;
diff --git a/src/handbook/src/ui/views/Content.tsx b/src/handbook/src/ui/views/Content.tsx
new file mode 100644
index 000000000..eaa4ececa
--- /dev/null
+++ b/src/handbook/src/ui/views/Content.tsx
@@ -0,0 +1,70 @@
+import React from "react";
+
+import HomePage from "@pages/HomePage";
+import CommandsPage from "@pages/CommandsPage";
+import AvatarsPage from "@pages/AvatarsPage";
+import ItemsPage from "@pages/ItemsPage";
+import EntitiesPage from "@pages/EntitiesPage";
+import ScenesPage from "@pages/ScenesPage";
+
+import type { Page } from "@backend/types";
+import { addNavListener, removeNavListener } from "@backend/events";
+
+import "@css/views/Content.scss";
+
+interface IProps {
+ initial?: Page | null;
+}
+
+interface IState {
+ current: Page;
+}
+
+class Content extends React.Component {
+ constructor(props: IProps) {
+ super(props);
+
+ this.state = {
+ current: props.initial ?? "Home"
+ };
+ }
+
+ /**
+ * Navigates to the specified page.
+ *
+ * @param page The page to navigate to.
+ * @private
+ */
+ private navigate(page: Page): void {
+ this.setState({ current: page });
+ }
+
+ componentDidMount() {
+ addNavListener(this.navigate.bind(this));
+ }
+
+ componentWillUnmount() {
+ removeNavListener(this.navigate.bind(this));
+ }
+
+ render() {
+ switch (this.state.current) {
+ default:
+ return undefined;
+ case "Home":
+ return ;
+ case "Commands":
+ return ;
+ case "Avatars":
+ return ;
+ case "Items":
+ return ;
+ case "Entities":
+ return ;
+ case "Scenes":
+ return ;
+ }
+ }
+}
+
+export default Content;
diff --git a/src/handbook/src/ui/views/SideBar.tsx b/src/handbook/src/ui/views/SideBar.tsx
new file mode 100644
index 000000000..7181acd92
--- /dev/null
+++ b/src/handbook/src/ui/views/SideBar.tsx
@@ -0,0 +1,78 @@
+import React, { ChangeEvent } from "react";
+
+import SideBarButton from "@app/ui/widgets/SideBarButton";
+
+import { navigate } from "@app/backend/events";
+
+import "@css/views/SideBar.scss";
+import { setTargetPlayer } from "@backend/server";
+
+interface IState {
+ uid: string | null;
+}
+
+class SideBar extends React.Component<{}, IState> {
+ constructor(props: {}) {
+ super(props);
+
+ this.state = {
+ uid: null
+ };
+ }
+
+ /**
+ * Invoked when the UID input changes.
+ *
+ * @param event The event.
+ * @private
+ */
+ private onChange(event: ChangeEvent): void {
+ const input = event.target.value;
+ const uid = input == "" ? null : input;
+ if (uid && uid.length > 10) return;
+
+ this.setState({ uid });
+ setTargetPlayer(parseInt(uid ?? "0"));
+ }
+
+ render() {
+ return (
+
+
navigate("Home")}>
+ The Ultimate Anime Game Handbook
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default SideBar;
diff --git a/src/handbook/src/ui/widgets/Card.tsx b/src/handbook/src/ui/widgets/Card.tsx
new file mode 100644
index 000000000..c8308bbcd
--- /dev/null
+++ b/src/handbook/src/ui/widgets/Card.tsx
@@ -0,0 +1,64 @@
+import React from "react";
+
+import "@css/widgets/Card.scss";
+
+interface IProps {
+ title: string;
+ alternate?: string;
+ description?: string | string[];
+
+ height?: number | string;
+ button?: React.ReactNode;
+ rightOffset?: number;
+
+ onClick?: () => void;
+ onOver?: () => void;
+ onOut?: () => void;
+}
+
+class Card extends React.PureComponent {
+ constructor(props: IProps) {
+ super(props);
+ }
+
+ render() {
+ return (
+
+
+
+
{this.props.title}
+ {this.props.alternate &&
{this.props.alternate}
}
+
+
+
+ {this.props.description ? (
+ Array.isArray(this.props.description) ? (
+ this.props.description.map((line, index) => (
+
+ {line}
+
+ ))
+ ) : (
+
{this.props.description}
+ )
+ ) : undefined}
+
+
+
+ {this.props.button ? (
+
+ {this.props.button}
+
+ ) : undefined}
+
+ );
+ }
+}
+
+export default Card;
diff --git a/src/handbook/src/ui/widgets/Character.tsx b/src/handbook/src/ui/widgets/Character.tsx
new file mode 100644
index 000000000..c2b2a8fa1
--- /dev/null
+++ b/src/handbook/src/ui/widgets/Character.tsx
@@ -0,0 +1,57 @@
+import React from "react";
+
+import type { Avatar } from "@backend/types";
+import { colorFor, formatAvatarName } from "@app/utils";
+
+import "@css/widgets/Character.scss";
+
+// Image base URL: https://paimon.moe/images/characters/(name).png
+
+const ignored = [
+ 10000001 // Kate
+];
+
+const nameSwitch: { [key: number]: string } = {
+ 10000005: "Lumine",
+ 10000007: "Aether"
+};
+
+interface IProps {
+ data: Avatar;
+
+ onClick?: () => void;
+}
+
+class Character extends React.PureComponent {
+ constructor(props: IProps) {
+ super(props);
+ }
+
+ render() {
+ const { name, quality, id } = this.props.data;
+ const qualityColor = colorFor(quality);
+
+ // Check if the avatar is blacklisted.
+ if (ignored.includes(id)) return undefined;
+
+ return (
+
+
+
+
+
{nameSwitch[id] ?? name}
+
+
+ );
+ }
+}
+
+export default Character;
diff --git a/src/handbook/src/ui/widgets/EntityCard.tsx b/src/handbook/src/ui/widgets/EntityCard.tsx
new file mode 100644
index 000000000..0e3829a45
--- /dev/null
+++ b/src/handbook/src/ui/widgets/EntityCard.tsx
@@ -0,0 +1,162 @@
+import React from "react";
+
+import type { Entity as EntityType, EntityInfo } from "@backend/types";
+import { entityIcon } from "@app/utils";
+
+import "@css/widgets/ItemCard.scss";
+
+/**
+ * Converts a description string into a list of paragraphs.
+ *
+ * @param description The description to convert.
+ */
+function toDescription(description: string | undefined): JSX.Element[] {
+ if (!description) return [];
+
+ return description.split("\\n").map((line, index) => {
+ return {line}
;
+ });
+}
+
+interface IProps {
+ entity: EntityType | null;
+ info: EntityInfo | null;
+}
+
+interface IState {
+ icon: boolean;
+ count: number | string;
+}
+
+const defaultState = {
+ icon: true,
+ count: 1
+};
+
+class EntityCard extends React.Component {
+ constructor(props: IProps) {
+ super(props);
+
+ this.state = defaultState;
+ }
+
+ /**
+ * Updates the count of the item.
+ *
+ * @param event The change event.
+ * @private
+ */
+ private updateCount(event: React.ChangeEvent) {
+ const value = event.target.value;
+ if (isNaN(parseInt(value)) && value.length > 1) return;
+
+ this.setState({ count: value });
+ }
+
+ /**
+ * Adds to the count of the entity.
+ *
+ * @param positive Is the count being added or subtracted?
+ * @param multiple Is the count being multiplied by 10?
+ * @private
+ */
+ private addCount(positive: boolean, multiple: boolean) {
+ let { count } = this.state;
+ if (count === "") count = 1;
+ if (typeof count == "string") count = parseInt(count);
+ if (count < 1) count = 1;
+
+ let increment = 1;
+ if (!positive) increment = -1;
+ if (multiple) increment *= 10;
+
+ count = Math.max(1, count + increment);
+
+ this.setState({ count });
+ }
+
+ /**
+ * Summons the entity at the connected player's position.
+ * @private
+ */
+ private async summonAtPlayer(): Promise {
+ // TODO: Implement server access.
+ }
+
+ componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any) {
+ if (this.props.entity != prevProps.entity) {
+ this.setState(defaultState);
+ }
+ }
+
+ render() {
+ const { entity, info } = this.props;
+ const data = info?.data;
+
+ return entity ? (
+
+
+
+
+
{data?.name ?? entity.name}
+
{data?.type ?? ""}
+
+
+ {this.state.icon && (
+
this.setState({ icon: false })}
+ />
+ )}
+
+
+
{toDescription(data?.description)}
+
+
+
+
+
this.addCount(false, false)}
+ onContextMenu={(e) => {
+ e.preventDefault();
+ this.addCount(false, true);
+ }}
+ className={"ItemCard_Operation"}
+ >
+ -
+
+
{
+ if (this.state.count == "") {
+ this.setState({ count: 1 });
+ }
+ }}
+ />
+
this.addCount(true, false)}
+ onContextMenu={(e) => {
+ e.preventDefault();
+ this.addCount(true, true);
+ }}
+ className={"ItemCard_Operation"}
+ >
+ +
+
+
+
+
+
+
+ ) : undefined;
+ }
+}
+
+export default EntityCard;
diff --git a/src/handbook/src/ui/widgets/HomeButton.tsx b/src/handbook/src/ui/widgets/HomeButton.tsx
new file mode 100644
index 000000000..dabc31563
--- /dev/null
+++ b/src/handbook/src/ui/widgets/HomeButton.tsx
@@ -0,0 +1,37 @@
+import React from "react";
+
+import type { Page } from "@backend/types";
+import { navigate } from "@backend/events";
+
+import "@css/widgets/HomeButton.scss";
+
+interface IProps {
+ name: string;
+ anchor: Page;
+}
+
+class HomeButton extends React.PureComponent {
+ constructor(props: IProps) {
+ super(props);
+ }
+
+ /**
+ * Redirects the user to the specified anchor.
+ * @private
+ */
+ private redirect(): void {
+ navigate(this.props.anchor);
+ }
+
+ render() {
+ return (
+ this.redirect()}>
+
+
+
{this.props.name}
+
+ );
+ }
+}
+
+export default HomeButton;
diff --git a/src/handbook/src/ui/widgets/ItemCard.tsx b/src/handbook/src/ui/widgets/ItemCard.tsx
new file mode 100644
index 000000000..af42dd7fe
--- /dev/null
+++ b/src/handbook/src/ui/widgets/ItemCard.tsx
@@ -0,0 +1,167 @@
+import React from "react";
+
+import type { Item as ItemType, ItemInfo } from "@backend/types";
+import { itemTypeToString } from "@backend/types";
+import { itemIcon } from "@app/utils";
+import { giveItem } from "@backend/server";
+
+import "@css/widgets/ItemCard.scss";
+
+/**
+ * Converts a description string into a list of paragraphs.
+ *
+ * @param description The description to convert.
+ */
+function toDescription(description: string | undefined): JSX.Element[] {
+ if (!description) return [];
+
+ return description.split("\\n").map((line, index) => {
+ return {line}
;
+ });
+}
+
+interface IProps {
+ item: ItemType | null;
+ info: ItemInfo | null;
+}
+
+interface IState {
+ icon: boolean;
+ count: number | string;
+}
+
+const defaultState = {
+ icon: true,
+ count: 1
+};
+
+class ItemCard extends React.Component {
+ constructor(props: IProps) {
+ super(props);
+
+ this.state = defaultState;
+ }
+
+ /**
+ * Updates the count of the item.
+ *
+ * @param event The change event.
+ * @private
+ */
+ private updateCount(event: React.ChangeEvent) {
+ const value = event.target.value;
+ if (isNaN(parseInt(value)) && value.length > 1) return;
+
+ this.setState({ count: value });
+ }
+
+ /**
+ * Adds to the count of the item.
+ *
+ * @param positive Is the count being added or subtracted?
+ * @param multiple Is the count being multiplied by 10?
+ * @private
+ */
+ private addCount(positive: boolean, multiple: boolean) {
+ let { count } = this.state;
+ if (count === "") count = 1;
+ if (typeof count == "string") count = parseInt(count);
+ if (count < 1) count = 1;
+
+ let increment = 1;
+ if (!positive) increment = -1;
+ if (multiple) increment *= 10;
+
+ count = Math.max(1, count + increment);
+
+ this.setState({ count });
+ }
+
+ /**
+ * Adds the item to the player's connected inventory.
+ * @private
+ */
+ private async addToInventory(): Promise {
+ await giveItem(
+ this.props.item?.id ?? 102,
+ typeof this.state.count == "string" ? parseInt(this.state.count) : this.state.count
+ );
+ }
+
+ componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any) {
+ if (this.props.item != prevProps.item) {
+ this.setState(defaultState);
+ }
+ }
+
+ render() {
+ const { item, info } = this.props;
+ const data = info?.data;
+
+ return item ? (
+
+
+
+
+
{data?.name ?? item.name}
+
{data?.type ?? itemTypeToString(item.type)}
+
+
+ {this.state.icon && (
+
this.setState({ icon: false })}
+ />
+ )}
+
+
+
{toDescription(data?.description)}
+
+
+
+
+
this.addCount(false, false)}
+ onContextMenu={(e) => {
+ e.preventDefault();
+ this.addCount(false, true);
+ }}
+ className={"ItemCard_Operation"}
+ >
+ -
+
+
{
+ if (this.state.count == "") {
+ this.setState({ count: 1 });
+ }
+ }}
+ />
+
this.addCount(true, false)}
+ onContextMenu={(e) => {
+ e.preventDefault();
+ this.addCount(true, true);
+ }}
+ className={"ItemCard_Operation"}
+ >
+ +
+
+
+
+
+
+
+ ) : undefined;
+ }
+}
+
+export default ItemCard;
diff --git a/src/handbook/src/ui/widgets/MiniCard.tsx b/src/handbook/src/ui/widgets/MiniCard.tsx
new file mode 100644
index 000000000..a286e1530
--- /dev/null
+++ b/src/handbook/src/ui/widgets/MiniCard.tsx
@@ -0,0 +1,77 @@
+import React from "react";
+
+import { itemIcon } from "@app/utils";
+
+import "@css/widgets/MiniCard.scss";
+
+interface IProps {
+ data: { name: string };
+ icon: string;
+
+ onClick?: () => void;
+}
+
+interface IState {
+ popout: boolean;
+ icon: boolean;
+ loaded: boolean;
+}
+
+class MiniCard extends React.Component {
+ loading: number | any;
+
+ constructor(props: IProps) {
+ super(props);
+
+ this.state = {
+ popout: false,
+ icon: true,
+ loaded: false
+ };
+ }
+
+ /**
+ * Replaces the icon with the item's name.
+ * @private
+ */
+ private replaceIcon(): void {
+ this.setState({ icon: false, loaded: false });
+ }
+
+ private forceReplace(): void {
+ if (!this.state.loaded) this.replaceIcon();
+ }
+
+ componentDidMount() {
+ this.loading = setTimeout(this.forceReplace.bind(this), 1e3);
+ }
+
+ componentWillUnmount() {
+ clearTimeout(this.loading);
+ this.loading = null;
+ }
+
+ render() {
+ return (
+
+
+ {this.state.icon && (
+
this.setState({ loaded: true })}
+ />
+ )}
+
+ {(!this.state.loaded || !this.state.icon) && (
+
{this.props.data.name}
+ )}
+
+
+ );
+ }
+}
+
+export default MiniCard;
diff --git a/src/handbook/src/ui/widgets/SideBarButton.tsx b/src/handbook/src/ui/widgets/SideBarButton.tsx
new file mode 100644
index 000000000..5a16af4c5
--- /dev/null
+++ b/src/handbook/src/ui/widgets/SideBarButton.tsx
@@ -0,0 +1,37 @@
+import React from "react";
+
+import type { Page } from "@backend/types";
+import { navigate } from "@backend/events";
+
+import "@css/widgets/SideBarButton.scss";
+
+interface IProps {
+ name: string;
+ anchor: Page;
+}
+
+class SideBarButton extends React.PureComponent {
+ constructor(props: IProps) {
+ super(props);
+ }
+
+ /**
+ * Redirects the user to the specified anchor.
+ * @private
+ */
+ private redirect(): void {
+ navigate(this.props.anchor);
+ }
+
+ render() {
+ return (
+ this.redirect()}>
+
+
+
{this.props.name}
+
+ );
+ }
+}
+
+export default SideBarButton;
diff --git a/src/handbook/src/utils.ts b/src/handbook/src/utils.ts
new file mode 100644
index 000000000..5ec928217
--- /dev/null
+++ b/src/handbook/src/utils.ts
@@ -0,0 +1,136 @@
+import type { Entity, Item, EntityInfo, ItemInfo } from "@backend/types";
+import { ItemType, Quality } from "@backend/types";
+
+/**
+ * Fetches the name of the CSS variable for the quality.
+ *
+ * @param quality The quality of the item.
+ */
+export function colorFor(quality: Quality): string {
+ switch (quality) {
+ default:
+ return "--legendary-color";
+ case "EPIC":
+ return "--epic-color";
+ case "RARE":
+ return "--rare-color";
+ case "UNCOMMON":
+ return "--uncommon-color";
+ case "COMMON":
+ return "--common-color";
+ case "UNKNOWN":
+ return "--unknown-color";
+ }
+}
+
+/**
+ * Checks if a value is between two numbers.
+ *
+ * @param value The value to check.
+ * @param min The minimum value.
+ * @param max The maximum value.
+ */
+export function inRange(value: number, min: number, max: number): boolean {
+ return value >= min && value <= max;
+}
+
+/**
+ * Gets the path to the icon for an item.
+ * Uses the Project Amber API to get the icon.
+ *
+ * @param item The item to get the icon for.
+ */
+export function itemIcon(item: Item): string {
+ // Check if the item matches a special case.
+ if (inRange(item.id, 1001, 1099)) {
+ return `https://paimon.moe/images/characters/${formatAvatarName(item.name, item.id)}.png`;
+ }
+
+ switch (item.type) {
+ default:
+ return `https://api.ambr.top/assets/UI/UI_${item.icon}.png`;
+ case ItemType.Furniture:
+ return `https://api.ambr.top/assets/UI/furniture/UI_${item.icon}.png`;
+ case ItemType.Reliquary:
+ return `https://api.ambr.top/assets/UI/reliquary/UI_${item.icon}.png`;
+ }
+}
+
+/**
+ * Gets the path to the icon for an entity.
+ * Uses the Project Amber API to get the icon.
+ *
+ * @param entity The entity to get the icon for. Project Amber data required.
+ */
+export function entityIcon(entity: Entity): string {
+ return `https://api.ambr.top/assets/UI/monster/UI_MonsterIcon_${entity.internal}.png`;
+}
+
+/**
+ * Formats a character's name to fit with the reference name.
+ * Example: Hu Tao -> hu_tao
+ *
+ * @param name The character's name.
+ * @param id The character's ID.
+ */
+export function formatAvatarName(name: string, id: number): string {
+ // Check if a different name is used for the character.
+ if (refSwitch[id]) name = refSwitch[id];
+ return name.toLowerCase().replace(" ", "_");
+}
+
+const refSwitch: { [key: number]: string } = {
+ 10000005: "traveler_anemo",
+ 10000007: "traveler_geo"
+};
+
+/**
+ * Gets the route for an item type.
+ *
+ * @param type The type of the item.
+ */
+export function typeToRoute(type: ItemType): string {
+ switch (type) {
+ default:
+ return "material";
+ case ItemType.Furniture:
+ return "furniture";
+ case ItemType.Reliquary:
+ return "reliquary";
+ case ItemType.Weapon:
+ return "weapon";
+ }
+}
+
+/**
+ * Fetches the data for an item.
+ * Uses the Project Amber API to get the data.
+ *
+ * @route GET https://api.ambr.top/v2/EN/{type}/{id}
+ * @param item The item to fetch the data for.
+ */
+export async function fetchItemData(item: Item): Promise {
+ let url = `https://api.ambr.top/v2/EN/(type)/(id)`;
+
+ // Replace the type and ID in the URL.
+ url = url.replace("(type)", typeToRoute(item.type));
+ url = url.replace("(id)", item.id.toString());
+
+ // Fetch the data.
+ return fetch(url)
+ .then((res) => res.json())
+ .catch(() => {});
+}
+
+/**
+ * Fetches the data for an entity.
+ * Uses the Project Amber API to get the data.
+ *
+ * @route GET https://api.ambr.top/v2/en/monster/{id}
+ * @param entity The entity to fetch the data for.
+ */
+export async function fetchEntityData(entity: Entity): Promise {
+ return fetch(`https://api.ambr.top/v2/en/monster/${entity.id}`)
+ .then((res) => res.json())
+ .catch(() => {});
+}
diff --git a/src/handbook/tsconfig.json b/src/handbook/tsconfig.json
new file mode 100644
index 000000000..f08be7441
--- /dev/null
+++ b/src/handbook/tsconfig.json
@@ -0,0 +1,35 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ "baseUrl": ".",
+ "paths": {
+ "@app/*": ["src/*"],
+ "@backend/*": ["src/backend/*"],
+ "@css/*": ["src/css/*"],
+ "@ui/*": ["src/ui/*"],
+ "@icons/*": ["src/icons/*"],
+ "@views/*": ["src/ui/views/*"],
+ "@pages/*": ["src/ui/pages/*"],
+ "@widgets/*": ["src/ui/widgets/*"],
+ "@components/*": ["src/ui/components/*"],
+ "@data/*": ["data/*"]
+ }
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
diff --git a/src/handbook/tsconfig.node.json b/src/handbook/tsconfig.node.json
new file mode 100644
index 000000000..b8afcc8fa
--- /dev/null
+++ b/src/handbook/tsconfig.node.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": [
+ "vite.config.ts"
+ ]
+}
diff --git a/src/handbook/vite.config.ts b/src/handbook/vite.config.ts
new file mode 100644
index 000000000..48079b4bd
--- /dev/null
+++ b/src/handbook/vite.config.ts
@@ -0,0 +1,25 @@
+// noinspection JSUnusedGlobalSymbols
+
+import { defineConfig } from "vite";
+
+import react from "@vitejs/plugin-react-swc";
+import tsconfigPaths from "vite-tsconfig-paths";
+
+import dsv from "@rollup/plugin-dsv";
+import viteSvgr from "vite-plugin-svgr";
+import { viteSingleFile } from "vite-plugin-singlefile";
+
+import postcss from "./cfg/postcss.config.js";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [ react(), tsconfigPaths(), dsv(),
+ viteSvgr(), viteSingleFile() ],
+ css: { postcss },
+
+ optimizeDeps: {
+ exclude: [
+ "react-virtualization"
+ ]
+ }
+});
diff --git a/src/main/java/emu/grasscutter/GameConstants.java b/src/main/java/emu/grasscutter/GameConstants.java
index edef16b7d..1768e94bf 100644
--- a/src/main/java/emu/grasscutter/GameConstants.java
+++ b/src/main/java/emu/grasscutter/GameConstants.java
@@ -1,10 +1,14 @@
package emu.grasscutter;
import emu.grasscutter.utils.Position;
+import emu.grasscutter.utils.SparseSet;
import emu.grasscutter.utils.Utils;
import java.util.Arrays;
public final class GameConstants {
+ public static String VERSION = "3.6.0";
+ public static final boolean DEBUG = true;
+
public static final int DEFAULT_TEAMS = 4;
public static final int MAX_TEAMS = 10;
public static final int MAIN_CHARACTER_MALE = 10000005;
@@ -28,9 +32,19 @@ public final class GameConstants {
"Avatar_Component_Initializer",
"Avatar_FallAnthem_Achievement_Listener"
};
+ public static final SparseSet ILLEGAL_WEAPONS = new SparseSet("""
+ 10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
+ 13503, 13506, 14411, 14503, 14505, 14508, 15504-15506
+ """);
+ public static final SparseSet ILLEGAL_RELICS = new SparseSet("""
+ 20001, 23300-23340, 23383-23385, 78310-78554, 99310-99554
+ """);
+ public static final SparseSet ILLEGAL_ITEMS = new SparseSet("""
+ 100086, 100087, 100100-101000, 101106-101110, 101306, 101500-104000,
+ 105001, 105004, 106000-107000, 107011, 108000, 109000-110000,
+ 115000-130000, 200200-200899, 220050, 220054
+ """);
public static final int[] DEFAULT_ABILITY_HASHES =
Arrays.stream(DEFAULT_ABILITY_STRINGS).mapToInt(Utils::abilityHash).toArray();
public static final int DEFAULT_ABILITY_NAME = Utils.abilityHash("Default");
- public static String VERSION = "3.6.0";
- public static final boolean DEBUG = true;
}
diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java
index c05a7d2c3..4fae4a872 100644
--- a/src/main/java/emu/grasscutter/Grasscutter.java
+++ b/src/main/java/emu/grasscutter/Grasscutter.java
@@ -21,6 +21,7 @@ import emu.grasscutter.server.http.HttpServer;
import emu.grasscutter.server.http.dispatch.DispatchHandler;
import emu.grasscutter.server.http.dispatch.RegionHandler;
import emu.grasscutter.server.http.documentation.DocumentationServerHandler;
+import emu.grasscutter.server.http.documentation.HandbookHandler;
import emu.grasscutter.server.http.handlers.AnnouncementsHandler;
import emu.grasscutter.server.http.handlers.GachaHandler;
import emu.grasscutter.server.http.handlers.GenericHandler;
@@ -136,6 +137,7 @@ public final class Grasscutter {
httpServer.addRouter(DispatchHandler.class);
httpServer.addRouter(GachaHandler.class);
httpServer.addRouter(DocumentationServerHandler.class);
+ httpServer.addRouter(HandbookHandler.class);
// Start servers.
var runMode = Grasscutter.getRunMode();
diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java
index 7dc1b928f..bd79a3c0d 100644
--- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java
+++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java
@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.function.BiConsumer;
import java.util.regex.Pattern;
+import static emu.grasscutter.GameConstants.*;
import static emu.grasscutter.command.CommandHelpers.*;
@Command(
@@ -42,18 +43,6 @@ public final class GiveCommand implements CommandHandler {
Map.entry(constellationRegex, GiveItemParameters::setConstellation),
Map.entry(skillLevelRegex, GiveItemParameters::setSkillLevel)
);
- private static final SparseSet illegalWeaponIds = new SparseSet("""
- 10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
- 13503, 13506, 14411, 14503, 14505, 14508, 15504-15506
- """);
- private static final SparseSet illegalRelicIds = new SparseSet("""
- 20001, 23300-23340, 23383-23385, 78310-78554, 99310-99554
- """);
- private static final SparseSet illegalItemIds = new SparseSet("""
- 100086, 100087, 100100-101000, 101106-101110, 101306, 101500-104000,
- 105001, 105004, 106000-107000, 107011, 108000, 109000-110000,
- 115000-130000, 200200-200899, 220050, 220054
- """);
private static Avatar makeAvatar(GiveItemParameters param) {
return makeAvatar(param.avatarData, param.lvl, Avatar.getMinPromoteLevel(param.lvl), param.constellation, param.skillLevel);
@@ -231,7 +220,7 @@ public final class GiveCommand implements CommandHandler {
for (ItemData itemdata : GameData.getItemDataMap().values()) {
int id = itemdata.getId();
if (id < 100_000) continue; // Nothing meaningful below this
- if (illegalItemIds.contains(id)) continue;
+ if (ILLEGAL_ITEMS.contains(id)) continue;
if (itemdata.isEquip()) continue;
GameItem item = new GameItem(itemdata);
@@ -251,7 +240,7 @@ public final class GiveCommand implements CommandHandler {
for (ItemData itemdata : GameData.getItemDataMap().values()) {
int id = itemdata.getId();
if (id < 11100 || id > 16000) continue; // All extant weapons are within this range
- if (illegalWeaponIds.contains(id)) continue;
+ if (ILLEGAL_WEAPONS.contains(id)) continue;
if (!itemdata.isEquip()) continue;
if (itemdata.getItemType() != ItemType.ITEM_WEAPON) continue;
@@ -333,7 +322,7 @@ public final class GiveCommand implements CommandHandler {
if (param.lvl < 0) param.lvl = 0;
if (param.lvl > 20) param.lvl = 20;
param.lvl += 1;
- if (illegalRelicIds.contains(param.id))
+ if (ILLEGAL_RELICS.contains(param.id))
CommandHandler.sendTranslatedMessage(sender, "commands.give.illegal_relic");
} else {
// Suitable for Avatars and Weapons
diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java
index 195554cdf..4ffa17a7e 100644
--- a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java
+++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java
@@ -10,6 +10,8 @@ import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.Scene;
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp;
@@ -43,11 +45,11 @@ public class DungeonSystem extends BaseGameSystem {
var handlerClasses = reflections.getSubTypesOf(clazz);
for (var obj : handlerClasses) {
- this.registerPacketHandler(map, obj);
+ this.registerHandler(map, obj);
}
}
- public void registerPacketHandler(Int2ObjectMap map, Class extends T> handlerClass) {
+ public void registerHandler(Int2ObjectMap map, Class extends T> handlerClass) {
try {
DungeonValue opcode = handlerClass.getAnnotation(DungeonValue.class);
@@ -178,5 +180,6 @@ public class DungeonSystem extends BaseGameSystem {
// Transfer player back to world
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
+ player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
}
}
diff --git a/src/main/java/emu/grasscutter/server/http/documentation/HandbookHandler.java b/src/main/java/emu/grasscutter/server/http/documentation/HandbookHandler.java
new file mode 100644
index 000000000..29a49f562
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/http/documentation/HandbookHandler.java
@@ -0,0 +1,173 @@
+package emu.grasscutter.server.http.documentation;
+
+import emu.grasscutter.Grasscutter;
+import emu.grasscutter.Grasscutter.ServerRunMode;
+import emu.grasscutter.data.GameData;
+import emu.grasscutter.game.avatar.Avatar;
+import emu.grasscutter.game.inventory.GameItem;
+import emu.grasscutter.game.props.ActionReason;
+import emu.grasscutter.server.http.Router;
+import emu.grasscutter.utils.FileUtils;
+import emu.grasscutter.utils.objects.HandbookBody;
+import io.javalin.Javalin;
+import io.javalin.http.Context;
+
+import static emu.grasscutter.config.Configuration.HANDBOOK;
+
+/** Handles requests for the new GM Handbook. */
+public final class HandbookHandler implements Router {
+ private final byte[] handbook;
+ private final boolean serve;
+
+ /**
+ * Constructor for the handbook router.
+ * Enables serving the handbook if the handbook file is found.
+ */
+ public HandbookHandler() {
+ this.handbook = FileUtils.readResource("/handbook.html");
+ this.serve = HANDBOOK.enable && this.handbook.length > 0;
+ }
+
+ @Override
+ public void applyRoutes(Javalin javalin) {
+ if (!this.serve) return;
+
+ // The handbook content. (built from src/handbook)
+ javalin.get("/handbook", this::serveHandbook);
+
+ // Handbook control routes.
+ javalin.post("/handbook/avatar", this::grantAvatar);
+ javalin.post("/handbook/item", this::giveItem);
+ }
+
+ /**
+ * @return True if the server can execute handbook commands.
+ */
+ private boolean controlSupported() {
+ return HANDBOOK.enable &&
+ Grasscutter.getRunMode() == ServerRunMode.HYBRID;
+ }
+
+ /**
+ * Serves the handbook if it is found.
+ *
+ * @route GET /handbook
+ * @param ctx The Javalin request context.
+ */
+ private void serveHandbook(Context ctx) {
+ if (!this.serve) {
+ ctx.status(500).result("Handbook not found.");
+ } else {
+ ctx.contentType("text/html").result(this.handbook);
+ }
+ }
+
+ /**
+ * Grants the avatar to the user.
+ *
+ * @route POST /handbook/avatar
+ * @param ctx The Javalin request context.
+ */
+ private void grantAvatar(Context ctx) {
+ if (!this.controlSupported()) {
+ ctx.status(500).result("Handbook control not supported.");
+ return;
+ }
+
+ // Parse the request body into a class.
+ var request = ctx.bodyAsClass(HandbookBody.GrantAvatar.class);
+ // Validate the request.
+ if (request.getPlayer() == null || request.getAvatar() == null) {
+ ctx.status(400).result("Invalid request.");
+ return;
+ }
+
+ try {
+ // Parse the requested player.
+ var playerId = Integer.parseInt(request.getPlayer());
+ var player = Grasscutter.getGameServer().getPlayerByUid(playerId);
+
+ // Parse the requested avatar.
+ var avatarId = Integer.parseInt(request.getAvatar());
+ var avatarData = GameData.getAvatarDataMap().get(avatarId);
+
+ // Validate the request.
+ if (player == null || avatarData == null) {
+ ctx.status(400).result("Invalid player UID or avatar ID.");
+ return;
+ }
+
+ // Create the new avatar.
+ var avatar = new Avatar(avatarData);
+ avatar.setLevel(request.getLevel());
+ avatar.setPromoteLevel(Avatar.getMinPromoteLevel(avatar.getLevel()));
+ avatar.getSkillDepot().getSkillsAndEnergySkill().forEach(id ->
+ avatar.setSkillLevel(id, request.getTalentLevels()));
+ avatar.forceConstellationLevel(request.getConstellations());
+ avatar.recalcStats(true); avatar.save();
+
+ player.addAvatar(avatar); // Add the avatar.
+ ctx.json(HandbookBody.Response.builder()
+ .status(200)
+ .message("Avatar granted.")
+ .build());
+ } catch (NumberFormatException ignored) {
+ ctx.status(500).result("Invalid player UID or avatar ID.");
+ } catch (Exception exception) {
+ ctx.status(500).result("An error occurred while granting the avatar.");
+ Grasscutter.getLogger().debug("A handbook command error occurred.", exception);
+ }
+ }
+
+ /**
+ * Gives an item to the user.
+ *
+ * @route POST /handbook/item
+ * @param ctx The Javalin request context.
+ */
+ private void giveItem(Context ctx) {
+ if (!this.controlSupported()) {
+ ctx.status(500).result("Handbook control not supported.");
+ return;
+ }
+
+ // Parse the request body into a class.
+ var request = ctx.bodyAsClass(HandbookBody.GiveItem.class);
+ // Validate the request.
+ if (request.getPlayer() == null || request.getItem() == null) {
+ ctx.status(400).result("Invalid request.");
+ return;
+ }
+
+ try {
+ // Parse the requested player.
+ var playerId = Integer.parseInt(request.getPlayer());
+ var player = Grasscutter.getGameServer().getPlayerByUid(playerId);
+
+ // Parse the requested item.
+ var itemId = Integer.parseInt(request.getItem());
+ var itemData = GameData.getItemDataMap().get(itemId);
+
+ // Validate the request.
+ if (player == null || itemData == null) {
+ ctx.status(400).result("Invalid player UID or item ID.");
+ return;
+ }
+
+ // Create the new item stack.
+ var itemStack = new GameItem(itemData, request.getAmount());
+ // Add the item to the inventory.
+ player.getInventory().addItem(itemStack, ActionReason.Gm);
+
+ ctx.json(HandbookBody.Response.builder()
+ .status(200)
+ .message("Item granted.")
+ .build());
+ } catch (NumberFormatException ignored) {
+ ctx.status(500).result("Invalid player UID or item ID.");
+ } catch (Exception exception) {
+ ctx.status(500).result("An error occurred while granting the item.");
+ Grasscutter.getLogger().debug("A handbook command error occurred.", exception);
+ }
+ }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWindSeedClientNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWindSeedClientNotify.java
new file mode 100644
index 000000000..aa7439145
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWindSeedClientNotify.java
@@ -0,0 +1,18 @@
+package emu.grasscutter.server.packet.send;
+
+import com.google.protobuf.ByteString;
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.WindSeedClientNotifyOuterClass.WindSeedClientNotify;
+import emu.grasscutter.net.proto.WindSeedClientNotifyOuterClass.WindSeedClientNotify.AreaNotify;
+
+public final class PacketWindSeedClientNotify extends BasePacket {
+ public PacketWindSeedClientNotify(byte[] compiledLua) {
+ super(PacketOpcodes.WindSeedClientNotify);
+
+ this.setData(WindSeedClientNotify.newBuilder()
+ .setAreaNotify(AreaNotify.newBuilder()
+ .setAreaId(1).setAreaType(1)
+ .setAreaCode(ByteString.copyFrom(compiledLua))));
+ }
+}
diff --git a/src/main/java/emu/grasscutter/tools/Dumpers.java b/src/main/java/emu/grasscutter/tools/Dumpers.java
index 75df3b17e..c7e3bf752 100644
--- a/src/main/java/emu/grasscutter/tools/Dumpers.java
+++ b/src/main/java/emu/grasscutter/tools/Dumpers.java
@@ -1,16 +1,325 @@
package emu.grasscutter.tools;
-import emu.grasscutter.net.proto.GetGachaInfoRspOuterClass.GetGachaInfoRsp;
-import emu.grasscutter.net.proto.GetShopRspOuterClass.GetShopRsp;
+import emu.grasscutter.command.Command;
+import emu.grasscutter.command.Command.TargetRequirement;
+import emu.grasscutter.command.CommandMap;
+import emu.grasscutter.data.GameData;
+import emu.grasscutter.data.ResourceLoader;
+import emu.grasscutter.game.inventory.ItemType;
+import emu.grasscutter.game.props.SceneType;
+import emu.grasscutter.utils.JsonUtils;
+import emu.grasscutter.utils.Language;
+import lombok.AllArgsConstructor;
-public final class Dumpers {
- public static void extractBanner(byte[] data) throws Exception {
- GetGachaInfoRsp proto = GetGachaInfoRsp.parseFrom(data);
- System.out.println(proto);
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public interface Dumpers {
+ // See `src/handbook/data/README.md` for attributions.
+
+ /**
+ * Fetches the description of a command.
+ *
+ * @param locale The locale to use.
+ * @param command The command to get the description of.
+ * @return The description of the command.
+ */
+ private static String commandDescription(String locale, Command command) {
+ try {
+ // Get the language by the locale.
+ var language = Language.getLanguage(locale);
+ if (language == null) throw new IllegalArgumentException("Invalid language.");
+
+ return language.get("commands." + command.label() + ".description");
+ } catch (IllegalArgumentException ignored) {
+ return command.label();
+ }
}
- public static void extractShop(byte[] data) throws Exception {
- GetShopRsp proto = GetShopRsp.parseFrom(data);
- System.out.println(proto);
+ /**
+ * Encodes the dump into comma separated values.
+ *
+ * @param dump The dump to encode.
+ * @return The encoded dump.
+ */
+ private static String miniEncode(Map dump) {
+ return dump.entrySet().stream()
+ .map(entry -> entry.getKey() + "," + entry.getValue().toString())
+ .collect(Collectors.joining("\n"));
+ }
+
+ /**
+ * Dumps all commands to a JSON file.
+ *
+ * @param locale The language to dump the commands in.
+ */
+ static void dumpCommands(String locale) {
+ // Check that commands are registered.
+ var commandMap = CommandMap.getInstance();
+ if (commandMap == null) commandMap = new CommandMap(true);
+
+ // Convert all registered commands to an info map.
+ var dump = new HashMap();
+ commandMap.getAnnotationsAsList().forEach(command -> {
+ var description = Dumpers.commandDescription(locale, command);
+ var labels = new ArrayList(){{
+ this.add(command.label());
+ this.addAll(List.of(command.aliases()));
+ }};
+
+ // Add the command info to the list.
+ dump.put(command.label(), new CommandInfo(
+ labels, description, List.of(command.usage()), List.of(
+ command.permission(), command.permissionTargeted()),
+ command.targetRequirement()));
+ });
+
+ try {
+ // Create a file for the dump.
+ var file = new File("commands.json");
+ if (file.exists() && !file.delete())
+ throw new RuntimeException("Failed to delete file.");
+ if (!file.exists() && !file.createNewFile())
+ throw new RuntimeException("Failed to create file.");
+
+ // Write the dump to the file.
+ Files.writeString(file.toPath(), JsonUtils.encode(dump));
+ } catch (IOException ignored) {
+ throw new RuntimeException("Failed to write to file.");
+ }
+ }
+
+ /**
+ * Dumps all avatars to a JSON file.
+ *
+ * @param locale The language to dump the avatars in.
+ */
+ static void dumpAvatars(String locale) {
+ // Reload resources.
+ ResourceLoader.loadAll();
+ Language.loadTextMaps();
+
+ // Convert all known avatars to an avatar map.
+ var dump = new HashMap();
+ GameData.getAvatarDataMap().forEach((id, avatar) -> {
+ var langHash = avatar.getNameTextMapHash();
+ dump.put(id, new AvatarInfo(
+ langHash == 0 ? avatar.getName() : Language.getTextMapKey(langHash).get(locale),
+ avatar.getQualityType().equals("QUALITY_PURPLE") ? Quality.EPIC : Quality.LEGENDARY
+ ));
+ });
+
+ try {
+ // Create a file for the dump.
+ var file = new File("avatars.csv");
+ if (file.exists() && !file.delete())
+ throw new RuntimeException("Failed to delete file.");
+ if (!file.exists() && !file.createNewFile())
+ throw new RuntimeException("Failed to create file.");
+
+ // Write the dump to the file.
+ Files.writeString(file.toPath(), Dumpers.miniEncode(dump));
+ } catch (IOException ignored) {
+ throw new RuntimeException("Failed to write to file.");
+ }
+ }
+
+ /**
+ * Dumps all items to a JSON file.
+ *
+ * @param locale The language to dump the items in.
+ */
+ static void dumpItems(String locale) {
+ // Reload resources.
+ ResourceLoader.loadAll();
+ Language.loadTextMaps();
+
+ // Convert all known items to an item map.
+ var originalDump = new ArrayList();
+ GameData.getItemDataMap().forEach((id, item) -> originalDump.add(new ItemInfo(id,
+ Language.getTextMapKey(item.getNameTextMapHash()).get(locale),
+ Quality.from(item.getRankLevel()), item.getItemType(),
+ item.getIcon().length() > 0 ? item.getIcon().substring(3) : ""
+ )));
+
+ // Create a new dump with filtered duplicates.
+ var names = new ArrayList();
+ var dump = new HashMap();
+ originalDump.forEach(item -> {
+ // Validate the item.
+ if (item.name.contains("[CHS]")) return;
+ if (names.contains(item.name)) return;
+ if (dump.containsKey(item.id)) return;
+ // Add the item to the dump.
+ names.add(item.name);
+ dump.put(item.id, item);
+ });
+
+ try {
+ // Create a file for the dump.
+ var file = new File("items.csv");
+ if (file.exists() && !file.delete())
+ throw new RuntimeException("Failed to delete file.");
+ if (!file.exists() && !file.createNewFile())
+ throw new RuntimeException("Failed to create file.");
+
+ // Write the dump to the file.
+ Files.writeString(file.toPath(), Dumpers.miniEncode(dump));
+ } catch (IOException ignored) {
+ throw new RuntimeException("Failed to write to file.");
+ }
+ }
+
+ /**
+ * Dumps all scenes to a JSON file.
+ */
+ static void dumpScenes() {
+ // Reload resources.
+ ResourceLoader.loadAll();
+ Language.loadTextMaps();
+
+ // Convert all known scenes to a scene map.
+ var dump = new HashMap();
+ GameData.getSceneDataMap().forEach((id, scene) ->
+ dump.put(id, new SceneInfo(scene.getScriptData(), scene.getSceneType())));
+
+ try {
+ // Create a file for the dump.
+ var file = new File("scenes.csv");
+ if (file.exists() && !file.delete())
+ throw new RuntimeException("Failed to delete file.");
+ if (!file.exists() && !file.createNewFile())
+ throw new RuntimeException("Failed to create file.");
+
+ // Write the dump to the file.
+ Files.writeString(file.toPath(), Dumpers.miniEncode(dump));
+ } catch (IOException ignored) {
+ throw new RuntimeException("Failed to write to file.");
+ }
+ }
+
+ /**
+ * Dumps all entities to a JSON file.
+ *
+ * @param locale The language to dump the entities in.
+ */
+ static void dumpEntities(String locale) {
+ // Reload resources.
+ ResourceLoader.loadAll();
+ Language.loadTextMaps();
+
+ // Convert all known avatars to an avatar map.
+ var dump = new HashMap();
+ GameData.getMonsterDataMap().forEach((id, monster) -> {
+ var langHash = monster.getNameTextMapHash();
+ dump.put(id, new EntityInfo(
+ langHash == 0 ? monster.getMonsterName() :
+ Language.getTextMapKey(langHash).get(locale),
+ monster.getMonsterName()
+ ));
+ });
+
+ try {
+ // Create a file for the dump.
+ var file = new File("entities.csv");
+ if (file.exists() && !file.delete())
+ throw new RuntimeException("Failed to delete file.");
+ if (!file.exists() && !file.createNewFile())
+ throw new RuntimeException("Failed to create file.");
+
+ // Write the dump to the file.
+ Files.writeString(file.toPath(), Dumpers.miniEncode(dump));
+ } catch (IOException ignored) {
+ throw new RuntimeException("Failed to write to file.");
+ }
+ }
+
+ @AllArgsConstructor
+ class CommandInfo {
+ public List name;
+ public String description;
+ public List usage;
+ public List permission;
+ public TargetRequirement target;
+ }
+
+ @AllArgsConstructor
+ class AvatarInfo {
+ public String name;
+ public Quality quality;
+
+ @Override
+ public String toString() {
+ return this.name + ","
+ + this.quality;
+ }
+ }
+
+ @AllArgsConstructor
+ class ItemInfo {
+ public Integer id;
+ public String name;
+ public Quality quality;
+ public ItemType type;
+ public String icon;
+
+ @Override
+ public String toString() {
+ return this.name + ","
+ + this.quality + ","
+ + this.type + ","
+ + this.icon;
+ }
+ }
+
+ @AllArgsConstructor
+ class SceneInfo {
+ public String identifier;
+ public SceneType type;
+
+ @Override
+ public String toString() {
+ return this.identifier + ","
+ + this.type;
+ }
+ }
+
+ @AllArgsConstructor
+ class EntityInfo {
+ public String name;
+ public String internal;
+
+ @Override
+ public String toString() {
+ return this.name + ","
+ + this.internal;
+ }
+ }
+
+ enum Quality {
+ LEGENDARY, EPIC, RARE, UNCOMMON, COMMON, UNKNOWN;
+
+ /**
+ * Convert a rank level to a quality.
+ *
+ * @param rankLevel The rank level to convert.
+ * @return The quality.
+ */
+ static Quality from(int rankLevel) {
+ return switch (rankLevel) {
+ case 0 -> UNKNOWN;
+ case 1 -> COMMON;
+ case 2 -> UNCOMMON;
+ case 3 -> RARE;
+ case 4 -> EPIC;
+ default -> LEGENDARY;
+ };
+ }
}
}
diff --git a/src/main/java/emu/grasscutter/utils/FileUtils.java b/src/main/java/emu/grasscutter/utils/FileUtils.java
index 4c7897042..efde279cd 100644
--- a/src/main/java/emu/grasscutter/utils/FileUtils.java
+++ b/src/main/java/emu/grasscutter/utils/FileUtils.java
@@ -209,7 +209,7 @@ public final class FileUtils {
return is.readAllBytes();
} catch (Exception exception) {
Grasscutter.getLogger().warn("Failed to read resource: " + resourcePath);
- exception.printStackTrace();
+ Grasscutter.getLogger().debug("Failed to load resource: " + resourcePath, exception);
}
return new byte[0];
diff --git a/src/main/java/emu/grasscutter/utils/StartupArguments.java b/src/main/java/emu/grasscutter/utils/StartupArguments.java
index c15cf1006..13392fd36 100644
--- a/src/main/java/emu/grasscutter/utils/StartupArguments.java
+++ b/src/main/java/emu/grasscutter/utils/StartupArguments.java
@@ -10,6 +10,8 @@ import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.net.packet.PacketOpcodesUtils;
import java.util.Map;
import java.util.function.Function;
+
+import emu.grasscutter.tools.Dumpers;
import org.slf4j.LoggerFactory;
/** A parser for start-up arguments. */
@@ -47,6 +49,7 @@ public final class StartupArguments {
SERVER.http.encryption.useEncryption = false;
return false;
},
+ "-dump", StartupArguments::dump,
// Aliases.
"-v", StartupArguments::printVersion,
@@ -124,4 +127,38 @@ public final class StartupArguments {
Grasscutter.getLogger().debug("The logger is now running in debug mode.");
return false;
}
+
+ /**
+ * Dumps the specified information.
+ *
+ * @param parameter The parameter to dump.
+ * @return True to exit early.
+ */
+ private static boolean dump(String parameter) {
+ // Parse the parameter.
+ if (!parameter.contains(",")) {
+ Grasscutter.getLogger().error("Dumper usage: -dump=,");
+ return true;
+ }
+
+ var split = parameter.split(",");
+ var content = split[0];
+ var language = split[1];
+
+ try {
+ switch (content.toLowerCase()) {
+ case "commands" -> Dumpers.dumpCommands(language);
+ case "avatars" -> Dumpers.dumpAvatars(language);
+ case "items" -> Dumpers.dumpItems(language);
+ case "scenes" -> Dumpers.dumpScenes();
+ case "entities" -> Dumpers.dumpEntities(language);
+ }
+
+ Grasscutter.getLogger().info("Finished dumping.");
+ } catch (Exception exception) {
+ Grasscutter.getLogger().error("Unable to complete dump.", exception);
+ }
+
+ return true;
+ }
}
diff --git a/src/main/java/emu/grasscutter/utils/objects/HandbookBody.java b/src/main/java/emu/grasscutter/utils/objects/HandbookBody.java
new file mode 100644
index 000000000..f0f76a161
--- /dev/null
+++ b/src/main/java/emu/grasscutter/utils/objects/HandbookBody.java
@@ -0,0 +1,32 @@
+package emu.grasscutter.utils.objects;
+
+import lombok.Builder;
+import lombok.Getter;
+
+/** HTTP request object for handbook controls. */
+@SuppressWarnings("FieldMayBeFinal")
+public interface HandbookBody {
+ @Builder
+ class Response {
+ private int status;
+ private String message;
+ }
+
+ @Getter
+ class GrantAvatar {
+ private String player; // Parse into online player ID.
+ private String avatar; // Parse into avatar ID.
+
+ private int level = 90; // Range between 1 - 90.
+ private int constellations = 6; // Range between 0 - 6.
+ private int talentLevels = 10; // Range between 1 - 15.
+ }
+
+ @Getter
+ class GiveItem {
+ private String player; // Parse into online player ID.
+ private String item; // Parse into item ID.
+
+ private int amount = 1; // Range between 1 - Long.MAX_VALUE.
+ }
+}