diff --git a/packages/react-three-portfolio/CHANGELOG.md b/packages/react-three-portfolio/CHANGELOG.md new file mode 100644 index 00000000..14145807 --- /dev/null +++ b/packages/react-three-portfolio/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +All notable changes to this package will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.1.0] - 2025-03-25 + +### Added + +- Initial release extracted from gabriel project +- `HeroCanvas` - Complete 3D scene with all effects combined +- `HeroOrb` - Pulsating orb with custom shader and noise-based animation +- `EnergyWaves` - Spherical wave effect with custom shader +- `Eye` - Realistic animated eye with blinking and mouse tracking +- `LightningEffect` - Procedural lightning bolts emanating from within +- `SparkleEffect` - Particle system with orbital motion +- `PostProcessing` - Bloom, noise, vignette, and chromatic aberration +- Full TypeScript support +- Build configuration with tsup +- Test suite with vitest + +### Notes + +- This is the initial extraction of Three.js/React Three Fiber components from the gabriel project +- Dependencies: `@react-three/fiber`, `@react-three/drei`, `@react-three/postprocessing`, `three`, `postprocessing` +- All components are designed for portfolio and landing page use cases +- Uses custom GLSL shaders for visual effects + +[Unreleased]: https://github.com/riceharvest/opensourceframework/compare/@opensourceframework/react-three-portfolio@0.1.0...main +[0.1.0]: https://github.com/riceharvest/opensourceframework/releases/tag/@opensourceframework/react-three-portfolio@0.1.0 diff --git a/packages/react-three-portfolio/LICENSE b/packages/react-three-portfolio/LICENSE new file mode 100644 index 00000000..bfe27feb --- /dev/null +++ b/packages/react-three-portfolio/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 OpenSource Framework Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/react-three-portfolio/README.md b/packages/react-three-portfolio/README.md new file mode 100644 index 00000000..ae603d37 --- /dev/null +++ b/packages/react-three-portfolio/README.md @@ -0,0 +1,269 @@ +# @opensourceframework/react-three-portfolio + +> Three.js/React Three Fiber visual components for portfolio and landing pages + +[![npm version](https://img.shields.io/npm/v/@opensourceframework/react-three-portfolio.svg)](https://www.npmjs.com/package/@opensourceframework/react-three-portfolio) +[![npm downloads](https://img.shields.io/npm/dm/@opensourceframework/react-three-portfolio.svg)](https://www.npmjs.com/package/@opensourceframework/react-three-portfolio) +[![License](https://img.shields.io/npm/l/@opensourceframework/react-three-portfolio.svg)](./LICENSE) + +## About + +A collection of stunning 3D visual components built with React Three Fiber and Three.js, designed for creating impressive portfolio sites and landing pages. This package includes reusable components featuring custom shaders, animations, and post-processing effects. + +### Components + +- **HeroCanvas** - A complete 3D scene combining multiple visual effects +- **HeroOrb** - A pulsating, transparent orb with custom shader material and noise-based animation +- **EnergyWaves** - A spherical mesh with radiating wave patterns and pulsating effect +- **Eye** - A realistic animated eye with iris texture, pupil, blinking eyelids, and mouse tracking +- **LightningEffect** - Dynamic lightning bolts emanating from within a central form +- **SparkleEffect** - A particle system creating ambient sparkles that orbit and drift +- **PostProcessing** - Bloom, noise, vignette, and chromatic aberration effects + +## Installation + +```bash +npm install @opensourceframework/react-three-portfolio three +# or +yarn add @opensourceframework/react-three-portfolio three +# or +pnpm add @opensourceframework/react-three-portfolio three +``` + +> **Note**: This package has `three` as a peer dependency. You must install it separately. + +## Usage + +### Basic Example + +```tsx +'use client'; + +import { HeroCanvas } from '@opensourceframework/react-three-portfolio'; + +export default function HomePage() { + return ( +
+ +
+ ); +} +``` + +### Using Individual Components + +```tsx +'use client'; + +import { Canvas } from '@react-three/fiber'; +import { HeroOrb, LightningEffect, SparkleEffect } from '@opensourceframework/react-three-portfolio'; + +export function MyCustomScene() { + return ( + + + + + + + + + ); +} +``` + +### Requirements + +- React 18.0 or higher +- React DOM 18.0 or higher +- @react-three/fiber 8.0 or higher +- Three.js 0.150.0 or higher + +## API Reference + +### `HeroCanvas` + +A complete pre-configured 3D scene that combines all visual effects into a cohesive composition. + +**Features:** +- Ambient and point lighting +- Eye component +- HeroOrb as the central element +- Lightning effects emanating from within +- Sparkle particles +- Post-processing effects (bloom, vignette, noise, chromatic aberration) + +**No props required** - Ready to use out of the box. + +--- + +### `HeroOrb` + +A large, central orb with a custom shader material that creates a living, breathing energy ball. + +**Features:** +- Custom GLSL shader with simplex noise +- Fresnel-based transparency and edge glow +- Pulsating expansion effect +- Optional mouse interaction (uMouse uniform) + +**Optional uniforms (can be passed as props):** +- `uColor1` - Core color (default: `#2a0044`) +- `uColor2` - Rim color (default: `#9d00ff`) + +--- + +### `Eye` + +A realistic human eye that follows the mouse cursor and blinks naturally. + +**Features:** +- Custom iris shader with noise-based texture +- Animated eyelashes +- Realistic blinking (automatic, random) +- Mouse tracking (follows cursor when active) +- Idle look behavior (random target looking) + +**Size:** Rendered at 0.6x scale by default (adjustable via group scale) + +--- + +### `LightningEffect` + +Generates procedural lightning bolts that appear to emanate from within a central form. + +**Features:** +- Procedural generation with midpoint displacement +- Jagged, animated lines with jitter +- Additive blending for glow effect +- Automatic spawning with adjustable frequency +- Bright electric purple colors + +**Customization:** +- Spawn frequency: ~0.05-0.2 seconds +- Duration: 0.1-0.25 seconds per bolt +- Intensity: 2.0-4.0 +- Bolt color: Electric purple (HSL 0.75-0.85) + +--- + +### `SparkleEffect` + +A particle system that creates floating sparkles with orbital motion. + +**Features:** +- GPU-accelerated points with custom shader +- Particle lifecycle (spawn, drift, fade) +- Swirling motion toward center +- Additive blending for glow +- Automatic spawning + +**Customization:** +- Spawn rate: 4-9 particles per spawn +- Spawn interval: 0.05 seconds +- Particle life: 1.5-3.0 seconds +- Bright HSL colors (0.7-0.85 hue) + +--- + +### `PostProcessing` + +Applies a chain of post-processing effects to the entire scene. + +**Effects included:** +- **Bloom** - Glow for bright areas (intensity: 1.0) +- **Noise** - Subtle film grain overlay (opacity: 0.05) +- **Vignette** - Darkened edges (offset: 0.1, darkness: 1.1) +- **Chromatic Aberration** - RGB shift (offset: 0.001) + +**Note**: Requires `@react-three/postprocessing` as a peer dependency, which is included in this package's dependencies. + +--- + +### `EnergyWaves` + +A spherical mesh with a custom shader that creates expanding energy wave patterns. + +**Features:** +- Pulsating radial waves +- Transparent, additive blending +- Continuous rotation + +**Note**: Not included in the default HeroCanvas to avoid visual clutter, but available as a standalone component. + +--- + +## Advanced Customization + +All components use custom shader materials with uniforms that can be adjusted via props. See source code for available uniforms and how to modify them. + +### Passing Uniforms to HeroOrb + +```tsx + +``` + +You may need to extend the component's prop types to accept custom uniforms, as they are currently typed as `any` for flexibility. + +## Performance Considerations + +- These components are GPU-intensive due to custom shaders and particle systems +- Use `dpr={[1, 2]}` to limit device pixel ratio (recommended) +- Consider disabling some effects on lower-end devices +- Post-processing particularly impacts performance + +## Browser Support + +Requires WebGL 2.0 and modern browser features. Tested on: +- Chrome 90+ +- Firefox 88+ +- Safari 15+ +- Edge 90+ + +## Development + +This package is part of the [OpenSource Framework](https://github.com/riceharvest/opensourceframework) monorepo. + +To develop locally: + +```bash +# From monorepo root +pnpm install +pnpm --filter @opensourceframework/react-three-portfolio dev +``` + +## Testing + +```bash +pnpm --filter @opensourceframework/react-three-portfolio test +``` + +## Building + +```bash +pnpm --filter @opensourceframework/react-three-portfolio build +``` + +## Contributing + +See the [root contributing guide](../../CONTRIBUTING.md) for general contribution information. + +For this package specifically: +- Follow existing code style and shader structure +- Test on multiple browsers +- Consider performance implications of shader changes +- Document any new uniforms or props + +## Changelog + +See [CHANGELOG.md](./CHANGELOG.md) for details about changes. + +## License + +MIT - See [LICENSE](./LICENSE) for full text. + +## Acknowledgements + +- Original source project: [gabriel](https://github.com/riceharvest/gabriel) +- [React Three Fiber](https://github.com/pmndrs/react-three-fiber) +- [Three.js](https://threejs.org/) diff --git a/packages/react-three-portfolio/llms.txt b/packages/react-three-portfolio/llms.txt new file mode 100644 index 00000000..a110116a --- /dev/null +++ b/packages/react-three-portfolio/llms.txt @@ -0,0 +1,28 @@ +# @opensourceframework/react-three-portfolio - AI Summary + +Three.js/React Three Fiber visual components for portfolio and landing pages. This package provides a collection of reusable 3D components with custom shaders, animations, and post-processing effects. + +## Maintainer Intent + +- Provide visually impressive, drop-in 3D components for portfolio websites +- Balance visual quality with reasonable performance overhead +- Support the React Three Fiber ecosystem and patterns +- Keep components framework-agnostic where possible (only depends on React Three Fiber, not Next.js specific) +- Document customization options and performance considerations clearly + +## Installation + +```bash +npm install @opensourceframework/react-three-portfolio three +# or +pnpm add @opensourceframework/react-three-portfolio three +``` + +## Links + +- Documentation: https://github.com/riceharvest/opensourceframework/tree/main/packages/react-three-portfolio#readme +- npm: https://www.npmjs.com/package/@opensourceframework/react-three-portfolio + +## Source + +Extracted from the gabriel project (https://github.com/riceharvest/gabriel). diff --git a/packages/react-three-portfolio/package.json b/packages/react-three-portfolio/package.json new file mode 100644 index 00000000..f53d134b --- /dev/null +++ b/packages/react-three-portfolio/package.json @@ -0,0 +1,91 @@ +{ + "name": "@opensourceframework/react-three-portfolio", + "version": "0.1.0", + "description": "Three.js/React Three Fiber visual components for portfolio and landing pages, including EnergyWaves, Eye, HeroCanvas, HeroOrb, LightningEffect, SparkleEffect, and PostProcessing effects.", + "author": "OpenSource Framework Contributors", + "license": "MIT", + "type": "module", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist", + "README.md", + "CHANGELOG.md", + "LICENSE", + "llms.txt" + ], + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "lint": "eslint . --ignore-pattern examples", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "clean": "rm -rf dist coverage node_modules" + }, + "devDependencies": { + "@types/three": "^0.160.0", + "eslint": "^9.0.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^5.0.0", + "tsup": "^8.5.1", + "typescript": "^5.9.3", + "vitest": "^2.1.9" + }, + "dependencies": { + "@react-three/drei": "^10.7.7", + "@react-three/fiber": "^9.5.0", + "@react-three/postprocessing": "^3.0.4", + "postprocessing": "^6.36.6", + "three": "^0.182.0" + }, + "peerDependencies": { + "@react-three/fiber": ">=8.0.0", + "react": ">=18.0.0", + "react-dom": ">=18.0.0", + "three": ">=0.150.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/riceharvest/opensourceframework.git", + "directory": "packages/react-three-portfolio" + }, + "bugs": { + "url": "https://github.com/riceharvest/opensourceframework/issues?q=is%3Aissue+is%3Aopen+react-three-portfolio" + }, + "homepage": "https://github.com/riceharvest/opensourceframework/tree/main/packages/react-three-portfolio#readme", + "engines": { + "node": ">=18.0.0" + }, + "publishConfig": { + "access": "public" + }, + "keywords": [ + "react", + "three", + "threejs", + "react-three-fiber", + "3d", + "canvas", + "portfolio", + "landing-page", + "visual-effects", + "webgl", + "shaders" + ] +} diff --git a/packages/react-three-portfolio/src/components/EnergyWaves.tsx b/packages/react-three-portfolio/src/components/EnergyWaves.tsx new file mode 100644 index 00000000..b6546fab --- /dev/null +++ b/packages/react-three-portfolio/src/components/EnergyWaves.tsx @@ -0,0 +1,102 @@ +"use client"; + +import * as THREE from 'three'; +import React, { useRef } from 'react'; +import { useFrame, extend, ThreeElement } from '@react-three/fiber'; +import { shaderMaterial } from '@react-three/drei'; + +// Energy wave material with pulsating effect +const EnergyWaveMaterial = shaderMaterial( + { + uTime: 0, + uColor: new THREE.Color('#9d4edd'), // Vibrant purple + }, + // Vertex Shader + ` + varying vec2 vUv; + varying vec3 vNormal; + uniform float uTime; + + void main() { + vUv = uv; + vNormal = normalize(normalMatrix * normal); + + vec3 pos = position; + + // Multiple pulsating waves + float wave1 = sin(length(pos) * 3.0 - uTime * 2.0) * 0.03; + float wave2 = cos(length(pos) * 5.0 - uTime * 1.5) * 0.02; + float pulse = sin(uTime * 2.5) * 0.5 + 0.5; + + // Expand and contract + float expansion = 0.05 * pulse; + pos += normal * (wave1 + wave2 + expansion); + + vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0); + gl_Position = projectionMatrix * modelViewPosition; + } + `, + // Fragment Shader + ` + varying vec2 vUv; + varying vec3 vNormal; + uniform vec3 uColor; + uniform float uTime; + + void main() { + vec3 normal = normalize(vNormal); + + // Animated wave patterns + float wave = sin(vUv.x * 10.0 - uTime * 3.0) * cos(vUv.y * 10.0 - uTime * 2.0); + float pulse = sin(uTime * 2.5) * 0.5 + 0.5; + + // Flowing pattern + float pattern = sin((vUv.x + vUv.y) * 15.0 - uTime * 4.0) * 0.5 + 0.5; + + // Combine effects + float intensity = wave * pattern * pulse; + + // Alpha based on pattern + float alpha = (intensity * 0.6 + 0.4) * pulse * 0.7; + + gl_FragColor = vec4(uColor, alpha); + } + ` +); + +extend({ EnergyWaveMaterial }); + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace React { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace JSX { + interface IntrinsicElements { + energyWaveMaterial: ThreeElement; + } + } + } +} + +export const EnergyWaves = () => { + const meshRef = useRef(null); + + const materialRef = useRef(null); + + useFrame((state) => { + if (materialRef.current) { + materialRef.current.uTime = state.clock.getElapsedTime(); + } + + if (meshRef.current) { + meshRef.current.rotation.z += 0.002; + } + }); + + return ( + + + + + ); +}; diff --git a/packages/react-three-portfolio/src/components/Eye.tsx b/packages/react-three-portfolio/src/components/Eye.tsx new file mode 100644 index 00000000..3e1a5d1a --- /dev/null +++ b/packages/react-three-portfolio/src/components/Eye.tsx @@ -0,0 +1,324 @@ +'use client' + +import * as THREE from 'three'; +import React, { useRef, useState, useEffect } from 'react'; +import { useFrame, extend, ThreeElement } from '@react-three/fiber'; +import { shaderMaterial } from '@react-three/drei'; + +// --- SHADERS --- + +// Realistic Eye Shader +const EyeMaterial = shaderMaterial( + { + uTime: 0, + uIrisColor: new THREE.Color("#ffffff"), + uPupilSize: 0.25, + uIrisSize: 0.45, + }, + // Vertex + ` + varying vec2 vUv; + varying vec3 vNormal; + varying vec3 vPosition; + + void main() { + vUv = uv; + vNormal = normalize(normalMatrix * normal); + vPosition = position; + vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); + gl_Position = projectionMatrix * mvPosition; + } + `, + // Fragment + ` + uniform float uTime; + uniform vec3 uIrisColor; + uniform float uPupilSize; + uniform float uIrisSize; + + varying vec2 vUv; + varying vec3 vNormal; + varying vec3 vPosition; + + // Simplex noise + vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } + vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } + vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); } + float snoise(vec2 v) { + const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439); + vec2 i = floor(v + dot(v, C.yy) ); + vec2 x0 = v - i + dot(i, C.xx); + vec2 i1; + i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + i = mod289(i); + vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 )); + vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); + m = m*m ; + m = m*m ; + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); + } + + void main() { + vec3 pos = normalize(vPosition); + // Direction vector (0,0,1) for where the eye texture looks + float dotZ = dot(pos, vec3(0.0, 0.0, 1.0)); + float angle = acos(dotZ); + + vec3 scleraColor = vec3(0.85, 0.85, 0.88); + vec3 pupilColor = vec3(0.0); + vec3 color = scleraColor; + + // Veins + float veinNoise = snoise(pos.xy * 6.0); + if (angle > uIrisSize) { + float vein = smoothstep(0.45, 0.55, veinNoise); + color = mix(color, vec3(0.8, 0.7, 0.7), vein * 0.05); + // Vignette/Shadow at edges of sphere + float edge = smoothstep(1.2, 2.5, angle); + color *= (1.0 - edge * 0.8); + } + + // Iris + if (angle < uIrisSize) { + vec2 irisUV = pos.xy; + float r = length(irisUV); + float theta = atan(irisUV.y, irisUV.x); + + float f1 = snoise(vec2(theta * 10.0, r * 3.0)); + float f2 = snoise(vec2(theta * 20.0, r * 8.0 - uTime * 0.2)); + float fibers = mix(f1, f2, 0.5); + + vec3 irisBase = uIrisColor; + vec3 irisDetail = uIrisColor * 0.6; // Darker/greyer for contrast + + float outerRing = smoothstep(uIrisSize - 0.05, uIrisSize, angle); + vec3 irisFill = mix(irisBase, irisDetail, fibers * 0.5 + 0.5); + irisFill *= (1.0 - outerRing * 0.8); // Dark limbus + + color = irisFill; + + // Pupil + if (angle < uIrisSize * uPupilSize) { + color = pupilColor; + } else { + float pupilEdge = smoothstep(uIrisSize * uPupilSize, uIrisSize * uPupilSize + 0.03, angle); + color = mix(pupilColor, color, pupilEdge); + } + } + + // Soft transition iris/sclera + // (Simple override for now, can be improved) + + // Specular + vec3 lightDir = normalize(vec3(0.5, 1.0, 1.0)); + float spec = pow(max(dot(vNormal, lightDir), 0.0), 30.0); + color += vec3(1.0) * spec * 0.6; + + gl_FragColor = vec4(color, 1.0); + } + ` +); + +extend({ EyeMaterial }); + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace React { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace JSX { + interface IntrinsicElements { + eyeMaterial: ThreeElement; + } + } + } +} + +const Eyelashes = ({ radius }: { radius: number }) => { + const count = 40; + const lashes = React.useMemo(() => { + const temp = []; + // Only cover top ~120 degrees + const spreadAngle = Math.PI * 0.8; + // Center around PI/2 (Front, Z+) + const startAngle = Math.PI/2 - spreadAngle / 2; + + for (let i = 0; i < count; i++) { + const ratio = i / (count - 1); + const angle = startAngle + ratio * spreadAngle; + + const x = radius * Math.cos(angle); + const z = radius * Math.sin(angle); + + // Lash geometry + // Point outwards (radial) and curl up + const rotY = -angle; + + temp.push({ x, z, rotY }); + } + return temp; + }, [radius]); + + return ( + {/* Rim is at Y=0 */} + {lashes.map((lash, i) => ( + + {/* Curl up */} + + + + + + ))} + + ); +}; + +export const Eye = () => { + const eyeBallRef = useRef(null); + + const materialRef = useRef(null); + const topEyelidRef = useRef(null); + const bottomEyelidRef = useRef(null); + + const [targetLook, setTargetLook] = useState(new THREE.Vector3(0, 0, 10)); + const lastMouseMove = useRef(0); + + const blinkState = useRef({ + isBlinking: false, + startTime: 0, + duration: 0.15, + }); + + const randomLookTimer = useRef(0); + const EYE_RADIUS = 0.8; + + useEffect(() => { + const handleMove = () => { + lastMouseMove.current = Date.now(); + }; + window.addEventListener('mousemove', handleMove); + window.addEventListener('touchmove', handleMove); + return () => { + window.removeEventListener('mousemove', handleMove); + window.removeEventListener('touchmove', handleMove); + }; + }, []); + + useFrame((state) => { + const time = state.clock.getElapsedTime(); + if (materialRef.current) materialRef.current.uTime = time; + + // --- BLINKING --- + // Smooth blink animation + if (!blinkState.current.isBlinking) { + // Random blink trigger (approx every 3-4s) + if (Math.random() < 0.005) { + blinkState.current.isBlinking = true; + blinkState.current.startTime = time; + blinkState.current.duration = 0.18 + Math.random() * 0.05; // ~200ms + } + } + + let blinkValue = 0; // 0 = Open, 1 = Closed + if (blinkState.current.isBlinking) { + const elapsed = time - blinkState.current.startTime; + const progress = elapsed / blinkState.current.duration; + + if (progress >= 1) { + blinkState.current.isBlinking = false; + blinkValue = 0; + } else { + // Smooth sine wave 0 -> 1 -> 0 + blinkValue = Math.sin(progress * Math.PI); + } + } + + // Eyelid Rotation Logic + // With Bottom Lid flipped via Z-rotation (see JSX): + // Both lids open by rotating "Back" (Negative X in local space). + // Top Lid: Neg X -> Edge moves Up. + // Bottom Lid (Z-flipped): Neg X -> Edge moves Down (Local Up is World Down). + + // Angles in Radians + const openAngle = -0.55; // Wide open + const closedAngle = 0.05; // Slightly overlapped + + const currentAngle = THREE.MathUtils.lerp(openAngle, closedAngle, blinkValue); + + if (topEyelidRef.current) topEyelidRef.current.rotation.x = currentAngle; + if (bottomEyelidRef.current) bottomEyelidRef.current.rotation.x = currentAngle; + + // --- LOOKING --- + + // --- LOOKING --- + const mouse = state.mouse; + const now = Date.now(); + const isIdle = (now - lastMouseMove.current) > 2000; + const finalTarget = new THREE.Vector3(); + + if (!isIdle) { + // Follow Mouse + const lookScale = 6.0; + finalTarget.set(mouse.x * lookScale, mouse.y * lookScale, 5); + } else { + // Random + if (time > randomLookTimer.current) { + const rX = (Math.random() - 0.5) * 12; + const rY = (Math.random() - 0.5) * 8; + const rZ = 5 + Math.random() * 5; + setTargetLook(new THREE.Vector3(rX, rY, rZ)); + randomLookTimer.current = time + 0.5 + Math.random() * 2.0; + } + finalTarget.copy(targetLook); + } + + if (eyeBallRef.current) { + const dummy = new THREE.Object3D(); + dummy.position.copy(eyeBallRef.current.position); + dummy.lookAt(finalTarget); + eyeBallRef.current.quaternion.slerp(dummy.quaternion, 0.1); + } + }); + + return ( + + + + + + + + {/* Top Eyelid - Hemisphere */} + + {/* Sphere segment: 0 to PI/2 theta = Top Hemisphere */} + + + + + + {/* Bottom Eyelid - Flipped via Group Z-rotation */} + + + + + + + + + ); +}; diff --git a/packages/react-three-portfolio/src/components/HeroCanvas.tsx b/packages/react-three-portfolio/src/components/HeroCanvas.tsx new file mode 100644 index 00000000..91cb91b5 --- /dev/null +++ b/packages/react-three-portfolio/src/components/HeroCanvas.tsx @@ -0,0 +1,42 @@ +"use client"; + +import React, { Suspense } from 'react'; +import { Canvas } from '@react-three/fiber'; +import { HeroOrb } from './HeroOrb'; +import { Eye } from './Eye'; +import { LightningEffect } from './LightningEffect'; +import { SparkleEffect } from './SparkleEffect'; +import { PostProcessing } from './PostProcessing'; + +export function HeroCanvas() { + return ( +
+ + + + {/* Backlight for transparency */} + + + {/* Rim lights */} + + + + + + + + + + +
+ ); +} diff --git a/packages/react-three-portfolio/src/components/HeroOrb.tsx b/packages/react-three-portfolio/src/components/HeroOrb.tsx new file mode 100644 index 00000000..ba4a7bec --- /dev/null +++ b/packages/react-three-portfolio/src/components/HeroOrb.tsx @@ -0,0 +1,196 @@ +"use client"; + +import * as THREE from 'three'; +import React, { useRef } from 'react'; +import { useFrame, extend, ThreeElement } from '@react-three/fiber'; +import { shaderMaterial } from '@react-three/drei'; + +// Custom shader material for the Orb +const OrbMaterial = shaderMaterial( + { + uTime: 0, + uMouse: new THREE.Vector2(0, 0), + uColor1: new THREE.Color('#2a0044'), // Deeper, Darker Purple for core transparency + uColor2: new THREE.Color('#9d00ff'), // Electric Purple for rim + }, + // Vertex Shader + ` + varying vec2 vUv; + varying vec3 vNormal; + varying vec3 vViewPosition; + uniform float uTime; + uniform vec2 uMouse; + + // Simplex noise function + vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } + vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } + vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); } + vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } + float snoise(vec3 v) { + const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); + vec3 i = floor(v + dot(v, C.yyy) ); + vec3 x0 = v - i + dot(i, C.xxx) ; + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min( g.xyz, l.zxy ); + vec3 i2 = max( g.xyz, l.zxy ); + vec3 x1 = x0 - i1 + C.xxx; + vec3 x2 = x0 - i2 + C.yyy; + vec3 x3 = x0 - D.yyy; + i = mod289(i); + vec4 p = permute( permute( permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0 )) + + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) + + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); + float n_ = 0.142857142857; + vec3 ns = n_ * D.wyz - D.xzx; + vec4 j = p - 49.0 * floor(p * ns.z * ns.z); + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_ ); + vec4 x = x_ *ns.x + ns.yyyy; + vec4 y = y_ *ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); + vec4 b0 = vec4( x.xy, y.xy ); + vec4 b1 = vec4( x.zw, y.zw ); + vec4 s0 = floor(b0)*2.0 + 1.0; + vec4 s1 = floor(b1)*2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0)); + vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; + vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; + vec3 p0 = vec3(a0.xy,h.x); + vec3 p1 = vec3(a0.zw,h.y); + vec3 p2 = vec3(a1.xy,h.z); + vec3 p3 = vec3(a1.zw,h.w); + vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + m = m * m; + return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), + dot(p2,x2), dot(p3,x3) ) ); + } + + void main() { + vUv = uv; + vNormal = normalize(normalMatrix * normal); + + vec3 pos = position; + + // Dynamic noise parameters + float noiseFreq = 0.9; + float noiseAmp = 0.25; + float noiseSpeed = 1.2; + + // Layered noise for more detail + float n1 = snoise(pos * noiseFreq + uTime * noiseSpeed); + float n2 = snoise(pos * noiseFreq * 2.0 - uTime * noiseSpeed * 1.5) * 0.5; + + float finalNoise = n1 + n2; + + // Pulsating expansion (heartbeat style) + // Sharp attack, slow decay + float pulse = exp(-mod(uTime * 1.5, 3.14159)) * 0.2; + + // Combine noise and pulse + pos += normal * (finalNoise * noiseAmp + pulse); + + vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0); + vViewPosition = -modelViewPosition.xyz; + gl_Position = projectionMatrix * modelViewPosition; + } + `, + // Fragment Shader + ` + varying vec2 vUv; + varying vec3 vNormal; + varying vec3 vViewPosition; + uniform vec3 uColor1; + uniform vec3 uColor2; + uniform float uTime; + + void main() { + vec3 normal = normalize(vNormal); + vec3 viewDir = normalize(vViewPosition); + + // Fresnel effect for edge glow + float fresnel = pow(1.0 - max(dot(viewDir, normal), 0.0), 2.0); // Slightly softer fresnel power + + // Pulsing internal energy + float energyPulse = sin(uTime * 3.0) * 0.5 + 0.5; + + // Wave effect moving up the orb + float wave = sin(vViewPosition.y * 2.0 - uTime * 4.0); + float waveIntensity = smoothstep(0.8, 1.0, wave) * 0.5; + + // Base color mix + // uColor1 is core, uColor2 is rim + vec3 color = mix(uColor1, uColor2, fresnel + energyPulse * 0.1); + + // Add wave highlight + color += uColor2 * waveIntensity; + + // Add a modest glow to the rim + color += uColor2 * fresnel * 0.8; + + // Transparency logic + // Pulse affects alpha slightly for "breathing" effect + float alpha = 0.02 + fresnel * 0.5 + waveIntensity * 0.2; + + gl_FragColor = vec4(color, alpha); + } + ` +); + +// Register the shader material +extend({ OrbMaterial }); + +// Type definition for JSX +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace React { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace JSX { + interface IntrinsicElements { + orbMaterial: ThreeElement; + } + } + } +} + +export const HeroOrb = () => { + const meshRef = useRef(null); + + const materialRef = useRef(null); + + useFrame((state) => { + if (materialRef.current) { + materialRef.current.uTime = state.clock.getElapsedTime(); + } + + if (meshRef.current) { + meshRef.current.rotation.y += 0.001; + meshRef.current.rotation.z = Math.sin(state.clock.getElapsedTime() * 0.1) * 0.1; + } + }); + + return ( + + + {/* + Using NormalBlending for proper transparency. + side={FrontSide} ensures we look through the front face into the inside. + depthWrite={false} avoids z-buffer sorting issues with the lightning inside. + */} + + + ); +}; diff --git a/packages/react-three-portfolio/src/components/LightningEffect.tsx b/packages/react-three-portfolio/src/components/LightningEffect.tsx new file mode 100644 index 00000000..785bfcaa --- /dev/null +++ b/packages/react-three-portfolio/src/components/LightningEffect.tsx @@ -0,0 +1,160 @@ +"use client"; + +import * as THREE from 'three'; +import React, { useRef } from 'react'; +import { useFrame } from '@react-three/fiber'; + +interface Lightning { + startTime: number; + duration: number; + startPos: THREE.Vector3; + endPos: THREE.Vector3; + intensity: number; + color: THREE.Color; +} + +export const LightningEffect = () => { + const groupRef = useRef(null); + const lightningsRef = useRef([]); + const nextLightningTimeRef = useRef(0); + const linesRef = useRef([]); + + // Initialize some lightning strikes + const generateLightning = ( + startPos: THREE.Vector3, + endPos: THREE.Vector3, + segments: number = 20 + ): THREE.Vector3[] => { + const points: THREE.Vector3[] = [startPos.clone()]; + const direction = endPos.clone().sub(startPos); + const length = direction.length(); + + // Recursive midpoint displacement could be better, but iterative works for now + for (let i = 1; i < segments; i++) { + const t = i / segments; + const newPoint = startPos.clone().add(direction.clone().multiplyScalar(t)); + + // Add jagged randomness that scales with distance from start/end + // More jagged in the middle + const displacement = Math.sin(t * Math.PI) * length * 0.25; // Increased displacement + + newPoint.x += (Math.random() - 0.5) * displacement; + newPoint.y += (Math.random() - 0.5) * displacement; + newPoint.z += (Math.random() - 0.5) * displacement; + + points.push(newPoint); + } + points.push(endPos.clone()); + return points; + }; + + useFrame((state) => { + if (!groupRef.current) return; + + const currentTime = state.clock.getElapsedTime(); + + // Spawn new lightning bolts + if (currentTime > nextLightningTimeRef.current) { + // "Lightning coming from within" + // Start near center (radius < 1) + const startRadius = Math.random() * 0.5; // Closer to core + const startAnglePhi = Math.acos(2 * Math.random() - 1); + const startAngleTheta = Math.random() * Math.PI * 2; + + const startPos = new THREE.Vector3().setFromSphericalCoords( + startRadius, + startAnglePhi, + startAngleTheta + ); + + // End at or near surface (radius ~2) + // Sometimes strike slightly outside + const endRadius = 1.9 + Math.random() * 0.4; + const endAnglePhi = Math.acos(2 * Math.random() - 1); + const endAngleTheta = Math.random() * Math.PI * 2; + + const endPos = new THREE.Vector3().setFromSphericalCoords( + endRadius, + endAnglePhi, + endAngleTheta + ); + + lightningsRef.current.push({ + startTime: currentTime, + duration: 0.1 + Math.random() * 0.15, // Faster + startPos, + endPos, + intensity: 2.0 + Math.random() * 2.0, // MUCH Brighter (up to 4.0) + color: new THREE.Color().setHSL(0.75 + Math.random() * 0.1, 1.0, 0.9), // Very bright electric purple + }); + + // Spawn frequency - faster + nextLightningTimeRef.current = currentTime + 0.05 + Math.random() * 0.15; + } + + // Update existing lightning bolts + lightningsRef.current = lightningsRef.current.filter((lightning) => { + const elapsed = currentTime - lightning.startTime; + + // Find existing line for this lightning + const existingLineIndex = linesRef.current.findIndex( + (line) => line.userData.lightningId === lightning.startTime + ); + + // Clean up previous frame's line + if (existingLineIndex >= 0) { + const existingLine = linesRef.current[existingLineIndex]; + if (existingLine) { + groupRef.current?.remove(existingLine); + + // Dispose resources + existingLine.geometry.dispose(); + if (existingLine.material instanceof THREE.Material) { + existingLine.material.dispose(); + } else if (Array.isArray(existingLine.material)) { + existingLine.material.forEach(m => m.dispose()); + } + + linesRef.current.splice(existingLineIndex, 1); + } + } + + // If lightning is finished, don't create a new one + if (elapsed > lightning.duration) { + return false; + } + + // Create new line geometry for current frame to animate the jitter + // Increased segments for more jagged look + const points = generateLightning( + lightning.startPos, + lightning.endPos, + 40 + Math.floor(Math.random() * 20) + ); + + const geometry = new THREE.BufferGeometry().setFromPoints(points); + const alpha = (1 - elapsed / lightning.duration) * lightning.intensity; + + // Flicker effect + const flicker = Math.random() > 0.3 ? 1.0 : 0.2; + + const lineMaterial = new THREE.LineBasicMaterial({ + color: lightning.color, + linewidth: 5, // Attempt thicker lines + transparent: true, + opacity: alpha * flicker, + blending: THREE.AdditiveBlending, + depthWrite: false, + }); + + const line = new THREE.LineSegments(geometry, lineMaterial); + line.userData.lightningId = lightning.startTime; + groupRef.current?.add(line); + linesRef.current.push(line); + + return true; + }); + }); + + return ; +}; diff --git a/packages/react-three-portfolio/src/components/PostProcessing.tsx b/packages/react-three-portfolio/src/components/PostProcessing.tsx new file mode 100644 index 00000000..c539c5da --- /dev/null +++ b/packages/react-three-portfolio/src/components/PostProcessing.tsx @@ -0,0 +1,35 @@ +"use client"; + +import * as THREE from "three"; +import React from "react"; +import { EffectComposer, Bloom, Noise, Vignette, ChromaticAberration } from "@react-three/postprocessing"; +import { BlendFunction } from "postprocessing"; +import { Vector2 } from "three"; + +export const PostProcessing = () => { + return ( + + + + + + + + + + ) +} diff --git a/packages/react-three-portfolio/src/components/SparkleEffect.tsx b/packages/react-three-portfolio/src/components/SparkleEffect.tsx new file mode 100644 index 00000000..fe518d31 --- /dev/null +++ b/packages/react-three-portfolio/src/components/SparkleEffect.tsx @@ -0,0 +1,191 @@ +"use client"; + +import * as THREE from 'three'; +import React, { useRef, useState } from 'react'; +import { useFrame, extend, ThreeElement } from '@react-three/fiber'; +import { shaderMaterial } from '@react-three/drei'; + +// Custom shader for realistic glowing sparkles +const SparkleMaterial = shaderMaterial( + { + uTime: 0, + uColor: new THREE.Color(1, 1, 1), + }, + // Vertex Shader + ` + attribute float size; + attribute vec3 color; + varying vec3 vColor; + varying float vAlpha; + uniform float uTime; + + void main() { + vColor = color; + vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); + + // Size attenuation + gl_PointSize = size * (300.0 / -mvPosition.z); + gl_Position = projectionMatrix * mvPosition; + } + `, + // Fragment Shader + ` + varying vec3 vColor; + + void main() { + // Create a soft circle/glow + vec2 xy = gl_PointCoord.xy - vec2(0.5); + float r = length(xy); + + // Soft edge: Gaussian-ish falloff + float alpha = 1.0 - smoothstep(0.3, 0.5, r); + + // Discard outer pixels for performance and round shape + if (alpha < 0.01) discard; + + gl_FragColor = vec4(vColor, alpha); + } + ` +); + +extend({ SparkleMaterial }); + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace React { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace JSX { + interface IntrinsicElements { + sparkleMaterial: ThreeElement; + } + } + } +} + +interface Particle { + position: THREE.Vector3; + velocity: THREE.Vector3; + life: number; + maxLife: number; + size: number; + color: THREE.Color; +} + +export const SparkleEffect = () => { + const particlesRef = useRef([]); + const pointsRef = useRef(null); + const nextSpawnTimeRef = useRef(0); + + const materialRef = useRef(null); + + const [geometry] = useState(() => new THREE.BufferGeometry()); + + useFrame((state, delta) => { + const currentTime = state.clock.getElapsedTime(); + if (materialRef.current) { + materialRef.current.uTime = currentTime; + } + + // Spawn new sparkles - MORE particles for "full reality" + if (currentTime > nextSpawnTimeRef.current) { + const spawnCount = 4 + Math.floor(Math.random() * 5); // Increased spawn rate + for (let i = 0; i < spawnCount; i++) { + // Spawn within the orb volume mostly + const anglePhi = Math.acos(2 * Math.random() - 1); + const angleTheta = Math.random() * Math.PI * 2; + const radius = Math.pow(Math.random(), 1/3) * 2.2; // Cube root for uniform distribution in sphere + + const position = new THREE.Vector3().setFromSphericalCoords( + radius, + anglePhi, + angleTheta + ); + + // Gentle drift velocity + const velocity = new THREE.Vector3( + (Math.random() - 0.5) * 0.5, // Faster movement + (Math.random() - 0.5) * 0.5, + (Math.random() - 0.5) * 0.5 + ); + + // Add orbital motion + const tangent = new THREE.Vector3(-position.z, 0, position.x).normalize().multiplyScalar(0.8); + velocity.add(tangent); + + particlesRef.current.push({ + position, + velocity, + life: 0, + maxLife: 1.5 + Math.random() * 1.5, + size: 0.1 + Math.random() * 0.15, // SMALLER size for "sparkle" effect + color: new THREE.Color().setHSL(0.7 + Math.random() * 0.15, 1.0, 0.9), // Very Bright + }); + } + nextSpawnTimeRef.current = currentTime + 0.05; // Faster spawn interval + } + + // Update particles + particlesRef.current = particlesRef.current.filter((particle) => { + particle.life += delta; + + if (particle.life > particle.maxLife) { + return false; + } + + // Move particle + particle.position.add(particle.velocity.clone().multiplyScalar(delta)); + + // Slight drag + particle.velocity.multiplyScalar(0.98); + + // Swirling force towards center or around Y axis + const toCenter = particle.position.clone().negate().normalize().multiplyScalar(0.2 * delta); + particle.velocity.add(toCenter); + + return true; + }); + + // Update geometry + if (particlesRef.current.length > 0 && pointsRef.current) { + const positions = new Float32Array(particlesRef.current.length * 3); + const colors = new Float32Array(particlesRef.current.length * 3); + const sizes = new Float32Array(particlesRef.current.length); + + particlesRef.current.forEach((particle, i) => { + positions[i * 3] = particle.position.x; + positions[i * 3 + 1] = particle.position.y; + positions[i * 3 + 2] = particle.position.z; + + const lifeRatio = particle.life / particle.maxLife; + // Fade in and out + const alpha = Math.sin(Math.PI * lifeRatio); + + colors[i * 3] = particle.color.r * alpha; // Premultiplied-ish + colors[i * 3 + 1] = particle.color.g * alpha; + colors[i * 3 + 2] = particle.color.b * alpha; + + sizes[i] = particle.size * alpha; + }); + + const geo = pointsRef.current.geometry; + geo.setAttribute('position', new THREE.BufferAttribute(positions, 3)); + geo.setAttribute('color', new THREE.BufferAttribute(colors, 3)); + geo.setAttribute('size', new THREE.BufferAttribute(sizes, 1)); // Custom attribute + + geo.attributes.position!.needsUpdate = true; + geo.attributes.color!.needsUpdate = true; + geo.attributes.size!.needsUpdate = true; + } + }); + + return ( + + + + ); +}; diff --git a/packages/react-three-portfolio/src/index.test.ts b/packages/react-three-portfolio/src/index.test.ts new file mode 100644 index 00000000..3fbcbce7 --- /dev/null +++ b/packages/react-three-portfolio/src/index.test.ts @@ -0,0 +1,42 @@ +import { describe, it, expect } from 'vitest'; +import * as exports from './index'; + +describe('react-three-portfolio', () => { + it('should export all expected components', () => { + expect(exports.HeroCanvas).toBeDefined(); + expect(exports.HeroOrb).toBeDefined(); + expect(exports.EnergyWaves).toBeDefined(); + expect(exports.Eye).toBeDefined(); + expect(exports.LightningEffect).toBeDefined(); + expect(exports.SparkleEffect).toBeDefined(); + expect(exports.PostProcessing).toBeDefined(); + }); + + it('should have HeroCanvas as a function', () => { + expect(typeof exports.HeroCanvas).toBe('function'); + }); + + it('should have HeroOrb as a function', () => { + expect(typeof exports.HeroOrb).toBe('function'); + }); + + it('should have EnergyWaves as a function', () => { + expect(typeof exports.EnergyWaves).toBe('function'); + }); + + it('should have Eye as a function', () => { + expect(typeof exports.Eye).toBe('function'); + }); + + it('should have LightningEffect as a function', () => { + expect(typeof exports.LightningEffect).toBe('function'); + }); + + it('should have SparkleEffect as a function', () => { + expect(typeof exports.SparkleEffect).toBe('function'); + }); + + it('should have PostProcessing as a function', () => { + expect(typeof exports.PostProcessing).toBe('function'); + }); +}); diff --git a/packages/react-three-portfolio/src/index.ts b/packages/react-three-portfolio/src/index.ts new file mode 100644 index 00000000..bf514194 --- /dev/null +++ b/packages/react-three-portfolio/src/index.ts @@ -0,0 +1,8 @@ +// Main components +export { HeroCanvas } from './components/HeroCanvas'; +export { HeroOrb } from './components/HeroOrb'; +export { EnergyWaves } from './components/EnergyWaves'; +export { Eye } from './components/Eye'; +export { LightningEffect } from './components/LightningEffect'; +export { SparkleEffect } from './components/SparkleEffect'; +export { PostProcessing } from './components/PostProcessing'; diff --git a/packages/react-three-portfolio/tsconfig.base.json b/packages/react-three-portfolio/tsconfig.base.json new file mode 100644 index 00000000..7f851161 --- /dev/null +++ b/packages/react-three-portfolio/tsconfig.base.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noUncheckedIndexedAccess": true, + "noEmit": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "jsx": "preserve" + }, + "exclude": ["node_modules", "dist"] +} diff --git a/packages/react-three-portfolio/tsconfig.build.json b/packages/react-three-portfolio/tsconfig.build.json new file mode 100644 index 00000000..3a37284a --- /dev/null +++ b/packages/react-three-portfolio/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": false, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/react-three-portfolio/tsconfig.json b/packages/react-three-portfolio/tsconfig.json new file mode 100644 index 00000000..2c69407f --- /dev/null +++ b/packages/react-three-portfolio/tsconfig.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"] +} diff --git a/packages/react-three-portfolio/tsup.config.ts b/packages/react-three-portfolio/tsup.config.ts new file mode 100644 index 00000000..7391ece3 --- /dev/null +++ b/packages/react-three-portfolio/tsup.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['cjs', 'esm'], + dts: true, + splitting: false, + sourcemap: true, + clean: true, + minify: false, + treeshake: true, + external: ['react', 'react-dom', 'three', '@react-three/fiber', '@react-three/drei', '@react-three/postprocessing', 'postprocessing'], +}); diff --git a/packages/react-three-portfolio/vitest.config.ts b/packages/react-three-portfolio/vitest.config.ts new file mode 100644 index 00000000..06930918 --- /dev/null +++ b/packages/react-three-portfolio/vitest.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'jsdom', + include: ['src/**/*.test.ts', 'src/**/*.test.tsx', 'test/**/*.test.ts', 'test/**/*.test.tsx'], + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + exclude: [ + 'node_modules/', + 'dist/', + '**/*.d.ts', + '**/*.test.ts', + '**/*.test.tsx', + '**/*.config.ts', + ], + }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b0f07cb..a3284d70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,7 +170,7 @@ importers: devDependencies: '@opensourceframework/tsconfig': specifier: '*' - version: 1.0.0 + version: link:../../tools/tsconfig '@vitest/coverage-v8': specifier: ^2.1.9 version: 2.1.9(vitest@2.1.9(@types/node@25.4.0)(happy-dom@20.8.3)(jsdom@25.0.1)(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.4.0)(typescript@5.9.3))(terser@5.46.0)) @@ -225,7 +225,7 @@ importers: version: 29.7.0(@babel/core@7.29.0) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.19.15)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) + version: 29.7.0(@types/node@25.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) packages/next-auth: dependencies: @@ -1139,6 +1139,52 @@ importers: specifier: ^2.1.9 version: 2.1.9(@types/node@22.19.11)(happy-dom@20.8.3)(jsdom@25.0.1)(lightningcss@1.32.0)(msw@2.12.10(@types/node@22.19.11)(typescript@5.9.3))(terser@5.46.0) + packages/react-three-portfolio: + dependencies: + '@react-three/drei': + specifier: ^10.7.7 + version: 10.7.7(@react-three/fiber@9.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0))(@types/react@19.2.14)(@types/three@0.160.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0) + '@react-three/fiber': + specifier: ^9.5.0 + version: 9.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0) + '@react-three/postprocessing': + specifier: ^3.0.4 + version: 3.0.4(@react-three/fiber@9.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0))(@types/three@0.160.0)(react@19.2.4)(three@0.182.0) + postprocessing: + specifier: ^6.36.6 + version: 6.39.0(three@0.182.0) + react: + specifier: '>=18.0.0' + version: 19.2.4 + react-dom: + specifier: '>=18.0.0' + version: 19.2.4(react@19.2.4) + three: + specifier: ^0.182.0 + version: 0.182.0 + devDependencies: + '@types/three': + specifier: ^0.160.0 + version: 0.160.0 + eslint: + specifier: ^9.0.0 + version: 9.39.4(jiti@2.6.1) + eslint-plugin-react: + specifier: ^7.37.0 + version: 7.37.5(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-react-hooks: + specifier: ^5.0.0 + version: 5.2.0(eslint@9.39.4(jiti@2.6.1)) + tsup: + specifier: ^8.5.1 + version: 8.5.1(@swc/core@1.15.13)(jiti@2.6.1)(postcss@8.5.8)(tsx@4.19.3)(typescript@5.9.3)(yaml@2.8.2) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^2.1.9 + version: 2.1.9(@types/node@25.4.0)(happy-dom@20.8.3)(jsdom@25.0.1)(lightningcss@1.32.0)(msw@2.12.10(@types/node@25.4.0)(typescript@5.9.3))(terser@5.46.0) + packages/react-virtualized: dependencies: '@babel/runtime': @@ -1270,7 +1316,7 @@ importers: version: 2.1.1 lucide-react: specifier: latest - version: 0.577.0(react@19.2.4) + version: 1.6.0(react@19.2.4) next: specifier: '>=16.1.7' version: 16.2.0(@babel/core@7.29.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -1301,7 +1347,7 @@ importers: version: 8.5.8 tailwindcss: specifier: latest - version: 4.2.1 + version: 4.2.2 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -2585,6 +2631,10 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-array@0.23.2': resolution: {integrity: sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -2593,10 +2643,18 @@ packages: resolution: {integrity: sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-helpers@0.5.2': resolution: {integrity: sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@1.1.0': resolution: {integrity: sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -2613,6 +2671,10 @@ packages: resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@10.0.1': resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -2630,10 +2692,22 @@ packages: resolution: {integrity: sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/object-schema@3.0.3': resolution: {integrity: sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/plugin-kit@0.6.0': resolution: {integrity: sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -3121,9 +3195,17 @@ packages: '@types/react': ^19.0.0 react: '>=16' + '@mediapipe/tasks-vision@0.10.17': + resolution: {integrity: sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==} + '@mermaid-js/parser@1.0.1': resolution: {integrity: sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ==} + '@monogrid/gainmap-js@3.4.0': + resolution: {integrity: sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==} + peerDependencies: + three: '>= 0.159.0' + '@mswjs/interceptors@0.41.3': resolution: {integrity: sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==} engines: {node: '>=18'} @@ -3321,9 +3403,6 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - '@opensourceframework/tsconfig@1.0.0': - resolution: {integrity: sha512-T1fr6BMd+VnSdsjfU8s9SXHysC/ghPqAMX1eLF1uyNfRHTr2sKnUAXeputattGTN3IULlcHiKjFf8TI99M8BHg==} - '@panva/asn1.js@1.0.0': resolution: {integrity: sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==} engines: {node: '>=10.13.0'} @@ -3390,6 +3469,49 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@react-three/drei@10.7.7': + resolution: {integrity: sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==} + peerDependencies: + '@react-three/fiber': ^9.0.0 + react: ^19 + react-dom: ^19 + three: '>=0.159' + peerDependenciesMeta: + react-dom: + optional: true + + '@react-three/fiber@9.5.0': + resolution: {integrity: sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA==} + peerDependencies: + expo: '>=43.0' + expo-asset: '>=8.4' + expo-file-system: '>=11.0' + expo-gl: '>=11.0' + react: '>=19 <19.3' + react-dom: '>=19 <19.3' + react-native: '>=0.78' + three: '>=0.156' + peerDependenciesMeta: + expo: + optional: true + expo-asset: + optional: true + expo-file-system: + optional: true + expo-gl: + optional: true + react-dom: + optional: true + react-native: + optional: true + + '@react-three/postprocessing@3.0.4': + resolution: {integrity: sha512-e4+F5xtudDYvhxx3y0NtWXpZbwvQ0x1zdOXWTbXMK6fFLVDd4qucN90YaaStanZGS4Bd5siQm0lGL/5ogf8iDQ==} + peerDependencies: + '@react-three/fiber': ^9.0.0 + react: ^19.0 + three: '>= 0.156.0' + '@react-types/shared@3.33.1': resolution: {integrity: sha512-oJHtjvLG43VjwemQDadlR5g/8VepK56B/xKO2XORPHt9zlW6IZs3tZrYlvH29BMvoqC7RtE7E5UjgbnbFtDGag==} peerDependencies: @@ -3649,8 +3771,8 @@ packages: '@sinclair/typebox@0.27.10': resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} - '@sinclair/typebox@0.34.49': - resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==} + '@sinclair/typebox@0.34.48': + resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} @@ -4054,6 +4176,9 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/draco3d@1.4.10': + resolution: {integrity: sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==} + '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} @@ -4150,6 +4275,9 @@ packages: '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/offscreencanvas@2019.7.3': + resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==} + '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -4164,6 +4292,11 @@ packages: peerDependencies: '@types/react': ^19.0.0 + '@types/react-reconciler@0.28.9': + resolution: {integrity: sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==} + peerDependencies: + '@types/react': ^19.0.0 + '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} @@ -4179,12 +4312,18 @@ packages: '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + '@types/stats.js@0.17.4': + resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} '@types/testing-library__jest-dom@5.14.9': resolution: {integrity: sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==} + '@types/three@0.160.0': + resolution: {integrity: sha512-jWlbUBovicUKaOYxzgkLlhkiEQJkhCVvg4W2IYD2trqD2om3VK4DGLpHH5zQHNr7RweZK/5re/4IVhbhvxbV9w==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -4194,6 +4333,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/webxr@0.5.24': + resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + '@types/whatwg-mimetype@3.0.2': resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} @@ -4558,6 +4700,14 @@ packages: '@upsetjs/venn.js@2.0.0': resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} + '@use-gesture/core@10.3.1': + resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} + + '@use-gesture/react@10.3.1': + resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==} + peerDependencies: + react: '>= 16.8.0' + '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -5112,12 +5262,15 @@ packages: bare-path@3.0.0: resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} - bare-stream@2.10.0: - resolution: {integrity: sha512-DOPZF/DDcDruKDA43cOw6e9Quq5daua7ygcAwJE/pKJsRWhgSSemi7qVNGE5kyDIxIeN1533G/zfbvWX7Wcb9w==} + bare-stream@2.11.0: + resolution: {integrity: sha512-Y/+iQ49fL3rIn6w/AVxI/2+BRrpmzJvdWt5Jv8Za6Ngqc6V227c+pYjYYgLdpR3MwQ9ObVXD0ZrqoBztakM0rw==} peerDependencies: + bare-abort-controller: '*' bare-buffer: '*' bare-events: '*' peerDependenciesMeta: + bare-abort-controller: + optional: true bare-buffer: optional: true bare-events: @@ -5147,6 +5300,9 @@ packages: peerDependencies: react: '>=16.8' + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} @@ -5258,6 +5414,12 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + camera-controls@3.1.2: + resolution: {integrity: sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==} + engines: {node: '>=22.0.0', npm: '>=10.5.1'} + peerDependencies: + three: '>=0.126.1' + caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} @@ -5584,6 +5746,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + cross-fetch@4.0.0: resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} @@ -5994,6 +6161,9 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-gpu@5.0.70: + resolution: {integrity: sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==} + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -6093,6 +6263,9 @@ packages: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} engines: {node: '>=10'} + draco3d@1.5.7: + resolution: {integrity: sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -6415,6 +6588,12 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + 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-plugin-react@7.37.5: resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} engines: {node: '>=4'} @@ -6435,6 +6614,10 @@ packages: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-scope@9.1.1: resolution: {integrity: sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -6459,6 +6642,10 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -6495,10 +6682,24 @@ packages: deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@11.1.1: resolution: {integrity: sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -6688,6 +6889,9 @@ packages: picomatch: optional: true + fflate@0.6.10: + resolution: {integrity: sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==} + fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -6990,6 +7194,10 @@ packages: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + globals@16.5.0: resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} @@ -7015,6 +7223,9 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + glsl-noise@0.0.0: + resolution: {integrity: sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -7141,6 +7352,9 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + hls.js@1.6.15: + resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==} + homedir-polyfill@1.0.3: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} engines: {node: '>=0.10.0'} @@ -7237,6 +7451,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + immutable@5.1.5: resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} @@ -7478,6 +7695,9 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-promise@2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -7595,6 +7815,11 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} + its-fine@2.0.0: + resolution: {integrity: sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==} + peerDependencies: + react: ^19.0.0 + jackspeak@2.3.6: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} engines: {node: '>=14'} @@ -8152,6 +8377,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + light-my-request@6.6.0: resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} @@ -8372,8 +8600,8 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - lucide-react@0.577.0: - resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==} + lucide-react@1.6.0: + resolution: {integrity: sha512-YxLKVCOF5ZDI1AhKQE5IBYMY9y/Nr4NT15+7QEWpsTSVCdn4vmZhww+6BP76jWYjQx8rSz1Z+gGme1f+UycWEw==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -8381,6 +8609,18 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true + maath@0.10.8: + resolution: {integrity: sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==} + peerDependencies: + '@types/three': '>=0.134.0' + three: '>=0.134.0' + + maath@0.6.0: + resolution: {integrity: sha512-dSb2xQuP7vDnaYqfoKzlApeRcR2xtN8/f7WV/TMAkBC8552TwTLtOO0JTcSygkYMjNDPoo6V01jTw/aPi4JrMw==} + peerDependencies: + '@types/three': '>=0.144.0' + three: '>=0.144.0' + magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} @@ -8547,6 +8787,14 @@ packages: mermaid@11.13.0: resolution: {integrity: sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw==} + meshline@3.3.1: + resolution: {integrity: sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==} + peerDependencies: + three: '>=0.137' + + meshoptimizer@0.18.1: + resolution: {integrity: sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==} + mhchemparser@4.2.1: resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} @@ -8771,6 +9019,12 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + n8ao@1.10.1: + resolution: {integrity: sha512-hhI1pC+BfOZBV1KMwynBrVlIm8wqLxj/abAWhF2nZ0qQKyzTSQa1QtLVS2veRiuoBQXojxobcnp0oe+PUoxf/w==} + peerDependencies: + postprocessing: '>=6.30.0' + three: '>=0.137' + nano-spawn@2.0.0: resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==} engines: {node: '>=20.17'} @@ -9740,6 +9994,14 @@ packages: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} + postprocessing@6.39.0: + resolution: {integrity: sha512-/G6JY8hs426lcto/pBZlnFSkyEo1fHsh4gy7FPJtq1SaSUOzJgDW6f6f1K/+aMOYzK/eQEefyOb3++jPPIUeDA==} + peerDependencies: + three: '>= 0.168.0 < 0.184.0' + + potpack@1.0.2: + resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==} + preact-render-to-string@5.2.6: resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} peerDependencies: @@ -9824,6 +10086,9 @@ packages: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} + promise-worker-transferable@1.0.4: + resolution: {integrity: sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==} + promise.series@0.2.0: resolution: {integrity: sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==} engines: {node: '>=0.12'} @@ -9964,6 +10229,15 @@ packages: peerDependencies: react: 17.0.2 + react-use-measure@2.1.7: + resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} + peerDependencies: + react: '>=16.13' + react-dom: '>=16.13' + peerDependenciesMeta: + react-dom: + optional: true + react@17.0.2: resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} engines: {node: '>=0.10.0'} @@ -10598,6 +10872,15 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + stats-gl@2.4.2: + resolution: {integrity: sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==} + peerDependencies: + '@types/three': '*' + three: '*' + + stats.js@0.17.0: + resolution: {integrity: sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==} + statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -10824,6 +11107,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + suspend-react@0.1.3: + resolution: {integrity: sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==} + peerDependencies: + react: '>=17.0' + svg-tags@1.0.0: resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} @@ -10867,9 +11155,6 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tailwindcss@4.2.1: - resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} - tailwindcss@4.2.2: resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} @@ -10951,6 +11236,19 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + three-mesh-bvh@0.8.3: + resolution: {integrity: sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==} + peerDependencies: + three: '>= 0.159.0' + + three-stdlib@2.36.1: + resolution: {integrity: sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==} + peerDependencies: + three: '>=0.128.0' + + three@0.182.0: + resolution: {integrity: sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==} + through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -11064,6 +11362,19 @@ packages: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} + troika-three-text@0.52.4: + resolution: {integrity: sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==} + peerDependencies: + three: '>=0.125.0' + + troika-three-utils@0.52.4: + resolution: {integrity: sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==} + peerDependencies: + three: '>=0.125.0' + + troika-worker-utils@0.52.0: + resolution: {integrity: sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==} + trough@1.0.5: resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} @@ -11175,6 +11486,9 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tunnel-rat@0.1.2: + resolution: {integrity: sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==} + turbo-darwin-64@1.13.4: resolution: {integrity: sha512-A0eKd73R7CGnRinTiS7txkMElg+R5rKFp9HV7baDiEL4xTG1FIg/56Vm7A5RVgg8UNgG2qNnrfatJtb+dRmNdw==} cpu: [x64] @@ -11580,6 +11894,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utility-types@3.11.0: + resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} + engines: {node: '>= 4'} + uuid@11.1.0: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true @@ -11827,6 +12145,12 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + webgl-constants@1.1.1: + resolution: {integrity: sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==} + + webgl-sdf-generator@1.1.1: + resolution: {integrity: sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -12083,6 +12407,21 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': ^19.0.0 + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + zustand@5.0.11: resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==} engines: {node: '>=12.20.0'} @@ -13646,8 +13985,21 @@ snapshots: eslint: 8.57.1 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))': + dependencies: + eslint: 9.39.4(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.2': {} + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + '@eslint/config-array@0.23.2': dependencies: '@eslint/object-schema': 3.0.3 @@ -13664,10 +14016,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + '@eslint/config-helpers@0.5.2': dependencies: '@eslint/core': 1.1.1 + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/core@1.1.0': dependencies: '@types/json-schema': 7.0.15 @@ -13704,6 +14064,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + '@eslint/js@10.0.1(eslint@10.0.3(jiti@2.6.1))': optionalDependencies: eslint: 10.0.3(jiti@2.6.1) @@ -13712,8 +14086,17 @@ snapshots: '@eslint/js@9.39.3': {} + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + '@eslint/object-schema@3.0.3': {} + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + '@eslint/plugin-kit@0.6.0': dependencies: '@eslint/core': 1.1.1 @@ -14350,7 +14733,7 @@ snapshots: '@jest/schemas@30.0.5': dependencies: - '@sinclair/typebox': 0.34.49 + '@sinclair/typebox': 0.34.48 '@jest/source-map@28.1.2': dependencies: @@ -14555,10 +14938,17 @@ snapshots: '@types/react': 19.2.14 react: 19.2.4 + '@mediapipe/tasks-vision@0.10.17': {} + '@mermaid-js/parser@1.0.1': dependencies: langium: 4.2.1 + '@monogrid/gainmap-js@3.4.0(three@0.182.0)': + dependencies: + promise-worker-transferable: 1.0.4 + three: 0.182.0 + '@mswjs/interceptors@0.41.3': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -14704,8 +15094,6 @@ snapshots: '@open-draft/until@2.1.0': {} - '@opensourceframework/tsconfig@1.0.0': {} - '@panva/asn1.js@1.0.0': {} '@pkgjs/parseargs@0.11.0': @@ -14784,6 +15172,70 @@ snapshots: '@swc/helpers': 0.5.15 react: 18.3.1 + '@react-three/drei@10.7.7(@react-three/fiber@9.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0))(@types/react@19.2.14)(@types/three@0.160.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0)': + dependencies: + '@babel/runtime': 7.28.6 + '@mediapipe/tasks-vision': 0.10.17 + '@monogrid/gainmap-js': 3.4.0(three@0.182.0) + '@react-three/fiber': 9.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0) + '@use-gesture/react': 10.3.1(react@19.2.4) + camera-controls: 3.1.2(three@0.182.0) + cross-env: 7.0.3 + detect-gpu: 5.0.70 + glsl-noise: 0.0.0 + hls.js: 1.6.15 + maath: 0.10.8(@types/three@0.160.0)(three@0.182.0) + meshline: 3.3.1(three@0.182.0) + react: 19.2.4 + stats-gl: 2.4.2(@types/three@0.160.0)(three@0.182.0) + stats.js: 0.17.0 + suspend-react: 0.1.3(react@19.2.4) + three: 0.182.0 + three-mesh-bvh: 0.8.3(three@0.182.0) + three-stdlib: 2.36.1(three@0.182.0) + troika-three-text: 0.52.4(three@0.182.0) + tunnel-rat: 0.1.2(@types/react@19.2.14)(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.4) + utility-types: 3.11.0 + zustand: 5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) + optionalDependencies: + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - '@types/three' + - immer + + '@react-three/fiber@9.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0)': + dependencies: + '@babel/runtime': 7.28.6 + '@types/webxr': 0.5.24 + base64-js: 1.5.1 + buffer: 6.0.3 + its-fine: 2.0.0(@types/react@19.2.14)(react@19.2.4) + react: 19.2.4 + react-use-measure: 2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + scheduler: 0.27.0 + suspend-react: 0.1.3(react@19.2.4) + three: 0.182.0 + use-sync-external-store: 1.6.0(react@19.2.4) + zustand: 5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) + optionalDependencies: + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - immer + + '@react-three/postprocessing@3.0.4(@react-three/fiber@9.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0))(@types/three@0.160.0)(react@19.2.4)(three@0.182.0)': + dependencies: + '@react-three/fiber': 9.5.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(three@0.182.0) + maath: 0.6.0(@types/three@0.160.0)(three@0.182.0) + n8ao: 1.10.1(postprocessing@6.39.0(three@0.182.0))(three@0.182.0) + postprocessing: 6.39.0(three@0.182.0) + react: 19.2.4 + three: 0.182.0 + transitivePeerDependencies: + - '@types/three' + '@react-types/shared@3.33.1(react@18.3.1)': dependencies: react: 18.3.1 @@ -15011,7 +15463,7 @@ snapshots: '@sinclair/typebox@0.27.10': {} - '@sinclair/typebox@0.34.49': {} + '@sinclair/typebox@0.34.48': {} '@sindresorhus/is@4.6.0': {} @@ -15433,6 +15885,8 @@ snapshots: '@types/deep-eql@4.0.2': {} + '@types/draco3d@1.4.10': {} + '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.1 @@ -15541,6 +15995,8 @@ snapshots: '@types/normalize-package-data@2.4.4': {} + '@types/offscreencanvas@2019.7.3': {} + '@types/parse-json@4.0.2': {} '@types/prettier@2.7.3': {} @@ -15553,6 +16009,10 @@ snapshots: dependencies: '@types/react': 19.2.14 + '@types/react-reconciler@0.28.9(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + '@types/react@19.2.14': dependencies: csstype: 3.2.3 @@ -15567,18 +16027,29 @@ snapshots: '@types/stack-utils@2.0.3': {} + '@types/stats.js@0.17.4': {} + '@types/statuses@2.0.6': {} '@types/testing-library__jest-dom@5.14.9': dependencies: '@types/jest': 30.0.0 + '@types/three@0.160.0': + dependencies: + '@types/stats.js': 0.17.4 + '@types/webxr': 0.5.24 + fflate: 0.6.10 + meshoptimizer: 0.18.1 + '@types/trusted-types@2.0.7': {} '@types/unist@2.0.11': {} '@types/unist@3.0.3': {} + '@types/webxr@0.5.24': {} + '@types/whatwg-mimetype@3.0.2': {} '@types/ws@8.18.1': @@ -15787,7 +16258,7 @@ snapshots: '@typescript-eslint/project-service@8.56.1(typescript@5.9.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3) - '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/types': 8.57.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -16163,6 +16634,13 @@ snapshots: d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) + '@use-gesture/core@10.3.1': {} + + '@use-gesture/react@10.3.1(react@19.2.4)': + dependencies: + '@use-gesture/core': 10.3.1 + react: 19.2.4 + '@vitejs/plugin-react@4.7.0(vite@7.3.1(@types/node@22.19.11)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.19.3)(yaml@2.8.2))': dependencies: '@babel/core': 7.29.0 @@ -16964,7 +17442,7 @@ snapshots: dependencies: bare-events: 2.8.2 bare-path: 3.0.0 - bare-stream: 2.10.0(bare-events@2.8.2) + bare-stream: 2.11.0(bare-events@2.8.2) bare-url: 2.4.0 fast-fifo: 1.3.2 transitivePeerDependencies: @@ -16980,14 +17458,13 @@ snapshots: bare-os: 3.8.0 optional: true - bare-stream@2.10.0(bare-events@2.8.2): + bare-stream@2.11.0(bare-events@2.8.2): dependencies: streamx: 2.25.0 teex: 1.0.1 optionalDependencies: bare-events: 2.8.2 transitivePeerDependencies: - - bare-abort-controller - react-native-b4a optional: true @@ -17011,6 +17488,10 @@ snapshots: mathjax-full: 3.2.2 react: 18.3.1 + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + big.js@5.2.2: {} binary-extensions@2.3.0: {} @@ -17124,6 +17605,10 @@ snapshots: camelcase@6.3.0: {} + camera-controls@3.1.2(three@0.182.0): + dependencies: + three: 0.182.0 + caniuse-api@3.0.0: dependencies: browserslist: 4.28.1 @@ -17487,13 +17972,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@22.19.15)(babel-plugin-macros@3.1.0): + create-jest@29.7.0(@types/node@24.10.13): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.19.15)(babel-plugin-macros@3.1.0) + jest-config: 29.7.0(@types/node@24.10.13) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -17501,14 +17986,15 @@ snapshots: - babel-plugin-macros - supports-color - ts-node + optional: true - create-jest@29.7.0(@types/node@24.10.13): + create-jest@29.7.0(@types/node@25.4.0): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@24.10.13) + jest-config: 29.7.0(@types/node@25.4.0) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -17518,13 +18004,13 @@ snapshots: - ts-node optional: true - create-jest@29.7.0(@types/node@25.4.0): + create-jest@29.7.0(@types/node@25.4.0)(babel-plugin-macros@3.1.0): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@25.4.0) + jest-config: 29.7.0(@types/node@25.4.0)(babel-plugin-macros@3.1.0) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -17532,7 +18018,10 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - optional: true + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.6 cross-fetch@4.0.0(encoding@0.1.13): dependencies: @@ -18026,6 +18515,10 @@ snapshots: dequal@2.0.3: {} + detect-gpu@5.0.70: + dependencies: + webgl-constants: 1.1.1 + detect-indent@6.1.0: {} detect-indent@7.0.2: {} @@ -18117,6 +18610,8 @@ snapshots: dotenv@8.6.0: {} + draco3d@1.5.7: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -18668,6 +19163,10 @@ snapshots: dependencies: eslint: 8.57.1 + eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(jiti@2.6.1)): + dependencies: + eslint: 9.39.4(jiti@2.6.1) + eslint-plugin-react@7.37.5(eslint@8.57.1): dependencies: array-includes: 3.1.9 @@ -18690,6 +19189,28 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 + eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.6.1)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.1 + eslint: 9.39.4(jiti@2.6.1) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + eslint-plugin-testing-library@5.11.1(eslint@8.57.1)(typescript@4.9.5): dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@4.9.5) @@ -18708,6 +19229,11 @@ snapshots: esrecurse: 4.3.0 estraverse: 5.3.0 + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-scope@9.1.1: dependencies: '@types/esrecurse': 4.3.1 @@ -18732,6 +19258,8 @@ snapshots: eslint-visitor-keys@3.4.3: {} + eslint-visitor-keys@4.2.1: {} + eslint-visitor-keys@5.0.1: {} eslint@10.0.2(jiti@2.6.1): @@ -18896,8 +19424,55 @@ snapshots: transitivePeerDependencies: - supports-color + eslint@9.39.4(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color + esm@3.2.25: {} + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + espree@11.1.1: dependencies: acorn: 8.16.0 @@ -19122,6 +19697,8 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fflate@0.6.10: {} + fflate@0.8.2: {} figures@1.7.0: @@ -19449,6 +20026,8 @@ snapshots: dependencies: type-fest: 0.20.2 + globals@14.0.0: {} + globals@16.5.0: {} globals@17.4.0: {} @@ -19473,6 +20052,8 @@ snapshots: globrex@0.1.2: {} + glsl-noise@0.0.0: {} + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -19692,6 +20273,8 @@ snapshots: highlight.js@10.7.3: {} + hls.js@1.6.15: {} + homedir-polyfill@1.0.3: dependencies: parse-passwd: 1.0.0 @@ -19769,6 +20352,8 @@ snapshots: ignore@7.0.5: {} + immediate@3.0.6: {} + immutable@5.1.5: {} import-cwd@3.0.0: @@ -19970,6 +20555,8 @@ snapshots: is-potential-custom-element-name@1.0.1: {} + is-promise@2.2.2: {} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.8 @@ -20103,6 +20690,13 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 + its-fine@2.0.0(@types/react@19.2.14)(react@19.2.4): + dependencies: + '@types/react-reconciler': 0.28.9(@types/react@19.2.14) + react: 19.2.4 + transitivePeerDependencies: + - '@types/react' + jackspeak@2.3.6: dependencies: '@isaacs/cliui': 8.0.2 @@ -20268,37 +20862,36 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@22.19.15)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2): + jest-cli@29.7.0(@types/node@24.10.13): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) + '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.19.15)(babel-plugin-macros@3.1.0) + create-jest: 29.7.0(@types/node@24.10.13) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.19.15)(babel-plugin-macros@3.1.0) + jest-config: 29.7.0(@types/node@24.10.13) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 - optionalDependencies: - node-notifier: 8.0.2 transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node + optional: true - jest-cli@29.7.0(@types/node@24.10.13): + jest-cli@29.7.0(@types/node@25.4.0): dependencies: '@jest/core': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@24.10.13) + create-jest: 29.7.0(@types/node@25.4.0) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@24.10.13) + jest-config: 29.7.0(@types/node@25.4.0) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -20309,25 +20902,26 @@ snapshots: - ts-node optional: true - jest-cli@29.7.0(@types/node@25.4.0): + jest-cli@29.7.0(@types/node@25.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@25.4.0) + create-jest: 29.7.0(@types/node@25.4.0)(babel-plugin-macros@3.1.0) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@25.4.0) + jest-config: 29.7.0(@types/node@25.4.0)(babel-plugin-macros@3.1.0) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 + optionalDependencies: + node-notifier: 8.0.2 transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node - optional: true jest-config@28.1.3(@types/node@22.19.15): dependencies: @@ -20573,6 +21167,36 @@ snapshots: - supports-color optional: true + jest-config@29.7.0(@types/node@25.4.0)(babel-plugin-macros@3.1.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(babel-plugin-macros@3.1.0) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 25.4.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-dev-server@9.0.2: dependencies: chalk: 4.1.2 @@ -21186,26 +21810,25 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@22.19.15)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2): + jest@29.7.0(@types/node@24.10.13): dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) + '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.19.15)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) - optionalDependencies: - node-notifier: 8.0.2 + jest-cli: 29.7.0(@types/node@24.10.13) transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node + optional: true - jest@29.7.0(@types/node@24.10.13): + jest@29.7.0(@types/node@25.4.0): dependencies: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@24.10.13) + jest-cli: 29.7.0(@types/node@25.4.0) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -21213,18 +21836,19 @@ snapshots: - ts-node optional: true - jest@29.7.0(@types/node@25.4.0): + jest@29.7.0(@types/node@25.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@25.4.0) + jest-cli: 29.7.0(@types/node@25.4.0)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) + optionalDependencies: + node-notifier: 8.0.2 transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node - optional: true jiti@1.21.7: {} @@ -21493,6 +22117,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + light-my-request@6.6.0: dependencies: cookie: 1.1.1 @@ -21682,12 +22310,22 @@ snapshots: lru-cache@7.18.3: {} - lucide-react@0.577.0(react@19.2.4): + lucide-react@1.6.0(react@19.2.4): dependencies: react: 19.2.4 lz-string@1.5.0: {} + maath@0.10.8(@types/three@0.160.0)(three@0.182.0): + dependencies: + '@types/three': 0.160.0 + three: 0.182.0 + + maath@0.6.0(@types/three@0.160.0)(three@0.182.0): + dependencies: + '@types/three': 0.160.0 + three: 0.182.0 + magic-string@0.25.9: dependencies: sourcemap-codec: 1.4.8 @@ -22030,6 +22668,12 @@ snapshots: ts-dedent: 2.2.0 uuid: 11.1.0 + meshline@3.3.1(three@0.182.0): + dependencies: + three: 0.182.0 + + meshoptimizer@0.18.1: {} + mhchemparser@4.2.1: {} microbundle@0.15.1(@types/babel__core@7.20.5): @@ -22550,6 +23194,11 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + n8ao@1.10.1(postprocessing@6.39.0(three@0.182.0))(three@0.182.0): + dependencies: + postprocessing: 6.39.0(three@0.182.0) + three: 0.182.0 + nano-spawn@2.0.0: {} nanoid@3.3.11: {} @@ -23609,6 +24258,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postprocessing@6.39.0(three@0.182.0): + dependencies: + three: 0.182.0 + + potpack@1.0.2: {} + preact-render-to-string@5.2.6(preact@10.28.4): dependencies: preact: 10.28.4 @@ -23685,6 +24340,11 @@ snapshots: progress@2.0.3: {} + promise-worker-transferable@1.0.4: + dependencies: + is-promise: 2.2.2 + lie: 3.3.0 + promise.series@0.2.0: {} prompts@2.4.2: @@ -23845,6 +24505,12 @@ snapshots: react-shallow-renderer: 16.15.0(react@17.0.2) scheduler: 0.20.2 + react-use-measure@2.1.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + optionalDependencies: + react-dom: 19.2.4(react@19.2.4) + react@17.0.2: dependencies: loose-envify: 1.4.0 @@ -24686,6 +25352,13 @@ snapshots: stackback@0.0.2: {} + stats-gl@2.4.2(@types/three@0.160.0)(three@0.182.0): + dependencies: + '@types/three': 0.160.0 + three: 0.182.0 + + stats.js@0.17.0: {} + statuses@2.0.2: {} std-env@3.10.0: {} @@ -24989,6 +25662,10 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + suspend-react@0.1.3(react@19.2.4): + dependencies: + react: 19.2.4 + svg-tags@1.0.0: {} svgo@2.8.2: @@ -25062,8 +25739,6 @@ snapshots: - tsx - yaml - tailwindcss@4.2.1: {} - tailwindcss@4.2.2: {} tapable@2.3.0: {} @@ -25174,6 +25849,22 @@ snapshots: dependencies: any-promise: 1.3.0 + three-mesh-bvh@0.8.3(three@0.182.0): + dependencies: + three: 0.182.0 + + three-stdlib@2.36.1(three@0.182.0): + dependencies: + '@types/draco3d': 1.4.10 + '@types/offscreencanvas': 2019.7.3 + '@types/webxr': 0.5.24 + draco3d: 1.5.7 + fflate: 0.6.10 + potpack: 1.0.2 + three: 0.182.0 + + three@0.182.0: {} + through@2.3.8: {} tiny-glob@0.2.9: @@ -25270,6 +25961,20 @@ snapshots: trim-newlines@3.0.1: {} + troika-three-text@0.52.4(three@0.182.0): + dependencies: + bidi-js: 1.0.3 + three: 0.182.0 + troika-three-utils: 0.52.4(three@0.182.0) + troika-worker-utils: 0.52.0 + webgl-sdf-generator: 1.1.1 + + troika-three-utils@0.52.4(three@0.182.0): + dependencies: + three: 0.182.0 + + troika-worker-utils@0.52.0: {} + trough@1.0.5: {} trough@2.2.0: {} @@ -25422,6 +26127,14 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tunnel-rat@0.1.2(@types/react@19.2.14)(react@19.2.4): + dependencies: + zustand: 4.5.7(@types/react@19.2.14)(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - immer + - react + turbo-darwin-64@1.13.4: optional: true @@ -25829,8 +26542,14 @@ snapshots: dependencies: react: 18.3.1 + use-sync-external-store@1.6.0(react@19.2.4): + dependencies: + react: 19.2.4 + util-deprecate@1.0.2: {} + utility-types@3.11.0: {} + uuid@11.1.0: {} uuid@8.3.2: {} @@ -26352,6 +27071,10 @@ snapshots: web-namespaces@2.0.1: {} + webgl-constants@1.1.1: {} + + webgl-sdf-generator@1.1.1: {} + webidl-conversions@3.0.1: {} webidl-conversions@4.0.2: {} @@ -26738,12 +27461,25 @@ snapshots: zod@4.3.6: {} + zustand@4.5.7(@types/react@19.2.14)(react@19.2.4): + dependencies: + use-sync-external-store: 1.6.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + react: 19.2.4 + zustand@5.0.11(@types/react@19.2.14)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)): optionalDependencies: '@types/react': 19.2.14 react: 18.3.1 use-sync-external-store: 1.6.0(react@18.3.1) + zustand@5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): + optionalDependencies: + '@types/react': 19.2.14 + react: 19.2.4 + use-sync-external-store: 1.6.0(react@19.2.4) + zwitch@1.0.5: {} zwitch@2.0.4: {} diff --git a/pospos2-rewrite/apps/agent/src/services/scale-reader.ts b/pospos2-rewrite/apps/agent/src/services/scale-reader.ts index 6783fd2e..4d70d4ea 100644 --- a/pospos2-rewrite/apps/agent/src/services/scale-reader.ts +++ b/pospos2-rewrite/apps/agent/src/services/scale-reader.ts @@ -46,10 +46,24 @@ class SerialScaleReader implements ScaleReader { this.port = new SerialPort({ path: this.portPath, baudRate: 9600 }); this.parser = this.port.pipe(new ReadlineParser({ delimiter: '\n' })); this.parser.on('data', (line) => { - const grams = parseInt(line.trim(), 10); - if (!isNaN(grams)) { - this.currentWeight = grams; - this.stable = true; + const trimmed = line.trim(); + // Try A&D style format: "ST,+00004.05 g" or "US, 000.00 kg" + const match = trimmed.match(/^([A-Z0-9]{2}),\s*([+-]?\d+\.?\d*)\s*([a-zA-Z]*)$/); + if (match) { + const status = match[1]; + const weightStr = match[2]; + const weight = parseFloat(weightStr); + if (!isNaN(weight)) { + this.currentWeight = weight; + this.stable = (status === 'ST'); + } + } else { + // Fallback: raw numeric value (e.g., "123.45") + const grams = parseFloat(trimmed); + if (!isNaN(grams)) { + this.currentWeight = grams; + this.stable = true; + } } }); console.log(`Scale '${this.scaleId}' connected on ${this.portPath}`);