├── examples ├── r3f-zustand │ ├── src │ │ ├── vite-env.d.ts │ │ ├── index.css │ │ ├── main.tsx │ │ ├── Controls │ │ │ ├── TEMPORARY_control-kit │ │ │ │ ├── index.ts │ │ │ │ ├── ControlRig.tsx │ │ │ │ ├── dom.ts │ │ │ │ ├── camera.ts │ │ │ │ └── SphericalCamera.tsx │ │ │ ├── cameraState.ts │ │ │ ├── index.tsx │ │ │ └── eventHandler.ts │ │ ├── CameraHints.tsx │ │ └── App.tsx │ ├── vite.config.ts │ ├── tsconfig.node.json │ ├── .gitignore │ ├── index.html │ ├── tsconfig.json │ ├── package.json │ ├── README.md │ └── package-lock.json ├── EXAMPLE-React-props-useState.md └── EXAMPLE-WIP-multitouch-gestures.md ├── src ├── index.ts ├── ControlRig.tsx ├── dom.ts ├── camera.ts └── SphericalCamera.tsx ├── .gitignore ├── tsconfig.json ├── package.json ├── README.md └── camera-state.svg /examples/r3f-zustand/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | overflow: hidden; 5 | } 6 | -------------------------------------------------------------------------------- /examples/r3f-zustand/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /examples/r3f-zustand/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // DOM API 2 | export { bitmaskToArray, addEventListeners, addPreventDefaults } from './dom' 3 | export { ControlRig } from './ControlRig' 4 | 5 | // Camera API 6 | export { normalizeCoords, getScreenXY } from './camera' 7 | export { SphericalCamera } from './SphericalCamera' 8 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/index.ts: -------------------------------------------------------------------------------- 1 | // DOM API 2 | export { bitmaskToArray, addEventListeners, addPreventDefaults } from './dom' 3 | export { ControlRig } from './ControlRig' 4 | 5 | // Camera API 6 | export { normalizeCoords, getScreenXY } from './camera' 7 | export { SphericalCamera } from './SphericalCamera' 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist-ssr 12 | *.local 13 | 14 | # Editor directories and files 15 | .vscode/* 16 | !.vscode/extensions.json 17 | .idea 18 | .DS_Store 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /examples/r3f-zustand/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /examples/r3f-zustand/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "outDir": "./dist", 6 | "declaration": true, 7 | "module": "esnext", 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "strict": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "jsx": "react-jsx" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/r3f-zustand/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /examples/r3f-zustand/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-r3f", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "format": "prettier src --write", 11 | "start": "npm run dev" 12 | }, 13 | "dependencies": { 14 | "@react-three/drei": "^9.66.1", 15 | "@react-three/fiber": "^8.13.0", 16 | "control-kit": "file:../", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "three": "^0.152.2", 20 | "zustand": "^4.3.7" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.0.28", 24 | "@types/react-dom": "^18.0.11", 25 | "@vitejs/plugin-react-swc": "^3.0.0", 26 | "typescript": "^4.9.3", 27 | "vite": "^4.2.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ControlRig.tsx: -------------------------------------------------------------------------------- 1 | import { memo, useEffect } from 'react' 2 | import { addEventListeners, addPreventDefaults } from './dom' 3 | 4 | interface ControlRigProps { 5 | target?: HTMLElement 6 | onEvent: any 7 | options?: boolean | AddEventListenerOptions 8 | preventDefaults?: string[] 9 | eventTypes?: string[] 10 | } 11 | 12 | export const ControlRig = memo( 13 | ({ 14 | target, 15 | onEvent, 16 | options, 17 | preventDefaults = [], 18 | eventTypes = [] 19 | }: ControlRigProps) => { 20 | useEffect( 21 | () => addPreventDefaults(target, preventDefaults), 22 | [target, preventDefaults] 23 | ) 24 | useEffect( 25 | () => addEventListeners(target, eventTypes, onEvent, options), 26 | [target, eventTypes, onEvent, options] 27 | ) 28 | return null 29 | } 30 | ) 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "control-kit", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "main": "src/index.js", 6 | "peerDependencies": { 7 | "react": ">=18.0" 8 | }, 9 | "scripts": { 10 | "build": "tsc", 11 | "test": "tsc --noEmit && echo 'no tests'", 12 | "dev": "tsc --watch --pretty", 13 | "format": "prettier src --write", 14 | "start": "cd examples/r3f-zustand && npm install && npm run dev" 15 | }, 16 | "dependencies": { 17 | "@react-three/drei": ">=9.53", 18 | "three": ">=0.149" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^18.11.19", 22 | "@types/react": "^18.2.0", 23 | "@types/react-dom": "^18.2.0", 24 | "@types/three": "^0.149.0", 25 | "prettier-config-standard": "^5.0.0", 26 | "typescript": "^5.0.0" 27 | }, 28 | "prettier": "prettier-config-standard" 29 | } 30 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/ControlRig.tsx: -------------------------------------------------------------------------------- 1 | import { memo, useEffect } from 'react' 2 | import { addEventListeners, addPreventDefaults } from './dom' 3 | 4 | interface ControlRigProps { 5 | target?: HTMLElement 6 | onEvent: any 7 | options?: boolean | AddEventListenerOptions 8 | preventDefaults?: string[] 9 | eventTypes?: string[] 10 | } 11 | 12 | export const ControlRig = memo( 13 | ({ 14 | target, 15 | onEvent, 16 | options, 17 | preventDefaults = [], 18 | eventTypes = [] 19 | }: ControlRigProps) => { 20 | useEffect( 21 | () => addPreventDefaults(target, preventDefaults), 22 | [target, preventDefaults] 23 | ) 24 | useEffect( 25 | () => addEventListeners(target, eventTypes, onEvent, options), 26 | [target, eventTypes, onEvent, options] 27 | ) 28 | return null 29 | } 30 | ) 31 | -------------------------------------------------------------------------------- /src/dom.ts: -------------------------------------------------------------------------------- 1 | // Add event listeners to a list of events on a target; return a function to remove them 2 | export const addEventListeners = ( 3 | target: undefined | HTMLElement, 4 | types: undefined | string[], 5 | listener: EventListenerOrEventListenerObject, 6 | options?: boolean | AddEventListenerOptions 7 | ) => { 8 | if (!target) return () => undefined 9 | types?.forEach((type) => target.addEventListener(type, listener, options)) 10 | return () => 11 | types?.forEach((type) => 12 | target.removeEventListener(type, listener, options) 13 | ) 14 | } 15 | 16 | // Convert a PointerEvent "buttons" prop into an array of booleans 17 | export const bitmaskToArray = (i: number) => [ 18 | !!(i & 1), 19 | !!(i & 2), 20 | !!(i & 4), 21 | !!(i & 8), 22 | !!(i & 16) 23 | ] 24 | 25 | // PreventDefault on a list of events 26 | export const addPreventDefaults = ( 27 | target: undefined | HTMLElement, 28 | types: undefined | string[] 29 | ) => addEventListeners(target, types, (event) => event.preventDefault()) 30 | -------------------------------------------------------------------------------- /src/camera.ts: -------------------------------------------------------------------------------- 1 | // TODO: Only import specific MathUtils 2 | import { Vector2, MathUtils } from 'three' 3 | 4 | // Reusable vectors (to save on GC) 5 | const handleMoveVector = new Vector2() 6 | const vector00 = new Vector2(0, 0) 7 | 8 | // Rotate X/Y vector by phi 9 | // TODO: Give this a better name, change arguments 10 | export const getScreenXY = ({ movementX, movementY, phi }) => 11 | handleMoveVector 12 | .set(movementX, movementY) 13 | .rotateAround(vector00, -phi) // Rotate screen X/Y coords to match camera rotation 14 | .toArray() 15 | 16 | // Normalize phi and constrain r & theta for an orbit camera. 17 | // This is basically half of an OrbitCamera (the 3D spherical camera transform is the other half). 18 | // TODO: Give this a better name 19 | export const normalizeCoords = ( 20 | { minR = 0, maxR = Infinity, minTheta = 0, maxTheta = Math.PI / 2 } = {}, 21 | [r, theta, phi]: number[] 22 | ) => [ 23 | MathUtils.clamp(r, minR, maxR), 24 | MathUtils.clamp(theta, minTheta, maxTheta), 25 | MathUtils.euclideanModulo(phi, Math.PI * 2) 26 | ] 27 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/dom.ts: -------------------------------------------------------------------------------- 1 | // Add event listeners to a list of events on a target; return a function to remove them 2 | export const addEventListeners = ( 3 | target: undefined | HTMLElement, 4 | types: undefined | string[], 5 | listener: EventListenerOrEventListenerObject, 6 | options?: boolean | AddEventListenerOptions 7 | ) => { 8 | if (!target) return () => undefined 9 | types?.forEach((type) => target.addEventListener(type, listener, options)) 10 | return () => 11 | types?.forEach((type) => 12 | target.removeEventListener(type, listener, options) 13 | ) 14 | } 15 | 16 | // Convert a PointerEvent "buttons" prop into an array of booleans 17 | export const bitmaskToArray = (i: number) => [ 18 | !!(i & 1), 19 | !!(i & 2), 20 | !!(i & 4), 21 | !!(i & 8), 22 | !!(i & 16) 23 | ] 24 | 25 | // PreventDefault on a list of events 26 | export const addPreventDefaults = ( 27 | target: undefined | HTMLElement, 28 | types: undefined | string[] 29 | ) => addEventListeners(target, types, (event) => event.preventDefault()) 30 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/camera.ts: -------------------------------------------------------------------------------- 1 | // TODO: Only import specific MathUtils 2 | import { Vector2, MathUtils } from 'three' 3 | 4 | // Reusable vectors (to save on GC) 5 | const handleMoveVector = new Vector2() 6 | const vector00 = new Vector2(0, 0) 7 | 8 | // Rotate X/Y vector by phi 9 | // TODO: Give this a better name, change arguments 10 | export const getScreenXY = ({ movementX, movementY, phi }) => 11 | handleMoveVector 12 | .set(movementX, movementY) 13 | .rotateAround(vector00, -phi) // Rotate screen X/Y coords to match camera rotation 14 | .toArray() 15 | 16 | // Normalize phi and constrain r & theta for an orbit camera. 17 | // This is basically half of an OrbitCamera (the 3D spherical camera transform is the other half). 18 | // TODO: Give this a better name 19 | export const normalizeCoords = ( 20 | { minR = 0, maxR = Infinity, minTheta = 0, maxTheta = Math.PI / 2 } = {}, 21 | [r, theta, phi]: number[] 22 | ) => [ 23 | MathUtils.clamp(r, minR, maxR), 24 | MathUtils.clamp(theta, minTheta, maxTheta), 25 | MathUtils.euclideanModulo(phi, Math.PI * 2) 26 | ] 27 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/CameraHints.tsx: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react' 2 | 3 | // Updates to this component are slower, 4 | // as they must pass through Reacts reconciliation system. 5 | export const CameraHintDeclarative = ({ origin }) => { 6 | return ( 7 | 8 | 9 | 10 | 11 | 12 | 13 | ) 14 | } 15 | 16 | // Updates to this component are faster, as they bypass React reconciliation. 17 | // The downside is far less maintainable code. 18 | export const CameraHintImperative = ({ updateStream }) => { 19 | const ref = useRef() 20 | useEffect( 21 | () => 22 | // Take a stream of {origin,coords} updates and imperatively move camera. 23 | // Escape hatch for bypassing the React update system (i.e. for animations). 24 | updateStream && 25 | updateStream(({ origin }) => { 26 | if (!ref.current) return 27 | if (origin) ref.current.position.set(...origin) 28 | }), 29 | [updateStream] 30 | ) 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 | 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/Controls/cameraState.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand' 2 | 3 | export const useCameraConfig = create((set) => ({ 4 | minR: 1, 5 | maxR: Infinity, 6 | minTheta: Math.PI / 16, 7 | maxTheta: Math.PI / 2, 8 | defaultOrigin: [0, 0, 0], 9 | defaultCoords: [1, Math.PI / 4, 0], 10 | updateCameraConfig: (updater: Function) => 11 | set( 12 | (state) => updater(state) ?? {} // Ignore null/undefined 13 | ) 14 | })) 15 | 16 | export const { updateCameraConfig } = useCameraConfig.getState() 17 | 18 | export const useCamera = create(() => ({ 19 | origin: useCameraConfig.getState().defaultOrigin, 20 | coords: useCameraConfig.getState().defaultCoords 21 | })) 22 | 23 | // If you were to keep the updateCamera action inside your Zustand state: 24 | // 25 | // export const useCamera = create((set) => ({ 26 | // origin: useCameraConfig.getState().defaultOrigin, 27 | // coords: useCameraConfig.getState().defaultCoords, 28 | // updateCamera: (updater: Function) => 29 | // set( 30 | // (state) => updater(state) ?? {} // Ignore null/undefined 31 | // ) 32 | // })) 33 | // 34 | // export const { updateCamera } = useCamera.getState() 35 | 36 | // Instead, we're doing a manual imperative update (for perf etc) 37 | export const updateCamera = (updater) => { 38 | const oldState = useCamera.getState() 39 | const state = updater(oldState) 40 | if (state === undefined || state === null) return 41 | if (!state.origin) state.origin = oldState.origin 42 | if (!state.coords) state.coords = oldState.coords 43 | useCamera.setState(state) 44 | } 45 | -------------------------------------------------------------------------------- /examples/EXAMPLE-React-props-useState.md: -------------------------------------------------------------------------------- 1 | # React props/`useState` example 2 | 3 | Directly setting `origin`/`coords` props (such as with `useState`) is idiomatic, but not should not be used, as React reconciliation adds excessive latency. 4 | 5 | TODO: Benchmark `updateStream` vs `memo` + props with the following datatypes to see if this advice has changed: 6 | 7 | - `Array` 8 | - [Tuple](https://github.com/tc39/proposal-record-tuple) 9 | - [List](https://github.com/funkia/list) 10 | 11 | ```js 12 | import { useState } from 'react' 13 | import { Canvas } from '@react-three/fiber' 14 | import { SphericalCamera, normalizeCoords, bitmaskToArray } from 'control-kit' 15 | 16 | function App() { 17 | const [origin, setOrigin] = useState([0, 0, 0]) 18 | const [coords, setCoords] = useState([16, Math.PI / 4, 0]) 19 | const [cameraConfig, setCameraConfig] = useState({ 20 | minR: 1, 21 | maxR: Infinity, 22 | minTheta: Math.PI / 16, 23 | maxTheta: Math.PI / 2 24 | }) 25 | 26 | const mouseMoveHandler = ({ buttons, movementX, movementY }) => { 27 | const [leftButton, rightButton] = bitmaskToArray(buttons) 28 | if (leftButton) { 29 | // Rotate camera 30 | const [r, theta, phi] = coords 31 | const newCoords = normalizeCoords(cameraConfig, [ 32 | r, 33 | theta + movementY / 100, 34 | phi - movementX / 100 35 | ]) 36 | setCoords(newCoords) 37 | } 38 | } 39 | 40 | return ( 41 | 42 | 43 | 44 | ) 45 | } 46 | ``` 47 | -------------------------------------------------------------------------------- /examples/r3f-zustand/README.md: -------------------------------------------------------------------------------- 1 | # examples/r3f-zustand 2 | 3 | This is the "kitchen sink" example of an orbit camera controller in react-three-fiber, using Zustand for state management. 4 | 5 | **Note:** Changes to <./src/Controls> must follow semver! 6 | 7 | To try it, run: 8 | 9 | ```sh 10 | npm install 11 | npm run dev 12 | ``` 13 | 14 | ## Usage as a module 15 | 16 | This example can be used as a batteries-included orbit camera controller: 17 | 18 | ```js 19 | import { 20 | Controls, 21 | useCamera, 22 | updateCamera, 23 | useCameraConfig, 24 | updateCameraConfig 25 | } from `control-kit/examples/r3f-zustand/src/Controls` 26 | ``` 27 | 28 | ### Initial state 29 | 30 | For complex apps, we recommend copying the entire `examples/r3f-zustand/Controls` directory, and editing the initial camera config yourself. 31 | However, if you're just importing `examples/r3f-zustand/Controls` directly, this is a pattern for setting initial state: 32 | 33 | ```js 34 | import { useEffect } from 'react' 35 | import { 36 | Controls, 37 | updateCamera, 38 | useCameraConfig, 39 | updateCameraConfig 40 | } from './Controls' 41 | 42 | export const App = () => { 43 | useEffect(() => 44 | // Set initial camera config; update camera 45 | { 46 | const cameraConfig = { 47 | defaultOrigin: [0, 0.5, 0], 48 | defaultCoords: [5, Math.PI / 4, Math.PI / 8] 49 | } 50 | updateCameraConfig(() => cameraConfig) 51 | updateCamera(() => ({ 52 | origin: cameraConfig.defaultOrigin, 53 | coords: cameraConfig.defaultCoords 54 | })) 55 | }, []) // Only run on initial component mount 56 | return ( 57 | 58 | 59 | 60 | ) 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /src/SphericalCamera.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react' 2 | import { invalidate } from '@react-three/fiber' 3 | import { PerspectiveCamera } from '@react-three/drei' 4 | 5 | export const SphericalCamera = ({ 6 | makeDefault = true, // Make this the default three camera 7 | updateStream, // Stream of { origin, coords } updates 8 | // TODO: Depreciate origin/coords in this type (hopefully we can recommend them in the future) 9 | origin = [0, 0, 0], // Target position 10 | coords = [0, 0, 0], // Camera rotation 11 | ...cameraProps 12 | }) => { 13 | const groupRef = useRef() 14 | const cameraRef = useRef() 15 | // TODO: Show a development-mode console depreciation warning if `origin`/`coords` is truthy 16 | useEffect( 17 | () => 18 | // Subscribe to a stream of {origin,coords} updates and imperatively move camera. 19 | // Escape hatch for bypassing React diffing. 20 | updateStream && 21 | updateStream(({ origin, coords }) => { 22 | if (!groupRef.current || !cameraRef.current) return 23 | if (origin) groupRef.current.position.set(...origin) 24 | if (coords) { 25 | const [r, theta, phi] = coords 26 | cameraRef.current.position.x = r 27 | groupRef.current.rotation.z = theta 28 | groupRef.current.rotation.y = phi 29 | } 30 | if (origin || coords) invalidate() 31 | }), 32 | [updateStream, invalidate] // TODO: Invalidate probably shouldn't be a dependency 33 | ) 34 | const [r, theta, phi] = coords 35 | // TODO: Use maths, not a https://github.com/garbo-succus/control-kit/issues/6 36 | return ( 37 | 38 | 45 | 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/Controls/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useThree } from '@react-three/fiber' 3 | // TODO: Remove `TEMPORARY_control-kit` - https://github.com/garbo-succus/control-kit/issues/7 4 | import { ControlRig, SphericalCamera } from './TEMPORARY_control-kit' 5 | import { useCamera, updateCamera } from './cameraState' 6 | import { eventHandler } from './eventHandler' 7 | 8 | export const Controls = () => { 9 | // Get r3f canvas element 10 | const target = useThree((three) => three.gl.domElement) 11 | // Force an initial updateStream state update to fire 12 | useEffect(() => void updateCamera(() => ({})), []) 13 | return ( 14 | <> 15 | {/* 16 | This would be idiomatic React: 17 | const origin = useCamera((s) => s.origin) 18 | const coords = useCamera((s) => s.coords) 19 | return ( ... 20 | 21 | ... ) 22 | However, with updateStream, we can bypass diffing for better perf and make Paul happy. 23 | */} 24 | 25 | 26 | {/* ControlRig lets us attach event handlers from anywhere in the app */} 27 | 42 | 43 | ) 44 | } 45 | 46 | // Export `examples/r3f-zustand/Controls` as a library 47 | export { 48 | useCamera, 49 | updateCamera, 50 | useCameraConfig, 51 | updateCameraConfig 52 | } from './cameraState' 53 | export { eventHandler, handlePointer, handleWheel } from './eventHandler' 54 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/SphericalCamera.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react' 2 | import { invalidate } from '@react-three/fiber' 3 | import { PerspectiveCamera } from '@react-three/drei' 4 | 5 | export const SphericalCamera = ({ 6 | makeDefault = true, // Make this the default three camera 7 | updateStream, // Stream of { origin, coords } updates 8 | // TODO: Depreciate origin/coords in this type (hopefully we can recommend them in the future) 9 | origin = [0, 0, 0], // Target position 10 | coords = [0, 0, 0], // Camera rotation 11 | ...cameraProps 12 | }) => { 13 | const groupRef = useRef() 14 | const cameraRef = useRef() 15 | // TODO: Show a development-mode console depreciation warning if `origin`/`coords` is truthy 16 | useEffect( 17 | () => 18 | // Subscribe to a stream of {origin,coords} updates and imperatively move camera. 19 | // Escape hatch for bypassing React diffing. 20 | updateStream && 21 | updateStream(({ origin, coords }) => { 22 | if (!groupRef.current || !cameraRef.current) return 23 | if (origin) groupRef.current.position.set(...origin) 24 | if (coords) { 25 | const [r, theta, phi] = coords 26 | cameraRef.current.position.x = r 27 | groupRef.current.rotation.z = theta 28 | groupRef.current.rotation.y = phi 29 | } 30 | if (origin || coords) invalidate() 31 | }), 32 | [updateStream, invalidate] // TODO: Invalidate probably shouldn't be a dependency 33 | ) 34 | const [r, theta, phi] = coords 35 | // TODO: Use maths, not a https://github.com/garbo-succus/control-kit/issues/6 36 | return ( 37 | 38 | 45 | 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { Canvas } from '@react-three/fiber' 3 | import { 4 | Controls, 5 | useCamera, 6 | updateCamera, 7 | useCameraConfig, 8 | updateCameraConfig 9 | } from './Controls' 10 | import { CameraHintImperative, CameraHintDeclarative } from './CameraHints' 11 | 12 | export default function App() { 13 | const origin = useCamera((s) => s.origin) 14 | 15 | // NOTE: For complex apps, we recommend copying the entire `examples/r3f-zustand/Controls` directory, 16 | // and editing the initial camera config yourself. 17 | // However, if you're just importing `examples/r3f-zustand/Controls` directly, 18 | // this is a pattern for setting initial state: 19 | useEffect(() => 20 | // Set initial camera config; update camera 21 | { 22 | const config = { 23 | defaultOrigin: [0, 0.5, 0], 24 | defaultCoords: [5, Math.PI / 4, Math.PI / 8] 25 | } 26 | updateCameraConfig(() => config) 27 | updateCamera(() => ({ 28 | origin: config.defaultOrigin, 29 | coords: config.defaultCoords 30 | })) 31 | }, []) // Only run on initial component mount 32 | return ( 33 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {/* Demonstrate bypassing React reconciliation */} 52 | {/* TODO: BUG: This does not render in the correct position for first frame */} 53 | 54 | 55 | 56 | ) 57 | } 58 | -------------------------------------------------------------------------------- /examples/r3f-zustand/src/Controls/eventHandler.ts: -------------------------------------------------------------------------------- 1 | // TODO: Remove `TEMPORARY_control-kit` - https://github.com/garbo-succus/control-kit/issues/7 2 | import { 3 | normalizeCoords, 4 | bitmaskToArray, 5 | getScreenXY 6 | } from './TEMPORARY_control-kit' 7 | import { useCameraConfig, updateCamera } from './cameraState' 8 | 9 | export const eventHandler = (event) => { 10 | const cameraConfig = useCameraConfig.getState() 11 | switch (event.type) { 12 | case 'pointerup': 13 | case 'pointerdown': 14 | case 'pointermove': 15 | return updateCamera(handlePointer(event, cameraConfig)) 16 | case 'wheel': 17 | return updateCamera(handleWheel(event, cameraConfig)) 18 | default: 19 | return 20 | } 21 | } 22 | 23 | export const handlePointer = 24 | ({ buttons, movementX, movementY }: PointerEvent, cameraConfig: Object) => 25 | ({ origin: [x, y, z], coords: [r, theta, phi] }) => { 26 | const [leftButton, rightButton, middleButton] = bitmaskToArray(buttons) 27 | 28 | // Simple (imperfect) way to scale mouse movement to camera movement 29 | // TODO: Match pointer movement to origin movement when looking straight down 30 | // Set the camera coords to [10, Math.PI / 2, 0] (i.e. looking straight down). 31 | // Pick a reference point at coords [0, 0, 0] and move the camera (origin [x, , z]). 32 | // The reference point should follow the cursor position exactly throughout the entire gesture. 33 | // Try the same at coords [100, Math.PI / 2, 0] etc. 34 | // NB: We want to ignore theta when gesturing along the screen Y axis, 35 | // so that pan gesture speed is always consistent regardless of camera rotation. 36 | const scaling = 100 37 | 38 | // Move camera (origin) 39 | if (leftButton) { 40 | const screenXY = getScreenXY({ movementX, movementY, phi }) 41 | // TODO: Remove this when we fix scaling 42 | .map((v: number) => (v * r) / (scaling * 10)) // Move faster as we move out further 43 | const origin = [x - screenXY[1], y, z + screenXY[0]] 44 | return { origin } 45 | } 46 | 47 | // Rotate camera (coords) 48 | if (rightButton) { 49 | const coords = normalizeCoords(cameraConfig, [ 50 | r, 51 | theta + movementY / scaling, 52 | phi - movementX / scaling 53 | ]) 54 | return { coords } 55 | } 56 | 57 | // Reset camera 58 | if (middleButton) { 59 | return { 60 | origin: cameraConfig.defaultOrigin, 61 | coords: cameraConfig.defaultCoords 62 | } 63 | } 64 | } 65 | 66 | export const handleWheel = (event: PointerEvent, cameraConfig: Object) => (state) => { 67 | // TODO: Add support for Safari touchpad gesture events 68 | const { 69 | altKey, 70 | ctrlKey // `true` on trackpads when pinch-zooming (or ctrl key is pressed) 71 | } = event 72 | 73 | // Move camera (origin) when alt+wheeling 74 | if (altKey) { 75 | // Swap deltaX/deltaY if user is holding the ctrl key, 76 | // so that 1-directional mouse scrollwheels can be used to move both forward/back and left/right 77 | const [deltaX, deltaY] = ctrlKey 78 | ? [event.deltaY, event.deltaX] 79 | : [event.deltaX, event.deltaY] 80 | const { 81 | origin: [x, y, z] 82 | } = state 83 | const scaling = 50 84 | const origin = [x - deltaX / scaling, y, z - deltaY / scaling] 85 | return { origin } 86 | } 87 | 88 | // Rotate camera (coords) 89 | const { 90 | coords: [r, theta, phi] 91 | } = state 92 | // Swap deltaY/deltaZ if user is pinching (or ctrl+wheeling) 93 | const [deltaY, deltaZ] = ctrlKey 94 | ? [event.deltaZ, event.deltaY] 95 | : [event.deltaY, event.deltaZ] 96 | const coords = normalizeCoords(cameraConfig, [ 97 | r + deltaY / (500 / r), // Dolly faster as we move out further 98 | theta - deltaZ / 100, 99 | phi + event.deltaX / 200 100 | ]) 101 | return { coords } 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🕹️ control-kit 2 | 3 | This is a toolkit for implementing react-three-fiber camera controls. 4 | 5 | ## Rationale 6 | 7 | Camera controls are generally the main way humans interact with 3D apps. 8 | They must handle multiple input methods (touch, keyboard, mouse, gamepads, XR, pen, etc) but should get out of the way when not needed. 9 | 10 | We have found "kitchen sink" camera controllers to be difficult to integrate into state management, and easy to outgrow. 11 | 12 | Rather than ship an entire controller, we aim to provide composable pieces for building one, patterns for common requirements, and boilerplate to get started with. 13 | 14 | ## Status: Alpha 15 | 16 | This library is brand new and currently awaiting feedback. 17 | Please DM me (neftaly) on the garbo.succus.games [Discord](https://discord.gg/sJVbJcd) with any comments, questions, or encouragement. 18 | 19 | - Incomplete types 20 | - No tests 21 | - No gestures <./examples/EXAMPLE-WIP-multitouch-gestures.md> 22 | - These functions will be renamed: 23 | - `normalizeCoords` 24 | - `getScreenXY` 25 | - These functions will have their signature (args, arg order) changed: 26 | - `getScreenXY` 27 | - `` is positioned with `` (some Drei components don't work correctly) 28 | - Major API changes not expected 29 | 30 | ## Example 31 | 32 | To see the full importable <./examples/r3f-zustand> example, run `npm start`. 33 | 34 | ```js 35 | import { Canvas, useThree } from '@react-three/fiber' 36 | import { create } from 'zustand' 37 | import { 38 | ControlRig, 39 | SphericalCamera, 40 | bitmaskToArray, 41 | normalizeCoords 42 | } from 'control-kit' 43 | 44 | const cameraConfig = { 45 | minR: 1, 46 | maxR: Infinity, 47 | minTheta: Math.PI / 16, 48 | maxTheta: Math.PI / 2 49 | } 50 | 51 | const useCamera = create(() => ({ 52 | origin: [0, 0, 0], 53 | coords: [0, 0, 0] 54 | })) 55 | 56 | const eventHandler = ({ buttons, movementX, movementY }) => { 57 | const { 58 | origin, 59 | coords: [r, theta, phi] 60 | } = useCamera.getState() 61 | const [leftButton, rightButton] = bitmaskToArray(buttons) 62 | if (leftButton) { 63 | // Rotate camera 64 | const coords = normalizeCoords(cameraConfig, [ 65 | r, 66 | theta + movementY / 100, 67 | phi - movementX / 100 68 | ]) 69 | useCamera.setState({ origin, coords }) 70 | } 71 | } 72 | 73 | const OrbitControls = () => { 74 | const target = useThree((three) => three.gl.domElement) // Canvas DOM element 75 | return ( 76 | <> 77 | 78 | 83 | 84 | ) 85 | } 86 | 87 | export const App = () => ( 88 | 89 | 90 | 91 | ) 92 | ``` 93 | 94 | ## API: DOM 95 | 96 | These functions are for input handling, independent of r3f. 97 | 98 | ### `ControlRig` 99 | 100 | This convenience component attaches an `onEvent` event handler to a `target` element, for every event listed in `eventTypes`. 101 | The `options` prop to is passed to the `addEventListener` call. 102 | 103 | `event.preventDefault()` is called on all events in the `preventDefaults` prop. 104 | See the note in for advice on which events to use. 105 | 106 | ```js 107 | const App = () => { 108 | const target = useThree((three) => three.gl.domElement) // Get r3f canvas DOM element 109 | return ( 110 | 111 | console.log(event.type)} 114 | preventDefaults={['wheel', 'touchstart', 'contextmenu']} 115 | eventTypes={[ 116 | 'wheel', 117 | 'pointerover', 118 | 'pointerenter', 119 | 'pointerdown', 120 | 'pointermove', 121 | 'pointerup', 122 | 'pointercancel', 123 | 'pointerout' 124 | ]} 125 | /> 126 | 127 | ) 128 | } 129 | ``` 130 | 131 | ### `bitmaskToArray` 132 | 133 | This function converts `event.buttons` from a pointer event into an array of booleans. 134 | 135 | ```js 136 | const onPointerMove = (event) => { 137 | const [ 138 | leftMouseButton, 139 | rightMouseButton, 140 | middleMouseButton, 141 | mouseButton4, 142 | mouseButton5 143 | ] = bitmaskToArray(event.buttons) 144 | } 145 | ``` 146 | 147 | ### `addEventListeners` 148 | 149 | Adds event listeners for list of events to a target; returns a function to remove them. 150 | See . 151 | 152 | **Note:** You should avoid using mouse and touch events, as they have been obsoleted by pointer events. 153 | 154 | **Note:** You should avoid using `contextmenu` events. 155 | They are not supported on iOS. 156 | Unless you have a specific need for `contextmenu`, you should use a right-click or long-press gesture instead. 157 | 158 | ```js 159 | const removeEventListeners = addEventListeners( 160 | document.getElementById('mydiv'), 161 | [ 162 | 'wheel', 163 | 'pointerover', 164 | 'pointerenter', 165 | 'pointerdown', 166 | 'pointermove', 167 | 'pointerup', 168 | 'pointercancel', 169 | 'pointerout' 170 | ], 171 | (event) => console.log(event.type), 172 | { capture: false, once: false, passive: false } 173 | ) 174 | ``` 175 | 176 | ### `addPreventDefaults` 177 | 178 | Adds `event.preventDefault()` to a list of events; returns a function to remove them. 179 | 180 | ```js 181 | const removePreventDefaults = addPreventDefaults( 182 | document.getElementById('mydiv'), 183 | ['wheel', 'touchstart', 'contextmenu'] 184 | ) 185 | ``` 186 | 187 | **Note:** You should preventDefault these events: 188 | 189 | ```js 190 | const types = [ 191 | // Block ctrl + mousewheel (zoom in/out on most browsers) 192 | // Block alt + mousewheel (history gesture in Firefox) 193 | 'wheel', 194 | 195 | // Block iOS swipe history gesture 196 | 'touchstart', 197 | 198 | // Stop right-clicks from opening the browser context menu 199 | 'contextmenu' 200 | ] 201 | ``` 202 | 203 | ## API: Camera 204 | 205 | These functions are specifically for handling 3D cameras. 206 | 207 | ![A visual diagram of the camera state](./camera-state.svg) 208 | 209 | ```js 210 | // origin: target world position 211 | const [x, y, z] = origin 212 | 213 | // coords: camera rotation, relative to origin 214 | const [ 215 | r, // Distance to origin 216 | theta, // Polar (up-down) angle 217 | phi // Azimuthal (left-right) angle 218 | ] = coords 219 | ``` 220 | 221 | ### `SphericalCamera` 222 | 223 | This component creates a `` looking at `origin`, rotated by `coords`. 224 | It accepts all PerspectiveCamera props (`makeDefault` is true by default). 225 | 226 | For performance reasons you should use `updateStream` instead of the `origin` & `coords` props. 227 | 228 | The `updateStream` prop is provided as an escape hatch to bypass React reconciliation with imperative `{origin?,coords?}` updates. 229 | For further discussion see <./examples/EXAMPLE-React-props-useState.md>. 230 | 231 | ### `normalizeCoords` 232 | 233 | **Note:** This function will be renamed in the future. 234 | 235 | This function takes a `coords` array and: 236 | 237 | - Constrains `r` (min/max distance to/from origin) 238 | - Constrains `theta` (min/max up/down angle) 239 | - Normalizes `phi` to 0 <> 2π rads (if we rotated 360°, reset to 0°) 240 | 241 | ```js 242 | const [r, theta, phi] = normalizeCoords( 243 | { 244 | minR: 0, 245 | maxR: Infinity, 246 | minTheta: 0, 247 | maxTheta: Math.PI / 2 248 | }, 249 | coords 250 | ) 251 | ``` 252 | 253 | ### `getScreenXY` 254 | 255 | **Note:** This function will be renamed in the future. 256 | 257 | **Note:** The arguments and return values for this function will be changed in the future. 258 | 259 | This function takes `movementX`, `movementY` from a PointerEvent and rotates them around `phi`. 260 | 261 | ## Notes 262 | 263 | ### Trackpad `wheel` events 264 | 265 | Most laptop trackpads support multitouch gestures. 266 | 267 | Firefox and Chrome handle them as `WheelEvent`s with `ctrlKey: true`. 268 | 269 | - 2-finger swipes behave like 2D mousewheel events (`deltaX`, `deltaY`). 270 | - 2-finger pinch (pinch-zooming) happens in the `deltaZ` direction. 271 | - 2-finger rotation gestures are not supported. 272 | 273 | [Diagram showing 2-finger swipe, pinch, and rotation multitouch gestures] 274 | 275 | Safari has more advanced support via the proprietary `gesturestart`/`gesturechange`/`gestureend` events; unfortunately the author has no experience with these. 276 | 277 | - A shim for Firefox/Chrome-style wheel event behavior in Safari should be found or made available. 278 | - An example handler for Safari gesture events should be made available. 279 | 280 | 2-finger-swipes can travel in both X and Y directions simultaneously, however the gesture may have to be initiated with diagonal touchpad movement (depending on firmware), otherwise it may only move along one axis. Users should be made aware of this in onboarding. 281 | 282 | It is recommended to show the "ctrl" key as a wheel modifier key in keyboard config interfaces, but to prevent it from being changed. 283 | 284 | ### `touchAction: 'none'` 285 | 286 | > Make sure you always set [touchAction](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) on a draggable element to prevent glitches with the browser native scrolling on touch devices 287 | 288 | -- [pmndrs/use-gesture](https://github.com/pmndrs/use-gesture) 289 | 290 | ### In-app cursors 291 | 292 | There is latency between the actual mouse cursor position and the position reported to the DOM. 293 | This can be visualized here: 294 | 295 | It is possible to mask this latency by hiding the system cursor and showing your own cursor (an emoji or SVG etc). 296 | 297 | This is generally not recommended, as it makes a11y worse, the cursor may fail to load, and there is discontinuity for the user when entering/leaving the window. However, in certain contexts (fullscreen apps etc), showing an in-app cursor can help with pointing precision and game feedback. 298 | -------------------------------------------------------------------------------- /camera-state.svg: -------------------------------------------------------------------------------- 1 | 2 | Origin [X, Y, Z]r (distance)theta (up/down angle)phi (left/right angle)Coords [r, theta, phi] 3 | -------------------------------------------------------------------------------- /examples/EXAMPLE-WIP-multitouch-gestures.md: -------------------------------------------------------------------------------- 1 | # (Multitouch) gestures - WIP 2 | 3 | This gesture handler works by processing pointer events in a [Flyd](https://github.com/paldepind/flyd) reducer. 4 | 5 | ## Planned 6 | 7 | ~~I hope to implement these before September 2023:~~ 8 | [Update Sep 2023]: My priorities have changed however this will still be done! I am working on integrating this with 9 | 10 | - short-press (tap) 11 | - drag 12 | - 2-finger pinch + rotation (ignore everything except the first 2 fingers) 13 | 14 | ## Unplanned 15 | 16 | These are out-of-scope (barring contributions, sponsorship, or trivial implementation): 17 | 18 | - long-press 19 | - 2-finger swipe 20 | - throw (finish a drag or 2+-finger swipe over a velocity threshold) 21 | - double-short-press (doubletap) 22 | - n-short-press (improved doubletap with any number of taps) 23 | - n-finger pinch + rotation, swipe (currently we ignore everything except the first 2 fingers) 24 | - n-finger drag (multiple fingers dragging different things simultaneously) 25 | - n-t-press (improved doubletap with any number of taps, of any time length - like morse code) 26 | - simultaneous independent pen input 27 | 28 | These would be cool to have, but aren't planned, and would require feasibility research: 29 | 30 | - glyphs (drag or use a pen to paint a shape; fuzzy match shape when finished) 31 | - n-hand 2-finger pinch + rotation, swipe (treat each pinch-zoom/swipe within an area approx the radius of a human hand as an individual gesture) 32 | - n-hand n-finger gestures (multiple independent multitouch gestures, as long as hands aren't too close together) 33 | 34 | ## 🚧 WORK IN PROGRESS 🚧 35 | 36 | This reducer could take a stream of DOM PointerEvents. 37 | It would then output a stream of GestureStream events (and optionally forward incoming events). 38 | It may be better to output React-style events and follow their design descisions, as long as this doesn't make interop worse. 39 | 40 | GestureStream events could have an API similar to PointerEvents, i.e. we treat different gestures as proprietary PointerEvents input devices. 41 | 42 | We could take a similar approach for turning WheelEvents and Safari touchpad gesture events into GestureStream TouchpadEvents or something. 43 | 44 | We should check the Gamepad, Keyboard, Web MIDI, and XR events and see if they could also be treated like proprietary PointerEvents (or visa-versa). 45 | 46 |
47 | 48 | _This is some old code from a prior multitouch implementation._ 49 | _I want to break it up, refactor, and if possible reduce or eliminate the dependancy on Flyd._ 50 | 51 | ### `types.d.ts` 52 | 53 | ```js 54 | 55 | type Vec2 = [number, number] 56 | type Vec3 = [number, number, number] 57 | 58 | interface GesturestreamEventGesture { 59 | touches: PointerEvent[] 60 | center: Vec2 61 | buttons: boolean[] 62 | deltaXY: Vec2 63 | targetSize?: Vec2 64 | angle?: number 65 | distance?: number 66 | deltaAngle?: number 67 | deltaDistance?: number 68 | wasTap?: boolean 69 | } 70 | 71 | interface GesturestreamEvent { 72 | event: PointerEvent 73 | gesture: GesturestreamEventGesture 74 | } 75 | ``` 76 | 77 | ### `handlers.js` 78 | 79 | ```js 80 | export const handleRotate = ({ 81 | deltaXY, 82 | targetSize 83 | }: GesturestreamEventGesture) => 84 | actions.updatePosition(({ coords: [r, oldTheta, oldPhi] }: CameraState) => { 85 | const theta = oldTheta + (deltaXY[1] / targetSize[1]) * (Math.PI * 1) 86 | const phi = oldPhi - (deltaXY[0] / targetSize[0]) * (Math.PI * 4) 87 | return { coords: [r, theta, phi] } 88 | }) 89 | 90 | export const handlePinch = ({ 91 | deltaXY, 92 | deltaAngle, 93 | deltaDistance 94 | }: GesturestreamEventGesture) => 95 | actions.updatePosition((state: CameraState) => { 96 | const { 97 | coords: [oldR, theta, oldPhi] 98 | } = state 99 | const { origin } = move(deltaXY)(state) 100 | const r = oldR + deltaDistance / (500 / oldR) 101 | const phi = oldPhi + deltaAngle 102 | return { origin, coords: [r, theta, phi] } 103 | }) 104 | ``` 105 | 106 | ### `gesturestream.js` 107 | 108 | ```js 109 | import flyd from 'flyd' 110 | import flydfilter from 'flyd/module/filter' 111 | import { 112 | assoc, 113 | dissoc, 114 | compose, 115 | values, 116 | filter, 117 | sortBy, 118 | take, 119 | map 120 | } from 'ramda' 121 | import flydDomEvents from 'flyd-dom-events' 122 | 123 | const eventTypes = [ 124 | 'wheel', 125 | 'pointerover', 126 | 'pointerenter', 127 | 'pointerdown', 128 | 'pointermove', 129 | 'pointerup', 130 | 'pointercancel', 131 | 'pointerout' 132 | ] 133 | 134 | // Subtract a list of 2D vectors 135 | const subtract = (...args) => 136 | args.reduce(([a1, a2], [b1, b2]) => [a1 - b1, a2 - b2]) 137 | 138 | // Divide a list of 2D vectors 139 | const divide = ([a, b], d) => [a / d, b / d] 140 | 141 | // Add gesture information to an event 142 | const updateGesture = ({ gesture: lastGesture = {}, event: lastEvent }, e) => { 143 | const { touches: lastTouches, center: lastCenter } = lastGesture 144 | const { pointers, event } = e 145 | if (!event) return {} // flyd.scan emits empty value on init 146 | 147 | const touchList = compose( 148 | map(({ clientX, clientY }) => [clientX, clientY]), 149 | take(2), // Ignore 3+ finger gestures 150 | sortBy((event) => event.pointerId), 151 | filter((event) => event.pointerType === 'touch'), 152 | values 153 | )(pointers) 154 | const touches = touchList.length 155 | 156 | const getDeltaCenter = (center) => 157 | lastCenter && 158 | // Don't calculate delta if we've gone from multitouch to singletouch 159 | touches === lastTouches && 160 | (lastEvent?.pointerType !== 'touch' || lastEvent?.type !== 'pointerout') 161 | ? subtract(center, lastCenter) 162 | : [0, 0] 163 | 164 | const gesture = (() => { 165 | const targetSize = [event.target.clientWidth, event.target.clientHeight] 166 | if (touches === 2) { 167 | const diff2 = subtract(...touchList) 168 | const center = subtract(touchList[0], divide(diff2, 2)) 169 | const angle = Math.PI + Math.atan2(...diff2) 170 | const distance = Math.hypot(...diff2) 171 | return { 172 | targetSize, 173 | touches, 174 | center, 175 | buttons: bitmaskToArray(touchList[0].buttons), 176 | deltaXY: getDeltaCenter(center), 177 | angle, 178 | deltaAngle: (lastGesture.angle || angle) - angle, 179 | distance, 180 | deltaDistance: (lastGesture.distance || distance) - distance 181 | } 182 | } else { 183 | const center = [event.clientX, event.clientY] 184 | const wasTap = 185 | event.type === 'pointerup' && lastEvent?.type === 'pointerdown' 186 | const buttons = 187 | event.type === 'pointerup' && lastEvent 188 | ? lastEvent.buttons 189 | : event.buttons 190 | // Initial X/Y position of gesture (when gesturing) 191 | // TODO: Should this be implemented for multitouch? 192 | const start = event.type === 'pointerdown' ? center : lastGesture.start 193 | // Total X/Y change of gesture (when gesturing) 194 | // TODO: Should this be implemented for multitouch? 195 | const offset = start 196 | ? [center[0] - start[0], center[1] - start[1]] 197 | : [0, 0] 198 | return { 199 | targetSize, 200 | touches, 201 | center, 202 | buttons: bitmaskToArray(buttons), 203 | deltaXY: getDeltaCenter(center), 204 | start, 205 | offset, 206 | wasTap 207 | } 208 | } 209 | })() 210 | return { ...e, gesture } 211 | } 212 | 213 | // Update the 'pointers' prop with currently active pointers 214 | const updatePointers = ({ pointers: lastPointers }, e) => { 215 | const { event } = e 216 | const { pointerId, type } = event 217 | const pointers = 218 | pointerId === undefined || type === 'pointerout' || type === 'pointercancel' 219 | ? dissoc(pointerId, lastPointers) 220 | : assoc(pointerId, event, lastPointers) 221 | return { ...e, pointers } 222 | } 223 | 224 | // Returns a flyd stream of gesture events from a DOM object 225 | const gesturestream = (target, options) => 226 | compose( 227 | flydfilter((e) => e.event), // flyd.scan emits empty value on init 228 | flyd.scan(updateGesture, {}), 229 | flyd.scan(updatePointers, {}), 230 | flyd.map((event) => ({ event })), 231 | flydDomEvents 232 | )(eventTypes, target, options) 233 | 234 | export default gesturestream 235 | ``` 236 | 237 | ### `useControls.jsx` 238 | 239 | This is application-specific code 240 | 241 | ```js 242 | import { handlers } from 'r3f-orbit-controls' 243 | import { useObjectStore } from 'utils/Interactable' 244 | import { map, compose, sort, assocPath } from 'ramda' 245 | import { create } from 'zustand' 246 | import { Raycaster, Vector2 } from 'three' 247 | import useStore from 'model' 248 | 249 | const coords = new Vector2() 250 | const raycaster = new Raycaster() 251 | const updateRaycaster = (center, targetSize, camera) => { 252 | const x = (center[0] / targetSize[0]) * 2 - 1 253 | const y = -(center[1] / targetSize[1]) * 2 + 1 254 | coords.set(x, y) 255 | raycaster.setFromCamera(coords, camera) 256 | return raycaster 257 | } 258 | 259 | // Get a list of object-raycaster intersections 260 | const getIntersects = (raycaster, firstHitOnly) => { 261 | const { objects } = useObjectStore.getState() 262 | // Bubble up to find the parent Interactable group 263 | const getInteractables = (object) => { 264 | if (!object) return [] 265 | const { handlers } = objects[object.uuid] || {} 266 | if (handlers) return [handlers, ...getInteractables(object.parent)] 267 | return getInteractables(object.parent) 268 | } 269 | const intersects = (() => { 270 | raycaster.firstHitOnly = !!firstHitOnly 271 | const i = raycaster.intersectObjects( 272 | Object.values(objects).map((o) => o.object), 273 | true 274 | ) 275 | raycaster.firstHitOnly = false 276 | return i 277 | })() 278 | return compose( 279 | map((intersection) => ({ 280 | intersection, 281 | handlers: getInteractables(intersection.object) 282 | })), 283 | sort((a, b) => { 284 | // Sort by depthTest = false (materials rendered on top) 285 | const dtA = a.object.material?.depthTest 286 | const dtB = b.object.material?.depthTest 287 | if (!dtA || !dtB) { 288 | if (dtA) return 1 289 | if (dtB) return -1 290 | } 291 | // Sort by renderOrder (like z-index, but only works when depthTest=false) 292 | const roA = (dtA && a.object.renderOrder) || 0 293 | const roB = (dtB && b.object.renderOrder) || 0 294 | if (roA || roB) return roA - roB 295 | // Sort by distance from camera 296 | return a.distance - b.distance 297 | }) 298 | )(intersects) 299 | } 300 | 301 | // Trigger onGesture event on targetObject with args 302 | // TODO: Only have 1 handler per object 303 | const onGesture = (targetObject, ...args) => 304 | targetObject?.handlers[0]?.onGesture?.(...args) 305 | 306 | const useControls = create((set, get) => ({ 307 | isHovering: false, 308 | targetObject: undefined, 309 | isGesturing: false, 310 | 311 | handleGesture: (onTapMissed, threecamera) => (e) => { 312 | // Decide what to do with an onGesture event. 313 | // Generally, we're either updating the camera, 314 | // or fowarding pointer events to the handlers on a Piece. 315 | const { event, gesture } = e 316 | const { type, pointerType } = event 317 | const { 318 | center, 319 | targetSize, 320 | touches, 321 | buttons: [leftButton, rightButton, middleButton] 322 | } = gesture 323 | const raycaster = updateRaycaster(center, targetSize, threecamera) 324 | const isGesturing = touches > 0 || event.buttons !== 0 325 | const isHovering = getIntersects(raycaster, true).length > 0 326 | const { dragToPanCamera } = useStore.getState().settings 327 | 328 | const sendFakePointercancelEvent = (e, raycaster) => 329 | set(({ targetObject }) => { 330 | onGesture( 331 | targetObject, 332 | assocPath(['event', 'type'], 'pointercancel', e), 333 | raycaster 334 | ) 335 | return { targetObject: undefined, isGesturing, isHovering } 336 | }) 337 | 338 | if (type === 'wheel') { 339 | handlers.handleWheel(event) 340 | } 341 | 342 | if ( 343 | type === 'pointerdown' && 344 | touches === 2 && 345 | pointerType === 'touch' && 346 | get().targetObject 347 | ) { 348 | // Drag started, but gesture became a multitouch 349 | return sendFakePointercancelEvent(e, raycaster) 350 | } 351 | 352 | if (type === 'pointermove') { 353 | if (pointerType === 'touch' && touches === 2) { 354 | handlers.handlePinch(gesture) 355 | } else if (rightButton) { 356 | handlers.handleRotate(gesture) 357 | } else if (leftButton || middleButton || pointerType === 'touch') { 358 | const { targetObject } = get() 359 | if ( 360 | middleButton || 361 | (!targetObject && (dragToPanCamera || !leftButton)) 362 | ) { 363 | handlers.handleMove(gesture) 364 | } else { 365 | onGesture(targetObject, e, raycaster) 366 | } 367 | } 368 | } else if (touches < 2) { 369 | if (type === 'pointerdown') { 370 | const intersects = getIntersects(raycaster) 371 | if (intersects.length > 0) { 372 | const targetObject = intersects[0] 373 | onGesture(targetObject, e, raycaster) 374 | return set((state) => ({ targetObject, isGesturing, isHovering })) 375 | } 376 | } else if (type === 'pointerup' || type === 'pointercancel') { 377 | return set(({ targetObject }) => { 378 | if (gesture.wasTap && !targetObject) { 379 | onTapMissed(e, raycaster) 380 | } else { 381 | onGesture(targetObject, e, raycaster) 382 | } 383 | return { targetObject: undefined, isGesturing, isHovering } 384 | }) 385 | } else if ( 386 | type === 'pointerenter' && 387 | !leftButton && 388 | pointerType !== 'touch' && 389 | get().targetObject 390 | ) { 391 | // User dragged out-of-bounds then released cursor 392 | return sendFakePointercancelEvent(e, raycaster) 393 | } 394 | } 395 | return set((state) => ({ isGesturing, isHovering })) 396 | } 397 | })) 398 | 399 | export default useControls 400 | ``` 401 | -------------------------------------------------------------------------------- /examples/r3f-zustand/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-r3f", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "example-r3f", 9 | "version": "0.0.0", 10 | "dependencies": { 11 | "@react-three/drei": "^9.66.1", 12 | "@react-three/fiber": "^8.13.0", 13 | "control-kit": "file:../", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "three": "^0.152.2", 17 | "zustand": "^4.3.7" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "^18.0.28", 21 | "@types/react-dom": "^18.0.11", 22 | "@vitejs/plugin-react-swc": "^3.0.0", 23 | "typescript": "^4.9.3", 24 | "vite": "^4.2.0" 25 | } 26 | }, 27 | "..": { 28 | "version": "0.0.0", 29 | "dependencies": { 30 | "@react-three/drei": ">=9.53", 31 | "three": ">=0.149" 32 | }, 33 | "devDependencies": { 34 | "@types/node": "^18.11.19", 35 | "@types/react": "^18.2.0", 36 | "@types/react-dom": "^18.2.0", 37 | "@types/three": "^0.149.0", 38 | "prettier-config-standard": "^5.0.0", 39 | "typescript": "^5.0.0" 40 | }, 41 | "peerDependencies": { 42 | "react": ">=18.0" 43 | } 44 | }, 45 | "node_modules/@babel/runtime": { 46 | "version": "7.21.5", 47 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", 48 | "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", 49 | "dependencies": { 50 | "regenerator-runtime": "^0.13.11" 51 | }, 52 | "engines": { 53 | "node": ">=6.9.0" 54 | } 55 | }, 56 | "node_modules/@chevrotain/cst-dts-gen": { 57 | "version": "10.5.0", 58 | "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", 59 | "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", 60 | "dependencies": { 61 | "@chevrotain/gast": "10.5.0", 62 | "@chevrotain/types": "10.5.0", 63 | "lodash": "4.17.21" 64 | } 65 | }, 66 | "node_modules/@chevrotain/gast": { 67 | "version": "10.5.0", 68 | "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", 69 | "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", 70 | "dependencies": { 71 | "@chevrotain/types": "10.5.0", 72 | "lodash": "4.17.21" 73 | } 74 | }, 75 | "node_modules/@chevrotain/types": { 76 | "version": "10.5.0", 77 | "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", 78 | "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==" 79 | }, 80 | "node_modules/@chevrotain/utils": { 81 | "version": "10.5.0", 82 | "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", 83 | "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==" 84 | }, 85 | "node_modules/@esbuild/android-arm": { 86 | "version": "0.17.18", 87 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", 88 | "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==", 89 | "cpu": [ 90 | "arm" 91 | ], 92 | "dev": true, 93 | "optional": true, 94 | "os": [ 95 | "android" 96 | ], 97 | "engines": { 98 | "node": ">=12" 99 | } 100 | }, 101 | "node_modules/@esbuild/android-arm64": { 102 | "version": "0.17.18", 103 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz", 104 | "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==", 105 | "cpu": [ 106 | "arm64" 107 | ], 108 | "dev": true, 109 | "optional": true, 110 | "os": [ 111 | "android" 112 | ], 113 | "engines": { 114 | "node": ">=12" 115 | } 116 | }, 117 | "node_modules/@esbuild/android-x64": { 118 | "version": "0.17.18", 119 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz", 120 | "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==", 121 | "cpu": [ 122 | "x64" 123 | ], 124 | "dev": true, 125 | "optional": true, 126 | "os": [ 127 | "android" 128 | ], 129 | "engines": { 130 | "node": ">=12" 131 | } 132 | }, 133 | "node_modules/@esbuild/darwin-arm64": { 134 | "version": "0.17.18", 135 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz", 136 | "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==", 137 | "cpu": [ 138 | "arm64" 139 | ], 140 | "dev": true, 141 | "optional": true, 142 | "os": [ 143 | "darwin" 144 | ], 145 | "engines": { 146 | "node": ">=12" 147 | } 148 | }, 149 | "node_modules/@esbuild/darwin-x64": { 150 | "version": "0.17.18", 151 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz", 152 | "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==", 153 | "cpu": [ 154 | "x64" 155 | ], 156 | "dev": true, 157 | "optional": true, 158 | "os": [ 159 | "darwin" 160 | ], 161 | "engines": { 162 | "node": ">=12" 163 | } 164 | }, 165 | "node_modules/@esbuild/freebsd-arm64": { 166 | "version": "0.17.18", 167 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz", 168 | "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==", 169 | "cpu": [ 170 | "arm64" 171 | ], 172 | "dev": true, 173 | "optional": true, 174 | "os": [ 175 | "freebsd" 176 | ], 177 | "engines": { 178 | "node": ">=12" 179 | } 180 | }, 181 | "node_modules/@esbuild/freebsd-x64": { 182 | "version": "0.17.18", 183 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz", 184 | "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==", 185 | "cpu": [ 186 | "x64" 187 | ], 188 | "dev": true, 189 | "optional": true, 190 | "os": [ 191 | "freebsd" 192 | ], 193 | "engines": { 194 | "node": ">=12" 195 | } 196 | }, 197 | "node_modules/@esbuild/linux-arm": { 198 | "version": "0.17.18", 199 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz", 200 | "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==", 201 | "cpu": [ 202 | "arm" 203 | ], 204 | "dev": true, 205 | "optional": true, 206 | "os": [ 207 | "linux" 208 | ], 209 | "engines": { 210 | "node": ">=12" 211 | } 212 | }, 213 | "node_modules/@esbuild/linux-arm64": { 214 | "version": "0.17.18", 215 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz", 216 | "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==", 217 | "cpu": [ 218 | "arm64" 219 | ], 220 | "dev": true, 221 | "optional": true, 222 | "os": [ 223 | "linux" 224 | ], 225 | "engines": { 226 | "node": ">=12" 227 | } 228 | }, 229 | "node_modules/@esbuild/linux-ia32": { 230 | "version": "0.17.18", 231 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz", 232 | "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==", 233 | "cpu": [ 234 | "ia32" 235 | ], 236 | "dev": true, 237 | "optional": true, 238 | "os": [ 239 | "linux" 240 | ], 241 | "engines": { 242 | "node": ">=12" 243 | } 244 | }, 245 | "node_modules/@esbuild/linux-loong64": { 246 | "version": "0.17.18", 247 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz", 248 | "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==", 249 | "cpu": [ 250 | "loong64" 251 | ], 252 | "dev": true, 253 | "optional": true, 254 | "os": [ 255 | "linux" 256 | ], 257 | "engines": { 258 | "node": ">=12" 259 | } 260 | }, 261 | "node_modules/@esbuild/linux-mips64el": { 262 | "version": "0.17.18", 263 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz", 264 | "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==", 265 | "cpu": [ 266 | "mips64el" 267 | ], 268 | "dev": true, 269 | "optional": true, 270 | "os": [ 271 | "linux" 272 | ], 273 | "engines": { 274 | "node": ">=12" 275 | } 276 | }, 277 | "node_modules/@esbuild/linux-ppc64": { 278 | "version": "0.17.18", 279 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz", 280 | "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==", 281 | "cpu": [ 282 | "ppc64" 283 | ], 284 | "dev": true, 285 | "optional": true, 286 | "os": [ 287 | "linux" 288 | ], 289 | "engines": { 290 | "node": ">=12" 291 | } 292 | }, 293 | "node_modules/@esbuild/linux-riscv64": { 294 | "version": "0.17.18", 295 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz", 296 | "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==", 297 | "cpu": [ 298 | "riscv64" 299 | ], 300 | "dev": true, 301 | "optional": true, 302 | "os": [ 303 | "linux" 304 | ], 305 | "engines": { 306 | "node": ">=12" 307 | } 308 | }, 309 | "node_modules/@esbuild/linux-s390x": { 310 | "version": "0.17.18", 311 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz", 312 | "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==", 313 | "cpu": [ 314 | "s390x" 315 | ], 316 | "dev": true, 317 | "optional": true, 318 | "os": [ 319 | "linux" 320 | ], 321 | "engines": { 322 | "node": ">=12" 323 | } 324 | }, 325 | "node_modules/@esbuild/linux-x64": { 326 | "version": "0.17.18", 327 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz", 328 | "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==", 329 | "cpu": [ 330 | "x64" 331 | ], 332 | "dev": true, 333 | "optional": true, 334 | "os": [ 335 | "linux" 336 | ], 337 | "engines": { 338 | "node": ">=12" 339 | } 340 | }, 341 | "node_modules/@esbuild/netbsd-x64": { 342 | "version": "0.17.18", 343 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz", 344 | "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==", 345 | "cpu": [ 346 | "x64" 347 | ], 348 | "dev": true, 349 | "optional": true, 350 | "os": [ 351 | "netbsd" 352 | ], 353 | "engines": { 354 | "node": ">=12" 355 | } 356 | }, 357 | "node_modules/@esbuild/openbsd-x64": { 358 | "version": "0.17.18", 359 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz", 360 | "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==", 361 | "cpu": [ 362 | "x64" 363 | ], 364 | "dev": true, 365 | "optional": true, 366 | "os": [ 367 | "openbsd" 368 | ], 369 | "engines": { 370 | "node": ">=12" 371 | } 372 | }, 373 | "node_modules/@esbuild/sunos-x64": { 374 | "version": "0.17.18", 375 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz", 376 | "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==", 377 | "cpu": [ 378 | "x64" 379 | ], 380 | "dev": true, 381 | "optional": true, 382 | "os": [ 383 | "sunos" 384 | ], 385 | "engines": { 386 | "node": ">=12" 387 | } 388 | }, 389 | "node_modules/@esbuild/win32-arm64": { 390 | "version": "0.17.18", 391 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz", 392 | "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==", 393 | "cpu": [ 394 | "arm64" 395 | ], 396 | "dev": true, 397 | "optional": true, 398 | "os": [ 399 | "win32" 400 | ], 401 | "engines": { 402 | "node": ">=12" 403 | } 404 | }, 405 | "node_modules/@esbuild/win32-ia32": { 406 | "version": "0.17.18", 407 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz", 408 | "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==", 409 | "cpu": [ 410 | "ia32" 411 | ], 412 | "dev": true, 413 | "optional": true, 414 | "os": [ 415 | "win32" 416 | ], 417 | "engines": { 418 | "node": ">=12" 419 | } 420 | }, 421 | "node_modules/@esbuild/win32-x64": { 422 | "version": "0.17.18", 423 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz", 424 | "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==", 425 | "cpu": [ 426 | "x64" 427 | ], 428 | "dev": true, 429 | "optional": true, 430 | "os": [ 431 | "win32" 432 | ], 433 | "engines": { 434 | "node": ">=12" 435 | } 436 | }, 437 | "node_modules/@react-spring/animated": { 438 | "version": "9.6.1", 439 | "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz", 440 | "integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==", 441 | "dependencies": { 442 | "@react-spring/shared": "~9.6.1", 443 | "@react-spring/types": "~9.6.1" 444 | }, 445 | "peerDependencies": { 446 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0" 447 | } 448 | }, 449 | "node_modules/@react-spring/core": { 450 | "version": "9.6.1", 451 | "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz", 452 | "integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==", 453 | "dependencies": { 454 | "@react-spring/animated": "~9.6.1", 455 | "@react-spring/rafz": "~9.6.1", 456 | "@react-spring/shared": "~9.6.1", 457 | "@react-spring/types": "~9.6.1" 458 | }, 459 | "funding": { 460 | "type": "opencollective", 461 | "url": "https://opencollective.com/react-spring/donate" 462 | }, 463 | "peerDependencies": { 464 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0" 465 | } 466 | }, 467 | "node_modules/@react-spring/rafz": { 468 | "version": "9.6.1", 469 | "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz", 470 | "integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==" 471 | }, 472 | "node_modules/@react-spring/shared": { 473 | "version": "9.6.1", 474 | "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz", 475 | "integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==", 476 | "dependencies": { 477 | "@react-spring/rafz": "~9.6.1", 478 | "@react-spring/types": "~9.6.1" 479 | }, 480 | "peerDependencies": { 481 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0" 482 | } 483 | }, 484 | "node_modules/@react-spring/three": { 485 | "version": "9.6.1", 486 | "resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.6.1.tgz", 487 | "integrity": "sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==", 488 | "dependencies": { 489 | "@react-spring/animated": "~9.6.1", 490 | "@react-spring/core": "~9.6.1", 491 | "@react-spring/shared": "~9.6.1", 492 | "@react-spring/types": "~9.6.1" 493 | }, 494 | "peerDependencies": { 495 | "@react-three/fiber": ">=6.0", 496 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0", 497 | "three": ">=0.126" 498 | } 499 | }, 500 | "node_modules/@react-spring/types": { 501 | "version": "9.6.1", 502 | "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz", 503 | "integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==" 504 | }, 505 | "node_modules/@react-three/drei": { 506 | "version": "9.66.1", 507 | "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-9.66.1.tgz", 508 | "integrity": "sha512-nbSMsdoL52bLZtOZFMjkjjGzLLkx8hOuYEgvxX19TxR9+YpFnjbfzC3NtsjK719O4OJdNEHZOxmmxoANJJAXEA==", 509 | "dependencies": { 510 | "@babel/runtime": "^7.11.2", 511 | "@react-spring/three": "~9.6.1", 512 | "@use-gesture/react": "^10.2.24", 513 | "camera-controls": "^2.3.1", 514 | "detect-gpu": "^5.0.14", 515 | "glsl-noise": "^0.0.0", 516 | "lodash.clamp": "^4.0.3", 517 | "lodash.omit": "^4.5.0", 518 | "lodash.pick": "^4.4.0", 519 | "maath": "^0.5.2", 520 | "meshline": "^3.1.6", 521 | "react-composer": "^5.0.3", 522 | "react-merge-refs": "^1.1.0", 523 | "stats.js": "^0.17.0", 524 | "suspend-react": "^0.0.8", 525 | "three-mesh-bvh": "^0.5.23", 526 | "three-stdlib": "^2.21.8", 527 | "troika-three-text": "^0.47.1", 528 | "utility-types": "^3.10.0", 529 | "zustand": "^3.5.13" 530 | }, 531 | "peerDependencies": { 532 | "@react-three/fiber": ">=8.0", 533 | "react": ">=18.0", 534 | "react-dom": ">=18.0", 535 | "three": ">=0.137" 536 | }, 537 | "peerDependenciesMeta": { 538 | "react-dom": { 539 | "optional": true 540 | } 541 | } 542 | }, 543 | "node_modules/@react-three/drei/node_modules/zustand": { 544 | "version": "3.7.2", 545 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", 546 | "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", 547 | "engines": { 548 | "node": ">=12.7.0" 549 | }, 550 | "peerDependencies": { 551 | "react": ">=16.8" 552 | }, 553 | "peerDependenciesMeta": { 554 | "react": { 555 | "optional": true 556 | } 557 | } 558 | }, 559 | "node_modules/@react-three/fiber": { 560 | "version": "8.13.0", 561 | "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.13.0.tgz", 562 | "integrity": "sha512-hPFzFNgikEMyEbL+NpSA7q+UWZxInrrkJldWaCR2w34Fwf20x9p68bsyN0/yn9oM2VlWoJcJjR8hw1tN9AxHuA==", 563 | "dependencies": { 564 | "@babel/runtime": "^7.17.8", 565 | "@types/react-reconciler": "^0.26.7", 566 | "its-fine": "^1.0.6", 567 | "react-reconciler": "^0.27.0", 568 | "react-use-measure": "^2.1.1", 569 | "scheduler": "^0.21.0", 570 | "suspend-react": "^0.0.8", 571 | "zustand": "^3.7.1" 572 | }, 573 | "peerDependencies": { 574 | "expo": ">=43.0", 575 | "expo-asset": ">=8.4", 576 | "expo-gl": ">=11.0", 577 | "react": ">=18.0", 578 | "react-dom": ">=18.0", 579 | "react-native": ">=0.64", 580 | "three": ">=0.133" 581 | }, 582 | "peerDependenciesMeta": { 583 | "expo": { 584 | "optional": true 585 | }, 586 | "expo-asset": { 587 | "optional": true 588 | }, 589 | "expo-gl": { 590 | "optional": true 591 | }, 592 | "react-dom": { 593 | "optional": true 594 | }, 595 | "react-native": { 596 | "optional": true 597 | } 598 | } 599 | }, 600 | "node_modules/@react-three/fiber/node_modules/scheduler": { 601 | "version": "0.21.0", 602 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", 603 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", 604 | "dependencies": { 605 | "loose-envify": "^1.1.0" 606 | } 607 | }, 608 | "node_modules/@react-three/fiber/node_modules/zustand": { 609 | "version": "3.7.2", 610 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", 611 | "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", 612 | "engines": { 613 | "node": ">=12.7.0" 614 | }, 615 | "peerDependencies": { 616 | "react": ">=16.8" 617 | }, 618 | "peerDependenciesMeta": { 619 | "react": { 620 | "optional": true 621 | } 622 | } 623 | }, 624 | "node_modules/@swc/core": { 625 | "version": "1.3.55", 626 | "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.55.tgz", 627 | "integrity": "sha512-w/lN3OuJsuy868yJZKop+voZLVzI5pVSoopQVtgDNkEzejnPuRp9XaeAValvuMaWqKoTMtOjLzEPyv/xiAGYQQ==", 628 | "dev": true, 629 | "hasInstallScript": true, 630 | "engines": { 631 | "node": ">=10" 632 | }, 633 | "funding": { 634 | "type": "opencollective", 635 | "url": "https://opencollective.com/swc" 636 | }, 637 | "optionalDependencies": { 638 | "@swc/core-darwin-arm64": "1.3.55", 639 | "@swc/core-darwin-x64": "1.3.55", 640 | "@swc/core-linux-arm-gnueabihf": "1.3.55", 641 | "@swc/core-linux-arm64-gnu": "1.3.55", 642 | "@swc/core-linux-arm64-musl": "1.3.55", 643 | "@swc/core-linux-x64-gnu": "1.3.55", 644 | "@swc/core-linux-x64-musl": "1.3.55", 645 | "@swc/core-win32-arm64-msvc": "1.3.55", 646 | "@swc/core-win32-ia32-msvc": "1.3.55", 647 | "@swc/core-win32-x64-msvc": "1.3.55" 648 | }, 649 | "peerDependencies": { 650 | "@swc/helpers": "^0.5.0" 651 | }, 652 | "peerDependenciesMeta": { 653 | "@swc/helpers": { 654 | "optional": true 655 | } 656 | } 657 | }, 658 | "node_modules/@swc/core-darwin-arm64": { 659 | "version": "1.3.55", 660 | "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.55.tgz", 661 | "integrity": "sha512-UnHC8aPg/JvHhgXxTU6EhTtfnYNS7nhq8EKB8laNPxlHbwEyMBVQ2QuJHlNCtFtvSfX/uH5l04Ld1iGXnBTfdQ==", 662 | "cpu": [ 663 | "arm64" 664 | ], 665 | "dev": true, 666 | "optional": true, 667 | "os": [ 668 | "darwin" 669 | ], 670 | "engines": { 671 | "node": ">=10" 672 | } 673 | }, 674 | "node_modules/@swc/core-darwin-x64": { 675 | "version": "1.3.55", 676 | "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.55.tgz", 677 | "integrity": "sha512-VNJkFVARrktIqtaLrD1NFA54gqekH7eAUcUY2U2SdHwO67HYjfMXMxlugLP5PDasSKpTkrVooUdhkffoA5W50g==", 678 | "cpu": [ 679 | "x64" 680 | ], 681 | "dev": true, 682 | "optional": true, 683 | "os": [ 684 | "darwin" 685 | ], 686 | "engines": { 687 | "node": ">=10" 688 | } 689 | }, 690 | "node_modules/@swc/core-linux-arm-gnueabihf": { 691 | "version": "1.3.55", 692 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.55.tgz", 693 | "integrity": "sha512-6OcohhIFKKNW/TpJt26Tpul8zyL7dmp1Lnyj2BX9ycsZZ5UnsNiGqn37mrqJgVTx/ansEmbyOmKu2mzm/Ct6cQ==", 694 | "cpu": [ 695 | "arm" 696 | ], 697 | "dev": true, 698 | "optional": true, 699 | "os": [ 700 | "linux" 701 | ], 702 | "engines": { 703 | "node": ">=10" 704 | } 705 | }, 706 | "node_modules/@swc/core-linux-arm64-gnu": { 707 | "version": "1.3.55", 708 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.55.tgz", 709 | "integrity": "sha512-MfZtXGBv21XWwvrSMP0CMxScDolT/iv5PRl9UBprYUehwWr7BNjA3V9W7QQ+kKoPyORWk7LX7OpJZF3FnO618Q==", 710 | "cpu": [ 711 | "arm64" 712 | ], 713 | "dev": true, 714 | "optional": true, 715 | "os": [ 716 | "linux" 717 | ], 718 | "engines": { 719 | "node": ">=10" 720 | } 721 | }, 722 | "node_modules/@swc/core-linux-arm64-musl": { 723 | "version": "1.3.55", 724 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.55.tgz", 725 | "integrity": "sha512-iZJo+7L5lv10W0f0C6SlyteAyMJt5Tp+aH3+nlAwKdtc+VjyL1sGhR8DJMXp2/buBRZJ9tjEtpXKDaWUdSdF7Q==", 726 | "cpu": [ 727 | "arm64" 728 | ], 729 | "dev": true, 730 | "optional": true, 731 | "os": [ 732 | "linux" 733 | ], 734 | "engines": { 735 | "node": ">=10" 736 | } 737 | }, 738 | "node_modules/@swc/core-linux-x64-gnu": { 739 | "version": "1.3.55", 740 | "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.55.tgz", 741 | "integrity": "sha512-Rmc8ny/mslzzz0+wNK9/mLdyAWVbMZHRSvljhpzASmq48NBkmZ5vk9/WID6MnUz2e9cQ0JxJQs8t39KlFJtW3g==", 742 | "cpu": [ 743 | "x64" 744 | ], 745 | "dev": true, 746 | "optional": true, 747 | "os": [ 748 | "linux" 749 | ], 750 | "engines": { 751 | "node": ">=10" 752 | } 753 | }, 754 | "node_modules/@swc/core-linux-x64-musl": { 755 | "version": "1.3.55", 756 | "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.55.tgz", 757 | "integrity": "sha512-Ymoc4xxINzS93ZjVd2UZfLZk1jF6wHjdCbC1JF+0zK3IrNrxCIDoWoaAj0+Bbvyo3hD1Xg/cneSTsqX8amnnuQ==", 758 | "cpu": [ 759 | "x64" 760 | ], 761 | "dev": true, 762 | "optional": true, 763 | "os": [ 764 | "linux" 765 | ], 766 | "engines": { 767 | "node": ">=10" 768 | } 769 | }, 770 | "node_modules/@swc/core-win32-arm64-msvc": { 771 | "version": "1.3.55", 772 | "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.55.tgz", 773 | "integrity": "sha512-OhnmFstq2qRU2GI5I0G/8L+vc2rx8+w+IOA6EZBrY4FuMCbPIZKKzlnAIxYn2W+yD4gvBzYP3tgEcaDfQk6EkA==", 774 | "cpu": [ 775 | "arm64" 776 | ], 777 | "dev": true, 778 | "optional": true, 779 | "os": [ 780 | "win32" 781 | ], 782 | "engines": { 783 | "node": ">=10" 784 | } 785 | }, 786 | "node_modules/@swc/core-win32-ia32-msvc": { 787 | "version": "1.3.55", 788 | "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.55.tgz", 789 | "integrity": "sha512-3VR5rHZ6uoL/Vo3djV30GgX2oyDwWWsk+Yp+nyvYyBaKYiH2zeHfxdYRLSQV3W7kSlCAH3oDYpSljrWZ0t5XEQ==", 790 | "cpu": [ 791 | "ia32" 792 | ], 793 | "dev": true, 794 | "optional": true, 795 | "os": [ 796 | "win32" 797 | ], 798 | "engines": { 799 | "node": ">=10" 800 | } 801 | }, 802 | "node_modules/@swc/core-win32-x64-msvc": { 803 | "version": "1.3.55", 804 | "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.55.tgz", 805 | "integrity": "sha512-KBtMFtRwnbxBugYf6i2ePqEGdxsk715KcqGMjGhxNg7BTACnXnhj37irHu2e7A7wZffbkUVUYuj/JEgVkEjSxg==", 806 | "cpu": [ 807 | "x64" 808 | ], 809 | "dev": true, 810 | "optional": true, 811 | "os": [ 812 | "win32" 813 | ], 814 | "engines": { 815 | "node": ">=10" 816 | } 817 | }, 818 | "node_modules/@types/offscreencanvas": { 819 | "version": "2019.7.0", 820 | "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz", 821 | "integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==" 822 | }, 823 | "node_modules/@types/prop-types": { 824 | "version": "15.7.5", 825 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", 826 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" 827 | }, 828 | "node_modules/@types/react": { 829 | "version": "18.2.0", 830 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz", 831 | "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==", 832 | "dependencies": { 833 | "@types/prop-types": "*", 834 | "@types/scheduler": "*", 835 | "csstype": "^3.0.2" 836 | } 837 | }, 838 | "node_modules/@types/react-dom": { 839 | "version": "18.2.1", 840 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.1.tgz", 841 | "integrity": "sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==", 842 | "dev": true, 843 | "dependencies": { 844 | "@types/react": "*" 845 | } 846 | }, 847 | "node_modules/@types/react-reconciler": { 848 | "version": "0.26.7", 849 | "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz", 850 | "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==", 851 | "dependencies": { 852 | "@types/react": "*" 853 | } 854 | }, 855 | "node_modules/@types/scheduler": { 856 | "version": "0.16.3", 857 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", 858 | "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" 859 | }, 860 | "node_modules/@types/stats.js": { 861 | "version": "0.17.0", 862 | "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.0.tgz", 863 | "integrity": "sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w==", 864 | "peer": true 865 | }, 866 | "node_modules/@types/three": { 867 | "version": "0.151.0", 868 | "resolved": "https://registry.npmjs.org/@types/three/-/three-0.151.0.tgz", 869 | "integrity": "sha512-hwOyN4szaikHsNzqbYLBfGvXi+TA/1TIKvK3ujJi+cQXzYzE5ZymzlDnOA6/cTSozY1juPFE3u5agToVQKb9TQ==", 870 | "peer": true, 871 | "dependencies": { 872 | "@types/stats.js": "*", 873 | "@types/webxr": "*", 874 | "fflate": "~0.6.9", 875 | "lil-gui": "~0.17.0" 876 | } 877 | }, 878 | "node_modules/@types/webxr": { 879 | "version": "0.5.1", 880 | "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.1.tgz", 881 | "integrity": "sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ==", 882 | "peer": true 883 | }, 884 | "node_modules/@use-gesture/core": { 885 | "version": "10.2.26", 886 | "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.2.26.tgz", 887 | "integrity": "sha512-NyFpQ3iID9iFBROXyyvU1D0NK+t+dP+WAVByhCvqHUenpxLD2NlRLVRpoK3XGGwksr6mU3PvZ2Nm4q0q+gLJPA==" 888 | }, 889 | "node_modules/@use-gesture/react": { 890 | "version": "10.2.26", 891 | "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.2.26.tgz", 892 | "integrity": "sha512-0QhaE5mhaQbFlip4MX7n1nwCX8gax6Da1LsP2fZ/BU6xW9zyEmV6NX7DPelDxq1rr2NiBJh30vx9RIp80YeA/A==", 893 | "dependencies": { 894 | "@use-gesture/core": "10.2.26" 895 | }, 896 | "peerDependencies": { 897 | "react": ">= 16.8.0" 898 | } 899 | }, 900 | "node_modules/@vitejs/plugin-react-swc": { 901 | "version": "3.3.0", 902 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.0.tgz", 903 | "integrity": "sha512-Ycg+n2eyCOTpn/wRy+evVo859+hw7qCj9iaX5CMny6x1fx1Uoq0xBG+a98lFtwLNGfGEnpI0F26YigRuxCRkwg==", 904 | "dev": true, 905 | "dependencies": { 906 | "@swc/core": "^1.3.42" 907 | }, 908 | "peerDependencies": { 909 | "vite": "^4" 910 | } 911 | }, 912 | "node_modules/@webgpu/glslang": { 913 | "version": "0.0.15", 914 | "resolved": "https://registry.npmjs.org/@webgpu/glslang/-/glslang-0.0.15.tgz", 915 | "integrity": "sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q==" 916 | }, 917 | "node_modules/bidi-js": { 918 | "version": "1.0.2", 919 | "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.2.tgz", 920 | "integrity": "sha512-rzSy/k7WdX5zOyeHHCOixGXbCHkyogkxPKL2r8QtzHmVQDiWCXUWa18bLdMWT9CYMLOYTjWpTHawuev2ouYJVw==", 921 | "dependencies": { 922 | "require-from-string": "^2.0.2" 923 | } 924 | }, 925 | "node_modules/camera-controls": { 926 | "version": "2.3.4", 927 | "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.3.4.tgz", 928 | "integrity": "sha512-swhc87YVHf9te0glBI7Oa/QBgsSCL4ZxtoR4V3vE6l7mEebsYRNL8y7Y2m2E6MrT0UTphM1S7mQqs0Sp7QTZ2g==", 929 | "peerDependencies": { 930 | "three": ">=0.126.1" 931 | } 932 | }, 933 | "node_modules/chevrotain": { 934 | "version": "10.5.0", 935 | "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", 936 | "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", 937 | "dependencies": { 938 | "@chevrotain/cst-dts-gen": "10.5.0", 939 | "@chevrotain/gast": "10.5.0", 940 | "@chevrotain/types": "10.5.0", 941 | "@chevrotain/utils": "10.5.0", 942 | "lodash": "4.17.21", 943 | "regexp-to-ast": "0.5.0" 944 | } 945 | }, 946 | "node_modules/control-kit": { 947 | "resolved": "..", 948 | "link": true 949 | }, 950 | "node_modules/csstype": { 951 | "version": "3.1.2", 952 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", 953 | "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" 954 | }, 955 | "node_modules/debounce": { 956 | "version": "1.2.1", 957 | "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", 958 | "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" 959 | }, 960 | "node_modules/detect-gpu": { 961 | "version": "5.0.21", 962 | "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.21.tgz", 963 | "integrity": "sha512-Dcf+4zxB3/4+7nVje3zQffJ3ksS7DiUztlNR0jC4AUF8FTBTeKWoIRxRAq4dGaRft8hEadUIDbjZbiKhCFo/Bw==", 964 | "dependencies": { 965 | "webgl-constants": "^1.1.1" 966 | } 967 | }, 968 | "node_modules/draco3d": { 969 | "version": "1.5.6", 970 | "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.6.tgz", 971 | "integrity": "sha512-+3NaRjWktb5r61ZFoDejlykPEFKT5N/LkbXsaddlw6xNSXBanUYpFc2AXXpbJDilPHazcSreU/DpQIaxfX0NfQ==" 972 | }, 973 | "node_modules/esbuild": { 974 | "version": "0.17.18", 975 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz", 976 | "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==", 977 | "dev": true, 978 | "hasInstallScript": true, 979 | "bin": { 980 | "esbuild": "bin/esbuild" 981 | }, 982 | "engines": { 983 | "node": ">=12" 984 | }, 985 | "optionalDependencies": { 986 | "@esbuild/android-arm": "0.17.18", 987 | "@esbuild/android-arm64": "0.17.18", 988 | "@esbuild/android-x64": "0.17.18", 989 | "@esbuild/darwin-arm64": "0.17.18", 990 | "@esbuild/darwin-x64": "0.17.18", 991 | "@esbuild/freebsd-arm64": "0.17.18", 992 | "@esbuild/freebsd-x64": "0.17.18", 993 | "@esbuild/linux-arm": "0.17.18", 994 | "@esbuild/linux-arm64": "0.17.18", 995 | "@esbuild/linux-ia32": "0.17.18", 996 | "@esbuild/linux-loong64": "0.17.18", 997 | "@esbuild/linux-mips64el": "0.17.18", 998 | "@esbuild/linux-ppc64": "0.17.18", 999 | "@esbuild/linux-riscv64": "0.17.18", 1000 | "@esbuild/linux-s390x": "0.17.18", 1001 | "@esbuild/linux-x64": "0.17.18", 1002 | "@esbuild/netbsd-x64": "0.17.18", 1003 | "@esbuild/openbsd-x64": "0.17.18", 1004 | "@esbuild/sunos-x64": "0.17.18", 1005 | "@esbuild/win32-arm64": "0.17.18", 1006 | "@esbuild/win32-ia32": "0.17.18", 1007 | "@esbuild/win32-x64": "0.17.18" 1008 | } 1009 | }, 1010 | "node_modules/fflate": { 1011 | "version": "0.6.10", 1012 | "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", 1013 | "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==" 1014 | }, 1015 | "node_modules/fsevents": { 1016 | "version": "2.3.2", 1017 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1018 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1019 | "dev": true, 1020 | "hasInstallScript": true, 1021 | "optional": true, 1022 | "os": [ 1023 | "darwin" 1024 | ], 1025 | "engines": { 1026 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1027 | } 1028 | }, 1029 | "node_modules/glsl-noise": { 1030 | "version": "0.0.0", 1031 | "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", 1032 | "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==" 1033 | }, 1034 | "node_modules/its-fine": { 1035 | "version": "1.1.1", 1036 | "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.1.1.tgz", 1037 | "integrity": "sha512-v1Ia1xl20KbuSGlwoaGsW0oxsw8Be+TrXweidxD9oT/1lAh6O3K3/GIM95Tt6WCiv6W+h2M7RB1TwdoAjQyyKw==", 1038 | "dependencies": { 1039 | "@types/react-reconciler": "^0.28.0" 1040 | }, 1041 | "peerDependencies": { 1042 | "react": ">=18.0" 1043 | } 1044 | }, 1045 | "node_modules/its-fine/node_modules/@types/react-reconciler": { 1046 | "version": "0.28.2", 1047 | "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.2.tgz", 1048 | "integrity": "sha512-8tu6lHzEgYPlfDf/J6GOQdIc+gs+S2yAqlby3zTsB3SP2svlqTYe5fwZNtZyfactP74ShooP2vvi1BOp9ZemWw==", 1049 | "dependencies": { 1050 | "@types/react": "*" 1051 | } 1052 | }, 1053 | "node_modules/js-tokens": { 1054 | "version": "4.0.0", 1055 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1056 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 1057 | }, 1058 | "node_modules/ktx-parse": { 1059 | "version": "0.4.5", 1060 | "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.4.5.tgz", 1061 | "integrity": "sha512-MK3FOody4TXbFf8Yqv7EBbySw7aPvEcPX++Ipt6Sox+/YMFvR5xaTyhfNSk1AEmMy+RYIw81ctN4IMxCB8OAlg==" 1062 | }, 1063 | "node_modules/lil-gui": { 1064 | "version": "0.17.0", 1065 | "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.17.0.tgz", 1066 | "integrity": "sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ==", 1067 | "peer": true 1068 | }, 1069 | "node_modules/lodash": { 1070 | "version": "4.17.21", 1071 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 1072 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 1073 | }, 1074 | "node_modules/lodash.clamp": { 1075 | "version": "4.0.3", 1076 | "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", 1077 | "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==" 1078 | }, 1079 | "node_modules/lodash.omit": { 1080 | "version": "4.5.0", 1081 | "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", 1082 | "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" 1083 | }, 1084 | "node_modules/lodash.pick": { 1085 | "version": "4.4.0", 1086 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", 1087 | "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" 1088 | }, 1089 | "node_modules/loose-envify": { 1090 | "version": "1.4.0", 1091 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 1092 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 1093 | "dependencies": { 1094 | "js-tokens": "^3.0.0 || ^4.0.0" 1095 | }, 1096 | "bin": { 1097 | "loose-envify": "cli.js" 1098 | } 1099 | }, 1100 | "node_modules/maath": { 1101 | "version": "0.5.3", 1102 | "resolved": "https://registry.npmjs.org/maath/-/maath-0.5.3.tgz", 1103 | "integrity": "sha512-ut63A4zTd9abtpi+sOHW1fPWPtAFrjK0E17eAthx1k93W/T2cWLKV5oaswyotJVDvvW1EXSdokAqhK5KOu0Qdw==", 1104 | "peerDependencies": { 1105 | "@types/three": ">=0.144.0", 1106 | "three": ">=0.144.0" 1107 | } 1108 | }, 1109 | "node_modules/meshline": { 1110 | "version": "3.1.6", 1111 | "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.1.6.tgz", 1112 | "integrity": "sha512-8JZJOdaL5oz3PI/upG8JvP/5FfzYUOhrkJ8np/WKvXzl0/PZ2V9pqTvCIjSKv+w9ccg2xb+yyBhXAwt6ier3ug==", 1113 | "peerDependencies": { 1114 | "three": ">=0.137" 1115 | } 1116 | }, 1117 | "node_modules/mmd-parser": { 1118 | "version": "1.0.4", 1119 | "resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.4.tgz", 1120 | "integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg==" 1121 | }, 1122 | "node_modules/nanoid": { 1123 | "version": "3.3.6", 1124 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", 1125 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", 1126 | "dev": true, 1127 | "funding": [ 1128 | { 1129 | "type": "github", 1130 | "url": "https://github.com/sponsors/ai" 1131 | } 1132 | ], 1133 | "bin": { 1134 | "nanoid": "bin/nanoid.cjs" 1135 | }, 1136 | "engines": { 1137 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1138 | } 1139 | }, 1140 | "node_modules/object-assign": { 1141 | "version": "4.1.1", 1142 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1143 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1144 | "engines": { 1145 | "node": ">=0.10.0" 1146 | } 1147 | }, 1148 | "node_modules/opentype.js": { 1149 | "version": "1.3.4", 1150 | "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz", 1151 | "integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==", 1152 | "dependencies": { 1153 | "string.prototype.codepointat": "^0.2.1", 1154 | "tiny-inflate": "^1.0.3" 1155 | }, 1156 | "bin": { 1157 | "ot": "bin/ot" 1158 | }, 1159 | "engines": { 1160 | "node": ">= 8.0.0" 1161 | } 1162 | }, 1163 | "node_modules/picocolors": { 1164 | "version": "1.0.0", 1165 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1166 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 1167 | "dev": true 1168 | }, 1169 | "node_modules/postcss": { 1170 | "version": "8.4.23", 1171 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", 1172 | "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", 1173 | "dev": true, 1174 | "funding": [ 1175 | { 1176 | "type": "opencollective", 1177 | "url": "https://opencollective.com/postcss/" 1178 | }, 1179 | { 1180 | "type": "tidelift", 1181 | "url": "https://tidelift.com/funding/github/npm/postcss" 1182 | }, 1183 | { 1184 | "type": "github", 1185 | "url": "https://github.com/sponsors/ai" 1186 | } 1187 | ], 1188 | "dependencies": { 1189 | "nanoid": "^3.3.6", 1190 | "picocolors": "^1.0.0", 1191 | "source-map-js": "^1.0.2" 1192 | }, 1193 | "engines": { 1194 | "node": "^10 || ^12 || >=14" 1195 | } 1196 | }, 1197 | "node_modules/potpack": { 1198 | "version": "1.0.2", 1199 | "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", 1200 | "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==" 1201 | }, 1202 | "node_modules/prop-types": { 1203 | "version": "15.8.1", 1204 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", 1205 | "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", 1206 | "dependencies": { 1207 | "loose-envify": "^1.4.0", 1208 | "object-assign": "^4.1.1", 1209 | "react-is": "^16.13.1" 1210 | } 1211 | }, 1212 | "node_modules/react": { 1213 | "version": "18.2.0", 1214 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 1215 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 1216 | "dependencies": { 1217 | "loose-envify": "^1.1.0" 1218 | }, 1219 | "engines": { 1220 | "node": ">=0.10.0" 1221 | } 1222 | }, 1223 | "node_modules/react-composer": { 1224 | "version": "5.0.3", 1225 | "resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz", 1226 | "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==", 1227 | "dependencies": { 1228 | "prop-types": "^15.6.0" 1229 | }, 1230 | "peerDependencies": { 1231 | "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" 1232 | } 1233 | }, 1234 | "node_modules/react-dom": { 1235 | "version": "18.2.0", 1236 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", 1237 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", 1238 | "dependencies": { 1239 | "loose-envify": "^1.1.0", 1240 | "scheduler": "^0.23.0" 1241 | }, 1242 | "peerDependencies": { 1243 | "react": "^18.2.0" 1244 | } 1245 | }, 1246 | "node_modules/react-is": { 1247 | "version": "16.13.1", 1248 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 1249 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" 1250 | }, 1251 | "node_modules/react-merge-refs": { 1252 | "version": "1.1.0", 1253 | "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz", 1254 | "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==", 1255 | "funding": { 1256 | "type": "github", 1257 | "url": "https://github.com/sponsors/gregberge" 1258 | } 1259 | }, 1260 | "node_modules/react-reconciler": { 1261 | "version": "0.27.0", 1262 | "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz", 1263 | "integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==", 1264 | "dependencies": { 1265 | "loose-envify": "^1.1.0", 1266 | "scheduler": "^0.21.0" 1267 | }, 1268 | "engines": { 1269 | "node": ">=0.10.0" 1270 | }, 1271 | "peerDependencies": { 1272 | "react": "^18.0.0" 1273 | } 1274 | }, 1275 | "node_modules/react-reconciler/node_modules/scheduler": { 1276 | "version": "0.21.0", 1277 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", 1278 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", 1279 | "dependencies": { 1280 | "loose-envify": "^1.1.0" 1281 | } 1282 | }, 1283 | "node_modules/react-use-measure": { 1284 | "version": "2.1.1", 1285 | "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", 1286 | "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==", 1287 | "dependencies": { 1288 | "debounce": "^1.2.1" 1289 | }, 1290 | "peerDependencies": { 1291 | "react": ">=16.13", 1292 | "react-dom": ">=16.13" 1293 | } 1294 | }, 1295 | "node_modules/regenerator-runtime": { 1296 | "version": "0.13.11", 1297 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", 1298 | "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" 1299 | }, 1300 | "node_modules/regexp-to-ast": { 1301 | "version": "0.5.0", 1302 | "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", 1303 | "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==" 1304 | }, 1305 | "node_modules/require-from-string": { 1306 | "version": "2.0.2", 1307 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 1308 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 1309 | "engines": { 1310 | "node": ">=0.10.0" 1311 | } 1312 | }, 1313 | "node_modules/rollup": { 1314 | "version": "3.21.0", 1315 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.0.tgz", 1316 | "integrity": "sha512-ANPhVcyeHvYdQMUyCbczy33nbLzI7RzrBje4uvNiTDJGIMtlKoOStmympwr9OtS1LZxiDmE2wvxHyVhoLtf1KQ==", 1317 | "dev": true, 1318 | "bin": { 1319 | "rollup": "dist/bin/rollup" 1320 | }, 1321 | "engines": { 1322 | "node": ">=14.18.0", 1323 | "npm": ">=8.0.0" 1324 | }, 1325 | "optionalDependencies": { 1326 | "fsevents": "~2.3.2" 1327 | } 1328 | }, 1329 | "node_modules/scheduler": { 1330 | "version": "0.23.0", 1331 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", 1332 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", 1333 | "dependencies": { 1334 | "loose-envify": "^1.1.0" 1335 | } 1336 | }, 1337 | "node_modules/source-map-js": { 1338 | "version": "1.0.2", 1339 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1340 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1341 | "dev": true, 1342 | "engines": { 1343 | "node": ">=0.10.0" 1344 | } 1345 | }, 1346 | "node_modules/stats.js": { 1347 | "version": "0.17.0", 1348 | "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", 1349 | "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==" 1350 | }, 1351 | "node_modules/string.prototype.codepointat": { 1352 | "version": "0.2.1", 1353 | "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", 1354 | "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==" 1355 | }, 1356 | "node_modules/suspend-react": { 1357 | "version": "0.0.8", 1358 | "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz", 1359 | "integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==", 1360 | "peerDependencies": { 1361 | "react": ">=17.0" 1362 | } 1363 | }, 1364 | "node_modules/three": { 1365 | "version": "0.152.2", 1366 | "resolved": "https://registry.npmjs.org/three/-/three-0.152.2.tgz", 1367 | "integrity": "sha512-Ff9zIpSfkkqcBcpdiFo2f35vA9ZucO+N8TNacJOqaEE6DrB0eufItVMib8bK8Pcju/ZNT6a7blE1GhTpkdsILw==" 1368 | }, 1369 | "node_modules/three-mesh-bvh": { 1370 | "version": "0.5.23", 1371 | "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.5.23.tgz", 1372 | "integrity": "sha512-nyk+MskdyDgECqkxdv57UjazqqhrMi+Al9PxJN6yFtx1CTW4r0eCQ27FtyYKY5gCIWhxjtNfWYDPVy8lzx6LkA==", 1373 | "peerDependencies": { 1374 | "three": ">= 0.123.0" 1375 | } 1376 | }, 1377 | "node_modules/three-stdlib": { 1378 | "version": "2.21.10", 1379 | "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.21.10.tgz", 1380 | "integrity": "sha512-YGkR3stmgmXSHL27+1A5dWAKKB+e63PLVkRWV/LLZ7f5+ExsqjLPm4YfBmJgkoSLgILYpQs6O7baY9gnIZlmNA==", 1381 | "dependencies": { 1382 | "@babel/runtime": "^7.16.7", 1383 | "@types/offscreencanvas": "^2019.6.4", 1384 | "@webgpu/glslang": "^0.0.15", 1385 | "chevrotain": "^10.1.2", 1386 | "draco3d": "^1.4.1", 1387 | "fflate": "^0.6.9", 1388 | "ktx-parse": "^0.4.5", 1389 | "mmd-parser": "^1.0.4", 1390 | "opentype.js": "^1.3.3", 1391 | "potpack": "^1.0.1", 1392 | "zstddec": "^0.0.2" 1393 | }, 1394 | "peerDependencies": { 1395 | "three": ">=0.122.0" 1396 | } 1397 | }, 1398 | "node_modules/tiny-inflate": { 1399 | "version": "1.0.3", 1400 | "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", 1401 | "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" 1402 | }, 1403 | "node_modules/troika-three-text": { 1404 | "version": "0.47.1", 1405 | "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.47.1.tgz", 1406 | "integrity": "sha512-/fPRUmxCkXxyUT8k6REC/aWeFzKbNr37ivrkrplSJNb3JcBUXvVt8MT0Ac5wTUvFsYTviYWprYS4/8Laen08WA==", 1407 | "dependencies": { 1408 | "bidi-js": "^1.0.2", 1409 | "troika-three-utils": "^0.47.0", 1410 | "troika-worker-utils": "^0.47.0", 1411 | "webgl-sdf-generator": "1.1.1" 1412 | }, 1413 | "peerDependencies": { 1414 | "three": ">=0.125.0" 1415 | } 1416 | }, 1417 | "node_modules/troika-three-utils": { 1418 | "version": "0.47.0", 1419 | "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.47.0.tgz", 1420 | "integrity": "sha512-yoVTQxVbpQX3a55giIwqwq6hyJA6oYvq7kaNGwFTeicoWmTZCqqTbytafx1gcuL5umrtw5MYgsxYUSOha+xp5w==", 1421 | "peerDependencies": { 1422 | "three": ">=0.125.0" 1423 | } 1424 | }, 1425 | "node_modules/troika-worker-utils": { 1426 | "version": "0.47.0", 1427 | "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.47.0.tgz", 1428 | "integrity": "sha512-PSUc9vunDEkbE23jpgXD3PcF96jQHKjgMjS+4o5g6DEK/ZAPTnldb+FNddhppawfUcuraMFrslo0GmIC8UpEmA==" 1429 | }, 1430 | "node_modules/typescript": { 1431 | "version": "4.9.5", 1432 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", 1433 | "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", 1434 | "dev": true, 1435 | "bin": { 1436 | "tsc": "bin/tsc", 1437 | "tsserver": "bin/tsserver" 1438 | }, 1439 | "engines": { 1440 | "node": ">=4.2.0" 1441 | } 1442 | }, 1443 | "node_modules/use-sync-external-store": { 1444 | "version": "1.2.0", 1445 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", 1446 | "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", 1447 | "peerDependencies": { 1448 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0" 1449 | } 1450 | }, 1451 | "node_modules/utility-types": { 1452 | "version": "3.10.0", 1453 | "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", 1454 | "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", 1455 | "engines": { 1456 | "node": ">= 4" 1457 | } 1458 | }, 1459 | "node_modules/vite": { 1460 | "version": "4.3.3", 1461 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz", 1462 | "integrity": "sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==", 1463 | "dev": true, 1464 | "dependencies": { 1465 | "esbuild": "^0.17.5", 1466 | "postcss": "^8.4.23", 1467 | "rollup": "^3.21.0" 1468 | }, 1469 | "bin": { 1470 | "vite": "bin/vite.js" 1471 | }, 1472 | "engines": { 1473 | "node": "^14.18.0 || >=16.0.0" 1474 | }, 1475 | "optionalDependencies": { 1476 | "fsevents": "~2.3.2" 1477 | }, 1478 | "peerDependencies": { 1479 | "@types/node": ">= 14", 1480 | "less": "*", 1481 | "sass": "*", 1482 | "stylus": "*", 1483 | "sugarss": "*", 1484 | "terser": "^5.4.0" 1485 | }, 1486 | "peerDependenciesMeta": { 1487 | "@types/node": { 1488 | "optional": true 1489 | }, 1490 | "less": { 1491 | "optional": true 1492 | }, 1493 | "sass": { 1494 | "optional": true 1495 | }, 1496 | "stylus": { 1497 | "optional": true 1498 | }, 1499 | "sugarss": { 1500 | "optional": true 1501 | }, 1502 | "terser": { 1503 | "optional": true 1504 | } 1505 | } 1506 | }, 1507 | "node_modules/webgl-constants": { 1508 | "version": "1.1.1", 1509 | "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", 1510 | "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" 1511 | }, 1512 | "node_modules/webgl-sdf-generator": { 1513 | "version": "1.1.1", 1514 | "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", 1515 | "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==" 1516 | }, 1517 | "node_modules/zstddec": { 1518 | "version": "0.0.2", 1519 | "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz", 1520 | "integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==" 1521 | }, 1522 | "node_modules/zustand": { 1523 | "version": "4.3.7", 1524 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.7.tgz", 1525 | "integrity": "sha512-dY8ERwB9Nd21ellgkBZFhudER8KVlelZm8388B5nDAXhO/+FZDhYMuRnqDgu5SYyRgz/iaf8RKnbUs/cHfOGlQ==", 1526 | "dependencies": { 1527 | "use-sync-external-store": "1.2.0" 1528 | }, 1529 | "engines": { 1530 | "node": ">=12.7.0" 1531 | }, 1532 | "peerDependencies": { 1533 | "immer": ">=9.0", 1534 | "react": ">=16.8" 1535 | }, 1536 | "peerDependenciesMeta": { 1537 | "immer": { 1538 | "optional": true 1539 | }, 1540 | "react": { 1541 | "optional": true 1542 | } 1543 | } 1544 | } 1545 | }, 1546 | "dependencies": { 1547 | "@babel/runtime": { 1548 | "version": "7.21.5", 1549 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", 1550 | "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", 1551 | "requires": { 1552 | "regenerator-runtime": "^0.13.11" 1553 | } 1554 | }, 1555 | "@chevrotain/cst-dts-gen": { 1556 | "version": "10.5.0", 1557 | "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", 1558 | "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", 1559 | "requires": { 1560 | "@chevrotain/gast": "10.5.0", 1561 | "@chevrotain/types": "10.5.0", 1562 | "lodash": "4.17.21" 1563 | } 1564 | }, 1565 | "@chevrotain/gast": { 1566 | "version": "10.5.0", 1567 | "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", 1568 | "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", 1569 | "requires": { 1570 | "@chevrotain/types": "10.5.0", 1571 | "lodash": "4.17.21" 1572 | } 1573 | }, 1574 | "@chevrotain/types": { 1575 | "version": "10.5.0", 1576 | "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", 1577 | "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==" 1578 | }, 1579 | "@chevrotain/utils": { 1580 | "version": "10.5.0", 1581 | "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", 1582 | "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==" 1583 | }, 1584 | "@esbuild/android-arm": { 1585 | "version": "0.17.18", 1586 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", 1587 | "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==", 1588 | "dev": true, 1589 | "optional": true 1590 | }, 1591 | "@esbuild/android-arm64": { 1592 | "version": "0.17.18", 1593 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz", 1594 | "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==", 1595 | "dev": true, 1596 | "optional": true 1597 | }, 1598 | "@esbuild/android-x64": { 1599 | "version": "0.17.18", 1600 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz", 1601 | "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==", 1602 | "dev": true, 1603 | "optional": true 1604 | }, 1605 | "@esbuild/darwin-arm64": { 1606 | "version": "0.17.18", 1607 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz", 1608 | "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==", 1609 | "dev": true, 1610 | "optional": true 1611 | }, 1612 | "@esbuild/darwin-x64": { 1613 | "version": "0.17.18", 1614 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz", 1615 | "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==", 1616 | "dev": true, 1617 | "optional": true 1618 | }, 1619 | "@esbuild/freebsd-arm64": { 1620 | "version": "0.17.18", 1621 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz", 1622 | "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==", 1623 | "dev": true, 1624 | "optional": true 1625 | }, 1626 | "@esbuild/freebsd-x64": { 1627 | "version": "0.17.18", 1628 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz", 1629 | "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==", 1630 | "dev": true, 1631 | "optional": true 1632 | }, 1633 | "@esbuild/linux-arm": { 1634 | "version": "0.17.18", 1635 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz", 1636 | "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==", 1637 | "dev": true, 1638 | "optional": true 1639 | }, 1640 | "@esbuild/linux-arm64": { 1641 | "version": "0.17.18", 1642 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz", 1643 | "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==", 1644 | "dev": true, 1645 | "optional": true 1646 | }, 1647 | "@esbuild/linux-ia32": { 1648 | "version": "0.17.18", 1649 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz", 1650 | "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==", 1651 | "dev": true, 1652 | "optional": true 1653 | }, 1654 | "@esbuild/linux-loong64": { 1655 | "version": "0.17.18", 1656 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz", 1657 | "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==", 1658 | "dev": true, 1659 | "optional": true 1660 | }, 1661 | "@esbuild/linux-mips64el": { 1662 | "version": "0.17.18", 1663 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz", 1664 | "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==", 1665 | "dev": true, 1666 | "optional": true 1667 | }, 1668 | "@esbuild/linux-ppc64": { 1669 | "version": "0.17.18", 1670 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz", 1671 | "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==", 1672 | "dev": true, 1673 | "optional": true 1674 | }, 1675 | "@esbuild/linux-riscv64": { 1676 | "version": "0.17.18", 1677 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz", 1678 | "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==", 1679 | "dev": true, 1680 | "optional": true 1681 | }, 1682 | "@esbuild/linux-s390x": { 1683 | "version": "0.17.18", 1684 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz", 1685 | "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==", 1686 | "dev": true, 1687 | "optional": true 1688 | }, 1689 | "@esbuild/linux-x64": { 1690 | "version": "0.17.18", 1691 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz", 1692 | "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==", 1693 | "dev": true, 1694 | "optional": true 1695 | }, 1696 | "@esbuild/netbsd-x64": { 1697 | "version": "0.17.18", 1698 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz", 1699 | "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==", 1700 | "dev": true, 1701 | "optional": true 1702 | }, 1703 | "@esbuild/openbsd-x64": { 1704 | "version": "0.17.18", 1705 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz", 1706 | "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==", 1707 | "dev": true, 1708 | "optional": true 1709 | }, 1710 | "@esbuild/sunos-x64": { 1711 | "version": "0.17.18", 1712 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz", 1713 | "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==", 1714 | "dev": true, 1715 | "optional": true 1716 | }, 1717 | "@esbuild/win32-arm64": { 1718 | "version": "0.17.18", 1719 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz", 1720 | "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==", 1721 | "dev": true, 1722 | "optional": true 1723 | }, 1724 | "@esbuild/win32-ia32": { 1725 | "version": "0.17.18", 1726 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz", 1727 | "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==", 1728 | "dev": true, 1729 | "optional": true 1730 | }, 1731 | "@esbuild/win32-x64": { 1732 | "version": "0.17.18", 1733 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz", 1734 | "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==", 1735 | "dev": true, 1736 | "optional": true 1737 | }, 1738 | "@react-spring/animated": { 1739 | "version": "9.6.1", 1740 | "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz", 1741 | "integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==", 1742 | "requires": { 1743 | "@react-spring/shared": "~9.6.1", 1744 | "@react-spring/types": "~9.6.1" 1745 | } 1746 | }, 1747 | "@react-spring/core": { 1748 | "version": "9.6.1", 1749 | "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz", 1750 | "integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==", 1751 | "requires": { 1752 | "@react-spring/animated": "~9.6.1", 1753 | "@react-spring/rafz": "~9.6.1", 1754 | "@react-spring/shared": "~9.6.1", 1755 | "@react-spring/types": "~9.6.1" 1756 | } 1757 | }, 1758 | "@react-spring/rafz": { 1759 | "version": "9.6.1", 1760 | "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz", 1761 | "integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==" 1762 | }, 1763 | "@react-spring/shared": { 1764 | "version": "9.6.1", 1765 | "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz", 1766 | "integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==", 1767 | "requires": { 1768 | "@react-spring/rafz": "~9.6.1", 1769 | "@react-spring/types": "~9.6.1" 1770 | } 1771 | }, 1772 | "@react-spring/three": { 1773 | "version": "9.6.1", 1774 | "resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.6.1.tgz", 1775 | "integrity": "sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==", 1776 | "requires": { 1777 | "@react-spring/animated": "~9.6.1", 1778 | "@react-spring/core": "~9.6.1", 1779 | "@react-spring/shared": "~9.6.1", 1780 | "@react-spring/types": "~9.6.1" 1781 | } 1782 | }, 1783 | "@react-spring/types": { 1784 | "version": "9.6.1", 1785 | "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz", 1786 | "integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==" 1787 | }, 1788 | "@react-three/drei": { 1789 | "version": "9.66.1", 1790 | "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-9.66.1.tgz", 1791 | "integrity": "sha512-nbSMsdoL52bLZtOZFMjkjjGzLLkx8hOuYEgvxX19TxR9+YpFnjbfzC3NtsjK719O4OJdNEHZOxmmxoANJJAXEA==", 1792 | "requires": { 1793 | "@babel/runtime": "^7.11.2", 1794 | "@react-spring/three": "~9.6.1", 1795 | "@use-gesture/react": "^10.2.24", 1796 | "camera-controls": "^2.3.1", 1797 | "detect-gpu": "^5.0.14", 1798 | "glsl-noise": "^0.0.0", 1799 | "lodash.clamp": "^4.0.3", 1800 | "lodash.omit": "^4.5.0", 1801 | "lodash.pick": "^4.4.0", 1802 | "maath": "^0.5.2", 1803 | "meshline": "^3.1.6", 1804 | "react-composer": "^5.0.3", 1805 | "react-merge-refs": "^1.1.0", 1806 | "stats.js": "^0.17.0", 1807 | "suspend-react": "^0.0.8", 1808 | "three-mesh-bvh": "^0.5.23", 1809 | "three-stdlib": "^2.21.8", 1810 | "troika-three-text": "^0.47.1", 1811 | "utility-types": "^3.10.0", 1812 | "zustand": "^3.5.13" 1813 | }, 1814 | "dependencies": { 1815 | "zustand": { 1816 | "version": "3.7.2", 1817 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", 1818 | "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", 1819 | "requires": {} 1820 | } 1821 | } 1822 | }, 1823 | "@react-three/fiber": { 1824 | "version": "8.13.0", 1825 | "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.13.0.tgz", 1826 | "integrity": "sha512-hPFzFNgikEMyEbL+NpSA7q+UWZxInrrkJldWaCR2w34Fwf20x9p68bsyN0/yn9oM2VlWoJcJjR8hw1tN9AxHuA==", 1827 | "requires": { 1828 | "@babel/runtime": "^7.17.8", 1829 | "@types/react-reconciler": "^0.26.7", 1830 | "its-fine": "^1.0.6", 1831 | "react-reconciler": "^0.27.0", 1832 | "react-use-measure": "^2.1.1", 1833 | "scheduler": "^0.21.0", 1834 | "suspend-react": "^0.0.8", 1835 | "zustand": "^3.7.1" 1836 | }, 1837 | "dependencies": { 1838 | "scheduler": { 1839 | "version": "0.21.0", 1840 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", 1841 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", 1842 | "requires": { 1843 | "loose-envify": "^1.1.0" 1844 | } 1845 | }, 1846 | "zustand": { 1847 | "version": "3.7.2", 1848 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", 1849 | "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", 1850 | "requires": {} 1851 | } 1852 | } 1853 | }, 1854 | "@swc/core": { 1855 | "version": "1.3.55", 1856 | "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.55.tgz", 1857 | "integrity": "sha512-w/lN3OuJsuy868yJZKop+voZLVzI5pVSoopQVtgDNkEzejnPuRp9XaeAValvuMaWqKoTMtOjLzEPyv/xiAGYQQ==", 1858 | "dev": true, 1859 | "requires": { 1860 | "@swc/core-darwin-arm64": "1.3.55", 1861 | "@swc/core-darwin-x64": "1.3.55", 1862 | "@swc/core-linux-arm-gnueabihf": "1.3.55", 1863 | "@swc/core-linux-arm64-gnu": "1.3.55", 1864 | "@swc/core-linux-arm64-musl": "1.3.55", 1865 | "@swc/core-linux-x64-gnu": "1.3.55", 1866 | "@swc/core-linux-x64-musl": "1.3.55", 1867 | "@swc/core-win32-arm64-msvc": "1.3.55", 1868 | "@swc/core-win32-ia32-msvc": "1.3.55", 1869 | "@swc/core-win32-x64-msvc": "1.3.55" 1870 | } 1871 | }, 1872 | "@swc/core-darwin-arm64": { 1873 | "version": "1.3.55", 1874 | "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.55.tgz", 1875 | "integrity": "sha512-UnHC8aPg/JvHhgXxTU6EhTtfnYNS7nhq8EKB8laNPxlHbwEyMBVQ2QuJHlNCtFtvSfX/uH5l04Ld1iGXnBTfdQ==", 1876 | "dev": true, 1877 | "optional": true 1878 | }, 1879 | "@swc/core-darwin-x64": { 1880 | "version": "1.3.55", 1881 | "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.55.tgz", 1882 | "integrity": "sha512-VNJkFVARrktIqtaLrD1NFA54gqekH7eAUcUY2U2SdHwO67HYjfMXMxlugLP5PDasSKpTkrVooUdhkffoA5W50g==", 1883 | "dev": true, 1884 | "optional": true 1885 | }, 1886 | "@swc/core-linux-arm-gnueabihf": { 1887 | "version": "1.3.55", 1888 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.55.tgz", 1889 | "integrity": "sha512-6OcohhIFKKNW/TpJt26Tpul8zyL7dmp1Lnyj2BX9ycsZZ5UnsNiGqn37mrqJgVTx/ansEmbyOmKu2mzm/Ct6cQ==", 1890 | "dev": true, 1891 | "optional": true 1892 | }, 1893 | "@swc/core-linux-arm64-gnu": { 1894 | "version": "1.3.55", 1895 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.55.tgz", 1896 | "integrity": "sha512-MfZtXGBv21XWwvrSMP0CMxScDolT/iv5PRl9UBprYUehwWr7BNjA3V9W7QQ+kKoPyORWk7LX7OpJZF3FnO618Q==", 1897 | "dev": true, 1898 | "optional": true 1899 | }, 1900 | "@swc/core-linux-arm64-musl": { 1901 | "version": "1.3.55", 1902 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.55.tgz", 1903 | "integrity": "sha512-iZJo+7L5lv10W0f0C6SlyteAyMJt5Tp+aH3+nlAwKdtc+VjyL1sGhR8DJMXp2/buBRZJ9tjEtpXKDaWUdSdF7Q==", 1904 | "dev": true, 1905 | "optional": true 1906 | }, 1907 | "@swc/core-linux-x64-gnu": { 1908 | "version": "1.3.55", 1909 | "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.55.tgz", 1910 | "integrity": "sha512-Rmc8ny/mslzzz0+wNK9/mLdyAWVbMZHRSvljhpzASmq48NBkmZ5vk9/WID6MnUz2e9cQ0JxJQs8t39KlFJtW3g==", 1911 | "dev": true, 1912 | "optional": true 1913 | }, 1914 | "@swc/core-linux-x64-musl": { 1915 | "version": "1.3.55", 1916 | "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.55.tgz", 1917 | "integrity": "sha512-Ymoc4xxINzS93ZjVd2UZfLZk1jF6wHjdCbC1JF+0zK3IrNrxCIDoWoaAj0+Bbvyo3hD1Xg/cneSTsqX8amnnuQ==", 1918 | "dev": true, 1919 | "optional": true 1920 | }, 1921 | "@swc/core-win32-arm64-msvc": { 1922 | "version": "1.3.55", 1923 | "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.55.tgz", 1924 | "integrity": "sha512-OhnmFstq2qRU2GI5I0G/8L+vc2rx8+w+IOA6EZBrY4FuMCbPIZKKzlnAIxYn2W+yD4gvBzYP3tgEcaDfQk6EkA==", 1925 | "dev": true, 1926 | "optional": true 1927 | }, 1928 | "@swc/core-win32-ia32-msvc": { 1929 | "version": "1.3.55", 1930 | "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.55.tgz", 1931 | "integrity": "sha512-3VR5rHZ6uoL/Vo3djV30GgX2oyDwWWsk+Yp+nyvYyBaKYiH2zeHfxdYRLSQV3W7kSlCAH3oDYpSljrWZ0t5XEQ==", 1932 | "dev": true, 1933 | "optional": true 1934 | }, 1935 | "@swc/core-win32-x64-msvc": { 1936 | "version": "1.3.55", 1937 | "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.55.tgz", 1938 | "integrity": "sha512-KBtMFtRwnbxBugYf6i2ePqEGdxsk715KcqGMjGhxNg7BTACnXnhj37irHu2e7A7wZffbkUVUYuj/JEgVkEjSxg==", 1939 | "dev": true, 1940 | "optional": true 1941 | }, 1942 | "@types/offscreencanvas": { 1943 | "version": "2019.7.0", 1944 | "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz", 1945 | "integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg==" 1946 | }, 1947 | "@types/prop-types": { 1948 | "version": "15.7.5", 1949 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", 1950 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" 1951 | }, 1952 | "@types/react": { 1953 | "version": "18.2.0", 1954 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz", 1955 | "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==", 1956 | "requires": { 1957 | "@types/prop-types": "*", 1958 | "@types/scheduler": "*", 1959 | "csstype": "^3.0.2" 1960 | } 1961 | }, 1962 | "@types/react-dom": { 1963 | "version": "18.2.1", 1964 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.1.tgz", 1965 | "integrity": "sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==", 1966 | "dev": true, 1967 | "requires": { 1968 | "@types/react": "*" 1969 | } 1970 | }, 1971 | "@types/react-reconciler": { 1972 | "version": "0.26.7", 1973 | "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz", 1974 | "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==", 1975 | "requires": { 1976 | "@types/react": "*" 1977 | } 1978 | }, 1979 | "@types/scheduler": { 1980 | "version": "0.16.3", 1981 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", 1982 | "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" 1983 | }, 1984 | "@types/stats.js": { 1985 | "version": "0.17.0", 1986 | "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.0.tgz", 1987 | "integrity": "sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w==", 1988 | "peer": true 1989 | }, 1990 | "@types/three": { 1991 | "version": "0.151.0", 1992 | "resolved": "https://registry.npmjs.org/@types/three/-/three-0.151.0.tgz", 1993 | "integrity": "sha512-hwOyN4szaikHsNzqbYLBfGvXi+TA/1TIKvK3ujJi+cQXzYzE5ZymzlDnOA6/cTSozY1juPFE3u5agToVQKb9TQ==", 1994 | "peer": true, 1995 | "requires": { 1996 | "@types/stats.js": "*", 1997 | "@types/webxr": "*", 1998 | "fflate": "~0.6.9", 1999 | "lil-gui": "~0.17.0" 2000 | } 2001 | }, 2002 | "@types/webxr": { 2003 | "version": "0.5.1", 2004 | "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.1.tgz", 2005 | "integrity": "sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ==", 2006 | "peer": true 2007 | }, 2008 | "@use-gesture/core": { 2009 | "version": "10.2.26", 2010 | "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.2.26.tgz", 2011 | "integrity": "sha512-NyFpQ3iID9iFBROXyyvU1D0NK+t+dP+WAVByhCvqHUenpxLD2NlRLVRpoK3XGGwksr6mU3PvZ2Nm4q0q+gLJPA==" 2012 | }, 2013 | "@use-gesture/react": { 2014 | "version": "10.2.26", 2015 | "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.2.26.tgz", 2016 | "integrity": "sha512-0QhaE5mhaQbFlip4MX7n1nwCX8gax6Da1LsP2fZ/BU6xW9zyEmV6NX7DPelDxq1rr2NiBJh30vx9RIp80YeA/A==", 2017 | "requires": { 2018 | "@use-gesture/core": "10.2.26" 2019 | } 2020 | }, 2021 | "@vitejs/plugin-react-swc": { 2022 | "version": "3.3.0", 2023 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.0.tgz", 2024 | "integrity": "sha512-Ycg+n2eyCOTpn/wRy+evVo859+hw7qCj9iaX5CMny6x1fx1Uoq0xBG+a98lFtwLNGfGEnpI0F26YigRuxCRkwg==", 2025 | "dev": true, 2026 | "requires": { 2027 | "@swc/core": "^1.3.42" 2028 | } 2029 | }, 2030 | "@webgpu/glslang": { 2031 | "version": "0.0.15", 2032 | "resolved": "https://registry.npmjs.org/@webgpu/glslang/-/glslang-0.0.15.tgz", 2033 | "integrity": "sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q==" 2034 | }, 2035 | "bidi-js": { 2036 | "version": "1.0.2", 2037 | "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.2.tgz", 2038 | "integrity": "sha512-rzSy/k7WdX5zOyeHHCOixGXbCHkyogkxPKL2r8QtzHmVQDiWCXUWa18bLdMWT9CYMLOYTjWpTHawuev2ouYJVw==", 2039 | "requires": { 2040 | "require-from-string": "^2.0.2" 2041 | } 2042 | }, 2043 | "camera-controls": { 2044 | "version": "2.3.4", 2045 | "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.3.4.tgz", 2046 | "integrity": "sha512-swhc87YVHf9te0glBI7Oa/QBgsSCL4ZxtoR4V3vE6l7mEebsYRNL8y7Y2m2E6MrT0UTphM1S7mQqs0Sp7QTZ2g==", 2047 | "requires": {} 2048 | }, 2049 | "chevrotain": { 2050 | "version": "10.5.0", 2051 | "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", 2052 | "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", 2053 | "requires": { 2054 | "@chevrotain/cst-dts-gen": "10.5.0", 2055 | "@chevrotain/gast": "10.5.0", 2056 | "@chevrotain/types": "10.5.0", 2057 | "@chevrotain/utils": "10.5.0", 2058 | "lodash": "4.17.21", 2059 | "regexp-to-ast": "0.5.0" 2060 | } 2061 | }, 2062 | "control-kit": { 2063 | "version": "file:..", 2064 | "requires": { 2065 | "@react-three/drei": ">=9.53", 2066 | "@types/node": "^18.11.19", 2067 | "@types/react": "^18.2.0", 2068 | "@types/react-dom": "^18.2.0", 2069 | "@types/three": "^0.149.0", 2070 | "prettier-config-standard": "^5.0.0", 2071 | "three": ">=0.149", 2072 | "typescript": "^5.0.0" 2073 | } 2074 | }, 2075 | "csstype": { 2076 | "version": "3.1.2", 2077 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", 2078 | "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" 2079 | }, 2080 | "debounce": { 2081 | "version": "1.2.1", 2082 | "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", 2083 | "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" 2084 | }, 2085 | "detect-gpu": { 2086 | "version": "5.0.21", 2087 | "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.21.tgz", 2088 | "integrity": "sha512-Dcf+4zxB3/4+7nVje3zQffJ3ksS7DiUztlNR0jC4AUF8FTBTeKWoIRxRAq4dGaRft8hEadUIDbjZbiKhCFo/Bw==", 2089 | "requires": { 2090 | "webgl-constants": "^1.1.1" 2091 | } 2092 | }, 2093 | "draco3d": { 2094 | "version": "1.5.6", 2095 | "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.6.tgz", 2096 | "integrity": "sha512-+3NaRjWktb5r61ZFoDejlykPEFKT5N/LkbXsaddlw6xNSXBanUYpFc2AXXpbJDilPHazcSreU/DpQIaxfX0NfQ==" 2097 | }, 2098 | "esbuild": { 2099 | "version": "0.17.18", 2100 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz", 2101 | "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==", 2102 | "dev": true, 2103 | "requires": { 2104 | "@esbuild/android-arm": "0.17.18", 2105 | "@esbuild/android-arm64": "0.17.18", 2106 | "@esbuild/android-x64": "0.17.18", 2107 | "@esbuild/darwin-arm64": "0.17.18", 2108 | "@esbuild/darwin-x64": "0.17.18", 2109 | "@esbuild/freebsd-arm64": "0.17.18", 2110 | "@esbuild/freebsd-x64": "0.17.18", 2111 | "@esbuild/linux-arm": "0.17.18", 2112 | "@esbuild/linux-arm64": "0.17.18", 2113 | "@esbuild/linux-ia32": "0.17.18", 2114 | "@esbuild/linux-loong64": "0.17.18", 2115 | "@esbuild/linux-mips64el": "0.17.18", 2116 | "@esbuild/linux-ppc64": "0.17.18", 2117 | "@esbuild/linux-riscv64": "0.17.18", 2118 | "@esbuild/linux-s390x": "0.17.18", 2119 | "@esbuild/linux-x64": "0.17.18", 2120 | "@esbuild/netbsd-x64": "0.17.18", 2121 | "@esbuild/openbsd-x64": "0.17.18", 2122 | "@esbuild/sunos-x64": "0.17.18", 2123 | "@esbuild/win32-arm64": "0.17.18", 2124 | "@esbuild/win32-ia32": "0.17.18", 2125 | "@esbuild/win32-x64": "0.17.18" 2126 | } 2127 | }, 2128 | "fflate": { 2129 | "version": "0.6.10", 2130 | "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", 2131 | "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==" 2132 | }, 2133 | "fsevents": { 2134 | "version": "2.3.2", 2135 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 2136 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 2137 | "dev": true, 2138 | "optional": true 2139 | }, 2140 | "glsl-noise": { 2141 | "version": "0.0.0", 2142 | "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", 2143 | "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==" 2144 | }, 2145 | "its-fine": { 2146 | "version": "1.1.1", 2147 | "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.1.1.tgz", 2148 | "integrity": "sha512-v1Ia1xl20KbuSGlwoaGsW0oxsw8Be+TrXweidxD9oT/1lAh6O3K3/GIM95Tt6WCiv6W+h2M7RB1TwdoAjQyyKw==", 2149 | "requires": { 2150 | "@types/react-reconciler": "^0.28.0" 2151 | }, 2152 | "dependencies": { 2153 | "@types/react-reconciler": { 2154 | "version": "0.28.2", 2155 | "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.2.tgz", 2156 | "integrity": "sha512-8tu6lHzEgYPlfDf/J6GOQdIc+gs+S2yAqlby3zTsB3SP2svlqTYe5fwZNtZyfactP74ShooP2vvi1BOp9ZemWw==", 2157 | "requires": { 2158 | "@types/react": "*" 2159 | } 2160 | } 2161 | } 2162 | }, 2163 | "js-tokens": { 2164 | "version": "4.0.0", 2165 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 2166 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 2167 | }, 2168 | "ktx-parse": { 2169 | "version": "0.4.5", 2170 | "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.4.5.tgz", 2171 | "integrity": "sha512-MK3FOody4TXbFf8Yqv7EBbySw7aPvEcPX++Ipt6Sox+/YMFvR5xaTyhfNSk1AEmMy+RYIw81ctN4IMxCB8OAlg==" 2172 | }, 2173 | "lil-gui": { 2174 | "version": "0.17.0", 2175 | "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.17.0.tgz", 2176 | "integrity": "sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ==", 2177 | "peer": true 2178 | }, 2179 | "lodash": { 2180 | "version": "4.17.21", 2181 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 2182 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 2183 | }, 2184 | "lodash.clamp": { 2185 | "version": "4.0.3", 2186 | "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", 2187 | "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==" 2188 | }, 2189 | "lodash.omit": { 2190 | "version": "4.5.0", 2191 | "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", 2192 | "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" 2193 | }, 2194 | "lodash.pick": { 2195 | "version": "4.4.0", 2196 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", 2197 | "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" 2198 | }, 2199 | "loose-envify": { 2200 | "version": "1.4.0", 2201 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 2202 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 2203 | "requires": { 2204 | "js-tokens": "^3.0.0 || ^4.0.0" 2205 | } 2206 | }, 2207 | "maath": { 2208 | "version": "0.5.3", 2209 | "resolved": "https://registry.npmjs.org/maath/-/maath-0.5.3.tgz", 2210 | "integrity": "sha512-ut63A4zTd9abtpi+sOHW1fPWPtAFrjK0E17eAthx1k93W/T2cWLKV5oaswyotJVDvvW1EXSdokAqhK5KOu0Qdw==", 2211 | "requires": {} 2212 | }, 2213 | "meshline": { 2214 | "version": "3.1.6", 2215 | "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.1.6.tgz", 2216 | "integrity": "sha512-8JZJOdaL5oz3PI/upG8JvP/5FfzYUOhrkJ8np/WKvXzl0/PZ2V9pqTvCIjSKv+w9ccg2xb+yyBhXAwt6ier3ug==", 2217 | "requires": {} 2218 | }, 2219 | "mmd-parser": { 2220 | "version": "1.0.4", 2221 | "resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.4.tgz", 2222 | "integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg==" 2223 | }, 2224 | "nanoid": { 2225 | "version": "3.3.6", 2226 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", 2227 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", 2228 | "dev": true 2229 | }, 2230 | "object-assign": { 2231 | "version": "4.1.1", 2232 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 2233 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 2234 | }, 2235 | "opentype.js": { 2236 | "version": "1.3.4", 2237 | "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz", 2238 | "integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==", 2239 | "requires": { 2240 | "string.prototype.codepointat": "^0.2.1", 2241 | "tiny-inflate": "^1.0.3" 2242 | } 2243 | }, 2244 | "picocolors": { 2245 | "version": "1.0.0", 2246 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 2247 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 2248 | "dev": true 2249 | }, 2250 | "postcss": { 2251 | "version": "8.4.23", 2252 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", 2253 | "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", 2254 | "dev": true, 2255 | "requires": { 2256 | "nanoid": "^3.3.6", 2257 | "picocolors": "^1.0.0", 2258 | "source-map-js": "^1.0.2" 2259 | } 2260 | }, 2261 | "potpack": { 2262 | "version": "1.0.2", 2263 | "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", 2264 | "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==" 2265 | }, 2266 | "prop-types": { 2267 | "version": "15.8.1", 2268 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", 2269 | "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", 2270 | "requires": { 2271 | "loose-envify": "^1.4.0", 2272 | "object-assign": "^4.1.1", 2273 | "react-is": "^16.13.1" 2274 | } 2275 | }, 2276 | "react": { 2277 | "version": "18.2.0", 2278 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 2279 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 2280 | "requires": { 2281 | "loose-envify": "^1.1.0" 2282 | } 2283 | }, 2284 | "react-composer": { 2285 | "version": "5.0.3", 2286 | "resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz", 2287 | "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==", 2288 | "requires": { 2289 | "prop-types": "^15.6.0" 2290 | } 2291 | }, 2292 | "react-dom": { 2293 | "version": "18.2.0", 2294 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", 2295 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", 2296 | "requires": { 2297 | "loose-envify": "^1.1.0", 2298 | "scheduler": "^0.23.0" 2299 | } 2300 | }, 2301 | "react-is": { 2302 | "version": "16.13.1", 2303 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", 2304 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" 2305 | }, 2306 | "react-merge-refs": { 2307 | "version": "1.1.0", 2308 | "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz", 2309 | "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==" 2310 | }, 2311 | "react-reconciler": { 2312 | "version": "0.27.0", 2313 | "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz", 2314 | "integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==", 2315 | "requires": { 2316 | "loose-envify": "^1.1.0", 2317 | "scheduler": "^0.21.0" 2318 | }, 2319 | "dependencies": { 2320 | "scheduler": { 2321 | "version": "0.21.0", 2322 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", 2323 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==", 2324 | "requires": { 2325 | "loose-envify": "^1.1.0" 2326 | } 2327 | } 2328 | } 2329 | }, 2330 | "react-use-measure": { 2331 | "version": "2.1.1", 2332 | "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", 2333 | "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==", 2334 | "requires": { 2335 | "debounce": "^1.2.1" 2336 | } 2337 | }, 2338 | "regenerator-runtime": { 2339 | "version": "0.13.11", 2340 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", 2341 | "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" 2342 | }, 2343 | "regexp-to-ast": { 2344 | "version": "0.5.0", 2345 | "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", 2346 | "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==" 2347 | }, 2348 | "require-from-string": { 2349 | "version": "2.0.2", 2350 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 2351 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" 2352 | }, 2353 | "rollup": { 2354 | "version": "3.21.0", 2355 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.0.tgz", 2356 | "integrity": "sha512-ANPhVcyeHvYdQMUyCbczy33nbLzI7RzrBje4uvNiTDJGIMtlKoOStmympwr9OtS1LZxiDmE2wvxHyVhoLtf1KQ==", 2357 | "dev": true, 2358 | "requires": { 2359 | "fsevents": "~2.3.2" 2360 | } 2361 | }, 2362 | "scheduler": { 2363 | "version": "0.23.0", 2364 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", 2365 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", 2366 | "requires": { 2367 | "loose-envify": "^1.1.0" 2368 | } 2369 | }, 2370 | "source-map-js": { 2371 | "version": "1.0.2", 2372 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 2373 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 2374 | "dev": true 2375 | }, 2376 | "stats.js": { 2377 | "version": "0.17.0", 2378 | "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", 2379 | "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==" 2380 | }, 2381 | "string.prototype.codepointat": { 2382 | "version": "0.2.1", 2383 | "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", 2384 | "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==" 2385 | }, 2386 | "suspend-react": { 2387 | "version": "0.0.8", 2388 | "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz", 2389 | "integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==", 2390 | "requires": {} 2391 | }, 2392 | "three": { 2393 | "version": "0.152.2", 2394 | "resolved": "https://registry.npmjs.org/three/-/three-0.152.2.tgz", 2395 | "integrity": "sha512-Ff9zIpSfkkqcBcpdiFo2f35vA9ZucO+N8TNacJOqaEE6DrB0eufItVMib8bK8Pcju/ZNT6a7blE1GhTpkdsILw==" 2396 | }, 2397 | "three-mesh-bvh": { 2398 | "version": "0.5.23", 2399 | "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.5.23.tgz", 2400 | "integrity": "sha512-nyk+MskdyDgECqkxdv57UjazqqhrMi+Al9PxJN6yFtx1CTW4r0eCQ27FtyYKY5gCIWhxjtNfWYDPVy8lzx6LkA==", 2401 | "requires": {} 2402 | }, 2403 | "three-stdlib": { 2404 | "version": "2.21.10", 2405 | "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.21.10.tgz", 2406 | "integrity": "sha512-YGkR3stmgmXSHL27+1A5dWAKKB+e63PLVkRWV/LLZ7f5+ExsqjLPm4YfBmJgkoSLgILYpQs6O7baY9gnIZlmNA==", 2407 | "requires": { 2408 | "@babel/runtime": "^7.16.7", 2409 | "@types/offscreencanvas": "^2019.6.4", 2410 | "@webgpu/glslang": "^0.0.15", 2411 | "chevrotain": "^10.1.2", 2412 | "draco3d": "^1.4.1", 2413 | "fflate": "^0.6.9", 2414 | "ktx-parse": "^0.4.5", 2415 | "mmd-parser": "^1.0.4", 2416 | "opentype.js": "^1.3.3", 2417 | "potpack": "^1.0.1", 2418 | "zstddec": "^0.0.2" 2419 | } 2420 | }, 2421 | "tiny-inflate": { 2422 | "version": "1.0.3", 2423 | "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", 2424 | "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" 2425 | }, 2426 | "troika-three-text": { 2427 | "version": "0.47.1", 2428 | "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.47.1.tgz", 2429 | "integrity": "sha512-/fPRUmxCkXxyUT8k6REC/aWeFzKbNr37ivrkrplSJNb3JcBUXvVt8MT0Ac5wTUvFsYTviYWprYS4/8Laen08WA==", 2430 | "requires": { 2431 | "bidi-js": "^1.0.2", 2432 | "troika-three-utils": "^0.47.0", 2433 | "troika-worker-utils": "^0.47.0", 2434 | "webgl-sdf-generator": "1.1.1" 2435 | } 2436 | }, 2437 | "troika-three-utils": { 2438 | "version": "0.47.0", 2439 | "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.47.0.tgz", 2440 | "integrity": "sha512-yoVTQxVbpQX3a55giIwqwq6hyJA6oYvq7kaNGwFTeicoWmTZCqqTbytafx1gcuL5umrtw5MYgsxYUSOha+xp5w==", 2441 | "requires": {} 2442 | }, 2443 | "troika-worker-utils": { 2444 | "version": "0.47.0", 2445 | "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.47.0.tgz", 2446 | "integrity": "sha512-PSUc9vunDEkbE23jpgXD3PcF96jQHKjgMjS+4o5g6DEK/ZAPTnldb+FNddhppawfUcuraMFrslo0GmIC8UpEmA==" 2447 | }, 2448 | "typescript": { 2449 | "version": "4.9.5", 2450 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", 2451 | "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", 2452 | "dev": true 2453 | }, 2454 | "use-sync-external-store": { 2455 | "version": "1.2.0", 2456 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", 2457 | "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", 2458 | "requires": {} 2459 | }, 2460 | "utility-types": { 2461 | "version": "3.10.0", 2462 | "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", 2463 | "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==" 2464 | }, 2465 | "vite": { 2466 | "version": "4.3.3", 2467 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz", 2468 | "integrity": "sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==", 2469 | "dev": true, 2470 | "requires": { 2471 | "esbuild": "^0.17.5", 2472 | "fsevents": "~2.3.2", 2473 | "postcss": "^8.4.23", 2474 | "rollup": "^3.21.0" 2475 | } 2476 | }, 2477 | "webgl-constants": { 2478 | "version": "1.1.1", 2479 | "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", 2480 | "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" 2481 | }, 2482 | "webgl-sdf-generator": { 2483 | "version": "1.1.1", 2484 | "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", 2485 | "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==" 2486 | }, 2487 | "zstddec": { 2488 | "version": "0.0.2", 2489 | "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz", 2490 | "integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==" 2491 | }, 2492 | "zustand": { 2493 | "version": "4.3.7", 2494 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.7.tgz", 2495 | "integrity": "sha512-dY8ERwB9Nd21ellgkBZFhudER8KVlelZm8388B5nDAXhO/+FZDhYMuRnqDgu5SYyRgz/iaf8RKnbUs/cHfOGlQ==", 2496 | "requires": { 2497 | "use-sync-external-store": "1.2.0" 2498 | } 2499 | } 2500 | } 2501 | } 2502 | --------------------------------------------------------------------------------