diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..4355b939b73ed87ee229c63c20da79bd520ea3d4 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,111 @@ +# =========================================== +# Configuration Git LFS pour HOLOKIA-AVATAR +# =========================================== +# Modèles 3D +*.glb filter=lfs diff=lfs merge=lfs -text +*.fbx filter=lfs diff=lfs merge=lfs -text +*.obj filter=lfs diff=lfs merge=lfs -text +*.dae filter=lfs diff=lfs merge=lfs -text +*.3ds filter=lfs diff=lfs merge=lfs -text +*.blend filter=lfs diff=lfs merge=lfs -text +*.max filter=lfs diff=lfs merge=lfs -text +*.ma filter=lfs diff=lfs merge=lfs -text +*.mb filter=lfs diff=lfs merge=lfs -text +*.c4d filter=lfs diff=lfs merge=lfs -text +*.x3d filter=lfs diff=lfs merge=lfs -text +*.ply filter=lfs diff=lfs merge=lfs -text +*.stl filter=lfs diff=lfs merge=lfs -text +*.wrl filter=lfs diff=lfs merge=lfs -text +*.3dm filter=lfs diff=lfs merge=lfs -text +*.3dmf filter=lfs diff=lfs merge=lfs -text +*.ac filter=lfs diff=lfs merge=lfs -text +*.ac3d filter=lfs diff=lfs merge=lfs -text +*.acc filter=lfs diff=lfs merge=lfs -text +*.ase filter=lfs diff=lfs merge=lfs -text +*.ask filter=lfs diff=lfs merge=lfs -text +*.b3d filter=lfs diff=lfs merge=lfs -text +*.bvh filter=lfs diff=lfs merge=lfs -text +*.cob filter=lfs diff=lfs merge=lfs -text +*.csm filter=lfs diff=lfs merge=lfs -text +*.dxf filter=lfs diff=lfs merge=lfs -text +*.enff filter=lfs diff=lfs merge=lfs -text +*.hmp filter=lfs diff=lfs merge=lfs -text +*.irrmesh filter=lfs diff=lfs merge=lfs -text +*.irr filter=lfs diff=lfs merge=lfs -text +*.lwo filter=lfs diff=lfs merge=lfs -text +*.lws filter=lfs diff=lfs merge=lfs -text +*.lxo filter=lfs diff=lfs merge=lfs -text +*.md2 filter=lfs diff=lfs merge=lfs -text +*.md3 filter=lfs diff=lfs merge=lfs -text +*.md5anim filter=lfs diff=lfs merge=lfs -text +*.md5camera filter=lfs diff=lfs merge=lfs -text +*.md5mesh filter=lfs diff=lfs merge=lfs -text +*.mdc filter=lfs diff=lfs merge=lfs -text +*.mdl filter=lfs diff=lfs merge=lfs -text +*.mesh filter=lfs diff=lfs merge=lfs -text +*.mesh.xml filter=lfs diff=lfs merge=lfs -text +*.mot filter=lfs diff=lfs merge=lfs -text +*.ms3d filter=lfs diff=lfs merge=lfs -text +*.ndo filter=lfs diff=lfs merge=lfs -text +*.nff filter=lfs diff=lfs merge=lfs -text +*.off filter=lfs diff=lfs merge=lfs -text +*.ogex filter=lfs diff=lfs merge=lfs -text +*.pmx filter=lfs diff=lfs merge=lfs -text +*.prj filter=lfs diff=lfs merge=lfs -text +*.q3o filter=lfs diff=lfs merge=lfs -text +*.q3s filter=lfs diff=lfs merge=lfs -text +*.raw filter=lfs diff=lfs merge=lfs -text +*.scn filter=lfs diff=lfs merge=lfs -text +*.smd filter=lfs diff=lfs merge=lfs -text +*.ter filter=lfs diff=lfs merge=lfs -text +*.uc filter=lfs diff=lfs merge=lfs -text +*.vta filter=lfs diff=lfs merge=lfs -text +*.x filter=lfs diff=lfs merge=lfs -text +*.xgl filter=lfs diff=lfs merge=lfs -text +*.xml filter=lfs diff=lfs merge=lfs -text +*.zae filter=lfs diff=lfs merge=lfs -text +*.zgl filter=lfs diff=lfs merge=lfs -text +# Textures et images volumineuses +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.tga filter=lfs diff=lfs merge=lfs -text +*.tiff filter=lfs diff=lfs merge=lfs -text +*.tif filter=lfs diff=lfs merge=lfs -text +*.bmp filter=lfs diff=lfs merge=lfs -text +*.exr filter=lfs diff=lfs merge=lfs -text +*.hdr filter=lfs diff=lfs merge=lfs -text +# Audio volumineux +*.wav filter=lfs diff=lfs merge=lfs -text +*.mp3 filter=lfs diff=lfs merge=lfs -text +*.ogg filter=lfs diff=lfs merge=lfs -text +*.flac filter=lfs diff=lfs merge=lfs -text +*.aac filter=lfs diff=lfs merge=lfs -text +# Vidéos +*.mp4 filter=lfs diff=lfs merge=lfs -text +*.avi filter=lfs diff=lfs merge=lfs -text +*.mov filter=lfs diff=lfs merge=lfs -text +*.mkv filter=lfs diff=lfs merge=lfs -text +*.webm filter=lfs diff=lfs merge=lfs -text +# Archives +*.zip filter=lfs diff=lfs merge=lfs -text +*.rar filter=lfs diff=lfs merge=lfs -text +*.7z filter=lfs diff=lfs merge=lfs -text +*.tar.gz filter=lfs diff=lfs merge=lfs -text +*.tar.bz2 filter=lfs diff=lfs merge=lfs -text +# Fichiers binaires +*.bin filter=lfs diff=lfs merge=lfs -text +*.dat filter=lfs diff=lfs merge=lfs -text +*.db filter=lfs diff=lfs merge=lfs -text +*.sqlite filter=lfs diff=lfs merge=lfs -text +*.sqlite3 filter=lfs diff=lfs merge=lfs -text +# Fichiers de cache et temporaires +*.cache filter=lfs diff=lfs merge=lfs -text +*.tmp filter=lfs diff=lfs merge=lfs -text +*.temp filter=lfs diff=lfs merge=lfs -text +# Fichiers de logs volumineux +*.log filter=lfs diff=lfs merge=lfs -text +# Fichiers de sauvegarde +*.bak filter=lfs diff=lfs merge=lfs -text +*.backup filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..29cc464299b86607a15807dab1f02ef9d8dc0bc0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ +# =========================================== +# .gitignore pour HOLOKIA-AVATAR +# =========================================== + +# Fichiers Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Environnements virtuels +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Variables d'environnement (IMPORTANT pour HF) +.env +.env.* +!.env.example + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Logs +*.log +logs/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Node.js +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Builds +dist/ +build/ +.cache/ + +# Fichiers temporaires +tmp/ +temp/ +*.tmp +*.temp + +# Fichiers système +.DS_Store +Thumbs.db + +# Cache TTS +Back-end/tts_cache/ +tts_cache/ + +# Fichiers de configuration locale +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Fichiers de test +coverage/ +.nyc_output/ +.pytest_cache/ + +# Docker +.dockerignore + +# Fichiers de déploiement (optionnels) +deploy_to_hf.sh +test_deployment.py +README_HF_DEPLOYMENT.md + +# Fichiers de développement +build-frontend.sh +docker-compose*.yml +docker/ + +# Fichiers Git LFS (seront gérés par LFS) +*.glb +*.fbx +*.obj +*.dae +*.3ds +*.blend +*.max +*.ma +*.mb +*.c4d +*.fbx +*.x3d +*.ply +*.stl +*.wrl +*.x3d +*.3dm +*.3dmf +*.ac +*.ac3d +*.acc +*.ase +*.ask +*.b3d +*.bvh +*.cob +*.csm +*.dae +*.dxf +*.enff +*.hmp +*.irrmesh +*.irr +*.lwo +*.lws +*.lxo +*.md2 +*.md3 +*.md5anim +*.md5camera +*.md5mesh +*.mdc +*.mdl +*.mesh +*.mesh.xml +*.mot +*.ms3d +*.ndo +*.nff +*.off +*.ogex +*.ply +*.pmx +*.prj +*.q3o +*.q3s +*.raw +*.scn +*.smd +*.stl +*.ter +*.uc +*.vta +*.x +*.x3d +*.xgl +*.xml +*.zae +*.zgl diff --git a/Back-end/README.md b/Back-end/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b9da5771e1b02d55d1aeefd13f1a29269eeed9da --- /dev/null +++ b/Back-end/README.md @@ -0,0 +1,118 @@ +# HOLOKIA-AVATAR Backend + +Ce dossier contient le backend du projet: il gère la génération de texte IA (LLM), la synthèse vocale (TTS), la reconnaissance vocale (STT), et le streaming audio en temps réel avec Websocket. + +--- + +## Structure du dossier + +``` +Back-end/ +├── .env # Variables d'environnement (API keys, config) +├── requirements.txt # Dépendances Python +├── start_service.py # Script de lancement des services # Script de démarrage principal +|__ tts_cache # Le cache +├── app/ +│ ├── __init__.py +│ └── main.py # API principale FastAPI +├── env/ # Environnement virtuel Python (venv) +├── services/ +│ ├── live_stream_service.py +│ ├── llm_service.py +│ ├── stt_service.py +│ ├── tts_service.py +``` + +--- + +## Prérequis + +- Python 3.10 ou supérieur +- Installer les dépendances : + ```sh + pip install -r requirements.txt + ``` +- Configurer le fichier `.env` avec vos clés API et paramètres nécessaires (voir exemple ci-dessous). + +--- + +## Configuration + +Exemple de fichier `.env` : +``` +GROQ_API_KEY=your_groq_api_key + +``` + +Adaptez les clés et chemins selon votre environnement. + +--- + +## Lancement du backend + +### Démarrage global + +```sh +python start_services.py +``` +Ce script lance tous les services backend (API principale, TTS, STT, streaming...). + +### Démarrage manuel de l’API FastAPI + +```sh +uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload +``` + +--- + +## Fonctionnalités principales + +- **Reconnaissance vocale (STT)** + Endpoint : `POST /stt/transcribe` + Envoie un fichier audio, reçoit le texte transcrit. + +- **Synthèse vocale (TTS)** + Endpoint : `POST /tts/generate-tts` + Envoie du texte, reçoit le chemin du fichier audio généré. + +- **Génération de texte IA (LLM)** + Endpoint : `POST /llm/generate` + Envoie un prompt, reçoit la réponse générée. + +- **Streaming audio en temps réel** + Endpoint : `/live_stream/{user_id}` + Permet la communication audio temps réel avec le backend. + +- **Health check** + Endpoint : `GET /health` + Vérifie l’état du backend. + +--- + +## Dossiers de cache et de test + +- `tts_cache/` : fichiers audio générés par le TTS + +--- + +## Bonnes pratiques + +- Utilisez l’environnement virtuel fourni (`env/`) pour isoler les dépendances. +- Protégez votre fichier `.env` et ne le partagez pas publiquement. +- Consultez et adaptez `lipsync_config.yaml` selon vos besoins de synchronisation labiale. +- En production, restreignez les origines CORS dans `main.py`. + +--- + +## Contribution + +Pour toute modification : +- Documentez votre code +- Ajoutez des tests si nécessaire +- Respectez la structure existante + +--- + +## Support + +Pour toute question ou problème, Veillez contacter Holokia particuliérement Mr Sinaly ou Mr Omar. diff --git a/Back-end/app/__init__.py b/Back-end/app/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Back-end/app/main.py b/Back-end/app/main.py new file mode 100644 index 0000000000000000000000000000000000000000..250dc5a16582a3d41e83d4e0f07e198cb3466991 --- /dev/null +++ b/Back-end/app/main.py @@ -0,0 +1,69 @@ +# app/main.py +from fastapi import FastAPI, UploadFile, File, WebSocket, WebSocketDisconnect +from fastapi.middleware.cors import CORSMiddleware +from services import stt_service, tts_service, llm_service, live_stream_service # type: ignore +import uvicorn + +app = FastAPI( + title="Holokia Avatar Backend", + description="Backend pour avatar 3D interactif avec STT, TTS, LLM et LiveKit streaming", + version="1.0.0" +) + +# ✅ Activer CORS pour éviter les blocages côté front +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # ⚠️ En prod, mettre le domaine du front + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# 🩺 Health check +@app.get("/health") +async def health(): + return {"status": "ok", "message": "Backend operational"} + +# 🎤 STT - Audio → Texte +@app.post("/stt/transcribe") +async def transcribe_audio(file: UploadFile = File(...)): + try: + result = await stt_service.transcribe_audio(file) + return {"text": result} + except Exception as e: + return {"error": str(e)} + +# 🗣 TTS - Texte → Audio +@app.post("/tts/generate") +async def generate_tts(text: str): + try: + audio_path = await tts_service.text_to_speech(text) + return {"audio_file": audio_path} + except Exception as e: + return {"error": str(e)} + +# 🤖 LLM - Prompt → Texte +@app.post("/llm/generate") +async def generate_llm(prompt: str): + try: + response = await llm_service.generate_response(prompt) + return {"response": response} + except Exception as e: + return {"error": str(e)} + +# 📡 Live streaming WebSocket +@app.websocket("/live-stream") +async def websocket_endpoint(websocket: WebSocket): + await websocket.accept() + try: + await live_stream_service.handle_connection(websocket) + except WebSocketDisconnect: + print("🔌 Client déconnecté du live stream") + except Exception as e: + print(f"❌ Erreur WebSocket : {e}") + finally: + await websocket.close() + +# 🚀 Point d'entrée +if __name__ == "__main__": + uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True) diff --git a/Back-end/requirements.txt b/Back-end/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..11ae9217aa10a187fc31e4d2ec8d7b2348380437 --- /dev/null +++ b/Back-end/requirements.txt @@ -0,0 +1,12 @@ +fastapi>=0.115 +uvicorn[standard]>=0.30 +aiohttp>=3.9 +python-multipart>=0.0.9 +gTTS>=2.5 +pydub>=0.25 +faster-whisper>=1.0 +ctranslate2>=4.6.0 +langdetect>=1.0.9 +httpx>=0.27 +langchain-groq # si tu utilises Groq +langgraph \ No newline at end of file diff --git a/Back-end/services/live_stream_service.py b/Back-end/services/live_stream_service.py new file mode 100644 index 0000000000000000000000000000000000000000..fafbc092a18f5fdd415b7dd134d8492527d20d03 --- /dev/null +++ b/Back-end/services/live_stream_service.py @@ -0,0 +1,445 @@ +import os +import sys +import logging +import json +import asyncio +import aiohttp # type: ignore +import tempfile +import wave +import audioop +from fastapi import FastAPI, WebSocket, WebSocketDisconnect +from fastapi.websockets import WebSocketState +from fastapi.middleware.cors import CORSMiddleware +from typing import Dict, List, Optional, TypedDict +import uvicorn +from langgraph.graph import StateGraph, END, START +from tenacity import retry, stop_after_attempt, wait_exponential + +# Configuration du logger +logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", stream=sys.stdout) +logger = logging.getLogger("live_stream_service") + +# URLs des micro-services +STT_SERVICE_URL = "http://localhost:5001/transcribe" +LLM_SERVICE_URL = "http://localhost:5002/generate" +TTS_SERVICE_URL = "http://localhost:5000/generate-tts" + +# Paramètres audio +SAMPLE_RATE = 16000 +CHANNELS = 1 +SAMPLE_WIDTH = 2 +SILENCE_THRESHOLD = 1000 +SILENCE_DURATION = 1.0 +MIN_AUDIO_DURATION = 1.0 +MAX_CONNECTIONS_PER_ROOM = 100 # Limite de connexions par salle + +app = FastAPI() + +# CORS +origins = ["http://localhost:5173", "http://127.0.0.1:5173"] +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Gestion des salles et participants +rooms: Dict[str, List[WebSocket]] = {} +participants_lang: Dict[str, str] = {} +user_histories: Dict[str, List[Dict[str, str]]] = {} # Historique persistant par user_id + +# État du graphe LangGraph +class ChatState(TypedDict): + audio_bytes: bytes + transcript: str + lang: Optional[str] + llm_response: str + audio_response: bytes + user_id: str + room_id: str + history: List[Dict[str, str]] # Historique: [{"role": "user|assistant", "content": "..."}] + +async def safe_delete_file(file_path: str, max_attempts: int = 3, delay: float = 0.1) -> None: + """Supprime un fichier avec retry en cas de PermissionError.""" + for attempt in range(max_attempts): + try: + if os.path.exists(file_path): + os.unlink(file_path) + logger.debug(f"Fichier supprimé: {file_path}") + return + except PermissionError as e: + logger.warning(f"Tentative {attempt + 1}/{max_attempts} de suppression de {file_path} échouée: {e}") + if attempt < max_attempts - 1: + await asyncio.sleep(delay) + except Exception as e: + logger.error(f"Erreur lors de la suppression de {file_path}: {e}") + return + logger.error(f"Échec de la suppression de {file_path} après {max_attempts} tentatives") + +# Nœuds du graphe avec retry +@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10)) +async def transcribe_node(state: ChatState) -> ChatState: + """Appelle le service STT pour transcrire l'audio avec retry.""" + audio_bytes = state.get("audio_bytes", b"") + user_id = state.get("user_id", "unknown") + print(f"[STT] Début transcription pour {user_id}, audio size: {len(audio_bytes)} bytes") + + if not audio_bytes: + print("[STT] Audio vide, skip") + return {"transcript": "", "lang": state.get("lang", "fr")} + + duration = len(audio_bytes) / (SAMPLE_RATE * SAMPLE_WIDTH * CHANNELS) + if duration < MIN_AUDIO_DURATION: + logger.debug(f"[STT] Audio trop court ({duration:.2f}s), skip") + return {"transcript": "", "lang": state.get("lang", "fr")} + + try: + rms_global = audioop.rms(audio_bytes, SAMPLE_WIDTH) + logger.debug(f"[STT] RMS global: {rms_global}") + if rms_global < SILENCE_THRESHOLD: + logger.debug(f"[STT] Audio silencieux (RMS={rms_global}), skip") + return {"transcript": "", "lang": state.get("lang", "fr")} + except Exception as e: + logger.warning(f"[STT] Erreur RMS global: {e}") + return {"transcript": "", "lang": state.get("lang", "fr")} + + tmp_path = None + file_handle = None + try: + with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmpfile: + with wave.open(tmpfile.name, "wb") as wf: + wf.setnchannels(CHANNELS) + wf.setsampwidth(SAMPLE_WIDTH) + wf.setframerate(SAMPLE_RATE) + wf.writeframes(audio_bytes) + tmp_path = tmpfile.name + + form = aiohttp.FormData() + file_handle = open(tmp_path, "rb") + form.add_field("file", file_handle, filename="audio.wav", content_type="audio/wav") + if state.get("lang"): + form.add_field("language", state["lang"]) + + async with aiohttp.ClientSession() as session: + async with session.post(STT_SERVICE_URL, data=form) as resp: + if resp.status != 200: + text = await resp.text() + raise RuntimeError(f"STT service error {resp.status}: {text}") + result = await resp.json() + transcript = result.get("transcript", "") + lang = result.get("lang", state.get("lang", "fr")) + if transcript.strip(): + history = state.get("history", []) + history.append({"role": "user", "content": transcript}) + return {"transcript": transcript, "lang": lang, "history": history} + return {"transcript": "", "lang": lang} + except Exception as e: + logger.error(f"[STT] Erreur: {e}", exc_info=True) + raise # Laisser tenacity gérer le retry + finally: + if file_handle: + file_handle.close() + if tmp_path: + await safe_delete_file(tmp_path) + +@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10)) +async def llm_node(state: ChatState) -> ChatState: + """Appelle le service LLM avec le transcript et l’historique.""" + transcript = state.get("transcript", "") + user_id = state.get("user_id", "") + if not transcript.strip(): + logger.debug("[LLM] Transcript vide, skip") + return {"llm_response": ""} + + # Construire le prompt avec l’historique + history = state.get("history", []) + logger.debug(f"[LLM] Historique envoyé: {history}") + try: + async with aiohttp.ClientSession() as session: + async with session.post( + LLM_SERVICE_URL, + json={ + "text": transcript, + "user_id": user_id, + "history": history[-3:] + } + ) as resp: + if resp.status != 200: + text = await resp.text() + raise RuntimeError(f"LLM service error {resp.status}: {text}") + result = await resp.json() + response = result.get("response", "") + new_history = result.get("history", history) + if response.strip(): + logger.info(f"[LLM] Réponse: {response}") + return {"llm_response": response, "history": new_history} + return {"llm_response": ""} + except Exception as e: + logger.error(f"[LLM] Erreur: {e}", exc_info=True) + raise # Laisser tenacity gérer le retry + +@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10)) +async def tts_node(state: ChatState) -> ChatState: + """Appelle le service TTS pour générer l’audio.""" + llm_response = state.get("llm_response", "") + if not llm_response.strip(): + logger.debug("[TTS] Pas de réponse LLM, skip") + return {"audio_response": b""} + + logger.debug(f"[TTS] Appel avec texte: {llm_response}, lang: {state['lang']}") + try: + async with aiohttp.ClientSession() as session: + async with session.post(TTS_SERVICE_URL, json={"text": llm_response, "lang": state["lang"]}) as resp: + if resp.status != 200: + text_resp = await resp.text() + raise RuntimeError(f"TTS service error {resp.status}: {text_resp}") + result = await resp.json() + audio_url = result.get("url") + if not audio_url: + raise RuntimeError("TTS no URL") + + # Construire l'URL complète si c'est une URL relative + if audio_url.startswith("/"): + audio_url = f"http://localhost:5000{audio_url}" + + async with session.get(audio_url) as audio_resp: + if audio_resp.status != 200: + raise RuntimeError(f"TTS file fetch error {audio_resp.status}") + audio_data = await audio_resp.read() + logger.info(f"[TTS] Audio généré, taille: {len(audio_data)} bytes") + return {"audio_response": audio_data} + except Exception as e: + logger.error(f"[TTS] Erreur: {e}", exc_info=True) + raise # Laisser tenacity gérer le retry + +# Construiction du graphe LangGraph +workflow = StateGraph(ChatState) +workflow.add_node("transcribe", transcribe_node) +workflow.add_node("llm", llm_node) +workflow.add_node("tts", tts_node) +workflow.add_edge(START, "transcribe") +workflow.add_edge("transcribe", "llm") +workflow.add_edge("llm", "tts") +workflow.add_edge("tts", END) +graph = workflow.compile() + +async def broadcast(room_id: str, message, sender: WebSocket = None): + """Diffuse message à la salle, sauf au sender.""" + for ws in rooms.get(room_id, []): + if ws != sender and ws.application_state == WebSocketState.CONNECTED: + try: + if isinstance(message, bytes): + await ws.send_bytes(message) + else: + await ws.send_text(message) + except Exception: + rooms[room_id].remove(ws) if ws in rooms.get(room_id, []) else None + +def is_silence(audio_chunk: bytes) -> bool: + """Vérifie si chunk est silence via RMS.""" + if not audio_chunk or len(audio_chunk) < SAMPLE_WIDTH * 2: + return True + if len(audio_chunk) % SAMPLE_WIDTH != 0: + audio_chunk = audio_chunk[:-(len(audio_chunk) % SAMPLE_WIDTH)] + try: + rms = audioop.rms(audio_chunk, SAMPLE_WIDTH) + logger.debug(f"[is_silence] RMS={rms}") + return rms < SILENCE_THRESHOLD + except Exception as e: + logger.warning(f"[is_silence] Erreur RMS: {e}") + return True + +@app.websocket("/live_stream/{user_id}") +async def websocket_endpoint(ws: WebSocket, user_id: str, lang: Optional[str] = None, room_id: str = "default"): + """Gère connexion WebSocket pour stream audio avec LangGraph.""" + await ws.accept() + print(f"[Live] Connecté: {user_id} (lang={lang or 'auto'}, room={room_id})") + logger.info(f"[Live] Connecté: {user_id} (lang={lang or 'auto'}, room={room_id})") + + if room_id not in rooms: + rooms[room_id] = [] + if len(rooms[room_id]) >= MAX_CONNECTIONS_PER_ROOM: + logger.warning(f"[Live] Limite de connexions atteinte pour la salle {room_id}") + await ws.close(code=1000, reason="Salle pleine") + return + rooms[room_id].append(ws) + participants_lang[user_id] = lang or "auto" + + # Initialise l'historique pour cet utilisateur s'il n'existe pas + if user_id not in user_histories: + user_histories[user_id] = [] + history = user_histories[user_id] + + buffer = bytearray() + silent_counter = 0.0 + + try: + while True: + try: + message = await asyncio.wait_for(ws.receive(), timeout=60) + print(f"[Live] Message reçu de {user_id}: {type(message)}") + except asyncio.TimeoutError: + print(f"[Live] Timeout WS pour {user_id}") + logger.warning(f"[Live] Timeout WS pour {user_id}") + break + except WebSocketDisconnect as e: + print(f"[Live] Disconnect ({e.code}) pour {user_id}") + logger.info(f"[Live] Disconnect ({e.code}) pour {user_id}") + break + except RuntimeError as e: + print(f"[Live] Receive error: {e}") + logger.info(f"[Live] Receive error: {e}") + break + except Exception as e: + print(f"[Live] ws.receive error: {e}") + logger.warning(f"[Live] ws.receive error: {e}") + break + + if "bytes" in message: + chunk = message["bytes"] + if not chunk: + continue + + if len(chunk) % SAMPLE_WIDTH != 0: + chunk = chunk[:-(len(chunk) % SAMPLE_WIDTH)] + logger.debug(f"[Live] Chunk aligné ({len(chunk)} bytes)") + + buffer.extend(chunk) + frames = len(chunk) / SAMPLE_WIDTH + time_delta = frames / SAMPLE_RATE + + if is_silence(chunk): + silent_counter += time_delta + else: + silent_counter = 0.0 + + if silent_counter >= SILENCE_DURATION and buffer: + audio_snapshot = bytes(buffer) + buffer.clear() + silent_counter = 0.0 + + # Exécute le graphe LangGraph + state = { + "audio_bytes": audio_snapshot, + "user_id": user_id, + "room_id": room_id, + "lang": lang or "fr", + "history": history, + "transcript": "", + "llm_response": "", + "audio_response": b"" + } + try: + print(f"[Live] Exécution du graphe pour {user_id}, audio size: {len(audio_snapshot)} bytes") + result = await graph.ainvoke(state) + print(f"[Live] Graphe exécuté avec succès pour {user_id}") + except Exception as e: + print(f"[Live] ERREUR lors de l'exécution du graphe pour {user_id}: {e}") + print(f"[Live] Audio size: {len(audio_snapshot)} bytes, lang: {lang}") + import traceback + print(f"[Live] Traceback: {traceback.format_exc()}") + if ws.application_state == WebSocketState.CONNECTED: + await ws.send_text(json.dumps({"type": "error", "message": f"Erreur de traitement audio: {str(e)}"})) + continue # Continue au lieu de break pour permettre d'autres tentatives + + # Mettre à jour l'historique global + history = result.get("history", history) + user_histories[user_id] = history + + # Envoie les résultats au user et à la salle + if result["transcript"].strip(): + effective_lang = result["lang"] or lang or "fr" + logger.info(f"[STT] {user_id}: {result['transcript']} (lang={effective_lang})") + await broadcast(room_id, f"{user_id}({effective_lang}): {result['transcript']}", sender=ws) + if result["llm_response"].strip(): + logger.debug(f"[Handle Response] Envoi réponse texte: {result['llm_response']}") + await ws.send_text(f"Système: {result['llm_response']}") + if result["audio_response"]: + logger.debug(f"[Handle Response] Envoi audio, taille: {len(result['audio_response'])} bytes") + await ws.send_bytes(result["audio_response"]) + await broadcast(room_id, result["audio_response"], sender=ws) + + elif "text" in message: + try: + data = json.loads(message["text"]) + ttype = data.get("type") + print(f"[Live] Message texte reçu de {user_id}: {ttype}") + if ttype == "start": + print(f"[Live] Début stream: {user_id}") + logger.info(f"[Live] Début stream: {user_id}") + elif ttype == "ping": + print(f"[Live] Ping reçu de {user_id}") + logger.debug(f"[Live] Ping de {user_id}") + if ws.application_state == WebSocketState.CONNECTED: + await ws.send_text(json.dumps({"type": "pong"})) + elif ttype == "stop": + logger.info(f"[Live] Arrêt stream: {user_id}") + if buffer: + state = { + "audio_bytes": bytes(buffer), + "user_id": user_id, + "room_id": room_id, + "lang": lang or "fr", + "history": history, + "transcript": "", + "llm_response": "", + "audio_response": b"" + } + try: + result = await graph.ainvoke(state) + except Exception as e: + logger.error(f"[Live] Erreur lors de l'exécution du graphe (stop): {e}", exc_info=True) + if ws.application_state == WebSocketState.CONNECTED: + await ws.send_text(json.dumps({"type": "error", "message": "Désolé, une erreur s'est produite, veuillez réessayer."})) + break + history = result.get("history", history) + user_histories[user_id] = history + if result["transcript"].strip(): + effective_lang = result["lang"] or lang or "fr" + logger.info(f"[STT] (final) {user_id}: {result['transcript']} (lang={effective_lang})") + await broadcast(room_id, f"{user_id}({effective_lang}): {result['transcript']}", sender=ws) + if result["llm_response"].strip(): + await ws.send_text(f"Système: {result['llm_response']}") + if result["audio_response"]: + await ws.send_bytes(result["audio_response"]) + await broadcast(room_id, result["audio_response"], sender=ws) + if ws.application_state == WebSocketState.CONNECTED: + await ws.send_text(json.dumps({"type": "stopped"})) + await ws.close() + return + else: + logger.warning(f"[Live] Type inconnu: {ttype}") + if ws.application_state == WebSocketState.CONNECTED: + await ws.send_text(json.dumps({"type": "error", "message": "Désolé, une erreur s'est produite, veuillez réessayer."})) + except json.JSONDecodeError: + logger.error(f"[Live] JSON invalide: {message['text']}") + if ws.application_state == WebSocketState.CONNECTED: + await ws.send_text(json.dumps({"type": "error", "message": "Désolé, une erreur s'est produite, veuillez réessayer."})) + break + except Exception as e: + logger.error(f"[Live] Erreur générale: {e}", exc_info=True) + if ws.application_state == WebSocketState.CONNECTED: + try: + await ws.send_text(json.dumps({"type": "error", "message": "Désolé, une erreur s'est produite, veuillez réessayer."})) + except Exception as send_error: + logger.error(f"[Live] Erreur lors de l'envoi du message d'erreur: {send_error}") + finally: + rooms[room_id].remove(ws) if ws in rooms.get(room_id, []) else None + participants_lang.pop(user_id, None) + buffer.clear() + logger.info(f"[Live] Cleanup: {user_id}") + if ws.application_state == WebSocketState.CONNECTED: + try: + await ws.close() + except Exception as close_error: + logger.error(f"[Live] Erreur lors de la fermeture de la connexion: {close_error}") + +@app.get("/health") +async def health_check(): + """Vérifie l’état du service.""" + return {"status": "ok", "service": "live_stream"} + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=5003, ws_ping_interval=20, ws_ping_timeout=120) \ No newline at end of file diff --git a/Back-end/services/llm_service.py b/Back-end/services/llm_service.py new file mode 100644 index 0000000000000000000000000000000000000000..2855d6ba24a7a2d07a2eb742bf73f248acab1c8b --- /dev/null +++ b/Back-end/services/llm_service.py @@ -0,0 +1,168 @@ +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel +import logging +import uvicorn +from langdetect import detect, DetectorFactory, LangDetectException +from langchain_groq import ChatGroq +from dotenv import load_dotenv +import os +from fastapi.middleware.cors import CORSMiddleware +from typing import List, Dict +import re + +load_dotenv() + +logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") +logger = logging.getLogger("LLM_Service") + +app = FastAPI() + +origins = ["http://localhost:5173", "http://127.0.0.1:5173"] +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +api_key = os.getenv("GROQ_API_KEY") +if not api_key: + logger.error("GROQ_API_KEY manquante dans l'environnement") + raise RuntimeError("GROQ_API_KEY non configurée") + +MODEL_NAME = "meta-llama/llama-4-scout-17b-16e-instruct" +logger.info(f"Chargement du modèle LLM Groq : {MODEL_NAME}") +llm = ChatGroq(model=MODEL_NAME, temperature=0) + +DetectorFactory.seed = 0 + +EN_WORDS = {"hi", "hello", "hey", "ok", "thanks", "bye", "yes", "no"} +AR_WORDS = {"salam", "salaam", "marhaban", "مرحبا", "سلام", "شكرا", "أهلا"} +FR_WORDS = {"bonjour", "salut", "merci", "oui", "non", "s'il vous plaît", "svp"} + +user_histories: Dict[str, List[Dict[str, str]]] = {} + +def normalize_text(text: str) -> str: + text = re.sub(r'[^\w\s]', '', text.lower().strip()) + text = re.sub(r'\s+', ' ', text) + return text + +def detect_language(user_text: str, user_id: str, default_lang: str = "fr") -> str: + if not user_text: + return default_lang + + text_normalized = normalize_text(user_text) + + if text_normalized in EN_WORDS: + return "en" + if text_normalized in AR_WORDS: + return "ar" + if text_normalized in FR_WORDS: + return "fr" + + history = user_histories.get(user_id, []) + if history and len(user_text) < 10: + last_msgs = [msg["content"] for msg in history[-2:] if msg["role"] == "user"] + for msg in last_msgs: + try: + lang = detect(msg) + if lang in {"fr", "en", "ar"}: + return lang + except LangDetectException: + pass + + try: + lang = detect(user_text) + except LangDetectException: + return default_lang + + if lang not in {"fr", "en", "ar"}: + return default_lang + + return lang + +class LLMRequest(BaseModel): + text: str + user_id: str + history: List[Dict[str, str]] = [] + +@app.post("/generate") +async def generate_response(request: LLMRequest): + user_text = request.text.strip() + user_id = request.user_id + if not user_text: + raise HTTPException(status_code=400, detail="Le texte ne peut pas être vide") + if not user_id: + raise HTTPException(status_code=400, detail="L'identifiant utilisateur est requis") + + lang = detect_language(user_text, user_id, default_lang="fr") + logger.info(f"Requête LLM reçue ({len(user_text)} caractères), langue: {lang}, user_id: {user_id}") + + if lang == "fr": + system_prompt = ( + "Tu es HOLOKIA, un avatar IA conversationnel. " + "Réponds uniquement aux questions posées avec précision, clarté et simplicité. " + "Sois toujours poli et chaleureux dans ta manière de parler. " + "Si la question n’est pas claire, demande gentiment une précision. " + "Ne donne pas d’informations inutiles et reste concentré sur le sujet." + ) + elif lang == "ar": + system_prompt = ( + "أنت HOLOKIA، شخصية ذكاء اصطناعي محادثة. " + "أجب فقط على الأسئلة المطروحة بدقة ووضوح وبأسلوب بسيط. " + "كن دائمًا مهذبًا ودودًا في طريقة كلامك. " + "إذا لم يكن السؤال واضحًا، اطلب بلطف توضيحًا. " + "لا تضف معلومات غير ضرورية وابقَ مركزًا على الموضوع." + ) + else: + system_prompt = ( + "You are HOLOKIA, a conversational AI avatar. " + "Answer only the questions asked, with accuracy, clarity, and simplicity. " + "Always remain polite and friendly in your tone. " + "If the question is unclear, kindly ask for clarification. " + "Do not add unnecessary information and stay focused on the topic." + ) + + history = request.history if request.history else user_histories.get(user_id, []) + history.append({"role": "user", "content": user_text}) + + messages = [("system", system_prompt)] + [ + (msg["role"], msg["content"]) for msg in history[-3:] + ] + + total_length = sum(len(msg[1]) for msg in messages) + if total_length > 4000: + logger.warning(f"Prompt trop long ({total_length} caractères), truncation") + messages = [("system", system_prompt)] + messages[-2:] + + logger.debug(f"Prompt envoyé au LLM: {messages}") + + try: + response = llm.invoke(messages) + response_text = response.content.strip() + history.append({"role": "assistant", "content": response_text}) + user_histories[user_id] = history[-5:] + logger.info("Réponse LLM générée avec succès") + return { + "response": response_text, + "lang": lang, + "history": user_histories[user_id] + } + except Exception as e: + logger.error(f"Erreur LLM: {str(e)}", exc_info=True) + raise HTTPException(status_code=500, detail="Échec de génération de réponse") + +@app.get("/health") +async def health_check(): + return {"status": "ok", "service": "llm"} + +@app.options("/generate") +async def options_generate(): + return {"message": "OK"} + +def run_service(): + uvicorn.run(app, host="0.0.0.0", port=5002) + +if __name__ == "__main__": + run_service() \ No newline at end of file diff --git a/Back-end/services/stt_service.py b/Back-end/services/stt_service.py new file mode 100644 index 0000000000000000000000000000000000000000..6b3251a052cddf96b0c98ea63d381de5cfd2784b --- /dev/null +++ b/Back-end/services/stt_service.py @@ -0,0 +1,180 @@ +import tempfile +from fastapi import FastAPI, UploadFile, File, HTTPException +import logging +import os +import uvicorn +from faster_whisper import WhisperModel +from tempfile import NamedTemporaryFile +from fastapi.middleware.cors import CORSMiddleware +from pydub import AudioSegment +from langdetect import detect, DetectorFactory, LangDetectException +import glob + +# Configuration du logger +logging.basicConfig(level=logging.DEBUG, format="%(asctime)s [%(levelname)s] %(message)s") +logger = logging.getLogger("STT_Service") + +# Initialisation FastAPI +app = FastAPI() + +# CORS pour frontend local +origins = ["http://localhost:5173", "http://127.0.0.1:5173"] +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Paramètres +MODEL_SIZE = "small" # Léger et rapide +MIN_AUDIO_SIZE = 1024 # Bytes (~0.03s à 16kHz) +MIN_AUDIO_DURATION = 0.75 # Sec min pour traitement +SAMPLE_RATE = 16000 # Hz, 16-bit mono +MAX_TEMP_FILES = 1000 # Limite de fichiers temporaires + +# Initialisation du modèle Whisper +logger.info(f"Chargement du modèle Faster-Whisper ({MODEL_SIZE})") +try: + model = WhisperModel(MODEL_SIZE, device="cpu", compute_type="int8") + logger.info("Modèle Whisper chargé avec succès") +except Exception as e: + logger.error(f"Erreur lors du chargement du modèle Whisper: {e}") + model = None + +# Rendre langdetect plus stable +DetectorFactory.seed = 0 + +# Correction langues fréquentes +EN_WORDS = {"hi", "hello", "hey", "ok", "thanks", "bye", "yes", "no"} +AR_WORDS = {"salam", "salaam", "marhaban", "مرحبا", "سلام", "شكرا", "أهلا"} + +def validate_language(transcript: str, whisper_lang: str, probability: float) -> str: + """Valide ou corrige la langue détectée par Whisper avec langdetect.""" + if not transcript.strip(): + return whisper_lang or "fr" + + try: + detected_lang = detect(transcript) + except LangDetectException: + return whisper_lang or "fr" + + # Normalisation et correction + text_lower = transcript.lower().strip() + if text_lower in EN_WORDS: + return "en" + if text_lower in AR_WORDS: + return "ar" + + # Si probabilité faible (<0.9) et langdetect donne une langue différente, préférer langdetect + if probability < 0.9 and detected_lang in {"fr", "en", "ar"}: + logger.debug(f"Langue corrigée par langdetect: {whisper_lang} -> {detected_lang}") + return detected_lang + + # Sinon, on garde la langue de Whisper + return whisper_lang or "fr" + +def clean_temp_files(): + """Supprime les fichiers temporaires excédentaires.""" + temp_files = glob.glob(f"{tempfile.gettempdir()}/*.wav") + if len(temp_files) > MAX_TEMP_FILES: + temp_files.sort(key=os.path.getmtime) + for file in temp_files[:-MAX_TEMP_FILES]: + try: + os.unlink(file) + logger.info(f"Fichier temporaire supprimé: {file}") + except Exception as e: + logger.warning(f"Erreur lors de la suppression de {file}: {e}") + +@app.post("/transcribe") +async def transcribe_audio(file: UploadFile = File(...), language: str = None): + """ + Transcrit un fichier audio en texte avec Faster-Whisper. + - file: Audio (WAV, MP3, M4A, WebM, etc.) + - language: Langue cible (optionnel: en, ar, fr, etc.), sinon autodétection + """ + logger.info(f"Fichier reçu: filename={file.filename}, content_type={file.content_type}") + + # Vérifier si le modèle est chargé + if model is None: + logger.error("Modèle Whisper non chargé") + raise HTTPException(status_code=503, detail="Service STT non disponible") + + temp_audio_path = None + try: + # Nettoie les fichiers temporaires si nécessaire + clean_temp_files() + + # Sauvegarde temporaire + suffix = os.path.splitext(file.filename)[1].lower() + content = await file.read() + if len(content) < MIN_AUDIO_SIZE: + logger.debug(f"Audio trop petit ({len(content)} bytes), skip") + return {"transcript": "", "lang": language or "unknown", "status": "skipped"} + + with NamedTemporaryFile(delete=False, suffix=suffix) as temp_audio: + temp_audio.write(content) + temp_audio_path = temp_audio.name + + # Vérification durée + duration = os.path.getsize(temp_audio_path) / (SAMPLE_RATE * 2) # 16kHz, 16-bit mono + logger.info(f"Processing audio, duration: {duration:.3f}s") + if duration < MIN_AUDIO_DURATION: + logger.debug(f"Audio trop court ({duration:.3f}s), skip") + return {"transcript": "", "lang": language or "unknown", "status": "skipped"} + + # Conversion si non supporté + supported_formats = [".wav", ".mp3", ".m4a", ".webm"] + wav_path = temp_audio_path + if suffix != ".wav": + try: + audio = AudioSegment.from_file(temp_audio_path) + with NamedTemporaryFile(delete=False, suffix=".wav") as wav_fd: + audio.export(wav_fd.name, format="wav") + wav_path = wav_fd.name + logger.info(f"Converti en WAV: {wav_path}") + except Exception as e: + logger.error(f"Erreur conversion WAV: {e}") + raise HTTPException(status_code=400, detail="Erreur conversion audio") + + # Transcription + segments, info = model.transcribe(wav_path, language=language if language else None) + transcript = " ".join([s.text.strip() for s in segments if s.text.strip()]) + + # Validation de la langue + detected_lang = language or validate_language(transcript, info.language, info.language_probability) + logger.info(f"Langue détectée: {info.language}, Probabilité: {info.language_probability:.2f}, Langue finale: {detected_lang}") + logger.info(f"Transcript: '{transcript}'") + + return { + "transcript": transcript, + "lang": detected_lang, + "status": "success" if transcript else "empty" + } + + except Exception as e: + logger.error(f"Échec transcription: {e}", exc_info=True) + raise HTTPException(status_code=500, detail="Erreur STT") + + finally: + # Nettoyage fichiers temporaires + try: + if temp_audio_path and os.path.exists(temp_audio_path): + os.unlink(temp_audio_path) + if "wav_path" in locals() and wav_path != temp_audio_path and os.path.exists(wav_path): + os.unlink(wav_path) + except Exception as e: + logger.warning(f"Erreur nettoyage fichiers: {e}") + +@app.get("/health") +async def health_check(): + """Vérifie l’état du service.""" + return {"status": "ok", "service": "stt"} + +def run_service(): + """Lance le service STT.""" + uvicorn.run(app, host="0.0.0.0", port=5001) + +if __name__ == "__main__": + run_service() \ No newline at end of file diff --git a/Back-end/services/tts_service.py b/Back-end/services/tts_service.py new file mode 100644 index 0000000000000000000000000000000000000000..08cfbcb9584a4f4261483d3bd11bd3029caabf76 --- /dev/null +++ b/Back-end/services/tts_service.py @@ -0,0 +1,119 @@ +# File: tts_service.py +from fastapi import FastAPI, HTTPException +from fastapi.responses import FileResponse +from pydantic import BaseModel +from gtts import gTTS +import hashlib +import os +import sys +import logging +import uvicorn +from pathlib import Path +from fastapi.middleware.cors import CORSMiddleware + +# Configuration du logging +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s | %(levelname)s | %(name)s | %(message)s", + stream=sys.stdout +) +logger = logging.getLogger("TTS_Service") + +app = FastAPI() + +# Chemins absolus +BASE_DIR = Path(__file__).parent.parent.resolve() +CACHE_DIR = BASE_DIR / "tts_cache" +CACHE_DIR.mkdir(exist_ok=True) + +# Configuration CORS +origins = ["*"] # Autoriser toutes les origines pour les tests + +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Langues supportées +SUPPORTED_LANGUAGES = {"fr", "en", "ar", "es", "de", "it"} + +class TTSRequest(BaseModel): + text: str + lang: str + +@app.post("/generate-tts") +async def generate_tts(request: TTSRequest): + text = request.text.strip() + lang = request.lang.strip().lower() + + # Validation des entrées + if not text: + raise HTTPException(status_code=400, detail="Le texte ne peut pas être vide") + if lang not in SUPPORTED_LANGUAGES: + raise HTTPException( + status_code=400, + detail=f"Langue non supportée. Choisissez parmi: {', '.join(SUPPORTED_LANGUAGES)}" + ) + + logger.info(f"Génération TTS: {len(text)} caractères en {lang}") + + # Génération du hash + normalized_text = text.replace(" ", "_").lower() + text_hash = hashlib.md5(f"{normalized_text}_{lang}".encode('utf-8')).hexdigest() + filename = f"{text_hash}.mp3" + filepath = CACHE_DIR / filename + + # Utilisation du cache si disponible + if filepath.exists(): + logger.info(f"Fichier existant: {filepath}") + return { + "audioPath": str(filepath.relative_to(BASE_DIR)), + "url": f"/audio/{filepath.relative_to(BASE_DIR)}" + } + + # Génération du fichier audio + try: + tts = gTTS(text=text, lang=lang) + tts.save(str(filepath)) + logger.info(f"Fichier généré: {filepath}") + + return { + "audioPath": str(filepath.relative_to(BASE_DIR)), + "url": f"/audio/{filepath.relative_to(BASE_DIR)}" + } + except Exception as e: + logger.error(f"Erreur TTS: {str(e)}", exc_info=True) + raise HTTPException( + status_code=500, + detail=f"Échec de génération audio: {str(e)}" + ) + +@app.get("/audio/{file_path:path}") +async def get_audio(file_path: str): + # Sécurité: empêcher les accès en dehors du cache + if ".." in file_path or file_path.startswith("/"): + raise HTTPException(status_code=403, detail="Accès non autorisé") + + full_path = BASE_DIR / file_path + + # Vérifier que le chemin est dans le cache + if not str(full_path).startswith(str(CACHE_DIR)): + raise HTTPException(status_code=403, detail="Accès non autorisé") + + if not full_path.exists(): + raise HTTPException(status_code=404, detail="Fichier audio non trouvé") + + return FileResponse(full_path, media_type="audio/mpeg") + +@app.get("/health") +async def health_check(): + return {"status": "ok", "service": "tts"} + +def run_service(): + uvicorn.run(app, host="0.0.0.0", port=5000) + +if __name__ == "__main__": + run_service() diff --git a/Back-end/start_services.py b/Back-end/start_services.py new file mode 100644 index 0000000000000000000000000000000000000000..2d82c67b5a57fb84ed6e639ab2b3d99ba7907fe1 --- /dev/null +++ b/Back-end/start_services.py @@ -0,0 +1,121 @@ +import os +import subprocess +import sys +import logging +from logging.handlers import RotatingFileHandler +from datetime import datetime +import threading +import time + +# ─────────────── UTF-8 Windows ─────────────── +if os.name == 'nt': + import ctypes + ctypes.windll.kernel32.SetConsoleCP(65001) + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + +# ─────────────── Répertoires de logs ─────────────── +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +LOG_DIR = os.path.join(BASE_DIR, "logs") +os.makedirs(LOG_DIR, exist_ok=True) + +# ─────────────── Logger central ─────────────── +central_logger = logging.getLogger("Holokia") +central_logger.setLevel(logging.INFO) + +console_handler = logging.StreamHandler(sys.stdout) +console_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) + +central_file_handler = RotatingFileHandler( + os.path.join(LOG_DIR, "holokia.log"), + maxBytes=5*1024*1024, + backupCount=5, + encoding="utf-8" +) +central_file_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) + +central_logger.addHandler(console_handler) +central_logger.addHandler(central_file_handler) + +central_logger.info("🚀 Démarrage de tous les services Holokia Avatar") +central_logger.info(f"📅 Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + +# ─────────────── Services ─────────────── +SERVICES = { + "TTS": {"script": "services/tts_service.py", "port": 5000, "prefix": "[TTS]"}, + "STT": {"script": "services/stt_service.py", "port": 5001, "prefix": "[STT]"}, + "LLM": {"script": "services/llm_service.py", "port": 5002, "prefix": "[LLM]"}, + "LiveStream": {"script": "services/live_stream_service.py", "port": 5003, "prefix": "[Live]"}, + # "Backend": {"script": os.path.join(BASE_DIR, "app/main.py"), "port": int(os.getenv("PORT", 8000)), "prefix": "[Backend]"} +} + +processes = {} + +# ─────────────── Fonction pour créer un logger par service ─────────────── +def create_service_logger(name): + logger = logging.getLogger(name) + logger.setLevel(logging.INFO) + + file_handler = RotatingFileHandler( + os.path.join(LOG_DIR, f"{name}.log"), + maxBytes=5*1024*1024, + backupCount=3, + encoding="utf-8" + ) + file_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) + + # Eviter d'ajouter plusieurs handlers si déjà créé + if not logger.handlers: + logger.addHandler(console_handler) + logger.addHandler(file_handler) + + return logger + +# ─────────────── Démarrer un service ─────────────── +def start_service(name, info): + prefix = info["prefix"] + port = info["port"] + script = info["script"] + + service_logger = create_service_logger(name) + + # Ajuster PYTHONPATH + #env = os.environ.copy() + #env["PYTHONPATH"] = f"{BASE_DIR};{os.path.dirname(script)};{env.get('PYTHONPATH','')}" + + service_logger.info(f"{prefix} 🚀 Démarrage du service sur le port {port} ...") + + proc = subprocess.Popen( + [sys.executable, script], + stdout=sys.stdout, + stderr=sys.stderr, + bufsize=1, + universal_newlines=True, + #env=env + ) + + # Les logs des services apparaîtront directement dans stdout + + return proc + +# ─────────────── Lancement de tous les services ─────────────── +for name, info in SERVICES.items(): + proc = start_service(name, info) + processes[name] = proc + +central_logger.info("⚙️ Tous les services ont été lancés. Ctrl+C pour arrêter.") + +# ─────────────── Boucle de surveillance ─────────────── +try: + while True: + for name, proc in list(processes.items()): + ret = proc.poll() + if ret is not None: + central_logger.error(f"{SERVICES[name]['prefix']} ❌ Service arrêté ! Code: {ret}") + proc_new = start_service(name, SERVICES[name]) + processes[name] = proc_new + time.sleep(2) +except KeyboardInterrupt: + central_logger.info("⏹ Arrêt de tous les services...") + for name, proc in processes.items(): + proc.terminate() + central_logger.info("✅ Tous les services ont été arrêtés.") diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..535fa498e92bb23506e46658d8572de1219f0cb7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,92 @@ +# =========================================== +# Dockerfile pour Hugging Face Spaces +# HOLOKIA-AVATAR - Avatar 3D Interactif +# =========================================== + +# Stage 1: Build frontend +FROM node:18-alpine AS frontend-build + +WORKDIR /app/frontend + +# Copy package files +COPY frontend/package*.json ./ +COPY frontend/wawa-lipsync/package*.json ./wawa-lipsync/ + +# Install dependencies (including dev dependencies for build) +RUN npm ci + +# Copy source code +COPY frontend/ . + +# Install wawa-lipsync dependencies and build +WORKDIR /app/frontend/wawa-lipsync +RUN npm install +RUN npm run build + +# Build main application +WORKDIR /app/frontend +RUN npm run build + +# Stage 2: Python backend +FROM python:3.11-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + curl \ + ffmpeg \ + nginx \ + wget \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# Create non-root user +RUN useradd --create-home --shell /bin/bash app + +# Set working directory +WORKDIR /app + +# Copy requirements first for better caching +COPY Back-end/requirements.txt . + +# Install Python dependencies +RUN pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir -r requirements.txt + +# Copy backend application code +COPY Back-end/ . + +# Copy frontend built files from previous stage +COPY --from=frontend-build /app/frontend/dist /usr/share/nginx/html + +# Copy nginx configuration +COPY docker/nginx.conf /etc/nginx/nginx.conf + +# Copy app.py (entry point for HF) +COPY app.py . + +# Copy diagnostic scripts +COPY debug_websocket.py . +COPY test_services_detailed.py . + +# Create necessary directories and set permissions +RUN mkdir -p logs tts_cache /var/cache/nginx /var/log/nginx /var/run /var/lib/nginx && \ + chown -R app:app /app && \ + chown -R app:app /usr/share/nginx/html && \ + chown -R app:app /var/cache/nginx /var/log/nginx /var/run /var/lib/nginx + +# Set environment variables +ENV PYTHONPATH=/app +ENV PORT=7860 + +# Expose port (Hugging Face standard) +EXPOSE 7860 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD curl -f http://localhost:7860/health || exit 1 + +# Switch to non-root user +USER app + +# Start application +CMD ["python", "app.py"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3eb947be9db30bda2d600ca33bd73560871fef5f --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +--- +title: HOLOKIA-AVATAR +emoji: 🤖 +colorFrom: blue +colorTo: purple +sdk: docker +pinned: false +license: mit +short_description: Avatar 3D intelligent avec IA conversationnelle +--- + +# 🤖 HOLOKIA-AVATAR - Avatar 3D Interactif + +Un avatar 3D intelligent avec capacités de conversation en temps réel, utilisant l'IA pour des interactions naturelles. + +## ✨ Fonctionnalités + +- 🎭 **Avatar 3D interactif** avec synchronisation labiale +- 🗣️ **Reconnaissance vocale** (Speech-to-Text) +- 🔊 **Synthèse vocale** (Text-to-Speech) +- 🧠 **IA conversationnelle** (Groq LLM) +- 📡 **Streaming temps réel** via WebSocket +- 🌐 **Interface web moderne** avec React et Three.js + +## 🚀 Utilisation + +1. **Parlez** à l'avatar via le microphone +2. **L'avatar écoute** et comprend votre message +3. **L'IA génère** une réponse intelligente +4. **L'avatar parle** avec synchronisation labiale +5. **Interaction continue** en temps réel + +## 🔧 Configuration + +Ajoutez votre clé API Groq dans les **Secrets** du Space : +- Nom : `GROQ_API_KEY` +- Valeur : `gsk_...` (votre clé API) + +## 📄 Licence + +MIT License + +--- + +**Développé avec ❤️ par l'équipe HOLOKIA** \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..28b4204ce70b28e1c9cfb813879bf23c562fa565 --- /dev/null +++ b/app.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +""" +HOLOKIA-AVATAR - Point d'entrée pour Hugging Face Spaces +Avatar 3D interactif avec IA conversationnelle +""" + +import os +import sys +import subprocess +import time +import logging +from pathlib import Path + +# Configuration du logging +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s" +) +logger = logging.getLogger("HOLOKIA-AVATAR") + +def check_requirements(): + """Vérifie que toutes les dépendances sont installées.""" + try: + import fastapi + import uvicorn + import gtts + import faster_whisper + import langchain_groq + logger.info("✅ Toutes les dépendances sont installées") + return True + except ImportError as e: + logger.error(f"❌ Dépendance manquante: {e}") + return False + +def check_environment(): + """Vérifie les variables d'environnement requises.""" + groq_key = os.getenv("GROQ_API_KEY") + if not groq_key: + logger.warning("⚠️ GROQ_API_KEY n'est pas définie") + logger.info("💡 Ajoutez votre clé API Groq dans les secrets du Space") + # Ne pas arrêter l'application, juste avertir + return True + + logger.info("✅ Variables d'environnement configurées") + return True + +def check_frontend(): + """Vérifie que le frontend est disponible.""" + nginx_html = Path("/usr/share/nginx/html") + + if nginx_html.exists() and any(nginx_html.iterdir()): + logger.info("✅ Frontend disponible (construit dans le Dockerfile)") + return True + else: + logger.error("❌ Frontend non disponible") + return False + +def test_services(): + """Teste que tous les services backend sont accessibles.""" + logger.info("🔍 Test des services backend...") + + import requests + + services = [ + ("TTS", "http://localhost:5000/health"), + ("STT", "http://localhost:5001/health"), + ("LLM", "http://localhost:5002/health"), + ("Live Stream", "http://localhost:5003/health"), + ] + + all_ok = True + for name, url in services: + try: + response = requests.get(url, timeout=5) + if response.status_code == 200: + logger.info(f"✅ {name}: OK") + else: + logger.error(f"❌ {name}: Erreur (status {response.status_code})") + all_ok = False + except Exception as e: + logger.error(f"❌ {name}: Erreur de connexion - {e}") + all_ok = False + + return all_ok + +def start_services(): + """Démarre tous les services backend.""" + logger.info("🚀 Démarrage des services backend...") + + try: + # Vérifier que le script existe + start_script = Path("start_services.py") + if not start_script.exists(): + logger.error(f"❌ Script de démarrage non trouvé: {start_script}") + return False + + # Démarrer les services en arrière-plan + logger.info("⏳ Démarrage des services en arrière-plan...") + process = subprocess.Popen([ + sys.executable, str(start_script) + ], cwd=os.getcwd(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + # Attendre un peu pour que les services démarrent + time.sleep(15) + + # Vérifier que le processus est toujours en vie + if process.poll() is not None: + logger.error("❌ Les services backend se sont arrêtés prématurément") + stdout, stderr = process.communicate() + logger.error(f"STDOUT: {stdout.decode()}") + logger.error(f"STDERR: {stderr.decode()}") + return False + + # Tester que les services sont accessibles + if not test_services(): + logger.error("❌ Certains services ne sont pas accessibles") + return False + + # Test détaillé des services + logger.info("🔍 Test détaillé des services...") + try: + result = subprocess.run([sys.executable, "debug_websocket.py"], + capture_output=True, text=True, timeout=30) + if result.stdout: + logger.info(f"Résultats des tests WebSocket:\n{result.stdout}") + if result.stderr: + logger.error(f"Erreurs des tests WebSocket:\n{result.stderr}") + except Exception as e: + logger.warning(f"Impossible d'exécuter les tests WebSocket: {e}") + + # Test détaillé des services + logger.info("🔍 Test détaillé des services...") + try: + result = subprocess.run([sys.executable, "test_services_detailed.py"], + capture_output=True, text=True, timeout=120) + if result.stdout: + logger.info(f"Résultats des tests détaillés:\n{result.stdout}") + if result.stderr: + logger.error(f"Erreurs des tests détaillés:\n{result.stderr}") + except Exception as e: + logger.warning(f"Impossible d'exécuter les tests détaillés: {e}") + + logger.info("✅ Services backend démarrés et testés") + return True + + except Exception as e: + logger.error(f"❌ Erreur lors du démarrage des services: {e}") + return False + +def start_nginx(): + """Démarre Nginx.""" + logger.info("🌐 Démarrage de Nginx...") + + try: + # Vérifier la configuration Nginx + result = subprocess.run(["nginx", "-t"], capture_output=True, text=True) + if result.returncode != 0: + logger.error(f"❌ Configuration Nginx invalide: {result.stderr}") + return False + + logger.info("✅ Configuration Nginx valide") + + # Démarrer Nginx en mode daemon + subprocess.run(["nginx"], check=True) + logger.info("✅ Nginx démarré") + + return True + + except subprocess.CalledProcessError as e: + logger.error(f"❌ Erreur Nginx: {e}") + return False + except Exception as e: + logger.error(f"❌ Erreur inattendue avec Nginx: {e}") + return False + +def main(): + """Point d'entrée principal.""" + logger.info("🤖 HOLOKIA-AVATAR - Démarrage sur Hugging Face Spaces (v2.2)") + logger.info(f"📁 Répertoire de travail: {os.getcwd()}") + logger.info(f"🐍 Version Python: {sys.version}") + + try: + # Vérifications préliminaires + if not check_requirements(): + logger.error("❌ Vérification des dépendances échouée") + sys.exit(1) + + if not check_environment(): + logger.warning("⚠️ Vérification de l'environnement échouée, mais on continue") + + if not check_frontend(): + logger.error("❌ Vérification du frontend échouée") + sys.exit(1) + + # Démarrage des services + if not start_services(): + logger.error("❌ Impossible de démarrer les services backend") + # Ne pas arrêter, essayer de continuer avec Nginx seulement + + # Démarrage de Nginx + if not start_nginx(): + logger.error("❌ Impossible de démarrer Nginx") + sys.exit(1) + + logger.info("🎉 Application démarrée avec succès!") + logger.info("🌐 Serveur web accessible sur le port 7860") + + # Garder l'application en vie + try: + while True: + time.sleep(60) + logger.info("💓 Application en vie...") + except KeyboardInterrupt: + logger.info("🛑 Arrêt de l'application") + + except Exception as e: + logger.error(f"❌ Erreur critique: {e}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/clean_and_restructure.ps1 b/clean_and_restructure.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..2c18f8d596ba2e2b2c564525007ef501a6402a27 --- /dev/null +++ b/clean_and_restructure.ps1 @@ -0,0 +1,278 @@ +# =========================================== +# Script PowerShell de nettoyage et restructuration pour Hugging Face +# HOLOKIA-AVATAR - Avatar 3D Interactif +# =========================================== + +param( + [switch]$Help +) + +# Fonctions utilitaires +function Write-Info { + param([string]$Message) + Write-Host "[INFO] $Message" -ForegroundColor Blue +} + +function Write-Success { + param([string]$Message) + Write-Host "[SUCCESS] $Message" -ForegroundColor Green +} + +function Write-Warning { + param([string]$Message) + Write-Host "[WARNING] $Message" -ForegroundColor Yellow +} + +function Write-Error { + param([string]$Message) + Write-Host "[ERROR] $Message" -ForegroundColor Red +} + +# Aide +if ($Help) { + Write-Host "Usage: .\clean_and_restructure.ps1 [OPTIONS]" + Write-Host "" + Write-Host "Ce script nettoie l'historique Git et restructure le projet pour Hugging Face Spaces." + Write-Host "" + Write-Host "Options:" + Write-Host " -Help Afficher cette aide" + Write-Host "" + Write-Host "ATTENTION: Ce script va supprimer l'historique Git existant!" + Write-Host "Une sauvegarde sera créée dans ..\backup_holokia_avatar\" + Write-Host "" + exit 0 +} + +# Vérifications préliminaires +function Test-Requirements { + Write-Info "Vérification des prérequis..." + + # Vérifier Git + if (-not (Get-Command git -ErrorAction SilentlyContinue)) { + Write-Error "Git n'est pas installé" + exit 1 + } + + # Vérifier Git LFS + if (-not (Get-Command git-lfs -ErrorAction SilentlyContinue)) { + Write-Warning "Git LFS n'est pas installé" + Write-Info "Téléchargez et installez Git LFS depuis: https://git-lfs.github.io/" + Write-Info "Ou utilisez: winget install Git.Git-LFS" + exit 1 + } + + Write-Success "Tous les prérequis sont satisfaits" +} + +# Sauvegarde des fichiers importants +function Backup-ImportantFiles { + Write-Info "Sauvegarde des fichiers importants..." + + # Créer un dossier de sauvegarde + $backupDir = "..\backup_holokia_avatar" + if (-not (Test-Path $backupDir)) { + New-Item -ItemType Directory -Path $backupDir -Force | Out-Null + } + + # Sauvegarder les fichiers de configuration + Copy-Item -Path "." -Destination $backupDir -Recurse -Force + + Write-Success "Sauvegarde terminée dans $backupDir" +} + +# Nettoyage de l'historique Git +function Clear-GitHistory { + Write-Info "Nettoyage de l'historique Git..." + + # Supprimer le dossier .git existant + if (Test-Path ".git") { + Write-Info "Suppression de l'historique Git existant..." + Remove-Item -Path ".git" -Recurse -Force + } + + # Initialiser un nouveau dépôt Git + Write-Info "Initialisation d'un nouveau dépôt Git..." + git init + + # Configurer Git LFS + Write-Info "Configuration de Git LFS..." + git lfs install + + # Ajouter les fichiers LFS + git lfs track "*.glb" + git lfs track "*.fbx" + git lfs track "*.obj" + git lfs track "*.png" + git lfs track "*.jpg" + git lfs track "*.jpeg" + git lfs track "*.wav" + git lfs track "*.mp3" + + Write-Success "Historique Git nettoyé et LFS configuré" +} + +# Restructuration des fichiers +function Restructure-Files { + Write-Info "Restructuration des fichiers pour Hugging Face..." + + # Supprimer les fichiers de développement + $filesToRemove = @( + "build-frontend.sh", + "deploy_to_hf.sh", + "test_deployment.py", + "README_HF_DEPLOYMENT.md", + "huggingface_hub_config.yaml" + ) + + foreach ($file in $filesToRemove) { + if (Test-Path $file) { + Remove-Item -Path $file -Force + Write-Info "Supprimé: $file" + } + } + + # Supprimer les dossiers de développement + $dirsToRemove = @( + "docker", + "Back-end\tts_cache", + "Back-end\logs", + "frontend\node_modules", + "frontend\dist", + "__pycache__", + ".pytest_cache" + ) + + foreach ($dir in $dirsToRemove) { + if (Test-Path $dir) { + Remove-Item -Path $dir -Recurse -Force + Write-Info "Supprimé: $dir" + } + } + + # Supprimer les fichiers docker-compose + Get-ChildItem -Path "." -Name "docker-compose*.yml" | ForEach-Object { + Remove-Item -Path $_ -Force + Write-Info "Supprimé: $_" + } + + # Supprimer les fichiers .env (sauf .env.example) + Get-ChildItem -Path "." -Name ".env*" -Recurse | Where-Object { $_.Name -ne ".env.example" } | ForEach-Object { + Remove-Item -Path $_.FullName -Force + Write-Info "Supprimé: $($_.FullName)" + } + + Write-Success "Restructuration terminée" +} + +# Création des fichiers nécessaires pour HF +function New-HuggingFaceFiles { + Write-Info "Création des fichiers nécessaires pour Hugging Face..." + + # Créer un README.md simple pour HF + $readmeContent = @" +--- +title: HOLOKIA-AVATAR +emoji: 🤖 +colorFrom: blue +colorTo: purple +sdk: docker +pinned: false +license: mit +short_description: Avatar 3D intelligent avec IA conversationnelle en temps réel +--- + +# 🤖 HOLOKIA-AVATAR - Avatar 3D Interactif + +Un avatar 3D intelligent avec capacités de conversation en temps réel, utilisant l'IA pour des interactions naturelles. + +## ✨ Fonctionnalités + +- 🎭 **Avatar 3D interactif** avec synchronisation labiale +- 🗣️ **Reconnaissance vocale** (Speech-to-Text) +- 🔊 **Synthèse vocale** (Text-to-Speech) +- 🧠 **IA conversationnelle** (Groq LLM) +- 📡 **Streaming temps réel** via WebSocket +- 🌐 **Interface web moderne** avec React et Three.js + +## 🚀 Utilisation + +1. **Parlez** à l'avatar via le microphone +2. **L'avatar écoute** et comprend votre message +3. **L'IA génère** une réponse intelligente +4. **L'avatar parle** avec synchronisation labiale +5. **Interaction continue** en temps réel + +## 🔧 Configuration + +Ajoutez votre clé API Groq dans les **Secrets** du Space : +- Nom : `GROQ_API_KEY` +- Valeur : `gsk_...` (votre clé API) + +## 📄 Licence + +MIT License + +--- + +**Développé avec ❤️ par l'équipe HOLOKIA** +"@ + + $readmeContent | Out-File -FilePath "README.md" -Encoding UTF8 + + Write-Success "Fichiers HF créés" +} + +# Configuration Git +function Set-GitConfiguration { + Write-Info "Configuration Git..." + + # Ajouter tous les fichiers + git add . + + # Commit initial + git commit -m "Initial commit: HOLOKIA-AVATAR for Hugging Face Spaces" + + Write-Success "Configuration Git terminée" +} + +# Fonction principale +function Main { + Write-Host "🧹 HOLOKIA-AVATAR - Nettoyage et restructuration pour Hugging Face" -ForegroundColor Cyan + Write-Host "==================================================================" -ForegroundColor Cyan + Write-Host "" + + # Vérifications + Test-Requirements + + # Sauvegarde + Backup-ImportantFiles + + # Nettoyage + Clear-GitHistory + + # Restructuration + Restructure-Files + + # Création des fichiers HF + New-HuggingFaceFiles + + # Configuration Git + Set-GitConfiguration + + Write-Host "" + Write-Success "🎉 Nettoyage et restructuration terminés!" + Write-Host "" + Write-Info "Prochaines étapes:" + Write-Host "1. Ajoutez le remote Hugging Face:" + Write-Host " git remote add origin https://huggingface.co/spaces/DataSage12/avatar-v2" + Write-Host "" + Write-Host "2. Poussez le code:" + Write-Host " git push -u origin main" + Write-Host "" + Write-Host "3. Ajoutez votre clé API Groq dans les secrets du Space" + Write-Host "" + Write-Warning "Sauvegarde disponible dans: ..\backup_holokia_avatar\" +} + +# Exécution +Main diff --git a/clean_and_restructure.sh b/clean_and_restructure.sh new file mode 100644 index 0000000000000000000000000000000000000000..5ba21282fbfb4ff3f12d89d89a2c3e0c88ef2e48 --- /dev/null +++ b/clean_and_restructure.sh @@ -0,0 +1,259 @@ +#!/bin/bash + +# =========================================== +# Script de nettoyage et restructuration pour Hugging Face +# HOLOKIA-AVATAR - Avatar 3D Interactif +# =========================================== + +set -e + +# Couleurs pour les logs +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Fonctions utilitaires +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Vérifications préliminaires +check_requirements() { + log_info "Vérification des prérequis..." + + # Vérifier Git + if ! command -v git &> /dev/null; then + log_error "Git n'est pas installé" + exit 1 + fi + + # Vérifier Git LFS + if ! command -v git-lfs &> /dev/null; then + log_warning "Git LFS n'est pas installé" + log_info "Installation de Git LFS..." + if command -v apt-get &> /dev/null; then + sudo apt-get install git-lfs + elif command -v brew &> /dev/null; then + brew install git-lfs + else + log_error "Impossible d'installer Git LFS automatiquement" + log_info "Installez Git LFS manuellement: https://git-lfs.github.io/" + exit 1 + fi + fi + + log_success "Tous les prérequis sont satisfaits" +} + +# Sauvegarde des fichiers importants +backup_important_files() { + log_info "Sauvegarde des fichiers importants..." + + # Créer un dossier de sauvegarde + mkdir -p ../backup_holokia_avatar + + # Sauvegarder les fichiers de configuration + cp -r . ../backup_holokia_avatar/ 2>/dev/null || true + + log_success "Sauvegarde terminée dans ../backup_holokia_avatar/" +} + +# Nettoyage de l'historique Git +clean_git_history() { + log_info "Nettoyage de l'historique Git..." + + # Supprimer le dossier .git existant + if [ -d ".git" ]; then + log_info "Suppression de l'historique Git existant..." + rm -rf .git + fi + + # Initialiser un nouveau dépôt Git + log_info "Initialisation d'un nouveau dépôt Git..." + git init + + # Configurer Git LFS + log_info "Configuration de Git LFS..." + git lfs install + + # Ajouter les fichiers LFS + git lfs track "*.glb" + git lfs track "*.fbx" + git lfs track "*.obj" + git lfs track "*.png" + git lfs track "*.jpg" + git lfs track "*.jpeg" + git lfs track "*.wav" + git lfs track "*.mp3" + + log_success "Historique Git nettoyé et LFS configuré" +} + +# Restructuration des fichiers +restructure_files() { + log_info "Restructuration des fichiers pour Hugging Face..." + + # Supprimer les fichiers de développement + rm -f build-frontend.sh + rm -f deploy_to_hf.sh + rm -f test_deployment.py + rm -f README_HF_DEPLOYMENT.md + rm -f huggingface_hub_config.yaml + rm -rf docker/ + rm -f docker-compose*.yml + + # Supprimer les fichiers de cache et temporaires + rm -rf Back-end/tts_cache/ + rm -rf Back-end/logs/ + rm -rf frontend/node_modules/ + rm -rf frontend/dist/ + rm -rf __pycache__/ + rm -rf .pytest_cache/ + + # Supprimer les fichiers .env s'ils existent + find . -name ".env*" -not -name ".env.example" -delete 2>/dev/null || true + + log_success "Restructuration terminée" +} + +# Création des fichiers nécessaires pour HF +create_hf_files() { + log_info "Création des fichiers nécessaires pour Hugging Face..." + + # Créer un README.md simple pour HF + cat > README.md << 'EOF' +--- +title: HOLOKIA-AVATAR +emoji: 🤖 +colorFrom: blue +colorTo: purple +sdk: docker +pinned: false +license: mit +short_description: Avatar 3D intelligent avec IA conversationnelle en temps réel +--- + +# 🤖 HOLOKIA-AVATAR - Avatar 3D Interactif + +Un avatar 3D intelligent avec capacités de conversation en temps réel, utilisant l'IA pour des interactions naturelles. + +## ✨ Fonctionnalités + +- 🎭 **Avatar 3D interactif** avec synchronisation labiale +- 🗣️ **Reconnaissance vocale** (Speech-to-Text) +- 🔊 **Synthèse vocale** (Text-to-Speech) +- 🧠 **IA conversationnelle** (Groq LLM) +- 📡 **Streaming temps réel** via WebSocket +- 🌐 **Interface web moderne** avec React et Three.js + +## 🚀 Utilisation + +1. **Parlez** à l'avatar via le microphone +2. **L'avatar écoute** et comprend votre message +3. **L'IA génère** une réponse intelligente +4. **L'avatar parle** avec synchronisation labiale +5. **Interaction continue** en temps réel + +## 🔧 Configuration + +Ajoutez votre clé API Groq dans les **Secrets** du Space : +- Nom : `GROQ_API_KEY` +- Valeur : `gsk_...` (votre clé API) + +## 📄 Licence + +MIT License + +--- + +**Développé avec ❤️ par l'équipe HOLOKIA** +EOF + + log_success "Fichiers HF créés" +} + +# Configuration Git +setup_git() { + log_info "Configuration Git..." + + # Ajouter tous les fichiers + git add . + + # Commit initial + git commit -m "Initial commit: HOLOKIA-AVATAR for Hugging Face Spaces" + + log_success "Configuration Git terminée" +} + +# Fonction principale +main() { + echo "🧹 HOLOKIA-AVATAR - Nettoyage et restructuration pour Hugging Face" + echo "==================================================================" + echo "" + + # Vérifications + check_requirements + + # Sauvegarde + backup_important_files + + # Nettoyage + clean_git_history + + # Restructuration + restructure_files + + # Création des fichiers HF + create_hf_files + + # Configuration Git + setup_git + + echo "" + log_success "🎉 Nettoyage et restructuration terminés!" + echo "" + log_info "Prochaines étapes:" + echo "1. Ajoutez le remote Hugging Face:" + echo " git remote add origin https://huggingface.co/spaces/DataSage12/avatar-v2" + echo "" + echo "2. Poussez le code:" + echo " git push -u origin main" + echo "" + echo "3. Ajoutez votre clé API Groq dans les secrets du Space" + echo "" + log_warning "Sauvegarde disponible dans: ../backup_holokia_avatar/" +} + +# Gestion des arguments +case "${1:-}" in + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Ce script nettoie l'historique Git et restructure le projet pour Hugging Face Spaces." + echo "" + echo "Options:" + echo " --help Afficher cette aide" + echo "" + echo "ATTENTION: Ce script va supprimer l'historique Git existant!" + echo "Une sauvegarde sera créée dans ../backup_holokia_avatar/" + echo "" + exit 0 + ;; + *) + main "$@" + ;; +esac diff --git a/debug_websocket.py b/debug_websocket.py new file mode 100644 index 0000000000000000000000000000000000000000..1c5dbea61e83ffc0288c7d5c85ce59adf0acf9ff --- /dev/null +++ b/debug_websocket.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +""" +Script de diagnostic pour le problème WebSocket +""" + +import asyncio +import aiohttp +import json +import sys + +async def test_websocket_connection(): + """Teste la connexion WebSocket""" + print("🔍 Test de connexion WebSocket...") + + try: + # Test de connexion WebSocket + async with aiohttp.ClientSession() as session: + async with session.ws_connect("ws://localhost:5003/live_stream/test_user?lang=fr&room_id=test") as ws: + print("✅ Connexion WebSocket établie") + + # Envoyer un message ping + ping_message = {"type": "ping"} + await ws.send_str(json.dumps(ping_message)) + print("✅ Message ping envoyé") + + # Attendre une réponse + try: + async with asyncio.timeout(5): + msg = await ws.receive() + print(f"✅ Réponse reçue: {msg}") + except asyncio.TimeoutError: + print("⚠️ Aucune réponse reçue dans les 5 secondes") + + await ws.close() + print("✅ Connexion WebSocket fermée") + + except Exception as e: + print(f"❌ Erreur WebSocket: {e}") + import traceback + print(f"Traceback: {traceback.format_exc()}") + +async def main(): + print("🚀 Démarrage du diagnostic WebSocket...") + await test_websocket_connection() + print("✅ Diagnostic terminé") + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/deploy_to_hf_final.ps1 b/deploy_to_hf_final.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..ed0295a20e13b432f53b30363554dd516eac5af7 --- /dev/null +++ b/deploy_to_hf_final.ps1 @@ -0,0 +1,252 @@ +# =========================================== +# Script de déploiement final pour Hugging Face Spaces +# HOLOKIA-AVATAR - Avatar 3D Interactif +# =========================================== + +param( + [switch]$Help, + [string]$HuggingFaceToken = "" +) + +# Configuration +$HF_USERNAME = "DataSage12" +$SPACE_NAME = "avatar-v2" +$REPO_URL = "https://huggingface.co/spaces/$HF_USERNAME/$SPACE_NAME" + +# Fonctions utilitaires +function Write-Info { + param([string]$Message) + Write-Host "[INFO] $Message" -ForegroundColor Blue +} + +function Write-Success { + param([string]$Message) + Write-Host "[SUCCESS] $Message" -ForegroundColor Green +} + +function Write-Warning { + param([string]$Message) + Write-Host "[WARNING] $Message" -ForegroundColor Yellow +} + +function Write-Error { + param([string]$Message) + Write-Host "[ERROR] $Message" -ForegroundColor Red +} + +# Aide +if ($Help) { + Write-Host "Usage: .\deploy_to_hf_final.ps1 [OPTIONS]" + Write-Host "" + Write-Host "Options:" + Write-Host " -Help Afficher cette aide" + Write-Host " -HuggingFaceToken Token d'accès Hugging Face (optionnel)" + Write-Host "" + Write-Host "Ce script déploie HOLOKIA-AVATAR sur Hugging Face Spaces." + Write-Host "" + Write-Host "Variables d'environnement requises:" + Write-Host " GROQ_API_KEY Clé API Groq" + Write-Host "" + exit 0 +} + +# Vérifications préliminaires +function Test-Requirements { + Write-Info "Vérification des prérequis..." + + # Vérifier Git + if (-not (Get-Command git -ErrorAction SilentlyContinue)) { + Write-Error "Git n'est pas installé" + exit 1 + } + + # Vérifier Git LFS + if (-not (Get-Command git-lfs -ErrorAction SilentlyContinue)) { + Write-Error "Git LFS n'est pas installé" + Write-Info "Installez Git LFS: winget install Git.Git-LFS" + exit 1 + } + + # Vérifier la clé API Groq + $groqKey = $env:GROQ_API_KEY + if (-not $groqKey) { + Write-Error "GROQ_API_KEY n'est pas définie" + Write-Info "Définissez votre clé API avec: `$env:GROQ_API_KEY = 'your_key_here'" + exit 1 + } + + Write-Success "Tous les prérequis sont satisfaits" + Write-Info "Clé API Groq: $($groqKey.Substring(0, 10))..." +} + +# Vérifier la structure du projet +function Test-ProjectStructure { + Write-Info "Vérification de la structure du projet..." + + $requiredFiles = @( + "Dockerfile", + "app.py", + "requirements.txt", + "Back-end\start_services.py", + "Back-end\services\tts_service.py", + "Back-end\services\stt_service.py", + "Back-end\services\llm_service.py", + "Back-end\services\live_stream_service.py", + "frontend\package.json", + "frontend\src\App.jsx" + ) + + $missingFiles = @() + foreach ($file in $requiredFiles) { + if (-not (Test-Path $file)) { + $missingFiles += $file + } + } + + if ($missingFiles.Count -gt 0) { + Write-Error "Fichiers manquants:" + foreach ($file in $missingFiles) { + Write-Host " - $file" -ForegroundColor Red + } + exit 1 + } + + Write-Success "Structure du projet validée" +} + +# Configuration Git +function Set-GitConfiguration { + Write-Info "Configuration Git..." + + # Vérifier si le remote HF existe + $existingRemote = git remote get-url origin 2>$null + if ($existingRemote) { + Write-Info "Remote existant: $existingRemote" + if ($existingRemote -ne $REPO_URL) { + Write-Info "Mise à jour du remote..." + git remote set-url origin $REPO_URL + } + } else { + Write-Info "Ajout du remote Hugging Face..." + git remote add origin $REPO_URL + } + + # Configurer l'authentification si un token est fourni + if ($HuggingFaceToken) { + Write-Info "Configuration de l'authentification..." + $authUrl = $REPO_URL -replace "https://", "https://$HuggingFaceToken@" + git remote set-url origin $authUrl + } + + Write-Success "Configuration Git terminée" +} + +# Test de l'image Docker +function Test-DockerImage { + Write-Info "Test de l'image Docker localement..." + + # Vérifier Docker + if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { + Write-Warning "Docker n'est pas installé, test ignoré" + return + } + + try { + # Construire l'image + Write-Info "Construction de l'image Docker..." + docker build -t holokia-avatar:test . + + # Tester l'image + Write-Info "Test de l'image..." + $containerId = docker run -d -p 7860:7860 -e GROQ_API_KEY=$env:GROQ_API_KEY holokia-avatar:test + + # Attendre le démarrage + Write-Info "Attente du démarrage du service..." + Start-Sleep -Seconds 60 + + # Vérifier la santé + try { + $response = Invoke-WebRequest -Uri "http://localhost:7860/health" -TimeoutSec 10 + if ($response.StatusCode -eq 200) { + Write-Success "Test local réussi" + } else { + Write-Warning "Test local: code de statut $($response.StatusCode)" + } + } catch { + Write-Warning "Test local: service non accessible" + } + + # Nettoyer + docker stop $containerId + docker rm $containerId + docker rmi holokia-avatar:test + + } catch { + Write-Warning "Erreur lors du test Docker: $($_.Exception.Message)" + } +} + +# Déploiement +function Deploy-ToHuggingFace { + Write-Info "Déploiement sur Hugging Face Spaces..." + + # Ajouter tous les fichiers + git add . + + # Commit + $commitMessage = "Deploy to Hugging Face Spaces - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" + git commit -m $commitMessage + + # Push vers HF + Write-Info "Push vers Hugging Face..." + git push -u origin main + + Write-Success "Déploiement terminé!" +} + +# Affichage des informations +function Show-Info { + Write-Info "Informations de déploiement:" + Write-Host " - Utilisateur HF: $HF_USERNAME" + Write-Host " - Nom du Space: $SPACE_NAME" + Write-Host " - URL du Space: $REPO_URL" + Write-Host " - Clé API Groq: $($env:GROQ_API_KEY.Substring(0, 10))..." + Write-Host "" +} + +# Fonction principale +function Main { + Write-Host "🚀 HOLOKIA-AVATAR - Déploiement sur Hugging Face Spaces" -ForegroundColor Cyan + Write-Host "=====================================================" -ForegroundColor Cyan + Write-Host "" + + Show-Info + + # Vérifications + Test-Requirements + Test-ProjectStructure + + # Test Docker (optionnel) + Test-DockerImage + + # Configuration Git + Set-GitConfiguration + + # Déploiement + Deploy-ToHuggingFace + + Write-Host "" + Write-Success "🎉 Déploiement terminé avec succès!" + Write-Host "" + Write-Info "Votre Space sera disponible à:" + Write-Host " $REPO_URL" + Write-Host "" + Write-Info "N'oubliez pas d'ajouter votre clé API Groq dans les secrets du Space:" + Write-Host " - Nom: GROQ_API_KEY" + Write-Host " - Valeur: $env:GROQ_API_KEY" + Write-Host "" + Write-Info "Le Space va automatiquement se construire et démarrer dans quelques minutes." +} + +# Exécution +Main diff --git a/env.example b/env.example new file mode 100644 index 0000000000000000000000000000000000000000..dc6a9977470e17ba9e6fb277010ab87a95818d9d --- /dev/null +++ b/env.example @@ -0,0 +1,37 @@ +# =========================================== +# Variables d'environnement pour HOLOKIA-AVATAR +# =========================================== + +# Clé API Groq (requise) +GROQ_API_KEY=gsk_your_groq_api_key_here + +# Configuration Hugging Face (pour le déploiement) +HF_USERNAME=your_huggingface_username +SPACE_NAME=holokia-avatar + +# Configuration des services +PYTHONPATH=/app +PORT=7860 + +# Configuration des logs +LOG_LEVEL=INFO + +# Configuration TTS +TTS_CACHE_DIR=/app/tts_cache +TTS_LANGUAGES=fr,en,ar + +# Configuration STT +STT_MODEL_SIZE=small +STT_MIN_AUDIO_DURATION=0.75 + +# Configuration LLM +LLM_MODEL_NAME=meta-llama/llama-4-scout-17b-16e-instruct +LLM_TEMPERATURE=0 + +# Configuration WebSocket +WS_PING_INTERVAL=20 +WS_PING_TIMEOUT=120 + +# Configuration Nginx +NGINX_WORKER_PROCESSES=auto +NGINX_WORKER_CONNECTIONS=1024 diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000000000000000000000000000000000000..e9619865245b8fc1444127066a36cfb544044a0f --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + Holokia — Avatar vocal + + +
+ + + \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..e35ae6d213103829e39b8dd85d619bf96832945f --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,5630 @@ +{ + "name": "frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "1.0.0", + "dependencies": { + "@react-three/drei": "^10.6.1", + "@react-three/fiber": "^9.3.0", + "@react-three/postprocessing": "^3.0.4", + "@tailwindcss/vite": "^4.0.9", + "axios": "^1.11.0", + "franc-min": "^6.2.0", + "leva": "^0.10.0", + "postcss": "^8.5.6", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "react-icons": "^5.5.0", + "three": "^0.174.0", + "uuid": "^11.1.0", + "wawa-lipsync": "^0.0.1", + "wawa-vfx": "^1.0.16", + "zustand": "^5.0.4" + }, + "devDependencies": { + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.21", + "framer-motion": "^11.18.2", + "globals": "^15.15.0", + "postcss": "^8.4.38", + "postcss-import": "^16.0.1", + "postcss-nested": "^6.0.1", + "tailwindcss": "^3.4.1", + "vite": "^6.2.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=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/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz", + "integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==", + "license": "MIT" + }, + "node_modules/@floating-ui/dom": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz", + "integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^0.7.3" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.2.tgz", + "integrity": "sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^0.5.3", + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@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.1.0", + "resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.1.0.tgz", + "integrity": "sha512-Obb0/gEd/HReTlg8ttaYk+0m62gQJmCblMOjHSMHRrBP2zdfKMHLCRbh/6ex9fSUJMKdjjIEiohwkbGD3wj2Nw==", + "license": "MIT", + "dependencies": { + "promise-worker-transferable": "^1.0.4" + }, + "peerDependencies": { + "three": ">= 0.159.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz", + "integrity": "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.0.tgz", + "integrity": "sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@react-three/drei": { + "version": "10.7.6", + "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.6.tgz", + "integrity": "sha512-ZSFwRlRaa4zjtB7yHO6Q9xQGuyDCzE7whXBhum92JslcMRC3aouivp0rAzszcVymIoJx6PXmibyP+xr+zKdwLg==", + "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.3.0", + "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.3.0.tgz", + "integrity": "sha512-myPe3YL/C8+Eq939/4qIVEPBW/uxV0iiUbmjfwrs9sGKYDG8ib8Dz3Okq7BQt8P+0k4igedONbjXMQy84aDFmQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8", + "@types/react-reconciler": "^0.32.0", + "@types/webxr": "*", + "base64-js": "^1.5.1", + "buffer": "^6.0.3", + "its-fine": "^2.0.0", + "react-reconciler": "^0.31.0", + "react-use-measure": "^2.1.7", + "scheduler": "^0.25.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.0.0", + "react-dom": "^19.0.0", + "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/@react-three/postprocessing": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@react-three/postprocessing/-/postprocessing-3.0.4.tgz", + "integrity": "sha512-e4+F5xtudDYvhxx3y0NtWXpZbwvQ0x1zdOXWTbXMK6fFLVDd4qucN90YaaStanZGS4Bd5siQm0lGL/5ogf8iDQ==", + "license": "MIT", + "dependencies": { + "maath": "^0.6.0", + "n8ao": "^1.9.4", + "postprocessing": "^6.36.6" + }, + "peerDependencies": { + "@react-three/fiber": "^9.0.0", + "react": "^19.0", + "three": ">= 0.156.0" + } + }, + "node_modules/@react-three/postprocessing/node_modules/maath": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/maath/-/maath-0.6.0.tgz", + "integrity": "sha512-dSb2xQuP7vDnaYqfoKzlApeRcR2xtN8/f7WV/TMAkBC8552TwTLtOO0JTcSygkYMjNDPoo6V01jTw/aPi4JrMw==", + "license": "MIT", + "peerDependencies": { + "@types/three": ">=0.144.0", + "three": ">=0.144.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.0.tgz", + "integrity": "sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.0.tgz", + "integrity": "sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.0.tgz", + "integrity": "sha512-YCdWlY/8ltN6H78HnMsRHYlPiKvqKagBP1r+D7SSylxX+HnsgXGCmLiV3Y4nSyY9hW8qr8U9LDUx/Lo7M6MfmQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.0.tgz", + "integrity": "sha512-z4nw6y1j+OOSGzuVbSWdIp1IUks9qNw4dc7z7lWuWDKojY38VMWBlEN7F9jk5UXOkUcp97vA1N213DF+Lz8BRg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.0.tgz", + "integrity": "sha512-Q/dv9Yvyr5rKlK8WQJZVrp5g2SOYeZUs9u/t2f9cQ2E0gJjYB/BWoedXfUT0EcDJefi2zzVfhcOj8drWCzTviw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.0.tgz", + "integrity": "sha512-kdBsLs4Uile/fbjZVvCRcKB4q64R+1mUq0Yd7oU1CMm1Av336ajIFqNFovByipciuUQjBCPMxwJhCgfG2re3rg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.0.tgz", + "integrity": "sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.0.tgz", + "integrity": "sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.0.tgz", + "integrity": "sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.0.tgz", + "integrity": "sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.0.tgz", + "integrity": "sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.0.tgz", + "integrity": "sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.0.tgz", + "integrity": "sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.0.tgz", + "integrity": "sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.0.tgz", + "integrity": "sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.0.tgz", + "integrity": "sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.0.tgz", + "integrity": "sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.0.tgz", + "integrity": "sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.0.tgz", + "integrity": "sha512-YQugafP/rH0eOOHGjmNgDURrpYHrIX0yuojOI8bwCyXwxC9ZdTd3vYkmddPX0oHONLXu9Rb1dDmT0VNpjkzGGw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.0.tgz", + "integrity": "sha512-zYdUYhi3Qe2fndujBqL5FjAFzvNeLxtIqfzNEVKD1I7C37/chv1VxhscWSQHTNfjPCrBFQMnynwA3kpZpZ8w4A==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.0.tgz", + "integrity": "sha512-fGk03kQylNaCOQ96HDMeT7E2n91EqvCDd3RwvT5k+xNdFCeMGnj5b5hEgTGrQuyidqSsD3zJDQ21QIaxXqTBJw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.0.tgz", + "integrity": "sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@stitches/react": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@stitches/react/-/react-1.2.8.tgz", + "integrity": "sha512-9g9dWI4gsSVe8bNLlb+lMkBYsnIKCZTmvqvDG+Avnn69XfmHZKiaMrx7cgTaddq7aTPPmXiTsbFcUy0xgI4+wA==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16.3.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", + "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.5.1", + "lightningcss": "1.30.1", + "magic-string": "^0.30.18", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.13" + } + }, + "node_modules/@tailwindcss/node/node_modules/tailwindcss": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", + "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", + "license": "MIT" + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", + "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-x64": "4.1.13", + "@tailwindcss/oxide-freebsd-x64": "4.1.13", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-x64-musl": "4.1.13", + "@tailwindcss/oxide-wasm32-wasi": "4.1.13", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", + "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", + "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", + "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", + "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", + "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", + "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", + "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", + "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", + "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", + "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@emnapi/wasi-threads": "^1.0.4", + "@napi-rs/wasm-runtime": "^0.2.12", + "@tybys/wasm-util": "^0.10.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", + "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", + "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.13.tgz", + "integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.13", + "@tailwindcss/oxide": "4.1.13", + "tailwindcss": "4.1.13" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@tailwindcss/vite/node_modules/tailwindcss": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", + "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", + "license": "MIT" + }, + "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/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "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/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "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.1.13", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz", + "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", + "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-reconciler": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.32.1.tgz", + "integrity": "sha512-RsqPttsBQ+6af0nATFXJJpemYQH7kL9+xLNm1z+0MjQFDKBZDM2R6SBrjdvRmHu9i9fM6povACj57Ft+pKRNOA==", + "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.180.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.180.0.tgz", + "integrity": "sha512-ykFtgCqNnY0IPvDro7h+9ZeLY+qjgUWv+qEvUt84grhenO60Hqd4hScHE7VTB9nOQ/3QM8lkbNE+4vKjEpUxKg==", + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": "*", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.22.0" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.23.tgz", + "integrity": "sha512-GPe4AsfOSpqWd3xA/0gwoKod13ChcfV67trvxaW2krUbgb9gxQjnCx8zGshzMl8LSHZlNH5gQ8LNScsDuc7nGQ==", + "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/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@webgpu/types": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.65.tgz", + "integrity": "sha512-cYrHab4d6wuVvDW5tdsfI6/o6vcLMDe6w2Citd1oS51Xxu2ycLCnVo4fqwujfKWijrZMInTJIKcXxteoy21nVA==", + "license": "BSD-3-Clause" + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/attr-accept": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "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/baseline-browser-mapping": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", + "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "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/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/camera-controls": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.0.tgz", + "integrity": "sha512-w5oULNpijgTRH0ARFJJ0R5ct1nUM3R3WP7/b8A6j9uTGpRfnsypc/RBMPQV8JQDPayUe37p/TZZY1PcUr4czOQ==", + "license": "MIT", + "engines": { + "node": ">=20.11.0", + "npm": ">=10.8.2" + }, + "peerDependencies": { + "three": ">=0.126.1" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "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/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "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/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.222", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", + "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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/file-selector": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.5.0.tgz", + "integrity": "sha512-s8KNnmIDTBoD0p9uJ9uD0XY38SCeBOtj0UMXyQSLg1Ypfrfj8+dAvwsLjYQkQ2GjhVtp2HrnF5cJzMhBjfD8HA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/franc-min": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/franc-min/-/franc-min-6.2.0.tgz", + "integrity": "sha512-1uDIEUSlUZgvJa2AKYR/dmJC66v/PvGQ9mWfI9nOr/kPpMFyvswK0gPXOwpYJYiYD008PpHLkGfG58SPjQJFxw==", + "license": "MIT", + "dependencies": { + "trigram-utils": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hls.js": { + "version": "1.6.12", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.12.tgz", + "integrity": "sha512-Pz+7IzvkbAht/zXvwLzA/stUHNqztqKvlLbfpq6ZYU68+gZ+CZMlsbQBPUviRap+3IQ41E39ke7Ia+yvhsehEQ==", + "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-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/its-fine/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/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/leva": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/leva/-/leva-0.10.0.tgz", + "integrity": "sha512-RiNJWmeqQdKIeHuVXgshmxIHu144a2AMYtLxKf8Nm1j93pisDPexuQDHKNdQlbo37wdyDQibLjY9JKGIiD7gaw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-portal": "1.0.2", + "@radix-ui/react-tooltip": "1.0.5", + "@stitches/react": "^1.2.8", + "@use-gesture/react": "^10.2.5", + "colord": "^2.9.2", + "dequal": "^2.0.2", + "merge-value": "^1.0.0", + "react-colorful": "^5.5.1", + "react-dropzone": "^12.0.0", + "v8n": "^1.3.3", + "zustand": "^3.6.9" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-portal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz", + "integrity": "sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.5.tgz", + "integrity": "sha512-cDKVcfzyO6PpckZekODJZDe5ZxZ2fCZlzKzTmPhe4mX9qTHRfLcKgqb0OKf22xLwDequ2tVleim+ZYx3rabD5w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-dismissable-layer": "1.0.3", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-popper": "1.1.1", + "@radix-ui/react-portal": "1.0.2", + "@radix-ui/react-presence": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-slot": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.0", + "@radix-ui/react-visually-hidden": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz", + "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.3.tgz", + "integrity": "sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-escape-keydown": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.2.tgz", + "integrity": "sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz", + "integrity": "sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-id/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.1.tgz", + "integrity": "sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "0.7.2", + "@radix-ui/react-arrow": "1.0.2", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-primitive": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0", + "@radix-ui/react-use-rect": "1.0.0", + "@radix-ui/react-use-size": "1.0.0", + "@radix-ui/rect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-arrow": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.2.tgz", + "integrity": "sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-use-rect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.0.tgz", + "integrity": "sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-use-size": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.0.tgz", + "integrity": "sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.0.tgz", + "integrity": "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz", + "integrity": "sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz", + "integrity": "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-controllable-state/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.2.tgz", + "integrity": "sha512-qirnJxtYn73HEk1rXL12/mXnu2rwsNHDID10th2JGtdK25T9wX+mxRmGt7iPSahw512GbZOc0syZX1nLQGoEOg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/leva/node_modules/zustand": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", + "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", + "license": "MIT", + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "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.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/merge-value/-/merge-value-1.0.0.tgz", + "integrity": "sha512-fJMmvat4NeKz63Uv9iHWcPDjCWcCkoiRoajRTEO8hlhUC6rwaHg0QCF9hBOTjZmm4JuglPckPSTtcuJL5kp0TQ==", + "license": "MIT", + "dependencies": { + "get-value": "^2.0.6", + "is-extendable": "^1.0.0", + "mixin-deep": "^1.2.0", + "set-value": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "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": "0.22.0", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz", + "integrity": "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==", + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "license": "MIT", + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/n-gram": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/n-gram/-/n-gram-2.0.2.tgz", + "integrity": "sha512-S24aGsn+HLBxUGVAUFOwGpKs7LBcG4RudKU//eWzt/mQ97/NMKQxDWHyHx63UNWk/OOdihgmzoETn1tf5nQDzQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/n8ao": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/n8ao/-/n8ao-1.10.1.tgz", + "integrity": "sha512-hhI1pC+BfOZBV1KMwynBrVlIm8wqLxj/abAWhF2nZ0qQKyzTSQa1QtLVS2veRiuoBQXojxobcnp0oe+PUoxf/w==", + "license": "ISC", + "peerDependencies": { + "postprocessing": ">=6.30.0", + "three": ">=0.137" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "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/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "16.1.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.1.tgz", + "integrity": "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postprocessing": { + "version": "6.37.8", + "resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.37.8.tgz", + "integrity": "sha512-qTFUKS51z/fuw2U+irz4/TiKJ/0oI70cNtvQG1WxlPKvBdJUfS1CcFswJd5ATY3slotWfvkDDZAsj1X0fU8BOQ==", + "license": "Zlib", + "peerDependencies": { + "three": ">= 0.157.0 < 0.181.0" + } + }, + "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/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-colorful": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", + "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.1" + } + }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/react-dropzone": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.1.0.tgz", + "integrity": "sha512-iBYHA1rbopIvtzokEX4QubO6qk5IF/x3BtKGu74rF2JkQDXnwC4uO/lHKpaw4PJIV6iIAYOlwLv2FpiGyqHNog==", + "license": "MIT", + "dependencies": { + "attr-accept": "^2.2.2", + "file-selector": "^0.5.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8" + } + }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-reconciler": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz", + "integrity": "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.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/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "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/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.52.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.0.tgz", + "integrity": "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.0", + "@rollup/rollup-android-arm64": "4.52.0", + "@rollup/rollup-darwin-arm64": "4.52.0", + "@rollup/rollup-darwin-x64": "4.52.0", + "@rollup/rollup-freebsd-arm64": "4.52.0", + "@rollup/rollup-freebsd-x64": "4.52.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", + "@rollup/rollup-linux-arm-musleabihf": "4.52.0", + "@rollup/rollup-linux-arm64-gnu": "4.52.0", + "@rollup/rollup-linux-arm64-musl": "4.52.0", + "@rollup/rollup-linux-loong64-gnu": "4.52.0", + "@rollup/rollup-linux-ppc64-gnu": "4.52.0", + "@rollup/rollup-linux-riscv64-gnu": "4.52.0", + "@rollup/rollup-linux-riscv64-musl": "4.52.0", + "@rollup/rollup-linux-s390x-gnu": "4.52.0", + "@rollup/rollup-linux-x64-gnu": "4.52.0", + "@rollup/rollup-linux-x64-musl": "4.52.0", + "@rollup/rollup-openharmony-arm64": "4.52.0", + "@rollup/rollup-win32-arm64-msvc": "4.52.0", + "@rollup/rollup-win32-ia32-msvc": "4.52.0", + "@rollup/rollup-win32-x64-gnu": "4.52.0", + "@rollup/rollup-win32-x64-msvc": "4.52.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "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/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/suspend-react": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", + "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=17.0" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/tailwindcss/node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.4.tgz", + "integrity": "sha512-O1z7ajPkjTgEgmTGz0v9X4eqeEXTDREPTO77pVC1Nbs86feBU1Zhdg+edzavPmYW1olxkwsqA2v4uOw6E8LeDg==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/three": { + "version": "0.174.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.174.0.tgz", + "integrity": "sha512-p+WG3W6Ov74alh3geCMkGK9NWuT62ee21cV3jEnun201zodVF4tCE5aZa2U122/mkLRmhJJUQmLLW1BH00uQJQ==", + "license": "MIT" + }, + "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.0", + "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.0.tgz", + "integrity": "sha512-kv0Byb++AXztEGsULgMAs8U2jgUdz6HPpAB/wDJnLiLlaWQX2APHhiTJIN7rqW+Of0eRgcp7jn05U1BsCP3xBA==", + "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/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trigram-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/trigram-utils/-/trigram-utils-2.0.1.tgz", + "integrity": "sha512-nfWIXHEaB+HdyslAfMxSqWKDdmqY9I32jS7GnqpdWQnLH89r6A5sdk3fDVYqGAZ0CrT8ovAFSAo6HRiWcWNIGQ==", + "license": "MIT", + "dependencies": { + "collapse-white-space": "^2.0.0", + "n-gram": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "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/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8n": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/v8n/-/v8n-1.5.1.tgz", + "integrity": "sha512-LdabyT4OffkyXFCe9UT+uMkxNBs5rcTVuZClvxQr08D5TUgo1OFKkoT65qYRCsiKBl/usHjpXvP4hHMzzDRj3A==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", + "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/wawa-lipsync": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/wawa-lipsync/-/wawa-lipsync-0.0.1.tgz", + "integrity": "sha512-4ynJX3LcztOi1vvnfzxxXiqewYxd1teY2EpQ+YfOT8T0WvUXlRouJ15gaBXabFe8eHq2EB8C5X5aGYxrNM1Daw==", + "license": "MIT" + }, + "node_modules/wawa-vfx": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/wawa-vfx/-/wawa-vfx-1.2.9.tgz", + "integrity": "sha512-ndRzqgL8CGMbThs2yE/aQQGcVQcdK3wsSXzIkUWdVVyi2cAlpLZzFC6aMJpsCTaqgrShO8GXi8BAFFW0mrE9SQ==", + "license": "MIT", + "dependencies": { + "wawa-vfx-vanilla": "^1.2.9" + }, + "peerDependencies": { + "@react-three/fiber": "^9.0.0", + "leva": "^0.10.0", + "react": "^19", + "react-dom": "^19", + "three": ">=0.159", + "zustand": "^5.0.0" + } + }, + "node_modules/wawa-vfx-vanilla": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/wawa-vfx-vanilla/-/wawa-vfx-vanilla-1.2.9.tgz", + "integrity": "sha512-ozRXRGPLPuQ2Qx5ZFXzf8547cwRZk9bq60hBoC6IR07QqBBTgDd+AhR9AZytbUhjYAIHuOW44xa3p3mP/RADKA==", + "license": "MIT", + "peerDependencies": { + "three": ">=0.159" + } + }, + "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/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "devOptional": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/zustand": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", + "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==", + "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/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000000000000000000000000000000000000..4d2a6c5213ef9dc11e021cf949483ac7427fbf97 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,42 @@ +{ + "name": "frontend", + "private": true, + "version": "1.0.0", + "type": "commonjs", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@react-three/drei": "^10.6.1", + "@react-three/fiber": "^9.3.0", + "@react-three/postprocessing": "^3.0.4", + "@tailwindcss/vite": "^4.0.9", + "axios": "^1.11.0", + "franc-min": "^6.2.0", + "leva": "^0.10.0", + "postcss": "^8.5.6", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "react-icons": "^5.5.0", + "three": "^0.174.0", + "uuid": "^11.1.0", + "wawa-lipsync": "^0.0.1", + "wawa-vfx": "^1.0.16", + "zustand": "^5.0.4" + }, + "devDependencies": { + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "autoprefixer": "^10.4.21", + "framer-motion": "^11.18.2", + "globals": "^15.15.0", + "postcss": "^8.4.38", + "postcss-import": "^16.0.1", + "postcss-nested": "^6.0.1", + "tailwindcss": "^3.4.1", + "vite": "^6.2.0" + } +} diff --git a/frontend/public/images/holokia.jpeg b/frontend/public/images/holokia.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..801955157da115beb3b1cf587337f40c69c3ce77 --- /dev/null +++ b/frontend/public/images/holokia.jpeg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cb9fe6a62fb131b7a8b13fa8ec1bbe020979c76aac203e26ce193af931593f1 +size 40755 diff --git a/frontend/public/images/wawasensei-white.png b/frontend/public/images/wawasensei-white.png new file mode 100644 index 0000000000000000000000000000000000000000..289a06a221bc913ce5e4b0240262a89cef3dc6a2 --- /dev/null +++ b/frontend/public/images/wawasensei-white.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ff1a3e6ada5327651f1a5d6365d8cf0fef61b0b316d970bf135a22719d03894 +size 17900 diff --git a/frontend/public/images/wawasensei.png b/frontend/public/images/wawasensei.png new file mode 100644 index 0000000000000000000000000000000000000000..81b1a66a5de1ce7ca7d54307626c4bf3a0359672 --- /dev/null +++ b/frontend/public/images/wawasensei.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53f6fd7dd3e69d77880f3b8b809cfa63525dbe41e79c52fa595d3a805d430b60 +size 12237 diff --git a/frontend/public/pcm-worklet.js b/frontend/public/pcm-worklet.js new file mode 100644 index 0000000000000000000000000000000000000000..bb8e4a687e4eafffc5ccd9128626866a66bd42ff --- /dev/null +++ b/frontend/public/pcm-worklet.js @@ -0,0 +1,108 @@ +class PCMProcessor extends AudioWorkletProcessor { + constructor(options) { + super(); + const o = options?.processorOptions || {}; + this.targetSampleRate = o.sampleRate || 16000; + this.inputSampleRate = o.inputSampleRate || sampleRate; + this.chunkSize = o.chunkSize || 1024; + this.silenceThreshold = o.silenceThreshold || 0.01; + this.silenceWindow = o.silenceWindow || 3; + this.buffer = []; + this.silenceCount = 0; + + this.port.onmessage = (e) => { + const data = e.data || {}; + if (data.type === "flush") { + this._flush(); + } else if (data.type === "config") { + if (typeof data.targetSampleRate === "number") this.targetSampleRate = data.targetSampleRate; + if (typeof data.inputSampleRate === "number") this.inputSampleRate = data.inputSampleRate; + if (typeof data.chunkSize === "number" && data.chunkSize > 0) this.chunkSize = data.chunkSize; + if (typeof data.silenceThreshold === "number") this.silenceThreshold = data.silenceThreshold; + if (typeof data.silenceWindow === "number") this.silenceWindow = data.silenceWindow; + } + }; + } + + _isSilence(chunk) { + if (!chunk || chunk.length === 0) return true; + let sum = 0; + for (let i = 0; i < chunk.length; i++) { + sum += chunk[i] * chunk[i]; + } + const rms = Math.sqrt(sum / chunk.length); + return rms < this.silenceThreshold; + } + + _resampleFloat32(buffer, inSR, outSR) { + if (inSR === outSR) return buffer; + const ratio = inSR / outSR; + const newLength = Math.max(1, Math.round(buffer.length / ratio)); + const out = new Float32Array(newLength); + for (let i = 0; i < newLength; i++) { + const idx = i * ratio; + const idxFloor = Math.floor(idx); + const idxCeil = Math.min(buffer.length - 1, idxFloor + 1); + const t = idx - idxFloor; + out[i] = (1 - t) * buffer[idxFloor] + t * buffer[idxCeil]; + } + return out; + } + + _floatTo16BitPCM(float32Array) { + const out = new Int16Array(float32Array.length); + for (let i = 0; i < float32Array.length; i++) { + const s = Math.max(-1, Math.min(1, float32Array[i])); + out[i] = s < 0 ? s * 0x8000 : s * 0x7FFF; + } + return out; + } + + _emitChunkIfReady() { + if (this.buffer.length >= this.chunkSize) { + const take = this.buffer.splice(0, this.chunkSize); + if (take.length === 0) return; + const f32 = new Float32Array(take); + const pcm16 = this._floatTo16BitPCM(f32); + this.port.postMessage({ buffer: pcm16.buffer }, [pcm16.buffer]); + } + } + + _flush() { + if (this.buffer.length > 0) { + const f32 = new Float32Array(this.buffer); + this.buffer.length = 0; + const pcm16 = this._floatTo16BitPCM(f32); + this.port.postMessage({ buffer: pcm16.buffer }, [pcm16.buffer]); + } + } + + process(inputs) { + const input = inputs[0]; + if (!input || input.length === 0) return true; + + const channel = input[0]; + if (!channel) return true; + + if (this._isSilence(channel)) { + this.silenceCount++; + if (this.silenceCount >= this.silenceWindow) { + return true; + } + } else { + this.silenceCount = 0; + } + + const resampled = this._resampleFloat32(channel, this.inputSampleRate, this.targetSampleRate); + + for (let i = 0; i < resampled.length; i++) { + this.buffer.push(resampled[i]); + if (this.buffer.length >= this.chunkSize) { + this._emitChunkIfReady(); + } + } + return true; + } +} + +registerProcessor("pcm-processor", PCMProcessor); \ No newline at end of file diff --git a/frontend/public/textures/Rotated/flame_05_rotated.png b/frontend/public/textures/Rotated/flame_05_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..abb55c896f3ee33f5e9d9fcf1675d0af810f0150 --- /dev/null +++ b/frontend/public/textures/Rotated/flame_05_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08afb8deef9edbfb47571805a2f5b1316c5c99d5056f23b13d060eece3a752de +size 12155 diff --git a/frontend/public/textures/Rotated/flame_06_rotated.png b/frontend/public/textures/Rotated/flame_06_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..b5a38f19b82f0bd171cd085f1a55d1514217410e --- /dev/null +++ b/frontend/public/textures/Rotated/flame_06_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c72289cfe6e25d2763eef9db9b55a38f04d027ba0ca7fe6d4830b2ebd9b63dce +size 14979 diff --git a/frontend/public/textures/Rotated/muzzle_01_rotated.png b/frontend/public/textures/Rotated/muzzle_01_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..799d141a4c2166d90807c006fbed1405f7408895 --- /dev/null +++ b/frontend/public/textures/Rotated/muzzle_01_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b0bae33c25ef54702f40272d121412131e8f089ae03c958533f4887288d8f59 +size 75703 diff --git a/frontend/public/textures/Rotated/muzzle_02_rotated.png b/frontend/public/textures/Rotated/muzzle_02_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..27af2bb57457730a736cc9273587d44c03f478be --- /dev/null +++ b/frontend/public/textures/Rotated/muzzle_02_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bca0de7b38b9bc7ea7ad38940720ebfe138363155848cfb7bf15e56681defe9 +size 49401 diff --git a/frontend/public/textures/Rotated/muzzle_03_rotated.png b/frontend/public/textures/Rotated/muzzle_03_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..2defdee50084af2b2eb7ef6cbf69eb9fc7b32791 --- /dev/null +++ b/frontend/public/textures/Rotated/muzzle_03_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a625935b4b2a89beb0f2aefb3db3a50c062f2bd3a99cf2a8aaf3f53cef437bb +size 48054 diff --git a/frontend/public/textures/Rotated/muzzle_04_rotated.png b/frontend/public/textures/Rotated/muzzle_04_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..679c7b97eea10899536ceb53e14de5aaa432e2b9 --- /dev/null +++ b/frontend/public/textures/Rotated/muzzle_04_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:76a51de75e489e5007d4e0140824120c7eda19877127d78e6531cfb0166df279 +size 51171 diff --git a/frontend/public/textures/Rotated/muzzle_05_rotated.png b/frontend/public/textures/Rotated/muzzle_05_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..9c181ce13e526d11157e38d8131de4e0b5a6b787 --- /dev/null +++ b/frontend/public/textures/Rotated/muzzle_05_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c86c85323f6a2ad0e0d1349f17bc88b3951b8ac653c4b0bc133fb0baa28eaae9 +size 44504 diff --git a/frontend/public/textures/Rotated/spark_05_rotated.png b/frontend/public/textures/Rotated/spark_05_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..f322378de9163e3b5abcdf2e7835cc96d2bda012 --- /dev/null +++ b/frontend/public/textures/Rotated/spark_05_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bceb2b1a8a46d9eaffb4c77d5c4114ff7af8fcd36e586feb1f91bfe2af0e671 +size 56533 diff --git a/frontend/public/textures/Rotated/spark_06_rotated.png b/frontend/public/textures/Rotated/spark_06_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..3a52391298ed298d98e7fa8f4f9699ca8cd4d40d --- /dev/null +++ b/frontend/public/textures/Rotated/spark_06_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c47a5d892ac1816c8eb18ebddb1ecf7769a35bbdd5a6671677b565006623936 +size 41431 diff --git a/frontend/public/textures/Rotated/trace_01_rotated.png b/frontend/public/textures/Rotated/trace_01_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..52a5cf852381acfbe95299d4ddfd8b30906493ec --- /dev/null +++ b/frontend/public/textures/Rotated/trace_01_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6832399b81f21dfb9379159e25ea2efab85258f24a03a2eb7a3f9285829f98d4 +size 28417 diff --git a/frontend/public/textures/Rotated/trace_02_rotated.png b/frontend/public/textures/Rotated/trace_02_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..4987ba4482229f2ba17da40b1696448565100e8b --- /dev/null +++ b/frontend/public/textures/Rotated/trace_02_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81c30ba255eacd9ff29b5c0b8028b2714a375d6dc7cd11133f515bc37123f0e3 +size 31249 diff --git a/frontend/public/textures/Rotated/trace_03_rotated.png b/frontend/public/textures/Rotated/trace_03_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..542ececad9817f300e81090fc590fb501ce46d1b --- /dev/null +++ b/frontend/public/textures/Rotated/trace_03_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0037f0c659d2a546b0de452b9ea2d910626c3994154395fb84bf28b9c249c2e1 +size 34894 diff --git a/frontend/public/textures/Rotated/trace_04_rotated.png b/frontend/public/textures/Rotated/trace_04_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..8d65e9d8c185a7b8a8839ecae064876608c8f142 --- /dev/null +++ b/frontend/public/textures/Rotated/trace_04_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:857d9fe44e1af7123c1d1006caac5a3707588412997bc8f16591f01a2f73f6e9 +size 36918 diff --git a/frontend/public/textures/Rotated/trace_05_rotated.png b/frontend/public/textures/Rotated/trace_05_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..d240e77314897f40ffffecb7770e39992a284a77 --- /dev/null +++ b/frontend/public/textures/Rotated/trace_05_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:513aca6608ab2c3a677aacc99ad1055a200cd9a703b247c69fabce9695cce2e0 +size 36725 diff --git a/frontend/public/textures/Rotated/trace_06_rotated.png b/frontend/public/textures/Rotated/trace_06_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..dcbda7eb8d41a642af7a85244ded4dd9c793a26b --- /dev/null +++ b/frontend/public/textures/Rotated/trace_06_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:098bd782fbbac098e30357acdef764184e601ccdf05d109f3dcfb698e5a7ea60 +size 30533 diff --git a/frontend/public/textures/Rotated/trace_07_rotated.png b/frontend/public/textures/Rotated/trace_07_rotated.png new file mode 100644 index 0000000000000000000000000000000000000000..551a46a578142ff71b0d592f94efac2972503c0e --- /dev/null +++ b/frontend/public/textures/Rotated/trace_07_rotated.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fdfcc20aa9a194f8eee4081bf0f8de21fef48abcba4f073c183a406cdeb45f86 +size 27368 diff --git a/frontend/public/textures/circle_01.png b/frontend/public/textures/circle_01.png new file mode 100644 index 0000000000000000000000000000000000000000..8a2ef4a453b5fbfa89d97a13d070ff62d0a24c3e --- /dev/null +++ b/frontend/public/textures/circle_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b2d03683bf0fe4a946567adc3bd86b8ba045da84cee58dc2cf8aef63bbfaa06 +size 69755 diff --git a/frontend/public/textures/circle_02.png b/frontend/public/textures/circle_02.png new file mode 100644 index 0000000000000000000000000000000000000000..36d2364a4248e9acaec73a285411677d7ed9a92c --- /dev/null +++ b/frontend/public/textures/circle_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a2112a6a5610c7cdcbfe8af0a5162b42dfb3431c63f494743945337b7981e51 +size 43380 diff --git a/frontend/public/textures/circle_03.png b/frontend/public/textures/circle_03.png new file mode 100644 index 0000000000000000000000000000000000000000..40c6327962cab66f3cc2795aa2de8d8b46645cc8 --- /dev/null +++ b/frontend/public/textures/circle_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed6c6f082e666c16bcaca89f15aefb55df9bf69450090518bcae1d3d61f82936 +size 72952 diff --git a/frontend/public/textures/circle_04.png b/frontend/public/textures/circle_04.png new file mode 100644 index 0000000000000000000000000000000000000000..3a8872ffd393b2d9aee9bc5e517573fe58651f7e --- /dev/null +++ b/frontend/public/textures/circle_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:742da1a1b96b93ae446700f6085385d1f62844352da870c89427307b7b7cf03b +size 59557 diff --git a/frontend/public/textures/circle_05.png b/frontend/public/textures/circle_05.png new file mode 100644 index 0000000000000000000000000000000000000000..8c4cdcd1426ad69bf72a259938042852a7f5c508 --- /dev/null +++ b/frontend/public/textures/circle_05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:925b8ac284436f74f9cadf0ecd058da1c08fba65c098e4e34fd220603022f02e +size 65336 diff --git a/frontend/public/textures/dirt_01.png b/frontend/public/textures/dirt_01.png new file mode 100644 index 0000000000000000000000000000000000000000..a20051719566b43d8b0c0c229c81e0ac7ddc741c --- /dev/null +++ b/frontend/public/textures/dirt_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6827a0a32a293ec9570fb98d63963b8dd6e6aaba9e1096afad932e93a378bea9 +size 47313 diff --git a/frontend/public/textures/dirt_02.png b/frontend/public/textures/dirt_02.png new file mode 100644 index 0000000000000000000000000000000000000000..402abbc5889422cf81404ce7077e2640678e31ea --- /dev/null +++ b/frontend/public/textures/dirt_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcecc70b561f80568b3391c3dc1180126d86ce4fbd4d1b01518e6045203e10a3 +size 54060 diff --git a/frontend/public/textures/dirt_03.png b/frontend/public/textures/dirt_03.png new file mode 100644 index 0000000000000000000000000000000000000000..1967aba4378aa3668e02fdcdbab939ba073364f5 --- /dev/null +++ b/frontend/public/textures/dirt_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:427d6dcad5a8d16b8143d412a5a48ce859716150a074ea5bb36eca577c0ae541 +size 114937 diff --git a/frontend/public/textures/fire_01.png b/frontend/public/textures/fire_01.png new file mode 100644 index 0000000000000000000000000000000000000000..b183ce2d23fd23ef6a0404f166c5a0384e372e03 --- /dev/null +++ b/frontend/public/textures/fire_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9727a9743ac6ab8dd4dd79f5fc5b0266290efae45f4d4452279eb5e4bcf3a978 +size 99891 diff --git a/frontend/public/textures/fire_02.png b/frontend/public/textures/fire_02.png new file mode 100644 index 0000000000000000000000000000000000000000..a6465c6cfb71bcb59abe7e54b5d833883dabe372 --- /dev/null +++ b/frontend/public/textures/fire_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2677330b0668033b0744276da74de605164aa5d58203bf251a8ec84e62c2108 +size 88453 diff --git a/frontend/public/textures/flame_01.png b/frontend/public/textures/flame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..43bb7cbdd03b353ab35d5b2a074e62a5df73f40d --- /dev/null +++ b/frontend/public/textures/flame_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92a70bcb90b1e21c57b2dae42843135703b2e988abee97433f84c3119723d144 +size 54099 diff --git a/frontend/public/textures/flame_02.png b/frontend/public/textures/flame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..dec1a55d0e3f4d396181b461b503770aca5ac58d --- /dev/null +++ b/frontend/public/textures/flame_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7afff66979a5a30319d4fa491b13a9027223ebbdadfa38fae4ae76e31d05d9c5 +size 68498 diff --git a/frontend/public/textures/flame_03.png b/frontend/public/textures/flame_03.png new file mode 100644 index 0000000000000000000000000000000000000000..49df5bfb3b290132aea28b81ad412c288e2ee128 --- /dev/null +++ b/frontend/public/textures/flame_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:366678a08a7d22ebb87baa7b42005aaaeb0fd51ab751f2d4e7c9edfa61a33e38 +size 49857 diff --git a/frontend/public/textures/flame_04.png b/frontend/public/textures/flame_04.png new file mode 100644 index 0000000000000000000000000000000000000000..e30687904b07bcab14b9d01dc78755b24bdf91ee --- /dev/null +++ b/frontend/public/textures/flame_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:71deb274feea6ae5e6bb82c802a6e4d22f168397aa36b7a9e89de863948f3cf5 +size 70731 diff --git a/frontend/public/textures/flame_05.png b/frontend/public/textures/flame_05.png new file mode 100644 index 0000000000000000000000000000000000000000..bc305204c5d332a15b6cefd751d1087eec5e56a7 --- /dev/null +++ b/frontend/public/textures/flame_05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c5cc587260010e4bb58b03cc2561ca0e96409e7db25ea4183cd3fb1d0237dc8 +size 13395 diff --git a/frontend/public/textures/flame_06.png b/frontend/public/textures/flame_06.png new file mode 100644 index 0000000000000000000000000000000000000000..19e0afa881971663d6b649f7c4c679cef3b974b3 --- /dev/null +++ b/frontend/public/textures/flame_06.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc95ac4d0e61b22d079aeb5feabf2914d380858ad95852e5b1638ad820526948 +size 15971 diff --git a/frontend/public/textures/flare_01.png b/frontend/public/textures/flare_01.png new file mode 100644 index 0000000000000000000000000000000000000000..8d005b873782d79ef86c56e84fb4100d05ec7046 --- /dev/null +++ b/frontend/public/textures/flare_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:254f5bd4f0548612bf29305d27899efdee8cb8d8306292761ebbddebd3e57935 +size 42886 diff --git a/frontend/public/textures/light_01.png b/frontend/public/textures/light_01.png new file mode 100644 index 0000000000000000000000000000000000000000..a5029404f61b4507c4f58dae1bef69c0feb840b5 --- /dev/null +++ b/frontend/public/textures/light_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:caedcc5df5bcac4470244d2520d6a64b45b20c1ccf4d918da516c55e3fa365a6 +size 93470 diff --git a/frontend/public/textures/light_02.png b/frontend/public/textures/light_02.png new file mode 100644 index 0000000000000000000000000000000000000000..fd4f7fd0ae5ff2bbb43e01b5747bd3756dc8874d --- /dev/null +++ b/frontend/public/textures/light_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1bdb57478703fec539f7b8eeceb494c4a23664652b296389f310104be80cfe0 +size 93415 diff --git a/frontend/public/textures/light_03.png b/frontend/public/textures/light_03.png new file mode 100644 index 0000000000000000000000000000000000000000..d32d7bb36306ac6d67984af820373152a67dd583 --- /dev/null +++ b/frontend/public/textures/light_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b68627734d08aab3eaf3f94718521553628c75aace1de6c74de28501c1a4c147 +size 100114 diff --git a/frontend/public/textures/magic_01.png b/frontend/public/textures/magic_01.png new file mode 100644 index 0000000000000000000000000000000000000000..c0d89559cf3aff2d6ada12e779e360aed7dd696d --- /dev/null +++ b/frontend/public/textures/magic_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:398479ea280c8161d0b6d509ca97eefb27e989c7832b404abcb65faae3e922ff +size 81687 diff --git a/frontend/public/textures/magic_02.png b/frontend/public/textures/magic_02.png new file mode 100644 index 0000000000000000000000000000000000000000..40559e83be50d08620b8038590f2fc9f822382d4 --- /dev/null +++ b/frontend/public/textures/magic_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6443e90834abcae4c3848d7ebca99ff0d9fcad19a07f49f77ad64710a3e39b36 +size 86500 diff --git a/frontend/public/textures/magic_03.png b/frontend/public/textures/magic_03.png new file mode 100644 index 0000000000000000000000000000000000000000..4059877339242fd54d6de23736f32e6a3aad6d76 --- /dev/null +++ b/frontend/public/textures/magic_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2ef5fe86cd2fd08e5b9768389ab6580b346c391db3c7c09fc9b0a63e00ec4ce +size 58093 diff --git a/frontend/public/textures/magic_04.png b/frontend/public/textures/magic_04.png new file mode 100644 index 0000000000000000000000000000000000000000..553480a8a23af6cf15aef2c20134ad7ab73c1ce7 --- /dev/null +++ b/frontend/public/textures/magic_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8255a4a3bd9209a0d3a45f0636524ff0d2953adc7d6819041703107379ab63ba +size 50082 diff --git a/frontend/public/textures/magic_05.png b/frontend/public/textures/magic_05.png new file mode 100644 index 0000000000000000000000000000000000000000..1f41409e32a8a212df709f6b3551ea3d53d1b67e --- /dev/null +++ b/frontend/public/textures/magic_05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92fb8c7a1d9bc14e052526c0505502c3e5b37d27502fa3d36d2c80b4565b62ef +size 74794 diff --git a/frontend/public/textures/muzzle_01.png b/frontend/public/textures/muzzle_01.png new file mode 100644 index 0000000000000000000000000000000000000000..6ca7a00e6ff803c70790ed084c600b2064ece913 --- /dev/null +++ b/frontend/public/textures/muzzle_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08f650f6a615b65024b48d8da32178158d74307d7c235f214f0c075b7aa688b9 +size 81811 diff --git a/frontend/public/textures/muzzle_02.png b/frontend/public/textures/muzzle_02.png new file mode 100644 index 0000000000000000000000000000000000000000..5ad3f3765612871d1a8a3bb20b439d147d3db524 --- /dev/null +++ b/frontend/public/textures/muzzle_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96164063353cc0386b3d3ad0d64b9be6c369e1d5d8510f84fcf33604b4729084 +size 58365 diff --git a/frontend/public/textures/muzzle_03.png b/frontend/public/textures/muzzle_03.png new file mode 100644 index 0000000000000000000000000000000000000000..80e3a8679a4dd53e0315708d86734dc7027668d1 --- /dev/null +++ b/frontend/public/textures/muzzle_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:579983452d263ebeca78080714b6dd55a0f95fca7452f309e91a9d466de6c058 +size 57240 diff --git a/frontend/public/textures/muzzle_04.png b/frontend/public/textures/muzzle_04.png new file mode 100644 index 0000000000000000000000000000000000000000..844eadd84d72fbaf56c0dd5f6f1c297bed6df950 --- /dev/null +++ b/frontend/public/textures/muzzle_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2acbc729b7bf38330b7e829cb0b13dcf922fec4b163f7eb2965b7b2932f89aeb +size 62593 diff --git a/frontend/public/textures/muzzle_05.png b/frontend/public/textures/muzzle_05.png new file mode 100644 index 0000000000000000000000000000000000000000..d8b1f8b54c75a6cf87a05799359aabb7547e043e --- /dev/null +++ b/frontend/public/textures/muzzle_05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc2d012c3bee75e668cc7800f965692b47af6edd6d3584081089a70abbe30a47 +size 54088 diff --git a/frontend/public/textures/scorch_01.png b/frontend/public/textures/scorch_01.png new file mode 100644 index 0000000000000000000000000000000000000000..fb1e15de576deb152241a6b5220f38d1512b7513 --- /dev/null +++ b/frontend/public/textures/scorch_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea72697696638786ccd4795a346d177b1b6a904331b5536a7e85a7ed927b93a5 +size 60598 diff --git a/frontend/public/textures/scorch_02.png b/frontend/public/textures/scorch_02.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a36380616dd6c1ed9ad6f324750c188cb3fb27 --- /dev/null +++ b/frontend/public/textures/scorch_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b36daaabbe0480b74e1e8d6b0b2e9a2d01974109b42b8a39c430af7739a953a1 +size 74485 diff --git a/frontend/public/textures/scorch_03.png b/frontend/public/textures/scorch_03.png new file mode 100644 index 0000000000000000000000000000000000000000..a13d88eb116698bac1025ab0836f55ed18e40a19 --- /dev/null +++ b/frontend/public/textures/scorch_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:531956355c8adcf6fbe0111788e68630c4b16209f9d7a6136978361a308202ec +size 91921 diff --git a/frontend/public/textures/scratch_01.png b/frontend/public/textures/scratch_01.png new file mode 100644 index 0000000000000000000000000000000000000000..948ac16f6c541e27f42f9c376d062ac02059e5e3 --- /dev/null +++ b/frontend/public/textures/scratch_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7dc0b16a5240738273684bfdf423eefd8a135356af1fee759217cdd560a4a935 +size 65462 diff --git a/frontend/public/textures/slash_01.png b/frontend/public/textures/slash_01.png new file mode 100644 index 0000000000000000000000000000000000000000..a72a30c0f23e1b13d1726b4a2f44bb05e1afcb21 --- /dev/null +++ b/frontend/public/textures/slash_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb4787978122bb863866a1681af22a7dec39a4566f08c400f233740eb1d3730c +size 25465 diff --git a/frontend/public/textures/slash_02.png b/frontend/public/textures/slash_02.png new file mode 100644 index 0000000000000000000000000000000000000000..d2017763e3a292782f096e39d69c35bb0de8a4bd --- /dev/null +++ b/frontend/public/textures/slash_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a79a9e297790e6126b78d41015a3045cda3f1863b421c88321d2442ed6f9032e +size 38224 diff --git a/frontend/public/textures/slash_03.png b/frontend/public/textures/slash_03.png new file mode 100644 index 0000000000000000000000000000000000000000..9dfae8f1f8e02b3446cd82e9ee4f1cde9a81a520 --- /dev/null +++ b/frontend/public/textures/slash_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c77e40ef75084e880f2ea69acf70af4419518f489acda00d29b668a64805608e +size 16634 diff --git a/frontend/public/textures/slash_04.png b/frontend/public/textures/slash_04.png new file mode 100644 index 0000000000000000000000000000000000000000..45dcab23b85bb18ffc4ae6762ece874e376cdcbd --- /dev/null +++ b/frontend/public/textures/slash_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1beed6d30b8b3fc0874cca12f57de073360f0b11fc6f4cd99a24b7e033784c47 +size 17889 diff --git a/frontend/public/textures/smoke_01.png b/frontend/public/textures/smoke_01.png new file mode 100644 index 0000000000000000000000000000000000000000..4f8f6a76709394180dba61f6af6bfc1b13958ae4 --- /dev/null +++ b/frontend/public/textures/smoke_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8724c219e8d35859167fc0a7e207e13c72ccf0c29704909b9bb3d3dc71c6cf7 +size 97452 diff --git a/frontend/public/textures/smoke_02.png b/frontend/public/textures/smoke_02.png new file mode 100644 index 0000000000000000000000000000000000000000..00978cdb32d228ba2a4460c09373ad089ebf7596 --- /dev/null +++ b/frontend/public/textures/smoke_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c405cc0ceb31a239dfd873fffdfdfe3ff2056eebd5fcc77dfba1d9fdfed585c7 +size 96825 diff --git a/frontend/public/textures/smoke_03.png b/frontend/public/textures/smoke_03.png new file mode 100644 index 0000000000000000000000000000000000000000..955bc9be06024cd0453d4acc80217d27c29e3324 --- /dev/null +++ b/frontend/public/textures/smoke_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a71f8abcac64f8d73a94625cc9a10033dbeafa7eaea750560cba0daa73fe8752 +size 39737 diff --git a/frontend/public/textures/smoke_04.png b/frontend/public/textures/smoke_04.png new file mode 100644 index 0000000000000000000000000000000000000000..070ca1f1e468e4b470472740ceec5f7414c0c26b --- /dev/null +++ b/frontend/public/textures/smoke_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2db2f69874cdb1e5203f2f86803d5debde30402590cf50a810afaba882184616 +size 97568 diff --git a/frontend/public/textures/smoke_05.png b/frontend/public/textures/smoke_05.png new file mode 100644 index 0000000000000000000000000000000000000000..82864ef288b8d0adb7f775f5dfd61edc761f3297 --- /dev/null +++ b/frontend/public/textures/smoke_05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e88c8416bd8da1b3f9f19e615dcebdf42f71045799cb4c41f545cb16dd835e35 +size 83445 diff --git a/frontend/public/textures/smoke_06.png b/frontend/public/textures/smoke_06.png new file mode 100644 index 0000000000000000000000000000000000000000..f93e774ee4d2932c606abcf0e8b45ced498f8e18 --- /dev/null +++ b/frontend/public/textures/smoke_06.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d988b03bb46797be913333f06b26ff2aad55ec082cfb7d3d18ce86ccb71559b5 +size 62156 diff --git a/frontend/public/textures/smoke_07.png b/frontend/public/textures/smoke_07.png new file mode 100644 index 0000000000000000000000000000000000000000..0d917c4b30364a1ec7fddce960e77758d4264cff --- /dev/null +++ b/frontend/public/textures/smoke_07.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7419fa4f11e346d0fb36a17ecbf616083c6962633c7847d92119c994ed9b4e4 +size 77838 diff --git a/frontend/public/textures/smoke_08.png b/frontend/public/textures/smoke_08.png new file mode 100644 index 0000000000000000000000000000000000000000..99dcf3c20813f2e127d83268b451e05f3af0dadc --- /dev/null +++ b/frontend/public/textures/smoke_08.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eae1d92c205a2654254281d470ee70cce86cf4e8e849db29e4d5edff0f67c695 +size 90424 diff --git a/frontend/public/textures/smoke_09.png b/frontend/public/textures/smoke_09.png new file mode 100644 index 0000000000000000000000000000000000000000..72d091397d49aeb20f5c2dbc19816e411d935b2f --- /dev/null +++ b/frontend/public/textures/smoke_09.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c66cf05c52aefb1f3e37c54917289a11d9268a995dbc4effc8ca80242a4122b +size 87963 diff --git a/frontend/public/textures/smoke_10.png b/frontend/public/textures/smoke_10.png new file mode 100644 index 0000000000000000000000000000000000000000..4c590a8c0b4f25844f913cab1ebbbd819435f33f --- /dev/null +++ b/frontend/public/textures/smoke_10.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35d1082a87de7558aeeab210fecb43997c958e162ede896587d1930f66e6ae67 +size 90497 diff --git a/frontend/public/textures/spark_01.png b/frontend/public/textures/spark_01.png new file mode 100644 index 0000000000000000000000000000000000000000..9e105d40e839346ac0fa94c47f6f5a3bea552445 --- /dev/null +++ b/frontend/public/textures/spark_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5902da3a324cad0a4b5a88ce1e3c9cc18fee50922c358ce93d8c9f1ad7708f0a +size 95070 diff --git a/frontend/public/textures/spark_02.png b/frontend/public/textures/spark_02.png new file mode 100644 index 0000000000000000000000000000000000000000..ceaff82d906d2736ef6b42924bd64876bfc90d92 --- /dev/null +++ b/frontend/public/textures/spark_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7c5f1f849657eed79fd7b663be5a57829450a1e4fdc4a062049a05b9a406e89 +size 101613 diff --git a/frontend/public/textures/spark_03.png b/frontend/public/textures/spark_03.png new file mode 100644 index 0000000000000000000000000000000000000000..8dd713ecb0fdb6fa064712bcb839498ffc4c713f --- /dev/null +++ b/frontend/public/textures/spark_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90f322bebe5facea2f60afe88d98dd80a21479ed3212e605d61e88a896f3e1db +size 79574 diff --git a/frontend/public/textures/spark_04.png b/frontend/public/textures/spark_04.png new file mode 100644 index 0000000000000000000000000000000000000000..3905d4cbf460aeed7b5f5934ae36101d92f4c1cd --- /dev/null +++ b/frontend/public/textures/spark_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3cba35b80a97c12283a8ecd41780a0412194de14562cbffc7f04e9157183f772 +size 83811 diff --git a/frontend/public/textures/spark_05.png b/frontend/public/textures/spark_05.png new file mode 100644 index 0000000000000000000000000000000000000000..5292e582881831ed8b24d4a3b73145922c8d6885 --- /dev/null +++ b/frontend/public/textures/spark_05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9624ea2017b8b2767a9d3b82e978b7fdf5154d83e6756cd919da51447bb97138 +size 64808 diff --git a/frontend/public/textures/spark_06.png b/frontend/public/textures/spark_06.png new file mode 100644 index 0000000000000000000000000000000000000000..f00b0191b02e10bf7bd8e572329bb1f7b3135904 --- /dev/null +++ b/frontend/public/textures/spark_06.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe6d473393d3b9350c7a654b7cb5fbd343766b5b5b11d3e4aba476832d4a6d3f +size 46347 diff --git a/frontend/public/textures/spark_07.png b/frontend/public/textures/spark_07.png new file mode 100644 index 0000000000000000000000000000000000000000..7f877c239e3496c899aa1f9aa540756d84deb062 --- /dev/null +++ b/frontend/public/textures/spark_07.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:216101e7b54c627b4aa780649a21f0ef9146d2566f52699284d091ef00825a26 +size 40968 diff --git a/frontend/public/textures/star_01.png b/frontend/public/textures/star_01.png new file mode 100644 index 0000000000000000000000000000000000000000..e97af71f7653858f8ee4e83c0a5c2d16d4838a94 --- /dev/null +++ b/frontend/public/textures/star_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ba3999c944a0767677449412d306c1f230daa1743b31f929533cf77a22ecf5d +size 42118 diff --git a/frontend/public/textures/star_02.png b/frontend/public/textures/star_02.png new file mode 100644 index 0000000000000000000000000000000000000000..c96941ea6b1d63494a80d4a45ded612c2a9a002f --- /dev/null +++ b/frontend/public/textures/star_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0b7624a2e6f6825a3e93ddec685728c4d3de03b77bbf2a2e425b7a6d3d0215b +size 49897 diff --git a/frontend/public/textures/star_03.png b/frontend/public/textures/star_03.png new file mode 100644 index 0000000000000000000000000000000000000000..78eecebdd60de75c06e21286be421fb4e56e216d --- /dev/null +++ b/frontend/public/textures/star_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dac75aa35e5191de1917ec076d47f3b43a47b80d9aae6eefffb920c5ceba3601 +size 56622 diff --git a/frontend/public/textures/star_04.png b/frontend/public/textures/star_04.png new file mode 100644 index 0000000000000000000000000000000000000000..8a82e5af6c2de1d7775312fdbeb2a41502a5f1a6 --- /dev/null +++ b/frontend/public/textures/star_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6485ac16c773663bd39346f3bedae04465ac14c661eb47cc5cfa935cdbf6c2ec +size 30613 diff --git a/frontend/public/textures/star_05.png b/frontend/public/textures/star_05.png new file mode 100644 index 0000000000000000000000000000000000000000..c425aacb0aeac6a773f8bbd1c5fa6f1228adfa10 --- /dev/null +++ b/frontend/public/textures/star_05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e8ab968178e507824ab28353c0ffffa918ae3adbcf89513d3db50a17b7bc8a2 +size 62216 diff --git a/frontend/public/textures/star_06.png b/frontend/public/textures/star_06.png new file mode 100644 index 0000000000000000000000000000000000000000..9283cb258329c3c12962adcc4b10d054fd04ecf5 --- /dev/null +++ b/frontend/public/textures/star_06.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65d3ff5aa87ec0770c11fe1293fdc70cdc5ccf0eabc2e2fd6a1ead922051035e +size 35453 diff --git a/frontend/public/textures/star_07.png b/frontend/public/textures/star_07.png new file mode 100644 index 0000000000000000000000000000000000000000..097ccb18068b93b5fc70efdbe45b647a900aa129 --- /dev/null +++ b/frontend/public/textures/star_07.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3bc9b8f1960baab452e8a5e816b9edeee4420dbed5ddd7ebe174519860a451a +size 32369 diff --git a/frontend/public/textures/star_08.png b/frontend/public/textures/star_08.png new file mode 100644 index 0000000000000000000000000000000000000000..239640112074783a450d6df2a08b2d55583a3fa2 --- /dev/null +++ b/frontend/public/textures/star_08.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:05f24d1ee76c1464074cbca58155e4be517fefd34a7001bb012561647cc3dec3 +size 26479 diff --git a/frontend/public/textures/star_09.png b/frontend/public/textures/star_09.png new file mode 100644 index 0000000000000000000000000000000000000000..2e447ccd03bc732fecb876a67671d878e3fab594 --- /dev/null +++ b/frontend/public/textures/star_09.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4255985e312a0058dc0c4f8b5befc902da17e6be5e5252007f3c7b02bb150157 +size 50142 diff --git a/frontend/public/textures/symbol_01.png b/frontend/public/textures/symbol_01.png new file mode 100644 index 0000000000000000000000000000000000000000..3021d919d6dcc1438648118b15c5ac9a21c24082 --- /dev/null +++ b/frontend/public/textures/symbol_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3eb3988f3e80acf3fda9df07733f49c3585eafa7d6a7ce5b31765fe180dd6d9c +size 9088 diff --git a/frontend/public/textures/symbol_02.png b/frontend/public/textures/symbol_02.png new file mode 100644 index 0000000000000000000000000000000000000000..94f3c2d1eacf984e5674f809d41e37c330b9174e --- /dev/null +++ b/frontend/public/textures/symbol_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1582e947d6793ec74fafabe587750af1b2655d1cd4ffe65bce1967c85b362ba8 +size 7608 diff --git a/frontend/public/textures/trace_01.png b/frontend/public/textures/trace_01.png new file mode 100644 index 0000000000000000000000000000000000000000..89c95a163d1f19ce43b43853e3bdbfaa85fcf0d6 --- /dev/null +++ b/frontend/public/textures/trace_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34de69bc3f665b23fe9bed15f388efd8e8e4dc1a99f2fc5db9ba8715c525711f +size 38406 diff --git a/frontend/public/textures/trace_02.png b/frontend/public/textures/trace_02.png new file mode 100644 index 0000000000000000000000000000000000000000..de9ee32c0653cf73f05de43d5dca1999e885422f --- /dev/null +++ b/frontend/public/textures/trace_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c224d6d4544d8437bce5ee51452957b31f5479e337c5f432dc1b55b1e2f9685 +size 39395 diff --git a/frontend/public/textures/trace_03.png b/frontend/public/textures/trace_03.png new file mode 100644 index 0000000000000000000000000000000000000000..29642a70ec823ab7c77fc1b758a07875c75ea787 --- /dev/null +++ b/frontend/public/textures/trace_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8c05800698cd502c3416a1f2335292544d52b3c8624c3c829a534c52b364860 +size 42254 diff --git a/frontend/public/textures/trace_04.png b/frontend/public/textures/trace_04.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5cc27fea59ec344cfa338ab5089393721d3335 --- /dev/null +++ b/frontend/public/textures/trace_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca69d82709295663ef1d9fcd90d346319fef5389a15dd63e3b71b4527e620cd8 +size 47104 diff --git a/frontend/public/textures/trace_05.png b/frontend/public/textures/trace_05.png new file mode 100644 index 0000000000000000000000000000000000000000..f37b6f01c071e32defb7d1fe638023cb4c0c0e87 --- /dev/null +++ b/frontend/public/textures/trace_05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f3d687bd2c70b10add33bdbd4616deda28f0138f757d358f9f0b78fb38bde6e +size 42174 diff --git a/frontend/public/textures/trace_06.png b/frontend/public/textures/trace_06.png new file mode 100644 index 0000000000000000000000000000000000000000..45d300f49ebf4ad454c55b9e4448f646d9e2161c --- /dev/null +++ b/frontend/public/textures/trace_06.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22c61c0e280db28b6cb6033c015dde4360a124b3488302ea708566b7f36fd900 +size 36247 diff --git a/frontend/public/textures/trace_07.png b/frontend/public/textures/trace_07.png new file mode 100644 index 0000000000000000000000000000000000000000..8f3187f2fde7e2b5f1cbb4e306d8ee1887733ce5 --- /dev/null +++ b/frontend/public/textures/trace_07.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e5f177b7717d291464f01538756eb2f471961b09b39ea82ea72447210af7110 +size 33145 diff --git a/frontend/public/textures/twirl_01.png b/frontend/public/textures/twirl_01.png new file mode 100644 index 0000000000000000000000000000000000000000..a42d9b096834fd24e5a07e3eca86fac0c39ef389 --- /dev/null +++ b/frontend/public/textures/twirl_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba3f4996a5a91ea3cba6817a9b4770a385a82ef0b9350cbda5c0f140d182af7e +size 51060 diff --git a/frontend/public/textures/twirl_02.png b/frontend/public/textures/twirl_02.png new file mode 100644 index 0000000000000000000000000000000000000000..5a5ee7ed6b31fe6abfa132bc4c8891dee468f320 --- /dev/null +++ b/frontend/public/textures/twirl_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3963633f6b2395a3fa95b9f4f65f22a4d38a2c561d9312dfff4646b71c7aa6e2 +size 76886 diff --git a/frontend/public/textures/twirl_03.png b/frontend/public/textures/twirl_03.png new file mode 100644 index 0000000000000000000000000000000000000000..62b61333a3a5c3ddd0bf6f80b65ed14f29998f2f --- /dev/null +++ b/frontend/public/textures/twirl_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69baaab4bc68473e9f926dc249a1d1b58f759c88906e10ac859bf43ac3cfbc8b +size 81463 diff --git a/frontend/public/textures/window_01.png b/frontend/public/textures/window_01.png new file mode 100644 index 0000000000000000000000000000000000000000..536c34a3a2528106b22b2b6328bab2a6bf654a0b --- /dev/null +++ b/frontend/public/textures/window_01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:373747a996fcfc320c76d093bde08252db2adde8d29672aca98430e521e702fa +size 33759 diff --git a/frontend/public/textures/window_02.png b/frontend/public/textures/window_02.png new file mode 100644 index 0000000000000000000000000000000000000000..d991dec585235df857dfdecaf357f1101a77f304 --- /dev/null +++ b/frontend/public/textures/window_02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b430446f1527409d00b418370975f7b08572282aea732503f9a22837055d54d +size 39286 diff --git a/frontend/public/textures/window_03.png b/frontend/public/textures/window_03.png new file mode 100644 index 0000000000000000000000000000000000000000..ba205ca4b49c5d145aa8cbf46426bb83fc418af1 --- /dev/null +++ b/frontend/public/textures/window_03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:452680cff9e62da90a4b3bff96dd7634d05624ee2d58fd5db7d89f48a60cdf8c +size 16860 diff --git a/frontend/public/textures/window_04.png b/frontend/public/textures/window_04.png new file mode 100644 index 0000000000000000000000000000000000000000..f3319076d297f7fa10720ea3c25f6f067342381e --- /dev/null +++ b/frontend/public/textures/window_04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:096be5e6f21547f16f302747a607020b2f1214842618de7a078587f6b8f1956e +size 52143 diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg new file mode 100644 index 0000000000000000000000000000000000000000..e7b8dfb1b2a60bd50538bec9f876511b9cac21e3 --- /dev/null +++ b/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000000000000000000000000000000000000..01d7cf950e63001c57ddf7ea0ec24c8aca9f15cc --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,150 @@ +import React, { useState, useRef, Suspense } from "react"; +import { motion } from "framer-motion"; +import { Canvas } from "@react-three/fiber"; +import { OrbitControls } from '@react-three/drei'; +import AssistantChat from "./components/AssistantChat"; +import AvatarModel from "./components/AvatarModel"; +import ErrorBoundary from "./components/ErrorBoundary"; +import Sidebar from "./components/Sidebar"; +import Header from "./components/Header"; + +export default function App() { + const [error, setError] = useState(null); + const [sidebarOpen, setSidebarOpen] = useState(false); + const [activeTab, setActiveTab] = useState("chat"); + const [audioUrl, setAudioUrl] = useState(null); + + // Audio partagé pour Avatar + Lipsync + const audioRef = useRef(new Audio()); + + // --- Gestion audio stable --- + const handleAudioGenerated = (url) => { + setAudioUrl(url); + if (!audioRef.current) return; + + // Pause et réinitialisation de l'audio actuel + audioRef.current.pause(); + audioRef.current.currentTime = 0; + + // Mettre à jour la source + audioRef.current.src = url; + audioRef.current.crossOrigin = "anonymous"; + + const playAudio = async () => { + try { + await audioRef.current.play(); + } catch (err) { + console.error("Erreur lecture audio:", err); + } finally { + audioRef.current.oncanplaythrough = null; + } + }; + + // Attendre que l'audio soit chargé + audioRef.current.oncanplaythrough = playAudio; + audioRef.current.onerror = () => { + console.error("Erreur de chargement audio"); + audioRef.current.oncanplaythrough = null; + }; + }; + + if (error) { + return ( +
+ +

Erreur d'application

+

{error.message}

+ +
+
+ ); + } + + return ( + +
+
setSidebarOpen(true)} /> +
+ setSidebarOpen(false)} + activeTab={activeTab} + setActiveTab={setActiveTab} + /> + +
+
+ +

