├── .gitignore ├── .npmignore ├── README.md ├── example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── index.css │ └── index.js ├── lib └── index.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── hooks │ ├── index.js │ ├── useCapsuleCollider.js │ ├── useCharacterState.js │ ├── useInputEventManager.js │ ├── useInputMovementRotation.js │ ├── useKeyboardMouseMovement.js │ ├── useRay.js │ ├── useThirdPersonAnimations.js │ └── useThirdPersonCameraControls.js └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-three-third-person 2 | 3 | Third person controls for driving a character model inside a `@react-three/fiber` app driven by `@react-three/cannon` physics. 4 | 5 | ## Dependencies 6 | 7 | - `react` v16.14.0 or higher 8 | - `@react-three/fiber` v7.0.26 or higher 9 | - `@react-three/drei` v8.7.3 or higher 10 | - `@react-three/cannon` v6.3.0 or higher 11 | 12 | ## Installation 13 | 14 | ```bash 15 | npm install react-three-third-person 16 | ``` 17 | 18 | ## Usage 19 | 20 | ```js 21 | import ThirdPersonCharacterControls from "react-three-third-person"; 22 | import { useGLTF } from "@react-three/drei"; 23 | 24 | const PATH = "https://yourhost.com/animations"; 25 | 26 | const animationPaths = { 27 | idle: `${PATH}/idle.glb`, 28 | walk: `${PATH}/walk.glb`, 29 | run: `${PATH}/run.glb`, 30 | jump: `${PATH}/jump.glb`, 31 | landing: `${PATH}/landing.glb`, 32 | inAir: `${PATH}/falling_idle.glb`, 33 | backpedal: `${PATH}/backpedal.glb`, 34 | turnLeft: `${PATH}/turn_left.glb`, 35 | turnRight: `${PATH}/turn_right.glb`, 36 | strafeLeft: `${PATH}/strafe_left.glb`, 37 | strafeRight: `${PATH}/strafe_right.glb`, 38 | }; 39 | 40 | function ThirdPersonCharacter() { 41 | const characterObj = useGLTF(`${PATH}/your_model.glb`); 42 | const characterProps = { 43 | scale: 1.75, 44 | velocity: 8, 45 | radius: 0.5, 46 | }; 47 | 48 | return ( 49 | 60 | ); 61 | } 62 | ``` 63 | 64 | ## Configuration 65 | 66 | | Prop | Type | Default | Description | 67 | | -------------- | -------------- | --------- | ------------------------------------------------- | 68 | | cameraOptions | object | {} | configuration object for control's camera options | 69 | | characterProps | object | {} | configuration object for character | 70 | | characterObj | THREE.Object3D | undefined | three.js object for character model | 71 | | animationPaths | object | {} | object for animation clip configuration | 72 | | onLoad | function | () => {} | called when animation clips are done loading | 73 | 74 | #### cameraOptions 75 | 76 | | Prop | Type | Default | Description | 77 | | ------------------- | ------- | ------- | -------------------------------------------------------------------- | 78 | | yOffset | float | 1.6 | amount of y added to the camera following the character model | 79 | | minDistance | float | 0.6 | maximum zoom in capability of camera | 80 | | maxDistance | float | 7 | maximum zoom out capability of camera | 81 | | collisionFilterMask | integer | 2 | the cannon.js group given to "world" objects for collision detection | 82 | | cameraCollisionOn | boolean | off | if turned on, will use colllisionFilterMask to add collision to the camera (experimental and unoptimized) | 83 | 84 | #### characterProps 85 | 86 | | Prop | Type | Default | Description | 87 | | ------------------- | ------- | ------- | -------------------------------------------------------------------- | 88 | | scale | float | 1 | amount to scale the character model | 89 | | radius | float | 0.3 | value used for creating character capsule collider | 90 | | velocity | float | 4 | speed at which character moves | 91 | 92 | #### characterObj 93 | 94 | you can use any model here compatible with the [mixamo](https://www.mixamo.com) rig. For best results, use a model sized to the default mixamo character. 95 | 96 | #### animationPaths 97 | 98 | this prop expects an object with key/value pairs pointing to a complete set of animations to use on your character model. Refer to the object below for the full list of animations needed, all of which are available as free animations on [mixamo](https://www.mixamo.com). 99 | 100 | ``` 101 | { 102 | idle: 'idle.glb', 103 | walk: 'walk.glb', 104 | run: 'run.glb', 105 | jump: 'jump.glb', 106 | landing: 'landing.glb', 107 | inAir: 'falling_idle.glb', 108 | backpedal: 'backpedal.glb', 109 | turnLeft: 'turn_left.glb', 110 | turnRight: 'turn_right.glb', 111 | strafeLeft: 'strafe_left.glb', 112 | strafeRight: 'strafe_right.glb', 113 | } 114 | ``` 115 | 116 | ## Example App Usage 117 | 118 | ```js 119 | import React from "react"; 120 | import ReactDOM from "react-dom/client"; 121 | import { Canvas, useThree } from "@react-three/fiber"; 122 | import { Physics, useBox } from "@react-three/cannon"; 123 | import manny from "manny"; 124 | import ThirdPersonCharacterControls from "react-three-third-person"; 125 | 126 | const BASE_ANIMATIONS_PATH = 127 | "https://mannys-game.s3.amazonaws.com/third-person/animations"; 128 | 129 | const animationPaths = { 130 | idle: `${BASE_ANIMATIONS_PATH}/idle.glb`, 131 | walk: `${BASE_ANIMATIONS_PATH}/walk.glb`, 132 | run: `${BASE_ANIMATIONS_PATH}/run.glb`, 133 | jump: `${BASE_ANIMATIONS_PATH}/jump.glb`, 134 | landing: `${BASE_ANIMATIONS_PATH}/landing.glb`, 135 | inAir: `${BASE_ANIMATIONS_PATH}/falling_idle.glb`, 136 | backpedal: `${BASE_ANIMATIONS_PATH}/backpedal.glb`, 137 | turnLeft: `${BASE_ANIMATIONS_PATH}/turn_left.glb`, 138 | turnRight: `${BASE_ANIMATIONS_PATH}/turn_right.glb`, 139 | strafeLeft: `${BASE_ANIMATIONS_PATH}/strafe_left.glb`, 140 | strafeRight: `${BASE_ANIMATIONS_PATH}/strafe_right.glb`, 141 | }; 142 | 143 | function ThirdPersonCharacter() { 144 | const mannyObj = manny(); 145 | 146 | return ( 147 | 157 | ); 158 | } 159 | 160 | function Lighting() { 161 | return ( 162 | <> 163 | 168 | 174 | 175 | ); 176 | } 177 | 178 | function Floor() { 179 | const [ref] = useBox(() => ({ 180 | type: "Static", 181 | args: [25, 0.2, 25], 182 | mass: 0, 183 | material: { 184 | friction: 0, 185 | name: "floor", 186 | }, 187 | collisionFilterGroup: 2, 188 | })); 189 | return ( 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | ); 198 | } 199 | 200 | function Wall({ args, ...props }) { 201 | const [ref] = useBox(() => ({ 202 | type: "Static", 203 | args, 204 | mass: 0, 205 | material: { 206 | friction: 0.3, 207 | name: "wall", 208 | }, 209 | collisionFilterGroup: 2, 210 | ...props, 211 | })); 212 | return ( 213 | 214 | 215 | 216 | 217 | ); 218 | } 219 | 220 | function App() { 221 | return ( 222 |
223 | 232 | 233 | 234 | 235 | 236 | 241 | 246 | 247 | 248 | 249 | 250 |
251 | ); 252 | } 253 | 254 | const root = ReactDOM.createRoot(document.getElementById("root")); 255 | root.render( 256 | 257 | 258 | 259 | ); 260 | ``` 261 | 262 | ## Running Locally 263 | 264 | ```bash 265 | npm run dev 266 | ``` 267 | 268 | Go to `localhost:3000` to see the local test application. 269 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@react-three/cannon": "^6.3.0", 7 | "@react-three/drei": "^8.7.3", 8 | "@react-three/fiber": "^7.0.26", 9 | "manny": "^3.0.0", 10 | "react-three-third-person": "file:../", 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0", 13 | "react-scripts": "5.0.1", 14 | "three": "^0.137.4", 15 | "web-vitals": "^2.1.4" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mannynotfound/react-three-third-person/737077f7e88445c9c3b7112b95616b45a489bccd/example/public/favicon.ico -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /example/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mannynotfound/react-three-third-person/737077f7e88445c9c3b7112b95616b45a489bccd/example/public/logo192.png -------------------------------------------------------------------------------- /example/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mannynotfound/react-three-third-person/737077f7e88445c9c3b7112b95616b45a489bccd/example/public/logo512.png -------------------------------------------------------------------------------- /example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /example/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /example/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | height: 100vh; 3 | width: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /example/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from "react"; 2 | import { Canvas } from "@react-three/fiber"; 3 | import { Physics, Debug, useBox } from "@react-three/cannon"; 4 | import manny from "manny"; 5 | import ThirdPersonCharacterControls from "react-three-third-person"; 6 | 7 | const BASE_ANIMATIONS_PATH = 8 | "https://mannys-game.s3.amazonaws.com/third-person/animations"; 9 | 10 | const animationPaths = { 11 | idle: `${BASE_ANIMATIONS_PATH}/idle.glb`, 12 | walk: `${BASE_ANIMATIONS_PATH}/walk.glb`, 13 | run: `${BASE_ANIMATIONS_PATH}/run.glb`, 14 | jump: `${BASE_ANIMATIONS_PATH}/jump.glb`, 15 | landing: `${BASE_ANIMATIONS_PATH}/landing.glb`, 16 | inAir: `${BASE_ANIMATIONS_PATH}/falling_idle.glb`, 17 | backpedal: `${BASE_ANIMATIONS_PATH}/backpedal.glb`, 18 | turnLeft: `${BASE_ANIMATIONS_PATH}/turn_left.glb`, 19 | turnRight: `${BASE_ANIMATIONS_PATH}/turn_right.glb`, 20 | strafeLeft: `${BASE_ANIMATIONS_PATH}/strafe_left.glb`, 21 | strafeRight: `${BASE_ANIMATIONS_PATH}/strafe_right.glb`, 22 | }; 23 | 24 | function ThirdPersonCharacter() { 25 | const mannyObj = manny(); 26 | 27 | return ( 28 | 38 | ); 39 | } 40 | 41 | function Lighting() { 42 | return ( 43 | <> 44 | 49 | 55 | 56 | ); 57 | } 58 | 59 | function Floor() { 60 | const [ref] = useBox(() => ({ 61 | type: "Static", 62 | args: [25, 0.2, 25], 63 | mass: 0, 64 | material: { 65 | friction: 0, 66 | name: "floor", 67 | }, 68 | collisionFilterGroup: 2, 69 | })); 70 | return ( 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | ); 79 | } 80 | 81 | function Wall({ args, ...props }) { 82 | const [ref] = useBox(() => ({ 83 | type: "Static", 84 | args, 85 | mass: 0, 86 | material: { 87 | friction: 0.3, 88 | name: "wall", 89 | }, 90 | collisionFilterGroup: 2, 91 | ...props, 92 | })); 93 | return ( 94 | 95 | 96 | 97 | 98 | ); 99 | } 100 | 101 | function App() { 102 | return ( 103 |
104 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 125 | 130 | 131 | 132 | 133 | 134 | 135 |
136 | ); 137 | } 138 | 139 | export default App; 140 | -------------------------------------------------------------------------------- /example/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById("root")); 7 | root.render( 8 | 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | import { useRef, useState, useEffect, useMemo, Suspense } from 'react'; 2 | import * as THREE from 'three'; 3 | import { AnimationMixer } from 'three'; 4 | import { useFrame, useThree } from '@react-three/fiber'; 5 | import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; 6 | import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'; 7 | import 'default-passive-events'; 8 | import { useRaycastClosest, useCompoundBody } from '@react-three/cannon'; 9 | import { jsx } from 'react/jsx-runtime'; 10 | 11 | const FBX_LOADER = new FBXLoader(); 12 | const GLTF_LOADER = new GLTFLoader(); 13 | async function asyncForEach(array, callback) { 14 | for (let index = 0; index < array.length; index++) { 15 | await callback(array[index], index, array); 16 | } 17 | } 18 | function loadModelSync(url, loader) { 19 | return new Promise((resolve, reject) => { 20 | loader.load(url, data => resolve(data), null, reject); 21 | }); 22 | } 23 | function useThirdPersonAnimations(characterObj, animationPaths, onLoad) { 24 | if (animationPaths === void 0) { 25 | animationPaths = {}; 26 | } 27 | if (onLoad === void 0) { 28 | onLoad = () => {}; 29 | } 30 | const ref = useRef(); 31 | const [clips, setClips] = useState([]); 32 | const [actualRef, setRef] = useState(ref); 33 | const [mixer, setMixer] = useState(new AnimationMixer(undefined)); 34 | const lazyActions = useRef({}); 35 | const [animations, setAnimations] = useState({}); 36 | 37 | // set character obj + mixer for character 38 | useEffect(() => { 39 | if (characterObj) { 40 | setRef({ 41 | current: characterObj 42 | }); 43 | setMixer(new AnimationMixer(undefined)); 44 | } 45 | }, [characterObj.name]); 46 | 47 | // load animations async initially 48 | useEffect(() => { 49 | const loadAnimations = async () => { 50 | const newAnimations = {}; 51 | const keys = ['idle', 'walk', 'run', 'jump', 'landing', 'inAir', 'backpedal', 'turnLeft', 'turnRight', 'strafeLeft', 'strafeRight']; 52 | await asyncForEach(keys, async key => { 53 | const fileExt = animationPaths[key].split('.').pop(); 54 | const loader = fileExt === 'fbx' ? FBX_LOADER : GLTF_LOADER; 55 | const model = await loadModelSync(animationPaths[key], loader); 56 | newAnimations[key] = model; 57 | }); 58 | setAnimations(newAnimations); 59 | onLoad(); 60 | }; 61 | loadAnimations(); 62 | }, []); 63 | 64 | // set clips once animations are loaded 65 | useEffect(() => { 66 | const clipsToSet = []; 67 | Object.keys(animations).forEach(name => { 68 | var _animations$name, _animations$name$anim; 69 | if ((_animations$name = animations[name]) != null && (_animations$name$anim = _animations$name.animations) != null && _animations$name$anim.length) { 70 | animations[name].animations[0].name = name; 71 | clipsToSet.push(animations[name].animations[0]); 72 | } 73 | }); 74 | if (clips.length < clipsToSet.length) { 75 | setClips(clipsToSet); 76 | } 77 | }, [animations]); 78 | const api = useMemo(() => { 79 | if (!mixer || !clips.length) { 80 | return { 81 | actions: {} 82 | }; 83 | } 84 | const actions = {}; 85 | clips.forEach(clip => Object.defineProperty(actions, clip.name, { 86 | enumerable: true, 87 | get() { 88 | if (actualRef.current) { 89 | lazyActions.current[clip.name] = mixer.clipAction(clip, actualRef.current); 90 | const clampers = ['jump', 'landing']; 91 | if (clampers.includes(clip.name)) { 92 | lazyActions.current[clip.name].setLoop(2200); // 2200 = THREE.LoopOnce 93 | lazyActions.current[clip.name].clampWhenFinished = true; 94 | } 95 | return lazyActions.current[clip.name]; 96 | } 97 | return null; 98 | } 99 | })); 100 | return { 101 | ref: actualRef, 102 | clips, 103 | actions, 104 | names: clips.map(c => c.name), 105 | mixer 106 | }; 107 | }, [clips, characterObj.name, mixer]); 108 | useEffect(() => { 109 | const currentRoot = actualRef.current; 110 | return () => { 111 | // Clean up only when clips change, wipe out lazy actions and uncache clips 112 | lazyActions.current = {}; 113 | Object.values(api.actions).forEach(action => { 114 | if (currentRoot) { 115 | mixer.uncacheAction(action, currentRoot); 116 | } 117 | }); 118 | }; 119 | }, [clips]); 120 | useFrame((_, delta) => { 121 | mixer.update(delta); 122 | }); 123 | return api; 124 | } 125 | 126 | /* 127 | * Based on code written by knav.eth for chainspace (https://somnet.chainrunners.xyz/chainspace) 128 | */ 129 | const CameraControlOperation = { 130 | NONE: -1, 131 | ROTATE: 0, 132 | TOUCH_ROTATE: 3, 133 | TOUCH_ZOOM_ROTATE: 6 134 | }; 135 | const ROTATION_ANGLE = new THREE.Vector3(0, 1, 0); 136 | class CameraState { 137 | operation = CameraControlOperation.NONE; 138 | pointers = []; 139 | pointerPositions = {}; 140 | reset() { 141 | this.operation = CameraControlOperation.NONE; 142 | this.pointers = []; 143 | this.pointerPositions = {}; 144 | } 145 | } 146 | class ThirdPersonCameraControls { 147 | enabled = true; 148 | // How far you can zoom in and out ( PerspectiveCamera only ) 149 | minDistance = 0; 150 | maxDistance = Infinity; 151 | 152 | // How far you can orbit vertically, upper and lower limits. 153 | // Range is 0 to Math.PI radians. 154 | minPolarAngle = 0; 155 | maxPolarAngle = Math.PI; 156 | enableZoom = true; 157 | zoomSpeed = 1.75; 158 | enableRotate = true; 159 | rotateSpeed = 1.0; 160 | 161 | // "target" sets the location of focus, where the object orbits around 162 | targetOffset = new THREE.Vector3(0, 0, 0); 163 | spherical = new THREE.Spherical(3.5, Math.PI / 3, Math.PI); 164 | rotateStart = new THREE.Vector2(); 165 | rotateEnd = new THREE.Vector2(); 166 | rotateDelta = new THREE.Vector2(); 167 | zoomStart = new THREE.Vector2(); 168 | zoomEnd = new THREE.Vector2(); 169 | zoomDelta = new THREE.Vector2(); 170 | outerCameraContainer = new THREE.Object3D(); 171 | constructor(camera, domElement, target, inputManager, options, cameraContainer) { 172 | var _options, _options2; 173 | if (options === void 0) { 174 | options = {}; 175 | } 176 | this.camera = camera; 177 | this.cameraState = new CameraState(); 178 | this.cameraContainer = cameraContainer; 179 | this.domElement = domElement; 180 | this.input = {}; 181 | const k = 'camera'; 182 | inputManager.subscribe('wheel', k, this.handleMouseWheel.bind(this)); 183 | inputManager.subscribe('pointerlockchange', k, this.onPointerLockChange.bind(this)); 184 | inputManager.subscribe('pointerdown', k, this.onPointerDown.bind(this)); 185 | inputManager.subscribe('pointerup', k, this.onPointerUp.bind(this)); 186 | inputManager.subscribe('pointermove', k, this.onPointerMove.bind(this)); 187 | inputManager.subscribe('pointercancel', k, this.onPointerCancel.bind(this)); 188 | inputManager.subscribe('pointerlockerror', k, e => console.error('POINTERLOCK ERROR ', e)); 189 | inputManager.subscribe('contextmenu', k, this.onContextMenu.bind(this)); 190 | this.cameraCollisionOn = (_options = options) == null ? void 0 : _options.cameraCollisionOn; 191 | this.targetOffset.y = ((_options2 = options) == null ? void 0 : _options2.yOffset) ?? 1.6; 192 | this.outerCameraContainer.position.copy(this.targetOffset); 193 | this.outerCameraContainer.add(this.cameraContainer); 194 | this.target = target; 195 | this.target.add(this.outerCameraContainer); 196 | } 197 | _cameraPos = new THREE.Vector3(); 198 | _raycastTargetVector = new THREE.Vector3(); 199 | getCameraPosition(rayResult) { 200 | this.cameraContainer.position.setFromSphericalCoords(this.spherical.radius, this.spherical.phi, this.spherical.theta); 201 | if (rayResult.hasHit && this.cameraCollisionOn) { 202 | this.cameraContainer.position.setFromSphericalCoords(rayResult.distance - 0.1, this.spherical.phi, this.spherical.theta); 203 | } 204 | this.cameraContainer.getWorldPosition(this._cameraPos); 205 | return this._cameraPos; 206 | } 207 | _workingVec = new THREE.Vector3(); 208 | getCameraLookVec() { 209 | this.target.getWorldPosition(this._workingVec).add(this.targetOffset); 210 | return this._workingVec; 211 | } 212 | _workingQuat = new THREE.Quaternion(); 213 | update(rayResult) { 214 | if (this.input.isMouseLooking) { 215 | this._workingQuat.setFromAxisAngle(ROTATION_ANGLE, this.spherical.theta - Math.PI); 216 | this.target.quaternion.multiply(this._workingQuat); 217 | this.spherical.theta = Math.PI; 218 | } 219 | 220 | // restrict phi to be between desired limits 221 | this.spherical.phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, this.spherical.phi)); 222 | this.spherical.makeSafe(); 223 | 224 | // restrict radius to be between desired limits 225 | this.spherical.radius = Math.max(this.minDistance, Math.min(this.maxDistance, this.spherical.radius)); 226 | 227 | // copy maths to actual three.js camera 228 | this.camera.position.copy(this.getCameraPosition(rayResult)); 229 | this.camera.lookAt(this.getCameraLookVec()); 230 | } 231 | getZoomScale() { 232 | return 0.95 ** this.zoomSpeed; 233 | } 234 | rotateLeft(angle) { 235 | this.spherical.theta -= angle; 236 | } 237 | rotateUp(angle) { 238 | this.spherical.phi -= angle; 239 | } 240 | handleApplyRotate(speedMultiplier) { 241 | if (speedMultiplier === void 0) { 242 | speedMultiplier = 1; 243 | } 244 | this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart).multiplyScalar(this.rotateSpeed * speedMultiplier); 245 | const element = this.domElement; 246 | this.rotateLeft(2 * Math.PI * this.rotateDelta.x / element.clientHeight); // yes, height 247 | 248 | this.rotateUp(2 * Math.PI * this.rotateDelta.y / element.clientHeight); 249 | this.rotateStart.copy(this.rotateEnd); 250 | } 251 | zoomOut(zoomScale) { 252 | this.spherical.radius /= zoomScale; 253 | } 254 | zoomIn(zoomScale) { 255 | this.spherical.radius *= zoomScale; 256 | } 257 | 258 | // Event Handlers 259 | handleMouseDownRotate(event) { 260 | this.rotateEnd.set(event.clientX, event.clientY); 261 | this.rotateStart.set(event.clientX, event.clientY); 262 | } 263 | handleMouseMoveRotate(event) { 264 | if (document.pointerLockElement === this.domElement) { 265 | this.rotateEnd.x += event.movementX * 0.25; 266 | this.rotateEnd.y += event.movementY * 0.25 * 0.8; 267 | } else { 268 | this.domElement.requestPointerLock(); 269 | this.domElement.style.cursor = 'none'; 270 | this.rotateEnd.set(event.clientX, event.clientY); 271 | } 272 | this.handleApplyRotate(); 273 | } 274 | handleMouseWheel(event) { 275 | if (event.deltaY < 0) { 276 | this.zoomIn(this.getZoomScale()); 277 | } else if (event.deltaY > 0) { 278 | this.zoomOut(this.getZoomScale()); 279 | } 280 | } 281 | handleTouchStartRotate() { 282 | if (this.cameraState.pointers.length === 1) { 283 | this.rotateStart.set(this.cameraState.pointers[0].pageX, this.cameraState.pointers[0].pageY); 284 | } else { 285 | const x = 0.5 * (this.cameraState.pointers[0].pageX + this.cameraState.pointers[1].pageX); 286 | const y = 0.5 * (this.cameraState.pointers[0].pageY + this.cameraState.pointers[1].pageY); 287 | this.rotateStart.set(x, y); 288 | } 289 | } 290 | handleTouchStartZoom() { 291 | const dx = this.cameraState.pointers[0].pageX - this.cameraState.pointers[1].pageX; 292 | const dy = this.cameraState.pointers[0].pageY - this.cameraState.pointers[1].pageY; 293 | const distance = Math.sqrt(dx * dx + dy * dy); 294 | this.zoomStart.set(0, distance); 295 | } 296 | handleTouchStartZoomRotate() { 297 | if (this.enableZoom) this.handleTouchStartZoom(); 298 | if (this.enableRotate) this.handleTouchStartRotate(); 299 | } 300 | handleTouchMoveRotate(event) { 301 | if (this.cameraState.pointers.length === 1) { 302 | this.rotateEnd.set(event.pageX, event.pageY); 303 | } else { 304 | const position = this.getSecondPointerPosition(event); 305 | const x = 0.5 * (event.pageX + position.x); 306 | const y = 0.5 * (event.pageY + position.y); 307 | this.rotateEnd.set(x, y); 308 | } 309 | this.handleApplyRotate(1.3); 310 | } 311 | handleTouchMoveZoom(event) { 312 | const position = this.getSecondPointerPosition(event); 313 | const dx = event.pageX - position.x; 314 | const dy = event.pageY - position.y; 315 | const distance = Math.sqrt(dx * dx + dy * dy); 316 | this.zoomEnd.set(0, distance); 317 | this.zoomDelta.set(0, (this.zoomEnd.y / this.zoomStart.y) ** this.zoomSpeed); 318 | this.zoomOut(this.zoomDelta.y); 319 | this.zoomStart.copy(this.zoomEnd); 320 | } 321 | handleTouchMoveZoomRotate(event) { 322 | if (this.enableZoom) this.handleTouchMoveZoom(event); 323 | if (this.enableRotate) this.handleTouchMoveRotate(event); 324 | } 325 | 326 | // Event Controllers 327 | onPointerDown(event) { 328 | if (!this.enabled) return; 329 | if (this.cameraState.pointers.length === 0) { 330 | this.domElement.setPointerCapture(event.pointerId); 331 | } 332 | this.addPointer(event); 333 | if (event.pointerType === 'touch') { 334 | this.onTouchStart(event); 335 | } else { 336 | this.onMouseDown(event); 337 | } 338 | } 339 | onPointerMove(event) { 340 | this.lastCheck = Date.now(); 341 | if (!this.enabled) return; 342 | if (!this.input.isMouseLocked && !this.cameraState.pointers.length) return; 343 | if (!this.cameraState.pointers.find(e => e.pointerId === event.pointerId)) { 344 | return; 345 | } 346 | if (event.pointerType === 'touch') { 347 | this.onTouchMove(event); 348 | } else { 349 | this.onMouseMove(event); 350 | } 351 | } 352 | onPointerUp(event) { 353 | if (event.pointerType === 'touch') { 354 | this.onTouchEnd(); 355 | } else { 356 | this.onMouseUp(); 357 | } 358 | this.removePointer(event); 359 | if (this.cameraState.pointers.length === 0 && event.pointerType === 'touch') { 360 | this.domElement.releasePointerCapture(event.pointerId); 361 | } 362 | } 363 | 364 | // Touch 365 | onTouchStart(event) { 366 | this.trackPointer(event); 367 | switch (this.cameraState.pointers.length) { 368 | case 1: 369 | if (!this.enableRotate) return; 370 | this.handleTouchStartRotate(); 371 | this.input.isMouseLooking = true; 372 | this.cameraState.operation = CameraControlOperation.TOUCH_ROTATE; 373 | break; 374 | case 2: 375 | if (!this.enableZoom && !this.enableRotate) return; 376 | this.handleTouchStartZoomRotate(); 377 | this.input.isMouseLooking = true; 378 | this.cameraState.operation = CameraControlOperation.TOUCH_ZOOM_ROTATE; 379 | break; 380 | default: 381 | this.cameraState.operation = CameraControlOperation.NONE; 382 | } 383 | } 384 | onTouchMove(event) { 385 | this.trackPointer(event); 386 | switch (this.cameraState.operation) { 387 | case CameraControlOperation.TOUCH_ROTATE: 388 | if (!this.enableRotate) return; 389 | this.handleTouchMoveRotate(event); 390 | break; 391 | case CameraControlOperation.TOUCH_ZOOM_ROTATE: 392 | if (!this.enableZoom && !this.enableRotate) return; 393 | this.handleTouchMoveZoomRotate(event); 394 | break; 395 | default: 396 | this.cameraState.operation = CameraControlOperation.NONE; 397 | } 398 | } 399 | onTouchEnd() { 400 | this.cameraState.operation = CameraControlOperation.NONE; 401 | } 402 | 403 | // Mouse 404 | onPointerLockChange() { 405 | // do initial check to see if mouse is locked 406 | this.input.isMouseLocked = document.pointerLockElement === this.domElement; 407 | if (!this.input.isMouseLocked) { 408 | // wait 100ms and then check again as sometimes document.pointerLockElement 409 | // is null after doing a document.requestPointerLock() 410 | setTimeout(() => { 411 | this.input.isMouseLocked = document.pointerLockElement === this.domElement; 412 | if (!this.input.isMouseLocked) { 413 | this.input.isMouseLooking = false; 414 | this.cameraState.operation = CameraControlOperation.NONE; 415 | } 416 | }, 100); 417 | } 418 | } 419 | onMouseDown(event) { 420 | switch (event.button) { 421 | case 0: 422 | if (!this.enableRotate) return; 423 | this.handleMouseDownRotate(event); 424 | this.cameraState.operation = CameraControlOperation.ROTATE; 425 | break; 426 | case 1: 427 | this.cameraState.operation = CameraControlOperation.NONE; 428 | break; 429 | case 2: 430 | if (!this.enableRotate) return; 431 | this.input.isMouseLooking = true; 432 | this.rightClickTime = Date.now(); 433 | this.handleMouseDownRotate(event); 434 | this.cameraState.operation = CameraControlOperation.ROTATE; 435 | break; 436 | default: 437 | this.cameraState.operation = CameraControlOperation.NONE; 438 | } 439 | } 440 | onMouseMove(event) { 441 | if (!this.enabled) return; 442 | if (this.cameraState.operation === CameraControlOperation.ROTATE) { 443 | if (!this.enableRotate) return; 444 | this.handleMouseMoveRotate(event); 445 | } 446 | } 447 | onMouseUp() { 448 | this.domElement.style.cursor = 'initial'; 449 | document.exitPointerLock(); 450 | this.input.isMouseLooking = false; 451 | } 452 | onMouseWheel(event) { 453 | if (!this.enabled || !this.enableZoom || this.cameraState.operation !== CameraControlOperation.NONE && this.cameraState.operation !== CameraControlOperation.ROTATE) { 454 | return; 455 | } 456 | this.handleMouseWheel(event); 457 | } 458 | 459 | // Pointer Utils 460 | getSecondPointerPosition(event) { 461 | const pointer = event.pointerId === this.cameraState.pointers[0].pointerId ? this.cameraState.pointers[1] : this.cameraState.pointers[0]; 462 | return this.cameraState.pointerPositions[pointer.pointerId]; 463 | } 464 | addPointer(event) { 465 | this.cameraState.pointers.push(event); 466 | } 467 | removePointer(event) { 468 | delete this.cameraState.pointerPositions[event.pointerId]; 469 | for (let i = 0; i < this.cameraState.pointers.length; i++) { 470 | if (this.cameraState.pointers[i].pointerId === event.pointerId) { 471 | this.cameraState.pointers.splice(i, 1); 472 | return; 473 | } 474 | } 475 | } 476 | trackPointer(event) { 477 | let position = this.cameraState.pointerPositions[event.pointerId]; 478 | if (position === undefined) { 479 | position = new THREE.Vector2(); 480 | this.cameraState.pointerPositions[event.pointerId] = position; 481 | } 482 | position.set(event.pageX, event.pageY); 483 | } 484 | onPointerCancel(event) { 485 | this.removePointer(event); 486 | } 487 | onContextMenu(event) { 488 | if (!this.enabled) return; 489 | event.preventDefault(); 490 | } 491 | reset() { 492 | this.cameraState.reset(); 493 | this.domElement.style.cursor = 'initial'; 494 | try { 495 | document.exitPointerLock(); 496 | } catch (e) { 497 | // lol 498 | } 499 | } 500 | dispose() { 501 | // remove event listeners here 502 | } 503 | } 504 | function useThirdPersonCameraControls(_ref) { 505 | let { 506 | camera, 507 | domElement, 508 | target, 509 | inputManager, 510 | cameraOptions, 511 | cameraContainer 512 | } = _ref; 513 | const [controls, setControls] = useState(null); 514 | useEffect(() => { 515 | if (!target) { 516 | return; 517 | } 518 | const newControls = new ThirdPersonCameraControls(camera, domElement, target, inputManager, { 519 | yOffset: cameraOptions.yOffset || 0 520 | }, cameraContainer.current); 521 | newControls.minDistance = (cameraOptions == null ? void 0 : cameraOptions.minDistance) || 404; 522 | newControls.maxDistance = (cameraOptions == null ? void 0 : cameraOptions.maxDistance) || 808; 523 | setControls(newControls); 524 | return () => { 525 | newControls.dispose(); 526 | }; 527 | }, [camera, domElement, target]); 528 | return controls; 529 | } 530 | 531 | function useInputEventManager(container) { 532 | if (container === void 0) { 533 | container = window; 534 | } 535 | const [subscriptions, setSubscriptions] = useState({}); 536 | const subscribe = (eventName, key, subscribeFn) => { 537 | setSubscriptions(prevState => ({ 538 | ...prevState, 539 | [eventName]: { 540 | ...prevState[eventName], 541 | [key]: subscribeFn 542 | } 543 | })); 544 | }; 545 | const unsubscribe = (eventName, key) => { 546 | setSubscriptions(prevState => { 547 | var _prevState$eventName; 548 | prevState == null ? true : (_prevState$eventName = prevState[eventName]) == null ? true : delete _prevState$eventName[key]; 549 | return prevState; 550 | }); 551 | }; 552 | const makeEventHandler = eventName => event => { 553 | const handlers = subscriptions[eventName] ?? {}; 554 | const subscribers = Object.values(handlers); 555 | subscribers.forEach(sub => sub(event)); 556 | }; 557 | const keydownHandler = makeEventHandler("keydown"); 558 | const keyupHandler = makeEventHandler("keyup"); 559 | const wheelHandler = makeEventHandler("wheel"); 560 | const pointerdownHandler = makeEventHandler("pointerdown"); 561 | const pointerupHandler = makeEventHandler("pointerup"); 562 | const pointermoveHandler = makeEventHandler("pointermove"); 563 | const pointercancelHandler = makeEventHandler("pointercancel"); 564 | const pointerlockchangeHandler = makeEventHandler("pointerlockchange"); 565 | const pointerlockerrorHandler = makeEventHandler("pointerlockerror"); 566 | const contextmenuHandler = makeEventHandler("contextmenu"); 567 | const setupEventListeners = () => { 568 | window.addEventListener("keydown", keydownHandler); 569 | window.addEventListener("keyup", keyupHandler); 570 | container.addEventListener("wheel", wheelHandler); 571 | container.addEventListener("pointerdown", pointerdownHandler); 572 | container.addEventListener("pointerup", pointerupHandler); 573 | container.addEventListener("pointermove", pointermoveHandler); 574 | container.addEventListener("pointercancel", pointercancelHandler); 575 | container.addEventListener("contextmenu", contextmenuHandler); 576 | document.addEventListener("pointerlockchange", pointerlockchangeHandler); 577 | document.addEventListener("pointerlockerror", pointerlockerrorHandler); 578 | return () => { 579 | window.removeEventListener("keydown", keydownHandler); 580 | window.removeEventListener("keyup", keyupHandler); 581 | container.removeEventListener("wheel", wheelHandler); 582 | container.removeEventListener("pointerdown", pointerdownHandler); 583 | container.removeEventListener("pointerup", pointerupHandler); 584 | container.removeEventListener("pointermove", pointermoveHandler); 585 | container.removeEventListener("pointercancel", pointercancelHandler); 586 | container.removeEventListener("contextmenu", contextmenuHandler); 587 | document.removeEventListener("pointerlockchange", pointerlockchangeHandler); 588 | document.removeEventListener("pointerlockerror", pointerlockerrorHandler); 589 | }; 590 | }; 591 | useEffect(setupEventListeners, [subscriptions, container]); 592 | return { 593 | subscribe, 594 | unsubscribe 595 | }; 596 | } 597 | 598 | const defaultMap = { 599 | up: "w", 600 | down: "s", 601 | right: "d", 602 | left: "a", 603 | jump: " ", 604 | walk: "Shift" 605 | }; 606 | const getInputFromKeyboard = (keyMap, keyPressed) => { 607 | let inputFound = ""; 608 | Object.entries(keyMap).forEach(_ref => { 609 | let [k, v] = _ref; 610 | if (v === keyPressed) { 611 | inputFound = k; 612 | } 613 | }); 614 | return inputFound; 615 | }; 616 | function useKeyboardInput(inputManager, userKeyMap) { 617 | if (userKeyMap === void 0) { 618 | userKeyMap = {}; 619 | } 620 | const [isMouseLooking, setIsMouseLooking] = useState(false); 621 | const [inputsPressed, setInputsPressed] = useState({}); 622 | const keyMap = { 623 | ...defaultMap, 624 | ...userKeyMap 625 | }; 626 | function downHandler(_ref2) { 627 | let { 628 | key 629 | } = _ref2; 630 | const input = getInputFromKeyboard(keyMap, key); 631 | if (input) { 632 | setInputsPressed(prevState => ({ 633 | ...prevState, 634 | [input]: true 635 | })); 636 | } 637 | } 638 | const upHandler = _ref3 => { 639 | let { 640 | key 641 | } = _ref3; 642 | const input = getInputFromKeyboard(keyMap, key); 643 | if (input) { 644 | setInputsPressed(prevState => ({ 645 | ...prevState, 646 | [input]: false 647 | })); 648 | } 649 | }; 650 | function pointerdownHandler(_ref4) { 651 | let { 652 | button 653 | } = _ref4; 654 | if (button === 2) { 655 | setIsMouseLooking(true); 656 | } 657 | } 658 | const pointerupHandler = _ref5 => { 659 | let { 660 | button 661 | } = _ref5; 662 | if (button === 2) { 663 | setIsMouseLooking(false); 664 | } 665 | }; 666 | useEffect(() => { 667 | inputManager.subscribe("keydown", "character-controls", downHandler); 668 | inputManager.subscribe("keyup", "character-controls", upHandler); 669 | inputManager.subscribe("pointerdown", "character-controls", pointerdownHandler); 670 | inputManager.subscribe("pointerup", "character-controls", pointerupHandler); 671 | return () => { 672 | inputManager.unsubscribe("keydown", "character-controls"); 673 | inputManager.unsubscribe("keyup", "character-controls"); 674 | inputManager.unsubscribe("pointerdown", "character-controls"); 675 | inputManager.unsubscribe("pointerup", "character-controls"); 676 | }; 677 | }, []); 678 | return { 679 | ...inputsPressed, 680 | isMouseLooking 681 | }; 682 | } 683 | 684 | /* 685 | * Based on code written by knav.eth for chainspace (https://somnet.chainrunners.xyz/chainspace) 686 | */ 687 | 688 | /** 689 | * Finds an angle between two vectors 690 | * @param {THREE.Vector3} v1 691 | * @param {THREE.Vector3} v2 692 | */ 693 | function getAngleBetweenVectors(v1, v2, dotThreshold) { 694 | if (dotThreshold === void 0) { 695 | dotThreshold = 0.0005; 696 | } 697 | let angle; 698 | const dot = v1.dot(v2); 699 | 700 | // If dot is close to 1, we'll round angle to zero 701 | if (dot > 1 - dotThreshold) { 702 | angle = 0; 703 | } else if (dot < -1 + dotThreshold) { 704 | // Dot too close to -1 705 | angle = Math.PI; 706 | } else { 707 | // Get angle difference in radians 708 | angle = Math.acos(dot); 709 | } 710 | return angle; 711 | } 712 | 713 | /** 714 | * Finds an angle between two vectors with a sign relative to normal vector 715 | */ 716 | function getSignedAngleBetweenVectors(v1, v2, normal, dotThreshold) { 717 | if (normal === void 0) { 718 | normal = new THREE.Vector3(0, 1, 0); 719 | } 720 | if (dotThreshold === void 0) { 721 | dotThreshold = 0.0005; 722 | } 723 | let angle = getAngleBetweenVectors(v1, v2, dotThreshold); 724 | 725 | // Get vector pointing up or down 726 | const cross = new THREE.Vector3().crossVectors(v1, v2); 727 | // Compare cross with normal to find out direction 728 | if (normal.dot(cross) < 0) { 729 | angle = -angle; 730 | } 731 | return angle; 732 | } 733 | function getRotationDirection(_ref) { 734 | let { 735 | left, 736 | right, 737 | isMouseLooking 738 | } = _ref; 739 | let direction = 0; 740 | if (!isMouseLooking) { 741 | if (left) { 742 | direction = -1; 743 | } 744 | if (right) { 745 | direction = 1; 746 | } 747 | } 748 | return direction; 749 | } 750 | function getMovementDirection(_ref2) { 751 | let { 752 | up, 753 | down, 754 | right, 755 | left, 756 | isMouseLooking 757 | } = _ref2; 758 | const positiveX = isMouseLooking && right ? -1 : 0; 759 | const negativeX = isMouseLooking && left ? 1 : 0; 760 | const positiveZ = up ? 1 : 0; 761 | const negativeZ = down ? -1 : 0; 762 | return new THREE.Vector3(positiveX + negativeX, 0, positiveZ + negativeZ).normalize(); 763 | } 764 | const FORWARD = new THREE.Vector3(0, 0, 1); 765 | function getModelRotation(inputs) { 766 | const { 767 | up, 768 | down, 769 | right, 770 | left, 771 | isMouseLooking 772 | } = inputs; 773 | const movementDirection = getMovementDirection(inputs); 774 | let modelRotation = 0; 775 | if ((up || down) && !(down && up) && (left || right) && isMouseLooking) { 776 | const rotationDirection = getRotationDirection(inputs); 777 | const movementAngle = getSignedAngleBetweenVectors(movementDirection, FORWARD); 778 | if (up) { 779 | modelRotation = rotationDirection === 0 ? -movementAngle : Math.PI / 8 * rotationDirection * -1; 780 | } else if (down) { 781 | if (rotationDirection === 0) { 782 | if (movementDirection.x > 0) { 783 | modelRotation = Math.PI - movementAngle; 784 | } else if (movementDirection.x < 0) { 785 | modelRotation = Math.PI - movementAngle; 786 | } 787 | } else { 788 | modelRotation = Math.PI / 8 * rotationDirection * -1; 789 | } 790 | } 791 | } 792 | return modelRotation; 793 | } 794 | function useInputMovementRotation(inputs) { 795 | const direction = getRotationDirection(inputs); 796 | const rotation = getModelRotation(inputs); 797 | const movement = getMovementDirection(inputs); 798 | return { 799 | model: { 800 | direction, 801 | rotation 802 | }, 803 | movement 804 | }; 805 | } 806 | 807 | const getAnimationFromUserInputs = inputs => { 808 | const { 809 | up, 810 | down, 811 | right, 812 | left, 813 | isMouseLooking 814 | } = inputs; 815 | if (up && !down) { 816 | return 'run'; 817 | } 818 | if (down && !up) { 819 | return 'backpedal'; 820 | } 821 | if (!right && left) { 822 | return isMouseLooking ? 'strafeLeft' : 'turnLeft'; 823 | } 824 | if (!left && right) { 825 | return isMouseLooking ? 'strafeRight' : 'turnRight'; 826 | } 827 | return 'idle'; 828 | }; 829 | function useCharacterState(inputs, position, mixer) { 830 | if (inputs === void 0) { 831 | inputs = {}; 832 | } 833 | const [characterState, setCharacterState] = useState({ 834 | animation: 'idle', 835 | isJumping: false, 836 | inAir: false, 837 | isMoving: false 838 | }); 839 | const [jumpPressed, setJumpPressed] = useState(false); 840 | const [landed, setLanded] = useState(); 841 | const { 842 | up, 843 | down, 844 | right, 845 | left, 846 | jump, 847 | isMouseLooking 848 | } = inputs; 849 | const { 850 | isJumping, 851 | inAir, 852 | isLanding 853 | } = characterState; 854 | useEffect(() => { 855 | setJumpPressed(jump); 856 | setLanded(false); 857 | }, [jump]); 858 | const rayFrom = [position[0], position[1], position[2]]; 859 | const rayTo = [position[0], position[1] - 0.2, position[2]]; 860 | useRaycastClosest({ 861 | from: rayFrom, 862 | to: rayTo, 863 | skipBackfaces: true 864 | }, e => { 865 | if (e.hasHit && !landed) { 866 | setLanded(true); 867 | } 868 | }, [position]); 869 | useEffect(() => { 870 | if (inAir && landed) { 871 | setCharacterState(prevState => ({ 872 | ...prevState, 873 | inAir: false, 874 | animation: 'landing', 875 | isLanding: true 876 | })); 877 | } 878 | }, [landed, inAir]); 879 | useEffect(() => { 880 | setCharacterState(prevState => ({ 881 | ...prevState, 882 | isMoving: up || down || left || right 883 | })); 884 | }, [up, down, left, right]); 885 | useEffect(() => { 886 | if (isJumping || inAir) { 887 | return; 888 | } 889 | const newState = { 890 | animation: getAnimationFromUserInputs(inputs) 891 | }; 892 | if (jump && !jumpPressed) { 893 | newState.animation = 'jump'; 894 | newState.isJumping = true; 895 | } 896 | 897 | // let landing animation playout if we're still landing 898 | if (isLanding && newState.animation === 'idle') { 899 | return; 900 | } 901 | setCharacterState(prevState => ({ 902 | ...prevState, 903 | isLanding: false, 904 | ...newState 905 | })); 906 | }, [up, down, left, right, jump, isMouseLooking, isJumping, inAir]); 907 | useEffect(() => { 908 | const checker = () => { 909 | setCharacterState(prevState => ({ 910 | ...prevState, 911 | isJumping: false, 912 | inAir: true, 913 | animation: 'inAir' 914 | })); 915 | }; 916 | if (characterState.isJumping) { 917 | // play 200ms of jump animation then transition to inAir 918 | setTimeout(checker, 200); 919 | } 920 | return () => { 921 | clearTimeout(checker); 922 | }; 923 | }, [characterState.isJumping]); 924 | useEffect(() => { 925 | if (!mixer) { 926 | return; 927 | } 928 | const onMixerFinish = () => { 929 | setCharacterState(prevState => ({ 930 | ...prevState, 931 | isJumping: false, 932 | inAir: false, 933 | isLanding: false, 934 | animation: 'idle' 935 | })); 936 | }; 937 | mixer.addEventListener('finished', onMixerFinish); 938 | return () => { 939 | mixer.removeEventListener('finished', onMixerFinish); 940 | }; 941 | }, [mixer]); 942 | return characterState; 943 | } 944 | 945 | function useCapsuleCollider(radius) { 946 | if (radius === void 0) { 947 | radius = 0.5; 948 | } 949 | const [, collider] = useCompoundBody(() => ({ 950 | mass: 0.2, 951 | fixedRotation: true, 952 | linearDamping: 0, 953 | angularDamping: 0, 954 | material: { 955 | friction: 0, 956 | name: 'no-fric-zone' 957 | }, 958 | shapes: [{ 959 | type: 'Sphere', 960 | position: [0, radius, 0], 961 | args: [radius] 962 | }, { 963 | type: 'Sphere', 964 | position: [0, radius * 4.2, 0], 965 | args: [radius] 966 | }, { 967 | type: 'Sphere', 968 | position: [0, radius * 5 - radius * 2.3, 0], 969 | args: [radius] 970 | }], 971 | position: [0, 0, 0], 972 | rotation: [0, Math.PI, 0], 973 | collisionFilterGroup: 1 974 | })); 975 | return collider; 976 | } 977 | 978 | function useRay(_ref) { 979 | let { 980 | rayVector, 981 | position, 982 | collisionFilterMask 983 | } = _ref; 984 | const rayChecker = useRef(setTimeout); 985 | const from = [position[0], position[1], position[2]]; 986 | const to = [rayVector.current.x, rayVector.current.y, rayVector.current.z]; 987 | const [ray, setRay] = useState({}); 988 | useRaycastClosest({ 989 | from, 990 | to, 991 | skipBackfaces: true, 992 | collisionFilterMask 993 | }, e => { 994 | clearTimeout(rayChecker.current); 995 | setRay({ 996 | hasHit: e.hasHit, 997 | distance: e.distance 998 | }); 999 | // this callback only fires constantly on collision so this 1000 | // timeout resets state once we've stopped colliding 1001 | rayChecker.current = setTimeout(() => { 1002 | setRay({}); 1003 | }, 100); 1004 | }, [from, to]); 1005 | return ray; 1006 | } 1007 | 1008 | const ThirdPersonCharacterControls = _ref => { 1009 | let { 1010 | cameraOptions = {}, 1011 | characterObj, 1012 | characterProps = {}, 1013 | animationPaths = {}, 1014 | onLoad 1015 | } = _ref; 1016 | const { 1017 | camera, 1018 | gl: { 1019 | domElement 1020 | } 1021 | } = useThree(); 1022 | // set up refs that influence character and camera position 1023 | const collider = useCapsuleCollider(characterProps.radius); 1024 | const [position, setPosition] = useState([0, 0, 0]); 1025 | const modelRef = useRef(); 1026 | const cameraContainer = useRef(new THREE.Object3D()); 1027 | const rayVector = useRef(new THREE.Vector3()); 1028 | const ray = useRay({ 1029 | position, 1030 | rayVector, 1031 | ...cameraOptions 1032 | }); 1033 | 1034 | // get character state based on user inputs + collider position + animations 1035 | const inputManager = useInputEventManager(domElement); 1036 | const inputs = useKeyboardInput(inputManager); 1037 | const controls = useThirdPersonCameraControls({ 1038 | camera, 1039 | domElement, 1040 | target: modelRef.current, 1041 | inputManager, 1042 | cameraOptions, 1043 | cameraContainer 1044 | }); 1045 | const { 1046 | actions, 1047 | mixer 1048 | } = useThirdPersonAnimations(characterObj, animationPaths, onLoad); 1049 | const { 1050 | animation, 1051 | isMoving 1052 | } = useCharacterState(inputs, position, mixer); 1053 | 1054 | // subscribe to collider velocity/position changes 1055 | const charVelocity = characterProps.velocity ?? 4; 1056 | const velocity = useRef([0, 0, 0]); 1057 | useEffect(() => { 1058 | collider.velocity.subscribe(v => { 1059 | velocity.current = v; 1060 | }); 1061 | collider.position.subscribe(p => { 1062 | var _modelRef$current; 1063 | // position is set on collider so we copy it to model 1064 | (_modelRef$current = modelRef.current) == null ? void 0 : _modelRef$current.position.set(...p); 1065 | // setState with position to useCharacterState 1066 | setPosition(p); 1067 | }); 1068 | }, []); 1069 | useFrame(() => { 1070 | let newRotation = new THREE.Euler(); 1071 | let xVelocity = 0; 1072 | let zVelocity = 0; 1073 | const { 1074 | quaternion 1075 | } = modelRef.current; 1076 | if (isMoving) { 1077 | const { 1078 | model, 1079 | movement 1080 | } = useInputMovementRotation(inputs); 1081 | 1082 | // first rotate the model group 1083 | modelRef.current.rotateY(model.direction * -0.05); 1084 | newRotation = characterObj.rotation.clone(); 1085 | newRotation.y = model.rotation; 1086 | const mtx = new THREE.Matrix4().makeRotationFromQuaternion(quaternion); 1087 | movement.applyMatrix4(mtx); 1088 | 1089 | // then apply velocity to collider influenced by model groups rotation 1090 | const baseVelocity = inputs.down ? charVelocity / 2 : charVelocity; 1091 | xVelocity = movement.x * baseVelocity; 1092 | zVelocity = movement.z * baseVelocity; 1093 | } 1094 | collider.velocity.set(xVelocity, velocity.current[1], zVelocity); 1095 | 1096 | // after applying x/z velocity, apply y velocity if user has jumped while grounded 1097 | const isGrounded = Math.abs(velocity.current[1].toFixed(2)) === 0; 1098 | if (animation === 'jump' && isGrounded) { 1099 | collider.velocity.set(velocity.current[0], 8, velocity.current[2]); 1100 | } 1101 | 1102 | // rotate character model inside model group 1103 | const newQuat = new THREE.Quaternion().setFromEuler(newRotation); 1104 | characterObj.quaternion.slerp(newQuat, 0.1); 1105 | 1106 | // quaternion is set on model group so we copy it to collider 1107 | collider.quaternion.copy(quaternion); 1108 | // check camera raycast collision and pass that to controls to 1109 | cameraContainer.current.getWorldPosition(rayVector.current); 1110 | controls == null ? void 0 : controls.update(ray); 1111 | }); 1112 | 1113 | // Transition to new animation when loaded 1114 | useEffect(() => { 1115 | var _actions$animation; 1116 | actions == null ? void 0 : (_actions$animation = actions[animation]) == null ? void 0 : _actions$animation.reset().fadeIn(0.2).play(); 1117 | return () => { 1118 | var _actions$animation2; 1119 | actions == null ? void 0 : (_actions$animation2 = actions[animation]) == null ? void 0 : _actions$animation2.fadeOut(0.2); 1120 | }; 1121 | }, [animation, actions]); 1122 | return /*#__PURE__*/jsx("group", { 1123 | ref: modelRef, 1124 | rotation: [0, Math.PI, 0], 1125 | ...characterProps, 1126 | children: /*#__PURE__*/jsx(Suspense, { 1127 | fallback: () => null, 1128 | children: /*#__PURE__*/jsx("primitive", { 1129 | object: characterObj, 1130 | dispose: null 1131 | }) 1132 | }) 1133 | }); 1134 | }; 1135 | 1136 | export { ThirdPersonCharacterControls as default }; 1137 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-three-third-person", 3 | "version": "0.4.06", 4 | "description": "package for adding third person controls in @react-three/fiber.", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "dev": "cd example && npm start", 8 | "build": "rollup -c" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/mannynotfound/react-three-third-person.git" 13 | }, 14 | "author": "@mannynotfound", 15 | "license": "WTFPL", 16 | "devDependencies": { 17 | "@babel/core": "^7.17.8", 18 | "@babel/plugin-transform-runtime": "^7.17.0", 19 | "@babel/preset-env": "^7.16.11", 20 | "@babel/preset-react": "^7.16.7", 21 | "@rollup/plugin-babel": "^5.3.1", 22 | "@rollup/plugin-node-resolve": "^13.1.3", 23 | "rollup": "^2.70.1" 24 | }, 25 | "dependencies": { 26 | "default-passive-events": "^2.0.0" 27 | }, 28 | "peerDependencies": { 29 | "@react-three/fiber": ">=7", 30 | "@react-three/drei": ">=8", 31 | "@react-three/cannon": ">=6", 32 | "react": ">=16", 33 | "three": ">=0.137" 34 | }, 35 | "keywords": [ 36 | "three.js", 37 | "react-three-fiber", 38 | "cannon-js" 39 | ], 40 | "homepage": "https://mannynotfound.github.io" 41 | } 42 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from "@rollup/plugin-babel"; 2 | import pluginNodeResolve from "@rollup/plugin-node-resolve"; 3 | 4 | // These are our dependencies, everything else is in the bundle 5 | const external = [ 6 | "@react-three/fiber", 7 | "@react-three/drei", 8 | "@react-three/cannon", 9 | "default-passive-events", 10 | "react", 11 | "react/jsx-runtime", 12 | "three", 13 | ]; 14 | const extensions = [".js", ".jsx", ".json"]; 15 | 16 | const getBabelOptions = ({ useESModules }, targets) => ({ 17 | babelHelpers: "runtime", 18 | babelrc: false, 19 | extensions, 20 | include: ["src/**/*", "**/node_modules/**"], 21 | plugins: [["@babel/transform-runtime", { regenerator: false, useESModules }]], 22 | presets: [ 23 | ["@babel/preset-env", { loose: true, modules: false, targets }], 24 | ["@babel/preset-react", { runtime: "automatic" }], 25 | ], 26 | }); 27 | 28 | export default [ 29 | { 30 | external, 31 | input: `./src/index.js`, 32 | output: { dir: "lib", format: "es" }, 33 | plugins: [ 34 | pluginNodeResolve({ extensions }), 35 | babel( 36 | getBabelOptions( 37 | { useESModules: true }, 38 | ">1%, not dead, not ie 11, not op_mini all" 39 | ) 40 | ), 41 | ], 42 | }, 43 | ]; 44 | -------------------------------------------------------------------------------- /src/hooks/index.js: -------------------------------------------------------------------------------- 1 | export { default as useThirdPersonAnimations } from "./useThirdPersonAnimations"; 2 | export { default as useThirdPersonCameraControls } from "./useThirdPersonCameraControls"; 3 | export { default as useInputEventManager } from "./useInputEventManager"; 4 | export { default as useKeyboardMouseMovement } from "./useKeyboardMouseMovement"; 5 | export { default as useInputMovementRotation } from "./useInputMovementRotation"; 6 | export { default as useCharacterState } from "./useCharacterState"; 7 | export { default as useCapsuleCollider } from "./useCapsuleCollider"; 8 | export { default as useRay } from "./useRay"; 9 | -------------------------------------------------------------------------------- /src/hooks/useCapsuleCollider.js: -------------------------------------------------------------------------------- 1 | import { useCompoundBody } from '@react-three/cannon'; 2 | 3 | export default function useCapsuleCollider(radius = 0.5) { 4 | const [, collider] = useCompoundBody(() => ({ 5 | mass: 0.2, 6 | fixedRotation: true, 7 | linearDamping: 0, 8 | angularDamping: 0, 9 | material: { 10 | friction: 0, 11 | name: 'no-fric-zone', 12 | }, 13 | shapes: [ 14 | { type: 'Sphere', position: [0, radius, 0], args: [radius] }, 15 | { type: 'Sphere', position: [0, radius * 4.2, 0], args: [radius] }, 16 | { 17 | type: 'Sphere', 18 | position: [0, radius * 5 - radius * 2.3, 0], 19 | args: [radius], 20 | }, 21 | ], 22 | position: [0, 0, 0], 23 | rotation: [0, Math.PI, 0], 24 | collisionFilterGroup: 1, 25 | })); 26 | 27 | return collider; 28 | } 29 | -------------------------------------------------------------------------------- /src/hooks/useCharacterState.js: -------------------------------------------------------------------------------- 1 | import { useRaycastClosest } from '@react-three/cannon'; 2 | import { useState, useEffect } from 'react'; 3 | 4 | const getAnimationFromUserInputs = (inputs) => { 5 | const { up, down, right, left, isMouseLooking } = inputs; 6 | 7 | if (up && !down) { 8 | return 'run'; 9 | } 10 | 11 | if (down && !up) { 12 | return 'backpedal'; 13 | } 14 | 15 | if (!right && left) { 16 | return isMouseLooking ? 'strafeLeft' : 'turnLeft'; 17 | } 18 | 19 | if (!left && right) { 20 | return isMouseLooking ? 'strafeRight' : 'turnRight'; 21 | } 22 | 23 | return 'idle'; 24 | }; 25 | 26 | export default function useCharacterState(inputs = {}, position, mixer) { 27 | const [characterState, setCharacterState] = useState({ 28 | animation: 'idle', 29 | isJumping: false, 30 | inAir: false, 31 | isMoving: false, 32 | }); 33 | 34 | const [jumpPressed, setJumpPressed] = useState(false); 35 | const [landed, setLanded] = useState(); 36 | 37 | const { up, down, right, left, jump, isMouseLooking } = inputs; 38 | const { isJumping, inAir, isLanding } = characterState; 39 | 40 | useEffect(() => { 41 | setJumpPressed(jump); 42 | setLanded(false); 43 | }, [jump]); 44 | 45 | const rayFrom = [position[0], position[1], position[2]]; 46 | const rayTo = [position[0], position[1] - 0.2, position[2]]; 47 | useRaycastClosest( 48 | { 49 | from: rayFrom, 50 | to: rayTo, 51 | skipBackfaces: true, 52 | }, 53 | (e) => { 54 | if (e.hasHit && !landed) { 55 | setLanded(true); 56 | } 57 | }, 58 | [position] 59 | ); 60 | 61 | useEffect(() => { 62 | if (inAir && landed) { 63 | setCharacterState((prevState) => ({ 64 | ...prevState, 65 | inAir: false, 66 | animation: 'landing', 67 | isLanding: true, 68 | })); 69 | } 70 | }, [landed, inAir]); 71 | 72 | useEffect(() => { 73 | setCharacterState((prevState) => ({ 74 | ...prevState, 75 | isMoving: up || down || left || right, 76 | })); 77 | }, [up, down, left, right]); 78 | 79 | useEffect(() => { 80 | if (isJumping || inAir) { 81 | return; 82 | } 83 | const newState = { 84 | animation: getAnimationFromUserInputs(inputs), 85 | }; 86 | 87 | if (jump && !jumpPressed) { 88 | newState.animation = 'jump'; 89 | newState.isJumping = true; 90 | } 91 | 92 | // let landing animation playout if we're still landing 93 | if (isLanding && newState.animation === 'idle') { 94 | return; 95 | } 96 | 97 | setCharacterState((prevState) => ({ 98 | ...prevState, 99 | isLanding: false, 100 | ...newState, 101 | })); 102 | }, [up, down, left, right, jump, isMouseLooking, isJumping, inAir]); 103 | 104 | useEffect(() => { 105 | const checker = () => { 106 | setCharacterState((prevState) => ({ 107 | ...prevState, 108 | isJumping: false, 109 | inAir: true, 110 | animation: 'inAir', 111 | })); 112 | }; 113 | if (characterState.isJumping) { 114 | // play 200ms of jump animation then transition to inAir 115 | setTimeout(checker, 200); 116 | } 117 | return () => { 118 | clearTimeout(checker); 119 | }; 120 | }, [characterState.isJumping]); 121 | 122 | useEffect(() => { 123 | if (!mixer) { 124 | return; 125 | } 126 | const onMixerFinish = () => { 127 | setCharacterState((prevState) => ({ 128 | ...prevState, 129 | isJumping: false, 130 | inAir: false, 131 | isLanding: false, 132 | animation: 'idle', 133 | })); 134 | }; 135 | 136 | mixer.addEventListener('finished', onMixerFinish); 137 | 138 | return () => { 139 | mixer.removeEventListener('finished', onMixerFinish); 140 | }; 141 | }, [mixer]); 142 | 143 | return characterState; 144 | } 145 | -------------------------------------------------------------------------------- /src/hooks/useInputEventManager.js: -------------------------------------------------------------------------------- 1 | import "default-passive-events"; 2 | import { useState, useEffect } from "react"; 3 | 4 | export default function useInputEventManager(container = window) { 5 | const [subscriptions, setSubscriptions] = useState({}); 6 | 7 | const subscribe = (eventName, key, subscribeFn) => { 8 | setSubscriptions((prevState) => ({ 9 | ...prevState, 10 | [eventName]: { 11 | ...prevState[eventName], 12 | [key]: subscribeFn, 13 | }, 14 | })); 15 | }; 16 | 17 | const unsubscribe = (eventName, key) => { 18 | setSubscriptions((prevState) => { 19 | delete prevState?.[eventName]?.[key]; 20 | return prevState; 21 | }); 22 | }; 23 | 24 | const makeEventHandler = (eventName) => (event) => { 25 | const handlers = subscriptions[eventName] ?? {}; 26 | const subscribers = Object.values(handlers); 27 | subscribers.forEach((sub) => sub(event)); 28 | }; 29 | 30 | const keydownHandler = makeEventHandler("keydown"); 31 | const keyupHandler = makeEventHandler("keyup"); 32 | const wheelHandler = makeEventHandler("wheel"); 33 | const pointerdownHandler = makeEventHandler("pointerdown"); 34 | const pointerupHandler = makeEventHandler("pointerup"); 35 | const pointermoveHandler = makeEventHandler("pointermove"); 36 | const pointercancelHandler = makeEventHandler("pointercancel"); 37 | const pointerlockchangeHandler = makeEventHandler("pointerlockchange"); 38 | const pointerlockerrorHandler = makeEventHandler("pointerlockerror"); 39 | const contextmenuHandler = makeEventHandler("contextmenu"); 40 | 41 | const setupEventListeners = () => { 42 | window.addEventListener("keydown", keydownHandler); 43 | window.addEventListener("keyup", keyupHandler); 44 | 45 | container.addEventListener("wheel", wheelHandler); 46 | container.addEventListener("pointerdown", pointerdownHandler); 47 | container.addEventListener("pointerup", pointerupHandler); 48 | container.addEventListener("pointermove", pointermoveHandler); 49 | container.addEventListener("pointercancel", pointercancelHandler); 50 | container.addEventListener("contextmenu", contextmenuHandler); 51 | 52 | document.addEventListener("pointerlockchange", pointerlockchangeHandler); 53 | document.addEventListener("pointerlockerror", pointerlockerrorHandler); 54 | 55 | return () => { 56 | window.removeEventListener("keydown", keydownHandler); 57 | window.removeEventListener("keyup", keyupHandler); 58 | 59 | container.removeEventListener("wheel", wheelHandler); 60 | container.removeEventListener("pointerdown", pointerdownHandler); 61 | container.removeEventListener("pointerup", pointerupHandler); 62 | container.removeEventListener("pointermove", pointermoveHandler); 63 | container.removeEventListener("pointercancel", pointercancelHandler); 64 | container.removeEventListener("contextmenu", contextmenuHandler); 65 | 66 | document.removeEventListener( 67 | "pointerlockchange", 68 | pointerlockchangeHandler 69 | ); 70 | document.removeEventListener("pointerlockerror", pointerlockerrorHandler); 71 | }; 72 | }; 73 | 74 | useEffect(setupEventListeners, [subscriptions, container]); 75 | 76 | return { 77 | subscribe, 78 | unsubscribe, 79 | }; 80 | } 81 | -------------------------------------------------------------------------------- /src/hooks/useInputMovementRotation.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on code written by knav.eth for chainspace (https://somnet.chainrunners.xyz/chainspace) 3 | */ 4 | import * as THREE from "three"; 5 | 6 | /** 7 | * Finds an angle between two vectors 8 | * @param {THREE.Vector3} v1 9 | * @param {THREE.Vector3} v2 10 | */ 11 | function getAngleBetweenVectors(v1, v2, dotThreshold = 0.0005) { 12 | let angle; 13 | const dot = v1.dot(v2); 14 | 15 | // If dot is close to 1, we'll round angle to zero 16 | if (dot > 1 - dotThreshold) { 17 | angle = 0; 18 | } else if (dot < -1 + dotThreshold) { 19 | // Dot too close to -1 20 | angle = Math.PI; 21 | } else { 22 | // Get angle difference in radians 23 | angle = Math.acos(dot); 24 | } 25 | 26 | return angle; 27 | } 28 | 29 | /** 30 | * Finds an angle between two vectors with a sign relative to normal vector 31 | */ 32 | function getSignedAngleBetweenVectors( 33 | v1, 34 | v2, 35 | normal = new THREE.Vector3(0, 1, 0), 36 | dotThreshold = 0.0005 37 | ) { 38 | let angle = getAngleBetweenVectors(v1, v2, dotThreshold); 39 | 40 | // Get vector pointing up or down 41 | const cross = new THREE.Vector3().crossVectors(v1, v2); 42 | // Compare cross with normal to find out direction 43 | if (normal.dot(cross) < 0) { 44 | angle = -angle; 45 | } 46 | 47 | return angle; 48 | } 49 | 50 | function getRotationDirection({ left, right, isMouseLooking }) { 51 | let direction = 0; 52 | if (!isMouseLooking) { 53 | if (left) { 54 | direction = -1; 55 | } 56 | if (right) { 57 | direction = 1; 58 | } 59 | } 60 | return direction; 61 | } 62 | 63 | function getMovementDirection({ up, down, right, left, isMouseLooking }) { 64 | const positiveX = isMouseLooking && right ? -1 : 0; 65 | const negativeX = isMouseLooking && left ? 1 : 0; 66 | const positiveZ = up ? 1 : 0; 67 | const negativeZ = down ? -1 : 0; 68 | 69 | return new THREE.Vector3( 70 | positiveX + negativeX, 71 | 0, 72 | positiveZ + negativeZ 73 | ).normalize(); 74 | } 75 | 76 | const FORWARD = new THREE.Vector3(0, 0, 1); 77 | 78 | function getModelRotation(inputs) { 79 | const { up, down, right, left, isMouseLooking } = inputs; 80 | const movementDirection = getMovementDirection(inputs); 81 | let modelRotation = 0; 82 | 83 | if ((up || down) && !(down && up) && (left || right) && isMouseLooking) { 84 | const rotationDirection = getRotationDirection(inputs); 85 | const movementAngle = getSignedAngleBetweenVectors( 86 | movementDirection, 87 | FORWARD 88 | ); 89 | 90 | if (up) { 91 | modelRotation = 92 | rotationDirection === 0 93 | ? -movementAngle 94 | : (Math.PI / 8) * rotationDirection * -1; 95 | } else if (down) { 96 | if (rotationDirection === 0) { 97 | if (movementDirection.x > 0) { 98 | modelRotation = Math.PI - movementAngle; 99 | } else if (movementDirection.x < 0) { 100 | modelRotation = Math.PI - movementAngle; 101 | } 102 | } else { 103 | modelRotation = (Math.PI / 8) * rotationDirection * -1; 104 | } 105 | } 106 | } 107 | 108 | return modelRotation; 109 | } 110 | 111 | export default function useInputMovementRotation(inputs) { 112 | const direction = getRotationDirection(inputs); 113 | const rotation = getModelRotation(inputs); 114 | const movement = getMovementDirection(inputs); 115 | return { model: { direction, rotation }, movement }; 116 | } 117 | -------------------------------------------------------------------------------- /src/hooks/useKeyboardMouseMovement.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | const defaultMap = { 4 | up: "w", 5 | down: "s", 6 | right: "d", 7 | left: "a", 8 | jump: " ", 9 | walk: "Shift", 10 | }; 11 | 12 | const getInputFromKeyboard = (keyMap, keyPressed) => { 13 | let inputFound = ""; 14 | Object.entries(keyMap).forEach(([k, v]) => { 15 | if (v === keyPressed) { 16 | inputFound = k; 17 | } 18 | }); 19 | return inputFound; 20 | }; 21 | 22 | export default function useKeyboardInput(inputManager, userKeyMap = {}) { 23 | const [isMouseLooking, setIsMouseLooking] = useState(false); 24 | const [inputsPressed, setInputsPressed] = useState({}); 25 | const keyMap = { 26 | ...defaultMap, 27 | ...userKeyMap, 28 | }; 29 | 30 | function downHandler({ key }) { 31 | const input = getInputFromKeyboard(keyMap, key); 32 | if (input) { 33 | setInputsPressed((prevState) => ({ 34 | ...prevState, 35 | [input]: true, 36 | })); 37 | } 38 | } 39 | 40 | const upHandler = ({ key }) => { 41 | const input = getInputFromKeyboard(keyMap, key); 42 | if (input) { 43 | setInputsPressed((prevState) => ({ 44 | ...prevState, 45 | [input]: false, 46 | })); 47 | } 48 | }; 49 | 50 | function pointerdownHandler({ button }) { 51 | if (button === 2) { 52 | setIsMouseLooking(true); 53 | } 54 | } 55 | 56 | const pointerupHandler = ({ button }) => { 57 | if (button === 2) { 58 | setIsMouseLooking(false); 59 | } 60 | }; 61 | 62 | useEffect(() => { 63 | inputManager.subscribe("keydown", "character-controls", downHandler); 64 | inputManager.subscribe("keyup", "character-controls", upHandler); 65 | inputManager.subscribe( 66 | "pointerdown", 67 | "character-controls", 68 | pointerdownHandler 69 | ); 70 | inputManager.subscribe("pointerup", "character-controls", pointerupHandler); 71 | 72 | return () => { 73 | inputManager.unsubscribe("keydown", "character-controls"); 74 | inputManager.unsubscribe("keyup", "character-controls"); 75 | inputManager.unsubscribe("pointerdown", "character-controls"); 76 | inputManager.unsubscribe("pointerup", "character-controls"); 77 | }; 78 | }, []); 79 | 80 | return { ...inputsPressed, isMouseLooking }; 81 | } 82 | -------------------------------------------------------------------------------- /src/hooks/useRay.js: -------------------------------------------------------------------------------- 1 | import { useState, useRef } from "react"; 2 | import { useRaycastClosest } from "@react-three/cannon"; 3 | 4 | export default function useRay({ rayVector, position, collisionFilterMask }) { 5 | const rayChecker = useRef(setTimeout); 6 | const from = [position[0], position[1], position[2]]; 7 | const to = [rayVector.current.x, rayVector.current.y, rayVector.current.z]; 8 | 9 | const [ray, setRay] = useState({}); 10 | useRaycastClosest( 11 | { 12 | from, 13 | to, 14 | skipBackfaces: true, 15 | collisionFilterMask, 16 | }, 17 | (e) => { 18 | clearTimeout(rayChecker.current); 19 | setRay({ 20 | hasHit: e.hasHit, 21 | distance: e.distance, 22 | }); 23 | // this callback only fires constantly on collision so this 24 | // timeout resets state once we've stopped colliding 25 | rayChecker.current = setTimeout(() => { 26 | setRay({}); 27 | }, 100); 28 | }, 29 | [from, to] 30 | ); 31 | 32 | return ray; 33 | } 34 | -------------------------------------------------------------------------------- /src/hooks/useThirdPersonAnimations.js: -------------------------------------------------------------------------------- 1 | import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; 2 | import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'; 3 | import { useEffect, useState, useRef, useMemo } from 'react'; 4 | import { AnimationMixer } from 'three'; 5 | import { useFrame } from '@react-three/fiber'; 6 | 7 | const FBX_LOADER = new FBXLoader(); 8 | const GLTF_LOADER = new GLTFLoader(); 9 | 10 | async function asyncForEach(array, callback) { 11 | for (let index = 0; index < array.length; index++) { 12 | await callback(array[index], index, array); 13 | } 14 | }; 15 | 16 | function loadModelSync(url, loader) { 17 | return new Promise((resolve, reject) => { 18 | loader.load(url, (data) => resolve(data), null, reject); 19 | }); 20 | } 21 | 22 | function useThirdPersonAnimations( 23 | characterObj, 24 | animationPaths = {}, 25 | onLoad = () => {} 26 | ) { 27 | const ref = useRef(); 28 | const [clips, setClips] = useState([]); 29 | const [actualRef, setRef] = useState(ref); 30 | const [mixer, setMixer] = useState(new AnimationMixer(undefined)); 31 | const lazyActions = useRef({}); 32 | const [animations, setAnimations] = useState({}); 33 | 34 | // set character obj + mixer for character 35 | useEffect(() => { 36 | if (characterObj) { 37 | setRef({ current: characterObj }); 38 | setMixer(new AnimationMixer(undefined)); 39 | } 40 | }, [characterObj.name]); 41 | 42 | // load animations async initially 43 | useEffect(() => { 44 | const loadAnimations = async () => { 45 | const newAnimations = {}; 46 | const keys = [ 47 | 'idle', 48 | 'walk', 49 | 'run', 50 | 'jump', 51 | 'landing', 52 | 'inAir', 53 | 'backpedal', 54 | 'turnLeft', 55 | 'turnRight', 56 | 'strafeLeft', 57 | 'strafeRight', 58 | ]; 59 | await asyncForEach(keys, async (key) => { 60 | const fileExt = animationPaths[key].split('.').pop(); 61 | const loader = fileExt === 'fbx' ? FBX_LOADER : GLTF_LOADER; 62 | const model = await loadModelSync(animationPaths[key], loader); 63 | newAnimations[key] = model; 64 | }); 65 | setAnimations(newAnimations); 66 | onLoad(); 67 | }; 68 | 69 | loadAnimations(); 70 | }, []); 71 | 72 | // set clips once animations are loaded 73 | useEffect(() => { 74 | const clipsToSet = []; 75 | 76 | Object.keys(animations).forEach((name) => { 77 | if (animations[name]?.animations?.length) { 78 | animations[name].animations[0].name = name; 79 | clipsToSet.push(animations[name].animations[0]); 80 | } 81 | }); 82 | 83 | if (clips.length < clipsToSet.length) { 84 | setClips(clipsToSet); 85 | } 86 | }, [animations]); 87 | 88 | const api = useMemo(() => { 89 | if (!mixer || !clips.length) { 90 | return { 91 | actions: {}, 92 | }; 93 | } 94 | const actions = {}; 95 | clips.forEach((clip) => 96 | Object.defineProperty(actions, clip.name, { 97 | enumerable: true, 98 | get() { 99 | if (actualRef.current) { 100 | lazyActions.current[clip.name] = mixer.clipAction( 101 | clip, 102 | actualRef.current 103 | ); 104 | 105 | const clampers = ['jump', 'landing']; 106 | if (clampers.includes(clip.name)) { 107 | lazyActions.current[clip.name].setLoop(2200); // 2200 = THREE.LoopOnce 108 | lazyActions.current[clip.name].clampWhenFinished = true; 109 | } 110 | 111 | return lazyActions.current[clip.name]; 112 | } 113 | 114 | return null; 115 | }, 116 | }) 117 | ); 118 | return { 119 | ref: actualRef, 120 | clips, 121 | actions, 122 | names: clips.map((c) => c.name), 123 | mixer, 124 | }; 125 | }, [clips, characterObj.name, mixer]); 126 | 127 | useEffect(() => { 128 | const currentRoot = actualRef.current; 129 | return () => { 130 | // Clean up only when clips change, wipe out lazy actions and uncache clips 131 | lazyActions.current = {}; 132 | Object.values(api.actions).forEach((action) => { 133 | if (currentRoot) { 134 | mixer.uncacheAction(action, currentRoot); 135 | } 136 | }); 137 | }; 138 | }, [clips]); 139 | 140 | useFrame((_, delta) => { 141 | mixer.update(delta); 142 | }); 143 | 144 | return api; 145 | } 146 | 147 | export default useThirdPersonAnimations; 148 | -------------------------------------------------------------------------------- /src/hooks/useThirdPersonCameraControls.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'three'; 2 | import { useState, useEffect } from 'react'; 3 | 4 | /* 5 | * Based on code written by knav.eth for chainspace (https://somnet.chainrunners.xyz/chainspace) 6 | */ 7 | const CameraControlOperation = { 8 | NONE: -1, 9 | ROTATE: 0, 10 | TOUCH_ROTATE: 3, 11 | TOUCH_ZOOM_ROTATE: 6, 12 | }; 13 | 14 | const ROTATION_ANGLE = new THREE.Vector3(0, 1, 0); 15 | 16 | class CameraState { 17 | operation = CameraControlOperation.NONE; 18 | pointers = []; 19 | pointerPositions = {}; 20 | 21 | reset() { 22 | this.operation = CameraControlOperation.NONE; 23 | this.pointers = []; 24 | this.pointerPositions = {}; 25 | } 26 | } 27 | 28 | class ThirdPersonCameraControls { 29 | enabled = true; 30 | // How far you can zoom in and out ( PerspectiveCamera only ) 31 | minDistance = 0; 32 | maxDistance = Infinity; 33 | 34 | // How far you can orbit vertically, upper and lower limits. 35 | // Range is 0 to Math.PI radians. 36 | minPolarAngle = 0; 37 | maxPolarAngle = Math.PI; 38 | 39 | enableZoom = true; 40 | zoomSpeed = 1.75; 41 | 42 | enableRotate = true; 43 | rotateSpeed = 1.0; 44 | 45 | // "target" sets the location of focus, where the object orbits around 46 | targetOffset = new THREE.Vector3(0, 0, 0); 47 | 48 | spherical = new THREE.Spherical(3.5, Math.PI / 3, Math.PI); 49 | 50 | rotateStart = new THREE.Vector2(); 51 | rotateEnd = new THREE.Vector2(); 52 | rotateDelta = new THREE.Vector2(); 53 | 54 | zoomStart = new THREE.Vector2(); 55 | zoomEnd = new THREE.Vector2(); 56 | zoomDelta = new THREE.Vector2(); 57 | 58 | outerCameraContainer = new THREE.Object3D(); 59 | 60 | constructor( 61 | camera, 62 | domElement, 63 | target, 64 | inputManager, 65 | options = {}, 66 | cameraContainer 67 | ) { 68 | this.camera = camera; 69 | this.cameraState = new CameraState(); 70 | this.cameraContainer = cameraContainer; 71 | this.domElement = domElement; 72 | 73 | this.input = {}; 74 | const k = 'camera'; 75 | inputManager.subscribe('wheel', k, this.handleMouseWheel.bind(this)); 76 | inputManager.subscribe( 77 | 'pointerlockchange', 78 | k, 79 | this.onPointerLockChange.bind(this) 80 | ); 81 | inputManager.subscribe('pointerdown', k, this.onPointerDown.bind(this)); 82 | inputManager.subscribe('pointerup', k, this.onPointerUp.bind(this)); 83 | inputManager.subscribe('pointermove', k, this.onPointerMove.bind(this)); 84 | inputManager.subscribe('pointercancel', k, this.onPointerCancel.bind(this)); 85 | inputManager.subscribe('pointerlockerror', k, (e) => 86 | console.error('POINTERLOCK ERROR ', e) 87 | ); 88 | inputManager.subscribe('contextmenu', k, this.onContextMenu.bind(this)); 89 | this.cameraCollisionOn = options?.cameraCollisionOn; 90 | this.targetOffset.y = options?.yOffset ?? 1.6; 91 | this.outerCameraContainer.position.copy(this.targetOffset); 92 | this.outerCameraContainer.add(this.cameraContainer); 93 | 94 | this.target = target; 95 | this.target.add(this.outerCameraContainer); 96 | } 97 | 98 | _cameraPos = new THREE.Vector3(); 99 | _raycastTargetVector = new THREE.Vector3(); 100 | 101 | getCameraPosition(rayResult) { 102 | this.cameraContainer.position.setFromSphericalCoords( 103 | this.spherical.radius, 104 | this.spherical.phi, 105 | this.spherical.theta 106 | ); 107 | 108 | if (rayResult.hasHit && this.cameraCollisionOn) { 109 | this.cameraContainer.position.setFromSphericalCoords( 110 | rayResult.distance - 0.1, 111 | this.spherical.phi, 112 | this.spherical.theta 113 | ); 114 | } 115 | 116 | this.cameraContainer.getWorldPosition(this._cameraPos); 117 | return this._cameraPos; 118 | } 119 | 120 | _workingVec = new THREE.Vector3(); 121 | 122 | getCameraLookVec() { 123 | this.target.getWorldPosition(this._workingVec).add(this.targetOffset); 124 | return this._workingVec; 125 | } 126 | 127 | _workingQuat = new THREE.Quaternion(); 128 | 129 | update(rayResult) { 130 | if (this.input.isMouseLooking) { 131 | this._workingQuat.setFromAxisAngle( 132 | ROTATION_ANGLE, 133 | this.spherical.theta - Math.PI 134 | ); 135 | 136 | this.target.quaternion.multiply(this._workingQuat); 137 | this.spherical.theta = Math.PI; 138 | } 139 | 140 | // restrict phi to be between desired limits 141 | this.spherical.phi = Math.max( 142 | this.minPolarAngle, 143 | Math.min(this.maxPolarAngle, this.spherical.phi) 144 | ); 145 | this.spherical.makeSafe(); 146 | 147 | // restrict radius to be between desired limits 148 | this.spherical.radius = Math.max( 149 | this.minDistance, 150 | Math.min(this.maxDistance, this.spherical.radius) 151 | ); 152 | 153 | // copy maths to actual three.js camera 154 | this.camera.position.copy(this.getCameraPosition(rayResult)); 155 | this.camera.lookAt(this.getCameraLookVec()); 156 | } 157 | 158 | getZoomScale() { 159 | return 0.95 ** this.zoomSpeed; 160 | } 161 | 162 | rotateLeft(angle) { 163 | this.spherical.theta -= angle; 164 | } 165 | 166 | rotateUp(angle) { 167 | this.spherical.phi -= angle; 168 | } 169 | 170 | handleApplyRotate(speedMultiplier = 1) { 171 | this.rotateDelta 172 | .subVectors(this.rotateEnd, this.rotateStart) 173 | .multiplyScalar(this.rotateSpeed * speedMultiplier); 174 | 175 | const element = this.domElement; 176 | 177 | this.rotateLeft((2 * Math.PI * this.rotateDelta.x) / element.clientHeight); // yes, height 178 | 179 | this.rotateUp((2 * Math.PI * this.rotateDelta.y) / element.clientHeight); 180 | 181 | this.rotateStart.copy(this.rotateEnd); 182 | } 183 | 184 | zoomOut(zoomScale) { 185 | this.spherical.radius /= zoomScale; 186 | } 187 | 188 | zoomIn(zoomScale) { 189 | this.spherical.radius *= zoomScale; 190 | } 191 | 192 | // Event Handlers 193 | handleMouseDownRotate(event) { 194 | this.rotateEnd.set(event.clientX, event.clientY); 195 | this.rotateStart.set(event.clientX, event.clientY); 196 | } 197 | 198 | handleMouseMoveRotate(event) { 199 | if (document.pointerLockElement === this.domElement) { 200 | this.rotateEnd.x += event.movementX * 0.25; 201 | this.rotateEnd.y += event.movementY * 0.25 * 0.8; 202 | } else { 203 | this.domElement.requestPointerLock(); 204 | this.domElement.style.cursor = 'none'; 205 | this.rotateEnd.set(event.clientX, event.clientY); 206 | } 207 | this.handleApplyRotate(); 208 | } 209 | 210 | handleMouseWheel(event) { 211 | if (event.deltaY < 0) { 212 | this.zoomIn(this.getZoomScale()); 213 | } else if (event.deltaY > 0) { 214 | this.zoomOut(this.getZoomScale()); 215 | } 216 | } 217 | 218 | handleTouchStartRotate() { 219 | if (this.cameraState.pointers.length === 1) { 220 | this.rotateStart.set( 221 | this.cameraState.pointers[0].pageX, 222 | this.cameraState.pointers[0].pageY 223 | ); 224 | } else { 225 | const x = 226 | 0.5 * 227 | (this.cameraState.pointers[0].pageX + 228 | this.cameraState.pointers[1].pageX); 229 | const y = 230 | 0.5 * 231 | (this.cameraState.pointers[0].pageY + 232 | this.cameraState.pointers[1].pageY); 233 | 234 | this.rotateStart.set(x, y); 235 | } 236 | } 237 | 238 | handleTouchStartZoom() { 239 | const dx = 240 | this.cameraState.pointers[0].pageX - this.cameraState.pointers[1].pageX; 241 | const dy = 242 | this.cameraState.pointers[0].pageY - this.cameraState.pointers[1].pageY; 243 | 244 | const distance = Math.sqrt(dx * dx + dy * dy); 245 | 246 | this.zoomStart.set(0, distance); 247 | } 248 | 249 | handleTouchStartZoomRotate() { 250 | if (this.enableZoom) this.handleTouchStartZoom(); 251 | 252 | if (this.enableRotate) this.handleTouchStartRotate(); 253 | } 254 | 255 | handleTouchMoveRotate(event) { 256 | if (this.cameraState.pointers.length === 1) { 257 | this.rotateEnd.set(event.pageX, event.pageY); 258 | } else { 259 | const position = this.getSecondPointerPosition(event); 260 | 261 | const x = 0.5 * (event.pageX + position.x); 262 | const y = 0.5 * (event.pageY + position.y); 263 | 264 | this.rotateEnd.set(x, y); 265 | } 266 | 267 | this.handleApplyRotate(1.3); 268 | } 269 | 270 | handleTouchMoveZoom(event) { 271 | const position = this.getSecondPointerPosition(event); 272 | 273 | const dx = event.pageX - position.x; 274 | const dy = event.pageY - position.y; 275 | 276 | const distance = Math.sqrt(dx * dx + dy * dy); 277 | 278 | this.zoomEnd.set(0, distance); 279 | 280 | this.zoomDelta.set( 281 | 0, 282 | (this.zoomEnd.y / this.zoomStart.y) ** this.zoomSpeed 283 | ); 284 | 285 | this.zoomOut(this.zoomDelta.y); 286 | 287 | this.zoomStart.copy(this.zoomEnd); 288 | } 289 | 290 | handleTouchMoveZoomRotate(event) { 291 | if (this.enableZoom) this.handleTouchMoveZoom(event); 292 | if (this.enableRotate) this.handleTouchMoveRotate(event); 293 | } 294 | 295 | // Event Controllers 296 | onPointerDown(event) { 297 | if (!this.enabled) return; 298 | 299 | if (this.cameraState.pointers.length === 0) { 300 | this.domElement.setPointerCapture(event.pointerId); 301 | } 302 | 303 | this.addPointer(event); 304 | if (event.pointerType === 'touch') { 305 | this.onTouchStart(event); 306 | } else { 307 | this.onMouseDown(event); 308 | } 309 | } 310 | 311 | onPointerMove(event) { 312 | this.lastCheck = Date.now(); 313 | if (!this.enabled) return; 314 | if (!this.input.isMouseLocked && !this.cameraState.pointers.length) return; 315 | if ( 316 | !this.cameraState.pointers.find((e) => e.pointerId === event.pointerId) 317 | ) { 318 | return; 319 | } 320 | 321 | if (event.pointerType === 'touch') { 322 | this.onTouchMove(event); 323 | } else { 324 | this.onMouseMove(event); 325 | } 326 | } 327 | 328 | onPointerUp(event) { 329 | if (event.pointerType === 'touch') { 330 | this.onTouchEnd(); 331 | } else { 332 | this.onMouseUp(); 333 | } 334 | 335 | this.removePointer(event); 336 | 337 | if ( 338 | this.cameraState.pointers.length === 0 && 339 | event.pointerType === 'touch' 340 | ) { 341 | this.domElement.releasePointerCapture(event.pointerId); 342 | } 343 | } 344 | 345 | // Touch 346 | onTouchStart(event) { 347 | this.trackPointer(event); 348 | 349 | switch (this.cameraState.pointers.length) { 350 | case 1: 351 | if (!this.enableRotate) return; 352 | 353 | this.handleTouchStartRotate(); 354 | this.input.isMouseLooking = true; 355 | this.cameraState.operation = CameraControlOperation.TOUCH_ROTATE; 356 | break; 357 | 358 | case 2: 359 | if (!this.enableZoom && !this.enableRotate) return; 360 | 361 | this.handleTouchStartZoomRotate(); 362 | this.input.isMouseLooking = true; 363 | this.cameraState.operation = CameraControlOperation.TOUCH_ZOOM_ROTATE; 364 | break; 365 | 366 | default: 367 | this.cameraState.operation = CameraControlOperation.NONE; 368 | } 369 | } 370 | 371 | onTouchMove(event) { 372 | this.trackPointer(event); 373 | 374 | switch (this.cameraState.operation) { 375 | case CameraControlOperation.TOUCH_ROTATE: 376 | if (!this.enableRotate) return; 377 | 378 | this.handleTouchMoveRotate(event); 379 | break; 380 | 381 | case CameraControlOperation.TOUCH_ZOOM_ROTATE: 382 | if (!this.enableZoom && !this.enableRotate) return; 383 | 384 | this.handleTouchMoveZoomRotate(event); 385 | break; 386 | 387 | default: 388 | this.cameraState.operation = CameraControlOperation.NONE; 389 | } 390 | } 391 | 392 | onTouchEnd() { 393 | this.cameraState.operation = CameraControlOperation.NONE; 394 | } 395 | 396 | // Mouse 397 | onPointerLockChange() { 398 | // do initial check to see if mouse is locked 399 | this.input.isMouseLocked = document.pointerLockElement === this.domElement; 400 | if (!this.input.isMouseLocked) { 401 | // wait 100ms and then check again as sometimes document.pointerLockElement 402 | // is null after doing a document.requestPointerLock() 403 | setTimeout(() => { 404 | this.input.isMouseLocked = 405 | document.pointerLockElement === this.domElement; 406 | if (!this.input.isMouseLocked) { 407 | this.input.isMouseLooking = false; 408 | this.cameraState.operation = CameraControlOperation.NONE; 409 | } 410 | }, 100); 411 | } 412 | } 413 | 414 | onMouseDown(event) { 415 | switch (event.button) { 416 | case 0: 417 | if (!this.enableRotate) return; 418 | 419 | this.handleMouseDownRotate(event); 420 | this.cameraState.operation = CameraControlOperation.ROTATE; 421 | break; 422 | case 1: 423 | this.cameraState.operation = CameraControlOperation.NONE; 424 | break; 425 | case 2: 426 | if (!this.enableRotate) return; 427 | this.input.isMouseLooking = true; 428 | this.rightClickTime = Date.now(); 429 | this.handleMouseDownRotate(event); 430 | this.cameraState.operation = CameraControlOperation.ROTATE; 431 | break; 432 | default: 433 | this.cameraState.operation = CameraControlOperation.NONE; 434 | } 435 | } 436 | 437 | onMouseMove(event) { 438 | if (!this.enabled) return; 439 | 440 | if (this.cameraState.operation === CameraControlOperation.ROTATE) { 441 | if (!this.enableRotate) return; 442 | this.handleMouseMoveRotate(event); 443 | } 444 | } 445 | 446 | onMouseUp() { 447 | this.domElement.style.cursor = 'initial'; 448 | document.exitPointerLock(); 449 | this.input.isMouseLooking = false; 450 | } 451 | 452 | onMouseWheel(event) { 453 | if ( 454 | !this.enabled || 455 | !this.enableZoom || 456 | (this.cameraState.operation !== CameraControlOperation.NONE && 457 | this.cameraState.operation !== CameraControlOperation.ROTATE) 458 | ) { 459 | return; 460 | } 461 | this.handleMouseWheel(event); 462 | } 463 | 464 | // Pointer Utils 465 | getSecondPointerPosition(event) { 466 | const pointer = 467 | event.pointerId === this.cameraState.pointers[0].pointerId 468 | ? this.cameraState.pointers[1] 469 | : this.cameraState.pointers[0]; 470 | 471 | return this.cameraState.pointerPositions[pointer.pointerId]; 472 | } 473 | 474 | addPointer(event) { 475 | this.cameraState.pointers.push(event); 476 | } 477 | 478 | removePointer(event) { 479 | delete this.cameraState.pointerPositions[event.pointerId]; 480 | 481 | for (let i = 0; i < this.cameraState.pointers.length; i++) { 482 | if (this.cameraState.pointers[i].pointerId === event.pointerId) { 483 | this.cameraState.pointers.splice(i, 1); 484 | return; 485 | } 486 | } 487 | } 488 | 489 | trackPointer(event) { 490 | let position = this.cameraState.pointerPositions[event.pointerId]; 491 | 492 | if (position === undefined) { 493 | position = new THREE.Vector2(); 494 | this.cameraState.pointerPositions[event.pointerId] = position; 495 | } 496 | 497 | position.set(event.pageX, event.pageY); 498 | } 499 | 500 | onPointerCancel(event) { 501 | this.removePointer(event); 502 | } 503 | 504 | onContextMenu(event) { 505 | if (!this.enabled) return; 506 | event.preventDefault(); 507 | } 508 | 509 | reset() { 510 | this.cameraState.reset(); 511 | this.domElement.style.cursor = 'initial'; 512 | try { 513 | document.exitPointerLock(); 514 | } catch (e) { 515 | // lol 516 | } 517 | } 518 | 519 | dispose() { 520 | // remove event listeners here 521 | } 522 | } 523 | 524 | export default function useThirdPersonCameraControls({ 525 | camera, 526 | domElement, 527 | target, 528 | inputManager, 529 | cameraOptions, 530 | cameraContainer, 531 | }) { 532 | const [controls, setControls] = useState(null); 533 | 534 | useEffect(() => { 535 | if (!target) { 536 | return; 537 | } 538 | const newControls = new ThirdPersonCameraControls( 539 | camera, 540 | domElement, 541 | target, 542 | inputManager, 543 | { yOffset: cameraOptions.yOffset || 0 }, 544 | cameraContainer.current 545 | ); 546 | 547 | newControls.minDistance = cameraOptions?.minDistance || 404; 548 | newControls.maxDistance = cameraOptions?.maxDistance || 808; 549 | setControls(newControls); 550 | return () => { 551 | newControls.dispose(); 552 | }; 553 | }, [camera, domElement, target]); 554 | 555 | return controls; 556 | } 557 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { Suspense, useEffect, useRef, useState } from 'react'; 2 | import * as THREE from 'three'; 3 | import { useFrame, useThree } from '@react-three/fiber'; 4 | import { 5 | useThirdPersonAnimations, 6 | useThirdPersonCameraControls, 7 | useInputEventManager, 8 | useKeyboardMouseMovement, 9 | useInputMovementRotation, 10 | useCharacterState, 11 | useCapsuleCollider, 12 | useRay, 13 | } from './hooks'; 14 | 15 | const ThirdPersonCharacterControls = ({ 16 | cameraOptions = {}, 17 | characterObj, 18 | characterProps = {}, 19 | animationPaths = {}, 20 | onLoad, 21 | }) => { 22 | const { 23 | camera, 24 | gl: { domElement }, 25 | } = useThree(); 26 | // set up refs that influence character and camera position 27 | const collider = useCapsuleCollider(characterProps.radius); 28 | const [position, setPosition] = useState([0, 0, 0]); 29 | const modelRef = useRef(); 30 | const cameraContainer = useRef(new THREE.Object3D()); 31 | const rayVector = useRef(new THREE.Vector3()); 32 | const ray = useRay({ position, rayVector, ...cameraOptions }); 33 | 34 | // get character state based on user inputs + collider position + animations 35 | const inputManager = useInputEventManager(domElement); 36 | const inputs = useKeyboardMouseMovement(inputManager); 37 | const controls = useThirdPersonCameraControls({ 38 | camera, 39 | domElement, 40 | target: modelRef.current, 41 | inputManager, 42 | cameraOptions, 43 | cameraContainer, 44 | }); 45 | const { actions, mixer } = useThirdPersonAnimations( 46 | characterObj, 47 | animationPaths, 48 | onLoad 49 | ); 50 | const { animation, isMoving } = useCharacterState(inputs, position, mixer); 51 | 52 | // subscribe to collider velocity/position changes 53 | const charVelocity = characterProps.velocity ?? 4; 54 | const velocity = useRef([0, 0, 0]); 55 | useEffect(() => { 56 | collider.velocity.subscribe((v) => { 57 | velocity.current = v; 58 | }); 59 | 60 | collider.position.subscribe((p) => { 61 | // position is set on collider so we copy it to model 62 | modelRef.current?.position.set(...p); 63 | // setState with position to useCharacterState 64 | setPosition(p); 65 | }); 66 | }, []); 67 | 68 | useFrame(() => { 69 | let newRotation = new THREE.Euler(); 70 | let xVelocity = 0; 71 | let zVelocity = 0; 72 | const { quaternion } = modelRef.current; 73 | 74 | if (isMoving) { 75 | const { model, movement } = useInputMovementRotation(inputs); 76 | 77 | // first rotate the model group 78 | modelRef.current.rotateY(model.direction * -0.05); 79 | newRotation = characterObj.rotation.clone(); 80 | newRotation.y = model.rotation; 81 | 82 | const mtx = new THREE.Matrix4().makeRotationFromQuaternion(quaternion); 83 | movement.applyMatrix4(mtx); 84 | 85 | // then apply velocity to collider influenced by model groups rotation 86 | const baseVelocity = inputs.down ? charVelocity / 2 : charVelocity; 87 | xVelocity = movement.x * baseVelocity; 88 | zVelocity = movement.z * baseVelocity; 89 | } 90 | 91 | collider.velocity.set(xVelocity, velocity.current[1], zVelocity); 92 | 93 | // after applying x/z velocity, apply y velocity if user has jumped while grounded 94 | const isGrounded = Math.abs(velocity.current[1].toFixed(2)) === 0; 95 | if (animation === 'jump' && isGrounded) { 96 | collider.velocity.set(velocity.current[0], 8, velocity.current[2]); 97 | } 98 | 99 | // rotate character model inside model group 100 | const newQuat = new THREE.Quaternion().setFromEuler(newRotation); 101 | characterObj.quaternion.slerp(newQuat, 0.1); 102 | 103 | // quaternion is set on model group so we copy it to collider 104 | collider.quaternion.copy(quaternion); 105 | // check camera raycast collision and pass that to controls to 106 | cameraContainer.current.getWorldPosition(rayVector.current); 107 | controls?.update(ray); 108 | }); 109 | 110 | // Transition to new animation when loaded 111 | useEffect(() => { 112 | actions?.[animation]?.reset().fadeIn(0.2).play(); 113 | return () => { 114 | actions?.[animation]?.fadeOut(0.2); 115 | }; 116 | }, [animation, actions]); 117 | 118 | return ( 119 | 120 | null}> 121 | 122 | 123 | 124 | ); 125 | }; 126 | 127 | export default ThirdPersonCharacterControls; 128 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@ampproject/remapping@^2.1.0": 6 | version "2.2.0" 7 | resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" 8 | integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== 9 | dependencies: 10 | "@jridgewell/gen-mapping" "^0.1.0" 11 | "@jridgewell/trace-mapping" "^0.3.9" 12 | 13 | "@babel/code-frame@^7.18.6": 14 | version "7.18.6" 15 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" 16 | integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== 17 | dependencies: 18 | "@babel/highlight" "^7.18.6" 19 | 20 | "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.19.3", "@babel/compat-data@^7.19.4": 21 | version "7.19.4" 22 | resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.4.tgz#95c86de137bf0317f3a570e1b6e996b427299747" 23 | integrity sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw== 24 | 25 | "@babel/core@^7.17.8": 26 | version "7.19.3" 27 | resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.3.tgz#2519f62a51458f43b682d61583c3810e7dcee64c" 28 | integrity sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ== 29 | dependencies: 30 | "@ampproject/remapping" "^2.1.0" 31 | "@babel/code-frame" "^7.18.6" 32 | "@babel/generator" "^7.19.3" 33 | "@babel/helper-compilation-targets" "^7.19.3" 34 | "@babel/helper-module-transforms" "^7.19.0" 35 | "@babel/helpers" "^7.19.0" 36 | "@babel/parser" "^7.19.3" 37 | "@babel/template" "^7.18.10" 38 | "@babel/traverse" "^7.19.3" 39 | "@babel/types" "^7.19.3" 40 | convert-source-map "^1.7.0" 41 | debug "^4.1.0" 42 | gensync "^1.0.0-beta.2" 43 | json5 "^2.2.1" 44 | semver "^6.3.0" 45 | 46 | "@babel/generator@^7.19.3", "@babel/generator@^7.19.4": 47 | version "7.19.4" 48 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.4.tgz#60050cf3f0a593d7b2471b4be4f62a56b949237f" 49 | integrity sha512-5T2lY5vXqS+5UEit/5TwcIUeCnwgCljcF8IQRT6XRQPBrvLeq5V8W+URv+GvwoF3FP8tkhp++evVyDzkDGzNmA== 50 | dependencies: 51 | "@babel/types" "^7.19.4" 52 | "@jridgewell/gen-mapping" "^0.3.2" 53 | jsesc "^2.5.1" 54 | 55 | "@babel/helper-annotate-as-pure@^7.18.6": 56 | version "7.18.6" 57 | resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" 58 | integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== 59 | dependencies: 60 | "@babel/types" "^7.18.6" 61 | 62 | "@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": 63 | version "7.18.9" 64 | resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" 65 | integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== 66 | dependencies: 67 | "@babel/helper-explode-assignable-expression" "^7.18.6" 68 | "@babel/types" "^7.18.9" 69 | 70 | "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.3": 71 | version "7.19.3" 72 | resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz#a10a04588125675d7c7ae299af86fa1b2ee038ca" 73 | integrity sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg== 74 | dependencies: 75 | "@babel/compat-data" "^7.19.3" 76 | "@babel/helper-validator-option" "^7.18.6" 77 | browserslist "^4.21.3" 78 | semver "^6.3.0" 79 | 80 | "@babel/helper-create-class-features-plugin@^7.18.6": 81 | version "7.19.0" 82 | resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz#bfd6904620df4e46470bae4850d66be1054c404b" 83 | integrity sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw== 84 | dependencies: 85 | "@babel/helper-annotate-as-pure" "^7.18.6" 86 | "@babel/helper-environment-visitor" "^7.18.9" 87 | "@babel/helper-function-name" "^7.19.0" 88 | "@babel/helper-member-expression-to-functions" "^7.18.9" 89 | "@babel/helper-optimise-call-expression" "^7.18.6" 90 | "@babel/helper-replace-supers" "^7.18.9" 91 | "@babel/helper-split-export-declaration" "^7.18.6" 92 | 93 | "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": 94 | version "7.19.0" 95 | resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" 96 | integrity sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw== 97 | dependencies: 98 | "@babel/helper-annotate-as-pure" "^7.18.6" 99 | regexpu-core "^5.1.0" 100 | 101 | "@babel/helper-define-polyfill-provider@^0.3.3": 102 | version "0.3.3" 103 | resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" 104 | integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== 105 | dependencies: 106 | "@babel/helper-compilation-targets" "^7.17.7" 107 | "@babel/helper-plugin-utils" "^7.16.7" 108 | debug "^4.1.1" 109 | lodash.debounce "^4.0.8" 110 | resolve "^1.14.2" 111 | semver "^6.1.2" 112 | 113 | "@babel/helper-environment-visitor@^7.18.9": 114 | version "7.18.9" 115 | resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" 116 | integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== 117 | 118 | "@babel/helper-explode-assignable-expression@^7.18.6": 119 | version "7.18.6" 120 | resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" 121 | integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== 122 | dependencies: 123 | "@babel/types" "^7.18.6" 124 | 125 | "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": 126 | version "7.19.0" 127 | resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" 128 | integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== 129 | dependencies: 130 | "@babel/template" "^7.18.10" 131 | "@babel/types" "^7.19.0" 132 | 133 | "@babel/helper-hoist-variables@^7.18.6": 134 | version "7.18.6" 135 | resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" 136 | integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== 137 | dependencies: 138 | "@babel/types" "^7.18.6" 139 | 140 | "@babel/helper-member-expression-to-functions@^7.18.9": 141 | version "7.18.9" 142 | resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" 143 | integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== 144 | dependencies: 145 | "@babel/types" "^7.18.9" 146 | 147 | "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.18.6": 148 | version "7.18.6" 149 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" 150 | integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== 151 | dependencies: 152 | "@babel/types" "^7.18.6" 153 | 154 | "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.0": 155 | version "7.19.0" 156 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz#309b230f04e22c58c6a2c0c0c7e50b216d350c30" 157 | integrity sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ== 158 | dependencies: 159 | "@babel/helper-environment-visitor" "^7.18.9" 160 | "@babel/helper-module-imports" "^7.18.6" 161 | "@babel/helper-simple-access" "^7.18.6" 162 | "@babel/helper-split-export-declaration" "^7.18.6" 163 | "@babel/helper-validator-identifier" "^7.18.6" 164 | "@babel/template" "^7.18.10" 165 | "@babel/traverse" "^7.19.0" 166 | "@babel/types" "^7.19.0" 167 | 168 | "@babel/helper-optimise-call-expression@^7.18.6": 169 | version "7.18.6" 170 | resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" 171 | integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== 172 | dependencies: 173 | "@babel/types" "^7.18.6" 174 | 175 | "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": 176 | version "7.19.0" 177 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz#4796bb14961521f0f8715990bee2fb6e51ce21bf" 178 | integrity sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw== 179 | 180 | "@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": 181 | version "7.18.9" 182 | resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" 183 | integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== 184 | dependencies: 185 | "@babel/helper-annotate-as-pure" "^7.18.6" 186 | "@babel/helper-environment-visitor" "^7.18.9" 187 | "@babel/helper-wrap-function" "^7.18.9" 188 | "@babel/types" "^7.18.9" 189 | 190 | "@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": 191 | version "7.19.1" 192 | resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" 193 | integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== 194 | dependencies: 195 | "@babel/helper-environment-visitor" "^7.18.9" 196 | "@babel/helper-member-expression-to-functions" "^7.18.9" 197 | "@babel/helper-optimise-call-expression" "^7.18.6" 198 | "@babel/traverse" "^7.19.1" 199 | "@babel/types" "^7.19.0" 200 | 201 | "@babel/helper-simple-access@^7.18.6": 202 | version "7.19.4" 203 | resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz#be553f4951ac6352df2567f7daa19a0ee15668e7" 204 | integrity sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg== 205 | dependencies: 206 | "@babel/types" "^7.19.4" 207 | 208 | "@babel/helper-skip-transparent-expression-wrappers@^7.18.9": 209 | version "7.18.9" 210 | resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" 211 | integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== 212 | dependencies: 213 | "@babel/types" "^7.18.9" 214 | 215 | "@babel/helper-split-export-declaration@^7.18.6": 216 | version "7.18.6" 217 | resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" 218 | integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== 219 | dependencies: 220 | "@babel/types" "^7.18.6" 221 | 222 | "@babel/helper-string-parser@^7.19.4": 223 | version "7.19.4" 224 | resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" 225 | integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== 226 | 227 | "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": 228 | version "7.19.1" 229 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" 230 | integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== 231 | 232 | "@babel/helper-validator-option@^7.18.6": 233 | version "7.18.6" 234 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" 235 | integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== 236 | 237 | "@babel/helper-wrap-function@^7.18.9": 238 | version "7.19.0" 239 | resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz#89f18335cff1152373222f76a4b37799636ae8b1" 240 | integrity sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg== 241 | dependencies: 242 | "@babel/helper-function-name" "^7.19.0" 243 | "@babel/template" "^7.18.10" 244 | "@babel/traverse" "^7.19.0" 245 | "@babel/types" "^7.19.0" 246 | 247 | "@babel/helpers@^7.19.0": 248 | version "7.19.4" 249 | resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.19.4.tgz#42154945f87b8148df7203a25c31ba9a73be46c5" 250 | integrity sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw== 251 | dependencies: 252 | "@babel/template" "^7.18.10" 253 | "@babel/traverse" "^7.19.4" 254 | "@babel/types" "^7.19.4" 255 | 256 | "@babel/highlight@^7.18.6": 257 | version "7.18.6" 258 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" 259 | integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== 260 | dependencies: 261 | "@babel/helper-validator-identifier" "^7.18.6" 262 | chalk "^2.0.0" 263 | js-tokens "^4.0.0" 264 | 265 | "@babel/parser@^7.18.10", "@babel/parser@^7.19.3", "@babel/parser@^7.19.4": 266 | version "7.19.4" 267 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.4.tgz#03c4339d2b8971eb3beca5252bafd9b9f79db3dc" 268 | integrity sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA== 269 | 270 | "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": 271 | version "7.18.6" 272 | resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" 273 | integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== 274 | dependencies: 275 | "@babel/helper-plugin-utils" "^7.18.6" 276 | 277 | "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": 278 | version "7.18.9" 279 | resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" 280 | integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== 281 | dependencies: 282 | "@babel/helper-plugin-utils" "^7.18.9" 283 | "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" 284 | "@babel/plugin-proposal-optional-chaining" "^7.18.9" 285 | 286 | "@babel/plugin-proposal-async-generator-functions@^7.19.1": 287 | version "7.19.1" 288 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz#34f6f5174b688529342288cd264f80c9ea9fb4a7" 289 | integrity sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q== 290 | dependencies: 291 | "@babel/helper-environment-visitor" "^7.18.9" 292 | "@babel/helper-plugin-utils" "^7.19.0" 293 | "@babel/helper-remap-async-to-generator" "^7.18.9" 294 | "@babel/plugin-syntax-async-generators" "^7.8.4" 295 | 296 | "@babel/plugin-proposal-class-properties@^7.18.6": 297 | version "7.18.6" 298 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" 299 | integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== 300 | dependencies: 301 | "@babel/helper-create-class-features-plugin" "^7.18.6" 302 | "@babel/helper-plugin-utils" "^7.18.6" 303 | 304 | "@babel/plugin-proposal-class-static-block@^7.18.6": 305 | version "7.18.6" 306 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" 307 | integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== 308 | dependencies: 309 | "@babel/helper-create-class-features-plugin" "^7.18.6" 310 | "@babel/helper-plugin-utils" "^7.18.6" 311 | "@babel/plugin-syntax-class-static-block" "^7.14.5" 312 | 313 | "@babel/plugin-proposal-dynamic-import@^7.18.6": 314 | version "7.18.6" 315 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" 316 | integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== 317 | dependencies: 318 | "@babel/helper-plugin-utils" "^7.18.6" 319 | "@babel/plugin-syntax-dynamic-import" "^7.8.3" 320 | 321 | "@babel/plugin-proposal-export-namespace-from@^7.18.9": 322 | version "7.18.9" 323 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" 324 | integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== 325 | dependencies: 326 | "@babel/helper-plugin-utils" "^7.18.9" 327 | "@babel/plugin-syntax-export-namespace-from" "^7.8.3" 328 | 329 | "@babel/plugin-proposal-json-strings@^7.18.6": 330 | version "7.18.6" 331 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" 332 | integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== 333 | dependencies: 334 | "@babel/helper-plugin-utils" "^7.18.6" 335 | "@babel/plugin-syntax-json-strings" "^7.8.3" 336 | 337 | "@babel/plugin-proposal-logical-assignment-operators@^7.18.9": 338 | version "7.18.9" 339 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" 340 | integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== 341 | dependencies: 342 | "@babel/helper-plugin-utils" "^7.18.9" 343 | "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" 344 | 345 | "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": 346 | version "7.18.6" 347 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" 348 | integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== 349 | dependencies: 350 | "@babel/helper-plugin-utils" "^7.18.6" 351 | "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" 352 | 353 | "@babel/plugin-proposal-numeric-separator@^7.18.6": 354 | version "7.18.6" 355 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" 356 | integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== 357 | dependencies: 358 | "@babel/helper-plugin-utils" "^7.18.6" 359 | "@babel/plugin-syntax-numeric-separator" "^7.10.4" 360 | 361 | "@babel/plugin-proposal-object-rest-spread@^7.19.4": 362 | version "7.19.4" 363 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz#a8fc86e8180ff57290c91a75d83fe658189b642d" 364 | integrity sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q== 365 | dependencies: 366 | "@babel/compat-data" "^7.19.4" 367 | "@babel/helper-compilation-targets" "^7.19.3" 368 | "@babel/helper-plugin-utils" "^7.19.0" 369 | "@babel/plugin-syntax-object-rest-spread" "^7.8.3" 370 | "@babel/plugin-transform-parameters" "^7.18.8" 371 | 372 | "@babel/plugin-proposal-optional-catch-binding@^7.18.6": 373 | version "7.18.6" 374 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" 375 | integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== 376 | dependencies: 377 | "@babel/helper-plugin-utils" "^7.18.6" 378 | "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" 379 | 380 | "@babel/plugin-proposal-optional-chaining@^7.18.9": 381 | version "7.18.9" 382 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" 383 | integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== 384 | dependencies: 385 | "@babel/helper-plugin-utils" "^7.18.9" 386 | "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" 387 | "@babel/plugin-syntax-optional-chaining" "^7.8.3" 388 | 389 | "@babel/plugin-proposal-private-methods@^7.18.6": 390 | version "7.18.6" 391 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" 392 | integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== 393 | dependencies: 394 | "@babel/helper-create-class-features-plugin" "^7.18.6" 395 | "@babel/helper-plugin-utils" "^7.18.6" 396 | 397 | "@babel/plugin-proposal-private-property-in-object@^7.18.6": 398 | version "7.18.6" 399 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" 400 | integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== 401 | dependencies: 402 | "@babel/helper-annotate-as-pure" "^7.18.6" 403 | "@babel/helper-create-class-features-plugin" "^7.18.6" 404 | "@babel/helper-plugin-utils" "^7.18.6" 405 | "@babel/plugin-syntax-private-property-in-object" "^7.14.5" 406 | 407 | "@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": 408 | version "7.18.6" 409 | resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" 410 | integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== 411 | dependencies: 412 | "@babel/helper-create-regexp-features-plugin" "^7.18.6" 413 | "@babel/helper-plugin-utils" "^7.18.6" 414 | 415 | "@babel/plugin-syntax-async-generators@^7.8.4": 416 | version "7.8.4" 417 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" 418 | integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== 419 | dependencies: 420 | "@babel/helper-plugin-utils" "^7.8.0" 421 | 422 | "@babel/plugin-syntax-class-properties@^7.12.13": 423 | version "7.12.13" 424 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" 425 | integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== 426 | dependencies: 427 | "@babel/helper-plugin-utils" "^7.12.13" 428 | 429 | "@babel/plugin-syntax-class-static-block@^7.14.5": 430 | version "7.14.5" 431 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" 432 | integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== 433 | dependencies: 434 | "@babel/helper-plugin-utils" "^7.14.5" 435 | 436 | "@babel/plugin-syntax-dynamic-import@^7.8.3": 437 | version "7.8.3" 438 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" 439 | integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== 440 | dependencies: 441 | "@babel/helper-plugin-utils" "^7.8.0" 442 | 443 | "@babel/plugin-syntax-export-namespace-from@^7.8.3": 444 | version "7.8.3" 445 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" 446 | integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== 447 | dependencies: 448 | "@babel/helper-plugin-utils" "^7.8.3" 449 | 450 | "@babel/plugin-syntax-import-assertions@^7.18.6": 451 | version "7.18.6" 452 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz#cd6190500a4fa2fe31990a963ffab4b63e4505e4" 453 | integrity sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ== 454 | dependencies: 455 | "@babel/helper-plugin-utils" "^7.18.6" 456 | 457 | "@babel/plugin-syntax-json-strings@^7.8.3": 458 | version "7.8.3" 459 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" 460 | integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== 461 | dependencies: 462 | "@babel/helper-plugin-utils" "^7.8.0" 463 | 464 | "@babel/plugin-syntax-jsx@^7.18.6": 465 | version "7.18.6" 466 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" 467 | integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== 468 | dependencies: 469 | "@babel/helper-plugin-utils" "^7.18.6" 470 | 471 | "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": 472 | version "7.10.4" 473 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" 474 | integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== 475 | dependencies: 476 | "@babel/helper-plugin-utils" "^7.10.4" 477 | 478 | "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": 479 | version "7.8.3" 480 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" 481 | integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== 482 | dependencies: 483 | "@babel/helper-plugin-utils" "^7.8.0" 484 | 485 | "@babel/plugin-syntax-numeric-separator@^7.10.4": 486 | version "7.10.4" 487 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" 488 | integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== 489 | dependencies: 490 | "@babel/helper-plugin-utils" "^7.10.4" 491 | 492 | "@babel/plugin-syntax-object-rest-spread@^7.8.3": 493 | version "7.8.3" 494 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" 495 | integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== 496 | dependencies: 497 | "@babel/helper-plugin-utils" "^7.8.0" 498 | 499 | "@babel/plugin-syntax-optional-catch-binding@^7.8.3": 500 | version "7.8.3" 501 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" 502 | integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== 503 | dependencies: 504 | "@babel/helper-plugin-utils" "^7.8.0" 505 | 506 | "@babel/plugin-syntax-optional-chaining@^7.8.3": 507 | version "7.8.3" 508 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" 509 | integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== 510 | dependencies: 511 | "@babel/helper-plugin-utils" "^7.8.0" 512 | 513 | "@babel/plugin-syntax-private-property-in-object@^7.14.5": 514 | version "7.14.5" 515 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" 516 | integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== 517 | dependencies: 518 | "@babel/helper-plugin-utils" "^7.14.5" 519 | 520 | "@babel/plugin-syntax-top-level-await@^7.14.5": 521 | version "7.14.5" 522 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" 523 | integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== 524 | dependencies: 525 | "@babel/helper-plugin-utils" "^7.14.5" 526 | 527 | "@babel/plugin-transform-arrow-functions@^7.18.6": 528 | version "7.18.6" 529 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" 530 | integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== 531 | dependencies: 532 | "@babel/helper-plugin-utils" "^7.18.6" 533 | 534 | "@babel/plugin-transform-async-to-generator@^7.18.6": 535 | version "7.18.6" 536 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" 537 | integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== 538 | dependencies: 539 | "@babel/helper-module-imports" "^7.18.6" 540 | "@babel/helper-plugin-utils" "^7.18.6" 541 | "@babel/helper-remap-async-to-generator" "^7.18.6" 542 | 543 | "@babel/plugin-transform-block-scoped-functions@^7.18.6": 544 | version "7.18.6" 545 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" 546 | integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== 547 | dependencies: 548 | "@babel/helper-plugin-utils" "^7.18.6" 549 | 550 | "@babel/plugin-transform-block-scoping@^7.19.4": 551 | version "7.19.4" 552 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz#315d70f68ce64426db379a3d830e7ac30be02e9b" 553 | integrity sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ== 554 | dependencies: 555 | "@babel/helper-plugin-utils" "^7.19.0" 556 | 557 | "@babel/plugin-transform-classes@^7.19.0": 558 | version "7.19.0" 559 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20" 560 | integrity sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A== 561 | dependencies: 562 | "@babel/helper-annotate-as-pure" "^7.18.6" 563 | "@babel/helper-compilation-targets" "^7.19.0" 564 | "@babel/helper-environment-visitor" "^7.18.9" 565 | "@babel/helper-function-name" "^7.19.0" 566 | "@babel/helper-optimise-call-expression" "^7.18.6" 567 | "@babel/helper-plugin-utils" "^7.19.0" 568 | "@babel/helper-replace-supers" "^7.18.9" 569 | "@babel/helper-split-export-declaration" "^7.18.6" 570 | globals "^11.1.0" 571 | 572 | "@babel/plugin-transform-computed-properties@^7.18.9": 573 | version "7.18.9" 574 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" 575 | integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== 576 | dependencies: 577 | "@babel/helper-plugin-utils" "^7.18.9" 578 | 579 | "@babel/plugin-transform-destructuring@^7.19.4": 580 | version "7.19.4" 581 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz#46890722687b9b89e1369ad0bd8dc6c5a3b4319d" 582 | integrity sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA== 583 | dependencies: 584 | "@babel/helper-plugin-utils" "^7.19.0" 585 | 586 | "@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": 587 | version "7.18.6" 588 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" 589 | integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== 590 | dependencies: 591 | "@babel/helper-create-regexp-features-plugin" "^7.18.6" 592 | "@babel/helper-plugin-utils" "^7.18.6" 593 | 594 | "@babel/plugin-transform-duplicate-keys@^7.18.9": 595 | version "7.18.9" 596 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" 597 | integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== 598 | dependencies: 599 | "@babel/helper-plugin-utils" "^7.18.9" 600 | 601 | "@babel/plugin-transform-exponentiation-operator@^7.18.6": 602 | version "7.18.6" 603 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" 604 | integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== 605 | dependencies: 606 | "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" 607 | "@babel/helper-plugin-utils" "^7.18.6" 608 | 609 | "@babel/plugin-transform-for-of@^7.18.8": 610 | version "7.18.8" 611 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" 612 | integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== 613 | dependencies: 614 | "@babel/helper-plugin-utils" "^7.18.6" 615 | 616 | "@babel/plugin-transform-function-name@^7.18.9": 617 | version "7.18.9" 618 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" 619 | integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== 620 | dependencies: 621 | "@babel/helper-compilation-targets" "^7.18.9" 622 | "@babel/helper-function-name" "^7.18.9" 623 | "@babel/helper-plugin-utils" "^7.18.9" 624 | 625 | "@babel/plugin-transform-literals@^7.18.9": 626 | version "7.18.9" 627 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" 628 | integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== 629 | dependencies: 630 | "@babel/helper-plugin-utils" "^7.18.9" 631 | 632 | "@babel/plugin-transform-member-expression-literals@^7.18.6": 633 | version "7.18.6" 634 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" 635 | integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== 636 | dependencies: 637 | "@babel/helper-plugin-utils" "^7.18.6" 638 | 639 | "@babel/plugin-transform-modules-amd@^7.18.6": 640 | version "7.18.6" 641 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" 642 | integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== 643 | dependencies: 644 | "@babel/helper-module-transforms" "^7.18.6" 645 | "@babel/helper-plugin-utils" "^7.18.6" 646 | babel-plugin-dynamic-import-node "^2.3.3" 647 | 648 | "@babel/plugin-transform-modules-commonjs@^7.18.6": 649 | version "7.18.6" 650 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" 651 | integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== 652 | dependencies: 653 | "@babel/helper-module-transforms" "^7.18.6" 654 | "@babel/helper-plugin-utils" "^7.18.6" 655 | "@babel/helper-simple-access" "^7.18.6" 656 | babel-plugin-dynamic-import-node "^2.3.3" 657 | 658 | "@babel/plugin-transform-modules-systemjs@^7.19.0": 659 | version "7.19.0" 660 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz#5f20b471284430f02d9c5059d9b9a16d4b085a1f" 661 | integrity sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A== 662 | dependencies: 663 | "@babel/helper-hoist-variables" "^7.18.6" 664 | "@babel/helper-module-transforms" "^7.19.0" 665 | "@babel/helper-plugin-utils" "^7.19.0" 666 | "@babel/helper-validator-identifier" "^7.18.6" 667 | babel-plugin-dynamic-import-node "^2.3.3" 668 | 669 | "@babel/plugin-transform-modules-umd@^7.18.6": 670 | version "7.18.6" 671 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" 672 | integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== 673 | dependencies: 674 | "@babel/helper-module-transforms" "^7.18.6" 675 | "@babel/helper-plugin-utils" "^7.18.6" 676 | 677 | "@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": 678 | version "7.19.1" 679 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz#ec7455bab6cd8fb05c525a94876f435a48128888" 680 | integrity sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw== 681 | dependencies: 682 | "@babel/helper-create-regexp-features-plugin" "^7.19.0" 683 | "@babel/helper-plugin-utils" "^7.19.0" 684 | 685 | "@babel/plugin-transform-new-target@^7.18.6": 686 | version "7.18.6" 687 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" 688 | integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== 689 | dependencies: 690 | "@babel/helper-plugin-utils" "^7.18.6" 691 | 692 | "@babel/plugin-transform-object-super@^7.18.6": 693 | version "7.18.6" 694 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" 695 | integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== 696 | dependencies: 697 | "@babel/helper-plugin-utils" "^7.18.6" 698 | "@babel/helper-replace-supers" "^7.18.6" 699 | 700 | "@babel/plugin-transform-parameters@^7.18.8": 701 | version "7.18.8" 702 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" 703 | integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== 704 | dependencies: 705 | "@babel/helper-plugin-utils" "^7.18.6" 706 | 707 | "@babel/plugin-transform-property-literals@^7.18.6": 708 | version "7.18.6" 709 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" 710 | integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== 711 | dependencies: 712 | "@babel/helper-plugin-utils" "^7.18.6" 713 | 714 | "@babel/plugin-transform-react-display-name@^7.18.6": 715 | version "7.18.6" 716 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" 717 | integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== 718 | dependencies: 719 | "@babel/helper-plugin-utils" "^7.18.6" 720 | 721 | "@babel/plugin-transform-react-jsx-development@^7.18.6": 722 | version "7.18.6" 723 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" 724 | integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== 725 | dependencies: 726 | "@babel/plugin-transform-react-jsx" "^7.18.6" 727 | 728 | "@babel/plugin-transform-react-jsx@^7.18.6": 729 | version "7.19.0" 730 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" 731 | integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg== 732 | dependencies: 733 | "@babel/helper-annotate-as-pure" "^7.18.6" 734 | "@babel/helper-module-imports" "^7.18.6" 735 | "@babel/helper-plugin-utils" "^7.19.0" 736 | "@babel/plugin-syntax-jsx" "^7.18.6" 737 | "@babel/types" "^7.19.0" 738 | 739 | "@babel/plugin-transform-react-pure-annotations@^7.18.6": 740 | version "7.18.6" 741 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" 742 | integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== 743 | dependencies: 744 | "@babel/helper-annotate-as-pure" "^7.18.6" 745 | "@babel/helper-plugin-utils" "^7.18.6" 746 | 747 | "@babel/plugin-transform-regenerator@^7.18.6": 748 | version "7.18.6" 749 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" 750 | integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== 751 | dependencies: 752 | "@babel/helper-plugin-utils" "^7.18.6" 753 | regenerator-transform "^0.15.0" 754 | 755 | "@babel/plugin-transform-reserved-words@^7.18.6": 756 | version "7.18.6" 757 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" 758 | integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== 759 | dependencies: 760 | "@babel/helper-plugin-utils" "^7.18.6" 761 | 762 | "@babel/plugin-transform-runtime@^7.17.0": 763 | version "7.19.1" 764 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.1.tgz#a3df2d7312eea624c7889a2dcd37fd1dfd25b2c6" 765 | integrity sha512-2nJjTUFIzBMP/f/miLxEK9vxwW/KUXsdvN4sR//TmuDhe6yU2h57WmIOE12Gng3MDP/xpjUV/ToZRdcf8Yj4fA== 766 | dependencies: 767 | "@babel/helper-module-imports" "^7.18.6" 768 | "@babel/helper-plugin-utils" "^7.19.0" 769 | babel-plugin-polyfill-corejs2 "^0.3.3" 770 | babel-plugin-polyfill-corejs3 "^0.6.0" 771 | babel-plugin-polyfill-regenerator "^0.4.1" 772 | semver "^6.3.0" 773 | 774 | "@babel/plugin-transform-shorthand-properties@^7.18.6": 775 | version "7.18.6" 776 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" 777 | integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== 778 | dependencies: 779 | "@babel/helper-plugin-utils" "^7.18.6" 780 | 781 | "@babel/plugin-transform-spread@^7.19.0": 782 | version "7.19.0" 783 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" 784 | integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== 785 | dependencies: 786 | "@babel/helper-plugin-utils" "^7.19.0" 787 | "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" 788 | 789 | "@babel/plugin-transform-sticky-regex@^7.18.6": 790 | version "7.18.6" 791 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" 792 | integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== 793 | dependencies: 794 | "@babel/helper-plugin-utils" "^7.18.6" 795 | 796 | "@babel/plugin-transform-template-literals@^7.18.9": 797 | version "7.18.9" 798 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" 799 | integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== 800 | dependencies: 801 | "@babel/helper-plugin-utils" "^7.18.9" 802 | 803 | "@babel/plugin-transform-typeof-symbol@^7.18.9": 804 | version "7.18.9" 805 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" 806 | integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== 807 | dependencies: 808 | "@babel/helper-plugin-utils" "^7.18.9" 809 | 810 | "@babel/plugin-transform-unicode-escapes@^7.18.10": 811 | version "7.18.10" 812 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" 813 | integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== 814 | dependencies: 815 | "@babel/helper-plugin-utils" "^7.18.9" 816 | 817 | "@babel/plugin-transform-unicode-regex@^7.18.6": 818 | version "7.18.6" 819 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" 820 | integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== 821 | dependencies: 822 | "@babel/helper-create-regexp-features-plugin" "^7.18.6" 823 | "@babel/helper-plugin-utils" "^7.18.6" 824 | 825 | "@babel/preset-env@^7.16.11": 826 | version "7.19.4" 827 | resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.4.tgz#4c91ce2e1f994f717efb4237891c3ad2d808c94b" 828 | integrity sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg== 829 | dependencies: 830 | "@babel/compat-data" "^7.19.4" 831 | "@babel/helper-compilation-targets" "^7.19.3" 832 | "@babel/helper-plugin-utils" "^7.19.0" 833 | "@babel/helper-validator-option" "^7.18.6" 834 | "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" 835 | "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" 836 | "@babel/plugin-proposal-async-generator-functions" "^7.19.1" 837 | "@babel/plugin-proposal-class-properties" "^7.18.6" 838 | "@babel/plugin-proposal-class-static-block" "^7.18.6" 839 | "@babel/plugin-proposal-dynamic-import" "^7.18.6" 840 | "@babel/plugin-proposal-export-namespace-from" "^7.18.9" 841 | "@babel/plugin-proposal-json-strings" "^7.18.6" 842 | "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" 843 | "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" 844 | "@babel/plugin-proposal-numeric-separator" "^7.18.6" 845 | "@babel/plugin-proposal-object-rest-spread" "^7.19.4" 846 | "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" 847 | "@babel/plugin-proposal-optional-chaining" "^7.18.9" 848 | "@babel/plugin-proposal-private-methods" "^7.18.6" 849 | "@babel/plugin-proposal-private-property-in-object" "^7.18.6" 850 | "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" 851 | "@babel/plugin-syntax-async-generators" "^7.8.4" 852 | "@babel/plugin-syntax-class-properties" "^7.12.13" 853 | "@babel/plugin-syntax-class-static-block" "^7.14.5" 854 | "@babel/plugin-syntax-dynamic-import" "^7.8.3" 855 | "@babel/plugin-syntax-export-namespace-from" "^7.8.3" 856 | "@babel/plugin-syntax-import-assertions" "^7.18.6" 857 | "@babel/plugin-syntax-json-strings" "^7.8.3" 858 | "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" 859 | "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" 860 | "@babel/plugin-syntax-numeric-separator" "^7.10.4" 861 | "@babel/plugin-syntax-object-rest-spread" "^7.8.3" 862 | "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" 863 | "@babel/plugin-syntax-optional-chaining" "^7.8.3" 864 | "@babel/plugin-syntax-private-property-in-object" "^7.14.5" 865 | "@babel/plugin-syntax-top-level-await" "^7.14.5" 866 | "@babel/plugin-transform-arrow-functions" "^7.18.6" 867 | "@babel/plugin-transform-async-to-generator" "^7.18.6" 868 | "@babel/plugin-transform-block-scoped-functions" "^7.18.6" 869 | "@babel/plugin-transform-block-scoping" "^7.19.4" 870 | "@babel/plugin-transform-classes" "^7.19.0" 871 | "@babel/plugin-transform-computed-properties" "^7.18.9" 872 | "@babel/plugin-transform-destructuring" "^7.19.4" 873 | "@babel/plugin-transform-dotall-regex" "^7.18.6" 874 | "@babel/plugin-transform-duplicate-keys" "^7.18.9" 875 | "@babel/plugin-transform-exponentiation-operator" "^7.18.6" 876 | "@babel/plugin-transform-for-of" "^7.18.8" 877 | "@babel/plugin-transform-function-name" "^7.18.9" 878 | "@babel/plugin-transform-literals" "^7.18.9" 879 | "@babel/plugin-transform-member-expression-literals" "^7.18.6" 880 | "@babel/plugin-transform-modules-amd" "^7.18.6" 881 | "@babel/plugin-transform-modules-commonjs" "^7.18.6" 882 | "@babel/plugin-transform-modules-systemjs" "^7.19.0" 883 | "@babel/plugin-transform-modules-umd" "^7.18.6" 884 | "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" 885 | "@babel/plugin-transform-new-target" "^7.18.6" 886 | "@babel/plugin-transform-object-super" "^7.18.6" 887 | "@babel/plugin-transform-parameters" "^7.18.8" 888 | "@babel/plugin-transform-property-literals" "^7.18.6" 889 | "@babel/plugin-transform-regenerator" "^7.18.6" 890 | "@babel/plugin-transform-reserved-words" "^7.18.6" 891 | "@babel/plugin-transform-shorthand-properties" "^7.18.6" 892 | "@babel/plugin-transform-spread" "^7.19.0" 893 | "@babel/plugin-transform-sticky-regex" "^7.18.6" 894 | "@babel/plugin-transform-template-literals" "^7.18.9" 895 | "@babel/plugin-transform-typeof-symbol" "^7.18.9" 896 | "@babel/plugin-transform-unicode-escapes" "^7.18.10" 897 | "@babel/plugin-transform-unicode-regex" "^7.18.6" 898 | "@babel/preset-modules" "^0.1.5" 899 | "@babel/types" "^7.19.4" 900 | babel-plugin-polyfill-corejs2 "^0.3.3" 901 | babel-plugin-polyfill-corejs3 "^0.6.0" 902 | babel-plugin-polyfill-regenerator "^0.4.1" 903 | core-js-compat "^3.25.1" 904 | semver "^6.3.0" 905 | 906 | "@babel/preset-modules@^0.1.5": 907 | version "0.1.5" 908 | resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" 909 | integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== 910 | dependencies: 911 | "@babel/helper-plugin-utils" "^7.0.0" 912 | "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" 913 | "@babel/plugin-transform-dotall-regex" "^7.4.4" 914 | "@babel/types" "^7.4.4" 915 | esutils "^2.0.2" 916 | 917 | "@babel/preset-react@^7.16.7": 918 | version "7.18.6" 919 | resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" 920 | integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== 921 | dependencies: 922 | "@babel/helper-plugin-utils" "^7.18.6" 923 | "@babel/helper-validator-option" "^7.18.6" 924 | "@babel/plugin-transform-react-display-name" "^7.18.6" 925 | "@babel/plugin-transform-react-jsx" "^7.18.6" 926 | "@babel/plugin-transform-react-jsx-development" "^7.18.6" 927 | "@babel/plugin-transform-react-pure-annotations" "^7.18.6" 928 | 929 | "@babel/runtime@^7.8.4": 930 | version "7.19.4" 931 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" 932 | integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA== 933 | dependencies: 934 | regenerator-runtime "^0.13.4" 935 | 936 | "@babel/template@^7.18.10": 937 | version "7.18.10" 938 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" 939 | integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== 940 | dependencies: 941 | "@babel/code-frame" "^7.18.6" 942 | "@babel/parser" "^7.18.10" 943 | "@babel/types" "^7.18.10" 944 | 945 | "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.19.3", "@babel/traverse@^7.19.4": 946 | version "7.19.4" 947 | resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.4.tgz#f117820e18b1e59448a6c1fa9d0ff08f7ac459a8" 948 | integrity sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g== 949 | dependencies: 950 | "@babel/code-frame" "^7.18.6" 951 | "@babel/generator" "^7.19.4" 952 | "@babel/helper-environment-visitor" "^7.18.9" 953 | "@babel/helper-function-name" "^7.19.0" 954 | "@babel/helper-hoist-variables" "^7.18.6" 955 | "@babel/helper-split-export-declaration" "^7.18.6" 956 | "@babel/parser" "^7.19.4" 957 | "@babel/types" "^7.19.4" 958 | debug "^4.1.0" 959 | globals "^11.1.0" 960 | 961 | "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.19.3", "@babel/types@^7.19.4", "@babel/types@^7.4.4": 962 | version "7.19.4" 963 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.4.tgz#0dd5c91c573a202d600490a35b33246fed8a41c7" 964 | integrity sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw== 965 | dependencies: 966 | "@babel/helper-string-parser" "^7.19.4" 967 | "@babel/helper-validator-identifier" "^7.19.1" 968 | to-fast-properties "^2.0.0" 969 | 970 | "@jridgewell/gen-mapping@^0.1.0": 971 | version "0.1.1" 972 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" 973 | integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== 974 | dependencies: 975 | "@jridgewell/set-array" "^1.0.0" 976 | "@jridgewell/sourcemap-codec" "^1.4.10" 977 | 978 | "@jridgewell/gen-mapping@^0.3.2": 979 | version "0.3.2" 980 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" 981 | integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== 982 | dependencies: 983 | "@jridgewell/set-array" "^1.0.1" 984 | "@jridgewell/sourcemap-codec" "^1.4.10" 985 | "@jridgewell/trace-mapping" "^0.3.9" 986 | 987 | "@jridgewell/resolve-uri@3.1.0": 988 | version "3.1.0" 989 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" 990 | integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== 991 | 992 | "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": 993 | version "1.1.2" 994 | resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" 995 | integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== 996 | 997 | "@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": 998 | version "1.4.14" 999 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" 1000 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== 1001 | 1002 | "@jridgewell/trace-mapping@^0.3.9": 1003 | version "0.3.16" 1004 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz#a7982f16c18cae02be36274365433e5b49d7b23f" 1005 | integrity sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA== 1006 | dependencies: 1007 | "@jridgewell/resolve-uri" "3.1.0" 1008 | "@jridgewell/sourcemap-codec" "1.4.14" 1009 | 1010 | "@rollup/plugin-babel@^5.3.1": 1011 | version "5.3.1" 1012 | resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" 1013 | integrity sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q== 1014 | dependencies: 1015 | "@babel/helper-module-imports" "^7.10.4" 1016 | "@rollup/pluginutils" "^3.1.0" 1017 | 1018 | "@rollup/plugin-node-resolve@^13.1.3": 1019 | version "13.3.0" 1020 | resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz#da1c5c5ce8316cef96a2f823d111c1e4e498801c" 1021 | integrity sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw== 1022 | dependencies: 1023 | "@rollup/pluginutils" "^3.1.0" 1024 | "@types/resolve" "1.17.1" 1025 | deepmerge "^4.2.2" 1026 | is-builtin-module "^3.1.0" 1027 | is-module "^1.0.0" 1028 | resolve "^1.19.0" 1029 | 1030 | "@rollup/pluginutils@^3.1.0": 1031 | version "3.1.0" 1032 | resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" 1033 | integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== 1034 | dependencies: 1035 | "@types/estree" "0.0.39" 1036 | estree-walker "^1.0.1" 1037 | picomatch "^2.2.2" 1038 | 1039 | "@types/estree@0.0.39": 1040 | version "0.0.39" 1041 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" 1042 | integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== 1043 | 1044 | "@types/node@*": 1045 | version "18.8.3" 1046 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.8.3.tgz#ce750ab4017effa51aed6a7230651778d54e327c" 1047 | integrity sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w== 1048 | 1049 | "@types/resolve@1.17.1": 1050 | version "1.17.1" 1051 | resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" 1052 | integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== 1053 | dependencies: 1054 | "@types/node" "*" 1055 | 1056 | ansi-styles@^3.2.1: 1057 | version "3.2.1" 1058 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 1059 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 1060 | dependencies: 1061 | color-convert "^1.9.0" 1062 | 1063 | babel-plugin-dynamic-import-node@^2.3.3: 1064 | version "2.3.3" 1065 | resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" 1066 | integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== 1067 | dependencies: 1068 | object.assign "^4.1.0" 1069 | 1070 | babel-plugin-polyfill-corejs2@^0.3.3: 1071 | version "0.3.3" 1072 | resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" 1073 | integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== 1074 | dependencies: 1075 | "@babel/compat-data" "^7.17.7" 1076 | "@babel/helper-define-polyfill-provider" "^0.3.3" 1077 | semver "^6.1.1" 1078 | 1079 | babel-plugin-polyfill-corejs3@^0.6.0: 1080 | version "0.6.0" 1081 | resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" 1082 | integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== 1083 | dependencies: 1084 | "@babel/helper-define-polyfill-provider" "^0.3.3" 1085 | core-js-compat "^3.25.1" 1086 | 1087 | babel-plugin-polyfill-regenerator@^0.4.1: 1088 | version "0.4.1" 1089 | resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" 1090 | integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== 1091 | dependencies: 1092 | "@babel/helper-define-polyfill-provider" "^0.3.3" 1093 | 1094 | browserslist@^4.21.3, browserslist@^4.21.4: 1095 | version "4.21.4" 1096 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" 1097 | integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== 1098 | dependencies: 1099 | caniuse-lite "^1.0.30001400" 1100 | electron-to-chromium "^1.4.251" 1101 | node-releases "^2.0.6" 1102 | update-browserslist-db "^1.0.9" 1103 | 1104 | builtin-modules@^3.3.0: 1105 | version "3.3.0" 1106 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" 1107 | integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== 1108 | 1109 | call-bind@^1.0.2: 1110 | version "1.0.2" 1111 | resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" 1112 | integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== 1113 | dependencies: 1114 | function-bind "^1.1.1" 1115 | get-intrinsic "^1.0.2" 1116 | 1117 | caniuse-lite@^1.0.30001400: 1118 | version "1.0.30001418" 1119 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz#5f459215192a024c99e3e3a53aac310fc7cf24e6" 1120 | integrity sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg== 1121 | 1122 | chalk@^2.0.0: 1123 | version "2.4.2" 1124 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 1125 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 1126 | dependencies: 1127 | ansi-styles "^3.2.1" 1128 | escape-string-regexp "^1.0.5" 1129 | supports-color "^5.3.0" 1130 | 1131 | color-convert@^1.9.0: 1132 | version "1.9.3" 1133 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 1134 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 1135 | dependencies: 1136 | color-name "1.1.3" 1137 | 1138 | color-name@1.1.3: 1139 | version "1.1.3" 1140 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 1141 | integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== 1142 | 1143 | convert-source-map@^1.7.0: 1144 | version "1.8.0" 1145 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" 1146 | integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== 1147 | dependencies: 1148 | safe-buffer "~5.1.1" 1149 | 1150 | core-js-compat@^3.25.1: 1151 | version "3.25.5" 1152 | resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.5.tgz#0016e8158c904f7b059486639e6e82116eafa7d9" 1153 | integrity sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA== 1154 | dependencies: 1155 | browserslist "^4.21.4" 1156 | 1157 | debug@^4.1.0, debug@^4.1.1: 1158 | version "4.3.4" 1159 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 1160 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 1161 | dependencies: 1162 | ms "2.1.2" 1163 | 1164 | deepmerge@^4.2.2: 1165 | version "4.2.2" 1166 | resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" 1167 | integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== 1168 | 1169 | default-passive-events@^2.0.0: 1170 | version "2.0.0" 1171 | resolved "https://registry.yarnpkg.com/default-passive-events/-/default-passive-events-2.0.0.tgz#79b1aa67becbaab38b718469b5480fef92eda649" 1172 | integrity sha512-eMtt76GpDVngZQ3ocgvRcNCklUMwID1PaNbCNxfpDXuiOXttSh0HzBbda1HU9SIUsDc02vb7g9+3I5tlqe/qMQ== 1173 | 1174 | define-properties@^1.1.4: 1175 | version "1.1.4" 1176 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" 1177 | integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== 1178 | dependencies: 1179 | has-property-descriptors "^1.0.0" 1180 | object-keys "^1.1.1" 1181 | 1182 | electron-to-chromium@^1.4.251: 1183 | version "1.4.276" 1184 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz#17837b19dafcc43aba885c4689358b298c19b520" 1185 | integrity sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ== 1186 | 1187 | escalade@^3.1.1: 1188 | version "3.1.1" 1189 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" 1190 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== 1191 | 1192 | escape-string-regexp@^1.0.5: 1193 | version "1.0.5" 1194 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 1195 | integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== 1196 | 1197 | estree-walker@^1.0.1: 1198 | version "1.0.1" 1199 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" 1200 | integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== 1201 | 1202 | esutils@^2.0.2: 1203 | version "2.0.3" 1204 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 1205 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 1206 | 1207 | fsevents@~2.3.2: 1208 | version "2.3.2" 1209 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 1210 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 1211 | 1212 | function-bind@^1.1.1: 1213 | version "1.1.1" 1214 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 1215 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 1216 | 1217 | gensync@^1.0.0-beta.2: 1218 | version "1.0.0-beta.2" 1219 | resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" 1220 | integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== 1221 | 1222 | get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: 1223 | version "1.1.3" 1224 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" 1225 | integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== 1226 | dependencies: 1227 | function-bind "^1.1.1" 1228 | has "^1.0.3" 1229 | has-symbols "^1.0.3" 1230 | 1231 | globals@^11.1.0: 1232 | version "11.12.0" 1233 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" 1234 | integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== 1235 | 1236 | has-flag@^3.0.0: 1237 | version "3.0.0" 1238 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 1239 | integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== 1240 | 1241 | has-property-descriptors@^1.0.0: 1242 | version "1.0.0" 1243 | resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" 1244 | integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== 1245 | dependencies: 1246 | get-intrinsic "^1.1.1" 1247 | 1248 | has-symbols@^1.0.3: 1249 | version "1.0.3" 1250 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" 1251 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== 1252 | 1253 | has@^1.0.3: 1254 | version "1.0.3" 1255 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 1256 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 1257 | dependencies: 1258 | function-bind "^1.1.1" 1259 | 1260 | is-builtin-module@^3.1.0: 1261 | version "3.2.0" 1262 | resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.0.tgz#bb0310dfe881f144ca83f30100ceb10cf58835e0" 1263 | integrity sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw== 1264 | dependencies: 1265 | builtin-modules "^3.3.0" 1266 | 1267 | is-core-module@^2.9.0: 1268 | version "2.10.0" 1269 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" 1270 | integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== 1271 | dependencies: 1272 | has "^1.0.3" 1273 | 1274 | is-module@^1.0.0: 1275 | version "1.0.0" 1276 | resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" 1277 | integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== 1278 | 1279 | js-tokens@^4.0.0: 1280 | version "4.0.0" 1281 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 1282 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 1283 | 1284 | jsesc@^2.5.1: 1285 | version "2.5.2" 1286 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" 1287 | integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== 1288 | 1289 | jsesc@~0.5.0: 1290 | version "0.5.0" 1291 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" 1292 | integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== 1293 | 1294 | json5@^2.2.1: 1295 | version "2.2.1" 1296 | resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" 1297 | integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== 1298 | 1299 | lodash.debounce@^4.0.8: 1300 | version "4.0.8" 1301 | resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" 1302 | integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== 1303 | 1304 | ms@2.1.2: 1305 | version "2.1.2" 1306 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 1307 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 1308 | 1309 | node-releases@^2.0.6: 1310 | version "2.0.6" 1311 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" 1312 | integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== 1313 | 1314 | object-keys@^1.1.1: 1315 | version "1.1.1" 1316 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" 1317 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 1318 | 1319 | object.assign@^4.1.0: 1320 | version "4.1.4" 1321 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" 1322 | integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== 1323 | dependencies: 1324 | call-bind "^1.0.2" 1325 | define-properties "^1.1.4" 1326 | has-symbols "^1.0.3" 1327 | object-keys "^1.1.1" 1328 | 1329 | path-parse@^1.0.7: 1330 | version "1.0.7" 1331 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 1332 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 1333 | 1334 | picocolors@^1.0.0: 1335 | version "1.0.0" 1336 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 1337 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 1338 | 1339 | picomatch@^2.2.2: 1340 | version "2.3.1" 1341 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 1342 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1343 | 1344 | regenerate-unicode-properties@^10.1.0: 1345 | version "10.1.0" 1346 | resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" 1347 | integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== 1348 | dependencies: 1349 | regenerate "^1.4.2" 1350 | 1351 | regenerate@^1.4.2: 1352 | version "1.4.2" 1353 | resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" 1354 | integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== 1355 | 1356 | regenerator-runtime@^0.13.4: 1357 | version "0.13.9" 1358 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" 1359 | integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== 1360 | 1361 | regenerator-transform@^0.15.0: 1362 | version "0.15.0" 1363 | resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" 1364 | integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== 1365 | dependencies: 1366 | "@babel/runtime" "^7.8.4" 1367 | 1368 | regexpu-core@^5.1.0: 1369 | version "5.2.1" 1370 | resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.1.tgz#a69c26f324c1e962e9ffd0b88b055caba8089139" 1371 | integrity sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ== 1372 | dependencies: 1373 | regenerate "^1.4.2" 1374 | regenerate-unicode-properties "^10.1.0" 1375 | regjsgen "^0.7.1" 1376 | regjsparser "^0.9.1" 1377 | unicode-match-property-ecmascript "^2.0.0" 1378 | unicode-match-property-value-ecmascript "^2.0.0" 1379 | 1380 | regjsgen@^0.7.1: 1381 | version "0.7.1" 1382 | resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" 1383 | integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== 1384 | 1385 | regjsparser@^0.9.1: 1386 | version "0.9.1" 1387 | resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" 1388 | integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== 1389 | dependencies: 1390 | jsesc "~0.5.0" 1391 | 1392 | resolve@^1.14.2, resolve@^1.19.0: 1393 | version "1.22.1" 1394 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 1395 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 1396 | dependencies: 1397 | is-core-module "^2.9.0" 1398 | path-parse "^1.0.7" 1399 | supports-preserve-symlinks-flag "^1.0.0" 1400 | 1401 | rollup@^2.70.1: 1402 | version "2.79.1" 1403 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" 1404 | integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== 1405 | optionalDependencies: 1406 | fsevents "~2.3.2" 1407 | 1408 | safe-buffer@~5.1.1: 1409 | version "5.1.2" 1410 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 1411 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 1412 | 1413 | semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: 1414 | version "6.3.0" 1415 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" 1416 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== 1417 | 1418 | supports-color@^5.3.0: 1419 | version "5.5.0" 1420 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 1421 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1422 | dependencies: 1423 | has-flag "^3.0.0" 1424 | 1425 | supports-preserve-symlinks-flag@^1.0.0: 1426 | version "1.0.0" 1427 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 1428 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 1429 | 1430 | to-fast-properties@^2.0.0: 1431 | version "2.0.0" 1432 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" 1433 | integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== 1434 | 1435 | unicode-canonical-property-names-ecmascript@^2.0.0: 1436 | version "2.0.0" 1437 | resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" 1438 | integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== 1439 | 1440 | unicode-match-property-ecmascript@^2.0.0: 1441 | version "2.0.0" 1442 | resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" 1443 | integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== 1444 | dependencies: 1445 | unicode-canonical-property-names-ecmascript "^2.0.0" 1446 | unicode-property-aliases-ecmascript "^2.0.0" 1447 | 1448 | unicode-match-property-value-ecmascript@^2.0.0: 1449 | version "2.0.0" 1450 | resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" 1451 | integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== 1452 | 1453 | unicode-property-aliases-ecmascript@^2.0.0: 1454 | version "2.1.0" 1455 | resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" 1456 | integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== 1457 | 1458 | update-browserslist-db@^1.0.9: 1459 | version "1.0.10" 1460 | resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" 1461 | integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== 1462 | dependencies: 1463 | escalade "^3.1.1" 1464 | picocolors "^1.0.0" 1465 | --------------------------------------------------------------------------------