[READ-ONLY] Mirror of https://github.com/flo-bit/tiny-creature. three.js journey #16 - tamagotchi flo-bit.dev/tiny-creature/
0

Configure Feed

Select the types of activity you want to include in your feed.

first commit

Florian (Feb 3, 2025, 12:48 PM +0100) 9a6ee6b3

+2170
+24
.gitignore
··· 1 + # Logs 2 + logs 3 + *.log 4 + npm-debug.log* 5 + yarn-debug.log* 6 + yarn-error.log* 7 + pnpm-debug.log* 8 + lerna-debug.log* 9 + 10 + node_modules 11 + dist 12 + dist-ssr 13 + *.local 14 + 15 + # Editor directories and files 16 + .vscode/* 17 + !.vscode/extensions.json 18 + .idea 19 + .DS_Store 20 + *.suo 21 + *.ntvs* 22 + *.njsproj 23 + *.sln 24 + *.sw?
+1
.prettierrc
··· 1 + {}
+1
app.css
··· 1 + @import "tailwindcss";
+30
index.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 + <title>tiny creature</title> 7 + <link rel="stylesheet" href="app.css" /> 8 + </head> 9 + <body class="bg-stone-950"> 10 + <div id="app"></div> 11 + <script type="module" src="/src/main.ts"></script> 12 + 13 + <div class="text-stone-100 text-sm absolute top-1 left-1 z-10"> 14 + work in progress for 15 + <a 16 + target="_blank" 17 + href="https://threejs-journey.com/challenges/tamagotchi" 18 + class="text-pink-400 font-medium" 19 + >three.js journey #016 20 + </a> 21 + by 22 + <a 23 + target="_blank" 24 + href="https://flo-bit.dev" 25 + class="text-pink-400 font-medium" 26 + >flo-bit</a 27 + > 28 + </div> 29 + </body> 30 + </html>
+1467
package-lock.json
··· 1 + { 2 + "name": "tamagotchi", 3 + "version": "0.0.0", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "tamagotchi", 9 + "version": "0.0.0", 10 + "dependencies": { 11 + "@tailwindcss/vite": "^4.0.3", 12 + "@types/three": "^0.173.0", 13 + "pixi.js": "^8.7.3", 14 + "prettier": "^3.4.2", 15 + "tailwindcss": "^4.0.3", 16 + "three": "^0.173.0", 17 + "wiggle": "^0.0.17" 18 + }, 19 + "devDependencies": { 20 + "typescript": "~5.6.2", 21 + "vite": "^6.0.5" 22 + } 23 + }, 24 + "node_modules/@esbuild/aix-ppc64": { 25 + "version": "0.24.2", 26 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", 27 + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", 28 + "cpu": [ 29 + "ppc64" 30 + ], 31 + "optional": true, 32 + "os": [ 33 + "aix" 34 + ], 35 + "engines": { 36 + "node": ">=18" 37 + } 38 + }, 39 + "node_modules/@esbuild/android-arm": { 40 + "version": "0.24.2", 41 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", 42 + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", 43 + "cpu": [ 44 + "arm" 45 + ], 46 + "optional": true, 47 + "os": [ 48 + "android" 49 + ], 50 + "engines": { 51 + "node": ">=18" 52 + } 53 + }, 54 + "node_modules/@esbuild/android-arm64": { 55 + "version": "0.24.2", 56 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", 57 + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", 58 + "cpu": [ 59 + "arm64" 60 + ], 61 + "optional": true, 62 + "os": [ 63 + "android" 64 + ], 65 + "engines": { 66 + "node": ">=18" 67 + } 68 + }, 69 + "node_modules/@esbuild/android-x64": { 70 + "version": "0.24.2", 71 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", 72 + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", 73 + "cpu": [ 74 + "x64" 75 + ], 76 + "optional": true, 77 + "os": [ 78 + "android" 79 + ], 80 + "engines": { 81 + "node": ">=18" 82 + } 83 + }, 84 + "node_modules/@esbuild/darwin-arm64": { 85 + "version": "0.24.2", 86 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", 87 + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", 88 + "cpu": [ 89 + "arm64" 90 + ], 91 + "optional": true, 92 + "os": [ 93 + "darwin" 94 + ], 95 + "engines": { 96 + "node": ">=18" 97 + } 98 + }, 99 + "node_modules/@esbuild/darwin-x64": { 100 + "version": "0.24.2", 101 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", 102 + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", 103 + "cpu": [ 104 + "x64" 105 + ], 106 + "optional": true, 107 + "os": [ 108 + "darwin" 109 + ], 110 + "engines": { 111 + "node": ">=18" 112 + } 113 + }, 114 + "node_modules/@esbuild/freebsd-arm64": { 115 + "version": "0.24.2", 116 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", 117 + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", 118 + "cpu": [ 119 + "arm64" 120 + ], 121 + "optional": true, 122 + "os": [ 123 + "freebsd" 124 + ], 125 + "engines": { 126 + "node": ">=18" 127 + } 128 + }, 129 + "node_modules/@esbuild/freebsd-x64": { 130 + "version": "0.24.2", 131 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", 132 + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", 133 + "cpu": [ 134 + "x64" 135 + ], 136 + "optional": true, 137 + "os": [ 138 + "freebsd" 139 + ], 140 + "engines": { 141 + "node": ">=18" 142 + } 143 + }, 144 + "node_modules/@esbuild/linux-arm": { 145 + "version": "0.24.2", 146 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", 147 + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", 148 + "cpu": [ 149 + "arm" 150 + ], 151 + "optional": true, 152 + "os": [ 153 + "linux" 154 + ], 155 + "engines": { 156 + "node": ">=18" 157 + } 158 + }, 159 + "node_modules/@esbuild/linux-arm64": { 160 + "version": "0.24.2", 161 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", 162 + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", 163 + "cpu": [ 164 + "arm64" 165 + ], 166 + "optional": true, 167 + "os": [ 168 + "linux" 169 + ], 170 + "engines": { 171 + "node": ">=18" 172 + } 173 + }, 174 + "node_modules/@esbuild/linux-ia32": { 175 + "version": "0.24.2", 176 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", 177 + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", 178 + "cpu": [ 179 + "ia32" 180 + ], 181 + "optional": true, 182 + "os": [ 183 + "linux" 184 + ], 185 + "engines": { 186 + "node": ">=18" 187 + } 188 + }, 189 + "node_modules/@esbuild/linux-loong64": { 190 + "version": "0.24.2", 191 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", 192 + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", 193 + "cpu": [ 194 + "loong64" 195 + ], 196 + "optional": true, 197 + "os": [ 198 + "linux" 199 + ], 200 + "engines": { 201 + "node": ">=18" 202 + } 203 + }, 204 + "node_modules/@esbuild/linux-mips64el": { 205 + "version": "0.24.2", 206 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", 207 + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", 208 + "cpu": [ 209 + "mips64el" 210 + ], 211 + "optional": true, 212 + "os": [ 213 + "linux" 214 + ], 215 + "engines": { 216 + "node": ">=18" 217 + } 218 + }, 219 + "node_modules/@esbuild/linux-ppc64": { 220 + "version": "0.24.2", 221 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", 222 + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", 223 + "cpu": [ 224 + "ppc64" 225 + ], 226 + "optional": true, 227 + "os": [ 228 + "linux" 229 + ], 230 + "engines": { 231 + "node": ">=18" 232 + } 233 + }, 234 + "node_modules/@esbuild/linux-riscv64": { 235 + "version": "0.24.2", 236 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", 237 + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", 238 + "cpu": [ 239 + "riscv64" 240 + ], 241 + "optional": true, 242 + "os": [ 243 + "linux" 244 + ], 245 + "engines": { 246 + "node": ">=18" 247 + } 248 + }, 249 + "node_modules/@esbuild/linux-s390x": { 250 + "version": "0.24.2", 251 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", 252 + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", 253 + "cpu": [ 254 + "s390x" 255 + ], 256 + "optional": true, 257 + "os": [ 258 + "linux" 259 + ], 260 + "engines": { 261 + "node": ">=18" 262 + } 263 + }, 264 + "node_modules/@esbuild/linux-x64": { 265 + "version": "0.24.2", 266 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", 267 + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", 268 + "cpu": [ 269 + "x64" 270 + ], 271 + "optional": true, 272 + "os": [ 273 + "linux" 274 + ], 275 + "engines": { 276 + "node": ">=18" 277 + } 278 + }, 279 + "node_modules/@esbuild/netbsd-arm64": { 280 + "version": "0.24.2", 281 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", 282 + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", 283 + "cpu": [ 284 + "arm64" 285 + ], 286 + "optional": true, 287 + "os": [ 288 + "netbsd" 289 + ], 290 + "engines": { 291 + "node": ">=18" 292 + } 293 + }, 294 + "node_modules/@esbuild/netbsd-x64": { 295 + "version": "0.24.2", 296 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", 297 + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", 298 + "cpu": [ 299 + "x64" 300 + ], 301 + "optional": true, 302 + "os": [ 303 + "netbsd" 304 + ], 305 + "engines": { 306 + "node": ">=18" 307 + } 308 + }, 309 + "node_modules/@esbuild/openbsd-arm64": { 310 + "version": "0.24.2", 311 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", 312 + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", 313 + "cpu": [ 314 + "arm64" 315 + ], 316 + "optional": true, 317 + "os": [ 318 + "openbsd" 319 + ], 320 + "engines": { 321 + "node": ">=18" 322 + } 323 + }, 324 + "node_modules/@esbuild/openbsd-x64": { 325 + "version": "0.24.2", 326 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", 327 + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", 328 + "cpu": [ 329 + "x64" 330 + ], 331 + "optional": true, 332 + "os": [ 333 + "openbsd" 334 + ], 335 + "engines": { 336 + "node": ">=18" 337 + } 338 + }, 339 + "node_modules/@esbuild/sunos-x64": { 340 + "version": "0.24.2", 341 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", 342 + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", 343 + "cpu": [ 344 + "x64" 345 + ], 346 + "optional": true, 347 + "os": [ 348 + "sunos" 349 + ], 350 + "engines": { 351 + "node": ">=18" 352 + } 353 + }, 354 + "node_modules/@esbuild/win32-arm64": { 355 + "version": "0.24.2", 356 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", 357 + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", 358 + "cpu": [ 359 + "arm64" 360 + ], 361 + "optional": true, 362 + "os": [ 363 + "win32" 364 + ], 365 + "engines": { 366 + "node": ">=18" 367 + } 368 + }, 369 + "node_modules/@esbuild/win32-ia32": { 370 + "version": "0.24.2", 371 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", 372 + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", 373 + "cpu": [ 374 + "ia32" 375 + ], 376 + "optional": true, 377 + "os": [ 378 + "win32" 379 + ], 380 + "engines": { 381 + "node": ">=18" 382 + } 383 + }, 384 + "node_modules/@esbuild/win32-x64": { 385 + "version": "0.24.2", 386 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", 387 + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", 388 + "cpu": [ 389 + "x64" 390 + ], 391 + "optional": true, 392 + "os": [ 393 + "win32" 394 + ], 395 + "engines": { 396 + "node": ">=18" 397 + } 398 + }, 399 + "node_modules/@pixi/colord": { 400 + "version": "2.9.6", 401 + "resolved": "https://registry.npmjs.org/@pixi/colord/-/colord-2.9.6.tgz", 402 + "integrity": "sha512-nezytU2pw587fQstUu1AsJZDVEynjskwOL+kibwcdxsMBFqPsFFNA7xl0ii/gXuDi6M0xj3mfRJj8pBSc2jCfA==" 403 + }, 404 + "node_modules/@rollup/rollup-android-arm-eabi": { 405 + "version": "4.34.0", 406 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.0.tgz", 407 + "integrity": "sha512-Eeao7ewDq79jVEsrtWIj5RNqB8p2knlm9fhR6uJ2gqP7UfbLrTrxevudVrEPDM7Wkpn/HpRC2QfazH7MXLz3vQ==", 408 + "cpu": [ 409 + "arm" 410 + ], 411 + "optional": true, 412 + "os": [ 413 + "android" 414 + ] 415 + }, 416 + "node_modules/@rollup/rollup-android-arm64": { 417 + "version": "4.34.0", 418 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.0.tgz", 419 + "integrity": "sha512-yVh0Kf1f0Fq4tWNf6mWcbQBCLDpDrDEl88lzPgKhrgTcDrTtlmun92ywEF9dCjmYO3EFiSuJeeo9cYRxl2FswA==", 420 + "cpu": [ 421 + "arm64" 422 + ], 423 + "optional": true, 424 + "os": [ 425 + "android" 426 + ] 427 + }, 428 + "node_modules/@rollup/rollup-darwin-arm64": { 429 + "version": "4.34.0", 430 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.0.tgz", 431 + "integrity": "sha512-gCs0ErAZ9s0Osejpc3qahTsqIPUDjSKIyxK/0BGKvL+Tn0n3Kwvj8BrCv7Y5sR1Ypz1K2qz9Ny0VvkVyoXBVUQ==", 432 + "cpu": [ 433 + "arm64" 434 + ], 435 + "optional": true, 436 + "os": [ 437 + "darwin" 438 + ] 439 + }, 440 + "node_modules/@rollup/rollup-darwin-x64": { 441 + "version": "4.34.0", 442 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.0.tgz", 443 + "integrity": "sha512-aIB5Anc8hngk15t3GUkiO4pv42ykXHfmpXGS+CzM9CTyiWyT8HIS5ygRAy7KcFb/wiw4Br+vh1byqcHRTfq2tQ==", 444 + "cpu": [ 445 + "x64" 446 + ], 447 + "optional": true, 448 + "os": [ 449 + "darwin" 450 + ] 451 + }, 452 + "node_modules/@rollup/rollup-freebsd-arm64": { 453 + "version": "4.34.0", 454 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.0.tgz", 455 + "integrity": "sha512-kpdsUdMlVJMRMaOf/tIvxk8TQdzHhY47imwmASOuMajg/GXpw8GKNd8LNwIHE5Yd1onehNpcUB9jHY6wgw9nHQ==", 456 + "cpu": [ 457 + "arm64" 458 + ], 459 + "optional": true, 460 + "os": [ 461 + "freebsd" 462 + ] 463 + }, 464 + "node_modules/@rollup/rollup-freebsd-x64": { 465 + "version": "4.34.0", 466 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.0.tgz", 467 + "integrity": "sha512-D0RDyHygOBCQiqookcPevrvgEarN0CttBecG4chOeIYCNtlKHmf5oi5kAVpXV7qs0Xh/WO2RnxeicZPtT50V0g==", 468 + "cpu": [ 469 + "x64" 470 + ], 471 + "optional": true, 472 + "os": [ 473 + "freebsd" 474 + ] 475 + }, 476 + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 477 + "version": "4.34.0", 478 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.0.tgz", 479 + "integrity": "sha512-mCIw8j5LPDXmCOW8mfMZwT6F/Kza03EnSr4wGYEswrEfjTfVsFOxvgYfuRMxTuUF/XmRb9WSMD5GhCWDe2iNrg==", 480 + "cpu": [ 481 + "arm" 482 + ], 483 + "optional": true, 484 + "os": [ 485 + "linux" 486 + ] 487 + }, 488 + "node_modules/@rollup/rollup-linux-arm-musleabihf": { 489 + "version": "4.34.0", 490 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.0.tgz", 491 + "integrity": "sha512-AwwldAu4aCJPob7zmjuDUMvvuatgs8B/QiVB0KwkUarAcPB3W+ToOT+18TQwY4z09Al7G0BvCcmLRop5zBLTag==", 492 + "cpu": [ 493 + "arm" 494 + ], 495 + "optional": true, 496 + "os": [ 497 + "linux" 498 + ] 499 + }, 500 + "node_modules/@rollup/rollup-linux-arm64-gnu": { 501 + "version": "4.34.0", 502 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.0.tgz", 503 + "integrity": "sha512-e7kDUGVP+xw05pV65ZKb0zulRploU3gTu6qH1qL58PrULDGxULIS0OSDQJLH7WiFnpd3ZKUU4VM3u/Z7Zw+e7Q==", 504 + "cpu": [ 505 + "arm64" 506 + ], 507 + "optional": true, 508 + "os": [ 509 + "linux" 510 + ] 511 + }, 512 + "node_modules/@rollup/rollup-linux-arm64-musl": { 513 + "version": "4.34.0", 514 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.0.tgz", 515 + "integrity": "sha512-SXYJw3zpwHgaBqTXeAZ31qfW/v50wq4HhNVvKFhRr5MnptRX2Af4KebLWR1wpxGJtLgfS2hEPuALRIY3LPAAcA==", 516 + "cpu": [ 517 + "arm64" 518 + ], 519 + "optional": true, 520 + "os": [ 521 + "linux" 522 + ] 523 + }, 524 + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { 525 + "version": "4.34.0", 526 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.0.tgz", 527 + "integrity": "sha512-e5XiCinINCI4RdyU3sFyBH4zzz7LiQRvHqDtRe9Dt8o/8hTBaYpdPimayF00eY2qy5j4PaaWK0azRgUench6WQ==", 528 + "cpu": [ 529 + "loong64" 530 + ], 531 + "optional": true, 532 + "os": [ 533 + "linux" 534 + ] 535 + }, 536 + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { 537 + "version": "4.34.0", 538 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.0.tgz", 539 + "integrity": "sha512-3SWN3e0bAsm9ToprLFBSro8nJe6YN+5xmB11N4FfNf92wvLye/+Rh5JGQtKOpwLKt6e61R1RBc9g+luLJsc23A==", 540 + "cpu": [ 541 + "ppc64" 542 + ], 543 + "optional": true, 544 + "os": [ 545 + "linux" 546 + ] 547 + }, 548 + "node_modules/@rollup/rollup-linux-riscv64-gnu": { 549 + "version": "4.34.0", 550 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.0.tgz", 551 + "integrity": "sha512-B1Oqt3GLh7qmhvfnc2WQla4NuHlcxAD5LyueUi5WtMc76ZWY+6qDtQYqnxARx9r+7mDGfamD+8kTJO0pKUJeJA==", 552 + "cpu": [ 553 + "riscv64" 554 + ], 555 + "optional": true, 556 + "os": [ 557 + "linux" 558 + ] 559 + }, 560 + "node_modules/@rollup/rollup-linux-s390x-gnu": { 561 + "version": "4.34.0", 562 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.0.tgz", 563 + "integrity": "sha512-UfUCo0h/uj48Jq2lnhX0AOhZPSTAq3Eostas+XZ+GGk22pI+Op1Y6cxQ1JkUuKYu2iU+mXj1QjPrZm9nNWV9rg==", 564 + "cpu": [ 565 + "s390x" 566 + ], 567 + "optional": true, 568 + "os": [ 569 + "linux" 570 + ] 571 + }, 572 + "node_modules/@rollup/rollup-linux-x64-gnu": { 573 + "version": "4.34.0", 574 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.0.tgz", 575 + "integrity": "sha512-chZLTUIPbgcpm+Z7ALmomXW8Zh+wE2icrG+K6nt/HenPLmtwCajhQC5flNSk1Xy5EDMt/QAOz2MhzfOfJOLSiA==", 576 + "cpu": [ 577 + "x64" 578 + ], 579 + "optional": true, 580 + "os": [ 581 + "linux" 582 + ] 583 + }, 584 + "node_modules/@rollup/rollup-linux-x64-musl": { 585 + "version": "4.34.0", 586 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.0.tgz", 587 + "integrity": "sha512-jo0UolK70O28BifvEsFD/8r25shFezl0aUk2t0VJzREWHkq19e+pcLu4kX5HiVXNz5qqkD+aAq04Ct8rkxgbyQ==", 588 + "cpu": [ 589 + "x64" 590 + ], 591 + "optional": true, 592 + "os": [ 593 + "linux" 594 + ] 595 + }, 596 + "node_modules/@rollup/rollup-win32-arm64-msvc": { 597 + "version": "4.34.0", 598 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.0.tgz", 599 + "integrity": "sha512-Vmg0NhAap2S54JojJchiu5An54qa6t/oKT7LmDaWggpIcaiL8WcWHEN6OQrfTdL6mQ2GFyH7j2T5/3YPEDOOGA==", 600 + "cpu": [ 601 + "arm64" 602 + ], 603 + "optional": true, 604 + "os": [ 605 + "win32" 606 + ] 607 + }, 608 + "node_modules/@rollup/rollup-win32-ia32-msvc": { 609 + "version": "4.34.0", 610 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.0.tgz", 611 + "integrity": "sha512-CV2aqhDDOsABKHKhNcs1SZFryffQf8vK2XrxP6lxC99ELZAdvsDgPklIBfd65R8R+qvOm1SmLaZ/Fdq961+m7A==", 612 + "cpu": [ 613 + "ia32" 614 + ], 615 + "optional": true, 616 + "os": [ 617 + "win32" 618 + ] 619 + }, 620 + "node_modules/@rollup/rollup-win32-x64-msvc": { 621 + "version": "4.34.0", 622 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.0.tgz", 623 + "integrity": "sha512-g2ASy1QwHP88y5KWvblUolJz9rN+i4ZOsYzkEwcNfaNooxNUXG+ON6F5xFo0NIItpHqxcdAyls05VXpBnludGw==", 624 + "cpu": [ 625 + "x64" 626 + ], 627 + "optional": true, 628 + "os": [ 629 + "win32" 630 + ] 631 + }, 632 + "node_modules/@tailwindcss/node": { 633 + "version": "4.0.3", 634 + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.3.tgz", 635 + "integrity": "sha512-QsVJokOl0pJ4AbJV33D2npvLcHGPWi5MOSZtrtE0GT3tSx+3D0JE2lokLA8yHS1x3oCY/3IyRyy7XX6tmzid7A==", 636 + "dependencies": { 637 + "enhanced-resolve": "^5.18.0", 638 + "jiti": "^2.4.2", 639 + "tailwindcss": "4.0.3" 640 + } 641 + }, 642 + "node_modules/@tailwindcss/oxide": { 643 + "version": "4.0.3", 644 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.3.tgz", 645 + "integrity": "sha512-FFcp3VNvRjjmFA39ORM27g2mbflMQljhvM7gxBAujHxUy4LXlKa6yMF9wbHdTbPqTONiCyyOYxccvJyVyI/XBg==", 646 + "engines": { 647 + "node": ">= 10" 648 + }, 649 + "optionalDependencies": { 650 + "@tailwindcss/oxide-android-arm64": "4.0.3", 651 + "@tailwindcss/oxide-darwin-arm64": "4.0.3", 652 + "@tailwindcss/oxide-darwin-x64": "4.0.3", 653 + "@tailwindcss/oxide-freebsd-x64": "4.0.3", 654 + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.3", 655 + "@tailwindcss/oxide-linux-arm64-gnu": "4.0.3", 656 + "@tailwindcss/oxide-linux-arm64-musl": "4.0.3", 657 + "@tailwindcss/oxide-linux-x64-gnu": "4.0.3", 658 + "@tailwindcss/oxide-linux-x64-musl": "4.0.3", 659 + "@tailwindcss/oxide-win32-arm64-msvc": "4.0.3", 660 + "@tailwindcss/oxide-win32-x64-msvc": "4.0.3" 661 + } 662 + }, 663 + "node_modules/@tailwindcss/oxide-android-arm64": { 664 + "version": "4.0.3", 665 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.3.tgz", 666 + "integrity": "sha512-S8XOTQuMnpijZRlPm5HBzPJjZ28quB+40LSRHjRnQF6rRYKsvpr1qkY7dfwsetNdd+kMLOMDsvmuT8WnqqETvg==", 667 + "cpu": [ 668 + "arm64" 669 + ], 670 + "optional": true, 671 + "os": [ 672 + "android" 673 + ], 674 + "engines": { 675 + "node": ">= 10" 676 + } 677 + }, 678 + "node_modules/@tailwindcss/oxide-darwin-arm64": { 679 + "version": "4.0.3", 680 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.3.tgz", 681 + "integrity": "sha512-smrY2DpzhXvgDhZtQlYAl8+vxJ04lv2/64C1eiRxvsRT2nkw/q+zA1/eAYKvUHat6cIuwqDku3QucmrUT6pCeg==", 682 + "cpu": [ 683 + "arm64" 684 + ], 685 + "optional": true, 686 + "os": [ 687 + "darwin" 688 + ], 689 + "engines": { 690 + "node": ">= 10" 691 + } 692 + }, 693 + "node_modules/@tailwindcss/oxide-darwin-x64": { 694 + "version": "4.0.3", 695 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.3.tgz", 696 + "integrity": "sha512-NTz8x/LcGUjpZAWUxz0ZuzHao90Wj9spoQgomwB+/hgceh5gcJDfvaBYqxLFpKzVglpnbDSq1Fg0p0zI4oa5Pg==", 697 + "cpu": [ 698 + "x64" 699 + ], 700 + "optional": true, 701 + "os": [ 702 + "darwin" 703 + ], 704 + "engines": { 705 + "node": ">= 10" 706 + } 707 + }, 708 + "node_modules/@tailwindcss/oxide-freebsd-x64": { 709 + "version": "4.0.3", 710 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.3.tgz", 711 + "integrity": "sha512-yQc9Q0JCOp3kkAV8gKgDctXO60IkQhHpqGB+KgOccDtD5UmN6Q5+gd+lcsDyQ7N8dRuK1fAud51xQpZJgKfm7g==", 712 + "cpu": [ 713 + "x64" 714 + ], 715 + "optional": true, 716 + "os": [ 717 + "freebsd" 718 + ], 719 + "engines": { 720 + "node": ">= 10" 721 + } 722 + }, 723 + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { 724 + "version": "4.0.3", 725 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.3.tgz", 726 + "integrity": "sha512-e1ivVMLSnxTOU1O3npnxN16FEyWM/g3SuH2pP6udxXwa0/SnSAijRwcAYRpqIlhVKujr158S8UeHxQjC4fGl4w==", 727 + "cpu": [ 728 + "arm" 729 + ], 730 + "optional": true, 731 + "os": [ 732 + "linux" 733 + ], 734 + "engines": { 735 + "node": ">= 10" 736 + } 737 + }, 738 + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { 739 + "version": "4.0.3", 740 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.3.tgz", 741 + "integrity": "sha512-PLrToqQqX6sdJ9DmMi8IxZWWrfjc9pdi9AEEPTrtMts3Jm9HBi1WqEeF1VwZZ2aW9TXloE5OwA35zuuq1Bhb/Q==", 742 + "cpu": [ 743 + "arm64" 744 + ], 745 + "optional": true, 746 + "os": [ 747 + "linux" 748 + ], 749 + "engines": { 750 + "node": ">= 10" 751 + } 752 + }, 753 + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { 754 + "version": "4.0.3", 755 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.3.tgz", 756 + "integrity": "sha512-YlzRxx7N1ampfgSKzEDw0iwDkJXUInR4cgNEqmR4TzHkU2Vhg59CGPJrTI7dxOBofD8+O35R13Nk9Ytyv0JUFg==", 757 + "cpu": [ 758 + "arm64" 759 + ], 760 + "optional": true, 761 + "os": [ 762 + "linux" 763 + ], 764 + "engines": { 765 + "node": ">= 10" 766 + } 767 + }, 768 + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { 769 + "version": "4.0.3", 770 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.3.tgz", 771 + "integrity": "sha512-Xfc3z/li6XkuD7Hs+Uk6pjyCXnfnd9zuQTKOyDTZJ544xc2yoMKUkuDw6Et9wb31MzU2/c0CIUpTDa71lL9KHw==", 772 + "cpu": [ 773 + "x64" 774 + ], 775 + "optional": true, 776 + "os": [ 777 + "linux" 778 + ], 779 + "engines": { 780 + "node": ">= 10" 781 + } 782 + }, 783 + "node_modules/@tailwindcss/oxide-linux-x64-musl": { 784 + "version": "4.0.3", 785 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.3.tgz", 786 + "integrity": "sha512-ugKVqKzwa/cjmqSQG17aS9DYrEcQ/a5NITcgmOr3JLW4Iz64C37eoDlkC8tIepD3S/Td/ywKAolTQ8fKbjEL4g==", 787 + "cpu": [ 788 + "x64" 789 + ], 790 + "optional": true, 791 + "os": [ 792 + "linux" 793 + ], 794 + "engines": { 795 + "node": ">= 10" 796 + } 797 + }, 798 + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { 799 + "version": "4.0.3", 800 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.3.tgz", 801 + "integrity": "sha512-qHPDMl+UUwsk1RMJMgAXvhraWqUUT+LR/tkXix5RA39UGxtTrHwsLIN1AhNxI5i2RFXAXfmFXDqZCdyQ4dWmAQ==", 802 + "cpu": [ 803 + "arm64" 804 + ], 805 + "optional": true, 806 + "os": [ 807 + "win32" 808 + ], 809 + "engines": { 810 + "node": ">= 10" 811 + } 812 + }, 813 + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { 814 + "version": "4.0.3", 815 + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.3.tgz", 816 + "integrity": "sha512-+ujwN4phBGyOsPyLgGgeCyUm4Mul+gqWVCIGuSXWgrx9xVUnf6LVXrw0BDBc9Aq1S2qMyOTX4OkCGbZeoIo8Qw==", 817 + "cpu": [ 818 + "x64" 819 + ], 820 + "optional": true, 821 + "os": [ 822 + "win32" 823 + ], 824 + "engines": { 825 + "node": ">= 10" 826 + } 827 + }, 828 + "node_modules/@tailwindcss/vite": { 829 + "version": "4.0.3", 830 + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.3.tgz", 831 + "integrity": "sha512-Qj6rSO+EvXnNDymloKZ11D54JJTnDrkRWJBzNHENDxjt0HtrCZJbSLIrcJ/WdaoU4othrel/oFqHpO/doxIS/Q==", 832 + "dependencies": { 833 + "@tailwindcss/node": "^4.0.3", 834 + "@tailwindcss/oxide": "^4.0.3", 835 + "lightningcss": "^1.29.1", 836 + "tailwindcss": "4.0.3" 837 + }, 838 + "peerDependencies": { 839 + "vite": "^5.2.0 || ^6" 840 + } 841 + }, 842 + "node_modules/@tweenjs/tween.js": { 843 + "version": "23.1.3", 844 + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", 845 + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==" 846 + }, 847 + "node_modules/@types/css-font-loading-module": { 848 + "version": "0.0.12", 849 + "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.12.tgz", 850 + "integrity": "sha512-x2tZZYkSxXqWvTDgveSynfjq/T2HyiZHXb00j/+gy19yp70PHCizM48XFdjBCWH7eHBD0R5i/pw9yMBP/BH5uA==" 851 + }, 852 + "node_modules/@types/earcut": { 853 + "version": "2.1.4", 854 + "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.4.tgz", 855 + "integrity": "sha512-qp3m9PPz4gULB9MhjGID7wpo3gJ4bTGXm7ltNDsmOvsPduTeHp8wSW9YckBj3mljeOh4F0m2z/0JKAALRKbmLQ==" 856 + }, 857 + "node_modules/@types/estree": { 858 + "version": "1.0.6", 859 + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", 860 + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" 861 + }, 862 + "node_modules/@types/stats.js": { 863 + "version": "0.17.3", 864 + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", 865 + "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==" 866 + }, 867 + "node_modules/@types/three": { 868 + "version": "0.173.0", 869 + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.173.0.tgz", 870 + "integrity": "sha512-KtNjfI/CRB6JVKIVeZM1R3GYDX2wkoV2itNcQu2j4d7qkhjGOuB+s2oF6jl9mztycDLGMtrAnJQYxInC8Bb20A==", 871 + "dependencies": { 872 + "@tweenjs/tween.js": "~23.1.3", 873 + "@types/stats.js": "*", 874 + "@types/webxr": "*", 875 + "@webgpu/types": "*", 876 + "fflate": "~0.8.2", 877 + "meshoptimizer": "~0.18.1" 878 + } 879 + }, 880 + "node_modules/@types/webxr": { 881 + "version": "0.5.21", 882 + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.21.tgz", 883 + "integrity": "sha512-geZIAtLzjGmgY2JUi6VxXdCrTb99A7yP49lxLr2Nm/uIK0PkkxcEi4OGhoGDO4pxCf3JwGz2GiJL2Ej4K2bKaA==" 884 + }, 885 + "node_modules/@webgpu/types": { 886 + "version": "0.1.54", 887 + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.54.tgz", 888 + "integrity": "sha512-81oaalC8LFrXjhsczomEQ0u3jG+TqE6V9QHLA8GNZq/Rnot0KDugu3LhSYSlie8tSdooAN1Hov05asrUUp9qgg==" 889 + }, 890 + "node_modules/@xmldom/xmldom": { 891 + "version": "0.8.10", 892 + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", 893 + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", 894 + "engines": { 895 + "node": ">=10.0.0" 896 + } 897 + }, 898 + "node_modules/detect-libc": { 899 + "version": "1.0.3", 900 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 901 + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", 902 + "bin": { 903 + "detect-libc": "bin/detect-libc.js" 904 + }, 905 + "engines": { 906 + "node": ">=0.10" 907 + } 908 + }, 909 + "node_modules/earcut": { 910 + "version": "2.2.4", 911 + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", 912 + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" 913 + }, 914 + "node_modules/enhanced-resolve": { 915 + "version": "5.18.0", 916 + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", 917 + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", 918 + "dependencies": { 919 + "graceful-fs": "^4.2.4", 920 + "tapable": "^2.2.0" 921 + }, 922 + "engines": { 923 + "node": ">=10.13.0" 924 + } 925 + }, 926 + "node_modules/esbuild": { 927 + "version": "0.24.2", 928 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", 929 + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", 930 + "hasInstallScript": true, 931 + "bin": { 932 + "esbuild": "bin/esbuild" 933 + }, 934 + "engines": { 935 + "node": ">=18" 936 + }, 937 + "optionalDependencies": { 938 + "@esbuild/aix-ppc64": "0.24.2", 939 + "@esbuild/android-arm": "0.24.2", 940 + "@esbuild/android-arm64": "0.24.2", 941 + "@esbuild/android-x64": "0.24.2", 942 + "@esbuild/darwin-arm64": "0.24.2", 943 + "@esbuild/darwin-x64": "0.24.2", 944 + "@esbuild/freebsd-arm64": "0.24.2", 945 + "@esbuild/freebsd-x64": "0.24.2", 946 + "@esbuild/linux-arm": "0.24.2", 947 + "@esbuild/linux-arm64": "0.24.2", 948 + "@esbuild/linux-ia32": "0.24.2", 949 + "@esbuild/linux-loong64": "0.24.2", 950 + "@esbuild/linux-mips64el": "0.24.2", 951 + "@esbuild/linux-ppc64": "0.24.2", 952 + "@esbuild/linux-riscv64": "0.24.2", 953 + "@esbuild/linux-s390x": "0.24.2", 954 + "@esbuild/linux-x64": "0.24.2", 955 + "@esbuild/netbsd-arm64": "0.24.2", 956 + "@esbuild/netbsd-x64": "0.24.2", 957 + "@esbuild/openbsd-arm64": "0.24.2", 958 + "@esbuild/openbsd-x64": "0.24.2", 959 + "@esbuild/sunos-x64": "0.24.2", 960 + "@esbuild/win32-arm64": "0.24.2", 961 + "@esbuild/win32-ia32": "0.24.2", 962 + "@esbuild/win32-x64": "0.24.2" 963 + } 964 + }, 965 + "node_modules/eventemitter3": { 966 + "version": "5.0.1", 967 + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", 968 + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" 969 + }, 970 + "node_modules/fflate": { 971 + "version": "0.8.2", 972 + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", 973 + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" 974 + }, 975 + "node_modules/fsevents": { 976 + "version": "2.3.3", 977 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 978 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 979 + "hasInstallScript": true, 980 + "optional": true, 981 + "os": [ 982 + "darwin" 983 + ], 984 + "engines": { 985 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 986 + } 987 + }, 988 + "node_modules/graceful-fs": { 989 + "version": "4.2.11", 990 + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 991 + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" 992 + }, 993 + "node_modules/ismobilejs": { 994 + "version": "1.1.1", 995 + "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz", 996 + "integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==" 997 + }, 998 + "node_modules/jiti": { 999 + "version": "2.4.2", 1000 + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", 1001 + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", 1002 + "bin": { 1003 + "jiti": "lib/jiti-cli.mjs" 1004 + } 1005 + }, 1006 + "node_modules/lightningcss": { 1007 + "version": "1.29.1", 1008 + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.1.tgz", 1009 + "integrity": "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==", 1010 + "dependencies": { 1011 + "detect-libc": "^1.0.3" 1012 + }, 1013 + "engines": { 1014 + "node": ">= 12.0.0" 1015 + }, 1016 + "funding": { 1017 + "type": "opencollective", 1018 + "url": "https://opencollective.com/parcel" 1019 + }, 1020 + "optionalDependencies": { 1021 + "lightningcss-darwin-arm64": "1.29.1", 1022 + "lightningcss-darwin-x64": "1.29.1", 1023 + "lightningcss-freebsd-x64": "1.29.1", 1024 + "lightningcss-linux-arm-gnueabihf": "1.29.1", 1025 + "lightningcss-linux-arm64-gnu": "1.29.1", 1026 + "lightningcss-linux-arm64-musl": "1.29.1", 1027 + "lightningcss-linux-x64-gnu": "1.29.1", 1028 + "lightningcss-linux-x64-musl": "1.29.1", 1029 + "lightningcss-win32-arm64-msvc": "1.29.1", 1030 + "lightningcss-win32-x64-msvc": "1.29.1" 1031 + } 1032 + }, 1033 + "node_modules/lightningcss-darwin-arm64": { 1034 + "version": "1.29.1", 1035 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.1.tgz", 1036 + "integrity": "sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==", 1037 + "cpu": [ 1038 + "arm64" 1039 + ], 1040 + "optional": true, 1041 + "os": [ 1042 + "darwin" 1043 + ], 1044 + "engines": { 1045 + "node": ">= 12.0.0" 1046 + }, 1047 + "funding": { 1048 + "type": "opencollective", 1049 + "url": "https://opencollective.com/parcel" 1050 + } 1051 + }, 1052 + "node_modules/lightningcss-darwin-x64": { 1053 + "version": "1.29.1", 1054 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.1.tgz", 1055 + "integrity": "sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==", 1056 + "cpu": [ 1057 + "x64" 1058 + ], 1059 + "optional": true, 1060 + "os": [ 1061 + "darwin" 1062 + ], 1063 + "engines": { 1064 + "node": ">= 12.0.0" 1065 + }, 1066 + "funding": { 1067 + "type": "opencollective", 1068 + "url": "https://opencollective.com/parcel" 1069 + } 1070 + }, 1071 + "node_modules/lightningcss-freebsd-x64": { 1072 + "version": "1.29.1", 1073 + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.1.tgz", 1074 + "integrity": "sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==", 1075 + "cpu": [ 1076 + "x64" 1077 + ], 1078 + "optional": true, 1079 + "os": [ 1080 + "freebsd" 1081 + ], 1082 + "engines": { 1083 + "node": ">= 12.0.0" 1084 + }, 1085 + "funding": { 1086 + "type": "opencollective", 1087 + "url": "https://opencollective.com/parcel" 1088 + } 1089 + }, 1090 + "node_modules/lightningcss-linux-arm-gnueabihf": { 1091 + "version": "1.29.1", 1092 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.1.tgz", 1093 + "integrity": "sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==", 1094 + "cpu": [ 1095 + "arm" 1096 + ], 1097 + "optional": true, 1098 + "os": [ 1099 + "linux" 1100 + ], 1101 + "engines": { 1102 + "node": ">= 12.0.0" 1103 + }, 1104 + "funding": { 1105 + "type": "opencollective", 1106 + "url": "https://opencollective.com/parcel" 1107 + } 1108 + }, 1109 + "node_modules/lightningcss-linux-arm64-gnu": { 1110 + "version": "1.29.1", 1111 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.1.tgz", 1112 + "integrity": "sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==", 1113 + "cpu": [ 1114 + "arm64" 1115 + ], 1116 + "optional": true, 1117 + "os": [ 1118 + "linux" 1119 + ], 1120 + "engines": { 1121 + "node": ">= 12.0.0" 1122 + }, 1123 + "funding": { 1124 + "type": "opencollective", 1125 + "url": "https://opencollective.com/parcel" 1126 + } 1127 + }, 1128 + "node_modules/lightningcss-linux-arm64-musl": { 1129 + "version": "1.29.1", 1130 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.1.tgz", 1131 + "integrity": "sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==", 1132 + "cpu": [ 1133 + "arm64" 1134 + ], 1135 + "optional": true, 1136 + "os": [ 1137 + "linux" 1138 + ], 1139 + "engines": { 1140 + "node": ">= 12.0.0" 1141 + }, 1142 + "funding": { 1143 + "type": "opencollective", 1144 + "url": "https://opencollective.com/parcel" 1145 + } 1146 + }, 1147 + "node_modules/lightningcss-linux-x64-gnu": { 1148 + "version": "1.29.1", 1149 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.1.tgz", 1150 + "integrity": "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==", 1151 + "cpu": [ 1152 + "x64" 1153 + ], 1154 + "optional": true, 1155 + "os": [ 1156 + "linux" 1157 + ], 1158 + "engines": { 1159 + "node": ">= 12.0.0" 1160 + }, 1161 + "funding": { 1162 + "type": "opencollective", 1163 + "url": "https://opencollective.com/parcel" 1164 + } 1165 + }, 1166 + "node_modules/lightningcss-linux-x64-musl": { 1167 + "version": "1.29.1", 1168 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.1.tgz", 1169 + "integrity": "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==", 1170 + "cpu": [ 1171 + "x64" 1172 + ], 1173 + "optional": true, 1174 + "os": [ 1175 + "linux" 1176 + ], 1177 + "engines": { 1178 + "node": ">= 12.0.0" 1179 + }, 1180 + "funding": { 1181 + "type": "opencollective", 1182 + "url": "https://opencollective.com/parcel" 1183 + } 1184 + }, 1185 + "node_modules/lightningcss-win32-arm64-msvc": { 1186 + "version": "1.29.1", 1187 + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.1.tgz", 1188 + "integrity": "sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==", 1189 + "cpu": [ 1190 + "arm64" 1191 + ], 1192 + "optional": true, 1193 + "os": [ 1194 + "win32" 1195 + ], 1196 + "engines": { 1197 + "node": ">= 12.0.0" 1198 + }, 1199 + "funding": { 1200 + "type": "opencollective", 1201 + "url": "https://opencollective.com/parcel" 1202 + } 1203 + }, 1204 + "node_modules/lightningcss-win32-x64-msvc": { 1205 + "version": "1.29.1", 1206 + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.1.tgz", 1207 + "integrity": "sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==", 1208 + "cpu": [ 1209 + "x64" 1210 + ], 1211 + "optional": true, 1212 + "os": [ 1213 + "win32" 1214 + ], 1215 + "engines": { 1216 + "node": ">= 12.0.0" 1217 + }, 1218 + "funding": { 1219 + "type": "opencollective", 1220 + "url": "https://opencollective.com/parcel" 1221 + } 1222 + }, 1223 + "node_modules/meshoptimizer": { 1224 + "version": "0.18.1", 1225 + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", 1226 + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==" 1227 + }, 1228 + "node_modules/nanoid": { 1229 + "version": "3.3.8", 1230 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", 1231 + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", 1232 + "funding": [ 1233 + { 1234 + "type": "github", 1235 + "url": "https://github.com/sponsors/ai" 1236 + } 1237 + ], 1238 + "bin": { 1239 + "nanoid": "bin/nanoid.cjs" 1240 + }, 1241 + "engines": { 1242 + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1243 + } 1244 + }, 1245 + "node_modules/parse-svg-path": { 1246 + "version": "0.1.2", 1247 + "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", 1248 + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==" 1249 + }, 1250 + "node_modules/picocolors": { 1251 + "version": "1.1.1", 1252 + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 1253 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" 1254 + }, 1255 + "node_modules/pixi.js": { 1256 + "version": "8.7.3", 1257 + "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.7.3.tgz", 1258 + "integrity": "sha512-wfWlhJYnGx1s4f2yoouevQjaeacbJ12LTkJGa+n9AIYNIjOnmJylBtZ2mARX7iFk3mr2xv0wuo//XPe2hk5OBw==", 1259 + "dependencies": { 1260 + "@pixi/colord": "^2.9.6", 1261 + "@types/css-font-loading-module": "^0.0.12", 1262 + "@types/earcut": "^2.1.4", 1263 + "@webgpu/types": "^0.1.40", 1264 + "@xmldom/xmldom": "^0.8.10", 1265 + "earcut": "^2.2.4", 1266 + "eventemitter3": "^5.0.1", 1267 + "ismobilejs": "^1.1.1", 1268 + "parse-svg-path": "^0.1.2" 1269 + } 1270 + }, 1271 + "node_modules/postcss": { 1272 + "version": "8.5.1", 1273 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", 1274 + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", 1275 + "funding": [ 1276 + { 1277 + "type": "opencollective", 1278 + "url": "https://opencollective.com/postcss/" 1279 + }, 1280 + { 1281 + "type": "tidelift", 1282 + "url": "https://tidelift.com/funding/github/npm/postcss" 1283 + }, 1284 + { 1285 + "type": "github", 1286 + "url": "https://github.com/sponsors/ai" 1287 + } 1288 + ], 1289 + "dependencies": { 1290 + "nanoid": "^3.3.8", 1291 + "picocolors": "^1.1.1", 1292 + "source-map-js": "^1.2.1" 1293 + }, 1294 + "engines": { 1295 + "node": "^10 || ^12 || >=14" 1296 + } 1297 + }, 1298 + "node_modules/prettier": { 1299 + "version": "3.4.2", 1300 + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", 1301 + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", 1302 + "bin": { 1303 + "prettier": "bin/prettier.cjs" 1304 + }, 1305 + "engines": { 1306 + "node": ">=14" 1307 + }, 1308 + "funding": { 1309 + "url": "https://github.com/prettier/prettier?sponsor=1" 1310 + } 1311 + }, 1312 + "node_modules/rollup": { 1313 + "version": "4.34.0", 1314 + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.0.tgz", 1315 + "integrity": "sha512-+4C/cgJ9w6sudisA0nZz0+O7lTP9a3CzNLsoDwaRumM8QHwghUsu6tqHXiTmNUp/rqNiM14++7dkzHDyCRs0Jg==", 1316 + "dependencies": { 1317 + "@types/estree": "1.0.6" 1318 + }, 1319 + "bin": { 1320 + "rollup": "dist/bin/rollup" 1321 + }, 1322 + "engines": { 1323 + "node": ">=18.0.0", 1324 + "npm": ">=8.0.0" 1325 + }, 1326 + "optionalDependencies": { 1327 + "@rollup/rollup-android-arm-eabi": "4.34.0", 1328 + "@rollup/rollup-android-arm64": "4.34.0", 1329 + "@rollup/rollup-darwin-arm64": "4.34.0", 1330 + "@rollup/rollup-darwin-x64": "4.34.0", 1331 + "@rollup/rollup-freebsd-arm64": "4.34.0", 1332 + "@rollup/rollup-freebsd-x64": "4.34.0", 1333 + "@rollup/rollup-linux-arm-gnueabihf": "4.34.0", 1334 + "@rollup/rollup-linux-arm-musleabihf": "4.34.0", 1335 + "@rollup/rollup-linux-arm64-gnu": "4.34.0", 1336 + "@rollup/rollup-linux-arm64-musl": "4.34.0", 1337 + "@rollup/rollup-linux-loongarch64-gnu": "4.34.0", 1338 + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.0", 1339 + "@rollup/rollup-linux-riscv64-gnu": "4.34.0", 1340 + "@rollup/rollup-linux-s390x-gnu": "4.34.0", 1341 + "@rollup/rollup-linux-x64-gnu": "4.34.0", 1342 + "@rollup/rollup-linux-x64-musl": "4.34.0", 1343 + "@rollup/rollup-win32-arm64-msvc": "4.34.0", 1344 + "@rollup/rollup-win32-ia32-msvc": "4.34.0", 1345 + "@rollup/rollup-win32-x64-msvc": "4.34.0", 1346 + "fsevents": "~2.3.2" 1347 + } 1348 + }, 1349 + "node_modules/source-map-js": { 1350 + "version": "1.2.1", 1351 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 1352 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 1353 + "engines": { 1354 + "node": ">=0.10.0" 1355 + } 1356 + }, 1357 + "node_modules/tailwindcss": { 1358 + "version": "4.0.3", 1359 + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.3.tgz", 1360 + "integrity": "sha512-ImmZF0Lon5RrQpsEAKGxRvHwCvMgSC4XVlFRqmbzTEDb/3wvin9zfEZrMwgsa3yqBbPqahYcVI6lulM2S7IZAA==" 1361 + }, 1362 + "node_modules/tapable": { 1363 + "version": "2.2.1", 1364 + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", 1365 + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", 1366 + "engines": { 1367 + "node": ">=6" 1368 + } 1369 + }, 1370 + "node_modules/three": { 1371 + "version": "0.173.0", 1372 + "resolved": "https://registry.npmjs.org/three/-/three-0.173.0.tgz", 1373 + "integrity": "sha512-AUwVmViIEUgBwxJJ7stnF0NkPpZxx1aZ6WiAbQ/Qq61h6I9UR4grXtZDmO8mnlaNORhHnIBlXJ1uBxILEKuVyw==" 1374 + }, 1375 + "node_modules/typescript": { 1376 + "version": "5.6.3", 1377 + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", 1378 + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", 1379 + "dev": true, 1380 + "bin": { 1381 + "tsc": "bin/tsc", 1382 + "tsserver": "bin/tsserver" 1383 + }, 1384 + "engines": { 1385 + "node": ">=14.17" 1386 + } 1387 + }, 1388 + "node_modules/vite": { 1389 + "version": "6.0.11", 1390 + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz", 1391 + "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==", 1392 + "dependencies": { 1393 + "esbuild": "^0.24.2", 1394 + "postcss": "^8.4.49", 1395 + "rollup": "^4.23.0" 1396 + }, 1397 + "bin": { 1398 + "vite": "bin/vite.js" 1399 + }, 1400 + "engines": { 1401 + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 1402 + }, 1403 + "funding": { 1404 + "url": "https://github.com/vitejs/vite?sponsor=1" 1405 + }, 1406 + "optionalDependencies": { 1407 + "fsevents": "~2.3.3" 1408 + }, 1409 + "peerDependencies": { 1410 + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 1411 + "jiti": ">=1.21.0", 1412 + "less": "*", 1413 + "lightningcss": "^1.21.0", 1414 + "sass": "*", 1415 + "sass-embedded": "*", 1416 + "stylus": "*", 1417 + "sugarss": "*", 1418 + "terser": "^5.16.0", 1419 + "tsx": "^4.8.1", 1420 + "yaml": "^2.4.2" 1421 + }, 1422 + "peerDependenciesMeta": { 1423 + "@types/node": { 1424 + "optional": true 1425 + }, 1426 + "jiti": { 1427 + "optional": true 1428 + }, 1429 + "less": { 1430 + "optional": true 1431 + }, 1432 + "lightningcss": { 1433 + "optional": true 1434 + }, 1435 + "sass": { 1436 + "optional": true 1437 + }, 1438 + "sass-embedded": { 1439 + "optional": true 1440 + }, 1441 + "stylus": { 1442 + "optional": true 1443 + }, 1444 + "sugarss": { 1445 + "optional": true 1446 + }, 1447 + "terser": { 1448 + "optional": true 1449 + }, 1450 + "tsx": { 1451 + "optional": true 1452 + }, 1453 + "yaml": { 1454 + "optional": true 1455 + } 1456 + } 1457 + }, 1458 + "node_modules/wiggle": { 1459 + "version": "0.0.17", 1460 + "resolved": "https://registry.npmjs.org/wiggle/-/wiggle-0.0.17.tgz", 1461 + "integrity": "sha512-vF4Kx9a+lHVWia3ZLhZcMnam9VhFGND0+9WYnHq0rcnYDLlbL4iFzYUXJk2cx5reDolwt+EN5mhUP3uHGhWtRQ==", 1462 + "peerDependencies": { 1463 + "three": ">= 0.152.0" 1464 + } 1465 + } 1466 + } 1467 + }
+24
package.json
··· 1 + { 2 + "name": "tamagotchi", 3 + "private": true, 4 + "version": "0.0.0", 5 + "type": "module", 6 + "scripts": { 7 + "dev": "vite", 8 + "build": "tsc && vite build", 9 + "preview": "vite preview" 10 + }, 11 + "devDependencies": { 12 + "typescript": "~5.6.2", 13 + "vite": "^6.0.5" 14 + }, 15 + "dependencies": { 16 + "@tailwindcss/vite": "^4.0.3", 17 + "@types/three": "^0.173.0", 18 + "pixi.js": "^8.7.3", 19 + "prettier": "^3.4.2", 20 + "tailwindcss": "^4.0.3", 21 + "three": "^0.173.0", 22 + "wiggle": "^0.0.17" 23 + } 24 + }
+23
tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2020", 4 + "useDefineForClassFields": true, 5 + "module": "ESNext", 6 + "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 + "skipLibCheck": true, 8 + 9 + /* Bundler mode */ 10 + "moduleResolution": "bundler", 11 + "allowImportingTsExtensions": true, 12 + "isolatedModules": true, 13 + "moduleDetection": "force", 14 + "noEmit": true, 15 + 16 + /* Linting */ 17 + "strict": true, 18 + "noUnusedLocals": true, 19 + "noUnusedParameters": true, 20 + "noFallthroughCasesInSwitch": true 21 + }, 22 + "include": ["src"] 23 + }
+11
vite.config.js
··· 1 + import { defineConfig } from "vite"; 2 + import tailwindcss from "@tailwindcss/vite"; 3 + 4 + // https://vitejs.dev/config/ 5 + export default defineConfig({ 6 + plugins: [tailwindcss()], 7 + build: { 8 + target: "esnext", 9 + }, 10 + base: "/tiny-creature/", 11 + });
public/model.glb

