diff --git a/.agent/AGENT.md b/.agent/AGENT.md new file mode 100644 index 0000000..0f732ba --- /dev/null +++ b/.agent/AGENT.md @@ -0,0 +1,55 @@ +# Agent - La Fabrik + +You are working on **La Fabrik**, an interactive 3D web experience built with React Three Fiber. + +## Read This First + +- `docs/technical/architecture.md` describes the code that exists today. +- `docs/technical/target-architecture.md` describes the intended target-state. +- Do not assume target-state systems already exist. + +## Current Implementation + +- Stack: React 19, Three.js, `@react-three/fiber`, `@react-three/drei`, `@react-three/rapier`, TypeScript, Vite +- No external global state library is used. +- Current singleton-style services are limited to: + - `InteractionManager` + - `AudioManager` + - `Debug` +- Current gameplay scope is still prototype-level: + - player movement + - trigger/grab interactions + - debug camera and scene switching + - simple audio playback + +## Current Architecture Rules + +- Scene objects live in `src/world/` and `src/components/3d/`. +- HTML overlays live in `src/components/ui/`. +- Shared static config lives in `src/data/`. +- Debug tooling lives in `src/utils/debug/` and `src/hooks/debug/`. +- Use the `@/` alias for imports from `src/`. +- Prefer small, direct changes over adding new abstraction layers. +- Shared types should live close to their domain and only move outward when they gain multiple real consumers. + +## Target-State Guidance + +The project may later grow toward a manager-driven gameplay architecture with clearer separation between: + +- production world code +- gameplay orchestration +- UI overlays +- debug tooling + +That target-state is aspirational until the matching code exists. If a target-state rule conflicts with the current implementation, treat the current code as the source of truth and improve it incrementally. + +## Do Not Assume + +- There is no `GameManager` in the current codebase. +- There are no implemented mission, zone, cinematic, or dialogue systems yet. +- Dependency versions are not pinned today; do not rewrite dependency strategy unless explicitly asked. +- The old `# route path ...` file header convention is not in use. + +## Skills + +Files in `.agent/skills/` are supplemental patterns and examples. Some describe target-state or generic practices rather than the exact current implementation, so verify against the code before applying them. diff --git a/.agent/AGENTS.md b/.agent/AGENTS.md deleted file mode 100644 index d6e9dc6..0000000 --- a/.agent/AGENTS.md +++ /dev/null @@ -1,90 +0,0 @@ -# Agent — La Fabrik - -You are working on **La Fabrik**, an interactive 3D web experience built with React Three Fiber. The player steps into the role of a technician in Altera (2050) and completes missions: repairing an e-bike, fixing a power grid, upgrading a vertical farm. - -## Project Identity - -- **Stack:** React 19, Three.js, @react-three/fiber 9, @react-three/drei, @react-three/rapier, GSAP, TypeScript, Vite -- **No external state lib.** State is managed by a custom `GameManager` singleton with a subscribe/getState pattern. -- **No Zustand, no Redux, no Context for global state.** -- **Versions are pinned** (no `^` in dependencies). Do not upgrade packages without explicit request. - -## Architecture Rules - -### Two patterns coexist - -1. **Singleton manager classes** — for orchestration, audio, cinematics, zone detection, debug -2. **Declarative React components** — for all 3D scene objects (map, zones, lights, player, postprocessing) - -Scene objects are **never** singleton classes. Managers are **never** React components. - -### State ownership - -- `GameManager` is the single source of truth for durable gameplay state (phase, zone, mission, input lock, dialogue) -- Other managers (`CinematicManager`, `AudioManager`, `ZoneManager`) handle side effects only — they read from GameManager but do not duplicate its state -- React components subscribe to GameManager through `useGameState()` hook -- **High-frequency values** (movement, camera interpolation, physics) stay in `useRef` + `useFrame` — never in React state - -### File conventions - -- Every file starts with a comment: `# route path ` (e.g. `# route path src/world/Map.tsx`) -- Scene components live in `src/world/` and `src/components/3d/` -- UI overlays live in `src/components/ui/` -- Managers live in `src/stateManager/` -- Debug tooling lives in `src/utils/debug/` -- Hooks live in `src/hooks/` -- Static data lives in `src/data/` -- Shaders live in `src/shaders/` -- Utilities live in `src/utils/` - -### Import paths - -Use `@/` alias for imports from `src/`: - -```ts -import { GameManager } from "@/stateManager/GameManager"; -import { useGameState } from "@/hooks/useGameState"; -``` - -### Memory management - -- Dispose only what you own (custom materials, render targets, manual clones) -- Never blindly deep-dispose shared/cached assets (drei loaders cache models) -- Use `Dispose.material()`, `Dispose.mesh()`, `Dispose.renderTarget()` from `src/utils/Dispose.ts` - -### Debug - -- Debug panel activates with `?debug` in URL -- All debug logic goes through `Debug.getInstance()` from `src/utils/debug/Debug.ts` -- Never scatter `if (isDev)` blocks across files -- `r3f-perf` is lazy-loaded only in debug mode via `src/utils/debug/DebugPerf.tsx` - -## Managers (4 max) - -| Manager | Responsibility | -| ------------------ | ------------------------------------------------------------------- | -| `GameManager` | Phase, zone, mission, input lock, dialogue — single source of truth | -| `CinematicManager` | GSAP timelines, camera lock/unlock | -| `AudioManager` | Music, SFX, spatial audio | -| `ZoneManager` | Zone detection, LOD triggers | - -## Do NOT - -- Create new manager classes without explicit request -- Use Zustand, Redux, or React Context for global state -- Put high-frequency values in React state (`useState`) -- Import `CinematicManager`/`AudioManager`/`ZoneManager` directly from components — always go through `GameManager` -- Upgrade pinned dependency versions -- Create files outside the documented architecture without explicit request - -## Skills - -See `.agent/skills/` for detailed patterns per technology: - -- `best-practices.md` — Code generation conventions (W3C, simple, scalable, modern) -- `r3f.md` — React Three Fiber component patterns -- `three.md` — Three.js conventions and AnimationMixer -- `gsap.md` — GSAP timeline and cinematic patterns -- `managers.md` — Singleton manager implementation -- `memory.md` — GPU memory and disposal rules -- `debug.md` — Debug utility and r3f-perf setup diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 44cec6b..265b858 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -75,6 +75,8 @@ jobs: steps: - name: ⬇️ Checkout uses: actions/checkout@v6 + with: + lfs: true - name: 🧰 Setup Node uses: actions/setup-node@v6 diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 66b47f7..c787ccb 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -57,6 +57,8 @@ jobs: steps: - name: ⬇️ Checkout uses: actions/checkout@v6 + with: + lfs: true - name: 🧰 Setup Node uses: actions/setup-node@v6 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..5b924de --- /dev/null +++ b/.prettierignore @@ -0,0 +1,19 @@ +dist +node_modules + +public/models +public/**/*.glb +public/**/*.gltf +public/**/*.png +public/**/*.jpg +public/**/*.jpeg +public/**/*.webp +public/**/*.hdr +public/**/*.exr +public/**/*.ktx +public/**/*.ktx2 +public/**/*.mp3 +public/**/*.wav +public/**/*.ogg +public/**/*.mp4 +public/**/*.webm diff --git a/README.md b/README.md index 40cee18..b7acd20 100644 --- a/README.md +++ b/README.md @@ -102,16 +102,19 @@ la-fabrik/ │ ├── EventEmitter.ts # Simple typed pub/sub utility │ ├── Sizes.ts # Viewport size tracking │ ├── Time.ts # Animation frame timing utility - │ ├── Readme.md │ └── debug/ # Dev-only tools and scene inspection │ ├── Debug.ts # Global lil-gui manager │ ├── DebugPerf.tsx # r3f-perf overlay mounted in Canvas - ├── hooks/ - │ └── debug/ - │ └── useCameraMode.ts + │ ├── isDebugEnabled.ts # Debug query-string helper │ └── scene/ │ ├── DebugHelpers.tsx # Grid + axes helpers shown in debug mode │ └── DebugCameraControls.tsx # Free debug camera for map inspection + ├── hooks/ + │ └── debug/ + │ ├── useCameraMode.ts + │ ├── useDebugFolder.ts + │ ├── useDebugStore.ts + │ └── useSceneMode.ts │ ├── App.tsx # Canvas bootstrap └── main.tsx @@ -126,8 +129,8 @@ npm install npm run dev ``` -- `http://localhost:5173` for the app -- `http://localhost:5173?debug` to enable debug tooling +- app: `http://localhost:5173` +- debug mode: `http://localhost:5173?debug` ## 📜 License diff --git a/docs/technical/architecture.md b/docs/technical/architecture.md index 97783d8..769575d 100644 --- a/docs/technical/architecture.md +++ b/docs/technical/architecture.md @@ -1,43 +1,47 @@ -# Implemented Architecture +# Current Architecture This document describes the code that exists today in the repository. ## Runtime Structure -- `src/App.tsx` mounts the `Canvas`, the 3D `World`, the debug perf overlay, and the HTML crosshair overlay. -- `src/world/World.tsx` composes the active 3D scene. -- `src/world/Map.tsx` loads and centers the blocking map model. -- `src/world/Lighting.tsx` owns the current ambient and directional light setup. -- `src/world/Environment.tsx` owns the current background color. -- `src/world/player/FPSController.tsx` provides the current player camera, pointer lock, and `ZQSD` movement. -- `src/utils/debug/` contains debug-only tooling such as `lil-gui`, scene helpers, and the free debug camera. -- `src/components/ui/Crosshair.tsx` is the only current HTML overlay component in use. +- `src/App.tsx` mounts the `Canvas`, the 3D `World`, the debug perf overlay, and the HTML overlays. +- `src/world/World.tsx` composes the active scene, including: + - environment and lighting + - debug helpers and debug camera mode + - either the map scene or the debug physics test scene + - the player rig when the active camera mode is `player` +- `src/world/Map.tsx` loads the main map model and builds the collision octree. +- `src/world/debug/TestScene.tsx` provides a debug-oriented interaction and physics scene. +- `src/world/player/PlayerComponent.tsx` mounts the camera and controller. +- `src/world/player/PlayerController.tsx` owns pointer lock movement, jump handling, and interaction input. -## Camera Modes +## Interaction Model -The application currently has two camera modes: +- `src/stateManager/InteractionManager.ts` is the current interaction state source. +- `src/components/3d/InteractableObject.tsx` handles focus detection through distance and raycasting. +- `src/components/3d/TriggerObject.tsx` implements trigger-style interactions. +- `src/components/3d/GrabbableObject.tsx` implements hold-and-release interactions. +- `src/hooks/useInteraction.ts` exposes the interaction snapshot to React UI. +- `src/components/ui/InteractPrompt.tsx` shows the `E` prompt for trigger interactions. -- `player` - - controlled by `FPSController` - - player height is `1.75m` - - movement uses `ZQSD` - - `E` is reserved for future interaction -- `debug` - - controlled by `DebugCameraControls` - - enabled from the debug panel +## Audio -The active mode is stored in the debug subsystem and consumed through `src/hooks/debug/useCameraMode.ts`. +- `src/stateManager/AudioManager.ts` currently provides pooled one-shot sound playback. +- Trigger interactions may play audio directly through `AudioManager`. ## Debug System -- `src/utils/debug/Debug.ts` is a singleton wrapper around `lil-gui` -- `src/utils/debug/DebugPerf.tsx` lazy-loads `r3f-perf` -- `src/utils/debug/scene/DebugHelpers.tsx` mounts grid and axes in debug mode -- `src/utils/debug/scene/DebugCameraControls.tsx` mounts the free camera in debug mode +- Debug mode is enabled with `?debug`. +- `src/utils/debug/Debug.ts` owns the `lil-gui` instance and debug controls. +- `src/hooks/debug/useCameraMode.ts` and `src/hooks/debug/useSceneMode.ts` subscribe to debug state. +- `src/utils/debug/DebugPerf.tsx` lazily mounts `r3f-perf` in debug mode. +- `src/utils/debug/scene/DebugHelpers.tsx` mounts debug helpers. +- `src/utils/debug/scene/DebugCameraControls.tsx` mounts the free debug camera. ## Current Limitations -- There is no gameplay state manager implemented yet. -- There are no zone systems, missions, dialogue systems, or cinematic systems implemented yet. -- Player movement currently uses a simple height clamp instead of real collision or ground detection. -- The map is currently a blocking preview scene, not a full playable world. +- The repository is still a prototype, not the full intended game runtime. +- `src/world/debug/TestScene.tsx` is still part of the active scene composition. +- There is no central gameplay orchestrator such as `GameManager` yet. +- Missions, zones, cinematics, and dialogue systems are not implemented. +- The player uses octree collision and simple movement rules, not a complete gameplay physics stack. diff --git a/docs/technical/target-architecture.md b/docs/technical/target-architecture.md index 38b1905..9ad9c0e 100644 --- a/docs/technical/target-architecture.md +++ b/docs/technical/target-architecture.md @@ -2,60 +2,69 @@ This document describes the intended medium-term architecture for the project. +## Relationship To The Current Code + +- `docs/technical/architecture.md` is the source of truth for what exists now. +- This document is intentionally aspirational. +- If this document conflicts with the current implementation, the current implementation wins. + ## Goals -- Keep `main` stable, `develop` as the integration branch, and `feat/*` for feature work. -- Keep the runtime split between scene composition, gameplay systems, debug tooling, and HTML UI. +- Keep `App.tsx` small and orchestration-oriented. +- Separate production world code from debug-only runtime paths. - Keep one clear source of truth per concern. +- Grow gameplay systems incrementally instead of prebuilding empty architecture. ## Intended Layers ### App Layer -- `App.tsx` should stay small and orchestration-oriented. -- It should mount the canvas scene and top-level HTML overlays. +- `App.tsx` mounts the canvas scene and top-level HTML overlays. +- It should stay thin and avoid gameplay logic. ### World Layer -- `src/world/` should contain only production scene objects and scene composition. +- `src/world/` should contain production scene composition and production scene objects. - Expected responsibilities: - world composition - - map/environment/lighting + - map, environment, lighting - player controller - - zones - - post-processing used in production + - production interaction anchors + - production post-processing, if needed ### Debug Layer -- `src/utils/debug/` should contain only developer tooling. +- Debug-only scenes and tooling should be isolated from the production world path. - Expected responsibilities: - `lil-gui` - performance overlay - scene helpers - free camera and calibration controls + - temporary test scenes used during development ### UI Layer -- `src/components/ui/` should contain HTML overlays used by the player. -- Expected examples: +- `src/components/ui/` should contain player-facing HTML overlays. +- Expected future examples: - crosshair - - loading screen + - loading flow - mission HUD - narrative overlays ### Gameplay Layer -- Gameplay state should eventually live in dedicated managers and thin hooks once those systems exist. -- Expected future concerns: +- As the project grows, gameplay state can move toward a clearer orchestration layer. +- Likely future concerns: - missions - zones - cinematics + - dialogue - audio - interactions ## Rules -- `world/` should not contain debug-only tooling. -- `debug/` should not own production gameplay systems. -- Shared types should live close to their domain and move outward only when they gain multiple real consumers. -- New files should only be created when they have an active runtime purpose. +- Prefer direct, working code over speculative scaffolding. +- Shared types should stay close to their domain until they have multiple real consumers. +- Avoid creating new managers or service layers without an active runtime need. +- Debug-only runtime paths should be clearly marked and easy to remove later. diff --git a/docs/user/features.md b/docs/user/features.md index 8334c37..133c126 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -1,44 +1,49 @@ # Implemented Features -This document lists features that are actually implemented in the current codebase. +This document lists features that are implemented in the current codebase. -## Scene Preview +## Scene - Fullscreen React Three Fiber scene -- Blocking map loaded from `public/models/map/blocking/model.glb` +- Main map scene loaded from `public/models/map/model.gltf` +- Debug physics test scene selectable from the debug panel - Ambient and directional lighting -- Solid background environment color +- Environment background setup -## Camera Modes +## Player - Player camera mode - - eye height at `1.75m` - - pointer lock mouse look - - movement with `ZQSD` - - vertical clamp to prevent falling below the map plane -- Debug camera mode - - free orbit camera - - switchable from the debug panel +- Pointer lock mouse look +- Movement with `ZQSD` +- Jumping +- Octree-based collision against the loaded map -## UI +## Interactions -- Center-screen crosshair shown only in player mode +- Focus detection by distance and raycast +- Trigger interactions activated with `E` +- Grab interactions activated with the primary mouse button +- Interaction prompt shown for trigger interactions + +## Audio + +- One-shot sound playback for trigger interactions +- Simple per-sound pooling through `AudioManager` ## Debug Tooling - `?debug` query param enables the debug panel -- `lil-gui` panel with camera mode selection -- debug lighting controls -- debug scene helpers +- `lil-gui` controls for camera mode, scene mode, and interaction spheres +- Debug scene helpers +- Free debug camera - `r3f-perf` overlay ## Not Implemented Yet -- missions -- interactions on `E` -- gameplay zones -- cinematics -- audio systems +- mission system +- zone system +- cinematic system +- dialogue system - loading flow - minimap and mission HUD -- collisions beyond the current simple player height clamp +- full production separation between gameplay and debug scenes diff --git a/package-lock.json b/package-lock.json index 996c858..d86843e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@react-three/postprocessing": "^3.0.4", "@react-three/rapier": "^2.2.0", "gsap": "^3.15.0", + "lil-gui": "^0.21.0", "r3f-perf": "^7.2.3", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -31,8 +32,6 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.4.0", - "lil-gui": "^0.21.0", - "madge": "^8.0.0", "prettier": "^3.8.2", "typescript": "~6.0.2", "typescript-eslint": "^8.58.0", @@ -288,20 +287,6 @@ "node": ">=6.9.0" } }, - "node_modules/@dependents/detective-less": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@dependents/detective-less/-/detective-less-5.0.1.tgz", - "integrity": "sha512-Y6+WUMsTFWE5jb20IFP4YGa5IrGY/+a/FbOSjDF/wz9gepU2hwCYSXRHP/vPwBvwcY3SVMASt4yXxbXNXigmZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "gonzales-pe": "^4.3.0", - "node-source-walk": "^7.0.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@dimforge/rapier3d-compat": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.19.2.tgz", @@ -309,9 +294,9 @@ "license": "Apache-2.0" }, "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", "optional": true, @@ -321,9 +306,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", "optional": true, @@ -500,29 +485,43 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -620,9 +619,9 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", - "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", "optional": true, @@ -639,9 +638,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.124.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", - "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", + "version": "0.127.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", + "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", "dev": true, "license": "MIT", "funding": { @@ -812,9 +811,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", "cpu": [ "arm64" ], @@ -829,9 +828,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", "cpu": [ "arm64" ], @@ -846,9 +845,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", "cpu": [ "x64" ], @@ -863,9 +862,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", "cpu": [ "x64" ], @@ -880,9 +879,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", - "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", + "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", "cpu": [ "arm" ], @@ -897,9 +896,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", "cpu": [ "arm64" ], @@ -914,9 +913,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", "cpu": [ "arm64" ], @@ -931,9 +930,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", "cpu": [ "ppc64" ], @@ -948,9 +947,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", "cpu": [ "s390x" ], @@ -965,9 +964,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", "cpu": [ "x64" ], @@ -982,9 +981,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", "cpu": [ "x64" ], @@ -999,9 +998,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", "cpu": [ "arm64" ], @@ -1016,9 +1015,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", - "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", + "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", "cpu": [ "wasm32" ], @@ -1026,18 +1025,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.9.2", - "@emnapi/runtime": "1.9.2", - "@napi-rs/wasm-runtime": "^1.1.3" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { - "node": ">=14.0.0" + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", "cpu": [ "arm64" ], @@ -1052,9 +1051,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", "cpu": [ "x64" ], @@ -1084,96 +1083,6 @@ "react": ">= 16.3.0" } }, - "node_modules/@ts-graphviz/adapter": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@ts-graphviz/adapter/-/adapter-2.0.6.tgz", - "integrity": "sha512-kJ10lIMSWMJkLkkCG5gt927SnGZcBuG0s0HHswGzcHTgvtUe7yk5/3zTEr0bafzsodsOq5Gi6FhQeV775nC35Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ts-graphviz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ts-graphviz" - } - ], - "license": "MIT", - "dependencies": { - "@ts-graphviz/common": "^2.1.5" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ts-graphviz/ast": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@ts-graphviz/ast/-/ast-2.0.7.tgz", - "integrity": "sha512-e6+2qtNV99UT6DJSoLbHfkzfyqY84aIuoV8Xlb9+hZAjgpum8iVHprGeAMQ4rF6sKUAxrmY8rfF/vgAwoPc3gw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ts-graphviz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ts-graphviz" - } - ], - "license": "MIT", - "dependencies": { - "@ts-graphviz/common": "^2.1.5" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ts-graphviz/common": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@ts-graphviz/common/-/common-2.1.5.tgz", - "integrity": "sha512-S6/9+T6x8j6cr/gNhp+U2olwo1n0jKj/682QVqsh7yXWV6ednHYqxFw0ZsY3LyzT0N8jaZ6jQY9YD99le3cmvg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ts-graphviz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ts-graphviz" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@ts-graphviz/core": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@ts-graphviz/core/-/core-2.0.7.tgz", - "integrity": "sha512-w071DSzP94YfN6XiWhOxnLpYT3uqtxJBDYdh6Jdjzt+Ce6DNspJsPQgpC7rbts/B8tEkq0LHoYuIF/O5Jh5rPg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ts-graphviz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ts-graphviz" - } - ], - "license": "MIT", - "dependencies": { - "@ts-graphviz/ast": "^2.0.7", - "@ts-graphviz/common": "^2.1.5" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@tweenjs/tween.js": { "version": "23.1.3", "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", @@ -1231,7 +1140,6 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -1263,18 +1171,17 @@ "license": "MIT" }, "node_modules/@types/three": { - "version": "0.183.1", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.183.1.tgz", - "integrity": "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==", + "version": "0.184.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.184.0.tgz", + "integrity": "sha512-4mY2tZAu0y0B0567w7013BBXSpsP0+Z48NJvmNo4Y/Pf76yCyz6Jw4P3tUVs10WuYNXXZ+wmHyGWpCek3amJxA==", "license": "MIT", "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": ">=0.5.17", - "@webgpu/types": "*", "fflate": "~0.8.2", - "meshoptimizer": "~1.0.1" + "meshoptimizer": "~1.1.1" } }, "node_modules/@types/three/node_modules/@dimforge/rapier3d-compat": { @@ -1290,17 +1197,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", - "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", + "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.58.2", - "@typescript-eslint/type-utils": "8.58.2", - "@typescript-eslint/utils": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/type-utils": "8.59.0", + "@typescript-eslint/utils": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -1313,7 +1220,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.58.2", + "@typescript-eslint/parser": "^8.59.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -1329,16 +1236,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", - "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", + "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.58.2", - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3" }, "engines": { @@ -1354,14 +1261,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", - "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", + "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.2", - "@typescript-eslint/types": "^8.58.2", + "@typescript-eslint/tsconfig-utils": "^8.59.0", + "@typescript-eslint/types": "^8.59.0", "debug": "^4.4.3" }, "engines": { @@ -1376,14 +1283,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", - "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", + "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2" + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1394,9 +1301,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", - "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", + "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", "dev": true, "license": "MIT", "engines": { @@ -1411,15 +1318,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", - "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", + "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2", - "@typescript-eslint/utils": "8.58.2", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/utils": "8.59.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1436,9 +1343,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", - "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", + "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", "dev": true, "license": "MIT", "engines": { @@ -1450,16 +1357,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", - "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", + "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.58.2", - "@typescript-eslint/tsconfig-utils": "8.58.2", - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2", + "@typescript-eslint/project-service": "8.59.0", + "@typescript-eslint/tsconfig-utils": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1530,16 +1437,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", - "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", + "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.2", - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2" + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1554,13 +1461,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", - "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", + "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/types": "8.59.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1645,73 +1552,6 @@ } } }, - "node_modules/@vue/compiler-core": { - "version": "3.5.32", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.32.tgz", - "integrity": "sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.2", - "@vue/shared": "3.5.32", - "entities": "^7.0.1", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.32", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.32.tgz", - "integrity": "sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.5.32", - "@vue/shared": "3.5.32" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.32", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.32.tgz", - "integrity": "sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.2", - "@vue/compiler-core": "3.5.32", - "@vue/compiler-dom": "3.5.32", - "@vue/compiler-ssr": "3.5.32", - "@vue/shared": "3.5.32", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.21", - "postcss": "^8.5.8", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.32", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.32.tgz", - "integrity": "sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.32", - "@vue/shared": "3.5.32" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.32", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.32.tgz", - "integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webgpu/types": { - "version": "0.1.69", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", - "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", - "license": "BSD-3-Clause" - }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -1736,9 +1576,9 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1752,16 +1592,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1778,20 +1608,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/app-module-path": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", - "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1799,16 +1615,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/ast-module-types": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-6.0.1.tgz", - "integrity": "sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1837,9 +1643,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.18", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.18.tgz", - "integrity": "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==", + "version": "2.10.23", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.23.tgz", + "integrity": "sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1858,43 +1664,6 @@ "require-from-string": "^2.0.2" } }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bl/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/brace-expansion": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", @@ -1988,9 +1757,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001788", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", - "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", "dev": true, "funding": [ { @@ -2025,42 +1794,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2081,23 +1814,6 @@ "dev": true, "license": "MIT" }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true, - "license": "MIT" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2161,7 +1877,6 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, "node_modules/debug": { @@ -2182,16 +1897,6 @@ } } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2199,62 +1904,6 @@ "dev": true, "license": "MIT" }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dependency-tree": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-11.4.0.tgz", - "integrity": "sha512-r4wZ1pfv8eQrnoWbIGdrJTVmlb0dkXdwBjKsotKO4gmfqrOsAMG+0+cfA5EZ3NO8umc85twXOl1eO27E5pjTzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^12.1.0", - "filing-cabinet": "^5.2.0", - "precinct": "^12.2.0", - "typescript": "^5.9.3" - }, - "bin": { - "dependency-tree": "bin/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/dependency-tree/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/dependency-tree/node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/detect-gpu": { "version": "5.0.70", "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz", @@ -2274,147 +1923,6 @@ "node": ">=8" } }, - "node_modules/detective-amd": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-6.0.1.tgz", - "integrity": "sha512-TtyZ3OhwUoEEIhTFoc1C9IyJIud3y+xYkSRjmvCt65+ycQuc3VcBrPRTMWoO/AnuCyOB8T5gky+xf7Igxtjd3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "ast-module-types": "^6.0.1", - "escodegen": "^2.1.0", - "get-amd-module-type": "^6.0.1", - "node-source-walk": "^7.0.1" - }, - "bin": { - "detective-amd": "bin/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/detective-cjs": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-6.1.0.tgz", - "integrity": "sha512-Qt3S4IddVNDb+71lm+jmt5NznIsgcKlibTnrw9Zr91rT9vRwKp+73+ImqLTNrQj4YuOxnzrC7GwIAVwF7136XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ast-module-types": "^6.0.1", - "node-source-walk": "^7.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/detective-es6": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-5.0.1.tgz", - "integrity": "sha512-XusTPuewnSUdoxRSx8OOI6xIA/uld/wMQwYsouvFN2LAg7HgP06NF1lHRV3x6BZxyL2Kkoih4ewcq8hcbGtwew==", - "dev": true, - "license": "MIT", - "dependencies": { - "node-source-walk": "^7.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/detective-postcss": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-7.0.1.tgz", - "integrity": "sha512-bEOVpHU9picRZux5XnwGsmCN4+8oZo7vSW0O0/Enq/TO5R2pIAP2279NsszpJR7ocnQt4WXU0+nnh/0JuK4KHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-url": "^1.2.4", - "postcss-values-parser": "^6.0.2" - }, - "engines": { - "node": "^14.0.0 || >=16.0.0" - }, - "peerDependencies": { - "postcss": "^8.4.47" - } - }, - "node_modules/detective-sass": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-6.0.1.tgz", - "integrity": "sha512-jSGPO8QDy7K7pztUmGC6aiHkexBQT4GIH+mBAL9ZyBmnUIOFbkfZnO8wPRRJFP/QP83irObgsZHCoDHZ173tRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "gonzales-pe": "^4.3.0", - "node-source-walk": "^7.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/detective-scss": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-5.0.1.tgz", - "integrity": "sha512-MAyPYRgS6DCiS6n6AoSBJXLGVOydsr9huwXORUlJ37K3YLyiN0vYHpzs3AdJOgHobBfispokoqrEon9rbmKacg==", - "dev": true, - "license": "MIT", - "dependencies": { - "gonzales-pe": "^4.3.0", - "node-source-walk": "^7.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/detective-stylus": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-5.0.1.tgz", - "integrity": "sha512-Dgn0bUqdGbE3oZJ+WCKf8Dmu7VWLcmRJGc6RCzBgG31DLIyai9WAoEhYRgIHpt/BCRMrnXLbGWGPQuBUrnF0TA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/detective-typescript": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-14.0.0.tgz", - "integrity": "sha512-pgN43/80MmWVSEi5LUuiVvO/0a9ss5V7fwVfrJ4QzAQRd3cwqU1SfWGXJFcNKUqoD5cS+uIovhw5t/0rSeC5Mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "^8.23.0", - "ast-module-types": "^6.0.1", - "node-source-walk": "^7.0.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "typescript": "^5.4.4" - } - }, - "node_modules/detective-vue2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/detective-vue2/-/detective-vue2-2.2.0.tgz", - "integrity": "sha512-sVg/t6O2z1zna8a/UIV6xL5KUa2cMTQbdTIIvqNM0NIPswp52fe43Nwmbahzj3ww4D844u/vC2PYfiGLvD3zFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@dependents/detective-less": "^5.0.1", - "@vue/compiler-sfc": "^3.5.13", - "detective-es6": "^5.0.1", - "detective-sass": "^6.0.1", - "detective-scss": "^5.0.1", - "detective-stylus": "^5.0.1", - "detective-typescript": "^14.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "typescript": "^5.4.4" - } - }, "node_modules/draco3d": { "version": "1.5.7", "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", @@ -2422,49 +1930,12 @@ "license": "Apache-2.0" }, "node_modules/electron-to-chromium": { - "version": "1.5.336", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.336.tgz", - "integrity": "sha512-AbH9q9J455r/nLmdNZes0G0ZKcRX73FicwowalLs6ijwOmCJSRRrLX63lcAlzy9ux3dWK1w1+1nsBJEWN11hcQ==", + "version": "1.5.344", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz", + "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==", "dev": true, "license": "ISC" }, - "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", - "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -2488,28 +1959,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, "node_modules/eslint": { "version": "9.39.4", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", @@ -2618,9 +2067,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", "dependencies": { @@ -2634,7 +2083,7 @@ "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-react-refresh": { @@ -2695,20 +2144,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", @@ -2745,13 +2180,6 @@ "node": ">=4.0" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2833,56 +2261,6 @@ "node": ">=16.0.0" } }, - "node_modules/filing-cabinet": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-5.3.0.tgz", - "integrity": "sha512-2EwtzdQkC37FJxDOrKuEOplTFzzaToCqzT008DrIWW27RQ6psxitfUi6hct5mUhMHO7C6xopOhxubyjyPCapbQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "app-module-path": "^2.2.0", - "commander": "^12.1.0", - "enhanced-resolve": "^5.20.1", - "module-definition": "^6.0.1", - "module-lookup-amd": "^9.1.1", - "resolve": "^1.22.12", - "resolve-dependency-path": "^4.0.1", - "sass-lookup": "^6.1.1", - "stylus-lookup": "^6.1.1", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.9.3" - }, - "bin": { - "filing-cabinet": "bin/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/filing-cabinet/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/filing-cabinet/node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2936,16 +2314,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2956,27 +2324,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-amd-module-type": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-6.0.2.tgz", - "integrity": "sha512-7zShVYAYtMnj9S65CfN+hvpBCByfuB1OY8xID01nZEzXTZbx4YyysAfi+nMl95JSR6odt4q8TCj2W63KAoyVLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ast-module-types": "^6.0.1", - "node-source-walk": "^7.0.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", - "dev": true, - "license": "ISC" - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3009,28 +2356,11 @@ "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==", "license": "MIT" }, - "node_modules/gonzales-pe": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", - "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "gonzales": "bin/gonzales.js" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" + "node_modules/gsap": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz", + "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==", + "license": "Standard 'no charge' license: https://gsap.com/standard-license." }, "node_modules/gsap": { "version": "3.15.0", @@ -3048,19 +2378,6 @@ "node": ">=8" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -3147,36 +2464,6 @@ "node": ">=0.8.19" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3200,75 +2487,12 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", "license": "MIT" }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-url": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-url-superb": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz", - "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3651,7 +2875,6 @@ "version": "0.21.0", "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.21.0.tgz", "integrity": "sha512-tpvxN7v1GvE/Tv+GRopfOp0W7fVEjF4PltkuX8vOCIfim22rD1ztvfkoEMcv9lzQeuNUSeIrUmUjBwmlW/oUew==", - "dev": true, "license": "MIT" }, "node_modules/locate-path": { @@ -3677,23 +2900,6 @@ "dev": true, "license": "MIT" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3726,55 +2932,6 @@ "three": ">=0.134.0" } }, - "node_modules/madge": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/madge/-/madge-8.0.0.tgz", - "integrity": "sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "commander": "^7.2.0", - "commondir": "^1.0.1", - "debug": "^4.3.4", - "dependency-tree": "^11.0.0", - "ora": "^5.4.1", - "pluralize": "^8.0.0", - "pretty-ms": "^7.0.1", - "rc": "^1.2.8", - "stream-to-array": "^2.3.0", - "ts-graphviz": "^2.1.2", - "walkdir": "^0.4.1" - }, - "bin": { - "madge": "bin/cli.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "individual", - "url": "https://www.paypal.me/pahen" - }, - "peerDependencies": { - "typescript": "^5.4.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, "node_modules/meshline": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz", @@ -3785,21 +2942,11 @@ } }, "node_modules/meshoptimizer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.0.1.tgz", - "integrity": "sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz", + "integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==", "license": "MIT" }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -3813,61 +2960,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/module-definition": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-6.0.1.tgz", - "integrity": "sha512-FeVc50FTfVVQnolk/WQT8MX+2WVcDnTGiq6Wo+/+lJ2ET1bRVi3HG3YlJUfqagNMc/kUlFSoR96AJkxGpKz13g==", - "dev": true, - "license": "MIT", - "dependencies": { - "ast-module-types": "^6.0.1", - "node-source-walk": "^7.0.1" - }, - "bin": { - "module-definition": "bin/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/module-lookup-amd": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-9.1.1.tgz", - "integrity": "sha512-JzXhQvud8K3yT9l24XTDMXMQ4/LD9a9oXBcbLP0ubdvBpVrGFsybm5+2PDIl0negUYP1l88fCgjQzoMMg247+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^12.1.0", - "requirejs": "^2.3.8", - "requirejs-config-file": "^4.0.0" - }, - "bin": { - "lookup-amd": "bin/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/module-lookup-amd/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3912,25 +3004,12 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", "dev": true, "license": "MIT" }, - "node_modules/node-source-walk": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-7.0.1.tgz", - "integrity": "sha512-3VW/8JpPqPvnJvseXowjZcirPisssnBuDikk6JIZ8jQzF7KJQX52iPFX4RYYxLycYH7IbMRSPUOga/esVjy5Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.7" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3940,22 +3019,6 @@ "node": ">=0.10.0" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3974,30 +3037,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -4043,16 +3082,6 @@ "node": ">=6" } }, - "node_modules/parse-ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", - "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4072,13 +3101,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -4099,20 +3121,10 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", "dev": true, "funding": [ { @@ -4138,22 +3150,13 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-values-parser": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz", - "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "color-name": "^1.1.4", - "is-url-superb": "^4.0.0", - "quote-unquote": "^1.0.0" - }, - "engines": { - "node": ">=10" - }, + "node_modules/postprocessing": { + "version": "6.39.1", + "resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.39.1.tgz", + "integrity": "sha512-R2dG2zy+BAx3USl5EHw+PvnrlbT5PKnZVp3se0HCR0pWH8WQdh742yNG4YWOsq6c0bFpffk0Gd2RqPeoP/wKng==", + "license": "Zlib", "peerDependencies": { - "postcss": "^8.2.9" + "three": ">= 0.168.0 < 0.185.0" } }, "node_modules/postprocessing": { @@ -4171,60 +3174,6 @@ "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", "license": "ISC" }, - "node_modules/precinct": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/precinct/-/precinct-12.3.0.tgz", - "integrity": "sha512-xHjunvRKzrSOxXzMhNNkqZO4feTob3BO4A85uMpm3UWPC0+ZipVWZrSYbfKrjik8ynBjDrPtH+tdFFuKcuczrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@dependents/detective-less": "^5.0.1", - "commander": "^12.1.0", - "detective-amd": "^6.0.1", - "detective-cjs": "^6.1.0", - "detective-es6": "^5.0.1", - "detective-postcss": "^7.0.1", - "detective-sass": "^6.0.1", - "detective-scss": "^5.0.1", - "detective-stylus": "^5.0.1", - "detective-typescript": "^14.0.0", - "detective-vue2": "^2.2.0", - "module-definition": "^6.0.1", - "node-source-walk": "^7.0.1", - "postcss": "^8.5.9", - "typescript": "^5.9.3" - }, - "bin": { - "precinct": "bin/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/precinct/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/precinct/node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4236,9 +3185,9 @@ } }, "node_modules/prettier": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.2.tgz", - "integrity": "sha512-8c3mgTe0ASwWAJK+78dpviD+A8EqhndQPUBpNUIPt6+xWlIigCwfN01lWr9MAede4uqXGTEKeQWTvzb3vjia0Q==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", "bin": { @@ -4264,22 +3213,6 @@ "node": ">=6.0.0" } }, - "node_modules/pretty-ms": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", - "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "parse-ms": "^2.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/promise-worker-transferable": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz", @@ -4311,13 +3244,6 @@ "node": ">=6" } }, - "node_modules/quote-unquote": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz", - "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==", - "dev": true, - "license": "MIT" - }, "node_modules/r3f-perf": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/r3f-perf/-/r3f-perf-7.2.3.tgz", @@ -4538,32 +3464,6 @@ } } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react": { "version": "19.2.5", "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", @@ -4644,21 +3544,6 @@ } } }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -4668,66 +3553,6 @@ "node": ">=0.10.0" } }, - "node_modules/requirejs": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.8.tgz", - "integrity": "sha512-7/cTSLOdYkNBNJcDMWf+luFvMriVm7eYxp4BcFCsAX0wF421Vyce5SXP17c+Jd5otXKGNehIonFlyQXSowL6Mw==", - "dev": true, - "license": "MIT", - "bin": { - "r_js": "bin/r.js", - "r.js": "bin/r.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/requirejs-config-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz", - "integrity": "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esprima": "^4.0.0", - "stringify-object": "^3.2.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-dependency-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-4.0.1.tgz", - "integrity": "sha512-YQftIIC4vzO9UMhO/sCgXukNyiwVRCVaxiWskCBy7Zpqkplm8kTAISZ8O1MoKW1ca6xzgLUBjZTcDgypXvXxiQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4738,29 +3563,15 @@ "node": ">=4" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/rolldown": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", - "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", + "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.124.0", - "@rolldown/pluginutils": "1.0.0-rc.15" + "@oxc-project/types": "=0.127.0", + "@rolldown/pluginutils": "1.0.0-rc.17" }, "bin": { "rolldown": "bin/cli.mjs" @@ -4769,78 +3580,30 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-x64": "1.0.0-rc.15", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" + "@rolldown/binding-android-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-x64": "1.0.0-rc.17", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" } }, "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", - "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz", + "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==", "dev": true, "license": "MIT" }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/sass-lookup": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-6.1.1.tgz", - "integrity": "sha512-12dvZdQYTeKZ1ypjuiijZYuMZ1m0F+4+BkRX5yJi2WA9W3DBUrcdCt7bVuKlagHl11n8eYtalWDle+m98Ol2DA==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^12.1.0", - "enhanced-resolve": "^5.20.0" - }, - "bin": { - "sass-lookup": "bin/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/sass-lookup/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -4884,24 +3647,6 @@ "node": ">=8" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4938,64 +3683,6 @@ "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==", "license": "MIT" }, - "node_modules/stream-to-array": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz", - "integrity": "sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.1.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5009,32 +3696,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stylus-lookup": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-6.1.1.tgz", - "integrity": "sha512-0+xmFLaqWksv5/pMiZtONG6gP82YNGVWgKiQXvw8cdKVFEJ++X9dySGR0hG+A+78PBtbHPqiJzXi2ZKoWr/7Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^12.1.0" - }, - "bin": { - "stylus-lookup": "bin/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/stylus-lookup/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5048,19 +3709,6 @@ "node": ">=8" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/suspend-react": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", @@ -5086,20 +3734,6 @@ "url": "https://opencollective.com/synckit" } }, - "node_modules/tapable": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", - "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/three": { "version": "0.183.2", "resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz", @@ -5198,47 +3832,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/ts-graphviz": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/ts-graphviz/-/ts-graphviz-2.1.6.tgz", - "integrity": "sha512-XyLVuhBVvdJTJr2FJJV2L1pc4MwSjMhcunRVgDE9k4wbb2ee7ORYnPewxMWUav12vxyfUM686MSGsqnVRIInuw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ts-graphviz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ts-graphviz" - } - ], - "license": "MIT", - "dependencies": { - "@ts-graphviz/adapter": "^2.0.6", - "@ts-graphviz/ast": "^2.0.7", - "@ts-graphviz/common": "^2.1.5", - "@ts-graphviz/core": "^2.0.7" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -5298,9 +3891,9 @@ } }, "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5312,16 +3905,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", - "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.0.tgz", + "integrity": "sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.58.2", - "@typescript-eslint/parser": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2", - "@typescript-eslint/utils": "8.58.2" + "@typescript-eslint/eslint-plugin": "8.59.0", + "@typescript-eslint/parser": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/utils": "8.59.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5392,13 +3985,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, "node_modules/utility-types": { "version": "3.11.0", "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", @@ -5409,17 +3995,17 @@ } }, "node_modules/vite": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", - "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", + "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.15", - "tinyglobby": "^0.2.15" + "postcss": "^8.5.10", + "rolldown": "1.0.0-rc.17", + "tinyglobby": "^0.2.16" }, "bin": { "vite": "bin/vite.js" @@ -5486,26 +4072,6 @@ } } }, - "node_modules/walkdir": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", - "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/webgl-constants": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", diff --git a/package.json b/package.json index 4a55bcd..c71412b 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@react-three/postprocessing": "^3.0.4", "@react-three/rapier": "^2.2.0", "gsap": "^3.15.0", + "lil-gui": "^0.21.0", "r3f-perf": "^7.2.3", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -37,7 +38,6 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.4.0", - "lil-gui": "^0.21.0", "prettier": "^3.8.2", "typescript": "~6.0.2", "typescript-eslint": "^8.58.0", diff --git a/public/models/lafabrik/model.gltf b/public/models/lafabrik/model.gltf new file mode 100644 index 0000000..13639cd --- /dev/null +++ b/public/models/lafabrik/model.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d623439755187553ac394395a4e8734e4ea7ca950c94b50649d2b00e6461387e +size 80888 diff --git a/public/models/map/model.gltf b/public/models/map/model.gltf index f477d5c..2daca27 100644 --- a/public/models/map/model.gltf +++ b/public/models/map/model.gltf @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3535a67501bb43ccf233a25e98b20b3804e29f1fe7ef8ba821bbdd00b98f140 -size 3279070 +oid sha256:838b942fbdb16367386f3f2a3cc6b13c363874ac02ae16e623654fcdfea609ea +size 3220908 diff --git a/public/skybox/sky.exr b/public/skybox/sky.exr new file mode 100644 index 0000000..c6ff8e1 --- /dev/null +++ b/public/skybox/sky.exr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:904b303c98f865526b9524b955f440e630f29d8e18a57bb7bf443fcd9715add1 +size 83079911 diff --git a/src/App.tsx b/src/App.tsx index 4a49d2b..2a63dea 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,5 @@ import { Routes, Route } from "react-router-dom"; +import { Suspense } from "react"; import { Canvas } from "@react-three/fiber"; import { Crosshair } from "@/components/ui/Crosshair"; import { InteractPrompt } from "@/components/ui/InteractPrompt"; @@ -14,8 +15,10 @@ function App(): React.JSX.Element { element={ <> - - + + + + @@ -27,4 +30,4 @@ function App(): React.JSX.Element { ); } -export default App; +export default App; \ No newline at end of file diff --git a/src/components/3d/GrabbableObject.tsx b/src/components/3d/GrabbableObject.tsx new file mode 100644 index 0000000..0499484 --- /dev/null +++ b/src/components/3d/GrabbableObject.tsx @@ -0,0 +1,140 @@ +import { useRef } from "react"; +import { useFrame, useThree } from "@react-three/fiber"; +import { RigidBody } from "@react-three/rapier"; +import type { RapierRigidBody } from "@react-three/rapier"; +import * as THREE from "three"; +import { InteractableObject } from "@/components/3d/InteractableObject"; +import { + GRAB_DEFAULT_COLLIDERS, + GRAB_DEFAULT_LABEL, + GRAB_HOLD_DISTANCE_DEFAULT, + GRAB_HOLD_DISTANCE_MAX, + GRAB_HOLD_DISTANCE_MIN, + GRAB_HOLD_DISTANCE_STEP, + GRAB_STIFFNESS_DEFAULT, + GRAB_STIFFNESS_MAX, + GRAB_STIFFNESS_MIN, + GRAB_STIFFNESS_STEP, + GRAB_THROW_BOOST_DEFAULT, + GRAB_THROW_BOOST_MAX, + GRAB_THROW_BOOST_MIN, + GRAB_THROW_BOOST_STEP, +} from "@/data/grabConfig"; +import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; +import type { ColliderShape, Vector3Tuple } from "@/types/3d"; + +interface GrabbableObjectProps { + position: Vector3Tuple; + children: React.ReactNode; + colliders?: ColliderShape; + label?: string; +} + +// Shared params let one debug folder drive every instance. +const params = { + stiffness: GRAB_STIFFNESS_DEFAULT, + throwBoost: GRAB_THROW_BOOST_DEFAULT, + holdDistance: GRAB_HOLD_DISTANCE_DEFAULT, +}; + +const ZERO_ANGULAR_VELOCITY = { x: 0, y: 0, z: 0 }; + +const _holdTarget = new THREE.Vector3(); +const _currentPos = new THREE.Vector3(); +const _velocity = new THREE.Vector3(); + +export function GrabbableObject({ + position, + children, + colliders = GRAB_DEFAULT_COLLIDERS, + label = GRAB_DEFAULT_LABEL, +}: GrabbableObjectProps): React.JSX.Element { + const camera = useThree((state) => state.camera); + const rbRef = useRef(null); + const isHolding = useRef(false); + + useDebugFolder("GrabbableObject", (folder) => { + folder + .add( + params, + "stiffness", + GRAB_STIFFNESS_MIN, + GRAB_STIFFNESS_MAX, + GRAB_STIFFNESS_STEP, + ) + .name("Hold stiffness"); + folder + .add( + params, + "throwBoost", + GRAB_THROW_BOOST_MIN, + GRAB_THROW_BOOST_MAX, + GRAB_THROW_BOOST_STEP, + ) + .name("Throw boost"); + folder + .add( + params, + "holdDistance", + GRAB_HOLD_DISTANCE_MIN, + GRAB_HOLD_DISTANCE_MAX, + GRAB_HOLD_DISTANCE_STEP, + ) + .name("Hold distance"); + }); + + useFrame(() => { + if (!isHolding.current || !rbRef.current) return; + + camera.getWorldDirection(_holdTarget); + _holdTarget.multiplyScalar(params.holdDistance).add(camera.position); + + const t = rbRef.current.translation(); + _currentPos.set(t.x, t.y, t.z); + + _velocity + .subVectors(_holdTarget, _currentPos) + .multiplyScalar(params.stiffness); + + rbRef.current.setLinvel( + { x: _velocity.x, y: _velocity.y, z: _velocity.z }, + true, + ); + rbRef.current.setAngvel(ZERO_ANGULAR_VELOCITY, true); + }); + + return ( + + { + isHolding.current = true; + }} + onRelease={() => { + isHolding.current = false; + if (!rbRef.current || params.throwBoost === GRAB_THROW_BOOST_DEFAULT) + return; + const v = rbRef.current.linvel(); + rbRef.current.setLinvel( + { + x: v.x * params.throwBoost, + y: v.y * params.throwBoost, + z: v.z * params.throwBoost, + }, + true, + ); + }} + > + {children} + + + ); +} diff --git a/src/components/3d/InteractableObject.tsx b/src/components/3d/InteractableObject.tsx index c08845f..4403579 100644 --- a/src/components/3d/InteractableObject.tsx +++ b/src/components/3d/InteractableObject.tsx @@ -1,73 +1,94 @@ -import { useEffect, useRef } from "react"; +import { useCallback, useEffect, useRef } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { RigidBody } from "@react-three/rapier"; import type { RapierRigidBody } from "@react-three/rapier"; import * as THREE from "three"; +import type GUI from "lil-gui"; import type { RefObject } from "react"; +import { + INTERACTION_DEBUG_SPHERE_COLOR, + INTERACTION_DEBUG_SPHERE_OPACITY, + INTERACTION_DEBUG_SPHERE_SEGMENTS, +} from "@/data/debugConfig"; import { Debug } from "@/utils/debug/Debug"; import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; -import { - InteractionManager, - type InteractableHandle, - type InteractableKind, -} from "@/stateManager/InteractionManager"; +import { InteractionManager } from "@/stateManager/InteractionManager"; import { INTERACTION_RADIUS } from "@/data/interactionConfig"; +import type { Vector3Tuple } from "@/types/3d"; +import type { InteractableHandle, InteractableKind } from "@/types/interaction"; -interface InteractableObjectProps { - kind: InteractableKind; +interface InteractableObjectBaseProps { label: string; - position: [number, number, number]; - rigidBodyType?: "dynamic" | "fixed"; - colliders?: "cuboid" | "ball" | "hull"; - rbRef?: RefObject; + position: Vector3Tuple; + bodyRef?: RefObject; onPress: () => void; - onRelease?: () => void; children: React.ReactNode; } +interface TriggerInteractableObjectProps extends InteractableObjectBaseProps { + kind: "trigger"; +} + +interface GrabInteractableObjectProps extends InteractableObjectBaseProps { + kind: "grab"; + onRelease: () => void; +} + +type InteractableObjectProps = + | TriggerInteractableObjectProps + | GrabInteractableObjectProps; + +type MutableInteractableHandle = { + kind: InteractableKind; + label: string; + onPress: () => void; + onRelease?: () => void; +}; + const _cameraPos = new THREE.Vector3(); const _cameraDir = new THREE.Vector3(); const _objectPos = new THREE.Vector3(); const _raycaster = new THREE.Raycaster(); -export function InteractableObject({ - kind, - label, - position, - rigidBodyType = "dynamic", - colliders = "cuboid", - rbRef, - onPress, - onRelease = () => {}, - children, -}: InteractableObjectProps): React.JSX.Element { +export function InteractableObject( + props: InteractableObjectProps, +): React.JSX.Element { + const { kind, label, position, bodyRef, onPress, children } = props; + const onRelease = props.kind === "grab" ? props.onRelease : undefined; const camera = useThree((state) => state.camera); - const internalRef = useRef(null); - const bodyRef = rbRef ?? internalRef; const groupRef = useRef(null); const debugSphereRef = useRef(null); - const handle = useRef({ - kind, - label, - onPress, - onRelease, - }); + const handle = useRef( + props.kind === "grab" + ? { kind: props.kind, label, onPress, onRelease: props.onRelease } + : { kind: props.kind, label, onPress }, + ); useEffect(() => { - handle.current.onPress = onPress; - handle.current.onRelease = onRelease; - }); + const current = handle.current as MutableInteractableHandle; + current.kind = kind; + current.label = label; + current.onPress = onPress; - useDebugFolder("Interaction", (folder) => { + if (kind === "grab" && onRelease) { + current.onRelease = onRelease; + return; + } + + delete current.onRelease; + return undefined; + }, [kind, label, onPress, onRelease]); + + const setupInteractionDebugFolder = useCallback((folder: GUI) => { folder .add({ radius: INTERACTION_RADIUS }, "radius") .name("Interaction radius") .disable(); - }); + }, []); + + useDebugFolder("Interaction", setupInteractionDebugFolder); useFrame(() => { - const body = bodyRef.current; const group = groupRef.current; const debug = Debug.getInstance(); const manager = InteractionManager.getInstance(); @@ -77,8 +98,8 @@ export function InteractableObject({ debug.active && debug.getShowInteractionSpheres(); } - if (body) { - const t = body.translation(); + if (bodyRef?.current) { + const t = bodyRef.current.translation(); _objectPos.set(t.x, t.y, t.z); } else { _objectPos.set(...position); @@ -99,7 +120,6 @@ export function InteractableObject({ _raycaster.far = INTERACTION_RADIUS; const hits = group ? _raycaster.intersectObject(group, true) : []; - const validHit = hits.find((h) => h.object !== debugSphereRef.current); if (validHit) { @@ -110,24 +130,23 @@ export function InteractableObject({ }); return ( - - - {children} - - - - - - + + {children} + + + + + ); } diff --git a/src/components/3d/TriggerObject.tsx b/src/components/3d/TriggerObject.tsx new file mode 100644 index 0000000..937dfae --- /dev/null +++ b/src/components/3d/TriggerObject.tsx @@ -0,0 +1,94 @@ +import { useState } from "react"; +import { useGLTF } from "@react-three/drei"; +import { RigidBody } from "@react-three/rapier"; +import { InteractableObject } from "@/components/3d/InteractableObject"; +import { + TRIGGER_DEFAULT_COLLIDERS, + TRIGGER_DEFAULT_LABEL, + TRIGGER_DEFAULT_SOUND_VOLUME, + TRIGGER_DEFAULT_SPAWN_OFFSET, +} from "@/data/triggerConfig"; +import { AudioManager } from "@/stateManager/AudioManager"; +import type { ColliderShape, Vector3Tuple } from "@/types/3d"; + +interface SpawnedModel { + id: number; + position: Vector3Tuple; +} + +interface TriggerObjectProps { + position: Vector3Tuple; + children: React.ReactNode; + colliders?: ColliderShape; + label?: string; + soundPath?: string; + soundVolume?: number; + spawnModel?: string; + spawnOffset?: Vector3Tuple; +} + +let _spawnCounter = 0; + +function SpawnedModelInstance({ + path, + position, +}: { + path: string; + position: Vector3Tuple; +}): React.JSX.Element { + const { scene } = useGLTF(path); + return ; +} + +export function TriggerObject({ + position, + children, + colliders = TRIGGER_DEFAULT_COLLIDERS, + label = TRIGGER_DEFAULT_LABEL, + soundPath, + soundVolume = TRIGGER_DEFAULT_SOUND_VOLUME, + spawnModel, + spawnOffset = TRIGGER_DEFAULT_SPAWN_OFFSET, +}: TriggerObjectProps): React.JSX.Element { + const [spawned, setSpawned] = useState([]); + + return ( + <> + + { + if (soundPath) { + AudioManager.getInstance().playSound(soundPath, soundVolume); + } + + if (spawnModel) { + const spawnPos: Vector3Tuple = [ + position[0] + spawnOffset[0], + position[1] + spawnOffset[1], + position[2] + spawnOffset[2], + ]; + setSpawned((prev) => [ + ...prev, + { id: ++_spawnCounter, position: spawnPos }, + ]); + } + }} + > + {children} + + + + {spawnModel && + spawned.map((s) => ( + + ))} + + ); +} diff --git a/src/components/ui/InteractPrompt.tsx b/src/components/ui/InteractPrompt.tsx index 8fff76d..4c86b21 100644 --- a/src/components/ui/InteractPrompt.tsx +++ b/src/components/ui/InteractPrompt.tsx @@ -1,3 +1,4 @@ +import { INTERACT_KEY } from "@/data/keybindings"; import { useCameraMode } from "@/hooks/debug/useCameraMode"; import { useInteraction } from "@/hooks/useInteraction"; @@ -10,7 +11,7 @@ export function InteractPrompt(): React.JSX.Element | null { return (
- E + {INTERACT_KEY.toUpperCase()} {focused.label}
); diff --git a/src/data/debugConfig.ts b/src/data/debugConfig.ts new file mode 100644 index 0000000..6f5e4fc --- /dev/null +++ b/src/data/debugConfig.ts @@ -0,0 +1,16 @@ +export const INTERACTION_DEBUG_SPHERE_SEGMENTS = 16; +export const INTERACTION_DEBUG_SPHERE_COLOR = "#facc15"; +export const INTERACTION_DEBUG_SPHERE_OPACITY = 0.25; + +export const MAP_DEBUG_BOX_HELPER_COLOR = 0x00ff88; + +export const DEBUG_CAMERA_DAMPING_FACTOR = 0.05; +export const DEBUG_CAMERA_MIN_DISTANCE = 100; +export const DEBUG_CAMERA_MAX_DISTANCE = 1000; + +export const DEBUG_GRID_SIZE = 180; +export const DEBUG_GRID_DIVISIONS = 36; +export const DEBUG_GRID_PRIMARY_COLOR = "#1d4ed8"; +export const DEBUG_GRID_SECONDARY_COLOR = "#1e293b"; +export const DEBUG_GRID_Y = 0.01; +export const DEBUG_AXES_SIZE = 10; diff --git a/src/data/environmentConfig.ts b/src/data/environmentConfig.ts new file mode 100644 index 0000000..fe277fa --- /dev/null +++ b/src/data/environmentConfig.ts @@ -0,0 +1,2 @@ +export const GAME_SCENE_SKYBOX_PATH = "/skybox/sky.exr"; +export const PHYSICS_SCENE_BACKGROUND_COLOR = "#0b1018"; diff --git a/src/data/grabConfig.ts b/src/data/grabConfig.ts new file mode 100644 index 0000000..774b515 --- /dev/null +++ b/src/data/grabConfig.ts @@ -0,0 +1,18 @@ +export const GRAB_DEFAULT_COLLIDERS = "cuboid"; +export const GRAB_DEFAULT_LABEL = "Prendre"; + +export const GRAB_STIFFNESS_DEFAULT = 15; +export const GRAB_THROW_BOOST_DEFAULT = 1.0; +export const GRAB_HOLD_DISTANCE_DEFAULT = 2; + +export const GRAB_STIFFNESS_MIN = 1; +export const GRAB_STIFFNESS_MAX = 50; +export const GRAB_STIFFNESS_STEP = 1; + +export const GRAB_THROW_BOOST_MIN = 0.5; +export const GRAB_THROW_BOOST_MAX = 3.0; +export const GRAB_THROW_BOOST_STEP = 0.1; + +export const GRAB_HOLD_DISTANCE_MIN = 0.5; +export const GRAB_HOLD_DISTANCE_MAX = 5.0; +export const GRAB_HOLD_DISTANCE_STEP = 0.1; diff --git a/src/data/keybindings.ts b/src/data/keybindings.ts new file mode 100644 index 0000000..04bd83b --- /dev/null +++ b/src/data/keybindings.ts @@ -0,0 +1,7 @@ +export const MOVE_FORWARD_KEY = "z"; +export const MOVE_BACKWARD_KEY = "s"; +export const MOVE_LEFT_KEY = "q"; +export const MOVE_RIGHT_KEY = "d"; +export const JUMP_KEY = " "; +export const INTERACT_KEY = "e"; +export const PRIMARY_INTERACT_MOUSE_BUTTON = 0; diff --git a/src/data/lightingConfig.ts b/src/data/lightingConfig.ts new file mode 100644 index 0000000..2b67660 --- /dev/null +++ b/src/data/lightingConfig.ts @@ -0,0 +1,30 @@ +export const AMBIENT_LIGHT_COLOR = "#dbeafe"; +export const SUN_LIGHT_COLOR = "#fff7ed"; + +export const LIGHTING_DEFAULTS = { + ambientIntensity: 1.8, + sunIntensity: 2.8, + sunX: 60, + sunY: 80, + sunZ: 30, +}; + +export const AMBIENT_INTENSITY_MIN = 0; +export const AMBIENT_INTENSITY_MAX = 5; +export const AMBIENT_INTENSITY_STEP = 0.1; + +export const SUN_INTENSITY_MIN = 0; +export const SUN_INTENSITY_MAX = 8; +export const SUN_INTENSITY_STEP = 0.1; + +export const SUN_X_MIN = -100; +export const SUN_X_MAX = 100; +export const SUN_X_STEP = 1; + +export const SUN_Y_MIN = 0; +export const SUN_Y_MAX = 150; +export const SUN_Y_STEP = 1; + +export const SUN_Z_MIN = -100; +export const SUN_Z_MAX = 100; +export const SUN_Z_STEP = 1; diff --git a/src/data/playerConfig.ts b/src/data/playerConfig.ts new file mode 100644 index 0000000..43e183e --- /dev/null +++ b/src/data/playerConfig.ts @@ -0,0 +1,15 @@ +import type { Vector3Tuple } from "@/types/3d"; + +export const PLAYER_EYE_HEIGHT = 1.75; +export const PLAYER_CAPSULE_RADIUS = 0.35; + +export const PLAYER_WALK_SPEED = 11; +export const PLAYER_AIR_CONTROL_FACTOR = 0.35; +export const PLAYER_JUMP_SPEED = 9; +export const PLAYER_GRAVITY = 30; +export const PLAYER_MAX_DELTA = 0.05; +export const PLAYER_ACCELERATION_MULTIPLIER = 9; +export const PLAYER_XZ_DAMPING_FACTOR = 8; + +export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = [0, 100, 0]; +export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [0, 3, 0]; diff --git a/src/data/testSceneConfig.ts b/src/data/testSceneConfig.ts new file mode 100644 index 0000000..0cd23bf --- /dev/null +++ b/src/data/testSceneConfig.ts @@ -0,0 +1,21 @@ +import type { Vector3Tuple } from "@/types/3d"; + +export const TEST_SCENE_FLOOR_POSITION: Vector3Tuple = [0, -0.5, 0]; +export const TEST_SCENE_FLOOR_SIZE: Vector3Tuple = [200, 1, 200]; +export const TEST_SCENE_FLOOR_COLLIDER_HALF_EXTENTS: Vector3Tuple = [ + 100, 0.5, 100, +]; + +export const TEST_SCENE_GRABBABLE_POSITION: Vector3Tuple = [0, 1, -3]; +export const TEST_SCENE_GRABBABLE_BOX_SIZE: Vector3Tuple = [0.5, 0.5, 0.5]; +export const TEST_SCENE_GRABBABLE_COLOR = "#e07b39"; +export const TEST_SCENE_GRABBABLE_ROUGHNESS = 0.6; +export const TEST_SCENE_GRABBABLE_METALNESS = 0.1; + +export const TEST_SCENE_TRIGGER_POSITION: Vector3Tuple = [3, 2, -3]; +export const TEST_SCENE_TRIGGER_SOUND_PATH = "/sounds/fa.mp3"; +export const TEST_SCENE_TRIGGER_RADIUS = 0.4; +export const TEST_SCENE_TRIGGER_SEGMENTS = 32; +export const TEST_SCENE_TRIGGER_COLOR = "#3b82f6"; +export const TEST_SCENE_TRIGGER_ROUGHNESS = 0.3; +export const TEST_SCENE_TRIGGER_METALNESS = 0.5; diff --git a/src/data/triggerConfig.ts b/src/data/triggerConfig.ts new file mode 100644 index 0000000..304ef82 --- /dev/null +++ b/src/data/triggerConfig.ts @@ -0,0 +1,6 @@ +import type { Vector3Tuple } from "@/types/3d"; + +export const TRIGGER_DEFAULT_COLLIDERS = "ball"; +export const TRIGGER_DEFAULT_LABEL = "Interagir"; +export const TRIGGER_DEFAULT_SOUND_VOLUME = 1; +export const TRIGGER_DEFAULT_SPAWN_OFFSET: Vector3Tuple = [0, 0, 0]; diff --git a/src/hooks/debug/useCameraMode.ts b/src/hooks/debug/useCameraMode.ts index 31ee4dc..90976f6 100644 --- a/src/hooks/debug/useCameraMode.ts +++ b/src/hooks/debug/useCameraMode.ts @@ -1,13 +1,6 @@ -import { useSyncExternalStore } from "react"; import type { CameraMode } from "@/types/debug"; -import { Debug } from "@/utils/debug/Debug"; +import { useDebugStore } from "@/hooks/debug/useDebugStore"; export function useCameraMode(): CameraMode { - const debug = Debug.getInstance(); - - return useSyncExternalStore( - (listener) => debug.subscribe(listener), - () => debug.getCameraMode(), - () => debug.getCameraMode(), - ); + return useDebugStore((debug) => debug.getCameraMode()); } diff --git a/src/hooks/debug/useDebugFolder.ts b/src/hooks/debug/useDebugFolder.ts index 7c19e96..ce51f1a 100644 --- a/src/hooks/debug/useDebugFolder.ts +++ b/src/hooks/debug/useDebugFolder.ts @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; import type GUI from "lil-gui"; import { Debug } from "@/utils/debug/Debug"; @@ -6,12 +6,23 @@ export function useDebugFolder( name: string, setup: (folder: GUI) => void, ): void { + const setupRef = useRef(setup); + + useEffect(() => { + setupRef.current = setup; + }, [setup]); + useEffect(() => { const debug = Debug.getInstance(); if (!debug.active) return; + const folder = debug.createFolder(name); - if (!folder) return; - setup(folder); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + if (folder) { + setupRef.current(folder); + } + + return () => { + debug.destroyFolder(name); + }; + }, [name]); } diff --git a/src/hooks/debug/useDebugStore.ts b/src/hooks/debug/useDebugStore.ts new file mode 100644 index 0000000..35f653e --- /dev/null +++ b/src/hooks/debug/useDebugStore.ts @@ -0,0 +1,12 @@ +import { useSyncExternalStore } from "react"; +import { Debug } from "@/utils/debug/Debug"; + +export function useDebugStore(selector: (debug: Debug) => T): T { + const debug = Debug.getInstance(); + + return useSyncExternalStore( + (listener) => debug.subscribe(listener), + () => selector(debug), + () => selector(debug), + ); +} diff --git a/src/hooks/debug/useSceneMode.ts b/src/hooks/debug/useSceneMode.ts index 629c1cd..5d14254 100644 --- a/src/hooks/debug/useSceneMode.ts +++ b/src/hooks/debug/useSceneMode.ts @@ -1,13 +1,6 @@ -import { useSyncExternalStore } from "react"; import type { SceneMode } from "@/types/debug"; -import { Debug } from "@/utils/debug/Debug"; +import { useDebugStore } from "@/hooks/debug/useDebugStore"; export function useSceneMode(): SceneMode { - const debug = Debug.getInstance(); - - return useSyncExternalStore( - (listener) => debug.subscribe(listener), - () => debug.getSceneMode(), - () => debug.getSceneMode(), - ); + return useDebugStore((debug) => debug.getSceneMode()); } diff --git a/src/hooks/useInteraction.ts b/src/hooks/useInteraction.ts index 503c2a6..58fabc2 100644 --- a/src/hooks/useInteraction.ts +++ b/src/hooks/useInteraction.ts @@ -1,18 +1,12 @@ -import { useEffect, useState } from "react"; -import { - InteractionManager, - type InteractionSnapshot, -} from "@/stateManager/InteractionManager"; +import { useSyncExternalStore } from "react"; +import { InteractionManager } from "@/stateManager/InteractionManager"; +import type { InteractionSnapshot } from "@/types/interaction"; + +const manager = InteractionManager.getInstance(); export function useInteraction(): InteractionSnapshot { - const manager = InteractionManager.getInstance(); - const [state, setState] = useState(manager.getState()); - - useEffect(() => { - return manager.subscribe(() => { - setState({ ...manager.getState() }); - }); - }, [manager]); - - return state; + return useSyncExternalStore( + manager.subscribe.bind(manager), + manager.getState.bind(manager), + ); } diff --git a/src/hooks/useOctreeGraphNode.ts b/src/hooks/useOctreeGraphNode.ts new file mode 100644 index 0000000..c03458c --- /dev/null +++ b/src/hooks/useOctreeGraphNode.ts @@ -0,0 +1,24 @@ +import { useEffect, useRef } from "react"; +import type { RefObject } from "react"; +import type { Object3D } from "three"; +import { Octree } from "three/addons/math/Octree.js"; +import type { OctreeReadyHandler } from "@/types/3d"; + +export function useOctreeGraphNode( + graphNodeRef: RefObject, + onOctreeReady: OctreeReadyHandler, +): void { + const octreeBuilt = useRef(false); + + useEffect(() => { + const graphNode = graphNodeRef.current; + if (octreeBuilt.current || !graphNode) return; + octreeBuilt.current = true; + + graphNode.updateMatrixWorld(true); + + const octree = new Octree(); + octree.fromGraphNode(graphNode); + onOctreeReady(octree); + }, [graphNodeRef, onOctreeReady]); +} diff --git a/src/stateManager/AudioManager.ts b/src/stateManager/AudioManager.ts index 7d7d418..1fcc256 100644 --- a/src/stateManager/AudioManager.ts +++ b/src/stateManager/AudioManager.ts @@ -1,5 +1,14 @@ +import { logger } from "@/utils/logger"; + export class AudioManager { private static _instance: AudioManager | null = null; + private readonly _audioPools = new Map(); + + private static readonly MAX_POOL_SIZE_PER_SOUND = 6; + private static readonly IGNORED_PLAYBACK_ERRORS = new Set([ + "AbortError", + "NotAllowedError", + ]); static getInstance(): AudioManager { if (!AudioManager._instance) { @@ -12,12 +21,65 @@ export class AudioManager { private constructor() {} playSound(path: string, volume = 1): void { - const audio = new Audio(path); + const audio = this._acquireAudio(path); audio.volume = Math.max(0, Math.min(1, volume)); - void audio.play(); + audio.currentTime = 0; + + void audio.play().catch((error: unknown) => { + if ( + error instanceof DOMException && + AudioManager.IGNORED_PLAYBACK_ERRORS.has(error.name) + ) { + return; + } + + logger.error("AudioManager", "Failed to play sound", { + path, + error: AudioManager._toLogValue(error), + }); + }); } destroy(): void { + this._audioPools.forEach((pool) => { + pool.forEach((audio) => { + audio.pause(); + audio.src = ""; + }); + }); + this._audioPools.clear(); AudioManager._instance = null; } + + private _acquireAudio(path: string): HTMLAudioElement { + const existingPool = this._audioPools.get(path); + + if (existingPool) { + const availableAudio = existingPool.find( + (audio) => audio.paused || audio.ended, + ); + if (availableAudio) return availableAudio; + + if (existingPool.length < AudioManager.MAX_POOL_SIZE_PER_SOUND) { + const pooledAudio = new Audio(path); + existingPool.push(pooledAudio); + return pooledAudio; + } + + const recycledAudio = existingPool[0]; + if (recycledAudio) return recycledAudio; + } + + const initialAudio = new Audio(path); + this._audioPools.set(path, [initialAudio]); + return initialAudio; + } + + private static _toLogValue(error: unknown): Error | DOMException | string { + if (error instanceof Error || error instanceof DOMException) { + return error; + } + + return String(error); + } } diff --git a/src/stateManager/InteractionManager.ts b/src/stateManager/InteractionManager.ts index 467a58c..6dcdd45 100644 --- a/src/stateManager/InteractionManager.ts +++ b/src/stateManager/InteractionManager.ts @@ -1,23 +1,19 @@ -export type InteractableKind = "grab" | "trigger"; - -export interface InteractableHandle { - kind: InteractableKind; - label: string; - onPress: () => void; - onRelease: () => void; -} - -export interface InteractionSnapshot { - focused: InteractableHandle | null; - holding: boolean; -} +import type { + GrabInteractableHandle, + InteractableHandle, + InteractionSnapshot, +} from "@/types/interaction"; export class InteractionManager { private static _instance: InteractionManager | null = null; private _focused: InteractableHandle | null = null; private _holding = false; - private _holdingHandle: InteractableHandle | null = null; + private _holdingHandle: GrabInteractableHandle | null = null; + private _snapshot: InteractionSnapshot = { + focused: null, + holding: false, + }; private readonly _listeners = new Set<() => void>(); static getInstance(): InteractionManager { @@ -31,20 +27,13 @@ export class InteractionManager { private constructor() {} getState(): InteractionSnapshot { - return { - focused: this._focused, - holding: this._holding, - }; + return this._snapshot; } setFocused(handle: InteractableHandle | null): void { if (this._focused === handle) return; - // Never interrupt an active grab via focus change - if (this._holding) { - this._focused = handle; - this._emit(); - return; - } + if (this._holding) return; + this._focused = handle; this._emit(); } @@ -52,14 +41,20 @@ export class InteractionManager { pressInteract(): void { if (!this._focused) return; - this._holding = this._focused.kind === "grab"; - if (this._holding) this._holdingHandle = this._focused; + if (this._focused.kind === "grab") { + this._holding = true; + this._holdingHandle = this._focused; + } else { + this._holding = false; + this._holdingHandle = null; + } + this._focused.onPress(); this._emit(); } releaseInteract(): void { - const handle = this._holdingHandle ?? this._focused; + const handle = this._holding ? this._holdingHandle : null; if (!handle) return; handle.onRelease(); @@ -77,11 +72,22 @@ export class InteractionManager { } destroy(): void { + this._focused = null; + this._holding = false; + this._holdingHandle = null; + this._snapshot = { + focused: null, + holding: false, + }; this._listeners.clear(); InteractionManager._instance = null; } private _emit(): void { + this._snapshot = { + focused: this._focused, + holding: this._holding, + }; this._listeners.forEach((cb) => cb()); } } diff --git a/src/types/3d.ts b/src/types/3d.ts new file mode 100644 index 0000000..9435e5a --- /dev/null +++ b/src/types/3d.ts @@ -0,0 +1,7 @@ +import type { Octree } from "three/addons/math/Octree.js"; + +export type Vector3Tuple = [number, number, number]; + +export type ColliderShape = "cuboid" | "ball" | "hull"; + +export type OctreeReadyHandler = (octree: Octree) => void; diff --git a/src/types/interaction.ts b/src/types/interaction.ts new file mode 100644 index 0000000..986bed9 --- /dev/null +++ b/src/types/interaction.ts @@ -0,0 +1,23 @@ +export type InteractableKind = "grab" | "trigger"; + +export interface TriggerInteractableHandle { + kind: "trigger"; + label: string; + onPress: () => void; +} + +export interface GrabInteractableHandle { + kind: "grab"; + label: string; + onPress: () => void; + onRelease: () => void; +} + +export type InteractableHandle = + | TriggerInteractableHandle + | GrabInteractableHandle; + +export interface InteractionSnapshot { + focused: InteractableHandle | null; + holding: boolean; +} diff --git a/src/types/logger.ts b/src/types/logger.ts new file mode 100644 index 0000000..3eb022a --- /dev/null +++ b/src/types/logger.ts @@ -0,0 +1,26 @@ +export type LogLevel = "debug" | "info" | "warn" | "error"; + +export type LogValue = + | string + | number + | boolean + | null + | undefined + | Error + | DOMException + | { [key: string]: LogValue } + | LogValue[]; + +export type LogContext = Readonly>; + +export interface LogEntry { + timestamp: string; + level: LogLevel; + scope: string; + message: string; + context?: LogContext; +} + +export interface LoggerConfig { + minLevel: LogLevel; +} diff --git a/src/utils/EventEmitter.ts b/src/utils/EventEmitter.ts index 5f921a1..48d5b7e 100644 --- a/src/utils/EventEmitter.ts +++ b/src/utils/EventEmitter.ts @@ -1,8 +1,5 @@ type Listener = (payload: TPayload) => void; -// TypeScript cannot narrow mapped-type indexed access by a generic key TKey -// (microsoft/TypeScript#30581). The helper below encapsulates the one necessary -// cast so the rest of the class stays cast-free. type ListenerMap> = { [TKey in keyof TEvents]?: Set>; }; diff --git a/src/utils/debug/Debug.ts b/src/utils/debug/Debug.ts index ba98a2a..2c97d30 100644 --- a/src/utils/debug/Debug.ts +++ b/src/utils/debug/Debug.ts @@ -1,5 +1,6 @@ import GUI from "lil-gui"; import type { CameraMode, SceneMode } from "@/types/debug"; +import { isDebugEnabled } from "@/utils/debug/isDebugEnabled"; export class Debug { private static instance: Debug | null = null; @@ -7,7 +8,7 @@ export class Debug { public readonly active: boolean; private readonly gui: GUI | null; private readonly folders = new Map(); - private readonly registeredFolders = new Set(); + private readonly folderRefCounts = new Map(); private readonly listeners = new Set<() => void>(); private readonly controls: { cameraMode: CameraMode; @@ -28,7 +29,7 @@ export class Debug { } private constructor() { - this.active = new URLSearchParams(window.location.search).has("debug"); + this.active = isDebugEnabled(); this.gui = this.active ? new GUI({ title: "La-Fabrik Debug" }) : null; if (this.gui) { @@ -63,27 +64,41 @@ export class Debug { } /** - * Creates a named GUI folder. Returns the folder on first call, null on - * subsequent calls with the same name — callers should skip `.add()` when - * null is returned to avoid duplicating controls under StrictMode double-mount. + * Acquires a named GUI folder. Returns the folder on first acquisition and null + * on subsequent acquisitions so callers only register controls once. */ createFolder(name: string): GUI | null { if (!this.gui) return null; - if (this.registeredFolders.has(name)) return null; - - this.registeredFolders.add(name); - const existing = this.folders.get(name); - if (existing) return existing; + if (existing) { + this.folderRefCounts.set(name, (this.folderRefCounts.get(name) ?? 0) + 1); + return null; + } const folder = this.gui.addFolder(name); this.folders.set(name, folder); + this.folderRefCounts.set(name, 1); return folder; } + destroyFolder(name: string): void { + const folder = this.folders.get(name); + const refCount = this.folderRefCounts.get(name); + if (!folder || refCount === undefined) return; + + if (refCount > 1) { + this.folderRefCounts.set(name, refCount - 1); + return; + } + + folder.destroy(); + this.folders.delete(name); + this.folderRefCounts.delete(name); + } + subscribe(listener: () => void): () => void { this.listeners.add(listener); diff --git a/src/utils/debug/isDebugEnabled.ts b/src/utils/debug/isDebugEnabled.ts new file mode 100644 index 0000000..63aa610 --- /dev/null +++ b/src/utils/debug/isDebugEnabled.ts @@ -0,0 +1,7 @@ +export function isDebugEnabled(): boolean { + if (typeof window === "undefined") { + return false; + } + + return new URLSearchParams(window.location.search).has("debug"); +} diff --git a/src/utils/debug/scene/DebugCameraControls.tsx b/src/utils/debug/scene/DebugCameraControls.tsx index 85977a0..9fc5f99 100644 --- a/src/utils/debug/scene/DebugCameraControls.tsx +++ b/src/utils/debug/scene/DebugCameraControls.tsx @@ -1,13 +1,28 @@ import { OrbitControls } from "@react-three/drei"; +import { + DEBUG_CAMERA_DAMPING_FACTOR, + DEBUG_CAMERA_MAX_DISTANCE, + DEBUG_CAMERA_MIN_DISTANCE, +} from "@/data/debugConfig"; +import { + PLAYER_EYE_HEIGHT, + PLAYER_SPAWN_POSITION_GAME, +} from "@/data/playerConfig"; + +const DEBUG_CAMERA_TARGET = [ + PLAYER_SPAWN_POSITION_GAME[0], + PLAYER_EYE_HEIGHT, + PLAYER_SPAWN_POSITION_GAME[2], +] as const; export function DebugCameraControls(): React.JSX.Element { return ( ); } diff --git a/src/utils/debug/scene/DebugHelpers.tsx b/src/utils/debug/scene/DebugHelpers.tsx index 5abf9a9..738c46e 100644 --- a/src/utils/debug/scene/DebugHelpers.tsx +++ b/src/utils/debug/scene/DebugHelpers.tsx @@ -1,3 +1,11 @@ +import { + DEBUG_AXES_SIZE, + DEBUG_GRID_DIVISIONS, + DEBUG_GRID_PRIMARY_COLOR, + DEBUG_GRID_SECONDARY_COLOR, + DEBUG_GRID_SIZE, + DEBUG_GRID_Y, +} from "@/data/debugConfig"; import { Debug } from "@/utils/debug/Debug"; export function DebugHelpers(): React.JSX.Element | null { @@ -10,10 +18,15 @@ export function DebugHelpers(): React.JSX.Element | null { return ( <> - + ); } diff --git a/src/utils/logger.ts b/src/utils/logger.ts new file mode 100644 index 0000000..b1629de --- /dev/null +++ b/src/utils/logger.ts @@ -0,0 +1,111 @@ +import type { + LogContext, + LogEntry, + LogLevel, + LoggerConfig, +} from "@/types/logger"; +import { isDebugEnabled } from "@/utils/debug/isDebugEnabled"; + +const LEVEL_PRIORITY: Record = { + debug: 10, + info: 20, + warn: 30, + error: 40, +}; + +const LEVEL_LABELS: Record = { + debug: "DEBUG", + info: "INFO", + warn: "WARN", + error: "ERROR", +}; + +const LEVEL_STYLES: Record = { + debug: "color: #94a3b8; font-weight: 600;", + info: "color: #60a5fa; font-weight: 600;", + warn: "color: #f59e0b; font-weight: 600;", + error: "color: #f87171; font-weight: 600;", +}; + +const SCOPE_STYLE = "color: #e5e7eb; font-weight: 600;"; +const MESSAGE_STYLE = "color: inherit;"; + +class Logger { + private readonly config: LoggerConfig; + + constructor(config: LoggerConfig) { + this.config = config; + } + + debug(scope: string, message: string, context?: LogContext): void { + this.log("debug", scope, message, context); + } + + info(scope: string, message: string, context?: LogContext): void { + this.log("info", scope, message, context); + } + + warn(scope: string, message: string, context?: LogContext): void { + this.log("warn", scope, message, context); + } + + error(scope: string, message: string, context?: LogContext): void { + this.log("error", scope, message, context); + } + + private log( + level: LogLevel, + scope: string, + message: string, + context?: LogContext, + ): void { + if (!this.shouldLog(level)) return; + + const entry: LogEntry = { + timestamp: new Date().toISOString(), + level, + scope, + message, + ...(context ? { context } : {}), + }; + + const formattedMessage = `%c[${LEVEL_LABELS[level]}]%c [${scope}]%c ${message}`; + const args = [ + formattedMessage, + LEVEL_STYLES[level], + SCOPE_STYLE, + MESSAGE_STYLE, + entry, + ] as const; + + switch (level) { + case "debug": + console.debug(...args); + return; + case "info": + console.info(...args); + return; + case "warn": + console.warn(...args); + return; + case "error": + console.error(...args); + } + } + + private shouldLog(level: LogLevel): boolean { + return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[this.config.minLevel]; + } +} + +function resolveMinLevel(): LogLevel { + if (typeof window === "undefined") { + return "info"; + } + + return isDebugEnabled() ? "debug" : "info"; +} + +export const logger = new Logger({ + minLevel: resolveMinLevel(), +}); diff --git a/src/world/Environment.tsx b/src/world/Environment.tsx index 7b8af01..72c12b6 100644 --- a/src/world/Environment.tsx +++ b/src/world/Environment.tsx @@ -1,3 +1,18 @@ +import { Environment as DreiEnvironment } from "@react-three/drei"; +import { + GAME_SCENE_SKYBOX_PATH, + PHYSICS_SCENE_BACKGROUND_COLOR, +} from "@/data/environmentConfig"; +import { useSceneMode } from "@/hooks/debug/useSceneMode"; + export function Environment(): React.JSX.Element { - return ; + const sceneMode = useSceneMode(); + + if (sceneMode === "physics") { + return ( + + ); + } + + return ; } diff --git a/src/world/Lighting.tsx b/src/world/Lighting.tsx index 00c5940..37be86f 100644 --- a/src/world/Lighting.tsx +++ b/src/world/Lighting.tsx @@ -1,6 +1,26 @@ import { useRef } from "react"; import { useFrame } from "@react-three/fiber"; import type { AmbientLight, DirectionalLight } from "three"; +import { + AMBIENT_INTENSITY_MAX, + AMBIENT_INTENSITY_MIN, + AMBIENT_INTENSITY_STEP, + AMBIENT_LIGHT_COLOR, + LIGHTING_DEFAULTS, + SUN_INTENSITY_MAX, + SUN_INTENSITY_MIN, + SUN_INTENSITY_STEP, + SUN_LIGHT_COLOR, + SUN_X_MAX, + SUN_X_MIN, + SUN_X_STEP, + SUN_Y_MAX, + SUN_Y_MIN, + SUN_Y_STEP, + SUN_Z_MAX, + SUN_Z_MIN, + SUN_Z_STEP, +} from "@/data/lightingConfig"; import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; type LightingState = { @@ -11,24 +31,40 @@ type LightingState = { sunZ: number; }; -const LIGHTING_STATE: LightingState = { - ambientIntensity: 1.8, - sunIntensity: 2.8, - sunX: 60, - sunY: 80, - sunZ: 30, -}; +const LIGHTING_STATE: LightingState = { ...LIGHTING_DEFAULTS }; export function Lighting(): React.JSX.Element { const ambient = useRef(null); const sun = useRef(null); useDebugFolder("Lighting", (folder) => { - folder.add(LIGHTING_STATE, "ambientIntensity", 0, 5, 0.1).name("Ambient"); - folder.add(LIGHTING_STATE, "sunIntensity", 0, 8, 0.1).name("Sun Intensity"); - folder.add(LIGHTING_STATE, "sunX", -100, 100, 1).name("Sun X"); - folder.add(LIGHTING_STATE, "sunY", 0, 150, 1).name("Sun Y"); - folder.add(LIGHTING_STATE, "sunZ", -100, 100, 1).name("Sun Z"); + folder + .add( + LIGHTING_STATE, + "ambientIntensity", + AMBIENT_INTENSITY_MIN, + AMBIENT_INTENSITY_MAX, + AMBIENT_INTENSITY_STEP, + ) + .name("Ambient"); + folder + .add( + LIGHTING_STATE, + "sunIntensity", + SUN_INTENSITY_MIN, + SUN_INTENSITY_MAX, + SUN_INTENSITY_STEP, + ) + .name("Sun Intensity"); + folder + .add(LIGHTING_STATE, "sunX", SUN_X_MIN, SUN_X_MAX, SUN_X_STEP) + .name("Sun X"); + folder + .add(LIGHTING_STATE, "sunY", SUN_Y_MIN, SUN_Y_MAX, SUN_Y_STEP) + .name("Sun Y"); + folder + .add(LIGHTING_STATE, "sunZ", SUN_Z_MIN, SUN_Z_MAX, SUN_Z_STEP) + .name("Sun Z"); }); useFrame(() => { @@ -51,7 +87,7 @@ export function Lighting(): React.JSX.Element { diff --git a/src/world/Map.tsx b/src/world/Map.tsx index c811b62..17a7d8f 100644 --- a/src/world/Map.tsx +++ b/src/world/Map.tsx @@ -2,34 +2,25 @@ import { useEffect, useRef } from "react"; import { useThree } from "@react-three/fiber"; import { useGLTF } from "@react-three/drei"; import * as THREE from "three"; -import { Octree } from "three/addons/math/Octree.js"; +import { MAP_DEBUG_BOX_HELPER_COLOR } from "@/data/debugConfig"; +import { useOctreeGraphNode } from "@/hooks/useOctreeGraphNode"; +import type { OctreeReadyHandler } from "@/types/3d"; import { Debug } from "@/utils/debug/Debug"; const MAP_PATH = "/models/map/model.gltf"; interface MapProps { - onOctreeReady: (octree: Octree) => void; + onOctreeReady: OctreeReadyHandler; } export function Map({ onOctreeReady }: MapProps): React.JSX.Element { const { scene: gltfScene } = useGLTF(MAP_PATH); const groupRef = useRef(null); - const octreeBuilt = useRef(false); const boxHelpersRef = useRef([]); const { scene } = useThree(); - useEffect(() => { - if (octreeBuilt.current || !groupRef.current) return; - octreeBuilt.current = true; + useOctreeGraphNode(groupRef, onOctreeReady); - groupRef.current.updateMatrixWorld(true); - - const octree = new Octree(); - octree.fromGraphNode(groupRef.current); - onOctreeReady(octree); - }, [onOctreeReady]); - - // BoxHelper wireframes in debug mode — one per mesh in the model useEffect(() => { const debug = Debug.getInstance(); if (!debug.active || !groupRef.current) return; @@ -38,7 +29,7 @@ export function Map({ onOctreeReady }: MapProps): React.JSX.Element { groupRef.current.traverse((child) => { if (!(child instanceof THREE.Mesh)) return; - const helper = new THREE.BoxHelper(child, 0x00ff88); + const helper = new THREE.BoxHelper(child, MAP_DEBUG_BOX_HELPER_COLOR); scene.add(helper); helpers.push(helper); }); diff --git a/src/world/World.tsx b/src/world/World.tsx index 566ffb1..af7c39f 100644 --- a/src/world/World.tsx +++ b/src/world/World.tsx @@ -1,5 +1,9 @@ -import { useState, useCallback } from "react"; +import { useState } from "react"; import type { Octree } from "three/addons/math/Octree.js"; +import { + PLAYER_SPAWN_POSITION_GAME, + PLAYER_SPAWN_POSITION_PHYSICS, +} from "@/data/playerConfig"; import { useCameraMode } from "@/hooks/debug/useCameraMode"; import { useSceneMode } from "@/hooks/debug/useSceneMode"; import { DebugCameraControls } from "@/utils/debug/scene/DebugCameraControls"; @@ -14,7 +18,10 @@ export function World(): React.JSX.Element { const cameraMode = useCameraMode(); const sceneMode = useSceneMode(); const [octree, setOctree] = useState(null); - const onOctreeReady = useCallback((o: Octree) => setOctree(o), []); + const playerSpawnPosition = + sceneMode === "game" + ? PLAYER_SPAWN_POSITION_GAME + : PLAYER_SPAWN_POSITION_PHYSICS; return ( <> @@ -24,16 +31,13 @@ export function World(): React.JSX.Element { {cameraMode === "debug" ? : null} {sceneMode === "game" ? ( - + ) : ( - + )} {cameraMode !== "debug" ? ( - + ) : null} ); diff --git a/src/world/debug/TestScene.tsx b/src/world/debug/TestScene.tsx index 79de99f..1bf51cf 100644 --- a/src/world/debug/TestScene.tsx +++ b/src/world/debug/TestScene.tsx @@ -1,48 +1,89 @@ -import { useEffect, useRef } from "react"; +import { useRef } from "react"; import { Physics, RigidBody, CuboidCollider } from "@react-three/rapier"; import * as THREE from "three"; -import { Octree } from "three/addons/math/Octree.js"; -import { GrabCube } from "@/world/objects/GrabCube"; -import { TriggerSphere } from "@/world/objects/TriggerSphere"; +import { GrabbableObject } from "@/components/3d/GrabbableObject"; +import { TriggerObject } from "@/components/3d/TriggerObject"; +import { + TEST_SCENE_FLOOR_COLLIDER_HALF_EXTENTS, + TEST_SCENE_FLOOR_POSITION, + TEST_SCENE_FLOOR_SIZE, + TEST_SCENE_GRABBABLE_BOX_SIZE, + TEST_SCENE_GRABBABLE_COLOR, + TEST_SCENE_GRABBABLE_METALNESS, + TEST_SCENE_GRABBABLE_POSITION, + TEST_SCENE_GRABBABLE_ROUGHNESS, + TEST_SCENE_TRIGGER_COLOR, + TEST_SCENE_TRIGGER_METALNESS, + TEST_SCENE_TRIGGER_POSITION, + TEST_SCENE_TRIGGER_RADIUS, + TEST_SCENE_TRIGGER_ROUGHNESS, + TEST_SCENE_TRIGGER_SEGMENTS, + TEST_SCENE_TRIGGER_SOUND_PATH, +} from "@/data/testSceneConfig"; +import { useOctreeGraphNode } from "@/hooks/useOctreeGraphNode"; +import type { OctreeReadyHandler } from "@/types/3d"; interface TestSceneProps { - onOctreeReady: (octree: Octree) => void; + onOctreeReady: OctreeReadyHandler; } export function TestScene({ onOctreeReady, }: TestSceneProps): React.JSX.Element { const floorRef = useRef(null); - const octreeBuilt = useRef(false); - useEffect(() => { - if (octreeBuilt.current || !floorRef.current) return; - octreeBuilt.current = true; - - floorRef.current.updateMatrixWorld(true); - - const octree = new Octree(); - octree.fromGraphNode(floorRef.current); - onOctreeReady(octree); - }, [onOctreeReady]); + useOctreeGraphNode(floorRef, onOctreeReady); return ( <> - {/* Invisible floor mesh for Octree player collision */} - - + + - {/* Rapier physics for interactable objects */} - + - - + + + + + + + + + + + + + + ); diff --git a/src/world/objects/GrabCube.tsx b/src/world/objects/GrabCube.tsx deleted file mode 100644 index 22fb707..0000000 --- a/src/world/objects/GrabCube.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useRef } from "react"; -import { useFrame, useThree } from "@react-three/fiber"; -import type { RapierRigidBody } from "@react-three/rapier"; -import * as THREE from "three"; -import { InteractableObject } from "@/components/3d/InteractableObject"; -import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; - -const CUBE_SIZE = 0.5; -const HOLD_DISTANCE = 2; -const SPAWN_POSITION: [number, number, number] = [0, 1, -3]; - -const params = { stiffness: 15, throwBoost: 1.0 }; - -const _holdTarget = new THREE.Vector3(); -const _currentPos = new THREE.Vector3(); -const _velocity = new THREE.Vector3(); - -export function GrabCube(): React.JSX.Element { - const camera = useThree((state) => state.camera); - const rbRef = useRef(null); - const isHolding = useRef(false); - - useDebugFolder("GrabCube", (folder) => { - folder.add(params, "stiffness", 1, 50, 1).name("Hold stiffness"); - folder.add(params, "throwBoost", 0.5, 3.0, 0.1).name("Throw boost"); - }); - - useFrame(() => { - if (!isHolding.current || !rbRef.current) return; - - camera.getWorldDirection(_holdTarget); - _holdTarget.multiplyScalar(HOLD_DISTANCE).add(camera.position); - - const t = rbRef.current.translation(); - _currentPos.set(t.x, t.y, t.z); - - _velocity - .subVectors(_holdTarget, _currentPos) - .multiplyScalar(params.stiffness); - - rbRef.current.setLinvel( - { x: _velocity.x, y: _velocity.y, z: _velocity.z }, - true, - ); - rbRef.current.setAngvel({ x: 0, y: 0, z: 0 }, true); - }); - - return ( - { - isHolding.current = true; - }} - onRelease={() => { - isHolding.current = false; - if (rbRef.current && params.throwBoost !== 1.0) { - const v = rbRef.current.linvel(); - rbRef.current.setLinvel( - { - x: v.x * params.throwBoost, - y: v.y * params.throwBoost, - z: v.z * params.throwBoost, - }, - true, - ); - } - }} - > - - - - - - ); -} diff --git a/src/world/objects/TriggerSphere.tsx b/src/world/objects/TriggerSphere.tsx deleted file mode 100644 index 8119dd7..0000000 --- a/src/world/objects/TriggerSphere.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { AudioManager } from "@/stateManager/AudioManager"; -import { InteractableObject } from "@/components/3d/InteractableObject"; - -const SPHERE_RADIUS = 0.4; -const SPAWN_POSITION: [number, number, number] = [3, 2, -3]; -const SOUND_PATH = "/sounds/fa.mp3"; - -interface TriggerSphereProps { - soundPath?: string; -} - -export function TriggerSphere({ - soundPath = SOUND_PATH, -}: TriggerSphereProps): React.JSX.Element { - return ( - { - AudioManager.getInstance().playSound(soundPath); - }} - > - - - - - - ); -} diff --git a/src/world/player/PlayerCamera.tsx b/src/world/player/PlayerCamera.tsx index e22d43c..483eed8 100644 --- a/src/world/player/PlayerCamera.tsx +++ b/src/world/player/PlayerCamera.tsx @@ -1,9 +1,6 @@ import { useEffect } from "react"; import { PointerLockControls } from "@react-three/drei"; -export const PLAYER_EYE_HEIGHT = 1.75; -export const PLAYER_CAPSULE_RADIUS = 0.35; - export function PlayerCamera(): React.JSX.Element { useEffect(() => { return () => { diff --git a/src/world/player/PlayerComponent.tsx b/src/world/player/PlayerComponent.tsx index 28b914b..3b950e0 100644 --- a/src/world/player/PlayerComponent.tsx +++ b/src/world/player/PlayerComponent.tsx @@ -1,28 +1,29 @@ import { useEffect } from "react"; import { useThree } from "@react-three/fiber"; import type { Octree } from "three/addons/math/Octree.js"; +import type { Vector3Tuple } from "@/types/3d"; import { PlayerCamera } from "@/world/player/PlayerCamera"; import { PlayerController } from "@/world/player/PlayerController"; interface PlayerComponentProps { - octree?: Octree | null; - spawnY?: number; + octree: Octree | null; + spawnPosition: Vector3Tuple; } export function PlayerComponent({ - octree = null, - spawnY = 100, + spawnPosition, + octree, }: PlayerComponentProps): React.JSX.Element { const camera = useThree((state) => state.camera); useEffect(() => { - camera.position.set(0, spawnY, 0); - }, [camera, spawnY]); + camera.position.set(...spawnPosition); + }, [camera, spawnPosition]); return ( <> - + ); } diff --git a/src/world/player/PlayerController.tsx b/src/world/player/PlayerController.tsx index e21a797..341c6d9 100644 --- a/src/world/player/PlayerController.tsx +++ b/src/world/player/PlayerController.tsx @@ -3,16 +3,28 @@ import { useFrame, useThree } from "@react-three/fiber"; import * as THREE from "three"; import { Capsule } from "three/addons/math/Capsule.js"; import type { Octree } from "three/addons/math/Octree.js"; -import { InteractionManager } from "@/stateManager/InteractionManager"; import { - PLAYER_EYE_HEIGHT, + INTERACT_KEY, + JUMP_KEY, + MOVE_BACKWARD_KEY, + MOVE_FORWARD_KEY, + MOVE_LEFT_KEY, + MOVE_RIGHT_KEY, + PRIMARY_INTERACT_MOUSE_BUTTON, +} from "@/data/keybindings"; +import { + PLAYER_ACCELERATION_MULTIPLIER, + PLAYER_AIR_CONTROL_FACTOR, PLAYER_CAPSULE_RADIUS, -} from "@/world/player/PlayerCamera"; - -const WALK_SPEED = 11; -const AIR_CONTROL = 0.35; -const JUMP_SPEED = 9; -const GRAVITY = 30; + PLAYER_EYE_HEIGHT, + PLAYER_GRAVITY, + PLAYER_JUMP_SPEED, + PLAYER_MAX_DELTA, + PLAYER_WALK_SPEED, + PLAYER_XZ_DAMPING_FACTOR, +} from "@/data/playerConfig"; +import { InteractionManager } from "@/stateManager/InteractionManager"; +import type { Vector3Tuple } from "@/types/3d"; type Keys = { forward: boolean; @@ -32,6 +44,7 @@ const DEFAULT_KEYS: Keys = { interface PlayerControllerProps { octree: Octree | null; + spawnPosition: Vector3Tuple; } const _forward = new THREE.Vector3(); @@ -39,15 +52,18 @@ const _right = new THREE.Vector3(); const _wishDir = new THREE.Vector3(); const _up = new THREE.Vector3(0, 1, 0); const _translateVec = new THREE.Vector3(); +const _collisionCorrection = new THREE.Vector3(); -export function PlayerController({ octree }: PlayerControllerProps): null { +export function PlayerController({ + octree, + spawnPosition, +}: PlayerControllerProps): null { const camera = useThree((state) => state.camera); const keys = useRef({ ...DEFAULT_KEYS }); const velocity = useRef(new THREE.Vector3()); const onFloor = useRef(false); const wantsJump = useRef(false); - // Capsule: start = feet, end = eyes const capsule = useRef( new Capsule( new THREE.Vector3(0, PLAYER_CAPSULE_RADIUS, 0), @@ -56,39 +72,40 @@ export function PlayerController({ octree }: PlayerControllerProps): null { ), ); - // Sync capsule to camera spawn position on mount useEffect(() => { - const spawnY = camera.position.y; capsule.current.start.set( - 0, - spawnY - PLAYER_EYE_HEIGHT + PLAYER_CAPSULE_RADIUS, - 0, + spawnPosition[0], + spawnPosition[1] - PLAYER_EYE_HEIGHT + PLAYER_CAPSULE_RADIUS, + spawnPosition[2], ); - capsule.current.end.set(0, spawnY, 0); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + capsule.current.end.set(...spawnPosition); + velocity.current.set(0, 0, 0); + onFloor.current = false; + wantsJump.current = false; + camera.position.copy(capsule.current.end); + }, [camera, spawnPosition]); useEffect(() => { const interaction = InteractionManager.getInstance(); const handleKeyDown = (event: KeyboardEvent): void => { switch (event.key.toLowerCase()) { - case "z": + case MOVE_FORWARD_KEY: keys.current.forward = true; break; - case "s": + case MOVE_BACKWARD_KEY: keys.current.backward = true; break; - case "q": + case MOVE_LEFT_KEY: keys.current.left = true; break; - case "d": + case MOVE_RIGHT_KEY: keys.current.right = true; break; - case " ": + case JUMP_KEY: wantsJump.current = true; break; - case "e": + case INTERACT_KEY: if (interaction.getState().focused?.kind === "trigger") { interaction.pressInteract(); } @@ -101,23 +118,18 @@ export function PlayerController({ octree }: PlayerControllerProps): null { const handleKeyUp = (event: KeyboardEvent): void => { switch (event.key.toLowerCase()) { - case "z": + case MOVE_FORWARD_KEY: keys.current.forward = false; break; - case "s": + case MOVE_BACKWARD_KEY: keys.current.backward = false; break; - case "q": + case MOVE_LEFT_KEY: keys.current.left = false; break; - case "d": + case MOVE_RIGHT_KEY: keys.current.right = false; break; - case "e": - if (interaction.getState().focused?.kind === "trigger") { - interaction.releaseInteract(); - } - break; default: return; } @@ -125,14 +137,14 @@ export function PlayerController({ octree }: PlayerControllerProps): null { }; const handleMouseDown = (event: MouseEvent): void => { - if (event.button !== 0) return; + if (event.button !== PRIMARY_INTERACT_MOUSE_BUTTON) return; if (interaction.getState().focused?.kind === "grab") { interaction.pressInteract(); } }; const handleMouseUp = (event: MouseEvent): void => { - if (event.button !== 0) return; + if (event.button !== PRIMARY_INTERACT_MOUSE_BUTTON) return; if (interaction.getState().holding) { interaction.releaseInteract(); } @@ -153,10 +165,8 @@ export function PlayerController({ octree }: PlayerControllerProps): null { }, []); useFrame((_, delta) => { - // Clamp delta so physics don't explode on tab focus regain - const dt = Math.min(delta, 0.05); + const dt = Math.min(delta, PLAYER_MAX_DELTA); - // Compute wish direction from camera yaw (XZ only) camera.getWorldDirection(_forward); _forward.setY(0); if (_forward.lengthSq() > 0) { @@ -171,33 +181,32 @@ export function PlayerController({ octree }: PlayerControllerProps): null { if (keys.current.right) _wishDir.add(_right); if (_wishDir.lengthSq() > 0) _wishDir.normalize(); - // Accelerate horizontally - const accel = onFloor.current ? WALK_SPEED : WALK_SPEED * AIR_CONTROL; - velocity.current.x += _wishDir.x * accel * dt * 9; - velocity.current.z += _wishDir.z * accel * dt * 9; + const accel = onFloor.current + ? PLAYER_WALK_SPEED + : PLAYER_WALK_SPEED * PLAYER_AIR_CONTROL_FACTOR; + velocity.current.x += + _wishDir.x * accel * dt * PLAYER_ACCELERATION_MULTIPLIER; + velocity.current.z += + _wishDir.z * accel * dt * PLAYER_ACCELERATION_MULTIPLIER; - // Exponential damping on XZ - const damping = Math.exp(-8 * dt); + const damping = Math.exp(-PLAYER_XZ_DAMPING_FACTOR * dt); velocity.current.x *= damping; velocity.current.z *= damping; - // Gravity + jump if (onFloor.current) { velocity.current.y = Math.max(0, velocity.current.y); if (wantsJump.current) { - velocity.current.y = JUMP_SPEED; + velocity.current.y = PLAYER_JUMP_SPEED; onFloor.current = false; } } else { - velocity.current.y -= GRAVITY * dt; + velocity.current.y -= PLAYER_GRAVITY * dt; } wantsJump.current = false; - // Move capsule _translateVec.copy(velocity.current).multiplyScalar(dt); capsule.current.translate(_translateVec); - // Resolve collisions against octree if (octree) { const result = octree.capsuleIntersect(capsule.current); onFloor.current = false; @@ -206,21 +215,18 @@ export function PlayerController({ octree }: PlayerControllerProps): null { onFloor.current = result.normal.y > 0; if (!onFloor.current) { - // Cancel velocity component going into the wall const vn = result.normal.dot(velocity.current); velocity.current.addScaledVector(result.normal, -vn); } else { velocity.current.y = Math.max(0, velocity.current.y); } - // Push capsule out of geometry capsule.current.translate( - result.normal.clone().multiplyScalar(result.depth), + _collisionCorrection.copy(result.normal).multiplyScalar(result.depth), ); } } - // Sync camera to capsule top (eye position) camera.position.copy(capsule.current.end); }); diff --git a/vite.config.ts b/vite.config.ts index 4078792..3ff34a5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -28,13 +28,13 @@ const saveMapPlugin = () => ({ }); }, }); +import { fileURLToPath } from "node:url"; -// https://vite.dev/config/ export default defineConfig({ plugins: [react(), saveMapPlugin()], resolve: { alias: { - "@": path.resolve(__dirname, "./src"), + "@": fileURLToPath(new URL("./src", import.meta.url)), }, }, });