Assistante Intelligent

+
+ +
+
+
+ +
+ + + + + + + + + + + +
+
+
+ +
+
+ + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/AssistantChat.jsx b/frontend/src/components/AssistantChat.jsx new file mode 100644 index 0000000000000000000000000000000000000000..184340f310ba3113196807bdf6d652966635f405 --- /dev/null +++ b/frontend/src/components/AssistantChat.jsx @@ -0,0 +1,358 @@ +import React, { useState, useEffect, useRef } from "react"; +import { FiSend, FiUser } from "react-icons/fi"; +import { FaMicrophone, FaStop } from "react-icons/fa"; +import { motion, AnimatePresence } from "framer-motion"; +import { v4 as uuidv4 } from "uuid"; +import { API_LLM, API_TTS, WS_URL } from "../config/api"; + +export default function AssistantChat({ onAudioGenerated, audioRef }) { + const [messages, setMessages] = useState([]); + const [inputText, setInputText] = useState(""); + const [isLoading, setIsLoading] = useState(false); + const [recording, setRecording] = useState(false); + const [transcript, setTranscript] = useState(""); + const [responseText, setResponseText] = useState(""); + const [isProcessing, setIsProcessing] = useState(false); + const [history, setHistory] = useState([]); + const [userId] = useState(uuidv4()); + + const audioContextRef = useRef(null); + const mediaStreamRef = useRef(null); + const wsRef = useRef(null); + const messagesEndRef = useRef(null); + const isStoppingRef = useRef(false); + const workletNodeRef = useRef(null); + + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); + return () => stopRecording(); + }, [messages]); + + const sendMessage = async () => { + const userMsg = inputText.trim(); + if (!userMsg || isLoading) return; + + setIsLoading(true); + setMessages((prev) => [...prev, { sender: "user", text: userMsg, timestamp: new Date() }]); + setInputText(""); + + try { + const updatedHistory = [...history, { role: "user", content: userMsg }]; + + const llmResp = await fetch(API_LLM, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text: userMsg, user_id: userId, history: updatedHistory }), + }); + const llmData = await llmResp.json(); + + if (!llmResp.ok) { + throw new Error(llmData.detail || "Erreur serveur LLM"); + } + + const assistantText = llmData?.response || "Je n'ai pas compris."; + const lang = llmData?.lang || "fr"; + const newHistory = llmData?.history || updatedHistory; + + console.log("Requête texte envoyée:", { text: userMsg, user_id: userId, history: updatedHistory }); + console.log("Réponse LLM reçue:", llmData); + + setMessages((prev) => [...prev, { sender: "assistant", text: assistantText, timestamp: new Date() }]); + setHistory(newHistory); + + const ttsResp = await fetch(API_TTS, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ text: assistantText, lang }), + }); + const ttsData = await ttsResp.json(); + const audioUrl = ttsData?.url; + + if (audioUrl) { + onAudioGenerated?.(audioUrl); + if (audioRef?.current) { + audioRef.current.src = audioUrl; + audioRef.current.crossOrigin = "anonymous"; + await audioRef.current.play().catch((err) => { + console.warn("Erreur lecture audio:", err); + }); + } + } + } catch (err) { + console.warn("Erreur LLM/TTS:", err); + setMessages((prev) => [...prev, { sender: "assistant", text: "Erreur serveur.", timestamp: new Date() }]); + } finally { + setIsLoading(false); + } + }; + + const handleKeyDown = (e) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } + }; + + const startRecording = async () => { + if (recording || isProcessing) return; + isStoppingRef.current = false; + + try { + setTranscript(""); + setResponseText(""); + setIsProcessing(true); + + const audioContext = new (window.AudioContext || window.webkitAudioContext)({ + sampleRate: 16000, + }); + audioContextRef.current = audioContext; + + const stream = await navigator.mediaDevices.getUserMedia({ + audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true, autoGainControl: true }, + }); + mediaStreamRef.current = stream; + + const source = audioContext.createMediaStreamSource(stream); + await audioContext.audioWorklet.addModule("/pcm-worklet.js").catch((err) => { + throw new Error("Échec chargement pcm-worklet.js: " + err.message); + }); + const recorderNode = new AudioWorkletNode(audioContext, "pcm-processor", { + processorOptions: { + sampleRate: 16000, + inputSampleRate: audioContext.sampleRate, + chunkSize: 1024, + silenceThreshold: 0.01, + silenceWindow: 3, + }, + }); + workletNodeRef.current = recorderNode; + source.connect(recorderNode); + + const ws = new WebSocket(`${WS_URL}/live_stream/${userId}?room_id=room1`); + ws.binaryType = "arraybuffer"; + wsRef.current = ws; + + ws.onopen = () => { + ws.send(JSON.stringify({ type: "start", sampleRate: 16000, format: "pcm16", channels: 1 })); + setRecording(true); + setIsProcessing(false); + }; + + ws.onmessage = async (event) => { + const msg = event.data; + console.log("WebSocket message:", msg); + if (typeof msg === "string") { + const langRegex = new RegExp(`${userId}\\((fr|en|ar)\\): (.+)`); + const match = msg.match(langRegex); + if (match) { + const trans = match[2].trim(); + if (trans) { + setTranscript((prev) => prev + trans + " "); + setMessages((prev) => [...prev, { sender: "user", text: trans, timestamp: new Date() }]); + setHistory((prev) => [...prev, { role: "user", content: trans }]); + } + } else if (msg.startsWith("Système: ")) { + const resp = msg.replace("Système: ", "").trim(); + if (resp) { + setResponseText((prev) => prev + resp + " "); + setMessages((prev) => [...prev, { sender: "assistant", text: resp, timestamp: new Date() }]); + setHistory((prev) => [...prev, { role: "assistant", content: resp }]); + } + } else { + try { + const data = JSON.parse(msg); + if (data.type === "pong") return; + if (data.type === "stopped") { + safeStopRecording(); + return; + } + if (data.type === "error" && data.message?.trim()) { + console.error("Erreur WebSocket:", data.message); // Log l'erreur dans la console + setMessages((prev) => [...prev, { sender: "assistant", text: data.message.trim(), timestamp: new Date() }]); + setHistory((prev) => [...prev, { role: "assistant", content: data.message.trim() }]); + } else if (data.message?.trim()) { + setResponseText((prev) => prev + data.message + " "); + setMessages((prev) => [...prev, { sender: "assistant", text: data.message.trim(), timestamp: new Date() }]); + setHistory((prev) => [...prev, { role: "assistant", content: data.message.trim() }]); + } + } catch {} + } + } else if (msg instanceof ArrayBuffer || msg instanceof Blob) { + const blob = msg instanceof Blob ? msg : new Blob([msg], { type: "audio/wav" }); + const url = URL.createObjectURL(blob); + if (audioRef?.current) { + audioRef.current.src = url; + await audioRef.current.play().catch((err) => { + console.warn("Erreur lecture audio:", err); + }); + } + } + }; + + ws.onclose = (e) => { + if (!isStoppingRef.current && e.code !== 1000) { + setTimeout(startRecording, 1000); + } + safeStopRecording(); + }; + + ws.onerror = (err) => { + console.warn("Erreur WebSocket:", err); + safeStopRecording(); + }; + + recorderNode.port.onmessage = (e) => { + const { buffer } = e.data || {}; + if (buffer && ws.readyState === WebSocket.OPEN && !isStoppingRef.current) { + ws.send(buffer); + } + }; + + wsRef.current.pingInterval = setInterval(() => { + if (ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ type: "ping" })); + } + }, 20000); + } catch (err) { + console.warn("Erreur enregistrement:", err); + console.error("Micro inaccessible:", err?.message || "Erreur inconnue"); + safeStopRecording(); + } + }; + + const safeStopRecording = () => { + if (isStoppingRef.current) return; + isStoppingRef.current = true; + + try { + workletNodeRef.current?.port?.postMessage({ type: "flush" }); + } catch {} + + try { + if (wsRef.current?.readyState === WebSocket.OPEN) { + wsRef.current.send(JSON.stringify({ type: "stop" })); + } + } catch {} + + if (wsRef.current?.pingInterval) clearInterval(wsRef.current.pingInterval); + + try { + mediaStreamRef.current?.getTracks()?.forEach((t) => t.stop()); + } catch {} + mediaStreamRef.current = null; + + try { + audioContextRef.current?.close(); + } catch {} + audioContextRef.current = null; + + if (transcript.trim() && !messages.some((msg) => msg.sender === "user" && msg.text === transcript.trim())) { + setMessages((prev) => [...prev, { sender: "user", text: transcript.trim(), timestamp: new Date() }]); + setHistory((prev) => [...prev, { role: "user", content: transcript.trim() }]); + } + + setRecording(false); + setIsProcessing(false); + setTranscript(""); + setResponseText(""); + + workletNodeRef.current = null; + wsRef.current = null; + }; + + const stopRecording = () => { + if (wsRef.current?.readyState === WebSocket.OPEN) { + wsRef.current.send(JSON.stringify({ type: "stop" })); + } + }; + + const formatTime = (date) => date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); + + return ( +
+
+ + {messages.map((msg, idx) => ( + +
+
+ {msg.sender === "user" ? :
} +
+ + {msg.sender === "user" ? "Vous" : "Assistant"} • {formatTime(msg.timestamp)} + +
+
+ {msg.text} +
+ + ))} + +
+
+ +
+
+ setInputText(e.target.value)} + onKeyDown={handleKeyDown} + disabled={isLoading || recording} + /> + +
+ + +
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/Avatar3D.jsx b/frontend/src/components/Avatar3D.jsx new file mode 100644 index 0000000000000000000000000000000000000000..37ec5d7becc0be914536e3c25b57057142f01aad --- /dev/null +++ b/frontend/src/components/Avatar3D.jsx @@ -0,0 +1,102 @@ +import React, { Suspense, useState, useEffect } from "react"; +import { Canvas } from "@react-three/fiber"; +import { OrbitControls, Environment } from "@react-three/drei"; +import AvatarModel from "./AvatarModel"; +import Loader from "./Loader"; + +// Vérification de la disponibilité WebGL +function checkWebGLSupport() { + try { + const canvas = document.createElement('canvas'); + return !!( + window.WebGLRenderingContext && + (canvas.getContext('webgl') || canvas.getContext('experimental-webgl')) + ); + } catch (e) { + return false; + } +} + +export default function Avatar3D({ audioUrl }) { // Accepte audioUrl comme prop optionnelle + const [webGLAvailable, setWebGLAvailable] = useState(true); + const [loading, setLoading] = useState(true); + + useEffect(() => { + setWebGLAvailable(checkWebGLSupport()); + }, []); + + if (!webGLAvailable) { + return ( +
+
+

WebGL non supporté

+

+ Votre navigateur ne supporte pas WebGL, nécessaire pour afficher l'avatar 3D. + Veuillez utiliser un navigateur moderne comme Chrome, Firefox ou Edge. +

+
+ Prévisualisation non disponible +
+
+
+ ); + } + + return ( +
+ setLoading(false)} + > + {/* Éclairage amélioré */} + + + + + {/* Environnement */} + + + {/* Avatar */} + + + + + {/* Contrôles */} + + + + {loading && ( +
+
+
+
+
+
+
+
+

Chargement de l'avatar...

+
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/AvatarModel.jsx b/frontend/src/components/AvatarModel.jsx new file mode 100644 index 0000000000000000000000000000000000000000..22e2eee450eb20200f6a4466b59369a5aee8e4a6 --- /dev/null +++ b/frontend/src/components/AvatarModel.jsx @@ -0,0 +1,207 @@ +import React, { useRef, useEffect, useState } from "react"; +import { useGLTF, useAnimations } from "@react-three/drei"; +import { useFrame } from "@react-three/fiber"; +import * as THREE from "three"; +import { VISEMES, Lipsync } from "wawa-lipsync"; + +const EXTENDED_VISEMES = { + ...VISEMES, + // Français + PP: "viseme_PP", + FF: "viseme_FF", + DD: "viseme_DD", + KK: "viseme_KK", + CH: "viseme_CH", + SS: "viseme_SS", + NN: "viseme_NN", + RR: "viseme_RR", + AA: "viseme_AA", + E: "viseme_E", + I: "viseme_I", + O: "viseme_O", + U: "viseme_U", + // Anglais + TH: "viseme_TH", // /θ/, /ð/ (ex. "think", "this") + ZH: "viseme_ZH", // /ʒ/ (ex. "measure") + SH: "viseme_SH", // /ʃ/ (ex. "she") + // Arabe + Q: "viseme_Q", // /q/ (emphatique, ex. "ق") + HA: "viseme_HA", // /ħ/ (pharyngal, ex. "ح") + AA2: "viseme_AA2", // /ʕ/ (pharyngal, ex. "ع") ou voyelles ouvertes + KH: "viseme_KH", // /x/ (ex. "خ") + GH: "viseme_GH" // /ɣ/ (ex. "غ") +}; + +const morphScale = { + viseme_PP: 1.0, + viseme_FF: 0.8, + viseme_DD: 0.9, + viseme_KK: 0.7, + viseme_CH: 0.8, + viseme_SS: 0.7, + viseme_NN: 0.7, + viseme_RR: 0.6, + viseme_AA: 1.0, + viseme_E: 0.9, + viseme_I: 0.8, + viseme_O: 0.9, + viseme_U: 0.8, + viseme_TH: 0.7, // Anglais: lèvres légèrement en avant + viseme_ZH: 0.6, // Anglais: arrondi léger + viseme_SH: 0.7, // Anglais: lèvres pincées + viseme_Q: 0.8, // Arabe: mâchoire ouverte, gorge serrée + viseme_HA: 0.7, // Arabe: pharyngal, lèvres neutres + viseme_AA2: 1.0, // Arabe: voyelle ouverte pharyngale + viseme_KH: 0.7, // Arabe: fricative, lèvres légèrement ouvertes + viseme_GH: 0.6 // Arabe: fricative, gorge ouverte +}; + +export default function AvatarModel({ audioRef, audioUrl, lang = "fr", ...props }) { + const group = useRef(); + const avatarData = useGLTF("/models/avatar-version1.glb"); + const animationsData = useGLTF("/models/animations.glb"); + const { actions } = useAnimations(animationsData?.animations || [], group); + + const [animation] = useState("Idle"); + const [blink, setBlink] = useState(false); + + const lipsyncRef = useRef(null); + const morphValues = useRef({}); + const morphTargetsRef = useRef({}); + const lastViseme = useRef(null); + const visemeTransition = useRef(0); + const debugMode = useRef(process.env.NODE_ENV === "development"); // Logs uniquement en dev + + // --- Cache morphTargets --- + useEffect(() => { + if (!avatarData?.scene) return; + avatarData.scene.traverse(child => { + if (child.isSkinnedMesh && child.morphTargetDictionary) { + morphTargetsRef.current = { ...morphTargetsRef.current, ...child.morphTargetDictionary }; + if (debugMode.current) { + console.debug("Morph Targets disponibles:", child.morphTargetDictionary); + } + } + }); + }, [avatarData]); + + // --- Setup Lipsync et connexion dynamique sur changement de src --- + useEffect(() => { + if (!audioRef.current) return; + + const handleLoadStart = () => { + if (!lipsyncRef.current) { + lipsyncRef.current = new Lipsync(); + } + lipsyncRef.current.connectAudio(audioRef.current); + if (debugMode.current) { + console.debug(`Lipsync connecté (lang: ${lang}):`, audioRef.current.src); + } + }; + + audioRef.current.addEventListener("loadstart", handleLoadStart); + + if (audioRef.current.src) { + handleLoadStart(); + } + + return () => { + audioRef.current.removeEventListener("loadstart", handleLoadStart); + }; + }, [audioRef, lang]); + + // --- Animation Idle --- + useEffect(() => { + if (actions[animation]) actions[animation].reset().fadeIn(0.5).play(); + return () => { if (actions[animation]) actions[animation].fadeOut(0.5); }; + }, [animation, actions]); + + const lerpMorphTarget = (target, value, speed = 0.15) => { + if (!avatarData?.scene) return; + avatarData.scene.traverse(child => { + if (child.isSkinnedMesh && child.morphTargetDictionary) { + const index = child.morphTargetDictionary[target]; + if (index !== undefined && child.morphTargetInfluences) { + const current = morphValues.current[target] || child.morphTargetInfluences[index] || 0; + const newValue = THREE.MathUtils.lerp(current, value, speed); + child.morphTargetInfluences[index] = newValue; + morphValues.current[target] = newValue; + } + } + }); + }; + + const mapToExtendedViseme = (phoneme) => { + if (!phoneme) return null; + let mapped; + switch (phoneme) { + case "x": mapped = EXTENDED_VISEMES.KH; break; + default: mapped = EXTENDED_VISEMES[phoneme] || phoneme; + } + if (debugMode.current) { + console.debug(`Lang: ${lang}, Phonème: ${phoneme}, Visème mappé: ${mapped}`); + } + return mapped; + }; + + // --- Frame loop --- + useFrame((_, delta) => { + lerpMorphTarget("eyeBlinkLeft", blink ? 1 : 0, 0.2); + lerpMorphTarget("eyeBlinkRight", blink ? 1 : 0, 0.2); + + if (lipsyncRef.current && audioRef.current && !audioRef.current.paused) { + lipsyncRef.current.processAudio(); + const phoneme = lipsyncRef.current.viseme; + const energy = lipsyncRef.current.energy || 0; + const viseme = mapToExtendedViseme(phoneme); + + if (viseme && viseme !== lastViseme.current) { + lastViseme.current = viseme; + visemeTransition.current = 0; + } + + if (viseme) { + // Ajuster la vitesse de transition par langue + const transitionSpeed = lang === "ar" ? delta * 3 : lang === "en" ? delta * 5 : delta * 4; + visemeTransition.current = Math.min(visemeTransition.current + transitionSpeed, 1); + const targetValue = (morphScale[viseme] || 1.0) * + THREE.MathUtils.lerp(0, 1, visemeTransition.current); + lerpMorphTarget(viseme, targetValue, 0.3); + + Object.values(EXTENDED_VISEMES).forEach(v => { + if (v !== viseme && (morphValues.current[v] || 0) > 0.01) { + lerpMorphTarget(v, 0, 0.2); + } + }); + } + + // Ajuster l'expressivité par langue + const smileScale = lang === "ar" ? 1.7 : lang === "en" ? 1.4 : 1.5; + const browScale = lang === "ar" ? 1.2 : lang === "en" ? 0.9 : 1.0; + lerpMorphTarget("mouthSmile", Math.min(energy * smileScale, 0.8), 0.2); + lerpMorphTarget("browInnerUp", Math.min(energy * browScale, 0.6), 0.15); + } + }); + + // --- Blink --- + useEffect(() => { + let blinkTimeout; + const nextBlink = () => { + blinkTimeout = setTimeout(() => { + setBlink(true); + setTimeout(() => { setBlink(false); nextBlink(); }, THREE.MathUtils.randInt(120, 250)); + }, THREE.MathUtils.randInt(2000, 6000)); + }; + nextBlink(); + return () => clearTimeout(blinkTimeout); + }, []); + + return ( + + {avatarData?.scene && } + + ); +} + +useGLTF.preload("/models/avatar-version1.glb"); +useGLTF.preload("/models/animations.glb"); \ No newline at end of file diff --git a/frontend/src/components/ErrorBoundary.jsx b/frontend/src/components/ErrorBoundary.jsx new file mode 100644 index 0000000000000000000000000000000000000000..0bbbaa36e10343b8cd6dcc10f54a42c82d762268 --- /dev/null +++ b/frontend/src/components/ErrorBoundary.jsx @@ -0,0 +1,28 @@ +import React, { Component } from 'react'; + +export default class ErrorBoundary extends Component { + state = { hasError: false, error: null }; + + static getDerivedStateFromError(error) { + return { hasError: true, error }; + } + + componentDidCatch(error, errorInfo) { + console.error("Erreur dans le composant:", error, errorInfo); + if (this.props.onError) { + this.props.onError(error); + } + } + + render() { + if (this.state.hasError) { + return this.props.fallback || ( +
+

Une erreur est survenue

+

{this.state.error.message}

+
+ ); + } + return this.props.children; + } +} \ No newline at end of file diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx new file mode 100644 index 0000000000000000000000000000000000000000..34bec011fc059d02661af256ff041c85a3c4640f --- /dev/null +++ b/frontend/src/components/Header.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { FiMenu } from 'react-icons/fi'; + +export default function Header({ onMenuClick }) { + return ( +
+
+
+ +
+ Holokia Logo +

Holokia Assistante

+
+
+ +
+ + +
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/Loader.jsx b/frontend/src/components/Loader.jsx new file mode 100644 index 0000000000000000000000000000000000000000..9d659d6b8d96af4173899d64a6f47c134e24b59f --- /dev/null +++ b/frontend/src/components/Loader.jsx @@ -0,0 +1,17 @@ +import React from 'react'; + +export default function Loader({ message = "Chargement..." }) { + return ( +
+
+
+
+
+
+
+
+

{message}

+
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/Sidebar.jsx b/frontend/src/components/Sidebar.jsx new file mode 100644 index 0000000000000000000000000000000000000000..0d3f7da8fad099f80c7271d21491dab9f2ad1cfd --- /dev/null +++ b/frontend/src/components/Sidebar.jsx @@ -0,0 +1,48 @@ +import React from "react"; +import { FaTimes, FaComments, FaUser } from "react-icons/fa"; + +export default function Sidebar({ isOpen, onClose, activeTab, setActiveTab }) { + return ( +
+
+

Menu

+ +
+ + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/config/api.js b/frontend/src/config/api.js new file mode 100644 index 0000000000000000000000000000000000000000..aafcc9db8c7cce1b9bdc720d5deb7d5739fbdbb8 --- /dev/null +++ b/frontend/src/config/api.js @@ -0,0 +1,12 @@ +// src/config/api.js +export const API_LLM = import.meta.env.VITE_API_URL_LLM || '/api/llm/generate'; +export const API_TTS = import.meta.env.VITE_API_URL_TTS || '/api/tts/generate-tts'; +export const API_STT = import.meta.env.VITE_API_URL_STT || '/api/stt/transcribe'; +export const WS_URL = import.meta.env.VITE_WS_URL || (() => { + // Auto-détection de l'URL WebSocket basée sur l'environnement + if (window.location.protocol === 'https:') { + return `wss://${window.location.host}/ws`; + } else { + return `ws://${window.location.host}/ws`; + } +})(); diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000000000000000000000000000000000000..af4971b6b5c68dd0ea1bc6e563c8fe8a0c6bef92 --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,110 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +html, body, #root { + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: linear-gradient(135deg, #f3f4f6, #ffffff); +} + +/* Card style with enhanced shadow and smoother transitions */ +.card { + background-color: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(16px); + border-radius: 1.25rem; + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05), 0 2px 8px rgba(0, 0, 0, 0.05); + padding: 1.25rem; + transition: all 0.3s ease-in-out; +} + +.card:hover { + transform: translateY(-2px); + box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1); +} + +@media (min-width: 768px) { + .card { + padding: 2rem; + } +} + +/* Primary button with modern gradient and dynamic hover */ +.btn-primary { + background-image: linear-gradient(90deg, #3b82f6, #8b5cf6); + color: white; + font-weight: 600; + padding: 0.75rem 1.5rem; + border-radius: 0.75rem; + transition: all 0.3s ease; +} + +.btn-primary:hover { + background-image: linear-gradient(90deg, #2563eb, #7c3aed); + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +.btn-primary:active { + transform: translateY(0); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); +} + +/* Input field with cleaner focus state */ +.input-field { + width: 100%; + background-color: rgba(255, 255, 255, 0.9); + border: 1px solid #d1d5db; + border-radius: 0.75rem; + padding: 0.875rem 1.25rem; + transition: all 0.2s ease; + font-size: 0.95rem; +} + +.input-field:focus { + outline: none; + border-color: #3b82f6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2); +} + +/* Custom scrollbar with smoother appearance */ +.custom-scrollbar::-webkit-scrollbar { + width: 6px; +} + +.custom-scrollbar::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +.custom-scrollbar::-webkit-scrollbar-thumb { + background: #a1a1aa; + border-radius: 3px; +} + +.custom-scrollbar::-webkit-scrollbar-thumb:hover { + background: #78788c; +} + +/* Message bubble styles */ +.message-bubble { + max-width: 85%; + padding: 0.75rem 1.25rem; + border-radius: 1rem; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); + transition: all 0.2s ease; +} + +.message-bubble.user { + background: linear-gradient(90deg, #3b82f6, #6366f1); + color: white; +} + +.message-bubble.assistant { + background: #ffffff; + color: #1f2937; + border: 1px solid #e5e7eb; +} \ No newline at end of file diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx new file mode 100644 index 0000000000000000000000000000000000000000..d71ac17582f3e9b2d4b6ae5210536f3fd483ffab --- /dev/null +++ b/frontend/src/main.jsx @@ -0,0 +1,10 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.jsx"; +import "./index.css"; + +ReactDOM.createRoot(document.getElementById("root")).render( + + + +); \ No newline at end of file diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js new file mode 100644 index 0000000000000000000000000000000000000000..4851df174712a907bfffe48d480c3c4f6688becf --- /dev/null +++ b/frontend/src/utils/api.js @@ -0,0 +1,55 @@ +import axios from 'axios'; +import { API_LLM, API_TTS, API_STT } from '../config/api'; + +// Configuration Axios globale +const api = axios.create({ + timeout: 30000, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + } +}); + +// Intercepteur pour la gestion globale des erreurs +api.interceptors.response.use( + response => response, + error => { + let errorMessage = "Une erreur s'est produite"; + + if (error.code === 'ECONNABORTED') { + errorMessage = "La requête a expiré"; + } + else if (error.response) { + const status = error.response.status; + + if (status === 400) { + errorMessage = error.response.data?.detail || "Requête invalide"; + } + else if (status === 401) { + errorMessage = "Non autorisé"; + } + else if (status >= 500) { + errorMessage = "Erreur serveur"; + } + } + else if (error.request) { + errorMessage = "Pas de réponse du serveur"; + } + + return Promise.reject({ message: errorMessage }); + } +); + +// Fonctions d'appel API +export const callLLM = (text) => api.post(API_LLM, { text }); +export const callTTS = (text, lang) => api.post(API_TTS, { text, lang }, { + responseType: 'blob', + headers: { 'Accept': 'audio/mpeg' } +}); +export const callSTT = (file) => api.post(API_STT, file, { + headers: { + 'Content-Type': 'multipart/form-data', + } +}); + +export default api; \ No newline at end of file diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000000000000000000000000000000000000..d2438998a27246f4b36e8169727d0bc56787162f --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,10 @@ +module.exports = { + content: [ + "./index.html", + "./src/**/*.{js,jsx,ts,tsx}", + ], + theme: { + extend: {} + }, + plugins: [], +} \ No newline at end of file diff --git a/frontend/vite.config.js b/frontend/vite.config.js new file mode 100644 index 0000000000000000000000000000000000000000..74450767ec42fe584a2cba0fb2956546e95bafab --- /dev/null +++ b/frontend/vite.config.js @@ -0,0 +1,32 @@ +const { defineConfig } = require('vite'); +const react = require('@vitejs/plugin-react'); +const path = require('path'); +const tailwindcss = require('tailwindcss'); +const autoprefixer = require('autoprefixer'); + +module.exports = defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + css: { + postcss: { + plugins: [ + tailwindcss, + autoprefixer + ] + } + }, + server: { + port: 5173, + open: true, + hmr: { + overlay: false + } + }, + optimizeDeps: { + include: ['three', '@react-three/fiber', '@react-three/drei'], + }, +}); \ No newline at end of file diff --git a/frontend/wawa-lipsync/README.md b/frontend/wawa-lipsync/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3172038a5285c0449200a355fce8fea3a1a26168 --- /dev/null +++ b/frontend/wawa-lipsync/README.md @@ -0,0 +1,82 @@ +# Wawa Lipsync + +A **simple** and **easy-to-use** library built in TypeScript for **real-time lipsyncing with JS and web audio API**. Ideal for animating 2D/3D characters in web apps, chatbots, and games. + +The examples shows how to use the library with a 3D model in Three.js with React Three Fiber. + +[Live demo](https://wawa-lipsync.wawasensei.dev/) + +## Installation + +```bash +npm install wawa-lipsync +``` + +or + +```bash +yarn add wawa-lipsync +``` + +## Usage + +First, import the library and create a `Lipsync` instance: + +```javascript +import { Lipsync } from "wawa-lipsync"; + +export const lipsyncManager = new Lipsync(); +``` + +Then, you need to connect the [HTML `