This is a binary file and will not be displayed.

+121
src/main.ts
··· 1 + import * as THREE from "three"; 2 + // @ts-ignore 3 + import { WiggleBone } from "wiggle"; 4 + import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js"; 5 + import { createFaceTexture, updateFace } from "./face/face"; 6 + 7 + const setup = async () => { 8 + // Create the scene 9 + const scene = new THREE.Scene(); 10 + 11 + const mouse = new THREE.Vector2(); 12 + 13 + // Create the camera 14 + const camera = new THREE.PerspectiveCamera( 15 + 75, // Field of view 16 + window.innerWidth / window.innerHeight, // Aspect ratio 17 + ); 18 + 19 + // Position the camera 20 + camera.position.z = 3; 21 + 22 + // Set up the renderer 23 + const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); 24 + renderer.setSize(window.innerWidth, window.innerHeight); 25 + document.body.appendChild(renderer.domElement); 26 + 27 + // set styles of the canvas 28 + renderer.domElement.style.position = "absolute"; 29 + renderer.domElement.style.top = "0"; 30 + renderer.domElement.style.left = "0"; 31 + renderer.domElement.style.width = "100%"; 32 + renderer.domElement.style.height = "100%"; 33 + 34 + renderer.outputColorSpace = THREE.SRGBColorSpace; // optional with post-processing 35 + renderer.toneMapping = THREE.ACESFilmicToneMapping; 36 + 37 + const { app } = await createFaceTexture({ 38 + width: 512, 39 + height: 512, 40 + }); 41 + 42 + const texture = new THREE.CanvasTexture(app.canvas); 43 + 44 + const loader = new GLTFLoader(); 45 + 46 + let rootBone: THREE.Bone; 47 + const wiggleBones: WiggleBone[] = []; 48 + 49 + loader.load("/tiny-creature/model.glb", ({ scene: obj }) => { 50 + const mesh = obj.getObjectByName("Icosphere"); 51 + if (!mesh) return; 52 + 53 + if (mesh instanceof THREE.Mesh) { 54 + mesh.material = new THREE.MeshStandardMaterial({ 55 + map: texture, 56 + }); 57 + } 58 + 59 + if (mesh instanceof THREE.SkinnedMesh) { 60 + mesh.skeleton.bones.forEach((bone) => { 61 + if (!(bone.parent instanceof THREE.Bone) || !bone.parent?.isBone) { 62 + rootBone = bone; 63 + 64 + rootBone.rotation.y = -Math.PI / 2; 65 + } else { 66 + const wiggleBone = new WiggleBone(bone, { 67 + velocity: 0.4, 68 + }); 69 + wiggleBones.push(wiggleBone); 70 + } 71 + }); 72 + } 73 + 74 + scene.add(obj); 75 + }); 76 + 77 + const light = new THREE.DirectionalLight(0xffffff, 1); 78 + light.position.set(10, 10, 10); 79 + scene.add(light); 80 + 81 + const ambientLight = new THREE.AmbientLight(0xffffff, 0.2); 82 + scene.add(ambientLight); 83 + 84 + let lastTime = performance.now(); 85 + const animate = () => { 86 + requestAnimationFrame(animate); 87 + texture.needsUpdate = true; 88 + 89 + const deltaTime = (performance.now() - lastTime) / 1000; 90 + lastTime = performance.now(); 91 + 92 + updateFace(deltaTime, mouse.x, mouse.y); 93 + 94 + if (rootBone) { 95 + rootBone.position.x = mouse.x * 2; 96 + rootBone.position.y = mouse.y * 2; 97 + } 98 + 99 + wiggleBones.forEach((wiggleBone) => { 100 + wiggleBone.update(); 101 + }); 102 + 103 + renderer.render(scene, camera); 104 + }; 105 + 106 + animate(); 107 + 108 + window.addEventListener("resize", () => { 109 + camera.aspect = window.innerWidth / window.innerHeight; 110 + camera.updateProjectionMatrix(); 111 + renderer.setSize(window.innerWidth, window.innerHeight); 112 + }); 113 + 114 + // get mouse position 115 + window.addEventListener("mousemove", (event) => { 116 + mouse.x = (event.clientX / window.innerWidth) * 2 - 1; 117 + mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; 118 + }); 119 + }; 120 + 121 + setup();
+51
.github/workflows/deploy.yml
··· 1 + name: Deploy to GitHub Pages 2 + 3 + on: 4 + push: 5 + branches: 6 + - main 7 + 8 + jobs: 9 + build_site: 10 + runs-on: ubuntu-latest 11 + steps: 12 + - name: Checkout 13 + uses: actions/checkout@v3 14 + 15 + - name: Install Node.js 16 + uses: actions/setup-node@v3 17 + with: 18 + node-version: 18 19 + cache: npm 20 + 21 + - name: Install dependencies 22 + run: npm ci 23 + 24 + - name: build 25 + env: 26 + BASE_PATH: "/${{ github.event.repository.name }}" 27 + run: | 28 + npm run build 29 + 30 + - name: Upload Artifacts 31 + uses: actions/upload-pages-artifact@v2 32 + with: 33 + # this should match the `pages` option in your adapter-static options 34 + path: "dist/" 35 + 36 + deploy: 37 + needs: build_site 38 + runs-on: ubuntu-latest 39 + 40 + permissions: 41 + pages: write 42 + id-token: write 43 + 44 + environment: 45 + name: github-pages 46 + url: ${{ steps.deployment.outputs.page_url }} 47 + 48 + steps: 49 + - name: Deploy 50 + id: deployment 51 + uses: actions/deploy-pages@v2
+183
src/face/eye.ts
··· 1 + import * as PIXI from "pixi.js"; 2 + import { AnimatedValue, degreesToRadians } from "./math-helper"; 3 + 4 + export type EyeOptions = { 5 + container: PIXI.Container; 6 + 7 + x: number; 8 + y: number; 9 + 10 + eyeColor: PIXI.ColorSource; 11 + pupilColor: PIXI.ColorSource; 12 + highlightColor: PIXI.ColorSource; 13 + 14 + size: number; 15 + 16 + eyebrow: Partial<{ 17 + length: number; 18 + 19 + width: number; 20 + 21 + y: number; 22 + x: number; 23 + 24 + curve: number; 25 + 26 + color: PIXI.ColorSource; 27 + }>; 28 + 29 + blinkStart: number; 30 + blinkTimer: number; 31 + }; 32 + 33 + export default class Eye { 34 + eyeContainer: PIXI.Container; 35 + 36 + container: PIXI.Container; 37 + 38 + base?: PIXI.Graphics; 39 + pupil?: PIXI.Graphics; 40 + highlight?: PIXI.Sprite; 41 + 42 + mask?: PIXI.Graphics; 43 + 44 + // how long one blink takes, split into four parts [close animation, closed, open animation, open] 45 + blinkSpeed: [number, number, number, number] = [0.05, 0.1, 0.1, 1]; 46 + 47 + private blinkTimer: number = 0; 48 + private blinkState: number = 3; 49 + 50 + _x: AnimatedValue; 51 + _y: AnimatedValue; 52 + 53 + dx: number = 0; 54 + dy: number = 0; 55 + 56 + size: number = 1; 57 + 58 + targetX: number = 0; 59 + targetY: number = 0; 60 + 61 + constructor(opts: Partial<EyeOptions>) { 62 + console.log("Eye", opts); 63 + this.container = new PIXI.Container(); 64 + 65 + this.eyeContainer = new PIXI.Container(); 66 + 67 + this.container.position.set(opts.x ?? 0, opts.y ?? 0); 68 + 69 + this.size = opts.size ?? 1; 70 + 71 + if (opts.blinkStart) { 72 + this.blinkTimer = opts.blinkStart; 73 + } 74 + if (opts.blinkTimer) { 75 + this.blinkTimer = opts.blinkTimer; 76 + } 77 + 78 + this._x = new AnimatedValue(); 79 + this._y = new AnimatedValue(); 80 + 81 + this.container.addChild(this.eyeContainer); 82 + 83 + if (opts.container) opts.container.addChild(this.container); 84 + 85 + this.base = new PIXI.Graphics() 86 + .ellipse(0, 0, this.size, this.size * 0.9) 87 + .fill(opts.eyeColor ?? 0xffffff); 88 + this.base.alpha = 1; 89 + this.eyeContainer.addChild(this.base); 90 + 91 + this.pupil = new PIXI.Graphics() 92 + .circle(0, 0, this.size * 0.6) 93 + .fill(opts.pupilColor ?? 0x000000) 94 + .circle(this.size * 0.3, this.size * 0.3, 0.2 * this.size) 95 + .fill({ color: 0xffffff, alpha: 0.7 }); 96 + this.eyeContainer.addChild(this.pupil); 97 + 98 + this.mask = new PIXI.Graphics() 99 + .ellipse(0, 0, this.size, this.size * 0.9) 100 + .fill(0xffffff); 101 + this.eyeContainer.addChild(this.mask); 102 + this.eyeContainer.mask = this.mask; 103 + 104 + let eyebrowWidth = opts?.eyebrow?.width ?? 2; 105 + let eyebrowCurve = opts?.eyebrow?.curve ?? 0; 106 + 107 + const eyebrow = new PIXI.Graphics() 108 + .moveTo((-eyebrowWidth / 2) * this.size, 0) 109 + .quadraticCurveTo( 110 + 0, 111 + eyebrowCurve * this.size, 112 + (eyebrowWidth / 2) * this.size, 113 + 0, 114 + ) 115 + .stroke({ 116 + color: opts.eyebrow?.color ?? 0, 117 + width: opts.eyebrow?.width ?? this.size * 0.2, 118 + }); 119 + eyebrow.position.set( 120 + opts.eyebrow?.x ?? 0, 121 + (opts.eyebrow?.y ?? 0.9) * this.size, 122 + ); 123 + 124 + this.container.addChild(eyebrow); 125 + } 126 + 127 + lookInDirection(direction: number) { 128 + const angle = degreesToRadians(direction); 129 + 130 + this.lookAt(Math.cos(angle), Math.sin(angle)); 131 + } 132 + 133 + lookAt(x: number, y: number) { 134 + this._x.value = x * this.size * 0.3; 135 + this._y.value = y * this.size * 0.3; 136 + } 137 + 138 + rotate(angle: number) { 139 + // get current angle 140 + let currentAngle = Math.atan2(this._y.value, this._x.value); 141 + 142 + let moveAngle = degreesToRadians(angle); 143 + 144 + this.lookAt( 145 + Math.cos(currentAngle + moveAngle), 146 + Math.sin(currentAngle + moveAngle), 147 + ); 148 + } 149 + 150 + update(deltaTime: number) { 151 + let updateX = this._x.update(deltaTime); 152 + let updateY = this._y.update(deltaTime); 153 + if (updateX || updateY) { 154 + this.pupil?.position.set(this._x.value, this._y.value); 155 + } 156 + 157 + if (this.mask) { 158 + // Blink animation 159 + this.blinkTimer += deltaTime; 160 + 161 + if (this.blinkTimer >= this.blinkSpeed[this.blinkState]) { 162 + this.blinkTimer = 0; 163 + this.blinkState = (this.blinkState + 1) % 4; 164 + } 165 + 166 + let scale: number; 167 + switch (this.blinkState) { 168 + case 0: // Closing 169 + scale = 1 - this.blinkTimer / this.blinkSpeed[0]; 170 + break; 171 + case 1: // Closed 172 + scale = 0; 173 + break; 174 + case 2: // Opening 175 + scale = this.blinkTimer / this.blinkSpeed[2]; 176 + break; 177 + default: // Open 178 + scale = 1; 179 + } 180 + this.mask.scale.y = scale; 181 + } 182 + } 183 + }
+54
src/face/face.ts
··· 1 + import { Application, Graphics, Renderer } from "pixi.js"; 2 + import Eye from "./eye"; 3 + 4 + let leftEye: Eye; 5 + let rightEye: Eye; 6 + 7 + export async function createFaceTexture({ 8 + width = 512, 9 + height = 512, 10 + }: { 11 + width?: number; 12 + height?: number; 13 + }): Promise<{ app: Application<Renderer> }> { 14 + const app = new Application(); 15 + 16 + await app.init({ 17 + width, 18 + height, 19 + background: "#db2777", 20 + }); 21 + 22 + rightEye = new Eye({ 23 + x: width * 0.5 + 75, 24 + y: height * 0.5, 25 + size: 50, 26 + }); 27 + 28 + leftEye = new Eye({ 29 + x: width * 0.5 - 75, 30 + y: height * 0.5, 31 + size: 50, 32 + }); 33 + 34 + app.stage.addChild(rightEye.container); 35 + app.stage.addChild(leftEye.container); 36 + 37 + // draw mouth 38 + const mouth = new Graphics(); 39 + mouth.rect(0, 0, 50, 10).fill(0x0); 40 + mouth.position.set(width * 0.5 - 25, height * 0.5 - 75); 41 + app.stage.addChild(mouth); 42 + 43 + return { 44 + app, 45 + }; 46 + } 47 + 48 + export function updateFace(dt: number, x: number, y: number) { 49 + leftEye.lookAt(x, y); 50 + rightEye.lookAt(x, y); 51 + 52 + leftEye.update(dt); 53 + rightEye.update(dt); 54 + }
+180
src/face/math-helper.ts
··· 1 + /** 2 + * Smoothly transitions between two values. 3 + * 4 + * This function helps you move from a starting value (source) to an ending value (target) 5 + * based on an amount between 0 and 1. If the amount is 0, you get the source value. 6 + * If it's 1, you get the target value. Values in between give you a mix of both. 7 + * 8 + * @param {number} source - The value to start from. 9 + * @param {number} target - The value to move towards. 10 + * @param {number} amount - A number between 0 and 1 that controls how much to move 11 + * from the source to the target. 12 + * @returns {number} The value in between source and target, depending on the amount. 13 + */ 14 + export function smoothStep( 15 + source: number, 16 + target: number, 17 + amount: number, 18 + ): number { 19 + return source * (1 - amount) + target * amount; 20 + } 21 + 22 + /** 23 + * Smoothly transitions between two values, adjusting for different frame rates. 24 + * 25 + * This function helps you move from a starting value (source) to an ending value (target), 26 + * but it also adjusts for how fast your program is running (its framerate). This ensures that 27 + * the transition is smooth and consistent, even if the framerate changes. 28 + * 29 + * If the `frameDelta` (the time between frames) is not provided, the function will simply move 30 + * from the source to the target based on the rate you provide, similar to the basic smooth transition. 31 + * 32 + * If the `frameDelta` is provided, the function takes this into account to make sure that the transition 33 + * speed is consistent, no matter how fast or slow the program runs. 34 + * 35 + * @param {number} source - The value to start from. 36 + * @param {number} target - The value to move towards. 37 + * @param {number} rate - Controls how quickly to move from source to target. 38 + * A value of 0 means no movement, and 1 means an instant jump to the target. 39 + * Values in between result in smoother, more gradual transitions. 40 + * @param {number} frameDelta - The time between frames (in seconds), used to adjust the rate 41 + * and ensure smooth transitions at any framerate. 42 + * @param {number} [targetFps=60] - The target frames per second, usually 60 by default. 43 + * @returns {number} The value in between source and target, adjusted for framerate. 44 + */ 45 + export function framerateIndependentSmoothStep( 46 + source: number, 47 + target: number, 48 + rate: number, 49 + frameDelta: number, 50 + targetFps = 60, 51 + ): number { 52 + if (typeof frameDelta === "undefined") { 53 + return smoothStep(source, target, rate); 54 + } 55 + 56 + const relativeDelta = frameDelta / (1 / targetFps); 57 + const smoothing = 1 - rate; 58 + return smoothStep(source, target, 1 - Math.pow(smoothing, relativeDelta)); 59 + } 60 + 61 + /** 62 + * Converts an angle from degrees to radians. 63 + * 64 + * @param {number} degrees - The angle in degrees. 65 + * @returns {number} The angle in radians. 66 + */ 67 + export function degreesToRadians(degrees: number): number { 68 + return degrees * (Math.PI / 180); 69 + } 70 + 71 + /** 72 + * Converts an angle from radians to degrees. 73 + * 74 + * @param {number} radians - The angle in radians. 75 + * @returns {number} The angle in degrees. 76 + */ 77 + export function radiansToDegrees(radians: number): number { 78 + return radians * (180 / Math.PI); 79 + } 80 + 81 + /** 82 + * Restricts a value to stay within a certain range. 83 + * 84 + * This function makes sure a number doesn't go below a minimum value or above a maximum value. 85 + * If the number is lower than the minimum, it returns the minimum. If it's higher than the maximum, 86 + * it returns the maximum. If the number is within the range, it stays unchanged. 87 + * 88 + * @param {number} value - The number you want to restrict. 89 + * @param {number} min - The lowest value the number can be. 90 + * @param {number} max - The highest value the number can be. 91 + * @returns {number} The number, but kept within the specified range. 92 + */ 93 + export function clamp(value: number, min: number, max: number): number { 94 + return Math.max(min, Math.min(max, value)); 95 + } 96 + 97 + /** 98 + * Loops a number around a range. 99 + * 100 + * This function makes a number loop back to the start if it goes past the end of a range, 101 + * or to the end if it goes below the start. For example, if you're counting seconds on a clock 102 + * and you reach 60, you wrap back around to 0. 103 + * 104 + * @param {number} value - The number you want to loop. 105 + * @param {number} min - The start of the range. 106 + * @param {number} max - The end of the range. 107 + * @returns {number} The wrapped number within the range. 108 + */ 109 + export function wrap(value: number, min: number, max: number): number { 110 + return ((value - min) % (max - min)) + min; 111 + } 112 + 113 + /** 114 + * Converts a number from one range to another. 115 + * 116 + * This function takes a number that's within one range of values and converts it to be within 117 + * another range of values. For example, you might want to take a score out of 100 and convert it 118 + * to a percentage out of 10, or take a health bar that's between 0 and 100 and map it to a progress 119 + * bar that's between 0 and 1. 120 + * 121 + * @param {number} value - The number you want to convert. 122 + * @param {number} inMin - The start of the current range. 123 + * @param {number} inMax - The end of the current range. 124 + * @param {number} outMin - The start of the new range. 125 + * @param {number} outMax - The end of the new range. 126 + * @returns {number} The number converted to the new range. 127 + */ 128 + export function map( 129 + value: number, 130 + inMin: number, 131 + inMax: number, 132 + outMin: number, 133 + outMax: number, 134 + ): number { 135 + return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin; 136 + } 137 + 138 + const epsilon = 0.001; 139 + 140 + export class AnimatedValue { 141 + _value: number; 142 + _target: number; 143 + 144 + rate: number; 145 + 146 + needsUpdate: boolean = false; 147 + 148 + constructor(opts?: { value: number; rate: number }) { 149 + this._value = opts?.value ?? 0; 150 + this._target = opts?.value ?? 0; 151 + this.rate = opts?.rate ?? 0.1; 152 + } 153 + 154 + get value() { 155 + return this._value; 156 + } 157 + 158 + set value(value: number) { 159 + this._target = value; 160 + this.needsUpdate = true; 161 + } 162 + 163 + update(dt: number) { 164 + if (!this.needsUpdate) return false; 165 + 166 + this._value = framerateIndependentSmoothStep( 167 + this._value, 168 + this._target, 169 + this.rate, 170 + dt, 171 + ); 172 + 173 + if (Math.abs(this._value - this._target) > epsilon) { 174 + this.needsUpdate = true; 175 + } else { 176 + this.needsUpdate = false; 177 + } 178 + return true; 179 + } 180 + }