diff --git a/.env b/.env deleted file mode 100644 index 79099d8..0000000 --- a/.env +++ /dev/null @@ -1,2 +0,0 @@ -GENERATE_SOURCEMAP=false -BROWSER=none \ No newline at end of file diff --git a/.env.development b/.env.development deleted file mode 100644 index fb1adaf..0000000 --- a/.env.development +++ /dev/null @@ -1 +0,0 @@ -PUBLIC_URL=/dev/ \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..d6c9537 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/.gitignore b/.gitignore index 1947a0b..bd98e77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,25 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -package-lock.json -yarn.lock -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - +# Logs +logs +*.log npm-debug.log* yarn-debug.log* yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +build +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/OpenBrowserPlugin.js b/OpenBrowserPlugin.js deleted file mode 100644 index ddde924..0000000 --- a/OpenBrowserPlugin.js +++ /dev/null @@ -1,49 +0,0 @@ -const open = require('open'); - -module.exports = class OpenBrowser { - constructor(options) { - if (typeof options === 'string') { - this.options = Object.assign( - { - hasOpen: false - }, - { - url: options - } - ); - } else { - this.options = Object.assign( - { - port: 8080, - host: 'localhost', - protocol: 'http:', - hasOpen: false - }, - options - ); - } - } - - apply(compiler) { - const options = this.options; - let url; - let hasOpen = options.hasOpen; - if (options.protocol && !options.protocol.endsWith(':')) options.protocol += ':'; - if (options.url) url = options.url; - else url = `${options.protocol}//${options.host}:${options.port}`; - if (compiler.hooks) { - compiler.hooks.afterEmit.tap('openBrowser', () => { - if (!hasOpen) open(url); - hasOpen = true; - this.options.hasOpen = true; - }); - } else { - compiler.plugin('after-emit', (c, cb) => { - if (!hasOpen) open(url); - hasOpen = true; - this.options.hasOpen = true; - return cb(); - }); - } - } -}; diff --git a/README.md b/README.md index 3429b04..66c5d8d 100644 --- a/README.md +++ b/README.md @@ -17,20 +17,12 @@ Fullfledged example of the React HUD made for HUD Manager. It has: - Custom Radar ## Keybinds: -### **Left Alt + S** ->Makes radar smaller by 20px; ### **Left Alt + B** +>Makes radar smaller by 20px; +### **Left Alt + V** >Makes radar bigger by 20px; -### **Left Alt + T** ->Shows trivia box -### **Left Alt + M** ->Toggles upcoming match box -### **Left Alt + P** ->Toggles player preview ### **Left Alt + C** >Toggles camera feed -### **Left Ctrl + B** ->Make radar invisible ## **Panel** ## Trivia settings @@ -83,48 +75,57 @@ To create Signed HUD to prevent at least from modyfing compiled Javascript files ## `panel.json` API -To get the incoming data from the HUD Manager, let's take a look at the `src/HUD/SideBoxes/SideBox.tsx` `componentDidMount()` method: -```javascript -import {configs} from './../../App'; -... -configs.onChange((data:any) => { - if(!data) return; - - const display = data.display_settings; - - if(!display) return; - - if(display[`${this.props.side}_title`]){ - this.setState({title:display[`${this.props.side}_title`]}) - } - if(display[`${this.props.side}_subtitle`]){ - this.setState({subtitle:display[`${this.props.side}_subtitle`]}) - } - if(display[`${this.props.side}_image`]){ - this.setState({image:display[`${this.props.side}_image`]}) - } -}); +To get the incoming data from the HUD Manager, let's take a look at the `src/HUD/SideBoxes/SideBox.tsx` component: +```typescript +const Sidebox = ({side, hide} : { side: 'left' | 'right', hide: boolean}) => { + const [ image, setImage ] = useState(null); + const data = useConfig('display_settings'); + + useOnConfigChange('display_settings', data => { + if(data && `${side}_image` in data){ + const imageUrl = `${apiUrl}api/huds/${hudIdentity.name || 'dev'}/display_settings/${side}_image?isDev=${hudIdentity.isDev}&cache=${(new Date()).getTime()}`; + setImage(imageUrl); + } + }, []); + + if(!data || !data[`${side}_title`]) return null; + return ( +
+
+
{data[`${side}_title`]}
+
{data[`${side}_subtitle`]}
+
+
+ {image ? {'Left'}/:null} +
+
+ ); +} ``` -To retrieve incoming data, you should just import `configs` object and then listen for the changes with `onChange` method. Usually you want to check for the specific data, as in the callback it will always serve the full form from the Manager. -However it looks different in the case of action input. In this case, let's look at the `src/HUD/Trivia/Trivia.tsx`: -```javascript -import {configs, actions} from './../../App'; -... -actions.on("triviaState", (state: any) => { - this.setState({show: state === "show"}) + +You can just read data from the HUDs settings by using `useConfig` hook. Everything is now strictly typed. If you make a change to panel or keybinds JSON files, Vite server will automatically generate types for you, so useConfig should always be up to date. + +If you want to listen for a change in settings, you can use `useOnConfigChange`. In this case we are using this to force refresh `src` attribute of the img element. + +If you want to listen for action input, you can just use `useAction` hook, like here in Trivia.tsx: +```typescript +useAction('triviaState', (state) => { + setShow(state === "show"); }); ``` For the action input we need to import the `actions` object and create listener with the parameter on it. ## `keybinds.json` API -Keybinds API works in very similiar to `panel.json` action API. One more time the example will be from `src/HUD/Trivia/Trivia.tsx`: -```javascript -import {configs, actions} from './../../App'; -... -actions.on("toggleTrivia", () => { - this.setState({show: !this.state.show}) -}); +Keybinds API works in very similiar to `panel.json` action API. This time the example will be from `RadarMaps.tsx`: +```typescript +useAction('radarBigger', () => { + setRadarSize(p => p+10); +}, []); + +useAction('radarSmaller', () => { + setRadarSize(p => p-10); +}, []); ``` -Keybinds listener works on the same object as action input, in this case however there are no parameter to retrieve. + ## Killfeed Because our `csgogsi` has the ability to process input from HLAE's MIRV, listening for kills is very easy. We can see than in `src/HUD/Killfeed/Killfeed.tsx`: diff --git a/craco.config.js b/craco.config.js deleted file mode 100644 index 4d6f774..0000000 --- a/craco.config.js +++ /dev/null @@ -1,45 +0,0 @@ -const path = require('path'); -const fs = require('fs'); -const homedir = require('os').homedir(); -const internalIp = require('internal-ip'); -const OpenBrowserPlugin = require('./OpenBrowserPlugin'); - -const pathToConfig = path.join(process.env.APPDATA || path.join(homedir, '.config'), 'hud-manager', 'databases', 'config'); -let port = 1349; - -const getPort = () => { - if(!fs.existsSync(pathToConfig)){ - console.warn('LHM Config file unavailable'); - return port; - } - - try { - const config = JSON.parse(fs.readFileSync(pathToConfig, 'utf-8')); - - if(!config.port){ - console.warn('LHM Port unavailable'); - } - - console.warn('LHM Port detected as', config.port); - return config.port; - - } catch { - console.warn('LHM Config file invalid'); - return port; - } -} - -port = getPort(); - -module.exports = { - devServer: { - port: 3500, - open: false - }, - webpack: { - configure: (webpackConfig) => { - webpackConfig.plugins.push(new OpenBrowserPlugin({ url: `http://${internalIp.v4.sync()}:${port}/development/`})) - return webpackConfig; - } - } -}; \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a3aa826 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4749 @@ +{ + "name": "cs2-react-hud", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cs2-react-hud", + "version": "1.0.0", + "dependencies": { + "csgogsi": "^3.0.1", + "jsonwebtoken": "^9.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "simple-peer": "^9.11.1", + "simple-websockets": "^1.2.0", + "socket.io-client": "^4.7.2", + "uuid": "^9.0.1" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@types/simple-peer": "^9.11.5", + "@types/uuid": "^9.0.4", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "events": "^3.3.0", + "npm-build-zip": "^1.0.4", + "process": "^0.11.10", + "sass": "^1.68.0", + "typescript": "^5.0.2", + "vite": "^4.4.5", + "vite-compatible-readable-stream": "^3.6.1", + "vite-plugin-svgr": "^4.1.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.17.tgz", + "integrity": "sha512-2EENLmhpwplDux5PSsZnSbnSkB3tZ6QTksgO25xwEL7pIDcNOMhF5v/s6RzwjMZzZzw9Ofc30gHv5ChCC8pifQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.22.15", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.22.17", + "@babel/helpers": "^7.22.15", + "@babel/parser": "^7.22.16", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.17", + "@babel/types": "^7.22.17", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.17", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.17.tgz", + "integrity": "sha512-XouDDhQESrLHTpnBtCKExJdyY4gJCdrvH2Pyv8r8kovX2U8G0dRUOT45T9XlbLtuu9CLXP15eusnkprhoPV5iQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.15.tgz", + "integrity": "sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", + "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", + "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", + "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", + "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "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/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.4.tgz", + "integrity": "sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==", + "dev": true, + "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/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "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-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "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": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "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": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "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": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "engines": { + "node": ">=14" + }, + "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": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "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": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "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": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "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": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", + "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz", + "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.21", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", + "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", + "dev": true + }, + "node_modules/@types/simple-peer": { + "version": "9.11.5", + "resolved": "https://registry.npmjs.org/@types/simple-peer/-/simple-peer-9.11.5.tgz", + "integrity": "sha512-haXgWcAa3Y3Sn+T8lzkE4ErQUpYzhW6Cz2lh00RhQTyWt+xZ3s87wJPztUxlqSdFRqGhe2MQIBd0XsyHP3No4w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.4.tgz", + "integrity": "sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz", + "integrity": "sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/type-utils": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.0.tgz", + "integrity": "sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz", + "integrity": "sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz", + "integrity": "sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.7.0", + "@typescript-eslint/utils": "6.7.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz", + "integrity": "sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz", + "integrity": "sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/visitor-keys": "6.7.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.0.tgz", + "integrity": "sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.7.0", + "@typescript-eslint/types": "6.7.0", + "@typescript-eslint/typescript-estree": "6.7.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz", + "integrity": "sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.7.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.4.tgz", + "integrity": "sha512-7wU921ABnNYkETiMaZy7XqpueMnpu5VxvVps13MjmCo+utBdD79sZzrApHawHtVX66cCJQQTXFcjH0y9dSUK8g==", + "dev": true, + "dependencies": { + "@babel/core": "^7.22.9", + "@babel/plugin-transform-react-jsx-self": "^7.22.5", + "@babel/plugin-transform-react-jsx-source": "^7.22.5", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/archiver": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.3.0.tgz", + "integrity": "sha512-4q/CtGPNVyC5aT9eYHhFP7SAEjKYzQIDIJWXfexUIPNxitNs1y6hORdX+sYxERSZ6qPeNNBJ5UolFsJdWTU02g==", + "dev": true, + "dependencies": { + "archiver-utils": "^1.3.0", + "async": "^2.0.0", + "buffer-crc32": "^0.2.1", + "glob": "^7.0.0", + "lodash": "^4.8.0", + "readable-stream": "^2.0.0", + "tar-stream": "^1.5.0", + "walkdir": "^0.0.11", + "zip-stream": "^1.1.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/archiver-promise": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archiver-promise/-/archiver-promise-1.0.0.tgz", + "integrity": "sha512-6/vW4PWUKyc1KsuacbRh7a7W2s8BBRwL6IM2zNCRUESWks22tMAMr1h45pGbMzzeyNi5XIlniuubcxuc5sBkxQ==", + "dev": true, + "dependencies": { + "archiver": "^1.2.0" + } + }, + "node_modules/archiver-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz", + "integrity": "sha512-h+hTREBXcW5e1L9RihGXdH4PHHdGipG/jE2sMZrqIH6BmZAxeGU5IWjVsKhokdCSWX7km6Kkh406zZNEElHFPQ==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "graceful-fs": "^4.1.0", + "lazystream": "^1.0.0", + "lodash": "^4.8.0", + "normalize-path": "^2.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/archiver-utils/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/archiver/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "dev": true, + "dependencies": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/bl/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-or-node": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-1.3.0.tgz", + "integrity": "sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==" + }, + "node_modules/browserslist": { + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001532", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001532.tgz", + "integrity": "sha512-FbDFnNat3nMnrROzqrsg314zhqN5LGQ1kyyMk2opcrwGbVGpHRhgCWtAgD5YJUqNAiQ+dklreil/c3Qf1dfCTw==", + "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" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "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/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/compress-commons": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.2.tgz", + "integrity": "sha512-SLTU8iWWmcORfUN+4351Z2aZXKJe1tr0jSilPMCZlLPzpdTXnkBW1LevW/MfuANBKJek8Xu9ggqrtVmQrChLtg==", + "dev": true, + "dependencies": { + "buffer-crc32": "^0.2.1", + "crc32-stream": "^2.0.0", + "normalize-path": "^2.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/compress-commons/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/compress-commons/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/compress-commons/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/crc/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/crc32-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz", + "integrity": "sha512-UjZSqFCbn+jZUHJIh6Y3vMF7EJLcJWNm4tKDf2peJRwlZKHvkkvOMTvAei6zjU9gO1xONVr3rRFw0gixm2eUng==", + "dev": true, + "dependencies": { + "crc": "^3.4.4", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/crc32-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/crc32-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csgogsi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/csgogsi/-/csgogsi-3.0.1.tgz", + "integrity": "sha512-ETCi2V1qeA1ZEaPiEaf/uJUNkBWHeBdTDizYv+vAhQzoObXkIyhTIsPOyJ51iazkJyltmsESuXM+Sy7hqnNt0A==" + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.513", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.513.tgz", + "integrity": "sha512-cOB0xcInjm+E5qIssHeXJ29BaUyWpMyFKT5RB3bsLENDheCja0wMkHJyiPl0NBE/VzDI7JDuNEQWhe6RitEUcw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz", + "integrity": "sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/err-code": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-3.0.1.tgz", + "integrity": "sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA==" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.3.tgz", + "integrity": "sha512-Hh0wv8bUNY877+sI0BlCUlsS0TYYQqvzEwJsJJPM2WF4RnTStSnSR3zdJYa2nPOJgg3UghXi54lVyMSmpCalzA==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/eslint/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/eslint/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/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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/eslint/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/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "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/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", + "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "dev": true, + "dependencies": { + "flatted": "^3.2.7", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-browser-rtc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz", + "integrity": "sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/immutable": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "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/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-build-zip": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/npm-build-zip/-/npm-build-zip-1.0.4.tgz", + "integrity": "sha512-mn4IPDkB+LlJiIaXMJ+vFa1yFrWhT4MGo0AeNN3Blyc+pIdqJzL1mQBvk+qnVT6tP+FbohnYvWFyNaTYEHtpZg==", + "dev": true, + "dependencies": { + "archiver-promise": "1.0.0", + "npm-packlist": "1.4.4", + "sanitize-filename": "1.6.3", + "yargs": "13.3.0" + }, + "bin": { + "npm-build-zip": "bin/npm-build-zip.js" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-packlist": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.4.tgz", + "integrity": "sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw==", + "dev": true, + "dependencies": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "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/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reconnecting-websocket": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz", + "integrity": "sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==" + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.1.tgz", + "integrity": "sha512-c+ebvQz0VIH4KhhCpDsI+Bik0eT8ZFEVZEYw0cGMVqIP8zc+gnwl7iXCamTw7vzv2MeuZFZfdx5JJIq+ehzDlg==", + "dev": true, + "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", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sass": { + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.68.0.tgz", + "integrity": "sha512-Lmj9lM/fef0nQswm1J2HJcEsBUba4wgNx2fea6yJHODREoMFnwRpZydBnX/RjyXw2REIwdkbqE4hrTo4qfDBUA==", + "dev": true, + "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": ">=14.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/simple-peer": { + "version": "9.11.1", + "resolved": "https://registry.npmjs.org/simple-peer/-/simple-peer-9.11.1.tgz", + "integrity": "sha512-D1SaWpOW8afq1CZGWB8xTfrT3FekjQmPValrqncJMX7QFl8YwhrPTZvMCANLtgBwwdS+7zURyqxDDEmY558tTw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "buffer": "^6.0.3", + "debug": "^4.3.2", + "err-code": "^3.0.1", + "get-browser-rtc": "^1.1.0", + "queue-microtask": "^1.2.3", + "randombytes": "^2.1.0", + "readable-stream": "^3.6.0" + } + }, + "node_modules/simple-websockets": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/simple-websockets/-/simple-websockets-1.2.0.tgz", + "integrity": "sha512-IiUdc/qECZnJvO2gwLD0tFneIuiTOsy+bjAIcK9NY6/cZGR+HScg4Uv39RWCG3H4M7hvtfASou7+GksAcKgKcg==", + "dependencies": { + "browser-or-node": "^1.3.0", + "reconnecting-websocket": "^4.4.0", + "ws": "^7.4.3" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/socket.io-client": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", + "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true + }, + "node_modules/tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "dev": true, + "dependencies": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/tar-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", + "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vite": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", + "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-compatible-readable-stream": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz", + "integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/vite-plugin-svgr": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.1.0.tgz", + "integrity": "sha512-v7Qic+FWmCChgQNGSI4V8X63OEYsdUoLt66iqIcHozq9bfK/Dwmr0V+LBy1NE8CE98Y8HouEBJ+pto4AMfN5xw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.4", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": "^2.6.0 || 3 || 4" + } + }, + "node_modules/walkdir": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz", + "integrity": "sha512-lMFYXGpf7eg+RInVL021ZbJJT4hqsvsBvq5sZBp874jfhs3IWlA7OPoG0ojQrYcXHuUSi+Nqp6qGN+pPGaMgPQ==", + "dev": true, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs-parser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz", + "integrity": "sha512-2olrDUuPM4NvRIgGPhvrp84f7/HmWR6RiQrgwFF2VctmnssFiogtYL3DcA8Vl2bsSmju79sVXe38TsII7JleUg==", + "dev": true, + "dependencies": { + "archiver-utils": "^1.3.0", + "compress-commons": "^1.2.0", + "lodash": "^4.8.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/zip-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/zip-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + } + } +} diff --git a/package.json b/package.json index 337551a..f020b69 100644 --- a/package.json +++ b/package.json @@ -1,60 +1,47 @@ { - "name": "lexogrine_cs2_hud", - "version": "1.0.1", - "homepage": "./", + "name": "cs2-react-hud", "private": true, - "dependencies": { - "@craco/craco": "^5.7.0", - "@types/jest": "24.0.19", - "@types/node": "16.11.20", - "@types/react": "17.0.38", - "@types/react-dom": "17.0.11", - "@types/simple-peer": "^9.11.3", - "buffer": "^6.0.3", - "csgogsi-socket": "^2.7.2", - "query-string": "^6.12.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "5.0.0", - "simple-peer": "^9.11.0", - "simple-websockets": "^1.1.0", - "typescript": "^4.5.4", - "uuid": "^8.3.2" - }, - "license": "GPL-3.0", + "version": "1.0.2", + "type": "module", "scripts": { "zip": "npm-build-zip", - "start": "craco start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject", + "dev": "vite --host", + "start": "vite --host", + "build": "tsc && vite build", "pack": "npm run build && npm run zip", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", "sign": "npm run build && node sign.js && npm-build-zip --name=signed" }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] + "dependencies": { + "csgogsi": "^3.0.1", + "jsonwebtoken": "^9.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "simple-peer": "^9.11.1", + "simple-websockets": "^1.2.0", + "socket.io-client": "^4.7.2", + "uuid": "^9.0.1" }, + "license": "MIT", "devDependencies": { - "@types/history": "^4.7.5", - "@types/socket.io-client": "^1.4.32", - "@types/uuid": "^8.3.1", - "internal-ip": "^6.2.0", - "jsonwebtoken": "^8.5.1", - "npm-build-zip": "^1.0.2", - "open": "^8.0.2", - "sass": "^1.32.5", - "socket.io-client": "^2.4.0" + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@types/simple-peer": "^9.11.5", + "@types/uuid": "^9.0.4", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "events": "^3.3.0", + "npm-build-zip": "^1.0.4", + "process": "^0.11.10", + "sass": "^1.68.0", + "typescript": "^5.0.2", + "vite": "^4.4.5", + "vite-compatible-readable-stream": "^3.6.1", + "vite-plugin-svgr": "^4.1.0" } } diff --git a/public/hud.json b/public/hud.json index 32d48c4..a670a1e 100644 --- a/public/hud.json +++ b/public/hud.json @@ -1,13 +1,13 @@ { "name":"Lexogrine CS2 HUD", - "version":"1.0.0", + "version":"1.0.2", "author":"Lexogrine", "legacy": false, "radar": true, "killfeed": false, "game":"cs2", "boltobserv":{ - "css":true, - "maps":true + "css":false, + "maps":false } } \ No newline at end of file diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 2342c68..0000000 --- a/public/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - React App - - - -
- - - diff --git a/public/keybinds.json b/public/keybinds.json index 26c411e..543016c 100644 --- a/public/keybinds.json +++ b/public/keybinds.json @@ -2,5 +2,13 @@ { "bind":"Alt+C", "action":"toggleCams" + }, + { + "bind":"Alt+V", + "action":"radarBigger" + }, + { + "bind":"Alt+B", + "action":"radarSmaller" } ] \ No newline at end of file diff --git a/public/radar.css b/public/radar.css deleted file mode 100644 index 8d305dd..0000000 --- a/public/radar.css +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Use this file to apply custom styles to the radar image - */ - -/* Any player dot */ -div.dot {} - -/* Players that are CT or T */ -div.dot.CT {} -div.dot.T {} - -/* The player with the bomb */ -div.dot.bomb{} - -/* The player currently being observed */ -div.dot.active {} - -/* A dead player */ -div.dot.dead {} - -/* The number on a player dot */ -div.label {} - -/* The number on the dot being spectated */ -div.label.active {} - -/* The dropped or planted bomb on the map */ -#bomb { - height: 4vmin; - width: 4vmin; - background-repeat:no-repeat; -} - -/* Smoke circles on the map */ -#smokes { - display: none; -} -#smokes > div { - display: none; -} - -/* Inferno circles on the map */ -.inferno > div {} - -/* The advisory on screen */ -#advisory { - display: none !important; -} \ No newline at end of file diff --git a/sign.js b/sign.js index 835e2c9..2cbc615 100644 --- a/sign.js +++ b/sign.js @@ -1,7 +1,10 @@ -const path = require('path'); -const fs = require('fs'); -const crypto = require('crypto'); -const jwt = require('jsonwebtoken'); +import path from 'path'; +import fs from 'fs'; +import crypto from 'crypto'; +import jwt from 'jsonwebtoken'; +import { fileURLToPath } from 'url'; + +const __esm_dirname = path.dirname(fileURLToPath(import.meta.url)); const sign = () => { const getAllFilesToSign = (hudDir) => { @@ -17,7 +20,7 @@ const sign = () => { return files; } - const dir = path.join(__dirname, 'build'); + const dir = path.join(__esm_dirname, 'build'); const keyFile = path.join(dir, 'key'); diff --git a/src/HUD/Camera/mediaStream.ts b/src/API/HUD/camera.ts similarity index 95% rename from src/HUD/Camera/mediaStream.ts rename to src/API/HUD/camera.ts index 12e6a0b..8d00c8b 100644 --- a/src/HUD/Camera/mediaStream.ts +++ b/src/API/HUD/camera.ts @@ -1,7 +1,7 @@ import { Instance, SignalData } from 'simple-peer'; -import api from '../../api/api'; -import { socket as Socket } from "../../App"; -const Peer = require('simple-peer'); +import api from '..'; +import { socket } from '../socket'; +import Peer from 'simple-peer'; const wait = (ms: number) => new Promise(r => setTimeout(r, ms)); @@ -73,7 +73,6 @@ const closeConnection = (steamid: string) => { } const initiateConnection = async () => { - const socket = Socket as SocketIOClient.Socket; const camera = await api.camera.get(); await wait(1000); @@ -104,7 +103,7 @@ const initiateConnection = async () => { if (camera.uuid !== roomId) return; - const peerConnection: PeerInstance = new Peer({ initiator: false, trickle: false }); + const peerConnection = new Peer({ initiator: false, trickle: false }) as PeerInstance; const mediaStreamPlayer: MediaStreamPlayer = { peerConnection, steamid }; diff --git a/src/API/HUD/index.ts b/src/API/HUD/index.ts new file mode 100644 index 0000000..bb5fb8c --- /dev/null +++ b/src/API/HUD/index.ts @@ -0,0 +1,88 @@ +import { CSGOGSI, Player, PlayerExtension } from 'csgogsi'; +import api, { isDev } from '..'; + +export const hudIdentity = { + name: '', + isDev +}; + +export const GSI = new CSGOGSI(); +GSI.regulationMR = 12; + +GSI.on("data", data => { + loadPlayers(data.players); +}); + +const requestedSteamIDs: string[] = []; + +const loadPlayers = async (players: Player[]) => { + const leftOvers = players.filter(player => !requestedSteamIDs.includes(player.steamid)); + const leftOverSteamids = leftOvers.map(player => player.steamid); + if(!leftOvers.length) return; + + requestedSteamIDs.push(...leftOverSteamids); + + const extensions = await api.players.get(leftOverSteamids); + + const playersExtensions: PlayerExtension[] = extensions.map(player => ( + { + id: player._id, + name: player.username, + realName: `${player.firstName} ${player.lastName}`, + steamid: player.steamid, + country: player.country, + avatar: player.avatar, + extra: player.extra, + }) + ) + + GSI.players.push(...playersExtensions); + + + leftOvers.forEach(player => { + loadAvatarURL(player); + }); +} + + +interface AvatarLoader { + loader: Promise, + url: string, +} + +const avatars: { [key: string]: AvatarLoader } = {}; + +const loadAvatarURL = (player: Player) => { + if(!player.steamid) return; + if(avatars[player.steamid]) return avatars[player.steamid].url; + avatars[player.steamid] = { + url: player.avatar || '', + loader: new Promise((resolve) => { + api.players.getAvatarURLs(player.steamid).then(result => { + const avatarUrl = result.custom || result.steam; + const existing = GSI.players.find(playerEx => playerEx.steamid === player.steamid); + const target = existing || { + id: player.steamid, + name: player.name, + realName: player.realName, + steamid: player.steamid, + country: player.country, + avatar: player.avatar, + extra: player.extra + } + if(target) target.avatar = avatarUrl; + + if(!existing){ + GSI.players.push(target); + } + + + avatars[player.steamid].url = result.custom || result.steam; + resolve(result.custom || result.custom); + }).catch(() => { + delete avatars[player.steamid]; + resolve(''); + }); + }) + } +} \ No newline at end of file diff --git a/src/API/contexts/actions.tsx b/src/API/contexts/actions.tsx new file mode 100644 index 0000000..85e6143 --- /dev/null +++ b/src/API/contexts/actions.tsx @@ -0,0 +1,62 @@ +import { useCallback, useEffect, useState } from "react"; +import ActionManager, { ActionHandler, ConfigManager } from "./managers"; +import { Events } from "csgogsi"; +import { GSI } from "../HUD"; +import type { AllActions, GetInputsFromSection, Sections } from "./settings"; + +export const actions = new ActionManager(); +export const configs = new ConfigManager(); + +type EmptyListener = () => void; + +type BaseEvents = keyof Events; +type Callback = K extends BaseEvents ? Events[K] | EmptyListener : EmptyListener; + +export function useAction(action: T, callback: ActionHandler, deps?: React.DependencyList) { + useEffect(() => { + + actions.on(action, callback); + return () => { + actions.off(action, callback); + }; + }, deps ? [action, ...deps] : [action, callback]); + return null; +} +export function useOnConfigChange(section: K, callback: ActionHandler<{ [L in keyof (K extends keyof Sections ? GetInputsFromSection : T)]?: (K extends keyof Sections ? GetInputsFromSection : T)[L] } | null>, deps?: React.DependencyList){ + + useEffect(() => { + const onDataChanged = (data: any) => { + callback(data?.[section] || null); + }; + + configs.onChange(onDataChanged); + onDataChanged(configs.data); + + return () => { + configs.off(onDataChanged); + } + }, deps ? [section, ...deps] : [section, callback]) + + return null; +} + +export function onGSI(event: T, callback: Callback, deps?: React.DependencyList){ + useEffect(() => { + GSI.on(event, callback); + + return () => { + GSI.off(event, callback); + } + }, deps ? [event, ...deps] : [event, callback]) +} + +export function useConfig(section: K){ + const [ data, setData ] = useState<{ [L in keyof (K extends keyof Sections ? GetInputsFromSection : T)]?: (K extends keyof Sections ? GetInputsFromSection : T)[L] } | null>(configs.data?.[section] || null); + + const onDataChanged = useCallback((sectionData: any) => { + setData(sectionData || null); + }, [section]); + + useOnConfigChange(section, onDataChanged); + return data; +} diff --git a/src/API/contexts/keybinds.ts b/src/API/contexts/keybinds.ts new file mode 100644 index 0000000..4f14ac6 --- /dev/null +++ b/src/API/contexts/keybinds.ts @@ -0,0 +1,14 @@ +export const keybindDefinition = [ + { + "bind": "Alt+C", + "action": "toggleCams" + }, + { + "bind": "Alt+V", + "action": "radarBigger" + }, + { + "bind": "Alt+B", + "action": "radarSmaller" + } +] as const; \ No newline at end of file diff --git a/src/API/contexts/managers.ts b/src/API/contexts/managers.ts new file mode 100644 index 0000000..4819997 --- /dev/null +++ b/src/API/contexts/managers.ts @@ -0,0 +1,71 @@ +export type ActionHandler = (data: T) => void; + +export default class ActionManager { + handlers: { [K in string]?: ActionHandler[] }; + + constructor(){ + this.handlers = {} + + /*this.on('data', _data => { + });*/ + } + execute = (eventName: string, argument?: T) => { + const handlers = this.handlers[eventName] || []; + for(const handler of handlers){ + handler(argument); + } + } + + on = (eventName: string, handler: ActionHandler) => { + if(!this.handlers[eventName]) this.handlers[eventName] = []; + this.handlers[eventName]!.push(handler); + } + + off = (eventName: string, handler: ActionHandler) => { + if(!this.handlers[eventName]) this.handlers[eventName] = []; + this.handlers[eventName] = this.handlers[eventName]!.filter(h => h !== handler); + } +} +export class ConfigManager { + listeners: ActionHandler[]; + data: { [K in string]?: any }; + + constructor(){ + this.listeners = []; + this.data = {}; + } + save(data: { [K in string]?: any }){ + this.data = data; + this.execute(); + + /*const listeners = this.listeners.get(eventName); + if(!listeners) return false; + listeners.forEach(callback => { + if(argument) callback(argument); + else callback(); + }); + return true;*/ + } + + execute(){ + const listeners = this.listeners; + if(!listeners || !listeners.length) return false; + listeners.forEach(listener => { + listener(this.data); + }); + return true; + } + + onChange = (listener: ActionHandler) => { + const listOfListeners = this.listeners || []; + listOfListeners.push(listener); + this.listeners = listOfListeners; + + return true; + } + + off = (listener: ActionHandler) => { + this.listeners = this.listeners.filter(l => l !== listener); + } + +} \ No newline at end of file diff --git a/src/API/contexts/panel.ts b/src/API/contexts/panel.ts new file mode 100644 index 0000000..113e2ad --- /dev/null +++ b/src/API/contexts/panel.ts @@ -0,0 +1,162 @@ +export const panelDefinition = [ + { + "label": "Trivia", + "name": "trivia", + "inputs": [ + { + "type": "text", + "name": "title", + "label": "Trivia title" + }, + { + "type": "text", + "name": "content", + "label": "Trivia content" + }, + { + "type": "action", + "name": "triviaState", + "values": [ + { + "name": "show", + "label": "Show trivia" + }, + { + "name": "hide", + "label": "Hide trivia" + } + ] + } + ] + }, + { + "label": "Display settings", + "name": "display_settings", + "inputs": [ + { + "type": "text", + "name": "left_title", + "label": "Left box's title" + }, + { + "type": "text", + "name": "right_title", + "label": "Right box's title" + }, + { + "type": "text", + "name": "left_subtitle", + "label": "Left box's subtitle" + }, + { + "type": "text", + "name": "right_subtitle", + "label": "Right box's subtitle" + }, + { + "type": "image", + "name": "left_image", + "label": "Left box's image logo" + }, + { + "type": "image", + "name": "right_image", + "label": "Right box's image logo" + }, + { + "type": "select", + "name": "replace_avatars", + "label": "Use team logos as player avatars", + "values": [ + { + "label": "Only if player has no avatar", + "name": "if_missing" + }, + { + "label": "Always", + "name": "always" + } + ] + }, + { + "type": "action", + "name": "boxesState", + "values": [ + { + "name": "show", + "label": "Show boxes" + }, + { + "name": "hide", + "label": "Hide boxes" + } + ] + }, + { + "type": "action", + "name": "toggleRadarView", + "values": [ + { + "name": "toggler", + "label": "Toggle radar view" + } + ] + } + ] + }, + { + "label": "Player & Match overview", + "name": "preview_settings", + "inputs": [ + { + "type": "match", + "name": "match_preview", + "label": "Pick an upcoming match" + }, + { + "type": "select", + "name": "select_preview", + "label": "Mood indicator", + "values": [ + { + "name": "show", + "label": ":)" + }, + { + "name": "hide", + "label": ":(" + } + ] + }, + { + "type": "player", + "name": "player_preview", + "label": "Pick a player to preview" + }, + { + "type": "checkbox", + "name": "player_preview_toggle", + "label": "Show player preview" + }, + { + "type": "checkbox", + "name": "match_preview_toggle", + "label": "Show upcoming match" + }, + { + "type": "action", + "name": "showTournament", + "values": [ + { + "name": "show", + "label": "Show tournament" + }, + { + "name": "hide", + "label": "Hide tournament" + } + ] + } + ] + } +] as const; \ No newline at end of file diff --git a/src/API/contexts/settings.ts b/src/API/contexts/settings.ts new file mode 100644 index 0000000..4f73be0 --- /dev/null +++ b/src/API/contexts/settings.ts @@ -0,0 +1,49 @@ +import { Match, Player, Team } from "../types" +import { keybindDefinition } from "./keybinds"; +import { panelDefinition } from "./panel"; + +type Prettify = { + [K in keyof T]: T[K]; + } & {}; + +type Settings = typeof panelDefinition; + +type Keybinds = typeof keybindDefinition; + + +type InputMapper = { + text: string; + player: { + type: "player", + id: string, + player: Player | null + }; + match: { + type: "match", + id: string, + match: Match | null + }; + team: { + type: "team", + id: string, + team: Team | null + }; + checkbox: boolean; + action: never; + image: string; + images: string[]; + select: string; +} +type NonNeverKeys = { [K in keyof T]: T[K] extends never ? never : K }[keyof T]; + + +type ValueMapper = { [K in T[number] as K["name"]]: K extends { type: "action" } ? never : (K extends { type: "select" } ? K["values"][number]["name"] | "" : InputMapper[K["type"]]) } + +export type GetInputsFromSection = { [K in NonNeverKeys>]: ValueMapper[K]}; +export type Sections = { [K in Settings[number] as K["name"]]: K["inputs"]} + +type ActionValueMapper = { [K in T[number] as K["name"]]: K extends { type: "action" } ? K["values"][number]["name"] : never } + +type GetActionsFromSection = { [K in NonNeverKeys>]: ActionValueMapper[K]}; + +export type AllActions = Prettify & { [K in Keybinds[number]["action"]]: never }>; diff --git a/src/api/api.ts b/src/API/index.ts similarity index 62% rename from src/api/api.ts rename to src/API/index.ts index 0b4d29c..82fd2ee 100644 --- a/src/api/api.ts +++ b/src/API/index.ts @@ -1,14 +1,12 @@ -import * as I from './interfaces'; -import queryString from 'query-string'; +import * as I from './types'; import { MapConfig } from '../HUD/Radar/LexoRadar/maps'; -const query = queryString.parseUrl(window.location.href).query; -export const variant = query?.variant || "default"; +const query = new URLSearchParams(window.location.search); +export const port = Number(query.get('port') || 1349); +export const variant = query.get("variant") || "default"; -export const port = (query && Number(query.port)) || 1349; - -export const isDev = !query.isProd; +export const isDev = !query.get("isProd"); export const config = {apiAddress:isDev ? `http://localhost:${port}/` : '/'} export const apiUrl = config.apiAddress; @@ -35,7 +33,22 @@ const api = { getCurrent: async (): Promise => apiV2(`match/current`) }, camera: { - get: (): Promise<{ availablePlayers: ({steamid:string, label: string})[], uuid: string }> => apiV2('camera') + get: (): Promise<{ availablePlayers: ({steamid:string, label: string})[], uuid: string }> => apiV2('camera'), + toggleVmix: (status?: boolean) => new Promise(r => { + const controller = new AbortController(); + const signal = controller.signal; + // let finished = false; + const timeoutId = setTimeout(() => { + controller.abort(); + r(false); + }, 1000) + fetch(`http://localhost:2715/visibility${status !== undefined ? `?status=${status}` : ''}`, { method: "POST", signal }).then(() => { + clearTimeout(timeoutId); + r(true); + //finished = true; + }).catch(() => { r(false); }); + + }) }, teams: { getOne: async (id: string): Promise => apiV2(`teams/${id}`), diff --git a/src/API/socket.ts b/src/API/socket.ts new file mode 100644 index 0000000..6f2265e --- /dev/null +++ b/src/API/socket.ts @@ -0,0 +1,69 @@ +import { io } from "socket.io-client"; +import { isDev, port } from "."; +import { GSI, hudIdentity } from "./HUD"; +import { CSGORaw } from "csgogsi"; +import { actions, configs } from "./contexts/actions"; +import { initiateConnection } from "./HUD/camera"; + +export const socket = io(isDev ? `localhost:${port}` : '/'); + +type RoundPlayerDamage = { + steamid: string; + damage: number; +}; +type RoundDamage = { + round: number; + players: RoundPlayerDamage[]; +}; + +socket.on("update", (data: any, damage: any) => { + if (damage) { + GSI.damage = damage; + } + GSI.digest(data); +}); + +const isInWindow = !!window.parent.ipcApi; + +if(isInWindow){ + window.parent.ipcApi.receive('raw', (data: CSGORaw, damage?: RoundDamage[]) => { + if(damage){ + GSI.damage = damage; + } + GSI.digest(data); + }); +} + +const href = window.location.href; + +socket.emit("started"); + +if (isDev) { + hudIdentity.name = (Math.random() * 1000 + 1).toString(36).replace(/[^a-z]+/g, '').substr(0, 15); + hudIdentity.isDev = true; +} else { + const segment = href.substr(href.indexOf('/huds/') + 6); + hudIdentity.name = segment.substr(0, segment.lastIndexOf('/')); +} + +socket.on("readyToRegister", () => { + socket.emit("register", hudIdentity.name, isDev, "cs2", isInWindow ? "IPC" : "DEFAULT"); + initiateConnection(); +}); +socket.on(`hud_config`, (data: any) => { + configs.save(data); +}); +socket.on(`hud_action`, (data: any) => { + actions.execute(data.action, data.data); +}); +socket.on('keybindAction', (action: string) => { + actions.execute(action); +}); + +socket.on("refreshHUD", () => { + window.top?.location.reload(); +}); + +socket.on("update_mirv", (data: any) => { + GSI.digestMIRV(data); +}) diff --git a/src/api/interfaces.ts b/src/API/types.ts similarity index 63% rename from src/api/interfaces.ts rename to src/API/types.ts index d988cf3..ad811f9 100644 --- a/src/api/interfaces.ts +++ b/src/API/types.ts @@ -26,37 +26,47 @@ export interface HUD { legacy: boolean, dir: string } - + export interface Config { port: number, steamApiKey: string, token: string, }*/ export interface TournamentMatchup { - _id: string; - loser_to: string | null; // IDs of Matchups, not Matches - winner_to: string | null; - label: string; - matchId: string | null; - parents: TournamentMatchup[]; + _id: string; + loser_to: string | null; // IDs of Matchups, not Matches + winner_to: string | null; + label: string; + matchId: string | null; + parents: TournamentMatchup[]; } export interface DepthTournamentMatchup extends TournamentMatchup { - depth: number; - parents: DepthTournamentMatchup[]; + depth: number; + parents: DepthTournamentMatchup[]; } +export type TournamentTypes = 'swiss' | 'single' | 'double'; + +export type TournamentStage = { + type: TournamentTypes; + matchups: TournamentMatchup[]; + teams: number; + phases: number; + participants: string[]; +}; export interface Tournament { - _id: string; - name: string; - logo: string; - matchups: TournamentMatchup[]; - autoCreate: boolean; + _id: string; + name: string; + logo: string; + groups: TournamentStage[]; + playoffs: TournamentStage; + autoCreate: boolean; } export interface RoundData { round: number, players: { - [steamid: string]: PlayerRoundData + [steamid: string]: PlayerRoundData }, winner: 'CT' | 'T' | null, win_type: 'bomb' | 'elimination' | 'defuse' | 'time', @@ -128,24 +138,24 @@ export type Weapon = export type Pistol = "c75a" | "deagle" | "elite" | "fiveseven" | "glock" | "hkp2000" | "p250" | "revolver" | "taser" | "tec9" | "usp_silencer"; export type Knife = -| "knife"// -| "knife_css"//-- -| "knife_butterfly"// -| "knife_falchion"// -| "knife_flip"// -| "knife_outdoor" // Nomad Knife -| "knife_gut"// -| "knife_gypsy_jackknife"// -| "knife_karambit"// -| "knife_bayonet" // -| "knife_cord" // -| "knife_m9_bayonet"// -| "knife_push" // Shadow daggers -| "knife_stiletto"// -| "knife_survival_bowie"// -| "knife_t"// -| "knife_skeleton" // -| "knife_tactical"// -| "knife_ursus"// -| "knife_widowmaker"// -| "knife_canis";// + | "knife"// + | "knife_css"//-- + | "knife_butterfly"// + | "knife_falchion"// + | "knife_flip"// + | "knife_outdoor" // Nomad Knife + | "knife_gut"// + | "knife_gypsy_jackknife"// + | "knife_karambit"// + | "knife_bayonet" // + | "knife_cord" // + | "knife_m9_bayonet"// + | "knife_push" // Shadow daggers + | "knife_stiletto"// + | "knife_survival_bowie"// + | "knife_t"// + | "knife_skeleton" // + | "knife_tactical"// + | "knife_ursus"// + | "knife_widowmaker"// + | "knife_canis";// diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..cef3b29 --- /dev/null +++ b/src/App.css @@ -0,0 +1,57 @@ +@import "fonts/montserrat.css"; +@import "fonts/stratum2.css"; +html, +body, +#root { + width: 100%; + height: 100%; +} + +body { + margin: 0; + font-family: 'Stratum2', 'Montserrat', 'Roboto', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + /*background-image: url('./assets/bg.png');/**/ +} + +@font-face { + font-family: 'Louis George Cafe'; + src: local('Louis George Cafe'), url('./fonts/Louis George Cafe.ttf') format('truetype'); +} + +@font-face { + font-family: 'Rounded_Elegance'; + src: local('Rounded_Elegance'), url('./fonts/Rounded_Elegance.ttf') format('truetype'); +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} + +.layout { + width: 100%; + height: 100%; + perspective: 500px; +} + +:root { + --main-panel-color: rgba(12, 15, 18, 0.85); + --sub-panel-color: rgba(0, 0, 0, 0.83); + --white-dull: rgba(10, 5, 5, 0.25); + --white-full: rgba(250, 250, 250, 1); + --white-half: rgba(250, 250, 250, 0.5); + --color-gray: rgba(191, 191, 191, 1.0); + --color-moneys: #a7d32e; + --dev-purple: rgba(200, 0, 255, 1); + --color-t: #c19511; + --color-ct: #5788a8; + --color-new-t: #f0c941; + --color-new-ct: #5ab8f4; + --color-bomb: #f22222; + --color-defuse: #2222f2; +} + + + diff --git a/src/App.tsx b/src/App.tsx index 780a1f2..20ff282 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,215 +1,81 @@ -import React from 'react'; +import { useEffect, useState } from 'react' +import './App.css' +import { CSGO } from 'csgogsi' +import { onGSI } from './API/contexts/actions' import Layout from './HUD/Layout/Layout'; -import api, { port, isDev } from './api/api'; -import { loadAvatarURL } from './api/avatars'; -import ActionManager, { ConfigManager } from './api/actionManager'; +import './API/socket'; +import { Match } from './API/types'; +import api from './API'; +import { GSI } from './API/HUD'; +import { socket } from './API/socket'; -import { CSGO, PlayerExtension, GSISocket, CSGORaw } from "csgogsi-socket"; -import { Match } from './api/interfaces'; -import { initiateConnection } from './HUD/Camera/mediaStream'; +function App() { + const [ game, setGame ] = useState(null); + const [ match, setMatch ] = useState(null); -let isInWindow = !!window.parent.ipcApi; + useEffect(() => { + const onMatchPing = () => { + api.match.getCurrent().then(match => { + if (!match) { + GSI.teams.left = null; + GSI.teams.right = null; + setMatch(null); + return; + } + setMatch(match); + + let isReversed = false; + if (GSI.last) { + const mapName = GSI.last.map.name.substring(GSI.last.map.name.lastIndexOf('/') + 1); + const current = match.vetos.filter(veto => veto.mapName === mapName)[0]; + if (current && current.reverseSide) { + isReversed = true; + } + } + if (match.left.id) { + api.teams.getOne(match.left.id).then(left => { + const gsiTeamData = { id: left._id, name: left.name, country: left.country, logo: left.logo, map_score: match.left.wins, extra: left.extra }; + + if (!isReversed) { + GSI.teams.left = gsiTeamData; + } + else GSI.teams.right = gsiTeamData; + }); + } + if (match.right.id) { + api.teams.getOne(match.right.id).then(right => { + const gsiTeamData = { id: right._id, name: right.name, country: right.country, logo: right.logo, map_score: match.right.wins, extra: right.extra }; + + if (!isReversed) GSI.teams.right = gsiTeamData; + else GSI.teams.left = gsiTeamData; + }); + } + + + + }).catch(() => { + GSI.teams.left = null; + GSI.teams.right = null; + setMatch(null); + }); + } + socket.on("match", onMatchPing); + onMatchPing(); -export const { GSI, socket } = GSISocket(isDev ? `localhost:${port}` : '/', "update"); + return () => { + socket.off("match", onMatchPing); + } + }, []) -GSI.regulationMR = 12; + onGSI('data', game => { -if(isInWindow){ - window.parent.ipcApi.receive('raw', (data: CSGORaw, damage?: RoundDamage[]) => { - if(damage){ - GSI.damage = damage; - } - GSI.digest(data); - }); + setGame(game); + }, []); + + if (!game) return null; + return ( + + ); } -type RoundPlayerDamage = { - steamid: string; - damage: number; -}; -type RoundDamage = { - round: number; - players: RoundPlayerDamage[]; -}; - -socket.on('update', (_csgo: any, damage?: RoundDamage[]) => { - if(damage) GSI.damage = damage; -}); - -export const actions = new ActionManager(); -export const configs = new ConfigManager(); - -export const hudIdentity = { - name: '', - isDev: false -}; - -interface DataLoader { - match: Promise | null -} - -const dataLoader: DataLoader = { - match: null -} - -class App extends React.Component { - constructor(props: any) { - super(props); - this.state = { - game: null, - steamids: [], - match: null, - checked: false - } - } - - verifyPlayers = async (game: CSGO) => { - const steamids = game.players.map(player => player.steamid); - steamids.forEach(steamid => { - loadAvatarURL(steamid); - }) - - if (steamids.every(steamid => this.state.steamids.includes(steamid))) { - return; - } - - const loaded = GSI.players.map(player => player.steamid); - - const notCheckedPlayers = steamids.filter(steamid => !loaded.includes(steamid)); - - const extensioned = await api.players.get(notCheckedPlayers); - - const lacking = notCheckedPlayers.filter(steamid => extensioned.map(player => player.steamid).includes(steamid)); - - const players: PlayerExtension[] = extensioned - .filter(player => lacking.includes(player.steamid)) - .map(player => ( - { - id: player._id, - name: player.username, - realName: `${player.firstName} ${player.lastName}`, - steamid: player.steamid, - country: player.country, - avatar: player.avatar, - extra: player.extra, - }) - ); - - const gsiLoaded = GSI.players; - - gsiLoaded.push(...players); - - GSI.players = gsiLoaded; - this.setState({ steamids }); - } - - - componentDidMount() { - this.loadMatch(); - const href = window.location.href; - socket.emit("started"); - let isDev = false; - let name = ''; - if (href.indexOf('/huds/') === -1) { - isDev = true; - name = (Math.random() * 1000 + 1).toString(36).replace(/[^a-z]+/g, '').substr(0, 15); - hudIdentity.isDev = true; - } else { - const segment = href.substr(href.indexOf('/huds/') + 6); - name = segment.substr(0, segment.lastIndexOf('/')); - hudIdentity.name = name; - } - - socket.on("readyToRegister", () => { - socket.emit("register", name, isDev, "cs2", isInWindow ? "IPC" : "DEFAULT"); - initiateConnection(); - }); - socket.on(`hud_config`, (data: any) => { - configs.save(data); - }); - socket.on(`hud_action`, (data: any) => { - actions.execute(data.action, data.data); - }); - socket.on('keybindAction', (action: string) => { - actions.execute(action); - }); - - socket.on("refreshHUD", () => { - window.top?.location.reload(); - }); - - socket.on("update_mirv", (data: any) => { - GSI.digestMIRV(data); - }) - GSI.on('data', game => { - if (!this.state.game || this.state.steamids.length) this.verifyPlayers(game); - - const wasLoaded = !!this.state.game; - - this.setState({ game }, () => { - if(!wasLoaded) this.loadMatch(true); - }); - }); - socket.on('match', () => { - - this.loadMatch(true); - }); - - } - - loadMatch = async (force = false) => { - if (!dataLoader.match || force) { - dataLoader.match = new Promise((resolve) => { - api.match.getCurrent().then(match => { - if (!match) { - GSI.teams.left = null; - GSI.teams.right = null; - return; - } - this.setState({ match }); - - let isReversed = false; - if (GSI.last) { - const mapName = GSI.last.map.name.substring(GSI.last.map.name.lastIndexOf('/') + 1); - const current = match.vetos.filter(veto => veto.mapName === mapName)[0]; - if (current && current.reverseSide) { - isReversed = true; - } - this.setState({ checked: true }); - } - if (match.left.id) { - api.teams.getOne(match.left.id).then(left => { - const gsiTeamData = { id: left._id, name: left.name, country: left.country, logo: left.logo, map_score: match.left.wins, extra: left.extra }; - - if (!isReversed) { - GSI.teams.left = gsiTeamData; - } - else GSI.teams.right = gsiTeamData; - }); - } - if (match.right.id) { - api.teams.getOne(match.right.id).then(right => { - const gsiTeamData = { id: right._id, name: right.name, country: right.country, logo: right.logo, map_score: match.right.wins, extra: right.extra }; - - if (!isReversed) GSI.teams.right = gsiTeamData; - else GSI.teams.left = gsiTeamData; - }); - } - - - - }).catch(() => { - //dataLoader.match = null; - }); - }); - } - } - render() { - if (!this.state.game) return null; - return ( - - ); - } - -} -export default App; +export default App diff --git a/src/HUD/Camera/Camera.tsx b/src/HUD/Camera/Camera.tsx index 21cebd0..55945b1 100644 --- a/src/HUD/Camera/Camera.tsx +++ b/src/HUD/Camera/Camera.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import { mediaStreams } from "./mediaStream"; import { v4 as uuidv4 } from 'uuid'; +import { mediaStreams } from "../../API/HUD/camera"; type Props = { steamid: string, visible: boolean; diff --git a/src/HUD/Camera/Container.tsx b/src/HUD/Camera/Container.tsx index 09dc952..cb5e7ed 100644 --- a/src/HUD/Camera/Container.tsx +++ b/src/HUD/Camera/Container.tsx @@ -1,6 +1,6 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import PlayerCamera from "./Camera"; -import api from "../../api/api"; +import api from "../../API"; import "./index.scss"; diff --git a/src/HUD/Indicators/Armor.tsx b/src/HUD/Indicators/Armor.tsx index 261ec8d..1a7bbc9 100644 --- a/src/HUD/Indicators/Armor.tsx +++ b/src/HUD/Indicators/Armor.tsx @@ -1,15 +1,13 @@ -import React from 'react'; -import { Player } from 'csgogsi-socket'; -import {ArmorHelmet, ArmorFull} from './../../assets/Icons'; -export default class Armor extends React.Component<{ player: Player }> { - render() { - const { player } = this.props; - if(!player.state.health || !player.state.armor) return ''; - return ( -
- {player.state.helmet ? : } -
- ); - } +import React from "react"; +import { ArmorFull, ArmorHelmet } from "./../../assets/Icons"; -} +const Armor = ({ health, armor, helmet }: { health: number, armor: number, helmet: boolean }) => { + if (!health || !armor) return null; + return ( +
+ {helmet ? : } +
+ ); +}; + +export default React.memo(Armor); diff --git a/src/HUD/Indicators/Bomb.tsx b/src/HUD/Indicators/Bomb.tsx index 9928e5e..0a8148b 100644 --- a/src/HUD/Indicators/Bomb.tsx +++ b/src/HUD/Indicators/Bomb.tsx @@ -1,15 +1,13 @@ -import React from 'react'; -import { Player } from 'csgogsi-socket'; -import {Bomb as BombIcon} from './../../assets/Icons'; -export default class Bomb extends React.Component<{ player: Player }> { - render() { - const { player } = this.props; - if(Object.values(player.weapons).every(weapon => weapon.type !== "C4")) return ''; - return ( -
- -
- ); - } - -} +import { Player } from "csgogsi"; +import { Bomb as BombIcon } from "./../../assets/Icons"; +const Bomb = ({ player }: { player: Player }) => { + if (Object.values(player.weapons).every((weapon) => weapon.type !== "C4")) { + return null; + } + return ( +
+ +
+ ); +}; +export default Bomb; diff --git a/src/HUD/Indicators/Defuse.tsx b/src/HUD/Indicators/Defuse.tsx index c7bf66e..9ae16bf 100644 --- a/src/HUD/Indicators/Defuse.tsx +++ b/src/HUD/Indicators/Defuse.tsx @@ -1,15 +1,11 @@ -import React from 'react'; -import { Player } from 'csgogsi-socket'; +import { Player } from 'csgogsi'; import {Defuse as DefuseIcon} from './../../assets/Icons'; -export default class Defuse extends React.Component<{ player: Player }> { - render() { - const { player } = this.props; +const Defuse = ({ player }: { player: Player }) => { if(!player.state.health || !player.state.defusekit) return ''; return (
); - } - } +export default Defuse; \ No newline at end of file diff --git a/src/HUD/Killfeed/Kill.tsx b/src/HUD/Killfeed/Kill.tsx index 7119df0..d3e52aa 100644 --- a/src/HUD/Killfeed/Kill.tsx +++ b/src/HUD/Killfeed/Kill.tsx @@ -1,13 +1,11 @@ import React from 'react'; import Weapon from './../Weapon/Weapon'; import flash_assist from './../../assets/flash_assist.png'; - - import { C4, Defuse, FlashedKill, Headshot, NoScope, SmokeKill, Suicide, Wallbang } from "./../../assets/Icons" import { ExtendedKillEvent, BombEvent } from "./Killfeed" -export default class Kill extends React.Component<{ event: ExtendedKillEvent | BombEvent }> { - render() { - const { event } = this.props; + + + const Kill = ({event}: { event: ExtendedKillEvent | BombEvent }) => { if (event.type !== "kill") { return (
@@ -50,6 +48,6 @@ export default class Kill extends React.Component<{ event: ExtendedKillEvent | B
); - } } +export default Kill; \ No newline at end of file diff --git a/src/HUD/Killfeed/Killfeed.tsx b/src/HUD/Killfeed/Killfeed.tsx index 683c11d..8197a0d 100644 --- a/src/HUD/Killfeed/Killfeed.tsx +++ b/src/HUD/Killfeed/Killfeed.tsx @@ -1,8 +1,9 @@ -import React from 'react'; -import { GSI } from './../../App'; -import { KillEvent, Player } from 'csgogsi-socket'; +import React, { useState } from 'react'; + +import { KillEvent, Player } from 'csgogsi'; import Kill from './Kill'; import './killfeed.scss'; +import { onGSI } from '../../API/contexts/actions'; export interface ExtendedKillEvent extends KillEvent { @@ -14,63 +15,24 @@ export interface BombEvent { type: 'plant' | 'defuse' } -export default class Killfeed extends React.Component { - - constructor(props: any){ - super(props); - this.state = { - events: [] - } - } - addKill = (kill: KillEvent) => { - this.setState(state => { - state.events.push({...kill, type: 'kill'}); - return state; - }) - } - - addBombEvent = (player: Player, type: 'plant' | 'defuse') => { - if(!player) return; - const event: BombEvent = { - player: player, - type: type - } - this.setState(state => { - state.events.push(event); - return state; - }) - } - - async componentDidMount() { - GSI.on("kill", kill => { - this.addKill(kill); - }); - GSI.on("data", data => { - - if(data.round && data.round.phase === "freezetime"){ - if(Number(data.phase_countdowns.phase_ends_in) < 10 && this.state.events.length > 0){ - this.setState({events:[]}) - } +const Killfeed = () => { + const [ events, setEvents ] = useState<(BombEvent | ExtendedKillEvent)[]>([]); + onGSI("kill", kill => { + setEvents(ev => [...ev, {...kill, type: 'kill'}]); + }, []); + onGSI("data", data => { + if(data.round && data.round.phase === "freezetime"){ + if(Number(data.phase_countdowns.phase_ends_in) < 10 && events.length > 0){ + setEvents([]); } - }); - - /* - GSI.on("bombPlant", player => { - this.addBombEvent(player, 'plant'); - }) - GSI.on("bombDefuse", player => { - this.addBombEvent(player, 'defuse'); - }) - - */ - - } - render() { - return ( -
- {this.state.events.map(event => )} -
- ); - } + } + }, []); + return ( +
+ {events.map(event => )} +
+ ); } + +export default React.memo(Killfeed); diff --git a/src/HUD/Layout/Layout.tsx b/src/HUD/Layout/Layout.tsx index c5ef70f..7891846 100644 --- a/src/HUD/Layout/Layout.tsx +++ b/src/HUD/Layout/Layout.tsx @@ -1,14 +1,11 @@ -import React from "react"; +import { useState } from "react"; import TeamBox from "./../Players/TeamBox"; import MatchBar from "../MatchBar/MatchBar"; import SeriesBox from "../MatchBar/SeriesBox"; import Observed from "./../Players/Observed"; -import { CSGO, Team } from "csgogsi-socket"; -import { Match } from "../../api/interfaces"; import RadarMaps from "./../Radar/RadarMaps"; import Trivia from "../Trivia/Trivia"; import SideBox from '../SideBoxes/SideBox'; -import { GSI, actions } from "./../../App"; import MoneyBox from '../SideBoxes/Money'; import UtilityLevel from '../SideBoxes/UtilityLevel'; import Killfeed from "../Killfeed/Killfeed"; @@ -17,119 +14,92 @@ import Overview from "../Overview/Overview"; import Tournament from "../Tournament/Tournament"; import Pause from "../PauseTimeout/Pause"; import Timeout from "../PauseTimeout/Timeout"; -import PlayerCamera from "../Camera/Camera"; +import { CSGO } from "csgogsi"; +import { Match } from "../../API/types"; +import { useAction } from "../../API/contexts/actions"; interface Props { game: CSGO, match: Match | null } - +/* interface State { winner: Team | null, showWin: boolean, forceHide: boolean -} +}*/ -export default class Layout extends React.Component { - constructor(props: Props) { - super(props); - this.state = { - winner: null, - showWin: false, - forceHide: false +const Layout = ({game,match}: Props) => { + const [ forceHide, setForceHide ] = useState(false); + + useAction('boxesState', (state) => { + console.log("UPDATE STATE UMC", state); + if (state === "show") { + setForceHide(false); + } else if (state === "hide") { + setForceHide(true); } - } + }); - componentDidMount() { - GSI.on('roundEnd', score => { - this.setState({ winner: score.winner, showWin: true }, () => { - setTimeout(() => { - this.setState({ showWin: false }) - }, 4000) - }); - }); - actions.on("boxesState", (state: string) => { - if (state === "show") { - this.setState({ forceHide: false }); - } else if (state === "hide") { - this.setState({ forceHide: true }); - } - }); - } + const left = game.map.team_ct.orientation === "left" ? game.map.team_ct : game.map.team_t; + const right = game.map.team_ct.orientation === "left" ? game.map.team_t : game.map.team_ct; - getVeto = () => { - const { game, match } = this.props; - const { map } = game; - if (!match) return null; - const mapName = map.name.substring(map.name.lastIndexOf('/') + 1); - const veto = match.vetos.find(veto => veto.mapName === mapName); - if (!veto) return null; - return veto; - } - - render() { - const { game, match } = this.props; - const left = game.map.team_ct.orientation === "left" ? game.map.team_ct : game.map.team_t; - const right = game.map.team_ct.orientation === "left" ? game.map.team_t : game.map.team_ct; - - const leftPlayers = game.players.filter(player => player.team.side === left.side); - const rightPlayers = game.players.filter(player => player.team.side === right.side); - const isFreezetime = (game.round && game.round.phase === "freezetime") || game.phase_countdowns.phase === "freezetime"; - const { forceHide } = this.state; - - return ( -
-
-
Players alive
-
-
{leftPlayers.filter(player => player.state.health > 0).length}
-
VS
-
{rightPlayers.filter(player => player.state.health > 0).length}
-
-
- - - - - - - - - - - - - - - - - - -
- - - player.state.equip_value).reduce((pre, now) => pre + now, 0)} - money={leftPlayers.map(player => player.state.money).reduce((pre, now) => pre + now, 0)} - show={isFreezetime && !forceHide} - /> -
-
- - - player.state.equip_value).reduce((pre, now) => pre + now, 0)} - money={rightPlayers.map(player => player.state.money).reduce((pre, now) => pre + now, 0)} - show={isFreezetime && !forceHide} - /> + const leftPlayers = game.players.filter(player => player.team.side === left.side); + const rightPlayers = game.players.filter(player => player.team.side === right.side); + const isFreezetime = (game.round && game.round.phase === "freezetime") || game.phase_countdowns.phase === "freezetime"; + return ( +
+
+
Players alive
+
+
{leftPlayers.filter(player => player.state.health > 0).length}
+
VS
+
{rightPlayers.filter(player => player.state.health > 0).length}
- ); - } + + + + + + + + + + + + + + + + + + +
+ + + player.state.equip_value).reduce((pre, now) => pre + now, 0)} + money={leftPlayers.map(player => player.state.money).reduce((pre, now) => pre + now, 0)} + show={isFreezetime && !forceHide} + /> +
+
+ + + player.state.equip_value).reduce((pre, now) => pre + now, 0)} + money={rightPlayers.map(player => player.state.money).reduce((pre, now) => pre + now, 0)} + show={isFreezetime && !forceHide} + /> +
+
+ ); } +export default Layout; diff --git a/src/HUD/MapSeries/MapSeries.tsx b/src/HUD/MapSeries/MapSeries.tsx index 5b0f0ad..5c74182 100644 --- a/src/HUD/MapSeries/MapSeries.tsx +++ b/src/HUD/MapSeries/MapSeries.tsx @@ -1,62 +1,66 @@ -import React from "react"; -import * as I from "csgogsi-socket"; -import { Match, Veto } from '../../api/interfaces'; +import * as I from "csgogsi"; import TeamLogo from "../MatchBar/TeamLogo"; import "./mapseries.scss"; +import { Match, Veto } from "../../API/types"; interface IProps { - match: Match | null; - teams: I.Team[]; - isFreezetime: boolean; - map: I.Map + match: Match | null; + teams: I.Team[]; + isFreezetime: boolean; + map: I.Map; } interface IVetoProps { - veto: Veto; - teams: I.Team[]; - active: boolean; + veto: Veto; + teams: I.Team[]; + active: boolean; } -class VetoEntry extends React.Component { - render(){ - const { veto, teams, active } = this.props; - return
-
- {veto.mapName} -
-
- team.id === veto.teamId)[0]} /> -
-
- team.id === veto.winner)[0]} /> -
-
- {Object.values((veto.score || ['-','-'])).sort().join(":")} -
-
-
Currently playing
-
-
- } -} +const VetoEntry = ({ veto, teams, active }: IVetoProps) => { + return ( +
+
+ {veto.mapName} +
+
+ team.id === veto.teamId)[0]} /> +
+
+ team.id === veto.winner)[0]} /> +
+
+ {Object.values(veto.score || ["-", "-"]).sort().join(":")} +
+
+
Currently playing
+
+
+ ); +}; -export default class MapSeries extends React.Component { - - render() { - const { match, teams, isFreezetime, map } = this.props; - if (!match || !match.vetos.length) return null; +const MapSeries = ({ match, teams, isFreezetime, map }: IProps) => { + if (!match || !match.vetos.length) return null; + return ( +
+
+
Picked
+
Winner
+
Score
+
+ {match.vetos.filter((veto) => + veto.type !== "ban" + ).map((veto) => { + if (!veto.mapName) return null; return ( -
-
-
Picked
-
Winner
-
Score
-
- {match.vetos.filter(veto => veto.type !== "ban").map(veto => { - if(!veto.mapName) return null; - return - })} -
+ ); - } -} + })} +
+ ); +}; +export default MapSeries; diff --git a/src/HUD/MatchBar/MatchBar.tsx b/src/HUD/MatchBar/MatchBar.tsx index b6ef43e..5676942 100644 --- a/src/HUD/MatchBar/MatchBar.tsx +++ b/src/HUD/MatchBar/MatchBar.tsx @@ -1,11 +1,10 @@ -import React from "react"; -import * as I from "csgogsi-socket"; +import * as I from "csgogsi"; import "./matchbar.scss"; import TeamScore from "./TeamScore"; import Bomb from "./../Timers/BombTimer"; -import Countdown from "./../Timers/Countdown"; -import { GSI } from "../../App"; -import { Match } from "../../api/interfaces"; +import { useBombTimer } from "./../Timers/Countdown"; +import { Match } from './../../API/types'; + function stringToClock(time: string | number, pad = true) { if (typeof time === "string") { @@ -28,176 +27,49 @@ interface IProps { } export interface Timer { - width: number; + time: number; active: boolean; - countdown: number; side: "left"|"right"; type: "defusing" | "planting"; player: I.Player | null; } - -interface IState { - defusing: Timer, - planting: Timer, - winState: { - side: "left"|"right", - show: boolean +const getRoundLabel = (mapRound: number) => { + const round = mapRound + 1; + if (round <= 30) { + return `Round ${round}/30`; } + const additionalRounds = round - 30; + const OT = Math.ceil(additionalRounds/6); + return `OT ${OT} (${additionalRounds - (OT - 1)*6}/6)`; } -export default class TeamBox extends React.Component { - constructor(props: IProps){ - super(props); - this.state = { - defusing: { - width: 0, - active: false, - countdown: 10, - side: "left", - type: "defusing", - player: null - }, - planting: { - width: 0, - active: false, - countdown: 10, // Fake - side: "right", - type: "planting", - player: null - }, - winState: { - side: 'left', - show: false - } - } - } - plantStop = () => this.setState(state => { - state.planting.active = false; - return state; - }); - - setWidth = (type: 'defusing' | 'planting', width: number) => { - this.setState(state => { - state[type].width = width; - return state; - }) - } - - initPlantTimer = () => { - const bomb = new Countdown(time => { - let width = time * 100; - this.setWidth("planting", width/3); - }); - GSI.on("bombPlantStart", player => { - if(!player || !player.team) return; - this.setState(state => { - state.planting.active = true; - state.planting.side = player.team.orientation; - state.planting.player = player; - }) - }) - GSI.on("data", data => { - if(!data.bomb || !data.bomb.countdown || data.bomb.state !== "planting") return this.plantStop(); - this.setState(state => { - state.planting.active = true; - }) - return bomb.go(data.bomb.countdown); - }); - } - - defuseStop = () => this.setState(state => { - state.defusing.active = false; - state.defusing.countdown = 10; - return state; - }); - - initDefuseTimer = () => { - const bomb = new Countdown(time => { - let width = time > this.state.defusing.countdown ? this.state.defusing.countdown*100 : time * 100; - this.setWidth("defusing", width/this.state.defusing.countdown); - }); - GSI.on("defuseStart", player => { - if(!player || !player.team) return; - this.setState(state => { - state.defusing.active = true; - state.defusing.countdown = !Boolean(player.state.defusekit) ? 10 : 5; - state.defusing.side = player.team.orientation; - state.defusing.player = player; - return state; - }) - }) - GSI.on("data", data => { - if(!data.bomb || !data.bomb.countdown || data.bomb.state !== "defusing") return this.defuseStop(); - this.setState(state => { - state.defusing.active = true; - return state; - }) - return bomb.go(data.bomb.countdown); - }); - } - - resetWin = () => { - setTimeout(() => { - this.setState(state => { - state.winState.show = false; - return state; - }) - }, 6000); - } - - componentDidMount(){ - this.initDefuseTimer(); - this.initPlantTimer(); - GSI.on("roundEnd", score => { - this.setState(state => { - state.winState.show = true; - state.winState.side = score.winner.orientation; - return state; - }, this.resetWin); - }); - } - getRoundLabel = () => { - const { map } = this.props; - const round = map.round + 1; - if (round <= 24) { - return `Round ${round}/24`; - } - const additionalRounds = round - 24; - const OT = Math.ceil(additionalRounds/6); - return `OT ${OT} (${additionalRounds - (OT - 1)*6}/6)`; - } - render() { - const { defusing, planting, winState } = this.state; - const { bomb, match, map, phase } = this.props; +const Matchbar = (props: IProps) => { + const { bomb, match, map, phase } = props; const time = stringToClock(phase.phase_ends_in); const left = map.team_ct.orientation === "left" ? map.team_ct : map.team_t; const right = map.team_ct.orientation === "left" ? map.team_t : map.team_ct; const isPlanted = bomb && (bomb.state === "defusing" || bomb.state === "planted"); const bo = (match && Number(match.matchType.substr(-1))) || 0; - let leftTimer: Timer | null = null, rightTimer: Timer | null = null; - if(defusing.active || planting.active){ - if(defusing.active){ - if(defusing.side === "left") leftTimer = defusing; - else rightTimer = defusing; - } else { - if(planting.side === "left") leftTimer = planting; - else rightTimer = planting; - } - } + + const bombData = useBombTimer(); + const plantTimer: Timer | null = bombData.state === "planting" ? { time:bombData.plantTime, active: true, side: bombData.player?.team.orientation || "right", player: bombData.player, type: "planting"} : null; + const defuseTimer: Timer | null = bombData.state === "defusing" ? { time:bombData.defuseTime, active: true, side: bombData.player?.team.orientation || "left", player: bombData.player, type: "defusing"} : null; + return ( <>
- +
{left.score}
{time}
-
{this.getRoundLabel()}
+
{getRoundLabel(map.round)}
{right.score}
- +
); - } } + +export default Matchbar; diff --git a/src/HUD/MatchBar/SeriesBox.tsx b/src/HUD/MatchBar/SeriesBox.tsx index 47bbdbf..9019813 100644 --- a/src/HUD/MatchBar/SeriesBox.tsx +++ b/src/HUD/MatchBar/SeriesBox.tsx @@ -1,16 +1,12 @@ -import React from "react"; -import * as I from "csgogsi-socket"; -import { Match } from "../../api/interfaces"; +import * as I from "csgogsi"; +import { Match } from "../../API/types"; interface Props { map: I.Map; - phase: I.PhaseRaw; match: Match | null; } -export default class SeriesBox extends React.Component { - render() { - const { match, map } = this.props; +const SeriesBox = ({ map, match }: Props) => { const amountOfMaps = (match && Math.floor(Number(match.matchType.substr(-1)) / 2) + 1) || 0; const bo = (match && Number(match.matchType.substr(-1))) || 0; const left = map.team_ct.orientation === "left" ? map.team_ct : map.team_t; @@ -40,5 +36,6 @@ export default class SeriesBox extends React.Component {
); - } } + +export default SeriesBox; \ No newline at end of file diff --git a/src/HUD/MatchBar/TeamLogo.tsx b/src/HUD/MatchBar/TeamLogo.tsx index 53a40e5..89cc88d 100644 --- a/src/HUD/MatchBar/TeamLogo.tsx +++ b/src/HUD/MatchBar/TeamLogo.tsx @@ -1,11 +1,8 @@ -import React from 'react'; -import { Team } from 'csgogsi-socket'; -import * as I from '../../api/interfaces'; -import { apiUrl } from './../../api/api'; +import { Team } from 'csgogsi'; +import * as I from '../../API/types'; +import { apiUrl } from './../../API'; -export default class TeamLogo extends React.Component<{ team?: Team | I.Team | null, height?: number, width?: number}> { - render(){ - const { team } = this.props; +const TeamLogo = ({team, height, width }: { team?: Team | I.Team | null, height?: number, width?: number}) => { if(!team) return null; let id = ''; const { logo } = team; @@ -16,9 +13,10 @@ export default class TeamLogo extends React.Component<{ team?: Team | I.Team | n } return (
- { logo && id ? {'Team : ''} + { logo && id ? {'Team : ''}
); - } } + +export default TeamLogo; \ No newline at end of file diff --git a/src/HUD/MatchBar/TeamScore.tsx b/src/HUD/MatchBar/TeamScore.tsx index 4e7c718..88eff5c 100644 --- a/src/HUD/MatchBar/TeamScore.tsx +++ b/src/HUD/MatchBar/TeamScore.tsx @@ -1,30 +1,40 @@ -import React from "react"; -import * as I from "csgogsi-socket"; -import WinIndicator from "./WinIndicator"; +import * as I from "csgogsi"; import { Timer } from "./MatchBar"; import TeamLogo from './TeamLogo'; import PlantDefuse from "../Timers/PlantDefuse" +import { onGSI } from "../../API/contexts/actions"; +import WinAnnouncement from "./WinIndicator"; +import { useState } from "react"; interface IProps { - team: I.Team; orientation: "left" | "right"; timer: Timer | null; - showWin: boolean; + team: I.Team; } -export default class TeamScore extends React.Component { - render() { - const { orientation, timer, team, showWin } = this.props; +const TeamScore = ({orientation, timer, team }: IProps) => { + const [ show, setShow ] = useState(false); + + onGSI("roundEnd", result => { + if(result.winner.orientation !== orientation) return; + setShow(true); + + setTimeout(() => { + setShow(false); + }, 5000); + }, [orientation]); + return ( <> -
-
{team.name}
+
+
{team?.name || null}
- + ); - } } + +export default TeamScore; \ No newline at end of file diff --git a/src/HUD/MatchBar/WinIndicator.tsx b/src/HUD/MatchBar/WinIndicator.tsx index 1fd3294..c1d8048 100644 --- a/src/HUD/MatchBar/WinIndicator.tsx +++ b/src/HUD/MatchBar/WinIndicator.tsx @@ -1,12 +1,11 @@ -import React from 'react'; -import { Team } from 'csgogsi-socket'; +import { Team } from 'csgogsi'; -export default class WinAnnouncement extends React.Component<{ team: Team | null, show: boolean }> { - render() { - const { team, show } = this.props; +const WinAnnouncement = ({team, show }: { team: Team | null, show: boolean }) => { if(!team) return null; return
WINS THE ROUND!
- } } + + +export default WinAnnouncement; \ No newline at end of file diff --git a/src/HUD/MatchOverview/MatchOverview.tsx b/src/HUD/MatchOverview/MatchOverview.tsx index b372af0..1838a2d 100644 --- a/src/HUD/MatchOverview/MatchOverview.tsx +++ b/src/HUD/MatchOverview/MatchOverview.tsx @@ -1,41 +1,39 @@ -import React from 'react'; -import * as I from '../../api/interfaces'; -import TeamLogo from '../MatchBar/TeamLogo'; +import * as I from "../../API/types"; +import "./index.scss"; +import TeamLogo from "../MatchBar/TeamLogo"; interface IProps { - match: I.Match, - show: boolean, - teams: I.Team[], - veto: I.Veto | null + match: I.Match; + show: boolean; + teams: I.Team[]; + veto: I.Veto | null; } -export default class MatchOverview extends React.Component { - render() { - const { match, teams, show } = this.props; - const left = teams.find(team => team._id === match.left.id); - const right = teams.find(team => team._id === match.right.id); - if(!match || !left || !right) return null; - return ( -
-
- Upcoming match -
-
-
-
- -
-
{left.name}
-
-
vs
-
-
- -
-
{right.name}
-
-
-
- ); - } -} +const MatchOverview = ({ match, teams, show }: IProps) => { + const left = teams.find((team) => team._id === match.left.id); + const right = teams.find((team) => team._id === match.right.id); + if (!match || !left || !right) return null; + return ( +
+
+ Upcoming match +
+
+
+
+ +
+
{(left.shortName || left.name).substring(0, 4)}
+
+
vs
+
+
+ +
+
{(right.shortName || right.name).substring(0, 4)}
+
+
+
+ ); +}; +export default MatchOverview; diff --git a/src/HUD/TeamOverview/teamoverview.scss b/src/HUD/MatchOverview/index.scss similarity index 99% rename from src/HUD/TeamOverview/teamoverview.scss rename to src/HUD/MatchOverview/index.scss index cf79af9..338d240 100644 --- a/src/HUD/TeamOverview/teamoverview.scss +++ b/src/HUD/MatchOverview/index.scss @@ -39,4 +39,4 @@ flex: unset; background-color: rgba(0,0,0,0.6); font-size: 30px; -} +} \ No newline at end of file diff --git a/src/HUD/Overview/Overview.tsx b/src/HUD/Overview/Overview.tsx index c6031b7..11d2689 100644 --- a/src/HUD/Overview/Overview.tsx +++ b/src/HUD/Overview/Overview.tsx @@ -1,27 +1,10 @@ -import React from 'react'; -import { actions, configs } from '../../App'; -import * as I from '../../api/interfaces'; +import { useState } from 'react'; import PlayerOverview from '../PlayerOverview/PlayerOverview'; import MatchOverview from '../MatchOverview/MatchOverview'; -import TeamOverview from '../TeamOverview/TeamOverview'; -import { Map, Player } from 'csgogsi-socket'; -import api from '../../api/api'; - -interface IState { - player: { - data: I.Player | null, - show: boolean - }, - match: { - data: I.Match | null, - show: boolean, - teams: I.Team[], - }, - team: { - data: I.Team | null, - show: boolean - } -} +import { Map, Player } from 'csgogsi'; +import * as I from './../../API/types'; +import api from './../../API'; +import { useConfig, useOnConfigChange } from '../../API/contexts/actions'; interface IProps { match: I.Match | null, @@ -29,97 +12,30 @@ interface IProps { players: Player[] } -export default class Overview extends React.Component { - constructor(props: IProps){ - super(props); - this.state = { - player: { - data: null, - show: false - }, - match: { - data: null, - show: false, - teams: [] - }, - team: { - data: null, - show: false, - } - } - } - loadTeams = async () => { - const { match } = this.state; - if(!match.data || !match.data.left.id || !match.data.right.id) return; - const teams = await Promise.all([api.teams.getOne(match.data.left.id), api.teams.getOne(match.data.right.id)]); +const Overview = ({ match, map, players }: IProps) => { + const [ teams, setTeams ] = useState([]); + const mapName = map.name.substring(map.name.lastIndexOf('/')+1); + + const previewData = useConfig("preview_settings"); + + useOnConfigChange('preview_settings', async data => { + console.log(data); + if(!data?.match_preview?.match?.left.id || !data?.match_preview?.match?.right.id) return; + + const teams = await Promise.all([api.teams.getOne(data?.match_preview?.match?.left.id), api.teams.getOne(data?.match_preview?.match?.right.id)]); if(!teams[0] || !teams[1]) return; - this.setState(state => { - state.match.teams = teams; - return state; - }); - } - componentDidMount() { - configs.onChange((data: any) => { - if(!data || !data.preview_settings) return; - this.setState({ - player: { - data: (data.preview_settings.player_preview && data.preview_settings.player_preview.player) || null, - show: Boolean(data.preview_settings.player_preview_toggle) - }, - team: { - data: (data.preview_settings.team_preview && data.preview_settings.team_preview.team) || null, - show: Boolean(data.preview_settings.team_preview_toggle) - }, - match: { - data: (data.preview_settings.match_preview && data.preview_settings.match_preview.match) || null, - show: Boolean(data.preview_settings.match_preview_toggle), - teams: this.state.match.teams - } - }, this.loadTeams); - }); - actions.on("toggleUpcomingMatch", () => { - this.setState(state => { - state.match.show = !state.match.show; - return state; - }) - }) - actions.on("togglePlayerPreview", () => { - this.setState(state => { - state.player.show = !state.player.show; - return state; - }) - }) - } - getVeto = () => { - const { map, match } = this.props; - if(!match) return null; - const mapName = map.name.substring(map.name.lastIndexOf('/')+1); - const veto = match.vetos.find(veto => veto.mapName === mapName); - if(!veto) return null; - return veto; - } - renderPlayer = () => { - const { player } = this.state; - if(!player.data) return null; - return - } - renderMatch = () => { - const { match } = this.state; - if(!match.data || !match.teams[0] || !match.teams[1]) return null; - return - } - renderTeam = () => { - const { team } = this.state; - if(!team.data) return null; - return - } - render() { - return ( - <> - {this.renderPlayer()} - {this.renderMatch()} - {this.renderTeam()} - - ); - } + + setTeams(teams); + }, []); + + const playerData = previewData?.player_preview?.player; + const matchData = previewData?.match_preview?.match; + + const veto = match?.vetos.find(veto => veto.mapName === mapName) || null; + return (<> + { playerData ? : null} + { matchData && teams[0] && teams[1] ? : null } + ) } + +export default Overview; \ No newline at end of file diff --git a/src/HUD/PauseTimeout/Pause.tsx b/src/HUD/PauseTimeout/Pause.tsx index 4379547..f3d84d3 100644 --- a/src/HUD/PauseTimeout/Pause.tsx +++ b/src/HUD/PauseTimeout/Pause.tsx @@ -1,17 +1,17 @@ -import React from "react"; -import { PhaseRaw } from "csgogsi-socket"; +import { PhaseRaw } from "csgogsi"; interface IProps { - phase: PhaseRaw | null + phase: PhaseRaw | null; } -export default class Pause extends React.Component { - render() { - const { phase } = this.props; - return ( -
- PAUSE -
- ); - } -} +const Pause = ({ phase }: IProps) => { + return ( +
+ PAUSE +
+ ); +}; +export default Pause; diff --git a/src/HUD/PauseTimeout/Timeout.tsx b/src/HUD/PauseTimeout/Timeout.tsx index bbc0521..c310ae4 100644 --- a/src/HUD/PauseTimeout/Timeout.tsx +++ b/src/HUD/PauseTimeout/Timeout.tsx @@ -1,22 +1,30 @@ -import React from "react"; -import { Map, PhaseRaw } from "csgogsi-socket"; - +import { Map, PhaseRaw } from "csgogsi"; interface IProps { - phase: PhaseRaw | null, - map: Map + phase: PhaseRaw | null; + map: Map; } -export default class Timeout extends React.Component { - render() { - const { phase, map } = this.props; - const time = phase && Math.abs(Math.ceil(parseFloat(phase.phase_ends_in))); - const team = phase && phase.phase === "timeout_t" ? map.team_t : map.team_ct; - - return ( -
2 && phase && (phase.phase === "timeout_t" || phase.phase === "timeout_ct") ? 'show' : ''} ${phase && (phase.phase === "timeout_t" || phase.phase === "timeout_ct") ? phase.phase.substr(8): ''}`}> - { team.name } TIMEOUT -
- ); - } -} +const Timeout = ({ phase, map }: IProps) => { + const time = phase && Math.abs(Math.ceil(parseFloat(phase.phase_ends_in))); + const team = phase && phase.phase === "timeout_t" ? map.team_t : map.team_ct; + + return ( +
2 && phase && + (phase.phase === "timeout_t" || phase.phase === "timeout_ct") + ? "show" + : "" + } ${ + phase && (phase.phase === "timeout_t" || phase.phase === "timeout_ct") + ? phase.phase.substring(8) + : "" + }`} + > + {team.name} TIMEOUT +
+ ); +}; +export default Timeout; diff --git a/src/HUD/PlayerOverview/PlayerOverview.tsx b/src/HUD/PlayerOverview/PlayerOverview.tsx index 133934c..5704f6a 100644 --- a/src/HUD/PlayerOverview/PlayerOverview.tsx +++ b/src/HUD/PlayerOverview/PlayerOverview.tsx @@ -1,10 +1,8 @@ -import React from 'react'; -import * as I from '../../api/interfaces'; -import { avatars } from './../../api/avatars'; -import { apiUrl } from '../../api/api'; +import * as I from '../../API/types'; import { getCountry } from '../countries'; -import { Player } from 'csgogsi-socket'; +import { Player } from 'csgogsi'; import "./playeroverview.scss"; +import { apiUrl } from '../../API'; interface IProps { player: I.Player, @@ -14,21 +12,34 @@ interface IProps { round: number } -export default class PlayerOverview extends React.Component { - sum = (data: number[]) => data.reduce((a, b) => a + b, 0); +const sum = (data: number[]) => data.reduce((a, b) => a + b, 0); - getData = () => { - const { veto, player, round } = this.props; - if(!player || !veto || !veto.rounds) return null; +const calcWidth = (val: number | string, max?: number) => { + const value = Number(val); + if(value === 0) return 0; + let maximum = max; + if(!maximum) { + maximum = Math.ceil(value/100)*100; + } + if(value > maximum){ + return 100; + } + return 100*value/maximum; +} + +const PlayerOverview = ({ player, show, veto, players, round }: IProps) => { + if(!player || !veto || !veto.rounds) return null; + const getData = () => { + if(!veto.rounds) return null; const stats = veto.rounds.map(round => round ? round.players[player.steamid] : { kills: 0, killshs: 0, damage: 0 }).filter(data => !!data); const overall = { - damage: this.sum(stats.map(round => round.damage)), - kills: this.sum(stats.map(round => round.kills)), - killshs: this.sum(stats.map(round => round.killshs)), + damage: sum(stats.map(round => round.damage)), + kills: sum(stats.map(round => round.kills)), + killshs: sum(stats.map(round => round.killshs)), }; const data = { adr: stats.length !== 0 ? (overall.damage/(round-1)).toFixed(0) : '0', @@ -39,66 +50,51 @@ export default class PlayerOverview extends React.Component { } return data; } - calcWidth = (val: number | string, max?: number) => { - const value = Number(val); - if(value === 0) return 0; - let maximum = max; - if(!maximum) { - maximum = Math.ceil(value/100)*100; - } - if(value > maximum){ - return 100; - } - return 100*value/maximum; - } - render() { - const { player, veto, players } = this.props; - const data = this.getData(); - if(!player || !veto || !veto.rounds || !data) return null; - let url = null; - // const avatarData = avatars.find(avatar => avatar.steamid === player.steamid); - const avatarData = avatars[player.steamid]; - if(avatarData && avatarData.url){ - url = avatarData.url; - } - const countryName = player.country ? getCountry(player.country) : null; - let side = ''; - const inGamePlayer = players.find(inGamePlayer => inGamePlayer.steamid === player.steamid); - if(inGamePlayer) side = inGamePlayer.team.side; - return ( -
-
- {url ? {`${player.username}'s : null} -
-
{url && countryName ? {countryName}/ : null }{player.username.toUpperCase()}
-
-
-
KILLS: {data.kills}
-
-
-
+ const data = getData(); + if(!data) return null; + const url = player.avatar; + + const countryName = player.country ? getCountry(player.country) : null; + let side = ''; + const inGamePlayer = players.find(inGamePlayer => inGamePlayer.steamid === player.steamid); + if(inGamePlayer) side = inGamePlayer.team.side; + return ( +
+
+ {url ? {`${player.username}'s : null} +
+
{url && countryName ? {countryName}/ : null }{player.username.toUpperCase()}
+ +
+
+
KILLS: {data.kills}
+
+
-
-
HS: {data.hsp}%
-
-
-
+
+
+
HS: {data.hsp}%
+
+
-
-
ADR: {data.adr}
-
-
-
+
+
+
ADR: {data.adr}
+
+
-
-
KPR: {data.kpr}
-
-
-
+
+
+
KPR: {data.kpr}
+
+
- ); - } +
+ ); + } + +export default PlayerOverview; \ No newline at end of file diff --git a/src/HUD/Players/Avatar.tsx b/src/HUD/Players/Avatar.tsx index c4be7d5..71f36b5 100644 --- a/src/HUD/Players/Avatar.tsx +++ b/src/HUD/Players/Avatar.tsx @@ -1,70 +1,50 @@ -import React from 'react'; -import CameraContainer from '../Camera/Container'; +import CameraContainer from "../Camera/Container"; import PlayerCamera from "./../Camera/Camera"; -import { avatars } from './../../api/avatars'; - -import { Skull } from './../../assets/Icons'; -import { configs } from '../../App'; -import { apiUrl } from '../../api/api'; +import { Skull } from "./../../assets/Icons"; +import { useConfig } from "../../API/contexts/actions"; +import { apiUrl } from "../../API"; interface IProps { - steamid: string, - teamId?: string | null, - slot?: number, - height?: number, - width?: number, - showSkull?: boolean, - showCam?: boolean, - sidePlayer?: boolean + steamid: string; + url: string | null; + slot?: number; + height?: number; + width?: number; + showSkull?: boolean; + showCam?: boolean; + sidePlayer?: boolean; + teamId?: string | null } -export default class Avatar extends React.Component { - constructor(props: IProps){ - super(props); - this.state = { - replaceAvatar: 'never' - } - } - componentDidMount() { - const onDataChange = (data:any) => { - if(!data) return; - const display = data.display_settings; - if(!display) return; - this.setState({ - replaceAvatar: display.replace_avatars || 'never' - }) - }; - configs.onChange(onDataChange); - onDataChange(configs.data); - } - getAvatarUrl = () => { - const avatarData = avatars[this.props.steamid] && avatars[this.props.steamid].url ? avatars[this.props.steamid].url : null; +const Avatar = ( + { steamid, url, height, width, showCam, showSkull, sidePlayer, teamId }: IProps, +) => { + const data = useConfig("display_settings"); - if(this.state.replaceAvatar === 'always' || (this.state.replaceAvatar === 'if_missing' && !avatarData)){ - return this.props.teamId ? `${apiUrl}api/teams/logo/${this.props.teamId}` : avatarData || null; - } - return avatarData || null; - - } - render() { - const { showCam, steamid, width, height, showSkull, sidePlayer } = this.props; - //const url = avatars.filter(avatar => avatar.steamid === this.props.steamid)[0]; - const avatarUrl = this.getAvatarUrl(); - if (!avatarUrl) { - return null; - } - - return ( -
- { - showCam ? ( sidePlayer ?
: ) : null - } - { - showSkull ? : {'Avatar'} - } - -
- ); - } - -} + const avatarUrl = teamId && (data?.replace_avatars === "always" || (data?.replace_avatars === "if_missing" && !url)) ? `${apiUrl}api/teams/logo/${teamId}` : url; + if(!avatarUrl && !showCam) return null; + return ( +
+ {showCam + ? (sidePlayer + ? ( +
+ +
+ ) + : ) + : null} + {showSkull + ? + : ( + avatarUrl ? {"Avatar"} : null + )} +
+ ); +}; +export default Avatar; \ No newline at end of file diff --git a/src/HUD/Players/Observed.tsx b/src/HUD/Players/Observed.tsx index 1810d2a..9c308a2 100644 --- a/src/HUD/Players/Observed.tsx +++ b/src/HUD/Players/Observed.tsx @@ -1,106 +1,86 @@ -import React from "react"; -import { Player } from "csgogsi-socket"; +import React, { useState } from "react"; +import { Player } from "csgogsi"; import Weapon from "./../Weapon/Weapon"; import Avatar from "./Avatar"; import TeamLogo from "./../MatchBar/TeamLogo"; import "./observed.scss"; -import { apiUrl } from './../../api/api'; import { getCountry } from "./../countries"; import { ArmorHelmet, ArmorFull, HealthFull, Bullets } from './../../assets/Icons'; -import { Veto } from "../../api/interfaces"; -import { actions } from "../../App"; +import { apiUrl } from './../../API'; +import { useAction } from "../../API/contexts/actions"; -class Statistic extends React.PureComponent<{ label: string; value: string | number, }> { - render() { - return ( -
-
{this.props.label}
-
{this.props.value}
-
- ); - } -} -export default class Observed extends React.Component<{ player: Player | null, veto: Veto | null, round: number }, { showCam: boolean }> { - constructor(props: any){ - super(props); - this.state = { - showCam: true - } - } - componentDidMount() { - actions.on('toggleCams', () => { - console.log(this.state.showCam) - this.setState({ showCam: !this.state.showCam }); - }); - } - getAdr = () => { - const { veto, player } = this.props; - if (!player || !veto || !veto.rounds) return null; - const damageInRounds = veto.rounds.map(round => round ? round.players[player.steamid] : { - kills: 0, - killshs: 0, - damage: 0 - }).filter(data => !!data).map(roundData => roundData.damage); - - return damageInRounds.reduce((a, b) => a + b, 0) / (this.props.round - 1); - } - render() { - if (!this.props.player) return ''; - const { player } = this.props; - const country = player.country || player.team.country; - const weapons = Object.values(player.weapons).map(weapon => ({ ...weapon, name: weapon.name.replace("weapon_", "") })); - const currentWeapon = weapons.filter(weapon => weapon.state === "active")[0]; - const grenades = weapons.filter(weapon => weapon.type === "Grenade"); - const { stats } = player; - const ratio = stats.deaths === 0 ? stats.kills : stats.kills / stats.deaths; - const countryName = country ? getCountry(country) : null; - return ( -
-
- {} - -
-
{player.name}
-
{player.realName}
-
-
{countryName ? {countryName} : ''}
-
- {grenades.map(grenade => - - { - grenade.ammo_reserve === 2 ? : null} - )} -
+const Statistic = React.memo(({ label, value }: { label: string; value: string | number, }) => { + return ( +
+
{label}
+
{value}
+
+ ); +}); + +const Observed = ({ player }: { player: Player | null }) => { + const [ showCam, setShowCam ] = useState(true); + + useAction('toggleCams', () => { + setShowCam(p => !p); + }); + + if (!player) return null; + + const country = player.country || player.team.country; + const currentWeapon = player.weapons.filter(weapon => weapon.state === "active")[0]; + const grenades = player.weapons.filter(weapon => weapon.type === "Grenade"); + const { stats } = player; + const ratio = stats.deaths === 0 ? stats.kills : stats.kills / stats.deaths; + const countryName = country ? getCountry(country) : null; + return ( +
+
+ + +
+
{player.name}
+
{player.realName}
-
-
-
- -
-
{player.state.health}
-
- {player.state.helmet ? : } -
-
{player.state.armor}
+
{countryName ? {countryName} : ''}
+
+ {grenades.map(grenade => + + { + grenade.ammo_reserve === 2 ? : null} + )} +
+
+
+
+
+
-
- - - - +
{player.state.health}
+
+ {player.state.helmet ? : }
-
-
- -
-
-
{(currentWeapon && currentWeapon.ammo_clip) || "-"}
-
/{(currentWeapon && currentWeapon.ammo_reserve) || "-"}
-
+
{player.state.armor}
+
+
+ + + + +
+
+
+ +
+
+
{(currentWeapon && currentWeapon.ammo_clip) || "-"}
+
/{(currentWeapon && currentWeapon.ammo_reserve) || "-"}
- ); - } +
+ ); } + +export default Observed; \ No newline at end of file diff --git a/src/HUD/Players/Player.tsx b/src/HUD/Players/Player.tsx index 62903a9..fc31275 100644 --- a/src/HUD/Players/Player.tsx +++ b/src/HUD/Players/Player.tsx @@ -1,10 +1,10 @@ -import React from "react"; -import * as I from "csgogsi-socket"; +import * as I from "csgogsi"; import Weapon from "./../Weapon/Weapon"; import Avatar from "./Avatar"; import Armor from "./../Indicators/Armor"; import Bomb from "./../Indicators/Bomb"; import Defuse from "./../Indicators/Defuse"; +import React from "react"; interface IProps { player: I.Player, @@ -24,9 +24,9 @@ const compareWeapon = (weaponOne: I.WeaponRaw, weaponTwo: I.WeaponRaw) => { return false; } -const compareWeapons = (weaponsObjectOne: { [key: string]: I.WeaponRaw }, weaponsObjectTwo: { [key: string]: I.WeaponRaw }) => { - const weaponsOne = Object.values(weaponsObjectOne).sort((a, b) => (a.name as any) - (b.name as any)) - const weaponsTwo = Object.values(weaponsObjectTwo).sort((a, b) => (a.name as any) - (b.name as any)) +const compareWeapons = (weaponsObjectOne: I.Weapon[], weaponsObjectTwo: I.Weapon[]) => { + const weaponsOne = [...weaponsObjectOne].sort((a, b) => a.name.localeCompare(b.name)) + const weaponsTwo = [...weaponsObjectTwo].sort((a, b) => a.name.localeCompare(b.name)) if (weaponsOne.length !== weaponsTwo.length) return false; @@ -58,6 +58,8 @@ const arePlayersEqual = (playerOne: I.Player, playerTwo: I.Player) => { playerOne.state.equip_value === playerTwo.state.equip_value && playerOne.state.adr === playerTwo.state.adr && playerOne.avatar === playerTwo.avatar && + !!playerOne.team.id === !!playerTwo.team.id && + playerOne.team.side === playerTwo.team.side && playerOne.country === playerTwo.country && playerOne.realName === playerTwo.realName && compareWeapons(playerOne.weapons, playerTwo.weapons) @@ -65,7 +67,6 @@ const arePlayersEqual = (playerOne: I.Player, playerTwo: I.Player) => { return false; } - const Player = ({ player, isObserved }: IProps) => { const weapons = Object.values(player.weapons).map(weapon => ({ ...weapon, name: weapon.name.replace("weapon_", "") })); @@ -76,7 +77,7 @@ const Player = ({ player, isObserved }: IProps) => { return (
- +
K
@@ -104,7 +105,7 @@ const Player = ({ player, isObserved }: IProps) => {
- +
${player.state.money}
@@ -131,5 +132,5 @@ const arePropsEqual = (prevProps: Readonly, nextProps: Readonly) return arePlayersEqual(prevProps.player, nextProps.player); } -//export default React.memo(Player, arePropsEqual); -export default Player; +export default React.memo(Player, arePropsEqual); +//export default Player; diff --git a/src/HUD/Players/TeamBox.tsx b/src/HUD/Players/TeamBox.tsx index fbd548d..721c901 100644 --- a/src/HUD/Players/TeamBox.tsx +++ b/src/HUD/Players/TeamBox.tsx @@ -1,6 +1,5 @@ -import React from 'react'; import Player from './Player' -import * as I from 'csgogsi-socket'; +import * as I from 'csgogsi'; import './players.scss'; interface Props { @@ -9,17 +8,15 @@ interface Props { side: 'right' | 'left', current: I.Player | null, } - -export default class TeamBox extends React.Component { - render() { - return ( -
- {this.props.players.map(player => )} -
- ); - } +const TeamBox = ({players, team, side, current}: Props) => { + return ( +
+ {players.map(player => )} +
+ ); } +export default TeamBox; \ No newline at end of file diff --git a/src/HUD/Radar/LexoRadar/LexoRadar.tsx b/src/HUD/Radar/LexoRadar/LexoRadar.tsx index 8473520..314b12d 100644 --- a/src/HUD/Radar/LexoRadar/LexoRadar.tsx +++ b/src/HUD/Radar/LexoRadar/LexoRadar.tsx @@ -1,9 +1,9 @@ -import React from 'react'; -import { Bomb } from 'csgogsi-socket'; -import maps, { ScaleConfig, MapConfig, ZoomAreas } from './maps'; -import './index.css'; +import type { Bomb, Side } from 'csgogsi'; +import maps, { MapConfig, ZoomAreas } from './maps'; +import './index.scss'; import { RadarPlayerObject, RadarGrenadeObject } from './interface'; import config from './config'; +import { parsePosition } from './utils'; interface IProps { players: RadarPlayerObject[]; grenades: RadarGrenadeObject[]; @@ -12,99 +12,154 @@ interface IProps { zoom?: ZoomAreas; mapConfig: MapConfig, reverseZoom: string, - parsePosition: (position: number[], size: number, config: ScaleConfig) => number[] +} +const isShooting = (lastShoot: number) => (new Date()).getTime() - lastShoot <= 250; + +type Explosion = { + position: number[], + grenadeId: string } -const isShooting = (lastShoot: number) => (new Date()).getTime() - lastShoot <= 250; -class App extends React.Component { - constructor(props: IProps) { - super(props); - this.state = { - players: [], - grenades: [], - bomb: null - } +/* +const SMOKE_PARTICLE_BASE_AMOUNT = 10; +//const PARTICLE_MAP_SOURCE = Array(SMOKE_PARTICLE_BASE_AMOUNT**2).fill(0); + +const SMOKE_PARTICLE_SIZE_ABSOLUT = config.smokeSize/SMOKE_PARTICLE_BASE_AMOUNT; + +const PARTICLE_SIDE = `${SMOKE_PARTICLE_SIZE_ABSOLUT}px`; + + + + +const FRAG_RADIUS = 40; +//const MAX_DISTANCE_BETWEEN_FRAG_AND_SMOKE = config.smokeSize + FRAG_RADIUS; + +const getDistance = (X: number[], Y: number[]) => { + const a = X[0] - Y[0]; + const b = X[1] - Y[1]; + + return Math.sqrt( a*a + b*b ); +} + +const getPosOfIndex = (index: number, origin: number[]) => { + const leftI = index%10; + const topI = Math.floor(index/10); + return ([origin[0] - config.smokeSize/2 + (leftI + 0.5) * SMOKE_PARTICLE_SIZE_ABSOLUT, origin[1] - config.smokeSize/2 + (topI + 0.5)*SMOKE_PARTICLE_SIZE_ABSOLUT]); +} + +const Particle = ({ index, explosions, origin }: { index: number, origin: number[], explosions: Explosion[] }) => { + const PARTICLE_POSITION = getPosOfIndex(index, origin); + const isHidden = getDistance(PARTICLE_POSITION, origin) > config.smokeSize/2 || explosions.some(expl => getDistance(expl.position, PARTICLE_POSITION) <= FRAG_RADIUS); + return (
); +} +*/ + +const Grenade = ({ reverseZoom, type, state, visible, position, flames, side }: { explosions: Explosion[], reverseZoom: string, side: Side | null, flames: boolean, type: RadarGrenadeObject["type"], state: RadarGrenadeObject["state"], visible: boolean, position: number[] }) => { + if (flames) { + return null; } - renderGrenade = (grenade: RadarGrenadeObject) => { - if ("flames" in grenade) { - return null; - } - const { reverseZoom } = this.props; + if(type === "smoke" && (state === "landed" || state === "exploded")){ return ( -
+
-
+
+
+
) } - renderDot = (player: RadarPlayerObject) => { - const { reverseZoom } = this.props; + return ( +
+
+
+
+
+
+ ) +} + +const Bomb = ({ bomb, mapConfig, reverseZoom }: { reverseZoom: string, bomb?: Bomb | null, mapConfig: MapConfig }) => { + if(!bomb) return null; + if(bomb.state === "carried" || bomb.state === "planting") return null; + if("config" in mapConfig){ + const position = parsePosition(bomb.position, mapConfig.config); + if(!position) return null; + return ( -
+
+
+
+
+
+ ) + } + return mapConfig.configs.map(config => { + const position = parsePosition(bomb.position, config.config); + if(!position) return null; + return ( +
+
+
+
+
+
+ ) + }); +} + +const PlayerDot = ({ player, reverseZoom }: { reverseZoom: string, player: RadarPlayerObject }) => { + const isShootingNow = isShooting(player.lastShoot); + //console.log('x',isShooting(player.lastShoot), player.steamid); + return ( +
+
-
-
-
{player.label}
-
- ) - } - renderBomb = () => { - const { bomb, mapConfig, reverseZoom } = this.props; - if(!bomb) return null; - if(bomb.state === "carried" || bomb.state === "planting") return null; - if("config" in mapConfig){ - const position = this.props.parsePosition(bomb.position, 30, mapConfig.config); - if(!position) return null; - - return ( -
-
-
+
+
+
{player.label}
- ) - } - return mapConfig.configs.map(config => { - const position = this.props.parsePosition(bomb.position, 30, config.config); - if(!position) return null; - return ( -
-
-
-
- ) - }); - } - render() { - const { players, grenades, zoom } = this.props; +
+ ) +} - const style: React.CSSProperties = { backgroundImage: `url(${maps[this.props.mapName].file})` } + +const Radar = ({ players, grenades, mapConfig, bomb, mapName, zoom, reverseZoom }: IProps) => { + //if(players.length === 0) return null; + + const style: React.CSSProperties = { backgroundImage: `url(${maps[mapName].file})` } if(zoom){ style.transform = `scale(${zoom.zoom})`; style.transformOrigin = `${zoom.origin[0]}px ${zoom.origin[1]}px`; } - //if(players.length === 0) return null; + const explosions = grenades.filter(grenade => grenade.type === "frag" && grenade.state === "exploded"); return
- {players.map(this.renderDot)} - {grenades.map(this.renderGrenade)} - {this.renderBomb()} + {players.map(player => )} + {grenades.map(grenade => ({ position: g.position, grenadeId: g.id }))} reverseZoom={reverseZoom} side={grenade.side} key={grenade.id} type={grenade.type} state={grenade.state} visible={grenade.visible} position={grenade.position} flames={"flames" in grenade} />)} +
; - } } -export default App; +export default Radar; diff --git a/src/HUD/Radar/LexoRadar/LexoRadarContainer.tsx b/src/HUD/Radar/LexoRadar/LexoRadarContainer.tsx index 4468d67..26c5175 100644 --- a/src/HUD/Radar/LexoRadar/LexoRadarContainer.tsx +++ b/src/HUD/Radar/LexoRadar/LexoRadarContainer.tsx @@ -1,322 +1,79 @@ -import React from 'react'; -import { Player, Bomb } from 'csgogsi-socket'; -import maps, { ScaleConfig } from './maps'; +import { Player, Bomb, Grenade, FragOrFireBombOrFlashbandGrenade } from 'csgogsi'; +import maps from './maps'; import LexoRadar from './LexoRadar'; -import { ExtendedGrenade, Grenade, RadarPlayerObject, RadarGrenadeObject } from './interface'; -import config from './config'; +import { RadarPlayerObject, RadarGrenadeObject } from './interface'; +import { EXPLODE_TIME_FRAG, explosionPlaces, extendGrenade, extendPlayer, grenadesStates, playersStates } from './utils'; +import { GSI } from '../../../API/HUD'; const DESCALE_ON_ZOOM = true; - - -let playersStates: Player[][] = []; -let grenadesStates: ExtendedGrenade[][] = []; -const directions: Record = {}; -type ShootingState = { - ammo: number, - weapon: string, - lastShoot: number -} -let shootingState: Record = {}; - -const calculateDirection = (player: Player) => { - if (directions[player.steamid] && !player.state.health) return directions[player.steamid]; - - const [forwardV1, forwardV2] = player.forward; - let direction = 0; - - const [axisA, axisB] = [Math.asin(forwardV1), Math.acos(forwardV2)].map(axis => axis * 180 / Math.PI); - - if (axisB < 45) { - direction = Math.abs(axisA); - } else if (axisB > 135) { - direction = 180 - Math.abs(axisA); - } else { - direction = axisB; - } - - if (axisA < 0) { - direction = -(direction -= 360); - } - - if (!directions[player.steamid]) { - directions[player.steamid] = direction; - } - - const previous = directions[player.steamid]; - - let modifier = previous; - modifier -= 360 * Math.floor(previous / 360); - modifier = -(modifier -= direction); - - if (Math.abs(modifier) > 180) { - modifier -= 360 * Math.abs(modifier) / modifier; - } - directions[player.steamid] += modifier; - - return directions[player.steamid]; -} - interface IProps { players: Player[], bomb?: Bomb | null, player: Player | null, - grenades?: any + grenades: Grenade[] size?: number, mapName: string } -class App extends React.Component { - round = (n: number) => { - const r = 0.02; - return Math.round(n / r) * r; +GSI.prependListener("data", () => { + + const currentGrenades = GSI.current?.grenades || [] + grenadesStates.unshift(currentGrenades); + grenadesStates.splice(5); + + playersStates.unshift(GSI.current?.players || []); + playersStates.splice(5); +}); + +GSI.prependListener("data", data => { + const { last } = GSI; + if(!last) return; + + for(const grenade of data.grenades.filter((grenade): grenade is FragOrFireBombOrFlashbandGrenade => grenade.type === "frag")){ + const old = last.grenades.find((oldGrenade): oldGrenade is FragOrFireBombOrFlashbandGrenade => oldGrenade.id === grenade.id); + if(!old) continue; + + if(grenade.lifetime >= EXPLODE_TIME_FRAG && old.lifetime < EXPLODE_TIME_FRAG){ + explosionPlaces[grenade.id] = grenade.position; + } } - - parsePosition = (position: number[], size: number, config: ScaleConfig) => { - if (!(this.props.mapName in maps)) { - return [0, 0]; + for(const grenadeId of Object.keys(explosionPlaces)){ + const doesExist = data.grenades.some(grenade => grenade.id === grenadeId); + if(!doesExist){ + delete explosionPlaces[grenadeId]; } - const left = config.origin.x + (position[0] * config.pxPerUX) - (size / 2); - const top = config.origin.y + (position[1] * config.pxPerUY) - (size / 2); - - return [this.round(left), this.round(top)]; } +}); - parseGrenadePosition = (grenade: ExtendedGrenade, config: ScaleConfig) => { - if (!("position" in grenade)) { - return null; - } - let size = 30; - if (grenade.type === "smoke") { - size = 60; - } - return this.parsePosition(grenade.position.split(", ").map(pos => Number(pos)), size, config); - } - getGrenadePosition = (grenade: ExtendedGrenade, config: ScaleConfig) => { - const grenadeData = grenadesStates.slice(0, 5).map(grenades => grenades.filter(gr => gr.id === grenade.id)[0]).filter(pl => !!pl); - if (grenadeData.length === 0) return null; - const positions = grenadeData.map(grenadeEntry => this.parseGrenadePosition(grenadeEntry, config)).filter(posData => posData !== null) as number[][]; - if (positions.length === 0) return null; - const entryAmount = positions.length; - let x = 0; - let y = 0; - for (const position of positions) { - x += position[0]; - y += position[1]; - } +const LexoRadarContainer = ({ size = 300, mapName, bomb, player, players, grenades }: IProps) => { + const offset = (size - (size * size / 1024)) / 2; - return [x / entryAmount, y / entryAmount]; - } - getPosition = (player: Player, mapConfig: ScaleConfig, scale: number) => { - const playerData = playersStates.slice(0, 5).map(players => players.filter(pl => pl.steamid === player.steamid)[0]).filter(pl => !!pl); - if (playerData.length === 0) return [0, 0]; - const positions = playerData.map(playerEntry => this.parsePosition(playerEntry.position, config.playerSize * scale, mapConfig)); - const entryAmount = positions.length; - let x = 0; - let y = 0; - for (const position of positions) { - x += position[0]; - y += position[1]; - } - - const degree = calculateDirection(player); - return [x / entryAmount, y / entryAmount, degree]; - } - mapPlayer = (active: Player | null) => (player: Player): RadarPlayerObject | RadarPlayerObject[] | null => { - if (!(this.props.mapName in maps)) { - return null; - } - - const weapons = player.weapons ? Object.values(player.weapons) : []; - const weapon = weapons.find(weapon => weapon.state === "active" && weapon.type !== "C4" && weapon.type !== "Knife" && weapon.type !== "Grenade"); - - const shooting: ShootingState = { ammo: weapon && weapon.ammo_clip || 0, weapon: weapon && weapon.name || '', lastShoot: 0 }; - - const lastShoot = shootingState[player.steamid] || shooting; - - let isShooting = false; - - if (shooting.weapon === lastShoot.weapon && shooting.ammo < lastShoot.ammo) { - isShooting = true; - } - - shooting.lastShoot = isShooting ? (new Date()).getTime() : lastShoot.lastShoot; - - shootingState[player.steamid] = shooting; - - - const map = maps[this.props.mapName]; - const playerObject: RadarPlayerObject = { - id: player.steamid, - label: player.observer_slot !== undefined ? player.observer_slot : "", - side: player.team.side, - position: [], - visible: true, - isActive: !!active && active.steamid === player.steamid, - forward: 0, - steamid: player.steamid, - isAlive: player.state.health > 0, - hasBomb: !!Object.values(player.weapons).find(weapon => weapon.type === "C4"), - flashed: player.state.flashed > 35, - shooting: isShooting, - lastShoot: shooting.lastShoot, - scale: 1, - player - } - if ("config" in map) { - const scale = map.config.originHeight === undefined ? 1 : (1 + (player.position[2] - map.config.originHeight) / 1000); - - playerObject.scale = scale; - - const position = this.getPosition(player, map.config, scale); - playerObject.position = position; - - return playerObject; - } - return map.configs.map(config => { - const scale = config.config.originHeight === undefined ? 1 : (1 + (player.position[2] - config.config.originHeight) / 750); - - playerObject.scale = scale; - - return ({ - ...playerObject, - position: this.getPosition(player, config.config, scale), - id: `${player.steamid}_${config.id}`, - visible: config.isVisible(player.position[2]) - }) - }); - } - mapGrenade = (extGrenade: ExtendedGrenade) => { - if (!(this.props.mapName in maps)) { - return null; - } - const map = maps[this.props.mapName]; - if (extGrenade.type === "inferno") { - const mapFlame = (id: string) => { - if ("config" in map) { - return ({ - position: this.parsePosition(extGrenade.flames[id].split(", ").map(pos => Number(pos)), 12, map.config), - id: `${id}_${extGrenade.id}`, - visible: true - }); - } - return map.configs.map(config => ({ - id: `${id}_${extGrenade.id}_${config.id}`, - visible: config.isVisible(extGrenade.flames[id].split(", ").map(Number)[2]), - position: this.parsePosition(extGrenade.flames[id].split(", ").map(pos => Number(pos)), 12, config.config) - })); - } - const flames = Object.keys(extGrenade.flames).map(mapFlame).flat(); - const flameObjects: RadarGrenadeObject[] = flames.map(flame => ({ - ...flame, - side: extGrenade.side, - type: 'inferno', - state: 'landed' - })); - return flameObjects; - } - - if ("config" in map) { - const position = this.getGrenadePosition(extGrenade, map.config); - if (!position) return null; - const grenadeObject: RadarGrenadeObject = { - type: extGrenade.type, - state: 'inair', - side: extGrenade.side, - position, - id: extGrenade.id, - visible: true - } - if (extGrenade.type === "smoke") { - if (extGrenade.effecttime !== "0.0") { - grenadeObject.state = "landed"; - if (Number(extGrenade.effecttime) >= 16.5) { - grenadeObject.state = 'exploded'; - } - } - } else if (extGrenade.type === 'flashbang' || extGrenade.type === 'frag') { - if (Number(extGrenade.lifetime) >= 1.25) { - grenadeObject.state = 'exploded'; - } - } - return grenadeObject; - } - return map.configs.map(config => { - const position = this.getGrenadePosition(extGrenade, config.config); - if (!position) return null; - const grenadeObject: RadarGrenadeObject = { - type: extGrenade.type, - state: 'inair', - side: extGrenade.side, - position, - id: `${extGrenade.id}_${config.id}`, - visible: config.isVisible(extGrenade.position.split(", ").map(Number)[2]) - } - if (extGrenade.type === "smoke") { - if (extGrenade.effecttime !== "0.0") { - grenadeObject.state = "landed"; - if (Number(extGrenade.effecttime) >= 16.5) { - grenadeObject.state = 'exploded'; - } - } - } else if (extGrenade.type === 'flashbang' || extGrenade.type === 'frag') { - if (Number(extGrenade.lifetime) >= 1.25) { - grenadeObject.state = 'exploded'; - } - } - return grenadeObject; - }).filter((grenade): grenade is RadarGrenadeObject => grenade !== null); - - } - getSideOfGrenade = (grenade: Grenade) => { - const owner = this.props.players.find(player => player.steamid === grenade.owner); - if (!owner) return null; - return owner.team.side; - } - render() { - const players: RadarPlayerObject[] = this.props.players.map(this.mapPlayer(this.props.player)).filter((player): player is RadarPlayerObject => player !== null).flat(); - playersStates.unshift(this.props.players); - if (playersStates.length > 5) { - playersStates = playersStates.slice(0, 5); - } - let grenades: RadarGrenadeObject[] = []; - const currentGrenades = Object.keys(this.props.grenades as { [key: string]: Grenade }).map(grenadeId => ({ ...this.props.grenades[grenadeId], id: grenadeId, side: this.getSideOfGrenade(this.props.grenades[grenadeId]) })) as ExtendedGrenade[]; - if (currentGrenades) { - grenades = currentGrenades.map(this.mapGrenade).filter(entry => entry !== null).flat() as RadarGrenadeObject[]; - grenadesStates.unshift(currentGrenades); - } - if (grenadesStates.length > 5) { - grenadesStates = grenadesStates.slice(0, 5); - } - const size = this.props.size || 300; - const offset = (size - (size * size / 1024)) / 2; - - const config = maps[this.props.mapName]; - - const zooms = config && config.zooms || []; - - const activeZoom = zooms.find(zoom => zoom.threshold(players.map(pl => pl.player))); - - const reverseZoom = 1/(activeZoom && activeZoom.zoom || 1); - - // s*(1024-s)/2048 - if (!(this.props.mapName in maps)) { - return
- Unsupported map -
; - } + if (!(mapName in maps)) { return
- + Unsupported map
; } + const playersExtended: RadarPlayerObject[] = players.map(pl => extendPlayer({ player: pl, steamId: player?.steamid || null, mapName })).filter((player): player is RadarPlayerObject => player !== null).flat(); + const grenadesExtended = grenades.map(grenade => extendGrenade({ grenade, side: playersExtended.find(player => player.steamid === grenade.owner)?.side || 'CT', mapName })).filter(entry => entry !== null).flat() as RadarGrenadeObject[]; + const config = maps[mapName]; + + const zooms = config && config.zooms || []; + + const activeZoom = zooms.find(zoom => zoom.threshold(playersExtended.map(pl => pl.player))); + + const reverseZoom = 1/(activeZoom && activeZoom.zoom || 1); + // s*(1024-s)/2048 + return
+ +
; } -export default App; +export default LexoRadarContainer; diff --git a/src/HUD/Radar/LexoRadar/assets/shootFire.zip b/src/HUD/Radar/LexoRadar/assets/shootFire.zip deleted file mode 100644 index 2d4524b..0000000 Binary files a/src/HUD/Radar/LexoRadar/assets/shootFire.zip and /dev/null differ diff --git a/src/HUD/Radar/LexoRadar/config.ts b/src/HUD/Radar/LexoRadar/config.ts index 59f230e..fcf4819 100644 --- a/src/HUD/Radar/LexoRadar/config.ts +++ b/src/HUD/Radar/LexoRadar/config.ts @@ -1,5 +1,6 @@ const config = { - playerSize: 60, + playerSize: 70, + smokeSize: 50 } export default config; \ No newline at end of file diff --git a/src/HUD/Radar/LexoRadar/index.css b/src/HUD/Radar/LexoRadar/index.scss similarity index 87% rename from src/HUD/Radar/LexoRadar/index.css rename to src/HUD/Radar/LexoRadar/index.scss index 72dea71..abbb754 100644 --- a/src/HUD/Radar/LexoRadar/index.css +++ b/src/HUD/Radar/LexoRadar/index.scss @@ -51,12 +51,20 @@ html, body, .map-container { } .map .player, .map .grenade, .map .bomb { position: absolute; - height:30px; - width:30px; + height:0px; + width:0px; display: flex; align-items: center; justify-content: center; transition: opacity 0.5s ease; + .content { + position: absolute; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + } /*transition: all 0.1s ease;/**/ } .map .player .background { @@ -66,7 +74,7 @@ html, body, .map-container { background-position: center; background-size: contain; background-repeat: no-repeat; - transition:transform 0.2s ease; + transition:transform 0.2s linear; } .map .player .background-fire { position: absolute; @@ -140,10 +148,12 @@ html, body, .map-container { } */ .map .player.active { - width:120%; - height:120%; z-index: 3; } +.map .player.active .content { + width:48px; + height:48px; +} .map .grenade .background { border-radius:50%; background-size: contain; @@ -157,9 +167,6 @@ html, body, .map-container { opacity: 1; transition: opacity 1s; } -.map .grenade.smoke { - transition: all 0.5s; -} .map .grenade.smoke.inair .background { background-color: transparent !important; border: none !important; @@ -167,7 +174,6 @@ html, body, .map-container { filter: invert(1); } .map .grenade.smoke.exploded .background { - opacity: 0; } .map .grenade.flashbang, .map .grenade.frag { filter: invert(1); @@ -183,6 +189,8 @@ html, body, .map-container { .map .grenade .explode-point, .map .bomb .explode-point { position: absolute; width: 2px; + top: calc(50% - 1px); + left: calc(50% - 1px); height: 2px; border-radius: 0.08px; } @@ -194,7 +202,6 @@ html, body, .map-container { opacity: 0; } .map .grenade.smoke .background { - border: 5px solid grey; background-color: rgba(255,255,255,0.5); } .map .grenade.smoke.CT .background { @@ -214,9 +221,8 @@ html, body, .map-container { width:12px; height:12px; } -.map .grenade.smoke { - width:60px; - height:60px; +.map .grenade .content { + position: absolute; } .map .grenade.inferno .background { background-color: red; @@ -270,16 +276,29 @@ html, body, .map-container { background: red; } -@keyframes Hidden { - from { - } - to { - display: none !important; - } +.map .player.dead .background { + display: none; +} +.map .player.dead .label { + background: transparent; +} +.map .player.dead.CT .label, .map .player.CT .label div { + color: var(--color-new-ct); +} +.map .player.dead.T .label, .map .player.T .label div { + color: var(--color-new-t); } .map .hidden { - opacity: 0; - animation: Hidden 1s ease 1s 1; - animation-fill-mode: forwards;/**/ + opacity: 0 !important; +} -} \ No newline at end of file +.map .grenade { + &.smoke { + &.exploded, &.landed { + .background { + display: flex; + flex-wrap: wrap; + } + } + } +} diff --git a/src/HUD/Radar/LexoRadar/interface.ts b/src/HUD/Radar/LexoRadar/interface.ts index 9614aac..74236ad 100644 --- a/src/HUD/Radar/LexoRadar/interface.ts +++ b/src/HUD/Radar/LexoRadar/interface.ts @@ -1,4 +1,4 @@ -import { Player, Side } from "csgogsi"; +import { Player, Side, Grenade } from "csgogsi"; export interface RadarPlayerObject { id: string, @@ -26,30 +26,5 @@ export interface RadarGrenadeObject { visible: boolean, id: string, } -export interface GrenadeBase { - owner: string, - type: 'decoy' | 'smoke' | 'frag' | 'firebomb' | 'flashbang' | 'inferno' - lifetime: string -} -export interface DecoySmokeGrenade extends GrenadeBase { - position: string, - velocity: string, - type: 'decoy' | 'smoke', - effecttime: string, -} - -export interface DefaultGrenade extends GrenadeBase { - position: string, - type: 'frag' | 'firebomb' | 'flashbang', - velocity: string, -} - -export interface InfernoGrenade extends GrenadeBase { - type: 'inferno', - flames: { [key: string]: string } -} - -export type Grenade = DecoySmokeGrenade | DefaultGrenade | InfernoGrenade; - -export type ExtendedGrenade = Grenade & { id: string, side: Side | null, }; \ No newline at end of file +export type ExtendedGrenade = Grenade & { side: Side | null, }; \ No newline at end of file diff --git a/src/HUD/Radar/LexoRadar/maps/de_anubis/index.ts b/src/HUD/Radar/LexoRadar/maps/de_anubis/index.ts deleted file mode 100644 index 44d5fb0..0000000 --- a/src/HUD/Radar/LexoRadar/maps/de_anubis/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import radar from './radar.png' - -const config = { - "config": { - "origin": { - "x": 536.3392873296655, - "y": 638.0789844851904 - }, - "pxPerUX": 0.1907910426894958, - "pxPerUY": -0.18993888105312648 - }, - "file":radar -} - -export default config; \ No newline at end of file diff --git a/src/HUD/Radar/LexoRadar/maps/de_anubis/radar.png b/src/HUD/Radar/LexoRadar/maps/de_anubis/radar.png deleted file mode 100644 index 496673a..0000000 Binary files a/src/HUD/Radar/LexoRadar/maps/de_anubis/radar.png and /dev/null differ diff --git a/src/HUD/Radar/LexoRadar/maps/de_mirage/index.ts b/src/HUD/Radar/LexoRadar/maps/de_mirage/index.ts index 5a42ebd..ac1d7c9 100644 --- a/src/HUD/Radar/LexoRadar/maps/de_mirage/index.ts +++ b/src/HUD/Radar/LexoRadar/maps/de_mirage/index.ts @@ -7,8 +7,7 @@ const config = { "y": 340.2921393569175 }, "pxPerUX": 0.20118507589946494, - "pxPerUY": -0.20138282875746794, - "originHeight": -170, + "pxPerUY": -0.20138282875746794 }, "file": radar } diff --git a/src/HUD/Radar/LexoRadar/maps/de_vertigo/index.ts b/src/HUD/Radar/LexoRadar/maps/de_vertigo/index.ts index 0724aae..cad07f1 100644 --- a/src/HUD/Radar/LexoRadar/maps/de_vertigo/index.ts +++ b/src/HUD/Radar/LexoRadar/maps/de_vertigo/index.ts @@ -1,4 +1,3 @@ -import { Player } from 'csgogsi-socket'; import radar from './radar.png' const high = { @@ -32,21 +31,6 @@ const config = { isVisible: (height: number) => height < 11700, }, ], - zooms: [{ - threshold: (players: Player[]) => { - const alivePlayers = players.filter(player => player.state.health); - return alivePlayers.length > 0 && alivePlayers.every(player => player.position[2] < 11700) - }, - origin: [472, 1130], - zoom: 2 - }, { - threshold: (players: Player[]) => { - const alivePlayers = players.filter(player => player.state.health); - return alivePlayers.length > 0 && players.filter(player => player.state.health).every(player => player.position[2] >= 11700); - }, - origin: [528, 15], - zoom: 1.75 - }], file: radar } diff --git a/src/HUD/Radar/LexoRadar/maps/index.ts b/src/HUD/Radar/LexoRadar/maps/index.ts index cbb99ad..170721d 100644 --- a/src/HUD/Radar/LexoRadar/maps/index.ts +++ b/src/HUD/Radar/LexoRadar/maps/index.ts @@ -6,17 +6,15 @@ import de_train from './de_train'; import de_overpass from './de_overpass'; import de_nuke from './de_nuke'; import de_vertigo from './de_vertigo'; -import de_anubis from './de_anubis'; import de_ancient from './de_ancient'; -import api from '../../../../api/api'; -import { Player } from 'csgogsi-socket'; +import api from '../../../../API'; +import { Player } from 'csgogsi'; export type ZoomAreas = { threshold: (players: Player[]) => boolean; origin: number[], zoom: number } - export interface ScaleConfig { origin: { x:number, @@ -29,7 +27,7 @@ export interface ScaleConfig { interface SingleLayer { config: ScaleConfig, - file: string, + file: string zooms?: ZoomAreas[] } @@ -39,7 +37,7 @@ interface DoubleLayer { config: ScaleConfig, isVisible: (height: number) => boolean }[], - file: string, + file: string zooms?: ZoomAreas[] } @@ -54,8 +52,7 @@ const maps: { [key: string] : MapConfig} = { de_overpass, de_nuke, de_vertigo, - de_ancient, - de_anubis + de_ancient } api.maps.get().then(fallbackMaps => { diff --git a/src/HUD/Radar/LexoRadar/utils.ts b/src/HUD/Radar/LexoRadar/utils.ts new file mode 100644 index 0000000..a830276 --- /dev/null +++ b/src/HUD/Radar/LexoRadar/utils.ts @@ -0,0 +1,244 @@ +import { Player, Side, Grenade } from "csgogsi"; +import maps, { ScaleConfig } from "./maps"; +import { ExtendedGrenade, RadarGrenadeObject, RadarPlayerObject } from "./interface"; +import { InfernoGrenade } from "csgogsi"; + +export const playersStates: Player[][] = []; +export const grenadesStates: Grenade[][] = []; +const directions: { [key: string]: number } = {}; + +export const explosionPlaces: Record = {}; + +type ShootingState = { + ammo: number, + weapon: string, + lastShoot: number +} +let shootingState: Record = {}; + +const calculateDirection = (player: Player) => { + if (directions[player.steamid] && !player.state.health) return directions[player.steamid]; + + const [forwardV1, forwardV2] = player.forward; + let direction = 0; + + const [axisA, axisB] = [Math.asin(forwardV1), Math.acos(forwardV2)].map(axis => axis * 180 / Math.PI); + + if (axisB < 45) { + direction = Math.abs(axisA); + } else if (axisB > 135) { + direction = 180 - Math.abs(axisA); + } else { + direction = axisB; + } + + if (axisA < 0) { + direction = -(direction -= 360); + } + + if (!directions[player.steamid]) { + directions[player.steamid] = direction; + } + + const previous = directions[player.steamid]; + + let modifier = previous; + modifier -= 360 * Math.floor(previous / 360); + modifier = -(modifier -= direction); + + if (Math.abs(modifier) > 180) { + modifier -= 360 * Math.abs(modifier) / modifier; + } + directions[player.steamid] += modifier; + + return directions[player.steamid]; +} + +export const round = (n: number) => { + const r = 0.02; + return Math.round(n / r) * r; +} + +export const parsePosition = (position: number[], config: ScaleConfig) => { + const left = config.origin.x + (position[0] * config.pxPerUX); + const top = config.origin.y + (position[1] * config.pxPerUY); + + return [round(left), round(top)]; +} + +export const parsePlayerPosition = (player: Player, mapConfig: ScaleConfig) => { + const playerData = playersStates.slice(0, 5).map(players => players.filter(pl => pl.steamid === player.steamid)[0]).filter(pl => !!pl); + if (playerData.length === 0) return [0, 0]; + const positions = playerData.map(playerEntry => parsePosition(playerEntry.position, mapConfig)); + const entryAmount = positions.length; + let x = 0; + let y = 0; + for (const position of positions) { + x += position[0]; + y += position[1]; + } + + const degree = calculateDirection(player); + return [x / entryAmount, y / entryAmount, degree]; +} + + +const parseGrenadePosition = (grenade: ExtendedGrenade, config: ScaleConfig) => { + if(grenade.id in explosionPlaces) return parsePosition(explosionPlaces[grenade.id], config); + const grenadeData = grenadesStates.slice(0, 5).map(grenades => grenades.filter(gr => gr.id === grenade.id)[0]).filter(pl => !!pl); + if (grenadeData.length === 0) return "position" in grenade ? parsePosition(grenade.position, config) : null; + const positions = grenadeData.map(grenadeEntry => ("position" in grenadeEntry ? parsePosition(grenadeEntry.position, config) : null)).filter(posData => posData !== null) as number[][]; + if (positions.length === 0) return null; + const entryAmount = positions.length; + let x = 0; + let y = 0; + for (const position of positions) { + x += position[0]; + y += position[1]; + } + + return [x / entryAmount, y / entryAmount]; +} + +export const EXPLODE_TIME_FRAG = 1.6; +export const EXPLODE_TIME_FLASH = 1.45; + +export const extendGrenade = ({grenade, mapName, side }: { side: Side, grenade: Grenade, mapName: string}) => { + // const owner = this.props.players.find(player => player.steamid === grenade.owner); + const extGrenade: ExtendedGrenade = { + ...grenade, + side:/* owner?.team.side ||*/ side + } + const map = maps[mapName]; + if (extGrenade.type === "inferno") { + const mapFlame = (flame: InfernoGrenade["flames"][number]) => { + if ("config" in map) { + return ({ + position: parsePosition(flame.position, map.config), + id: `${flame.id}_${extGrenade.id}`, + visible: true + }); + } + return map.configs.map(config => ({ + id: `${flame}_${extGrenade.id}_${config.id}`, + visible: config.isVisible(flame.position[2]), + position: parsePosition(flame.position, config.config) + })); + } + const flames = extGrenade.flames.map(mapFlame).flat(); + const flameObjects: RadarGrenadeObject[] = flames.map(flame => ({ + ...flame, + side: extGrenade.side, + type: 'inferno', + state: 'landed' + })); + return flameObjects; + } + + if ("config" in map) { + const position = parseGrenadePosition(extGrenade, map.config); + + if (!position) return null; + const grenadeObject: RadarGrenadeObject = { + type: extGrenade.type, + state: 'inair', + side: extGrenade.side, + position, + id: extGrenade.id, + visible: true + } + if (extGrenade.type === "smoke") { + if (extGrenade.effecttime !== 0) { + grenadeObject.state = "landed"; + if (extGrenade.effecttime >= 16.5) { + grenadeObject.state = 'exploded'; + } + } + } else if ((extGrenade.type === 'flashbang' && extGrenade.lifetime >= EXPLODE_TIME_FLASH) || (extGrenade.type === 'frag' && extGrenade.lifetime >= EXPLODE_TIME_FRAG)) { + grenadeObject.state = 'exploded'; + } + return grenadeObject; + } + return map.configs.map(config => { + const position = parseGrenadePosition(extGrenade, config.config); + if (!position) return null; + const grenadeObject: RadarGrenadeObject = { + type: extGrenade.type, + state: 'inair', + side: extGrenade.side, + position, + id: `${extGrenade.id}_${config.id}`, + visible: config.isVisible(extGrenade.position[2]) + } + if (extGrenade.type === "smoke") { + if (extGrenade.effecttime !== 0) { + grenadeObject.state = "landed"; + if (extGrenade.effecttime >= 16.5) { + grenadeObject.state = 'exploded'; + } + } + } else if ((extGrenade.type === 'flashbang' && extGrenade.lifetime >= EXPLODE_TIME_FLASH) || (extGrenade.type === 'frag' && extGrenade.lifetime >= EXPLODE_TIME_FRAG)) { + grenadeObject.state = 'exploded'; + } + return grenadeObject; + }).filter((grenade): grenade is RadarGrenadeObject => grenade !== null); +} + +export const extendPlayer = ({ player, steamId, mapName }: { mapName: string, player: Player, steamId: string | null}): RadarPlayerObject | RadarPlayerObject[] | null => { + const weapons = player.weapons ? Object.values(player.weapons) : []; + const weapon = weapons.find(weapon => weapon.state === "active" && weapon.type !== "C4" && weapon.type !== "Knife" && weapon.type !== "Grenade"); + + const shooting: ShootingState = { ammo: weapon && weapon.ammo_clip || 0, weapon: weapon && weapon.name || '', lastShoot: 0 }; + + const lastShoot = shootingState[player.steamid] || shooting; + + let isShooting = false; + + if (shooting.weapon === lastShoot.weapon && shooting.ammo < lastShoot.ammo) { + isShooting = true; + } + + shooting.lastShoot = isShooting ? (new Date()).getTime() : lastShoot.lastShoot; + + shootingState[player.steamid] = shooting; + const map = maps[mapName]; + const playerObject: RadarPlayerObject = { + id: player.steamid, + label: player.observer_slot !== undefined ? player.observer_slot : "", + side: player.team.side, + position: [], + visible: true, + isActive: steamId === player.steamid, + forward: 0, + scale: 1, + steamid: player.steamid, + flashed: player.state.flashed > 35, + shooting: isShooting, + lastShoot: shooting.lastShoot, + isAlive: player.state.health > 0, + hasBomb: !!Object.values(player.weapons).find(weapon => weapon.type === "C4"), + player + } + if ("config" in map) { + const scale = map.config.originHeight === undefined ? 1 : (1 + (player.position[2] - map.config.originHeight) / 1000); + + playerObject.scale = scale; + + const position = parsePlayerPosition(player, map.config); + playerObject.position = position; + + return playerObject; + } + return map.configs.map(config => { + const scale = config.config.originHeight === undefined ? 1 : (1 + (player.position[2] - config.config.originHeight) / 750); + + playerObject.scale = scale; + + return ({ + ...playerObject, + position: parsePlayerPosition(player, config.config), + id: `${player.steamid}_${config.id}`, + visible: config.isVisible(player.position[2]) + }) + }); +} diff --git a/src/HUD/Radar/Radar.tsx b/src/HUD/Radar/Radar.tsx index ae0b7f5..dc0aec3 100644 --- a/src/HUD/Radar/Radar.tsx +++ b/src/HUD/Radar/Radar.tsx @@ -1,50 +1,20 @@ -import React from "react"; -import { isDev } from './../../api/api'; -import { CSGO } from "csgogsi-socket"; +import { CSGO } from "csgogsi"; import LexoRadarContainer from './LexoRadar/LexoRadarContainer'; interface Props { radarSize: number, game: CSGO } -interface State { - showRadar: boolean, - loaded: boolean, - boltobserv:{ - css: boolean, - maps: boolean - } + +const Radar = ({ radarSize, game }: Props) => { + const { players, player, bomb, grenades, map } = game; + return } -export default class Radar extends React.Component { - state = { - showRadar: true, - loaded: !isDev, - boltobserv: { - css: true, - maps: true - } - } - async componentDidMount(){ - /*if(isDev){ - const response = await fetch('hud.json'); - const hud = await response.json(); - const boltobserv = { - css: Boolean(hud && hud.boltobserv && hud.boltobserv.css), - maps: Boolean(hud && hud.boltobserv && hud.boltobserv.maps) - } - this.setState({boltobserv, loaded: true}); - }*/ - } - - render() { - const { players, player, bomb, grenades, map } = this.props.game; - return - } -} +export default Radar; \ No newline at end of file diff --git a/src/HUD/Radar/RadarMaps.tsx b/src/HUD/Radar/RadarMaps.tsx index d5c8f24..7c6c2a1 100644 --- a/src/HUD/Radar/RadarMaps.tsx +++ b/src/HUD/Radar/RadarMaps.tsx @@ -1,70 +1,58 @@ -import React from "react"; +import { useState } from "react"; import "./radar.scss"; -import { Match, Veto } from "../../api/interfaces"; -import { Map, CSGO, Team } from 'csgogsi-socket'; -import { actions } from './../../App'; +import { Match, Veto } from "../../API/types"; +import { Map, CSGO, Team } from 'csgogsi'; import Radar from './Radar' -import TeamLogo from "../MatchBar/TeamLogo"; + +import { useAction } from "../../API/contexts/actions"; interface Props { match: Match | null, map: Map, game: CSGO } -interface State { showRadar: boolean, radarSize: number, showBig: boolean } -export default class RadarMaps extends React.Component { - state = { - showRadar: true, - radarSize: 350, - showBig: false - } - componentDidMount() { - actions.on('radarBigger', () => this.radarChangeSize(20)); - actions.on('radarSmaller', () => this.radarChangeSize(-20)); - actions.on('toggleRadar', () => { this.setState(state => ({ showRadar: !state.showRadar })) }); + const RadarMaps = ({ match, map, game }: Props) => { + const [ radarSize, setRadarSize ] = useState(366); + const [ showBig, setShowBig ] = useState(false); - actions.on("toggleRadarView", () => { - this.setState({showBig:!this.state.showBig}); - }); - } - radarChangeSize = (delta: number) => { - const newSize = this.state.radarSize + delta; - this.setState({ radarSize: newSize > 0 ? newSize : this.state.radarSize }); - } - render() { - const { match } = this.props; - const { radarSize, showBig, showRadar } = this.state; - const size = showBig ? 600 : radarSize; - return ( -
-
- {match ? : null} -
- ); - } + useAction('radarBigger', () => { + setRadarSize(p => p+10); + }, []); + + useAction('radarSmaller', () => { + setRadarSize(p => p-10); + }, []); + + useAction('toggleRadarView', () => { + setShowBig(p => !p); + }, []); + + return ( +
+ {match ? : null} + +
+ ); } -class MapsBar extends React.PureComponent { - render() { - const { match, map } = this.props; - if (!match || !match.vetos.length) return ''; - const picks = match.vetos.filter(veto => veto.type !== "ban" && veto.mapName); - if (picks.length > 3) { - const current = picks.find(veto => map.name.includes(veto.mapName)); - if (!current) return null; - return
- {} -
- } +export default RadarMaps; + +const MapsBar = ({ match, map }: Props) => { + if (!match || !match.vetos.length) return ''; + const picks = match.vetos.filter(veto => veto.type !== "ban" && veto.mapName); + if (picks.length > 3) { + const current = picks.find(veto => map.name.includes(veto.mapName)); + if (!current) return null; return
- {match.vetos.filter(veto => veto.type !== "ban").filter(veto => veto.teamId || veto.type === "decider").map(veto => )} +
Best of {match.matchType.replace("bo", "")}
+ {}
} + return
+
Best of {match.matchType.replace("bo", "")}
+ {match.vetos.filter(veto => veto.type !== "ban").filter(veto => veto.teamId || veto.type === "decider").map(veto => )} +
} -class MapEntry extends React.PureComponent<{ veto: Veto, map: Map, team: Team | null }> { - render() { - const { veto, map, team } = this.props; - return
-
{team ? : null}
-
{veto.mapName}
-
- } +const MapEntry = ({ veto, map }: { veto: Veto, map: Map, team: Team | null }) => { + return
+
{veto.mapName.replace("de_", "")}
+
} \ No newline at end of file diff --git a/src/HUD/Radar/radar.scss b/src/HUD/Radar/radar.scss index e45b815..1505f47 100644 --- a/src/HUD/Radar/radar.scss +++ b/src/HUD/Radar/radar.scss @@ -3,7 +3,6 @@ top: 10px; left: 10px; border: none; - background-color: rgba(0,0,0,0.5); transition: all 1s; .map-container { transition: all 1s; @@ -24,18 +23,24 @@ perspective: 500px; } } -.radar-component-container { - width: 350px; - height:350px; - overflow: hidden; -} #maps_container { width: 100%; height: 30px; display: flex; flex-direction: row; justify-content: space-evenly; - background-color: rgba(0,0,0,0.5); + color: white; + background-color: var(--sub-panel-color); + + .bestof { + width: 121px; + font-size: 16px; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + text-transform: uppercase; + } } .veto_entry { display: flex; @@ -59,8 +64,15 @@ width: 23px; } } - .map_name.active { - text-shadow: 0 0 15px white; - font-weight: 600; + .map_name { + font-size:12px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + opacity: 0.5; + + &.active { + opacity: 1; + } } } diff --git a/src/HUD/SideBoxes/Money.tsx b/src/HUD/SideBoxes/Money.tsx index dba91dc..449112d 100644 --- a/src/HUD/SideBoxes/Money.tsx +++ b/src/HUD/SideBoxes/Money.tsx @@ -1,45 +1,47 @@ -import React from 'react'; +import React from "react"; -class LossBox extends React.PureComponent<{ active: boolean, side: 'CT' | 'T' }>{ - render(){ - return
- } -} +const LossBox = React.memo(({ active, side }: { active: boolean; side: "CT" | "T" }) => { + return ( +
+
+ ); +}); interface Props { - side: 'left' | 'right', - team: 'CT' | 'T', - loss: number, - equipment: number, - money: number, - show: boolean, + side: "left" | "right"; + team: "CT" | "T"; + loss: number; + equipment: number; + money: number; + show: boolean; } -export default class Money extends React.PureComponent { - - render() { - return ( -
-
- = 4} /> - = 3} /> - = 2} /> - = 1} /> -
-
-
Loss Bonus
-
${this.props.loss}
-
-
-
Team Money
-
${this.props.money}
-
-
-
Equipment Value
-
${this.props.equipment}
-
-
- ); - } - -} +const Money = ({ side, team, loss, equipment, money, show }: Props) => { + return ( +
+
+ = 4} /> + = 3} /> + = 2} /> + = 1} /> +
+
+
Loss Bonus
+
${loss}
+
+
+
Team Money
+
${money}
+
+
+
Equipment Value
+
${equipment}
+
+
+ ); +}; +export default Money; diff --git a/src/HUD/SideBoxes/SideBox.tsx b/src/HUD/SideBoxes/SideBox.tsx index 7c9d98e..9e73779 100644 --- a/src/HUD/SideBoxes/SideBox.tsx +++ b/src/HUD/SideBoxes/SideBox.tsx @@ -1,49 +1,32 @@ -import React from 'react'; +import { useState } from 'react'; import './sideboxes.scss' -import {configs, hudIdentity} from './../../App'; -import { apiUrl } from '../../api/api'; +import { apiUrl } from './../../API'; +import { useConfig, useOnConfigChange } from '../../API/contexts/actions'; +import { hudIdentity } from '../../API/HUD'; -export default class SideBox extends React.Component<{ side: 'left' | 'right', hide: boolean}, { title: string, subtitle: string, image?: string }> { - constructor(props: any) { - super(props); - this.state = { - title:'Title', - subtitle:'Content', - } - } +const Sidebox = ({side, hide} : { side: 'left' | 'right', hide: boolean}) => { + const [ image, setImage ] = useState(null); + const data = useConfig('display_settings'); - componentDidMount() { - configs.onChange((data:any) => { - if(!data) return; - const display = data.display_settings; - if(!display) return; - if(`${this.props.side}_title` in display){ - this.setState({title:display[`${this.props.side}_title`]}) - } - if(`${this.props.side}_subtitle` in display){ - this.setState({subtitle:display[`${this.props.side}_subtitle`]}) - } - if(`${this.props.side}_image` in display){ - const imageUrl = `${apiUrl}api/huds/${hudIdentity.name || 'dev'}/display_settings/${this.props.side}_image?isDev=${hudIdentity.isDev}&cache=${(new Date()).getTime()}`; - this.setState({image:imageUrl}) - } - }); - } - - render() { - const { image, title, subtitle} = this.state; - if(!title) return ''; - return ( -
-
-
{title}
-
{subtitle}
-
-
- {image ? {'Left'}/:''} -
+ useOnConfigChange('display_settings', data => { + if(data && `${side}_image` in data){ + const imageUrl = `${apiUrl}api/huds/${hudIdentity.name || 'dev'}/display_settings/${side}_image?isDev=${hudIdentity.isDev}&cache=${(new Date()).getTime()}`; + setImage(imageUrl); + } + }, []); + + if(!data || !data[`${side}_title`]) return null; + return ( +
+
+
{data[`${side}_title`]}
+
{data[`${side}_subtitle`]}
- ); - } - +
+ {image ? {'Left'}/:null} +
+
+ ); } + +export default Sidebox; \ No newline at end of file diff --git a/src/HUD/SideBoxes/UtilityLevel.tsx b/src/HUD/SideBoxes/UtilityLevel.tsx index 7ef49f7..86b7c8c 100644 --- a/src/HUD/SideBoxes/UtilityLevel.tsx +++ b/src/HUD/SideBoxes/UtilityLevel.tsx @@ -1,12 +1,11 @@ -import React from "react"; import Weapon from "./../Weapon/Weapon"; -import { Player, WeaponRaw, Side } from "csgogsi-socket"; +import { Player, Side, WeaponRaw } from "csgogsi"; interface Props { - sides?: 'reversed', - show: boolean; - side: 'CT' | 'T', - players: Player[] + sides?: "reversed"; + show: boolean; + side: "CT" | "T"; + players: Player[]; } function utilityState(amount: number) { @@ -49,17 +48,30 @@ function utilityColor(amount: number) { function sum(grenades: WeaponRaw[], name: string) { return ( - grenades.filter(grenade => grenade.name === name).reduce((prev, next) => ({ ...next, ammo_reserve: (prev.ammo_reserve || 0) + (next.ammo_reserve || 0) }), { name: "", ammo_reserve: 0 }) + grenades.filter((grenade) => grenade.name === name).reduce( + (prev, next) => ({ + ...next, + ammo_reserve: (prev.ammo_reserve || 0) + (next.ammo_reserve || 0), + }), + { name: "", ammo_reserve: 0 }, + ) .ammo_reserve || 0 ); } function parseGrenades(players: Player[], side: Side) { const grenades = players - .filter(player => player.team.side === side) - .map(player => Object.values(player.weapons).filter(weapon => weapon.type === "Grenade")) + .filter((player) => player.team.side === side) + .map((player) => + Object.values(player.weapons).filter((weapon) => + weapon.type === "Grenade" + ) + ) .flat() - .map(grenade => ({ ...grenade, name: grenade.name.replace("weapon_", "") })); + .map((grenade) => ({ + ...grenade, + name: grenade.name.replace("weapon_", ""), + })); return grenades; } @@ -69,40 +81,44 @@ export function summarise(players: Player[], side: Side) { hg: sum(grenades, "hegrenade"), flashes: sum(grenades, "flashbang"), smokes: sum(grenades, "smokegrenade"), - inc: sum(grenades, "incgrenade") + sum(grenades, "molotov") + inc: sum(grenades, "incgrenade") + sum(grenades, "molotov"), }; } -class GrenadeContainer extends React.PureComponent<{ grenade: string; amount: number }> { - render() { - return ( -
-
- -
-
x{this.props.amount}
+const GrenadeContainer = ( + { grenade, amount }: { grenade: string; amount: number }, +) => { + return ( +
+
+
- ); - } -} +
x{amount}
+
+ ); +}; -export default class SideBox extends React.Component { - render() { - const grenades = summarise(this.props.players, this.props.side); - const total = Object.values(grenades).reduce((a, b) => a+b, 0); - return ( -
-
-
Utility Level - 
-
{utilityState(total)}
-
-
- - - - -
-
- ); - } -} +const SideBox = ({ players, side, show }: Props) => { + const grenades = summarise(players, side); + const total = Object.values(grenades).reduce((a, b) => a + b, 0); + return ( +
+
+
Utility Level - 
+
+ {utilityState(total)} +
+
+
+ + + + +
+
+ ); +}; +export default SideBox; diff --git a/src/HUD/TeamOverview/TeamOverview.tsx b/src/HUD/TeamOverview/TeamOverview.tsx deleted file mode 100644 index 3733386..0000000 --- a/src/HUD/TeamOverview/TeamOverview.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import * as I from '../../api/interfaces'; -import "./teamoverview.scss"; - -interface IProps { - team: I.Team, - show: boolean, - veto: I.Veto | null -} - -export default class TeamOverview extends React.Component { - render() { - if(!this.props.team) return null; - return ( - null - ); - } -} diff --git a/src/HUD/Timers/BombTimer.tsx b/src/HUD/Timers/BombTimer.tsx index 9a8284b..3b349cf 100644 --- a/src/HUD/Timers/BombTimer.tsx +++ b/src/HUD/Timers/BombTimer.tsx @@ -1,49 +1,18 @@ -import React from "react"; - -import { GSI } from "./../../App"; -import BombTimer from "./Countdown"; +import { MAX_TIMER, useBombTimer } from "./Countdown"; import { C4 } from "./../../assets/Icons"; -export default class Bomb extends React.Component { - constructor(props: any) { - super(props); - this.state = { - height: 0, - show: false - }; - } - hide = () => { - this.setState({ show: false, height: 100 }); - }; - componentDidMount() { - const bomb = new BombTimer(time => { - let height = time > 40 ? 4000 : time * 100; - this.setState({ height: height / 40 }); - }); - bomb.onReset(this.hide); - GSI.on("data", data => { - if (data.bomb && data.bomb.countdown) { - if (data.bomb.state === "planted") { - this.setState({ show: true }); - return bomb.go(data.bomb.countdown); - } - if (data.bomb.state !== "defusing") { - this.hide(); - } - } else { - this.hide(); - } - }); - } - - render() { - return ( -
-
-
- -
+const Bomb = () => { + const bombData = useBombTimer(); + const show = bombData.state === "planted" || bombData.state === "defusing"; + + return ( +
+
+
+
- ); - } +
+ ); } + +export default Bomb; \ No newline at end of file diff --git a/src/HUD/Timers/Countdown.ts b/src/HUD/Timers/Countdown.ts index 3b92af1..ffc5003 100644 --- a/src/HUD/Timers/Countdown.ts +++ b/src/HUD/Timers/Countdown.ts @@ -1,46 +1,83 @@ -export default class Countdown { - last: number; - time: number; - step: (time: number) => void; - on: boolean; - resetFunc?: Function; +import { Bomb, Events, Player } from "csgogsi"; +import { useEffect, useRef, useState } from "react"; +import { GSI } from "../../API/HUD"; - constructor(step: (time: number) => void){ - this.last = 0; - this.time = 0; - this.on = false; - this.step = step; - } - onReset(func: Function) { - this.resetFunc = func; - } - stepWrapper = (time: number) =>{ - if(this.time < 0) return this.reset(); - if(!this.on) return this.reset(); - if(!this.last) this.last = time; - if(this.time !== Number((this.time - (time - this.last)/1000))){ - this.time = Number((this.time - (time - this.last)/1000)); - this.step(this.time); +export const MAX_TIMER = { + planting: 3, + defuse_kit: 5, + defuse_nokit: 10, + bomb: 40 +} +const findNewTime = (current: number, newTime: number) => Math.abs(current - newTime) > 2 ? newTime : current; + +export const useBombTimer = () => { + const [ player, setPlayerSteamId ] = useState(null); + const [ bombState, setBombState ] = useState(null); + const [ site, setBombSite ] = useState(null); + + const [ plantTime, setPlantTime ] = useState(0); + const [ bombTime, setBombTime ] = useState(0); + const [ defuseTime, setDefuseTime ] = useState(0); + + // Inner loop logic + const previousTimeRef = useRef(0); + const rAFRef = useRef(); + + useEffect(() => { + const onData: Events["data"] = data => { + const { bomb } = data; + + const bombPlayer = bomb?.player || null; + const state = bomb?.state || null; + const site = bomb?.site || null; + + const countdown = parseFloat(bomb?.countdown || '0'); + + setPlayerSteamId(bombPlayer); + setBombState(state); + setBombSite(site); + + const plantNewTime = state === "planting" ? countdown : 0; + const defuseNewTime = state === "defusing" ? countdown : 0; + + + setPlantTime(curr => findNewTime(curr, plantNewTime)); + setDefuseTime(curr => findNewTime(curr, defuseNewTime)); + setBombTime(p => state === "planted" ? findNewTime(p, countdown) : p); + } + + GSI.on("data", onData); + + const animationFrame = (time: number) => { + if(previousTimeRef.current){ + const deltaTime = time - previousTimeRef.current; + const dTs = deltaTime/1000; + + setPlantTime(p => p <= 0 ? 0 : p - dTs); + setDefuseTime(p => p <= 0 ? 0 : p - dTs); + setBombTime(p => p <= 0 ? 0 : p - dTs); + } + previousTimeRef.current = time; + rAFRef.current = requestAnimationFrame(animationFrame); + } + + rAFRef.current = requestAnimationFrame(animationFrame); + + return () => { + GSI.off("data", onData); + if(rAFRef.current) cancelAnimationFrame(rAFRef.current); } - this.last =time; - if(this.last) requestAnimationFrame(this.stepWrapper) - } + }, []); - go(duration: string | number){ - //console.log("STARTED WITH ", duration); - if(typeof duration === "string") duration = Number(duration); - if(Math.abs(duration - this.time) > 2) this.time = duration; - this.on = true; - if(!this.last ) requestAnimationFrame(this.stepWrapper); - } - - reset(){ - this.last = 0; - this.time = 0; - this.on = false; - if(this.resetFunc) this.resetFunc(); - } -} \ No newline at end of file + return ({ + state: bombState, + player, + site, + defuseTime, + bombTime, + plantTime + }) +} diff --git a/src/HUD/Timers/PlantDefuse.tsx b/src/HUD/Timers/PlantDefuse.tsx index 027049e..a2fafa7 100644 --- a/src/HUD/Timers/PlantDefuse.tsx +++ b/src/HUD/Timers/PlantDefuse.tsx @@ -1,41 +1,39 @@ -import React from "react"; - import { Timer } from "../MatchBar/MatchBar"; import { Player } from "csgogsi"; import * as I from "./../../assets/Icons"; +import { MAX_TIMER } from "./Countdown"; interface IProps { timer: Timer | null; side: "right" | "left" } -export default class Bomb extends React.Component { - getCaption = (type: "defusing" | "planting", player: Player | null) => { - if(!player) return null; - if(type === "defusing"){ - return <> - -
{player.name} is defusing the bomb
- ; - } +const getCaption = (type: "defusing" | "planting", player: Player | null) => { + if(!player) return null; + if(type === "defusing"){ return <> - -
{player.name} is planting the bomb
+ +
{player.name} is defusing the bomb
; } - render() { - const { side, timer } = this.props; - return ( -
- { - timer ? -
- {this.getCaption(timer.type, timer.player)} -
: null - } - -
-
- ); - } + return <> + +
{player.name} is planting the bomb
+ ; } +const Bomb = ({ timer, side }: IProps) =>{ + if(!timer) return null; + return ( +
+ { + timer ? +
+ {getCaption(timer.type, timer.player)} +
: null + } + +
+
+ ); +} +export default Bomb; \ No newline at end of file diff --git a/src/HUD/Tournament/Ladder.tsx b/src/HUD/Tournament/Ladder.tsx index e245596..459cc66 100644 --- a/src/HUD/Tournament/Ladder.tsx +++ b/src/HUD/Tournament/Ladder.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import * as I from './../../api/interfaces'; +import * as I from './../../API/types'; +import { apiUrl } from '../../API'; interface MatchData { left: { name: string; score: string | number; logo: string }; @@ -11,83 +11,79 @@ interface Props { teams: I.Team[] } -export default class Ladder extends React.Component { - joinParents = (matchup: I.TournamentMatchup, matchups: I.TournamentMatchup[]) => { - const { tournament } = this.props; - if (!tournament || !matchup) return matchup; +const joinParents = (matchup: I.TournamentMatchup, matchups: I.TournamentMatchup[]) => { + if (!matchup) return matchup; - if (matchup.parents.length) return matchup; + if (matchup.parents.length) return matchup; - const parents = matchups.filter(m => m.winner_to === matchup._id || m.loser_to === matchup._id); - if (!parents.length) return matchup; - matchup.parents.push(...parents.map(parent => this.joinParents(parent, matchups))); + const parents = matchups.filter(m => m.winner_to === matchup._id || m.loser_to === matchup._id); + if (!parents.length) return matchup; + matchup.parents.push(...parents.map(parent => joinParents(parent, matchups))); - return matchup; + return matchup; +}; + +const copyMatchups = (currentMatchups: I.TournamentMatchup[]): I.DepthTournamentMatchup[] => { + const matchups = JSON.parse(JSON.stringify(currentMatchups)) as I.DepthTournamentMatchup[]; + return matchups; +}; + +const setDepth = (matchups: I.DepthTournamentMatchup[], matchup: I.DepthTournamentMatchup, depth: number, force = false) => { + const getParents = (matchup: I.DepthTournamentMatchup) => { + return matchups.filter(parent => parent.loser_to === matchup._id || parent.winner_to === matchup._id); }; - copyMatchups = (): I.DepthTournamentMatchup[] => { - if (!this.props.tournament) return []; - const matchups = JSON.parse(JSON.stringify(this.props.tournament.matchups)) as I.DepthTournamentMatchup[]; - return matchups; + if (!matchup.depth || force) { + matchup.depth = depth; + getParents(matchup).forEach(matchup => setDepth(matchups, matchup, depth + 1)); + } + if (matchup.depth <= depth - 1) { + setDepth(matchups, matchup, depth - 1, true); + } + return matchup; +}; + +const getMatch = ({ matchup, matches, teams: allTeams}: { matches: I.Match[], teams: I.Team[], matchup: I.TournamentMatchup}) => { + const matchData: MatchData = { + left: { name: 'TBD', score: '-', logo: '' }, + right: { name: 'TBD', score: '-', logo: '' } }; + const match = matches.find(match => match.id === matchup.matchId); + if (!match) return matchData; + const teams = [ + allTeams.find(team => team._id === match.left.id), + allTeams.find(team => team._id === match.right.id) + ]; + if (teams[0]) { + matchData.left.name = teams[0].name; + matchData.left.score = match.left.wins; + matchData.left.logo = teams[0].logo; + } + if (teams[1]) { + matchData.right.name = teams[1].name; + matchData.right.score = match.right.wins; + matchData.right.logo = teams[1].logo; + } + return matchData; +}; - setDepth = (matchups: I.DepthTournamentMatchup[], matchup: I.DepthTournamentMatchup, depth: number, force = false) => { - const getParents = (matchup: I.DepthTournamentMatchup) => { - return matchups.filter(parent => parent.loser_to === matchup._id || parent.winner_to === matchup._id); - }; - - if (!matchup.depth || force) { - matchup.depth = depth; - getParents(matchup).forEach(matchup => this.setDepth(matchups, matchup, depth + 1)); - } - if (matchup.depth <= depth - 1) { - this.setDepth(matchups, matchup, depth - 1, true); - } - return matchup; - }; - - getMatch = (matchup: I.TournamentMatchup) => { - const { matches } = this.props; - const matchData: MatchData = { - left: { name: 'TBD', score: '-', logo: '' }, - right: { name: 'TBD', score: '-', logo: '' } - }; - const match = matches.find(match => match.id === matchup.matchId); - if (!match) return matchData; - const teams = [ - this.props.teams.find(team => team._id === match.left.id), - this.props.teams.find(team => team._id === match.right.id) - ]; - if (teams[0]) { - matchData.left.name = teams[0].name; - matchData.left.score = match.left.wins; - matchData.left.logo = teams[0].logo; - } - if (teams[1]) { - matchData.right.name = teams[1].name; - matchData.right.score = match.right.wins; - matchData.right.logo = teams[1].logo; - } - return matchData; - }; - - renderBracket = ( +const Ladder = ({ tournament, matches, teams }: Props) => { + const renderBracket = ( matchup: I.DepthTournamentMatchup | null | undefined, depth: number, fromChildId: string | undefined, childVisibleParents: number, isLast = false ) => { - const { tournament, matches } = this.props; if (!matchup || !tournament) return null; - const match = this.getMatch(matchup); + const match = getMatch({ teams: teams, matches: matches, matchup}); if (fromChildId === matchup.loser_to) return null; const parentsToRender = matchup.parents.filter(matchupParent => matchupParent.loser_to !== matchup._id); if (matchup.depth > depth) { return (
- {this.renderBracket(matchup, depth + 1, fromChildId, parentsToRender.length)} + {renderBracket(matchup, depth + 1, fromChildId, parentsToRender.length)}
); @@ -97,8 +93,8 @@ export default class Ladder extends React.Component { return (
- {this.renderBracket(matchup.parents[0], depth + 1, matchup._id, parentsToRender.length)} - {this.renderBracket(matchup.parents[1], depth + 1, matchup._id, parentsToRender.length)} + {renderBracket(matchup.parents[0], depth + 1, matchup._id, parentsToRender.length)} + {renderBracket(matchup.parents[1], depth + 1, matchup._id, parentsToRender.length)}
{
- {match.left.logo ? Logo : null} + {match.left.logo ? Logo : null}
{match.left.name}
{match.left.score}
- {match.right.logo ? Logo : null} + {match.right.logo ? Logo : null}
{match.right.name}
{match.right.score}
@@ -132,14 +128,13 @@ export default class Ladder extends React.Component { ); }; - render() { - const { tournament } = this.props; if (!tournament) return null; - const matchups = this.copyMatchups(); + const matchups = copyMatchups(tournament.playoffs.matchups); const gf = matchups.find(matchup => matchup.winner_to === null); if (!gf) return null; - const joinedParents = this.joinParents(gf, matchups); - const matchupWithDepth = this.setDepth(matchups, joinedParents as I.DepthTournamentMatchup, 0); - return this.renderBracket(matchupWithDepth, 0, undefined, 2, true); - } + const joinedParents = joinParents(gf, matchups); + const matchupWithDepth = setDepth(matchups, joinedParents as I.DepthTournamentMatchup, 0); + return renderBracket(matchupWithDepth, 0, undefined, 2, true); } + +export default Ladder; \ No newline at end of file diff --git a/src/HUD/Tournament/Tournament.tsx b/src/HUD/Tournament/Tournament.tsx index 3507ea3..30204cc 100644 --- a/src/HUD/Tournament/Tournament.tsx +++ b/src/HUD/Tournament/Tournament.tsx @@ -1,62 +1,51 @@ import React from 'react'; import './tournament.scss'; -import { actions } from '../../App'; -import * as I from './../../api/interfaces'; -import api from '../../api/api'; +import * as I from './../../API/types'; +import api from './../../API'; import Ladder from './Ladder'; -interface State { - tournament: I.Tournament | null, - teams: I.Team[], - matches: I.Match[], - show: boolean, -} -export default class Tournament extends React.Component<{}, State> { - constructor(props: {}) { - super(props); - this.state = { - tournament: null, - matches: [], - teams: [], - show: false - } - } - async componentDidMount() { - const { tournament } = await api.tournaments.get(); - if(tournament){ - actions.on("showTournament", async (show: string) => { - if(show !== "show"){ - return this.setState({show: false}); - } - - this.setState({tournament}, () => { - this.setState({show:true}) +import { useAction } from '../../API/contexts/actions'; +import { useEffect, useState } from 'react'; + +const Tournament = () => { + const [ show, setShow ] = useState(false); + const [ teams, setTeams ] = useState([]); + const [ matches, setMatches ] = useState([]); + const [ tournament, setTournament ] = useState(null); + + useAction("showTournament", (data) => { + setShow(data === "show"); + }); + + useEffect(() => { + api.tournaments.get().then(({ tournament }) => { + if(tournament){ + setTournament(tournament); + + Promise.allSettled([api.match.get(), api.teams.get()]).then(([matches, teams]) =>{ + setTeams(teams.status === "fulfilled" ? teams.value : []); + setMatches(matches.status === "fulfilled" ? matches.value : []); }); - }); - - Promise.all([api.match.get(), api.teams.get()]).then(([matches, teams]) =>{ - this.setState({matches, teams}); - }); - } - } - render() { - const { tournament, matches, teams, show } = this.state; - if(!tournament) return null; - return ( -
-
- { tournament.logo ? {tournament.name} : null } -
- {tournament.name} -
+ } + }) + }, []); + + if(!tournament) return null; + return ( +
+
+ { tournament.logo ? {tournament.name} : null } +
+ {tournament.name}
- -
- ); - } + +
+ ); } + +export default React.memo(Tournament); \ No newline at end of file diff --git a/src/HUD/Trivia/Trivia.tsx b/src/HUD/Trivia/Trivia.tsx index fae4f2d..64d2428 100644 --- a/src/HUD/Trivia/Trivia.tsx +++ b/src/HUD/Trivia/Trivia.tsx @@ -1,43 +1,26 @@ -import React from 'react'; import './trivia.scss'; +import { useAction, useConfig } from '../../API/contexts/actions'; +import React, { useState } from 'react'; -import {configs, actions} from './../../App'; +const Trivia = () => { + const [ show, setShow ] = useState(false); + + const data = useConfig('trivia'); -export default class Trivia extends React.Component { - constructor(props: any) { - super(props); - this.state = { - title:'Title', - content:'Content', - show: false - } - } + useAction('triviaState', (state) => { + setShow(state === "show"); + }); - componentDidMount() { - configs.onChange((data:any) => { - if(!data) return; - const trivia = data.trivia; - if(!trivia) return; - - if(trivia.title && trivia.content){ - this.setState({title:trivia.title, content:trivia.content}) - } - }); - actions.on("triviaState", (state: any) => { - this.setState({show: state === "show"}) - }); - actions.on("toggleTrivia", () => { - this.setState({show: !this.state.show}) - }); - } - - render() { - return ( -
-
{this.state.title}
-
{this.state.content}
-
- ); - } + useAction('toggleCams', () => { + setShow(p => !p); + }); + return ( +
+
{data?.title}
+
{data?.content}
+
+ ); } + +export default React.memo(Trivia); diff --git a/src/HUD/Weapon/Weapon.tsx b/src/HUD/Weapon/Weapon.tsx index 2e9225b..9e2c0bc 100644 --- a/src/HUD/Weapon/Weapon.tsx +++ b/src/HUD/Weapon/Weapon.tsx @@ -1,19 +1,25 @@ -import React from 'react'; -import * as Weapons from './../../assets/Weapons'; +import React from "react"; +import * as Weapons from "./../../assets/Weapons"; interface IProps extends React.SVGProps { - weapon: string, - active: boolean, - isGrenade?: boolean + weapon: string; + active: boolean; + isGrenade?: boolean; } -export default class WeaponImage extends React.Component { - render() { - const { weapon, active, isGrenade, ...rest } = this.props; - const Weapon = (Weapons as any)[weapon]; - const { className, ...svgProps } = rest; - if(!Weapon) return null; - return ( - - ); - } -} \ No newline at end of file +const WeaponImage = ({ weapon, active, isGrenade, ...rest }: IProps) => { + const weaponId = weapon.replace("weapon_", ""); + const Weapon = (Weapons as any)[weaponId]; + const { className, ...svgProps } = rest; + if (!Weapon) return null; + return ( + + ); +}; + +export default React.memo(WeaponImage); diff --git a/src/api/actionManager.ts b/src/api/actionManager.ts deleted file mode 100644 index bfc4f73..0000000 --- a/src/api/actionManager.ts +++ /dev/null @@ -1,65 +0,0 @@ -export default class ActionManager { - listeners: Map; - - constructor(){ - this.listeners = new Map(); - - /*this.on('data', _data => { - });*/ - } - execute(eventName: string, argument?: any){ - const listeners = this.listeners.get(eventName); - if(!listeners) return false; - listeners.forEach(callback => { - if(argument) callback(argument); - else callback(); - }); - return true; - } - - on(eventName: string, listener: Function){ - const listOfListeners = this.listeners.get(eventName) || []; - listOfListeners.push(listener); - this.listeners.set(eventName, listOfListeners); - - return true; - } -} -export class ConfigManager { - listeners: Function[]; - data: any; - - constructor(){ - this.listeners = []; - this.data = {}; - } - save(data: any){ - this.data = data; - this.execute(); - - /*const listeners = this.listeners.get(eventName); - if(!listeners) return false; - listeners.forEach(callback => { - if(argument) callback(argument); - else callback(); - }); - return true;*/ - } - - execute(){ - const listeners = this.listeners; - if(!listeners || !listeners.length) return false; - listeners.forEach(listener => { - listener(this.data); - }); - return true; - } - - onChange(listener: Function){ - const listOfListeners = this.listeners || []; - listOfListeners.push(listener); - this.listeners = listOfListeners; - - return true; - } -} \ No newline at end of file diff --git a/src/api/avatars.ts b/src/api/avatars.ts deleted file mode 100644 index 94adf6e..0000000 --- a/src/api/avatars.ts +++ /dev/null @@ -1,24 +0,0 @@ -import api from './api'; -interface AvatarLoader { - loader: Promise, - url: string, -} - -export const avatars: { [key: string]: AvatarLoader } = {}; - -export const loadAvatarURL = (steamid: string) => { - if(!steamid) return; - if(avatars[steamid]) return avatars[steamid].url; - avatars[steamid] = { - url: '', - loader: new Promise((resolve) => { - api.players.getAvatarURLs(steamid).then(result => { - avatars[steamid].url = result.custom || result.steam; - resolve(result.custom || result.custom); - }).catch(() => { - delete avatars[steamid]; - resolve(''); - }); - }) - } -} diff --git a/src/assets/Icons.tsx b/src/assets/Icons.tsx index 1e4421b..e000b6b 100644 --- a/src/assets/Icons.tsx +++ b/src/assets/Icons.tsx @@ -1,32 +1,32 @@ -import { ReactComponent as ArmorFull } from './../assets/images/icon_armor_full_default.svg'; -import { ReactComponent as ArmorHalf } from './../assets/images/icon_armor_half_default.svg'; -import { ReactComponent as ArmorHalfHelmet } from './../assets/images/icon_armor_half_helmet_default.svg'; -import { ReactComponent as ArmorHelmet } from './../assets/images/icon_armor_helmet_default.svg'; -import { ReactComponent as ArmorNone } from './../assets/images/icon_armor_none_default.svg'; -import { ReactComponent as Blind } from './../assets/images/icon_blind.svg'; -import { ReactComponent as Bomb } from './../assets/images/icon_bomb_default.svg'; -import { ReactComponent as BombExplosion } from './../assets/images/icon_bomb_explosion_default.svg'; -import { ReactComponent as Bullets } from './../assets/images/icon_bullets_default.svg'; -import { ReactComponent as Burning } from './../assets/images/icon_burning.svg'; -import { ReactComponent as C4 } from './../assets/images/icon_c4_default.svg'; -import { ReactComponent as Defuse } from './../assets/images/icon_defuse_default.svg'; -import { ReactComponent as Health } from './../assets/images/icon_health_default.svg'; -import { ReactComponent as HealthFull } from './../assets/images/icon_health_full_default.svg'; -import { ReactComponent as Hourglass } from './../assets/images/icon_hourglass_default.svg'; -import { ReactComponent as Microphone } from './../assets/images/icon_microphone.svg'; -import { ReactComponent as Pause } from './../assets/images/icon_pause_default.svg'; -import { ReactComponent as Skull } from './../assets/images/icon_skull_default.svg'; -import { ReactComponent as Timer } from './../assets/images/icon_timer_default.svg'; -import { ReactComponent as FlashedKill } from './../assets/images/flashed_kill.svg'; -import { ReactComponent as Headshot } from './../assets/images/headshot.svg'; -import { ReactComponent as NoScope } from './../assets/images/noscope.svg'; -import { ReactComponent as SmokeKill } from './../assets/images/smoke_kill.svg'; -import { ReactComponent as Suicide } from './../assets/images/suicide.svg'; -import { ReactComponent as Wallbang } from './../assets/images/wallbang.svg'; +/// +import ArmorFull from './../assets/images/icon_armor_full_default.svg?react'; +import ArmorHalf from './../assets/images/icon_armor_half_default.svg?react'; +import ArmorHalfHelmet from './../assets/images/icon_armor_half_helmet_default.svg?react'; +import ArmorHelmet from './../assets/images/icon_armor_helmet_default.svg?react'; +import ArmorNone from './../assets/images/icon_armor_none_default.svg?react'; +import Blind from './../assets/images/icon_blind.svg?react'; +import Bomb from './../assets/images/icon_bomb_default.svg?react'; +import BombExplosion from './../assets/images/icon_bomb_explosion_default.svg?react'; +import Bullets from './../assets/images/icon_bullets_default.svg?react'; +import Burning from './../assets/images/icon_burning.svg?react'; +import C4 from './../assets/images/icon_c4_default.svg?react'; +import Defuse from './../assets/images/icon_defuse_default.svg?react'; +import Health from './../assets/images/icon_health_default.svg?react'; +import HealthFull from './../assets/images/icon_health_full_default.svg?react'; +import Hourglass from './../assets/images/icon_hourglass_default.svg?react'; +import Microphone from './../assets/images/icon_microphone.svg?react'; +import Pause from './../assets/images/icon_pause_default.svg?react'; +import Skull from './../assets/images/icon_skull_default.svg?react'; +import Timer from './../assets/images/icon_timer_default.svg?react'; +import FlashedKill from './../assets/images/flashed_kill.svg?react'; +import Headshot from './../assets/images/headshot.svg?react'; +import NoScope from './../assets/images/noscope.svg?react'; +import SmokeKill from './../assets/images/smoke_kill.svg?react'; +import Suicide from './../assets/images/suicide.svg?react'; +import Wallbang from './../assets/images/wallbang.svg?react'; import LogoCT from './../assets/images/logo_CT_default.png'; import LogoT from './../assets/images/logo_T_default.png'; -import { ReactComponent as SmallBomb } from "./../assets/images/bomb.svg"; - +import SmallBomb from "./../assets/images/bomb.svg?react"; export { SmallBomb, diff --git a/src/assets/Weapons.tsx b/src/assets/Weapons.tsx index 9cd5697..60d69dc 100644 --- a/src/assets/Weapons.tsx +++ b/src/assets/Weapons.tsx @@ -1,73 +1,73 @@ -import { ReactComponent as ak47 } from './weapons/ak47.svg'; -import { ReactComponent as aug } from './weapons/aug.svg'; -import { ReactComponent as awp } from './weapons/awp.svg'; -import { ReactComponent as bayonet } from './weapons/bayonet.svg'; -import { ReactComponent as bizon } from './weapons/bizon.svg'; -import { ReactComponent as c4 } from './weapons/c4.svg'; -import { ReactComponent as cz75a } from './weapons/cz75a.svg'; -import { ReactComponent as deagle } from './weapons/deagle.svg'; -import { ReactComponent as decoy } from './weapons/decoy.svg'; -import { ReactComponent as elite } from './weapons/elite.svg'; -import { ReactComponent as famas } from './weapons/famas.svg'; -import { ReactComponent as fiveseven } from './weapons/fiveseven.svg'; -import { ReactComponent as flashbang } from './weapons/flashbang.svg'; -import { ReactComponent as g3sg1 } from './weapons/g3sg1.svg'; -import { ReactComponent as galilar } from './weapons/galilar.svg'; -import { ReactComponent as glock } from './weapons/glock.svg'; -import { ReactComponent as hegrenade } from './weapons/hegrenade.svg'; -import { ReactComponent as hkp2000 } from './weapons/hkp2000.svg'; -import { ReactComponent as incgrenade } from './weapons/incgrenade.svg'; -import { ReactComponent as inferno } from './weapons/inferno.svg'; -import { ReactComponent as knife } from './weapons/knife.svg'; -import { ReactComponent as knife_bayonet } from './weapons/knife_bayonet.svg'; -import { ReactComponent as knife_butterfly } from './weapons/knife_butterfly.svg'; -import { ReactComponent as knife_canis } from './weapons/knife_canis.svg'; -import { ReactComponent as knife_cord } from './weapons/knife_cord.svg'; -import { ReactComponent as knife_css } from './weapons/knife_css.svg'; -import { ReactComponent as knife_falchion } from './weapons/knife_falchion.svg'; -import { ReactComponent as knife_flip } from './weapons/knife_flip.svg'; -import { ReactComponent as knife_gut } from './weapons/knife_gut.svg'; -import { ReactComponent as knife_gypsy_jackknife } from './weapons/knife_gypsy_jackknife.svg'; -import { ReactComponent as knife_karambit } from './weapons/knife_karambit.svg'; -import { ReactComponent as knife_m9_bayonet } from './weapons/knife_m9_bayonet.svg'; -import { ReactComponent as knife_outdoor } from './weapons/knife_outdoor.svg'; -import { ReactComponent as knife_push } from './weapons/knife_push.svg'; -import { ReactComponent as knife_skeleton } from './weapons/knife_skeleton.svg'; -import { ReactComponent as knife_stiletto } from './weapons/knife_stiletto.svg'; -import { ReactComponent as knife_survival_bowie } from './weapons/knife_survival_bowie.svg'; -import { ReactComponent as knife_t } from './weapons/knife_t.svg'; -import { ReactComponent as knife_tactical } from './weapons/knife_tactical.svg'; -import { ReactComponent as knife_ursus } from './weapons/knife_ursus.svg'; -import { ReactComponent as knife_widowmaker } from './weapons/knife_widowmaker.svg'; -import { ReactComponent as m249 } from './weapons/m249.svg'; -import { ReactComponent as m4a1 } from './weapons/m4a1.svg'; -import { ReactComponent as m4a1_silencer } from './weapons/m4a1_silencer.svg'; -import { ReactComponent as m4a1_silencer_off } from './weapons/m4a1_silencer_off.svg'; -import { ReactComponent as mac10 } from './weapons/mac10.svg'; -import { ReactComponent as mag7 } from './weapons/mag7.svg'; -import { ReactComponent as molotov } from './weapons/molotov.svg'; -import { ReactComponent as mp5sd } from './weapons/mp5sd.svg'; -import { ReactComponent as mp7 } from './weapons/mp7.svg'; -import { ReactComponent as mp9 } from './weapons/mp9.svg'; -import { ReactComponent as negev } from './weapons/negev.svg'; -import { ReactComponent as nova } from './weapons/nova.svg'; -import { ReactComponent as out } from './weapons/out.svg'; -import { ReactComponent as p250 } from './weapons/p250.svg'; -import { ReactComponent as p90 } from './weapons/p90.svg'; -import { ReactComponent as revolver } from './weapons/revolver.svg'; -import { ReactComponent as sawedoff } from './weapons/sawedoff.svg'; -import { ReactComponent as scar20 } from './weapons/scar20.svg'; -import { ReactComponent as sg556 } from './weapons/sg556.svg'; -import { ReactComponent as smokegrenade } from './weapons/smokegrenade.svg'; -import { ReactComponent as ssg08 } from './weapons/ssg08.svg'; -import { ReactComponent as taser } from './weapons/taser.svg'; -import { ReactComponent as tec9 } from './weapons/tec9.svg'; -import { ReactComponent as trigger_hurt } from './weapons/trigger_hurt.svg'; -import { ReactComponent as ump45 } from './weapons/ump45.svg'; -import { ReactComponent as usp_silencer } from './weapons/usp_silencer.svg'; -import { ReactComponent as usp_silencer_off } from './weapons/usp_silencer_off.svg'; -import { ReactComponent as world } from './weapons/world.svg'; -import { ReactComponent as xm1014 } from './weapons/xm1014.svg'; +import ak47 from './weapons/ak47.svg?react'; +import aug from './weapons/aug.svg?react'; +import awp from './weapons/awp.svg?react'; +import bayonet from './weapons/bayonet.svg?react'; +import bizon from './weapons/bizon.svg?react'; +import c4 from './weapons/c4.svg?react'; +import cz75a from './weapons/cz75a.svg?react'; +import deagle from './weapons/deagle.svg?react'; +import decoy from './weapons/decoy.svg?react'; +import elite from './weapons/elite.svg?react'; +import famas from './weapons/famas.svg?react'; +import fiveseven from './weapons/fiveseven.svg?react'; +import flashbang from './weapons/flashbang.svg?react'; +import g3sg1 from './weapons/g3sg1.svg?react'; +import galilar from './weapons/galilar.svg?react'; +import glock from './weapons/glock.svg?react'; +import hegrenade from './weapons/hegrenade.svg?react'; +import hkp2000 from './weapons/hkp2000.svg?react'; +import incgrenade from './weapons/incgrenade.svg?react'; +import inferno from './weapons/inferno.svg?react'; +import knife from './weapons/knife.svg?react'; +import knife_bayonet from './weapons/knife_bayonet.svg?react'; +import knife_butterfly from './weapons/knife_butterfly.svg?react'; +import knife_canis from './weapons/knife_canis.svg?react'; +import knife_cord from './weapons/knife_cord.svg?react'; +import knife_css from './weapons/knife_css.svg?react'; +import knife_falchion from './weapons/knife_falchion.svg?react'; +import knife_flip from './weapons/knife_flip.svg?react'; +import knife_gut from './weapons/knife_gut.svg?react'; +import knife_gypsy_jackknife from './weapons/knife_gypsy_jackknife.svg?react'; +import knife_karambit from './weapons/knife_karambit.svg?react'; +import knife_m9_bayonet from './weapons/knife_m9_bayonet.svg?react'; +import knife_outdoor from './weapons/knife_outdoor.svg?react'; +import knife_push from './weapons/knife_push.svg?react'; +import knife_skeleton from './weapons/knife_skeleton.svg?react'; +import knife_stiletto from './weapons/knife_stiletto.svg?react'; +import knife_survival_bowie from './weapons/knife_survival_bowie.svg?react'; +import knife_t from './weapons/knife_t.svg?react'; +import knife_tactical from './weapons/knife_tactical.svg?react'; +import knife_ursus from './weapons/knife_ursus.svg?react'; +import knife_widowmaker from './weapons/knife_widowmaker.svg?react'; +import m249 from './weapons/m249.svg?react'; +import m4a1 from './weapons/m4a1.svg?react'; +import m4a1_silencer from './weapons/m4a1_silencer.svg?react'; +import m4a1_silencer_off from './weapons/m4a1_silencer_off.svg?react'; +import mac10 from './weapons/mac10.svg?react'; +import mag7 from './weapons/mag7.svg?react'; +import molotov from './weapons/molotov.svg?react'; +import mp5sd from './weapons/mp5sd.svg?react'; +import mp7 from './weapons/mp7.svg?react'; +import mp9 from './weapons/mp9.svg?react'; +import negev from './weapons/negev.svg?react'; +import nova from './weapons/nova.svg?react'; +import out from './weapons/out.svg?react'; +import p250 from './weapons/p250.svg?react'; +import p90 from './weapons/p90.svg?react'; +import revolver from './weapons/revolver.svg?react'; +import sawedoff from './weapons/sawedoff.svg?react'; +import scar20 from './weapons/scar20.svg?react'; +import sg556 from './weapons/sg556.svg?react'; +import smokegrenade from './weapons/smokegrenade.svg?react'; +import ssg08 from './weapons/ssg08.svg?react'; +import taser from './weapons/taser.svg?react'; +import tec9 from './weapons/tec9.svg?react'; +import trigger_hurt from './weapons/trigger_hurt.svg?react'; +import ump45 from './weapons/ump45.svg?react'; +import usp_silencer from './weapons/usp_silencer.svg?react'; +import usp_silencer_off from './weapons/usp_silencer_off.svg?react'; +import world from './weapons/world.svg?react'; +import xm1014 from './weapons/xm1014.svg?react'; export { ak47, aug, diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/index.css b/src/index.css index cef3b29..2c3fac6 100644 --- a/src/index.css +++ b/src/index.css @@ -1,57 +1,69 @@ -@import "fonts/montserrat.css"; -@import "fonts/stratum2.css"; -html, -body, -#root { - width: 100%; - height: 100%; +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; } body { margin: 0; - font-family: 'Stratum2', 'Montserrat', 'Roboto', sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - /*background-image: url('./assets/bg.png');/**/ + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; } -@font-face { - font-family: 'Louis George Cafe'; - src: local('Louis George Cafe'), url('./fonts/Louis George Cafe.ttf') format('truetype'); +h1 { + font-size: 3.2em; + line-height: 1.1; } -@font-face { - font-family: 'Rounded_Elegance'; - src: local('Rounded_Elegance'), url('./fonts/Rounded_Elegance.ttf') format('truetype'); +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; } -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } } - -.layout { - width: 100%; - height: 100%; - perspective: 500px; -} - -:root { - --main-panel-color: rgba(12, 15, 18, 0.85); - --sub-panel-color: rgba(0, 0, 0, 0.83); - --white-dull: rgba(10, 5, 5, 0.25); - --white-full: rgba(250, 250, 250, 1); - --white-half: rgba(250, 250, 250, 0.5); - --color-gray: rgba(191, 191, 191, 1.0); - --color-moneys: #a7d32e; - --dev-purple: rgba(200, 0, 255, 1); - --color-t: #c19511; - --color-ct: #5788a8; - --color-new-t: #f0c941; - --color-new-ct: #5ab8f4; - --color-bomb: #f22222; - --color-defuse: #2222f2; -} - - - diff --git a/src/index.tsx b/src/index.tsx deleted file mode 100644 index 919d652..0000000 --- a/src/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import "./index.css"; -import "./fonts/Louis George Cafe.ttf"; -import "./fonts/Rounded_Elegance.ttf"; -import App from "./App"; -declare global { - interface Window { - ipcApi: { - send: (channel: string, ...arg: any) => void; - receive: (channel: string, func: (...arg: any) => void) => void; - }; - } -} - -ReactDOM.render(, document.getElementById("root")); - -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: https://bit.ly/CRA-PWA diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..0119e6a --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import * as process from "process"; +globalThis.process = process; +declare global { + interface Window { + ipcApi: { + send: (channel: string, ...arg: any) => void; + receive: (channel: string, func: (...arg: any) => void) => void; + }; + } +} + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5..0000000 --- a/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.json b/tsconfig.json index d0c548f..a7fc6fb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,26 +1,25 @@ { "compilerOptions": { - "noFallthroughCasesInSwitch": true, - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react" + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true }, - "include": [ - "src" - ] + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] } diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..7ae3f71 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,77 @@ +import { PluginOption, defineConfig, loadEnv } from 'vite' +import react from '@vitejs/plugin-react' +import svgr from 'vite-plugin-svgr' +import path from 'path'; +import fs from 'fs'; + +const getDefinitionTemplate = (variable: string, content: string) => { + return `export const ${variable} = ${content} as const;`; +} + +const updateDefinitonFile = async ({ filePath, content }: { filePath: string, content?: string }) => { + const fileContent = content || await fs.promises.readFile(filePath, 'utf-8'); + const fileName = filePath.endsWith("panel.json") ? "panel.ts" : "keybinds.ts"; + + try { + const json = JSON.parse(fileContent); + const variableName = filePath.endsWith("panel.json") ? "panelDefinition" : "keybindDefinition"; + fs.writeFileSync(path.join(".", "src", "API", "contexts", fileName), getDefinitionTemplate(variableName, JSON.stringify(json, null, 2))); + } catch (e) { + console.error(`Updating ${fileName} failed:`); + console.log(e); + } +} + +const settingsAndKeybindsPlugin = () => { + return { + name: 'settingsAndKeybinds', + enforce: 'post', + // HMR + async handleHotUpdate({ file, read }) { + if (file.endsWith('panel.json') || file.endsWith("keybinds.json")) { + console.debug(`[vite][${path.basename(file)}] Rebuilding type definitions...`); + const content = await read(); + + await updateDefinitonFile({ filePath: file, content }); + } + }, + } satisfies PluginOption +} +// https://vitejs.dev/config/ + +export default defineConfig(async ({ command, mode }) => { + + await updateDefinitonFile({ filePath: path.join(".", "public", "panel.json") }); + await updateDefinitonFile({ filePath: path.join(".", "public", "keybinds.json") }); + return ( + { + plugins: [ + react(), + svgr(), + settingsAndKeybindsPlugin() + ], + build: { + outDir: 'build' + }, + resolve: { + alias: { + "readable-stream": "vite-compatible-readable-stream" + } + }, + base: mode === "development" ? '/dev/' : './', + server: { + open: "http://localhost:1349/development/", + host: 'localhost', + port: 3500, + }, + optimizeDeps: { + esbuildOptions: { + // Node.js global to browser globalThis + define: { + global: 'globalThis', + }, + }, + }, + } + ) +})