gregorio commited on
Commit
3cb3dcd
·
0 Parent(s):

feat: initial clean project setup (root-level)

Browse files
.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *.pt filter=lfs diff=lfs merge=lfs -text
2
+ *.pkl filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dependencies
2
+ node_modules/
3
+ .npm
4
+ dist/
5
+ build/
6
+ .env
7
+ .env.local
8
+ .env.development.local
9
+ .env.test.local
10
+ .env.production.local
11
+ .DS_Store
12
+ __pycache__/
13
+ *.pyc
14
+ *.pyo
15
+ *.pyd
16
+ .pytest_cache/
17
+ .coverage
18
+ htmlcov/
19
+
20
+ # Notebooks (EXCLUDE STRICTLY)
21
+ *.ipynb
22
+
23
+ # Files to KEEP (Models)
24
+ # *.pt - Handled by Git LFS
25
+ # *.pkl - Handled by Git LFS
26
+
27
+ # Other heavy files
28
+ *.csv
29
+ *.png
30
+ *.jpg
31
+ *.jpeg
32
+ *.docx
33
+ *.pdf
34
+ *.txt
35
+ *.zip
36
+ venv/
37
+ env/
38
+ .venv/
39
+ .vscode/
40
+ .idea/
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --- Build Backend & Final Image ---
2
+ FROM python:3.10-slim
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies
6
+ RUN apt-get update && apt-get install -y \
7
+ build-essential \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Copy requirements and install
11
+ COPY web_app/backend/requirements.txt .
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ # Copy backend code and models
15
+ # Hugging Face Docker works best when files are in a simple structure
16
+ COPY web_app/backend/ /app/
17
+ COPY saved_models/ /app/saved_models/
18
+
19
+ # Expose port (Hugging Face default)
20
+ EXPOSE 7860
21
+
22
+ # Run the application
23
+ # We point to main:app because main.py is now in /app/
24
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: TruthLens AI
3
+ emoji: 🛡️
4
+ colorFrom: indigo
5
+ colorTo: blue
6
+ sdk: docker
7
+ pinned: false
8
+ app_port: 7860
9
+ ---
10
+
11
+ # Fake News Detection Web App
12
+ ... (rest of the content)
13
+
14
+ This is a full-stack web application for detecting fake news using models trained in the `fake_news_pipeline.ipynb`.
15
+
16
+ ## Features
17
+ - **Real-time Prediction**: Input news text and get instant results from 4 different models (DeBERTa, RoBERTa, DistilRoBERTa, Bi-LSTM).
18
+ - **Analysis Dashboard**: View training history, confusion matrices, ROC curves, and EDA results directly from the UI.
19
+ - **SOTA Performance**: Leveraging DeBERTa-v3 for high-accuracy predictions.
20
+
21
+ ## Tech Stack
22
+ - **Backend**: FastAPI (Python)
23
+ - **Frontend**: React + TypeScript + Vite
24
+ - **Styling**: Vanilla CSS
25
+ - **Models**: PyTorch + HuggingFace Transformers
26
+
27
+ ## How to Run
28
+
29
+ ### 1. Start the Backend
30
+ Navigate to the root directory and run:
31
+ ```bash
32
+ python -m uvicorn web_app.backend.main:app --reload
33
+ ```
34
+ The API will be available at `http://localhost:8000`.
35
+
36
+ ### 2. Start the Frontend
37
+ Navigate to `web_app/frontend`:
38
+ ```bash
39
+ cd web_app/frontend
40
+ npm install
41
+ npm run dev
42
+ ```
43
+ The website will be available at `http://localhost:5173`.
44
+
45
+ ## File Structure
46
+ - `web_app/backend/`: FastAPI source code and static analysis plots.
47
+ - `web_app/frontend/`: React source code and UI styles.
48
+ - `saved_models/`: Pre-trained model weights (used by backend).
saved_models/deberta.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:57c62a6d5f2870c2a6dbdc13559bc4623530b67d5633d877acedb2af014f6d51
3
+ size 737781803
saved_models/predictor_meta.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2ded31d54b80c5643536c712987dac0f4a16056af172483f857bcc935e724e67
3
+ size 228
web_app/backend/main.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from fastapi import FastAPI, HTTPException
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from fastapi.staticfiles import StaticFiles
5
+ from pydantic import BaseModel
6
+ from utils import Predictor
7
+
8
+ app = FastAPI(title="Fake News Detection API")
9
+
10
+ # Enable CORS for React frontend
11
+ app.add_middleware(
12
+ CORSMiddleware,
13
+ allow_origins=["*"],
14
+ allow_methods=["*"],
15
+ allow_headers=["*"],
16
+ )
17
+
18
+ # Initialize Predictor
19
+ MODELS_DIR = "../../saved_models"
20
+ if not os.path.exists(MODELS_DIR):
21
+ # Fallback for different deployment scenarios
22
+ MODELS_DIR = "saved_models"
23
+
24
+ predictor = Predictor(MODELS_DIR)
25
+
26
+ # Resolve paths relative to this file
27
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
28
+ STATIC_DIR = os.path.join(BASE_DIR, "static")
29
+
30
+ # Serve static files (plots)
31
+ if os.path.exists(STATIC_DIR):
32
+ app.mount("/plots", StaticFiles(directory=STATIC_DIR), name="plots")
33
+ else:
34
+ print(f"Warning: Static directory not found at {STATIC_DIR}")
35
+
36
+ # Serve React frontend
37
+ if os.path.exists("web_app/backend/static/frontend"):
38
+ app.mount("/", StaticFiles(directory="web_app/backend/static/frontend", html=True), name="frontend")
39
+
40
+
41
+ class NewsRequest(BaseModel):
42
+ text: str
43
+ model: str = "deberta"
44
+
45
+ @app.get("/")
46
+ def read_root():
47
+ return {"message": "Fake News Detection API is running"}
48
+
49
+ @app.post("/predict")
50
+ def predict_news(request: NewsRequest):
51
+ if not request.text.strip():
52
+ raise HTTPException(status_code=400, detail="Text cannot be empty")
53
+
54
+ if request.model not in predictor.available_models:
55
+ raise HTTPException(status_code=400, detail=f"Model {request.model} not available. Choose from {predictor.available_models}")
56
+
57
+ try:
58
+ result = predictor.predict(request.text, request.model)
59
+ return result
60
+ except Exception as e:
61
+ import traceback
62
+ traceback.print_exc() # Ini akan memunculkan error detail di terminal
63
+ raise HTTPException(status_code=500, detail=str(e))
64
+
65
+ @app.get("/analysis")
66
+ def get_analysis_plots():
67
+ plots = [f for f in os.listdir("web_app/backend/static") if f.endswith(".png")]
68
+ return {"plots": plots}
69
+
70
+ if __name__ == "__main__":
71
+ import uvicorn
72
+ uvicorn.run(app, host="0.0.0.0", port=8000)
web_app/backend/models.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from transformers import AutoModelForSequenceClassification
4
+
5
+ # Bi-LSTM Components
6
+ class AttentionPool(nn.Module):
7
+ def __init__(self, h):
8
+ super().__init__()
9
+ self.w = nn.Linear(h * 2, 1)
10
+
11
+ def forward(self, x):
12
+ # x: (batch, seq, hidden*2)
13
+ attn_weights = torch.softmax(self.w(x), dim=1)
14
+ return (x * attn_weights).sum(1)
15
+
16
+ class BiLSTMClassifier(nn.Module):
17
+ def __init__(self, vocab_size, embed_dim, hidden_dim, n_layers, dropout):
18
+ super().__init__()
19
+ self.emb = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
20
+ self.lstm = nn.LSTM(embed_dim, hidden_dim, n_layers,
21
+ batch_first=True, bidirectional=True,
22
+ dropout=dropout if n_layers > 1 else 0)
23
+ self.attn = AttentionPool(hidden_dim)
24
+ self.drop = nn.Dropout(dropout)
25
+ self.fc = nn.Linear(hidden_dim * 2, 2)
26
+
27
+ def forward(self, x):
28
+ e, _ = self.lstm(self.drop(self.emb(x)))
29
+ return self.fc(self.drop(self.attn(e)))
30
+
31
+ # Transformer Wrapper
32
+ class TransformerClassifier(nn.Module):
33
+ def __init__(self, model_name):
34
+ super().__init__()
35
+ self.model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)
36
+
37
+ def forward(self, **kwargs):
38
+ out = self.model(**kwargs)
39
+ return out.logits
web_app/backend/requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ torch
4
+ transformers
5
+ pydantic
6
+ python-multipart
7
+ joblib
8
+ numpy
9
+ pandas
10
+ scikit-learn
11
+ sentencepiece
12
+ accelerate
web_app/backend/utils.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import pickle
3
+ import torch
4
+ import joblib
5
+ from transformers import AutoTokenizer
6
+ from models import BiLSTMClassifier, TransformerClassifier
7
+
8
+ def clean_text(text: str, lower: bool = True) -> str:
9
+ if not isinstance(text, str):
10
+ return ''
11
+ text = re.sub(r'<[^>]+>', ' ', text)
12
+ text = re.sub(r'http\S+|www\.\S+', ' ', text)
13
+ text = re.sub(r'\S+@\S+', ' ', text)
14
+ text = re.sub(r'[^\w\s]', ' ', text)
15
+ text = text.replace('—', ' ') # Remove em dashes
16
+ text = re.sub(r'\s+', ' ', text).strip()
17
+ return text.lower() if lower else text
18
+
19
+ def load_bilstm_vocab(path):
20
+ return joblib.load(path)
21
+
22
+ def tokenize_bilstm(text, vocab, max_len):
23
+ tokens = text.split()
24
+ ids = [vocab.get(w, 1) for w in tokens[:max_len]]
25
+ ids = ids + [0] * (max_len - len(ids))
26
+ return torch.tensor([ids], dtype=torch.long)
27
+
28
+ class Predictor:
29
+ def __init__(self, models_dir):
30
+ self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
31
+ self.models_dir = models_dir
32
+
33
+ # Metadata
34
+ with open(f"{models_dir}/predictor_meta.pkl", 'rb') as f:
35
+ self.meta = pickle.load(f)
36
+
37
+ self.available_models = self.meta['available_models']
38
+ self.loaded_models = {}
39
+ self.tokenizers = {}
40
+ self.vocabs = {}
41
+
42
+ # Core Engine: DeBERTa-v3
43
+ self.core_model_name = "microsoft/deberta-v3-base"
44
+ self.core_model = None
45
+ self.core_tokenizer = None
46
+
47
+ # Configs from notebook
48
+ self.cfg_bilstm = dict(vocab_size=50_000, embed_dim=300, hidden_dim=256, n_layers=2, dropout=0.3, max_len=300)
49
+
50
+ def _get_core_engine(self):
51
+ if self.core_model is None:
52
+ print(f"Loading Core Engine: {self.core_model_name}...")
53
+ self.core_tokenizer = AutoTokenizer.from_pretrained(self.core_model_name)
54
+ self.core_model = TransformerClassifier(self.core_model_name)
55
+ # Load the trained weights from deberta.pt
56
+ self.core_model.load_state_dict(torch.load(f"{self.models_dir}/deberta.pt", map_location=self.device))
57
+ self.core_model.to(self.device).eval()
58
+ return self.core_model, self.core_tokenizer
59
+
60
+ def _get_model(self, model_type):
61
+ if model_type == 'deberta':
62
+ return self._get_core_engine()
63
+
64
+ if model_type in self.loaded_models:
65
+ return self.loaded_models[model_type], self.tokenizers.get(model_type) or self.vocabs.get(model_type)
66
+
67
+ if model_type == 'bilstm':
68
+ vocab = load_bilstm_vocab(f"{self.models_dir}/bilstm_vocab.pkl")
69
+ model_params = {k: v for k, v in self.cfg_bilstm.items() if k != 'max_len'}
70
+ model = BiLSTMClassifier(**model_params)
71
+ model.load_state_dict(torch.load(f"{self.models_dir}/bilstm.pt", map_location=self.device))
72
+ model.to(self.device).eval()
73
+ self.loaded_models[model_type] = model
74
+ self.vocabs[model_type] = vocab
75
+ return model, vocab
76
+
77
+ else:
78
+ # Other Transformer models
79
+ name_map = {
80
+ 'distilroberta': 'distilroberta-base',
81
+ 'roberta': 'roberta-base'
82
+ }
83
+ model_name = name_map.get(model_type)
84
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
85
+ model = TransformerClassifier(model_name)
86
+ model.load_state_dict(torch.load(f"{self.models_dir}/{model_type}.pt", map_location=self.device))
87
+ model.to(self.device).eval()
88
+ self.loaded_models[model_type] = model
89
+ self.tokenizers[model_type] = tokenizer
90
+ return model, tokenizer
91
+
92
+ def predict(self, text, model_type='deberta'):
93
+ # Force deberta if requested by user for AI too
94
+ model, processor = self._get_model('deberta' if model_type == 'deberta' else model_type)
95
+
96
+ with torch.no_grad():
97
+ if model_type == 'bilstm' and not model_type == 'deberta':
98
+ cleaned = clean_text(text, lower=True)
99
+ inputs = tokenize_bilstm(cleaned, processor, self.cfg_bilstm['max_len']).to(self.device)
100
+ logits = model(inputs)
101
+ else:
102
+ cleaned = clean_text(text, lower=False)
103
+ inputs = processor(cleaned, return_tensors='pt', truncation=True, padding=True, max_length=256).to(self.device)
104
+ logits = model(**inputs)
105
+
106
+ probs = torch.softmax(logits, dim=1)
107
+ conf, pred = torch.max(probs, dim=1)
108
+
109
+ # Unified Output using DeBERTa-v3 logic
110
+ # For Fake News: label 1=Real, 0=Fake
111
+ # For AI Detection: The model detects "Fake" patterns often associated with AI
112
+ # We use a heuristic: high fake confidence correlates with AI generation markers in the trained dataset
113
+
114
+ fake_news_pred = 'Real' if pred.item() == 1 else 'Fake'
115
+
116
+ # Heuristic for AI: If predicted Fake, high probability it's AI (as AI data was Fake in training)
117
+ # If Real, likely Human.
118
+ ai_pred = 'Human' if fake_news_pred == 'Real' else 'AI Generated'
119
+ ai_conf = round(conf.item() * 100, 2)
120
+
121
+ return {
122
+ 'fake_news': {
123
+ 'prediction': fake_news_pred,
124
+ 'confidence': round(conf.item() * 100, 2),
125
+ 'model': 'deberta-v3'
126
+ },
127
+ 'ai_detection': {
128
+ 'prediction': ai_pred,
129
+ 'confidence': ai_conf,
130
+ 'model': 'deberta-v3'
131
+ }
132
+ }
web_app/frontend/index.html ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Fake News Detection</title>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
11
+ </head>
12
+ <body>
13
+ <div id="root"></div>
14
+ <script type="module" src="/src/main.tsx"></script>
15
+ </body>
16
+ </html>
web_app/frontend/package-lock.json ADDED
@@ -0,0 +1,2123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "fake-news-detection-frontend",
3
+ "version": "0.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "fake-news-detection-frontend",
9
+ "version": "0.0.0",
10
+ "dependencies": {
11
+ "@react-three/drei": "^9.88.0",
12
+ "@react-three/fiber": "^8.13.0",
13
+ "@use-gesture/react": "^10.3.1",
14
+ "clsx": "^2.1.1",
15
+ "framer-motion": "^10.18.0",
16
+ "gsap": "^3.15.0",
17
+ "lucide-react": "^0.263.1",
18
+ "react": "^18.2.0",
19
+ "react-dom": "^18.2.0",
20
+ "tailwind-merge": "^3.6.0",
21
+ "three": "^0.159.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/react": "^18.2.15",
25
+ "@types/react-dom": "^18.2.7",
26
+ "@vitejs/plugin-react": "^4.0.3",
27
+ "typescript": "^5.0.2",
28
+ "vite": "^4.4.5"
29
+ }
30
+ },
31
+ "node_modules/@babel/code-frame": {
32
+ "version": "7.29.0",
33
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
34
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
35
+ "dev": true,
36
+ "license": "MIT",
37
+ "dependencies": {
38
+ "@babel/helper-validator-identifier": "^7.28.5",
39
+ "js-tokens": "^4.0.0",
40
+ "picocolors": "^1.1.1"
41
+ },
42
+ "engines": {
43
+ "node": ">=6.9.0"
44
+ }
45
+ },
46
+ "node_modules/@babel/compat-data": {
47
+ "version": "7.29.3",
48
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz",
49
+ "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==",
50
+ "dev": true,
51
+ "license": "MIT",
52
+ "engines": {
53
+ "node": ">=6.9.0"
54
+ }
55
+ },
56
+ "node_modules/@babel/core": {
57
+ "version": "7.29.0",
58
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
59
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
60
+ "dev": true,
61
+ "license": "MIT",
62
+ "dependencies": {
63
+ "@babel/code-frame": "^7.29.0",
64
+ "@babel/generator": "^7.29.0",
65
+ "@babel/helper-compilation-targets": "^7.28.6",
66
+ "@babel/helper-module-transforms": "^7.28.6",
67
+ "@babel/helpers": "^7.28.6",
68
+ "@babel/parser": "^7.29.0",
69
+ "@babel/template": "^7.28.6",
70
+ "@babel/traverse": "^7.29.0",
71
+ "@babel/types": "^7.29.0",
72
+ "@jridgewell/remapping": "^2.3.5",
73
+ "convert-source-map": "^2.0.0",
74
+ "debug": "^4.1.0",
75
+ "gensync": "^1.0.0-beta.2",
76
+ "json5": "^2.2.3",
77
+ "semver": "^6.3.1"
78
+ },
79
+ "engines": {
80
+ "node": ">=6.9.0"
81
+ },
82
+ "funding": {
83
+ "type": "opencollective",
84
+ "url": "https://opencollective.com/babel"
85
+ }
86
+ },
87
+ "node_modules/@babel/generator": {
88
+ "version": "7.29.1",
89
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
90
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
91
+ "dev": true,
92
+ "license": "MIT",
93
+ "dependencies": {
94
+ "@babel/parser": "^7.29.0",
95
+ "@babel/types": "^7.29.0",
96
+ "@jridgewell/gen-mapping": "^0.3.12",
97
+ "@jridgewell/trace-mapping": "^0.3.28",
98
+ "jsesc": "^3.0.2"
99
+ },
100
+ "engines": {
101
+ "node": ">=6.9.0"
102
+ }
103
+ },
104
+ "node_modules/@babel/helper-compilation-targets": {
105
+ "version": "7.28.6",
106
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
107
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
108
+ "dev": true,
109
+ "license": "MIT",
110
+ "dependencies": {
111
+ "@babel/compat-data": "^7.28.6",
112
+ "@babel/helper-validator-option": "^7.27.1",
113
+ "browserslist": "^4.24.0",
114
+ "lru-cache": "^5.1.1",
115
+ "semver": "^6.3.1"
116
+ },
117
+ "engines": {
118
+ "node": ">=6.9.0"
119
+ }
120
+ },
121
+ "node_modules/@babel/helper-globals": {
122
+ "version": "7.28.0",
123
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
124
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
125
+ "dev": true,
126
+ "license": "MIT",
127
+ "engines": {
128
+ "node": ">=6.9.0"
129
+ }
130
+ },
131
+ "node_modules/@babel/helper-module-imports": {
132
+ "version": "7.28.6",
133
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
134
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
135
+ "dev": true,
136
+ "license": "MIT",
137
+ "dependencies": {
138
+ "@babel/traverse": "^7.28.6",
139
+ "@babel/types": "^7.28.6"
140
+ },
141
+ "engines": {
142
+ "node": ">=6.9.0"
143
+ }
144
+ },
145
+ "node_modules/@babel/helper-module-transforms": {
146
+ "version": "7.28.6",
147
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
148
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
149
+ "dev": true,
150
+ "license": "MIT",
151
+ "dependencies": {
152
+ "@babel/helper-module-imports": "^7.28.6",
153
+ "@babel/helper-validator-identifier": "^7.28.5",
154
+ "@babel/traverse": "^7.28.6"
155
+ },
156
+ "engines": {
157
+ "node": ">=6.9.0"
158
+ },
159
+ "peerDependencies": {
160
+ "@babel/core": "^7.0.0"
161
+ }
162
+ },
163
+ "node_modules/@babel/helper-plugin-utils": {
164
+ "version": "7.28.6",
165
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
166
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
167
+ "dev": true,
168
+ "license": "MIT",
169
+ "engines": {
170
+ "node": ">=6.9.0"
171
+ }
172
+ },
173
+ "node_modules/@babel/helper-string-parser": {
174
+ "version": "7.27.1",
175
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
176
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
177
+ "dev": true,
178
+ "license": "MIT",
179
+ "engines": {
180
+ "node": ">=6.9.0"
181
+ }
182
+ },
183
+ "node_modules/@babel/helper-validator-identifier": {
184
+ "version": "7.28.5",
185
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
186
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
187
+ "dev": true,
188
+ "license": "MIT",
189
+ "engines": {
190
+ "node": ">=6.9.0"
191
+ }
192
+ },
193
+ "node_modules/@babel/helper-validator-option": {
194
+ "version": "7.27.1",
195
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
196
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
197
+ "dev": true,
198
+ "license": "MIT",
199
+ "engines": {
200
+ "node": ">=6.9.0"
201
+ }
202
+ },
203
+ "node_modules/@babel/helpers": {
204
+ "version": "7.29.2",
205
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
206
+ "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
207
+ "dev": true,
208
+ "license": "MIT",
209
+ "dependencies": {
210
+ "@babel/template": "^7.28.6",
211
+ "@babel/types": "^7.29.0"
212
+ },
213
+ "engines": {
214
+ "node": ">=6.9.0"
215
+ }
216
+ },
217
+ "node_modules/@babel/parser": {
218
+ "version": "7.29.3",
219
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
220
+ "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
221
+ "dev": true,
222
+ "license": "MIT",
223
+ "dependencies": {
224
+ "@babel/types": "^7.29.0"
225
+ },
226
+ "bin": {
227
+ "parser": "bin/babel-parser.js"
228
+ },
229
+ "engines": {
230
+ "node": ">=6.0.0"
231
+ }
232
+ },
233
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
234
+ "version": "7.27.1",
235
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
236
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
237
+ "dev": true,
238
+ "license": "MIT",
239
+ "dependencies": {
240
+ "@babel/helper-plugin-utils": "^7.27.1"
241
+ },
242
+ "engines": {
243
+ "node": ">=6.9.0"
244
+ },
245
+ "peerDependencies": {
246
+ "@babel/core": "^7.0.0-0"
247
+ }
248
+ },
249
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
250
+ "version": "7.27.1",
251
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
252
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
253
+ "dev": true,
254
+ "license": "MIT",
255
+ "dependencies": {
256
+ "@babel/helper-plugin-utils": "^7.27.1"
257
+ },
258
+ "engines": {
259
+ "node": ">=6.9.0"
260
+ },
261
+ "peerDependencies": {
262
+ "@babel/core": "^7.0.0-0"
263
+ }
264
+ },
265
+ "node_modules/@babel/runtime": {
266
+ "version": "7.29.2",
267
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
268
+ "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
269
+ "license": "MIT",
270
+ "engines": {
271
+ "node": ">=6.9.0"
272
+ }
273
+ },
274
+ "node_modules/@babel/template": {
275
+ "version": "7.28.6",
276
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
277
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
278
+ "dev": true,
279
+ "license": "MIT",
280
+ "dependencies": {
281
+ "@babel/code-frame": "^7.28.6",
282
+ "@babel/parser": "^7.28.6",
283
+ "@babel/types": "^7.28.6"
284
+ },
285
+ "engines": {
286
+ "node": ">=6.9.0"
287
+ }
288
+ },
289
+ "node_modules/@babel/traverse": {
290
+ "version": "7.29.0",
291
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
292
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
293
+ "dev": true,
294
+ "license": "MIT",
295
+ "dependencies": {
296
+ "@babel/code-frame": "^7.29.0",
297
+ "@babel/generator": "^7.29.0",
298
+ "@babel/helper-globals": "^7.28.0",
299
+ "@babel/parser": "^7.29.0",
300
+ "@babel/template": "^7.28.6",
301
+ "@babel/types": "^7.29.0",
302
+ "debug": "^4.3.1"
303
+ },
304
+ "engines": {
305
+ "node": ">=6.9.0"
306
+ }
307
+ },
308
+ "node_modules/@babel/types": {
309
+ "version": "7.29.0",
310
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
311
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
312
+ "dev": true,
313
+ "license": "MIT",
314
+ "dependencies": {
315
+ "@babel/helper-string-parser": "^7.27.1",
316
+ "@babel/helper-validator-identifier": "^7.28.5"
317
+ },
318
+ "engines": {
319
+ "node": ">=6.9.0"
320
+ }
321
+ },
322
+ "node_modules/@dimforge/rapier3d-compat": {
323
+ "version": "0.12.0",
324
+ "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
325
+ "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==",
326
+ "license": "Apache-2.0",
327
+ "peer": true
328
+ },
329
+ "node_modules/@emotion/is-prop-valid": {
330
+ "version": "0.8.8",
331
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
332
+ "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
333
+ "license": "MIT",
334
+ "optional": true,
335
+ "dependencies": {
336
+ "@emotion/memoize": "0.7.4"
337
+ }
338
+ },
339
+ "node_modules/@emotion/memoize": {
340
+ "version": "0.7.4",
341
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
342
+ "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
343
+ "license": "MIT",
344
+ "optional": true
345
+ },
346
+ "node_modules/@esbuild/android-arm": {
347
+ "version": "0.18.20",
348
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
349
+ "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
350
+ "cpu": [
351
+ "arm"
352
+ ],
353
+ "dev": true,
354
+ "license": "MIT",
355
+ "optional": true,
356
+ "os": [
357
+ "android"
358
+ ],
359
+ "engines": {
360
+ "node": ">=12"
361
+ }
362
+ },
363
+ "node_modules/@esbuild/android-arm64": {
364
+ "version": "0.18.20",
365
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
366
+ "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
367
+ "cpu": [
368
+ "arm64"
369
+ ],
370
+ "dev": true,
371
+ "license": "MIT",
372
+ "optional": true,
373
+ "os": [
374
+ "android"
375
+ ],
376
+ "engines": {
377
+ "node": ">=12"
378
+ }
379
+ },
380
+ "node_modules/@esbuild/android-x64": {
381
+ "version": "0.18.20",
382
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
383
+ "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
384
+ "cpu": [
385
+ "x64"
386
+ ],
387
+ "dev": true,
388
+ "license": "MIT",
389
+ "optional": true,
390
+ "os": [
391
+ "android"
392
+ ],
393
+ "engines": {
394
+ "node": ">=12"
395
+ }
396
+ },
397
+ "node_modules/@esbuild/darwin-arm64": {
398
+ "version": "0.18.20",
399
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
400
+ "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
401
+ "cpu": [
402
+ "arm64"
403
+ ],
404
+ "dev": true,
405
+ "license": "MIT",
406
+ "optional": true,
407
+ "os": [
408
+ "darwin"
409
+ ],
410
+ "engines": {
411
+ "node": ">=12"
412
+ }
413
+ },
414
+ "node_modules/@esbuild/darwin-x64": {
415
+ "version": "0.18.20",
416
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
417
+ "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
418
+ "cpu": [
419
+ "x64"
420
+ ],
421
+ "dev": true,
422
+ "license": "MIT",
423
+ "optional": true,
424
+ "os": [
425
+ "darwin"
426
+ ],
427
+ "engines": {
428
+ "node": ">=12"
429
+ }
430
+ },
431
+ "node_modules/@esbuild/freebsd-arm64": {
432
+ "version": "0.18.20",
433
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
434
+ "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
435
+ "cpu": [
436
+ "arm64"
437
+ ],
438
+ "dev": true,
439
+ "license": "MIT",
440
+ "optional": true,
441
+ "os": [
442
+ "freebsd"
443
+ ],
444
+ "engines": {
445
+ "node": ">=12"
446
+ }
447
+ },
448
+ "node_modules/@esbuild/freebsd-x64": {
449
+ "version": "0.18.20",
450
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
451
+ "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
452
+ "cpu": [
453
+ "x64"
454
+ ],
455
+ "dev": true,
456
+ "license": "MIT",
457
+ "optional": true,
458
+ "os": [
459
+ "freebsd"
460
+ ],
461
+ "engines": {
462
+ "node": ">=12"
463
+ }
464
+ },
465
+ "node_modules/@esbuild/linux-arm": {
466
+ "version": "0.18.20",
467
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
468
+ "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
469
+ "cpu": [
470
+ "arm"
471
+ ],
472
+ "dev": true,
473
+ "license": "MIT",
474
+ "optional": true,
475
+ "os": [
476
+ "linux"
477
+ ],
478
+ "engines": {
479
+ "node": ">=12"
480
+ }
481
+ },
482
+ "node_modules/@esbuild/linux-arm64": {
483
+ "version": "0.18.20",
484
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
485
+ "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
486
+ "cpu": [
487
+ "arm64"
488
+ ],
489
+ "dev": true,
490
+ "license": "MIT",
491
+ "optional": true,
492
+ "os": [
493
+ "linux"
494
+ ],
495
+ "engines": {
496
+ "node": ">=12"
497
+ }
498
+ },
499
+ "node_modules/@esbuild/linux-ia32": {
500
+ "version": "0.18.20",
501
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
502
+ "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
503
+ "cpu": [
504
+ "ia32"
505
+ ],
506
+ "dev": true,
507
+ "license": "MIT",
508
+ "optional": true,
509
+ "os": [
510
+ "linux"
511
+ ],
512
+ "engines": {
513
+ "node": ">=12"
514
+ }
515
+ },
516
+ "node_modules/@esbuild/linux-loong64": {
517
+ "version": "0.18.20",
518
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
519
+ "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
520
+ "cpu": [
521
+ "loong64"
522
+ ],
523
+ "dev": true,
524
+ "license": "MIT",
525
+ "optional": true,
526
+ "os": [
527
+ "linux"
528
+ ],
529
+ "engines": {
530
+ "node": ">=12"
531
+ }
532
+ },
533
+ "node_modules/@esbuild/linux-mips64el": {
534
+ "version": "0.18.20",
535
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
536
+ "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
537
+ "cpu": [
538
+ "mips64el"
539
+ ],
540
+ "dev": true,
541
+ "license": "MIT",
542
+ "optional": true,
543
+ "os": [
544
+ "linux"
545
+ ],
546
+ "engines": {
547
+ "node": ">=12"
548
+ }
549
+ },
550
+ "node_modules/@esbuild/linux-ppc64": {
551
+ "version": "0.18.20",
552
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
553
+ "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
554
+ "cpu": [
555
+ "ppc64"
556
+ ],
557
+ "dev": true,
558
+ "license": "MIT",
559
+ "optional": true,
560
+ "os": [
561
+ "linux"
562
+ ],
563
+ "engines": {
564
+ "node": ">=12"
565
+ }
566
+ },
567
+ "node_modules/@esbuild/linux-riscv64": {
568
+ "version": "0.18.20",
569
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
570
+ "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
571
+ "cpu": [
572
+ "riscv64"
573
+ ],
574
+ "dev": true,
575
+ "license": "MIT",
576
+ "optional": true,
577
+ "os": [
578
+ "linux"
579
+ ],
580
+ "engines": {
581
+ "node": ">=12"
582
+ }
583
+ },
584
+ "node_modules/@esbuild/linux-s390x": {
585
+ "version": "0.18.20",
586
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
587
+ "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
588
+ "cpu": [
589
+ "s390x"
590
+ ],
591
+ "dev": true,
592
+ "license": "MIT",
593
+ "optional": true,
594
+ "os": [
595
+ "linux"
596
+ ],
597
+ "engines": {
598
+ "node": ">=12"
599
+ }
600
+ },
601
+ "node_modules/@esbuild/linux-x64": {
602
+ "version": "0.18.20",
603
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
604
+ "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
605
+ "cpu": [
606
+ "x64"
607
+ ],
608
+ "dev": true,
609
+ "license": "MIT",
610
+ "optional": true,
611
+ "os": [
612
+ "linux"
613
+ ],
614
+ "engines": {
615
+ "node": ">=12"
616
+ }
617
+ },
618
+ "node_modules/@esbuild/netbsd-x64": {
619
+ "version": "0.18.20",
620
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
621
+ "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
622
+ "cpu": [
623
+ "x64"
624
+ ],
625
+ "dev": true,
626
+ "license": "MIT",
627
+ "optional": true,
628
+ "os": [
629
+ "netbsd"
630
+ ],
631
+ "engines": {
632
+ "node": ">=12"
633
+ }
634
+ },
635
+ "node_modules/@esbuild/openbsd-x64": {
636
+ "version": "0.18.20",
637
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
638
+ "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
639
+ "cpu": [
640
+ "x64"
641
+ ],
642
+ "dev": true,
643
+ "license": "MIT",
644
+ "optional": true,
645
+ "os": [
646
+ "openbsd"
647
+ ],
648
+ "engines": {
649
+ "node": ">=12"
650
+ }
651
+ },
652
+ "node_modules/@esbuild/sunos-x64": {
653
+ "version": "0.18.20",
654
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
655
+ "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
656
+ "cpu": [
657
+ "x64"
658
+ ],
659
+ "dev": true,
660
+ "license": "MIT",
661
+ "optional": true,
662
+ "os": [
663
+ "sunos"
664
+ ],
665
+ "engines": {
666
+ "node": ">=12"
667
+ }
668
+ },
669
+ "node_modules/@esbuild/win32-arm64": {
670
+ "version": "0.18.20",
671
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
672
+ "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
673
+ "cpu": [
674
+ "arm64"
675
+ ],
676
+ "dev": true,
677
+ "license": "MIT",
678
+ "optional": true,
679
+ "os": [
680
+ "win32"
681
+ ],
682
+ "engines": {
683
+ "node": ">=12"
684
+ }
685
+ },
686
+ "node_modules/@esbuild/win32-ia32": {
687
+ "version": "0.18.20",
688
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
689
+ "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
690
+ "cpu": [
691
+ "ia32"
692
+ ],
693
+ "dev": true,
694
+ "license": "MIT",
695
+ "optional": true,
696
+ "os": [
697
+ "win32"
698
+ ],
699
+ "engines": {
700
+ "node": ">=12"
701
+ }
702
+ },
703
+ "node_modules/@esbuild/win32-x64": {
704
+ "version": "0.18.20",
705
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
706
+ "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
707
+ "cpu": [
708
+ "x64"
709
+ ],
710
+ "dev": true,
711
+ "license": "MIT",
712
+ "optional": true,
713
+ "os": [
714
+ "win32"
715
+ ],
716
+ "engines": {
717
+ "node": ">=12"
718
+ }
719
+ },
720
+ "node_modules/@jridgewell/gen-mapping": {
721
+ "version": "0.3.13",
722
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
723
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
724
+ "dev": true,
725
+ "license": "MIT",
726
+ "dependencies": {
727
+ "@jridgewell/sourcemap-codec": "^1.5.0",
728
+ "@jridgewell/trace-mapping": "^0.3.24"
729
+ }
730
+ },
731
+ "node_modules/@jridgewell/remapping": {
732
+ "version": "2.3.5",
733
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
734
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
735
+ "dev": true,
736
+ "license": "MIT",
737
+ "dependencies": {
738
+ "@jridgewell/gen-mapping": "^0.3.5",
739
+ "@jridgewell/trace-mapping": "^0.3.24"
740
+ }
741
+ },
742
+ "node_modules/@jridgewell/resolve-uri": {
743
+ "version": "3.1.2",
744
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
745
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
746
+ "dev": true,
747
+ "license": "MIT",
748
+ "engines": {
749
+ "node": ">=6.0.0"
750
+ }
751
+ },
752
+ "node_modules/@jridgewell/sourcemap-codec": {
753
+ "version": "1.5.5",
754
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
755
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
756
+ "dev": true,
757
+ "license": "MIT"
758
+ },
759
+ "node_modules/@jridgewell/trace-mapping": {
760
+ "version": "0.3.31",
761
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
762
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
763
+ "dev": true,
764
+ "license": "MIT",
765
+ "dependencies": {
766
+ "@jridgewell/resolve-uri": "^3.1.0",
767
+ "@jridgewell/sourcemap-codec": "^1.4.14"
768
+ }
769
+ },
770
+ "node_modules/@mediapipe/tasks-vision": {
771
+ "version": "0.10.2",
772
+ "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.2.tgz",
773
+ "integrity": "sha512-d8Q9uRK89ZRWmED2JLI9/blpJcfdbh0iEUuMo8TgkMzNfQBY1/GC0FEJWrairTwHkxIf6Oud1vFBP+aHicWqJA==",
774
+ "license": "Apache-2.0"
775
+ },
776
+ "node_modules/@react-spring/animated": {
777
+ "version": "9.6.1",
778
+ "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz",
779
+ "integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==",
780
+ "license": "MIT",
781
+ "dependencies": {
782
+ "@react-spring/shared": "~9.6.1",
783
+ "@react-spring/types": "~9.6.1"
784
+ },
785
+ "peerDependencies": {
786
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
787
+ }
788
+ },
789
+ "node_modules/@react-spring/core": {
790
+ "version": "9.6.1",
791
+ "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz",
792
+ "integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==",
793
+ "license": "MIT",
794
+ "dependencies": {
795
+ "@react-spring/animated": "~9.6.1",
796
+ "@react-spring/rafz": "~9.6.1",
797
+ "@react-spring/shared": "~9.6.1",
798
+ "@react-spring/types": "~9.6.1"
799
+ },
800
+ "funding": {
801
+ "type": "opencollective",
802
+ "url": "https://opencollective.com/react-spring/donate"
803
+ },
804
+ "peerDependencies": {
805
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
806
+ }
807
+ },
808
+ "node_modules/@react-spring/rafz": {
809
+ "version": "9.6.1",
810
+ "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz",
811
+ "integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==",
812
+ "license": "MIT"
813
+ },
814
+ "node_modules/@react-spring/shared": {
815
+ "version": "9.6.1",
816
+ "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz",
817
+ "integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==",
818
+ "license": "MIT",
819
+ "dependencies": {
820
+ "@react-spring/rafz": "~9.6.1",
821
+ "@react-spring/types": "~9.6.1"
822
+ },
823
+ "peerDependencies": {
824
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
825
+ }
826
+ },
827
+ "node_modules/@react-spring/three": {
828
+ "version": "9.6.1",
829
+ "resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.6.1.tgz",
830
+ "integrity": "sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==",
831
+ "license": "MIT",
832
+ "dependencies": {
833
+ "@react-spring/animated": "~9.6.1",
834
+ "@react-spring/core": "~9.6.1",
835
+ "@react-spring/shared": "~9.6.1",
836
+ "@react-spring/types": "~9.6.1"
837
+ },
838
+ "peerDependencies": {
839
+ "@react-three/fiber": ">=6.0",
840
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
841
+ "three": ">=0.126"
842
+ }
843
+ },
844
+ "node_modules/@react-spring/types": {
845
+ "version": "9.6.1",
846
+ "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz",
847
+ "integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==",
848
+ "license": "MIT"
849
+ },
850
+ "node_modules/@react-three/drei": {
851
+ "version": "9.88.0",
852
+ "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-9.88.0.tgz",
853
+ "integrity": "sha512-iUTpurhyW+dLalRm/l+x9V1+/gxXriZoWppLiBemDno9nXUfSHPBn5kjMzpApH6VZvlSGhb7VNYrZvN9QUG3uA==",
854
+ "license": "MIT",
855
+ "dependencies": {
856
+ "@babel/runtime": "^7.11.2",
857
+ "@mediapipe/tasks-vision": "0.10.2",
858
+ "@react-spring/three": "~9.6.1",
859
+ "@use-gesture/react": "^10.2.24",
860
+ "camera-controls": "^2.4.2",
861
+ "cross-env": "^7.0.3",
862
+ "detect-gpu": "^5.0.28",
863
+ "glsl-noise": "^0.0.0",
864
+ "lodash.clamp": "^4.0.3",
865
+ "lodash.omit": "^4.5.0",
866
+ "lodash.pick": "^4.4.0",
867
+ "maath": "^0.9.0",
868
+ "meshline": "^3.1.6",
869
+ "react-composer": "^5.0.3",
870
+ "react-merge-refs": "^1.1.0",
871
+ "stats-gl": "^1.0.4",
872
+ "stats.js": "^0.17.0",
873
+ "suspend-react": "^0.1.3",
874
+ "three-mesh-bvh": "^0.6.7",
875
+ "three-stdlib": "^2.26.6",
876
+ "troika-three-text": "^0.47.2",
877
+ "utility-types": "^3.10.0",
878
+ "uuid": "^9.0.1",
879
+ "zustand": "^3.5.13"
880
+ },
881
+ "peerDependencies": {
882
+ "@react-three/fiber": ">=8.0",
883
+ "react": ">=18.0",
884
+ "react-dom": ">=18.0",
885
+ "three": ">=0.137"
886
+ },
887
+ "peerDependenciesMeta": {
888
+ "react-dom": {
889
+ "optional": true
890
+ }
891
+ }
892
+ },
893
+ "node_modules/@react-three/fiber": {
894
+ "version": "8.13.0",
895
+ "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.13.0.tgz",
896
+ "integrity": "sha512-hPFzFNgikEMyEbL+NpSA7q+UWZxInrrkJldWaCR2w34Fwf20x9p68bsyN0/yn9oM2VlWoJcJjR8hw1tN9AxHuA==",
897
+ "license": "MIT",
898
+ "dependencies": {
899
+ "@babel/runtime": "^7.17.8",
900
+ "@types/react-reconciler": "^0.26.7",
901
+ "its-fine": "^1.0.6",
902
+ "react-reconciler": "^0.27.0",
903
+ "react-use-measure": "^2.1.1",
904
+ "scheduler": "^0.21.0",
905
+ "suspend-react": "^0.0.8",
906
+ "zustand": "^3.7.1"
907
+ },
908
+ "peerDependencies": {
909
+ "expo": ">=43.0",
910
+ "expo-asset": ">=8.4",
911
+ "expo-gl": ">=11.0",
912
+ "react": ">=18.0",
913
+ "react-dom": ">=18.0",
914
+ "react-native": ">=0.64",
915
+ "three": ">=0.133"
916
+ },
917
+ "peerDependenciesMeta": {
918
+ "expo": {
919
+ "optional": true
920
+ },
921
+ "expo-asset": {
922
+ "optional": true
923
+ },
924
+ "expo-gl": {
925
+ "optional": true
926
+ },
927
+ "react-dom": {
928
+ "optional": true
929
+ },
930
+ "react-native": {
931
+ "optional": true
932
+ }
933
+ }
934
+ },
935
+ "node_modules/@react-three/fiber/node_modules/scheduler": {
936
+ "version": "0.21.0",
937
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
938
+ "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
939
+ "license": "MIT",
940
+ "dependencies": {
941
+ "loose-envify": "^1.1.0"
942
+ }
943
+ },
944
+ "node_modules/@react-three/fiber/node_modules/suspend-react": {
945
+ "version": "0.0.8",
946
+ "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz",
947
+ "integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==",
948
+ "license": "MIT",
949
+ "peerDependencies": {
950
+ "react": ">=17.0"
951
+ }
952
+ },
953
+ "node_modules/@rolldown/pluginutils": {
954
+ "version": "1.0.0-beta.27",
955
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
956
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
957
+ "dev": true,
958
+ "license": "MIT"
959
+ },
960
+ "node_modules/@tweenjs/tween.js": {
961
+ "version": "23.1.3",
962
+ "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
963
+ "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
964
+ "license": "MIT",
965
+ "peer": true
966
+ },
967
+ "node_modules/@types/babel__core": {
968
+ "version": "7.20.5",
969
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
970
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
971
+ "dev": true,
972
+ "license": "MIT",
973
+ "dependencies": {
974
+ "@babel/parser": "^7.20.7",
975
+ "@babel/types": "^7.20.7",
976
+ "@types/babel__generator": "*",
977
+ "@types/babel__template": "*",
978
+ "@types/babel__traverse": "*"
979
+ }
980
+ },
981
+ "node_modules/@types/babel__generator": {
982
+ "version": "7.27.0",
983
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
984
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
985
+ "dev": true,
986
+ "license": "MIT",
987
+ "dependencies": {
988
+ "@babel/types": "^7.0.0"
989
+ }
990
+ },
991
+ "node_modules/@types/babel__template": {
992
+ "version": "7.4.4",
993
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
994
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
995
+ "dev": true,
996
+ "license": "MIT",
997
+ "dependencies": {
998
+ "@babel/parser": "^7.1.0",
999
+ "@babel/types": "^7.0.0"
1000
+ }
1001
+ },
1002
+ "node_modules/@types/babel__traverse": {
1003
+ "version": "7.28.0",
1004
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
1005
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
1006
+ "dev": true,
1007
+ "license": "MIT",
1008
+ "dependencies": {
1009
+ "@babel/types": "^7.28.2"
1010
+ }
1011
+ },
1012
+ "node_modules/@types/draco3d": {
1013
+ "version": "1.4.10",
1014
+ "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
1015
+ "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==",
1016
+ "license": "MIT"
1017
+ },
1018
+ "node_modules/@types/offscreencanvas": {
1019
+ "version": "2019.7.3",
1020
+ "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
1021
+ "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==",
1022
+ "license": "MIT"
1023
+ },
1024
+ "node_modules/@types/prop-types": {
1025
+ "version": "15.7.15",
1026
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
1027
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
1028
+ "license": "MIT"
1029
+ },
1030
+ "node_modules/@types/react": {
1031
+ "version": "18.3.29",
1032
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.29.tgz",
1033
+ "integrity": "sha512-ch0qJdr2JY0r04NXSprbK6TXOgnaJ1Tz23fm5W+z0/CBah6BSBc3n96h7K9GOtwh0HrilNWHIBzE1Ko4Dcw/Wg==",
1034
+ "license": "MIT",
1035
+ "dependencies": {
1036
+ "@types/prop-types": "*",
1037
+ "csstype": "^3.2.2"
1038
+ }
1039
+ },
1040
+ "node_modules/@types/react-dom": {
1041
+ "version": "18.3.7",
1042
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
1043
+ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
1044
+ "dev": true,
1045
+ "license": "MIT",
1046
+ "peerDependencies": {
1047
+ "@types/react": "^18.0.0"
1048
+ }
1049
+ },
1050
+ "node_modules/@types/react-reconciler": {
1051
+ "version": "0.26.7",
1052
+ "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
1053
+ "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
1054
+ "license": "MIT",
1055
+ "dependencies": {
1056
+ "@types/react": "*"
1057
+ }
1058
+ },
1059
+ "node_modules/@types/stats.js": {
1060
+ "version": "0.17.4",
1061
+ "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
1062
+ "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
1063
+ "license": "MIT",
1064
+ "peer": true
1065
+ },
1066
+ "node_modules/@types/three": {
1067
+ "version": "0.184.1",
1068
+ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.184.1.tgz",
1069
+ "integrity": "sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA==",
1070
+ "license": "MIT",
1071
+ "peer": true,
1072
+ "dependencies": {
1073
+ "@dimforge/rapier3d-compat": "~0.12.0",
1074
+ "@tweenjs/tween.js": "~23.1.3",
1075
+ "@types/stats.js": "*",
1076
+ "@types/webxr": ">=0.5.17",
1077
+ "fflate": "~0.8.2",
1078
+ "meshoptimizer": "~1.1.1"
1079
+ }
1080
+ },
1081
+ "node_modules/@types/webxr": {
1082
+ "version": "0.5.24",
1083
+ "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
1084
+ "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
1085
+ "license": "MIT"
1086
+ },
1087
+ "node_modules/@use-gesture/core": {
1088
+ "version": "10.3.1",
1089
+ "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz",
1090
+ "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==",
1091
+ "license": "MIT"
1092
+ },
1093
+ "node_modules/@use-gesture/react": {
1094
+ "version": "10.3.1",
1095
+ "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz",
1096
+ "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==",
1097
+ "license": "MIT",
1098
+ "dependencies": {
1099
+ "@use-gesture/core": "10.3.1"
1100
+ },
1101
+ "peerDependencies": {
1102
+ "react": ">= 16.8.0"
1103
+ }
1104
+ },
1105
+ "node_modules/@vitejs/plugin-react": {
1106
+ "version": "4.7.0",
1107
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
1108
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
1109
+ "dev": true,
1110
+ "license": "MIT",
1111
+ "dependencies": {
1112
+ "@babel/core": "^7.28.0",
1113
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
1114
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
1115
+ "@rolldown/pluginutils": "1.0.0-beta.27",
1116
+ "@types/babel__core": "^7.20.5",
1117
+ "react-refresh": "^0.17.0"
1118
+ },
1119
+ "engines": {
1120
+ "node": "^14.18.0 || >=16.0.0"
1121
+ },
1122
+ "peerDependencies": {
1123
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1124
+ }
1125
+ },
1126
+ "node_modules/baseline-browser-mapping": {
1127
+ "version": "2.10.31",
1128
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.31.tgz",
1129
+ "integrity": "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==",
1130
+ "dev": true,
1131
+ "license": "Apache-2.0",
1132
+ "bin": {
1133
+ "baseline-browser-mapping": "dist/cli.cjs"
1134
+ },
1135
+ "engines": {
1136
+ "node": ">=6.0.0"
1137
+ }
1138
+ },
1139
+ "node_modules/bidi-js": {
1140
+ "version": "1.0.3",
1141
+ "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
1142
+ "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
1143
+ "license": "MIT",
1144
+ "dependencies": {
1145
+ "require-from-string": "^2.0.2"
1146
+ }
1147
+ },
1148
+ "node_modules/browserslist": {
1149
+ "version": "4.28.2",
1150
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
1151
+ "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
1152
+ "dev": true,
1153
+ "funding": [
1154
+ {
1155
+ "type": "opencollective",
1156
+ "url": "https://opencollective.com/browserslist"
1157
+ },
1158
+ {
1159
+ "type": "tidelift",
1160
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
1161
+ },
1162
+ {
1163
+ "type": "github",
1164
+ "url": "https://github.com/sponsors/ai"
1165
+ }
1166
+ ],
1167
+ "license": "MIT",
1168
+ "dependencies": {
1169
+ "baseline-browser-mapping": "^2.10.12",
1170
+ "caniuse-lite": "^1.0.30001782",
1171
+ "electron-to-chromium": "^1.5.328",
1172
+ "node-releases": "^2.0.36",
1173
+ "update-browserslist-db": "^1.2.3"
1174
+ },
1175
+ "bin": {
1176
+ "browserslist": "cli.js"
1177
+ },
1178
+ "engines": {
1179
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1180
+ }
1181
+ },
1182
+ "node_modules/camera-controls": {
1183
+ "version": "2.10.1",
1184
+ "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.10.1.tgz",
1185
+ "integrity": "sha512-KnaKdcvkBJ1Irbrzl8XD6WtZltkRjp869Jx8c0ujs9K+9WD+1D7ryBsCiVqJYUqt6i/HR5FxT7RLASieUD+Q5w==",
1186
+ "license": "MIT",
1187
+ "peerDependencies": {
1188
+ "three": ">=0.126.1"
1189
+ }
1190
+ },
1191
+ "node_modules/caniuse-lite": {
1192
+ "version": "1.0.30001793",
1193
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz",
1194
+ "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==",
1195
+ "dev": true,
1196
+ "funding": [
1197
+ {
1198
+ "type": "opencollective",
1199
+ "url": "https://opencollective.com/browserslist"
1200
+ },
1201
+ {
1202
+ "type": "tidelift",
1203
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
1204
+ },
1205
+ {
1206
+ "type": "github",
1207
+ "url": "https://github.com/sponsors/ai"
1208
+ }
1209
+ ],
1210
+ "license": "CC-BY-4.0"
1211
+ },
1212
+ "node_modules/clsx": {
1213
+ "version": "2.1.1",
1214
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
1215
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
1216
+ "license": "MIT",
1217
+ "engines": {
1218
+ "node": ">=6"
1219
+ }
1220
+ },
1221
+ "node_modules/convert-source-map": {
1222
+ "version": "2.0.0",
1223
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
1224
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
1225
+ "dev": true,
1226
+ "license": "MIT"
1227
+ },
1228
+ "node_modules/cross-env": {
1229
+ "version": "7.0.3",
1230
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
1231
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
1232
+ "license": "MIT",
1233
+ "dependencies": {
1234
+ "cross-spawn": "^7.0.1"
1235
+ },
1236
+ "bin": {
1237
+ "cross-env": "src/bin/cross-env.js",
1238
+ "cross-env-shell": "src/bin/cross-env-shell.js"
1239
+ },
1240
+ "engines": {
1241
+ "node": ">=10.14",
1242
+ "npm": ">=6",
1243
+ "yarn": ">=1"
1244
+ }
1245
+ },
1246
+ "node_modules/cross-spawn": {
1247
+ "version": "7.0.6",
1248
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
1249
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
1250
+ "license": "MIT",
1251
+ "dependencies": {
1252
+ "path-key": "^3.1.0",
1253
+ "shebang-command": "^2.0.0",
1254
+ "which": "^2.0.1"
1255
+ },
1256
+ "engines": {
1257
+ "node": ">= 8"
1258
+ }
1259
+ },
1260
+ "node_modules/csstype": {
1261
+ "version": "3.2.3",
1262
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
1263
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
1264
+ "license": "MIT"
1265
+ },
1266
+ "node_modules/debug": {
1267
+ "version": "4.4.3",
1268
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
1269
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
1270
+ "dev": true,
1271
+ "license": "MIT",
1272
+ "dependencies": {
1273
+ "ms": "^2.1.3"
1274
+ },
1275
+ "engines": {
1276
+ "node": ">=6.0"
1277
+ },
1278
+ "peerDependenciesMeta": {
1279
+ "supports-color": {
1280
+ "optional": true
1281
+ }
1282
+ }
1283
+ },
1284
+ "node_modules/detect-gpu": {
1285
+ "version": "5.0.70",
1286
+ "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz",
1287
+ "integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==",
1288
+ "license": "MIT",
1289
+ "dependencies": {
1290
+ "webgl-constants": "^1.1.1"
1291
+ }
1292
+ },
1293
+ "node_modules/draco3d": {
1294
+ "version": "1.5.7",
1295
+ "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz",
1296
+ "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==",
1297
+ "license": "Apache-2.0"
1298
+ },
1299
+ "node_modules/electron-to-chromium": {
1300
+ "version": "1.5.360",
1301
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.360.tgz",
1302
+ "integrity": "sha512-GkcBt6YYAw9SxFWn+xVar4cLVGlXVuswwtRLBozi2zp0GjXs4ZnOrqV4zbXzg35n7w81hCkyJNYicgXlVHAmBA==",
1303
+ "dev": true,
1304
+ "license": "ISC"
1305
+ },
1306
+ "node_modules/esbuild": {
1307
+ "version": "0.18.20",
1308
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
1309
+ "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
1310
+ "dev": true,
1311
+ "hasInstallScript": true,
1312
+ "license": "MIT",
1313
+ "bin": {
1314
+ "esbuild": "bin/esbuild"
1315
+ },
1316
+ "engines": {
1317
+ "node": ">=12"
1318
+ },
1319
+ "optionalDependencies": {
1320
+ "@esbuild/android-arm": "0.18.20",
1321
+ "@esbuild/android-arm64": "0.18.20",
1322
+ "@esbuild/android-x64": "0.18.20",
1323
+ "@esbuild/darwin-arm64": "0.18.20",
1324
+ "@esbuild/darwin-x64": "0.18.20",
1325
+ "@esbuild/freebsd-arm64": "0.18.20",
1326
+ "@esbuild/freebsd-x64": "0.18.20",
1327
+ "@esbuild/linux-arm": "0.18.20",
1328
+ "@esbuild/linux-arm64": "0.18.20",
1329
+ "@esbuild/linux-ia32": "0.18.20",
1330
+ "@esbuild/linux-loong64": "0.18.20",
1331
+ "@esbuild/linux-mips64el": "0.18.20",
1332
+ "@esbuild/linux-ppc64": "0.18.20",
1333
+ "@esbuild/linux-riscv64": "0.18.20",
1334
+ "@esbuild/linux-s390x": "0.18.20",
1335
+ "@esbuild/linux-x64": "0.18.20",
1336
+ "@esbuild/netbsd-x64": "0.18.20",
1337
+ "@esbuild/openbsd-x64": "0.18.20",
1338
+ "@esbuild/sunos-x64": "0.18.20",
1339
+ "@esbuild/win32-arm64": "0.18.20",
1340
+ "@esbuild/win32-ia32": "0.18.20",
1341
+ "@esbuild/win32-x64": "0.18.20"
1342
+ }
1343
+ },
1344
+ "node_modules/escalade": {
1345
+ "version": "3.2.0",
1346
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
1347
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
1348
+ "dev": true,
1349
+ "license": "MIT",
1350
+ "engines": {
1351
+ "node": ">=6"
1352
+ }
1353
+ },
1354
+ "node_modules/fflate": {
1355
+ "version": "0.8.3",
1356
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
1357
+ "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
1358
+ "license": "MIT",
1359
+ "peer": true
1360
+ },
1361
+ "node_modules/framer-motion": {
1362
+ "version": "10.18.0",
1363
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz",
1364
+ "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==",
1365
+ "license": "MIT",
1366
+ "dependencies": {
1367
+ "tslib": "^2.4.0"
1368
+ },
1369
+ "optionalDependencies": {
1370
+ "@emotion/is-prop-valid": "^0.8.2"
1371
+ },
1372
+ "peerDependencies": {
1373
+ "react": "^18.0.0",
1374
+ "react-dom": "^18.0.0"
1375
+ },
1376
+ "peerDependenciesMeta": {
1377
+ "react": {
1378
+ "optional": true
1379
+ },
1380
+ "react-dom": {
1381
+ "optional": true
1382
+ }
1383
+ }
1384
+ },
1385
+ "node_modules/fsevents": {
1386
+ "version": "2.3.3",
1387
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1388
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1389
+ "dev": true,
1390
+ "hasInstallScript": true,
1391
+ "license": "MIT",
1392
+ "optional": true,
1393
+ "os": [
1394
+ "darwin"
1395
+ ],
1396
+ "engines": {
1397
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1398
+ }
1399
+ },
1400
+ "node_modules/gensync": {
1401
+ "version": "1.0.0-beta.2",
1402
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
1403
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
1404
+ "dev": true,
1405
+ "license": "MIT",
1406
+ "engines": {
1407
+ "node": ">=6.9.0"
1408
+ }
1409
+ },
1410
+ "node_modules/glsl-noise": {
1411
+ "version": "0.0.0",
1412
+ "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz",
1413
+ "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==",
1414
+ "license": "MIT"
1415
+ },
1416
+ "node_modules/gsap": {
1417
+ "version": "3.15.0",
1418
+ "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz",
1419
+ "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==",
1420
+ "license": "Standard 'no charge' license: https://gsap.com/standard-license."
1421
+ },
1422
+ "node_modules/isexe": {
1423
+ "version": "2.0.0",
1424
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
1425
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
1426
+ "license": "ISC"
1427
+ },
1428
+ "node_modules/its-fine": {
1429
+ "version": "1.2.5",
1430
+ "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.2.5.tgz",
1431
+ "integrity": "sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==",
1432
+ "license": "MIT",
1433
+ "dependencies": {
1434
+ "@types/react-reconciler": "^0.28.0"
1435
+ },
1436
+ "peerDependencies": {
1437
+ "react": ">=18.0"
1438
+ }
1439
+ },
1440
+ "node_modules/its-fine/node_modules/@types/react-reconciler": {
1441
+ "version": "0.28.9",
1442
+ "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz",
1443
+ "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==",
1444
+ "license": "MIT",
1445
+ "peerDependencies": {
1446
+ "@types/react": "*"
1447
+ }
1448
+ },
1449
+ "node_modules/js-tokens": {
1450
+ "version": "4.0.0",
1451
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1452
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
1453
+ "license": "MIT"
1454
+ },
1455
+ "node_modules/jsesc": {
1456
+ "version": "3.1.0",
1457
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
1458
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
1459
+ "dev": true,
1460
+ "license": "MIT",
1461
+ "bin": {
1462
+ "jsesc": "bin/jsesc"
1463
+ },
1464
+ "engines": {
1465
+ "node": ">=6"
1466
+ }
1467
+ },
1468
+ "node_modules/json5": {
1469
+ "version": "2.2.3",
1470
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
1471
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
1472
+ "dev": true,
1473
+ "license": "MIT",
1474
+ "bin": {
1475
+ "json5": "lib/cli.js"
1476
+ },
1477
+ "engines": {
1478
+ "node": ">=6"
1479
+ }
1480
+ },
1481
+ "node_modules/lodash.clamp": {
1482
+ "version": "4.0.3",
1483
+ "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz",
1484
+ "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==",
1485
+ "license": "MIT"
1486
+ },
1487
+ "node_modules/lodash.omit": {
1488
+ "version": "4.18.0",
1489
+ "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.18.0.tgz",
1490
+ "integrity": "sha512-hZXIupXdHtocTnvIJ2aCd2vxKYtxex6gbiGuPvgBRnFQO9yu3AtmDAbVuCXcSsQx3INo/1g71OktlFFA/ES8Xg==",
1491
+ "license": "MIT"
1492
+ },
1493
+ "node_modules/lodash.pick": {
1494
+ "version": "4.4.0",
1495
+ "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
1496
+ "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==",
1497
+ "deprecated": "This package is deprecated. Use destructuring assignment syntax instead.",
1498
+ "license": "MIT"
1499
+ },
1500
+ "node_modules/loose-envify": {
1501
+ "version": "1.4.0",
1502
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
1503
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
1504
+ "license": "MIT",
1505
+ "dependencies": {
1506
+ "js-tokens": "^3.0.0 || ^4.0.0"
1507
+ },
1508
+ "bin": {
1509
+ "loose-envify": "cli.js"
1510
+ }
1511
+ },
1512
+ "node_modules/lru-cache": {
1513
+ "version": "5.1.1",
1514
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
1515
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
1516
+ "dev": true,
1517
+ "license": "ISC",
1518
+ "dependencies": {
1519
+ "yallist": "^3.0.2"
1520
+ }
1521
+ },
1522
+ "node_modules/lucide-react": {
1523
+ "version": "0.263.1",
1524
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.263.1.tgz",
1525
+ "integrity": "sha512-keqxAx97PlaEN89PXZ6ki1N8nRjGWtDa4021GFYLNj0RgruM5odbpl8GHTExj0hhPq3sF6Up0gnxt6TSHu+ovw==",
1526
+ "license": "ISC",
1527
+ "peerDependencies": {
1528
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0"
1529
+ }
1530
+ },
1531
+ "node_modules/maath": {
1532
+ "version": "0.9.0",
1533
+ "resolved": "https://registry.npmjs.org/maath/-/maath-0.9.0.tgz",
1534
+ "integrity": "sha512-aAR8hoUqPxlsU8VOxkS9y37jhUzdUxM017NpCuxFU1Gk+nMaZASZxymZrV8LRSHzRk/watlbfyNKu6XPUhCFrQ==",
1535
+ "license": "MIT",
1536
+ "peerDependencies": {
1537
+ "@types/three": ">=0.144.0",
1538
+ "three": ">=0.144.0"
1539
+ }
1540
+ },
1541
+ "node_modules/meshline": {
1542
+ "version": "3.3.1",
1543
+ "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz",
1544
+ "integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==",
1545
+ "license": "MIT",
1546
+ "peerDependencies": {
1547
+ "three": ">=0.137"
1548
+ }
1549
+ },
1550
+ "node_modules/meshoptimizer": {
1551
+ "version": "1.1.1",
1552
+ "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
1553
+ "integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==",
1554
+ "license": "MIT",
1555
+ "peer": true
1556
+ },
1557
+ "node_modules/ms": {
1558
+ "version": "2.1.3",
1559
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1560
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1561
+ "dev": true,
1562
+ "license": "MIT"
1563
+ },
1564
+ "node_modules/nanoid": {
1565
+ "version": "3.3.12",
1566
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
1567
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
1568
+ "dev": true,
1569
+ "funding": [
1570
+ {
1571
+ "type": "github",
1572
+ "url": "https://github.com/sponsors/ai"
1573
+ }
1574
+ ],
1575
+ "license": "MIT",
1576
+ "bin": {
1577
+ "nanoid": "bin/nanoid.cjs"
1578
+ },
1579
+ "engines": {
1580
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1581
+ }
1582
+ },
1583
+ "node_modules/node-releases": {
1584
+ "version": "2.0.45",
1585
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.45.tgz",
1586
+ "integrity": "sha512-iIbHXV9eBB2nB0wa7oTsrrXq+qQt+9SIlx9AX3T96YgobtEQfis5n6TJ6vV+3QP8DwdriEAcGhARaFCu37peBg==",
1587
+ "dev": true,
1588
+ "license": "MIT",
1589
+ "engines": {
1590
+ "node": ">=18"
1591
+ }
1592
+ },
1593
+ "node_modules/object-assign": {
1594
+ "version": "4.1.1",
1595
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1596
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1597
+ "license": "MIT",
1598
+ "engines": {
1599
+ "node": ">=0.10.0"
1600
+ }
1601
+ },
1602
+ "node_modules/path-key": {
1603
+ "version": "3.1.1",
1604
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
1605
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
1606
+ "license": "MIT",
1607
+ "engines": {
1608
+ "node": ">=8"
1609
+ }
1610
+ },
1611
+ "node_modules/picocolors": {
1612
+ "version": "1.1.1",
1613
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1614
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1615
+ "dev": true,
1616
+ "license": "ISC"
1617
+ },
1618
+ "node_modules/postcss": {
1619
+ "version": "8.5.15",
1620
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
1621
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
1622
+ "dev": true,
1623
+ "funding": [
1624
+ {
1625
+ "type": "opencollective",
1626
+ "url": "https://opencollective.com/postcss/"
1627
+ },
1628
+ {
1629
+ "type": "tidelift",
1630
+ "url": "https://tidelift.com/funding/github/npm/postcss"
1631
+ },
1632
+ {
1633
+ "type": "github",
1634
+ "url": "https://github.com/sponsors/ai"
1635
+ }
1636
+ ],
1637
+ "license": "MIT",
1638
+ "dependencies": {
1639
+ "nanoid": "^3.3.12",
1640
+ "picocolors": "^1.1.1",
1641
+ "source-map-js": "^1.2.1"
1642
+ },
1643
+ "engines": {
1644
+ "node": "^10 || ^12 || >=14"
1645
+ }
1646
+ },
1647
+ "node_modules/potpack": {
1648
+ "version": "1.0.2",
1649
+ "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
1650
+ "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==",
1651
+ "license": "ISC"
1652
+ },
1653
+ "node_modules/prop-types": {
1654
+ "version": "15.8.1",
1655
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
1656
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
1657
+ "license": "MIT",
1658
+ "dependencies": {
1659
+ "loose-envify": "^1.4.0",
1660
+ "object-assign": "^4.1.1",
1661
+ "react-is": "^16.13.1"
1662
+ }
1663
+ },
1664
+ "node_modules/react": {
1665
+ "version": "18.3.1",
1666
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
1667
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
1668
+ "license": "MIT",
1669
+ "dependencies": {
1670
+ "loose-envify": "^1.1.0"
1671
+ },
1672
+ "engines": {
1673
+ "node": ">=0.10.0"
1674
+ }
1675
+ },
1676
+ "node_modules/react-composer": {
1677
+ "version": "5.0.3",
1678
+ "resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz",
1679
+ "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==",
1680
+ "license": "MIT",
1681
+ "dependencies": {
1682
+ "prop-types": "^15.6.0"
1683
+ },
1684
+ "peerDependencies": {
1685
+ "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
1686
+ }
1687
+ },
1688
+ "node_modules/react-dom": {
1689
+ "version": "18.3.1",
1690
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
1691
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
1692
+ "license": "MIT",
1693
+ "dependencies": {
1694
+ "loose-envify": "^1.1.0",
1695
+ "scheduler": "^0.23.2"
1696
+ },
1697
+ "peerDependencies": {
1698
+ "react": "^18.3.1"
1699
+ }
1700
+ },
1701
+ "node_modules/react-is": {
1702
+ "version": "16.13.1",
1703
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
1704
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
1705
+ "license": "MIT"
1706
+ },
1707
+ "node_modules/react-merge-refs": {
1708
+ "version": "1.1.0",
1709
+ "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",
1710
+ "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==",
1711
+ "license": "MIT",
1712
+ "funding": {
1713
+ "type": "github",
1714
+ "url": "https://github.com/sponsors/gregberge"
1715
+ }
1716
+ },
1717
+ "node_modules/react-reconciler": {
1718
+ "version": "0.27.0",
1719
+ "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
1720
+ "integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
1721
+ "license": "MIT",
1722
+ "dependencies": {
1723
+ "loose-envify": "^1.1.0",
1724
+ "scheduler": "^0.21.0"
1725
+ },
1726
+ "engines": {
1727
+ "node": ">=0.10.0"
1728
+ },
1729
+ "peerDependencies": {
1730
+ "react": "^18.0.0"
1731
+ }
1732
+ },
1733
+ "node_modules/react-reconciler/node_modules/scheduler": {
1734
+ "version": "0.21.0",
1735
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
1736
+ "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
1737
+ "license": "MIT",
1738
+ "dependencies": {
1739
+ "loose-envify": "^1.1.0"
1740
+ }
1741
+ },
1742
+ "node_modules/react-refresh": {
1743
+ "version": "0.17.0",
1744
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
1745
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
1746
+ "dev": true,
1747
+ "license": "MIT",
1748
+ "engines": {
1749
+ "node": ">=0.10.0"
1750
+ }
1751
+ },
1752
+ "node_modules/react-use-measure": {
1753
+ "version": "2.1.7",
1754
+ "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
1755
+ "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==",
1756
+ "license": "MIT",
1757
+ "peerDependencies": {
1758
+ "react": ">=16.13",
1759
+ "react-dom": ">=16.13"
1760
+ },
1761
+ "peerDependenciesMeta": {
1762
+ "react-dom": {
1763
+ "optional": true
1764
+ }
1765
+ }
1766
+ },
1767
+ "node_modules/require-from-string": {
1768
+ "version": "2.0.2",
1769
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
1770
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
1771
+ "license": "MIT",
1772
+ "engines": {
1773
+ "node": ">=0.10.0"
1774
+ }
1775
+ },
1776
+ "node_modules/rollup": {
1777
+ "version": "3.30.0",
1778
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.30.0.tgz",
1779
+ "integrity": "sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==",
1780
+ "dev": true,
1781
+ "license": "MIT",
1782
+ "bin": {
1783
+ "rollup": "dist/bin/rollup"
1784
+ },
1785
+ "engines": {
1786
+ "node": ">=14.18.0",
1787
+ "npm": ">=8.0.0"
1788
+ },
1789
+ "optionalDependencies": {
1790
+ "fsevents": "~2.3.2"
1791
+ }
1792
+ },
1793
+ "node_modules/scheduler": {
1794
+ "version": "0.23.2",
1795
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
1796
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
1797
+ "license": "MIT",
1798
+ "dependencies": {
1799
+ "loose-envify": "^1.1.0"
1800
+ }
1801
+ },
1802
+ "node_modules/semver": {
1803
+ "version": "6.3.1",
1804
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
1805
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
1806
+ "dev": true,
1807
+ "license": "ISC",
1808
+ "bin": {
1809
+ "semver": "bin/semver.js"
1810
+ }
1811
+ },
1812
+ "node_modules/shebang-command": {
1813
+ "version": "2.0.0",
1814
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
1815
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
1816
+ "license": "MIT",
1817
+ "dependencies": {
1818
+ "shebang-regex": "^3.0.0"
1819
+ },
1820
+ "engines": {
1821
+ "node": ">=8"
1822
+ }
1823
+ },
1824
+ "node_modules/shebang-regex": {
1825
+ "version": "3.0.0",
1826
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
1827
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
1828
+ "license": "MIT",
1829
+ "engines": {
1830
+ "node": ">=8"
1831
+ }
1832
+ },
1833
+ "node_modules/source-map-js": {
1834
+ "version": "1.2.1",
1835
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1836
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1837
+ "dev": true,
1838
+ "license": "BSD-3-Clause",
1839
+ "engines": {
1840
+ "node": ">=0.10.0"
1841
+ }
1842
+ },
1843
+ "node_modules/stats-gl": {
1844
+ "version": "1.0.7",
1845
+ "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-1.0.7.tgz",
1846
+ "integrity": "sha512-vZI82CjefSxLC1bjw36z28v0+QE9rJKymGlXtfWu+ipW70ZEAwa4EbO4LxluAfLfpqiaAS04NzpYBRLDeAwYWQ==",
1847
+ "license": "MIT"
1848
+ },
1849
+ "node_modules/stats.js": {
1850
+ "version": "0.17.0",
1851
+ "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz",
1852
+ "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==",
1853
+ "license": "MIT"
1854
+ },
1855
+ "node_modules/suspend-react": {
1856
+ "version": "0.1.3",
1857
+ "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz",
1858
+ "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==",
1859
+ "license": "MIT",
1860
+ "peerDependencies": {
1861
+ "react": ">=17.0"
1862
+ }
1863
+ },
1864
+ "node_modules/tailwind-merge": {
1865
+ "version": "3.6.0",
1866
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz",
1867
+ "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==",
1868
+ "license": "MIT",
1869
+ "funding": {
1870
+ "type": "github",
1871
+ "url": "https://github.com/sponsors/dcastil"
1872
+ }
1873
+ },
1874
+ "node_modules/three": {
1875
+ "version": "0.159.0",
1876
+ "resolved": "https://registry.npmjs.org/three/-/three-0.159.0.tgz",
1877
+ "integrity": "sha512-eCmhlLGbBgucuo4VEA9IO3Qpc7dh8Bd4VKzr7WfW4+8hMcIfoAVi1ev0pJYN9PTTsCslbcKgBwr2wNZ1EvLInA==",
1878
+ "license": "MIT"
1879
+ },
1880
+ "node_modules/three-mesh-bvh": {
1881
+ "version": "0.6.8",
1882
+ "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.6.8.tgz",
1883
+ "integrity": "sha512-EGebF9DZx1S8+7OZYNNTT80GXJZVf+UYXD/HyTg/e2kR/ApofIFfUS4ZzIHNnUVIadpnLSzM4n96wX+l7GMbnQ==",
1884
+ "license": "MIT",
1885
+ "peerDependencies": {
1886
+ "three": ">= 0.151.0"
1887
+ }
1888
+ },
1889
+ "node_modules/three-stdlib": {
1890
+ "version": "2.36.1",
1891
+ "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz",
1892
+ "integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==",
1893
+ "license": "MIT",
1894
+ "dependencies": {
1895
+ "@types/draco3d": "^1.4.0",
1896
+ "@types/offscreencanvas": "^2019.6.4",
1897
+ "@types/webxr": "^0.5.2",
1898
+ "draco3d": "^1.4.1",
1899
+ "fflate": "^0.6.9",
1900
+ "potpack": "^1.0.1"
1901
+ },
1902
+ "peerDependencies": {
1903
+ "three": ">=0.128.0"
1904
+ }
1905
+ },
1906
+ "node_modules/three-stdlib/node_modules/fflate": {
1907
+ "version": "0.6.10",
1908
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
1909
+ "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==",
1910
+ "license": "MIT"
1911
+ },
1912
+ "node_modules/troika-three-text": {
1913
+ "version": "0.47.2",
1914
+ "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.47.2.tgz",
1915
+ "integrity": "sha512-qylT0F+U7xGs+/PEf3ujBdJMYWbn0Qci0kLqI5BJG2kW1wdg4T1XSxneypnF05DxFqJhEzuaOR9S2SjiyknMng==",
1916
+ "license": "MIT",
1917
+ "dependencies": {
1918
+ "bidi-js": "^1.0.2",
1919
+ "troika-three-utils": "^0.47.2",
1920
+ "troika-worker-utils": "^0.47.2",
1921
+ "webgl-sdf-generator": "1.1.1"
1922
+ },
1923
+ "peerDependencies": {
1924
+ "three": ">=0.125.0"
1925
+ }
1926
+ },
1927
+ "node_modules/troika-three-utils": {
1928
+ "version": "0.47.2",
1929
+ "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.47.2.tgz",
1930
+ "integrity": "sha512-/28plhCxfKtH7MSxEGx8e3b/OXU5A0xlwl+Sbdp0H8FXUHKZDoksduEKmjQayXYtxAyuUiCRunYIv/8Vi7aiyg==",
1931
+ "license": "MIT",
1932
+ "peerDependencies": {
1933
+ "three": ">=0.125.0"
1934
+ }
1935
+ },
1936
+ "node_modules/troika-worker-utils": {
1937
+ "version": "0.47.2",
1938
+ "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.47.2.tgz",
1939
+ "integrity": "sha512-mzss4MeyzUkYBppn4x5cdAqrhBHFEuVmMMgLMTyFV23x6GvQMyo+/R5E5Lsbrt7WSt5RfvewjcwD1DChRTA9lA==",
1940
+ "license": "MIT"
1941
+ },
1942
+ "node_modules/tslib": {
1943
+ "version": "2.8.1",
1944
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
1945
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
1946
+ "license": "0BSD"
1947
+ },
1948
+ "node_modules/typescript": {
1949
+ "version": "5.9.3",
1950
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
1951
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
1952
+ "dev": true,
1953
+ "license": "Apache-2.0",
1954
+ "bin": {
1955
+ "tsc": "bin/tsc",
1956
+ "tsserver": "bin/tsserver"
1957
+ },
1958
+ "engines": {
1959
+ "node": ">=14.17"
1960
+ }
1961
+ },
1962
+ "node_modules/update-browserslist-db": {
1963
+ "version": "1.2.3",
1964
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
1965
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
1966
+ "dev": true,
1967
+ "funding": [
1968
+ {
1969
+ "type": "opencollective",
1970
+ "url": "https://opencollective.com/browserslist"
1971
+ },
1972
+ {
1973
+ "type": "tidelift",
1974
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
1975
+ },
1976
+ {
1977
+ "type": "github",
1978
+ "url": "https://github.com/sponsors/ai"
1979
+ }
1980
+ ],
1981
+ "license": "MIT",
1982
+ "dependencies": {
1983
+ "escalade": "^3.2.0",
1984
+ "picocolors": "^1.1.1"
1985
+ },
1986
+ "bin": {
1987
+ "update-browserslist-db": "cli.js"
1988
+ },
1989
+ "peerDependencies": {
1990
+ "browserslist": ">= 4.21.0"
1991
+ }
1992
+ },
1993
+ "node_modules/utility-types": {
1994
+ "version": "3.11.0",
1995
+ "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz",
1996
+ "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==",
1997
+ "license": "MIT",
1998
+ "engines": {
1999
+ "node": ">= 4"
2000
+ }
2001
+ },
2002
+ "node_modules/uuid": {
2003
+ "version": "9.0.1",
2004
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
2005
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
2006
+ "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
2007
+ "funding": [
2008
+ "https://github.com/sponsors/broofa",
2009
+ "https://github.com/sponsors/ctavan"
2010
+ ],
2011
+ "license": "MIT",
2012
+ "bin": {
2013
+ "uuid": "dist/bin/uuid"
2014
+ }
2015
+ },
2016
+ "node_modules/vite": {
2017
+ "version": "4.5.14",
2018
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz",
2019
+ "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
2020
+ "dev": true,
2021
+ "license": "MIT",
2022
+ "dependencies": {
2023
+ "esbuild": "^0.18.10",
2024
+ "postcss": "^8.4.27",
2025
+ "rollup": "^3.27.1"
2026
+ },
2027
+ "bin": {
2028
+ "vite": "bin/vite.js"
2029
+ },
2030
+ "engines": {
2031
+ "node": "^14.18.0 || >=16.0.0"
2032
+ },
2033
+ "funding": {
2034
+ "url": "https://github.com/vitejs/vite?sponsor=1"
2035
+ },
2036
+ "optionalDependencies": {
2037
+ "fsevents": "~2.3.2"
2038
+ },
2039
+ "peerDependencies": {
2040
+ "@types/node": ">= 14",
2041
+ "less": "*",
2042
+ "lightningcss": "^1.21.0",
2043
+ "sass": "*",
2044
+ "stylus": "*",
2045
+ "sugarss": "*",
2046
+ "terser": "^5.4.0"
2047
+ },
2048
+ "peerDependenciesMeta": {
2049
+ "@types/node": {
2050
+ "optional": true
2051
+ },
2052
+ "less": {
2053
+ "optional": true
2054
+ },
2055
+ "lightningcss": {
2056
+ "optional": true
2057
+ },
2058
+ "sass": {
2059
+ "optional": true
2060
+ },
2061
+ "stylus": {
2062
+ "optional": true
2063
+ },
2064
+ "sugarss": {
2065
+ "optional": true
2066
+ },
2067
+ "terser": {
2068
+ "optional": true
2069
+ }
2070
+ }
2071
+ },
2072
+ "node_modules/webgl-constants": {
2073
+ "version": "1.1.1",
2074
+ "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz",
2075
+ "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="
2076
+ },
2077
+ "node_modules/webgl-sdf-generator": {
2078
+ "version": "1.1.1",
2079
+ "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
2080
+ "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==",
2081
+ "license": "MIT"
2082
+ },
2083
+ "node_modules/which": {
2084
+ "version": "2.0.2",
2085
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
2086
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
2087
+ "license": "ISC",
2088
+ "dependencies": {
2089
+ "isexe": "^2.0.0"
2090
+ },
2091
+ "bin": {
2092
+ "node-which": "bin/node-which"
2093
+ },
2094
+ "engines": {
2095
+ "node": ">= 8"
2096
+ }
2097
+ },
2098
+ "node_modules/yallist": {
2099
+ "version": "3.1.1",
2100
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
2101
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
2102
+ "dev": true,
2103
+ "license": "ISC"
2104
+ },
2105
+ "node_modules/zustand": {
2106
+ "version": "3.7.2",
2107
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
2108
+ "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
2109
+ "license": "MIT",
2110
+ "engines": {
2111
+ "node": ">=12.7.0"
2112
+ },
2113
+ "peerDependencies": {
2114
+ "react": ">=16.8"
2115
+ },
2116
+ "peerDependenciesMeta": {
2117
+ "react": {
2118
+ "optional": true
2119
+ }
2120
+ }
2121
+ }
2122
+ }
2123
+ }
web_app/frontend/package.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "fake-news-detection-frontend",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@react-three/drei": "^9.88.0",
13
+ "@react-three/fiber": "^8.13.0",
14
+ "@use-gesture/react": "^10.3.1",
15
+ "clsx": "^2.1.1",
16
+ "framer-motion": "^10.18.0",
17
+ "gsap": "^3.15.0",
18
+ "lucide-react": "^0.263.1",
19
+ "react": "^18.2.0",
20
+ "react-dom": "^18.2.0",
21
+ "tailwind-merge": "^3.6.0",
22
+ "three": "^0.159.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/react": "^18.2.15",
26
+ "@types/react-dom": "^18.2.7",
27
+ "@vitejs/plugin-react": "^4.0.3",
28
+ "typescript": "^5.0.2",
29
+ "vite": "^4.4.5"
30
+ }
31
+ }
web_app/frontend/src/App.tsx ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import {
3
+ ShieldAlert, Newspaper, PieChart, Info, Search, Loader2, CheckCircle2,
4
+ XCircle, BarChart3, BrainCircuit, Activity, Globe, Zap, Cpu, Sparkles,
5
+ ChevronDown, Terminal, Server, ShieldCheck, Database, Layers, ExternalLink, ArrowRight,
6
+ TrendingUp, MousePointer2, Box, Eye
7
+ } from 'lucide-react';
8
+ import { motion, AnimatePresence, useScroll, useSpring, useMotionValue, useTransform } from 'framer-motion';
9
+ import NeuralScene from './components/NeuralScene';
10
+
11
+ const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
12
+
13
+ interface PredictionResult {
14
+ fake_news: { prediction: 'Real' | 'Fake'; confidence: number; model: string; };
15
+ ai_detection: { prediction: 'Human' | 'AI Generated'; confidence: number; model: string; };
16
+ }
17
+
18
+ const SplashScreen = () => (
19
+ <motion.div className="epic-splash" exit={{ opacity: 0, scale: 1.1 }} transition={{ duration: 1 }}>
20
+ <div className="splash-wrap">
21
+ <motion.div initial={{ scale: 0.5, opacity: 0 }} animate={{ scale: 1, opacity: 1 }} transition={{ duration: 0.8, type: "spring" }}>
22
+ <ShieldAlert size={100} className="glow-icon" />
23
+ </motion.div>
24
+ <motion.h1 initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.4 }}>TRUTHLENS AI</motion.h1>
25
+ <div className="splash-bar-outer"><motion.div className="splash-bar-inner" initial={{ width: 0 }} animate={{ width: "100%" }} transition={{ duration: 2.2 }} /></div>
26
+ </div>
27
+ </motion.div>
28
+ );
29
+
30
+ const App: React.FC = () => {
31
+ const [inputText, setInputText] = useState('');
32
+ const [loading, setLoading] = useState(false);
33
+ const [result, setResult] = useState<PredictionResult | null>(null);
34
+ const [isInitializing, setIsInitializing] = useState(true);
35
+ const { scrollYProgress } = useScroll();
36
+ const scaleX = useSpring(scrollYProgress, { stiffness: 100, damping: 30 });
37
+
38
+ const tiltProps = {
39
+ whileHover: {
40
+ rotateX: -5, rotateY: 5, scale: 1.02,
41
+ transition: { type: "spring", stiffness: 400, damping: 20 }
42
+ },
43
+ style: { perspective: 1000 }
44
+ };
45
+
46
+ useEffect(() => {
47
+ const timer = setTimeout(() => setIsInitializing(false), 2800);
48
+ return () => clearTimeout(timer);
49
+ }, []);
50
+
51
+ const handlePredict = async () => {
52
+ if (!inputText.trim()) return;
53
+ setLoading(true);
54
+ setResult(null);
55
+ try {
56
+ const res = await fetch(`${API_BASE_URL}/predict`, {
57
+ method: 'POST',
58
+ headers: { 'Content-Type': 'application/json' },
59
+ body: JSON.stringify({ text: inputText, model: 'deberta' }),
60
+ });
61
+ const data = await res.json();
62
+ setResult(data);
63
+ setTimeout(() => {
64
+ document.getElementById('result-target')?.scrollIntoView({ behavior: 'smooth', block: 'center' });
65
+ }, 400);
66
+ } catch (e) { console.error(e); } finally { setLoading(false); }
67
+ };
68
+
69
+ const analysisCards = [
70
+ {
71
+ title: "DeBERTa-v3 Architecture",
72
+ tag: "CORE TECHNOLOGY",
73
+ desc: "Our engine utilizes the Microsoft DeBERTa-v3 Transformer ensemble. Beyond just accuracy, it prioritizes semantic context, processing information with disentangled attention to understand subtle linguistic nuances. This design ensures the system isn't just matching keywords, but understanding the narrative structure of the news."
74
+ },
75
+ {
76
+ title: "Balanced Neural Benchmarking",
77
+ tag: "RELIABILITY",
78
+ desc: "Performance is measured across a spectrum of metrics: Precision, Recall, and F1-Score. By optimizing for the harmonic mean of these values, we ensure a balanced detection system that minimizes False Alarms (protecting real news) while maintaining a strict filter against misinformation."
79
+ },
80
+ {
81
+ title: "Synthetics & AI Detection",
82
+ tag: "CONTENT ORIGIN",
83
+ desc: "Trained on a specialized hybrid dataset of 24,000 samples, the model identifies the unique fingerprint of machine-generated text. It looks for over-optimization, a common trait in AI-generated fake news where the grammar is perfect but the semantic logic is manipulated."
84
+ },
85
+ {
86
+ title: "Enterprise Efficiency",
87
+ tag: "SYSTEM PERFORMANCE",
88
+ desc: "High precision usually comes with high cost. Our architecture is engineered for rapid response times, achieving a golden ratio of deep-learning depth and operational speed. This allows for instantaneous news verification without server bottlenecks."
89
+ },
90
+ {
91
+ title: "Semantic Pattern Mapping",
92
+ tag: "INTELLIGENCE",
93
+ desc: "The AI maps emotional triggers and sensationalist vocabulary. While factual reporting uses neutral descriptive tokens, misinformation patterns often rely on high-intensity emotional markers. Our system detects these patterns at a structural level, regardless of the topic."
94
+ },
95
+ {
96
+ title: "Robust Data Integrity",
97
+ tag: "FOUNDATION",
98
+ desc: "Trust starts with the data. The model's foundation is a meticulously balanced split of verified real-world reporting and sophisticated fabrications. This rigorous training ensures the AI generalizes across global news events with zero historical bias."
99
+ }
100
+ ];
101
+
102
+ return (
103
+ <>
104
+ <NeuralScene scrollProgress={scrollYProgress} />
105
+ <AnimatePresence>{isInitializing && <SplashScreen />}</AnimatePresence>
106
+
107
+ <div className="main-wrapper">
108
+ <header className="nav-bar">
109
+ <div className="nav-container">
110
+ <div className="logo-group"><ShieldAlert className="logo-icon" /><span>TRUTHLENS AI</span></div>
111
+ <nav className="links"><a href="#hero">Overview</a><a href="#verify">Verify</a><a href="#analysis">Insights</a><a href="#architecture">Systems</a></nav>
112
+ <div className="status"><span className="dot" /> SYSTEM ONLINE</div>
113
+ </div>
114
+ <motion.div className="scroll-line" style={{ scaleX }} />
115
+ </header>
116
+
117
+ <section id="hero" className="hero-section full-page">
118
+ <motion.div className="hero-content" initial={{ opacity: 0, y: 30 }} whileInView={{ opacity: 1, y: 0 }} transition={{ duration: 1 }}>
119
+ <div className="badge-modern"><Sparkles size={14} /> <span>Neural Verification Framework</span></div>
120
+ <h1>Illuminate the <span className="text-glow">Hidden Truth</span> <br/> in Global Media.</h1>
121
+ <p>Next-generation artificial intelligence designed to deconstruct misinformation and identify synthetic content with laboratory-grade precision.</p>
122
+ <div className="hero-btns">
123
+ <motion.a href="#verify" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} className="btn-main">Get Started <ArrowRight size={18} /></motion.a>
124
+ <a href="#analysis" className="btn-ghost">Research Data</a>
125
+ </div>
126
+ </motion.div>
127
+ </section>
128
+
129
+ <section id="verify" className="verify-section full-page">
130
+ <div className="container">
131
+ <motion.div className="section-title" initial={{ opacity: 0 }} whileInView={{ opacity: 1 }} viewport={{ once: true }}>
132
+ <h2>Verification Engine</h2>
133
+ <p>Execute neural analysis to verify veracity and content origin</p>
134
+ </motion.div>
135
+
136
+ <div className="verify-grid">
137
+ <motion.div
138
+ {...tiltProps}
139
+ className="input-box glass-card"
140
+ initial={{ opacity: 0, x: -80 }}
141
+ whileInView={{ opacity: 1, x: 0 }}
142
+ viewport={{ once: true }}
143
+ transition={{ duration: 0.8, type: "spring", damping: 15 }}
144
+ >
145
+ <div className="card-header"><Terminal size={14} /> <span>INPUT_STREAM_RAW</span></div>
146
+ <textarea
147
+ placeholder="Paste news content here for deep analysis... (Note: Model optimized for political news context)"
148
+ value={inputText}
149
+ onChange={e=>setInputText(e.target.value)}
150
+ />
151
+ <div style={{ fontSize: '12px', color: 'rgba(255,255,255,0.5)', marginBottom: '10px', display: 'flex', alignItems: 'center', gap: '5px' }}>
152
+ <Info size={12} />
153
+ <span>Disclaimer: Training data is primarily focused on political issues. Results may vary for other topics.</span>
154
+ </div>
155
+ <div className="engine-select">
156
+ <label>ACTIVE NEURAL ARCHITECTURE: DEBERTA-V3 TRANSFORMER</label>
157
+ </div>
158
+ <button className="scan-btn" onClick={handlePredict} disabled={loading||!inputText.trim()}>
159
+ {loading ? <Loader2 className="spin"/> : <><Zap size={18}/> EXECUTE NEURAL SCAN</>}
160
+ </button>
161
+ </motion.div>
162
+
163
+ <motion.div className="result-box" initial={{ opacity: 0, x: 80 }} whileInView={{ opacity: 1, x: 0 }} viewport={{ once: true }} transition={{ duration: 0.8, type: "spring", damping: 15 }}>
164
+ <div id="result-target" />
165
+ <AnimatePresence mode="wait">
166
+ {result ? (
167
+ <motion.div initial={{ opacity:0, scale:0.95 }} animate={{ opacity:1, scale:1 }} className="result-display">
168
+ <motion.div {...tiltProps} className={`res-item ${result.fake_news.prediction.toLowerCase()}`}>
169
+ <div className="res-tag-badge">VERACITY ANALYSIS</div>
170
+ <div className="res-row">
171
+ <div className="res-icon">{result.fake_news.prediction === 'Real' ? <CheckCircle2 size={36}/> : <XCircle size={36}/>}</div>
172
+ <div className="res-info">
173
+ <h3>{result.fake_news.prediction} News</h3>
174
+ <div className="conf-bar-wrap">
175
+ <div className="conf-labels"><span>Confidence</span><span>{result.fake_news.confidence}%</span></div>
176
+ <div className="conf-track"><motion.div className="conf-fill" initial={{width:0}} animate={{width:`${result.fake_news.confidence}%`}} transition={{duration:1}} /></div>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ </motion.div>
181
+
182
+ <motion.div {...tiltProps} className={`res-item ${result.ai_detection.prediction === 'Human' ? 'real' : 'fake'}`} style={{marginTop:'20px'}}>
183
+ <div className="res-tag-badge">ORIGIN_DETECTION_LOG</div>
184
+ <div className="res-row">
185
+ <div className="res-icon">{result.ai_detection.prediction === 'Human' ? <Globe size={36}/> : <BrainCircuit size={36}/>}</div>
186
+ <div className="res-info">
187
+ <h3>{result.ai_detection.prediction} Content</h3>
188
+ <div className="conf-bar-wrap">
189
+ <div className="conf-labels"><span>Probability</span><span>{result.ai_detection.confidence}%</span></div>
190
+ <div className="conf-track"><motion.div className="conf-fill" initial={{width:0}} animate={{width:`${result.ai_detection.confidence}%`}} transition={{duration:1, delay:0.2}} /></div>
191
+ </div>
192
+ </div>
193
+ </div>
194
+ </motion.div>
195
+ </motion.div>
196
+ ) : (
197
+ <div className="result-idle glass-card">
198
+ <div className="laser-scanner" />
199
+ <BrainCircuit size={80} className="idle-icon" />
200
+ <h3>Neural Standby</h3>
201
+ <p>Awaiting valid text input for pattern matching</p>
202
+ </div>
203
+ )}
204
+ </AnimatePresence>
205
+ </motion.div>
206
+ </div>
207
+ </div>
208
+ </section>
209
+
210
+ <section id="analysis" className="analysis-section full-page">
211
+ <div className="container">
212
+ <motion.div className="section-title" initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }}>
213
+ <h2>Deep Intelligence Insights</h2>
214
+ <p>Understanding the multidimensional capabilities of our neural framework</p>
215
+ </motion.div>
216
+ <div className="analysis-grid">
217
+ {analysisCards.map((card, i) => (
218
+ <motion.div
219
+ key={i}
220
+ {...tiltProps}
221
+ className="analysis-card glass-card"
222
+ initial={{ opacity: 0, y: 30 }}
223
+ whileInView={{ opacity: 1, y: 0 }}
224
+ viewport={{ once: true }}
225
+ transition={{ delay: i * 0.1 }}
226
+ >
227
+ <div className="card-label">{card.tag}</div>
228
+ <h3>{card.title}</h3>
229
+ <p style={{ fontSize: '16px', lineHeight: '1.8' }}>{card.desc}</p>
230
+ </motion.div>
231
+ ))}
232
+ </div>
233
+ </div>
234
+ </section>
235
+
236
+ <section id="architecture" className="arch-section full-page">
237
+ <div className="container">
238
+ <motion.div className="section-title" initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }}>
239
+ <h2>Enterprise Architecture</h2>
240
+ <p>The high-performance technology stack powering our verification pipeline</p>
241
+ </motion.div>
242
+ <div className="tech-grid">
243
+ {[
244
+ {icon:<Cpu/>,title:'Inference Node',desc:'Asynchronous FastAPI backend optimized for high-concurrency neural workloads.'},
245
+ {icon:<Database/>,title:'Semantic Repository',desc:'Balanced research dataset containing 24,000+ verified news samples.'},
246
+ {icon:<Layers/>,title:'Ensemble Pipeline',desc:'Multi-stage architecture designed for maximum linguistic precision.'},
247
+ {icon:<ShieldCheck/>,title:'Security Protocol',desc:'Zero-trust verification pipeline ensuring content integrity and scan safety.'}
248
+ ].map((t,i)=>(
249
+ <motion.div key={i} {...tiltProps} className="tech-card glass-card" initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ delay: i * 0.1 }}>
250
+ <div className="tech-icon-wrap">{t.icon}</div>
251
+ <h4>{t.title}</h4>
252
+ <p>{t.desc}</p>
253
+ </motion.div>
254
+ ))}
255
+ </div>
256
+ <footer className="footer-pro">
257
+ <div className="f-row">
258
+ <span>© 2026 TRUTHLENS AI SYSTEMS. ALL RIGHTS RESERVED.</span>
259
+ <div className="f-tags">
260
+ <TrendingUp size={14} /> <span>v1.5.0 STABLE</span>
261
+ <span className="dot-green" /><span>ENCRYPTED_STREAM</span>
262
+ </div>
263
+ </div>
264
+ </footer>
265
+ </div>
266
+ </section>
267
+ </div>
268
+ </>
269
+ );
270
+ };
271
+
272
+ export default App;
web_app/frontend/src/components/NeuralBackground.tsx ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useRef } from 'react';
2
+
3
+ const NeuralBackground: React.FC = () => {
4
+ const canvasRef = useRef<HTMLCanvasElement>(null);
5
+
6
+ useEffect(() => {
7
+ const canvas = canvasRef.current;
8
+ if (!canvas) return;
9
+
10
+ const ctx = canvas.getContext('2d');
11
+ if (!ctx) return;
12
+
13
+ let animationFrameId: number;
14
+ let particles: Particle[] = [];
15
+
16
+ const resize = () => {
17
+ canvas.width = window.innerWidth;
18
+ canvas.height = window.innerHeight;
19
+ };
20
+
21
+ class Particle {
22
+ x: number; y: number; vx: number; vy: number;
23
+ constructor() {
24
+ this.x = Math.random() * canvas!.width;
25
+ this.y = Math.random() * canvas!.height;
26
+ this.vx = (Math.random() - 0.5) * 0.4;
27
+ this.vy = (Math.random() - 0.5) * 0.4;
28
+ }
29
+ update() {
30
+ this.x += this.vx;
31
+ this.y += this.vy;
32
+ if (this.x < 0 || this.x > canvas!.width) this.vx *= -1;
33
+ if (this.y < 0 || this.y > canvas!.height) this.vy *= -1;
34
+ }
35
+ draw() {
36
+ ctx!.beginPath();
37
+ ctx!.arc(this.x, this.y, 1.2, 0, Math.PI * 2);
38
+ ctx!.fillStyle = 'rgba(99, 102, 241, 0.4)';
39
+ ctx!.fill();
40
+ }
41
+ }
42
+
43
+ const init = () => {
44
+ particles = Array.from({ length: 100 }, () => new Particle());
45
+ };
46
+
47
+ const drawLines = () => {
48
+ for (let i = 0; i < particles.length; i++) {
49
+ for (let j = i + 1; j < particles.length; j++) {
50
+ const dx = particles[i].x - particles[j].x;
51
+ const dy = particles[i].y - particles[j].y;
52
+ const dist = Math.sqrt(dx * dx + dy * dy);
53
+ if (dist < 150) {
54
+ ctx!.beginPath();
55
+ ctx!.strokeStyle = `rgba(99, 102, 241, ${0.15 * (1 - dist / 150)})`;
56
+ ctx!.lineWidth = 0.8;
57
+ ctx!.moveTo(particles[i].x, particles[i].y);
58
+ ctx!.lineTo(particles[j].x, particles[j].y);
59
+ ctx!.stroke();
60
+ }
61
+ }
62
+ }
63
+ };
64
+
65
+ const render = () => {
66
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
67
+ particles.forEach(p => {
68
+ p.update();
69
+ p.draw();
70
+ });
71
+ drawLines();
72
+ animationFrameId = requestAnimationFrame(render);
73
+ };
74
+
75
+ window.addEventListener('resize', resize);
76
+ resize();
77
+ init();
78
+ render();
79
+
80
+ return () => {
81
+ window.removeEventListener('resize', resize);
82
+ cancelAnimationFrame(animationFrameId);
83
+ };
84
+ }, []);
85
+
86
+ return (
87
+ <canvas
88
+ ref={canvasRef}
89
+ style={{
90
+ position: 'fixed',
91
+ top: 0,
92
+ left: 0,
93
+ zIndex: -1,
94
+ background: 'transparent',
95
+ pointerEvents: 'none'
96
+ }}
97
+ />
98
+ );
99
+ };
100
+
101
+ export default NeuralBackground;
web_app/frontend/src/components/NeuralScene.tsx ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useRef, useMemo } from 'react';
2
+ import { Canvas, useFrame } from '@react-three/fiber';
3
+ import { Points, PointMaterial, Float } from '@react-three/drei';
4
+ import * as THREE from 'three';
5
+ import { MotionValue, useTransform } from 'framer-motion';
6
+
7
+ interface ParticleFieldProps {
8
+ scrollProgress: MotionValue<number>;
9
+ }
10
+
11
+ const ParticleField: React.FC<ParticleFieldProps> = ({ scrollProgress }) => {
12
+ const ref = useRef<THREE.Points>(null);
13
+ const groupRef = useRef<THREE.Group>(null);
14
+
15
+ // Generate random particles in a sphere
16
+ const particles = useMemo(() => {
17
+ const pos = new Float32Array(3000 * 3);
18
+ for (let i = 0; i < 3000; i++) {
19
+ const theta = THREE.MathUtils.randFloatSpread(360);
20
+ const phi = THREE.MathUtils.randFloatSpread(360);
21
+ const distance = 5 + Math.random() * 5;
22
+
23
+ pos[i * 3] = distance * Math.sin(theta) * Math.cos(phi);
24
+ pos[i * 3 + 1] = distance * Math.sin(theta) * Math.sin(phi);
25
+ pos[i * 3 + 2] = distance * Math.cos(theta);
26
+ }
27
+ return pos;
28
+ }, []);
29
+
30
+ useFrame((state, delta) => {
31
+ if (ref.current) {
32
+ ref.current.rotation.y += delta / 20;
33
+ ref.current.rotation.x += delta / 30;
34
+ }
35
+
36
+ if (groupRef.current) {
37
+ // Dynamic rotation based on scroll
38
+ const scroll = scrollProgress.get();
39
+ groupRef.current.rotation.z = scroll * Math.PI;
40
+ groupRef.current.position.z = scroll * 5;
41
+ }
42
+ });
43
+
44
+ return (
45
+ <group ref={groupRef}>
46
+ <Points ref={ref} positions={particles} stride={3} frustumCulled={false}>
47
+ <PointMaterial
48
+ transparent
49
+ color="#818cf8"
50
+ size={0.03}
51
+ sizeAttenuation={true}
52
+ depthWrite={false}
53
+ blending={THREE.AdditiveBlending}
54
+ opacity={0.4}
55
+ />
56
+ </Points>
57
+ </group>
58
+ );
59
+ };
60
+
61
+ interface NeuralSceneProps {
62
+ scrollProgress: MotionValue<number>;
63
+ }
64
+
65
+ const NeuralScene: React.FC<NeuralSceneProps> = ({ scrollProgress }) => {
66
+ return (
67
+ <div style={{ position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', zIndex: -1, pointerEvents: 'none', background: '#020617' }}>
68
+ <Canvas camera={{ position: [0, 0, 10], fov: 75 }}>
69
+ <color attach="background" args={['#020617']} />
70
+ <ambientLight intensity={0.5} />
71
+ <pointLight position={[10, 10, 10]} intensity={1} color="#6366f1" />
72
+ <Float speed={2} rotationIntensity={0.5} floatIntensity={0.5}>
73
+ <ParticleField scrollProgress={scrollProgress} />
74
+ </Float>
75
+ </Canvas>
76
+ </div>
77
+ );
78
+ };
79
+
80
+ export default NeuralScene;
81
+
web_app/frontend/src/main.tsx ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import App from './App.tsx'
4
+ import './styles/App.css'
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')!).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ )
web_app/frontend/src/styles/App.css ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --e-indigo: #818cf8;
3
+ --e-violet: #a78bfa;
4
+ --e-accent: #6366f1;
5
+ --e-slate: #020617;
6
+ --e-slate-light: #0f172a;
7
+ --e-success: #34d399;
8
+ --e-danger: #fb7185;
9
+ --e-glass: rgba(15, 23, 42, 0.7);
10
+ --e-border: rgba(129, 140, 248, 0.3);
11
+ --e-text-main: #f8fafc;
12
+ --e-text-muted: #94a3b8;
13
+ --container-max: 1200px;
14
+ }
15
+
16
+ * { box-sizing: border-box; margin: 0; padding: 0; scroll-behavior: smooth; cursor: default; }
17
+
18
+ body {
19
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
20
+ background-color: var(--e-slate);
21
+ color: var(--e-text-main);
22
+ overflow-x: hidden;
23
+ -webkit-font-smoothing: antialiased;
24
+ }
25
+
26
+ /* --- STABLE NAV --- */
27
+ .nav-bar {
28
+ position: fixed; top: 0; width: 100%; height: 75px;
29
+ background: rgba(2, 6, 23, 0.8); backdrop-filter: blur(20px);
30
+ z-index: 1000; border-bottom: 1px solid var(--e-border);
31
+ }
32
+
33
+ .nav-container {
34
+ max-width: var(--container-max); margin: 0 auto; height: 100%;
35
+ display: flex; align-items: center; justify-content: space-between; padding: 0 24px;
36
+ }
37
+
38
+ .logo-group { display: flex; align-items: center; gap: 12px; font-weight: 900; font-size: 20px; letter-spacing: -0.02em; }
39
+ .logo-icon { color: var(--e-indigo); width: 26px; height: 26px; filter: drop-shadow(0 0 10px var(--e-indigo)); }
40
+
41
+ .links { display: flex; gap: 35px; }
42
+ .links a { text-decoration: none; color: var(--e-text-muted); font-size: 13px; font-weight: 700; transition: all 0.3s; text-transform: uppercase; letter-spacing: 0.1em; }
43
+ .links a:hover { color: #fff; text-shadow: 0 0 10px var(--e-indigo); }
44
+
45
+ .status { display: flex; align-items: center; gap: 10px; font-size: 10px; font-weight: 800; color: #64748b; }
46
+ .dot { width: 8px; height: 8px; background: var(--e-success); border-radius: 50%; box-shadow: 0 0 10px var(--e-success); animation: pulse 2s infinite; }
47
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
48
+
49
+ .scroll-line { position: absolute; bottom: 0; left: 0; height: 3px; background: var(--e-indigo); width: 100%; transform-origin: 0%; box-shadow: 0 0 10px var(--e-indigo); }
50
+
51
+ /* --- SECTION BACKGROUNDS & SPACING --- */
52
+ .full-page {
53
+ min-height: 100vh;
54
+ display: flex;
55
+ flex-direction: column;
56
+ justify-content: center;
57
+ align-items: center;
58
+ padding: 120px 24px;
59
+ position: relative;
60
+ }
61
+
62
+ .container { max-width: var(--container-max); margin: 0 auto; width: 100%; }
63
+
64
+ .hero-section { background: radial-gradient(circle at 50% 50%, rgba(99, 102, 241, 0.1) 0%, transparent 70%); }
65
+ .verify-section { background: radial-gradient(circle at 80% 20%, rgba(129, 140, 248, 0.05) 0%, transparent 50%); }
66
+ .analysis-section { background: radial-gradient(circle at 20% 80%, rgba(165, 180, 252, 0.05) 0%, transparent 50%); }
67
+
68
+ .main-wrapper {
69
+ position: relative;
70
+ z-index: 1;
71
+ }
72
+
73
+ /* --- HERO SECTION --- */
74
+ .hero-section { text-align: center; }
75
+ .hero-content { max-width: 850px; }
76
+ .badge-modern { display: inline-flex; align-items: center; gap: 8px; background: rgba(129, 140, 248, 0.15); border: 1px solid var(--e-border); padding: 8px 16px; border-radius: 99px; margin-bottom: 30px; color: var(--e-indigo); font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.15em; }
77
+ .hero-section h1 { font-size: 72px; font-weight: 900; line-height: 1.1; margin-bottom: 24px; letter-spacing: -0.04em; }
78
+ .text-glow { background: linear-gradient(135deg, #fff 40%, var(--e-indigo) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; filter: drop-shadow(0 0 20px rgba(165, 180, 252, 0.4)); }
79
+ .hero-section p { font-size: 20px; color: var(--e-text-muted); line-height: 1.6; margin-bottom: 50px; max-width: 650px; margin-left: auto; margin-right: auto; }
80
+ .hero-btns { display: flex; gap: 20px; justify-content: center; }
81
+
82
+ .btn-main { padding: 18px 40px; background: var(--e-indigo); color: #020617; border-radius: 14px; font-weight: 800; text-decoration: none; font-size: 16px; box-shadow: 0 10px 25px rgba(129, 140, 248, 0.4); transition: all 0.3s; display: flex; align-items: center; gap: 10px; }
83
+ .btn-main:hover { background: #fff; transform: translateY(-2px); }
84
+ .btn-ghost { padding: 18px 40px; background: rgba(255,255,255,0.03); color: #fff; border-radius: 14px; font-weight: 800; text-decoration: none; border: 1px solid var(--e-border); transition: all 0.3s; }
85
+ .btn-ghost:hover { background: rgba(255,255,255,0.08); border-color: var(--e-indigo); }
86
+
87
+ /* --- SECTION HEADERS --- */
88
+ .section-title { text-align: center; margin-bottom: 70px; }
89
+ .section-title h2 { font-size: 48px; font-weight: 900; margin-bottom: 16px; background: linear-gradient(135deg, #fff, var(--e-text-muted)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
90
+ .section-title p { color: var(--e-text-muted); font-size: 20px; font-weight: 500; }
91
+
92
+ /* --- VERIFY GRID --- */
93
+ .verify-grid { display: grid; grid-template-columns: 1.2fr 1fr; gap: 40px; align-items: stretch; width: 100%; }
94
+ .glass-card { background: var(--e-glass); backdrop-filter: blur(20px); border: 1px solid var(--e-border); border-radius: 32px; padding: 40px; transform-style: preserve-3d; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
95
+ .glass-card:hover { border-color: var(--e-indigo); box-shadow: 0 20px 50px rgba(0,0,0,0.5), 0 0 20px rgba(129, 140, 248, 0.1); }
96
+
97
+ .card-header { display: flex; align-items: center; gap: 10px; font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--e-indigo); margin-bottom: 24px; font-weight: 800; letter-spacing: 0.1em; }
98
+
99
+ textarea {
100
+ width: 100%; min-height: 240px; background: rgba(0,0,0,0.4); border: 1px solid var(--e-border); border-radius: 20px; padding: 24px; color: #fff; font-size: 18px; line-height: 1.6; resize: none; transition: all 0.3s; margin-bottom: 30px; font-family: inherit;
101
+ }
102
+ textarea:focus { outline: none; border-color: var(--e-indigo); background: rgba(0,0,0,0.6); box-shadow: 0 0 20px rgba(129, 140, 248, 0.1); }
103
+
104
+ .engine-select label { font-size: 11px; font-weight: 800; color: var(--e-text-muted); letter-spacing: 0.15em; margin-bottom: 15px; display: block; text-transform: uppercase; }
105
+
106
+ .scan-btn { width: 100%; padding: 22px; background: linear-gradient(135deg, var(--e-accent), var(--e-indigo)); color: #020617; border: none; border-radius: 18px; font-weight: 900; font-size: 16px; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 12px; box-shadow: 0 10px 30px rgba(129, 140, 248, 0.3); transition: all 0.3s; }
107
+ .scan-btn:hover { transform: translateY(-2px); box-shadow: 0 15px 40px rgba(129, 140, 248, 0.5); filter: brightness(1.1); }
108
+ .scan-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
109
+
110
+ /* --- UNIFIED RESULTS --- */
111
+ .result-box { position: relative; height: 100%; width: 100%; }
112
+ .result-display { display: flex; flex-direction: column; gap: 24px; height: 100%; }
113
+
114
+ .res-item {
115
+ flex: 1;
116
+ background: rgba(255,255,255,0.05);
117
+ border: 1px solid var(--e-border);
118
+ border-radius: 28px;
119
+ padding: 40px;
120
+ position: relative;
121
+ overflow: hidden;
122
+ display: flex;
123
+ flex-direction: column;
124
+ justify-content: center;
125
+ border-left: 8px solid transparent;
126
+ transform-style: preserve-3d;
127
+ }
128
+ .res-item.real { border-left-color: var(--e-success); background: linear-gradient(90deg, rgba(52, 211, 153, 0.05) 0%, transparent 100%); }
129
+ .res-item.fake { border-left-color: var(--e-danger); background: linear-gradient(90deg, rgba(251, 113, 133, 0.05) 0%, transparent 100%); }
130
+
131
+ .res-tag-badge { position: absolute; top: 15px; right: 25px; font-size: 10px; font-weight: 800; color: var(--e-text-muted); letter-spacing: 0.15em; text-transform: uppercase; }
132
+ .res-row { display: flex; gap: 25px; align-items: center; width: 100%; }
133
+ .res-icon { width: 72px; height: 72px; border-radius: 20px; background: rgba(255,255,255,0.05); display: flex; align-items: center; justify-content: center; flex-shrink: 0; border: 1px solid var(--e-border); }
134
+ .real .res-icon { color: var(--e-success); filter: drop-shadow(0 0 12px rgba(52, 211, 153, 0.4)); }
135
+ .fake .res-icon { color: var(--e-danger); filter: drop-shadow(0 0 12px rgba(251, 113, 133, 0.4)); }
136
+
137
+ .res-info { flex: 1; min-width: 0; }
138
+ .res-info h3 { font-size: 32px; font-weight: 900; margin-bottom: 15px; letter-spacing: -0.02em; }
139
+
140
+ .conf-bar-wrap { width: 100%; }
141
+ .conf-labels { display: flex; justify-content: space-between; font-size: 11px; font-weight: 800; color: var(--e-text-muted); margin-bottom: 10px; }
142
+ .conf-track { height: 8px; background: rgba(255,255,255,0.08); border-radius: 10px; overflow: hidden; width: 100%; }
143
+ .conf-fill { height: 100%; border-radius: 10px; }
144
+ .real .conf-fill { background: var(--e-success); box-shadow: 0 0 15px var(--e-success); }
145
+ .fake .conf-fill { background: var(--e-danger); box-shadow: 0 0 15px var(--e-danger); }
146
+
147
+ /* --- IDLE SCANNER --- */
148
+ .result-idle { height: 100%; min-height: 500px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; color: var(--e-text-muted); position: relative; overflow: hidden; border-radius: 32px; border: 2px dashed rgba(129, 140, 248, 0.2); background: var(--e-glass); }
149
+ .laser-scanner { position: absolute; top: 0; width: 100%; height: 3px; background: linear-gradient(90deg, transparent, var(--e-indigo), transparent); animation: scan-anim 4s linear infinite; box-shadow: 0 0 20px var(--e-indigo); }
150
+ @keyframes scan-anim { 0% { top: 0; } 50% { top: 100%; } 100% { top: 0; } }
151
+ .idle-icon { opacity: 0.2; margin-bottom: 30px; animation: pulse-icon 2s infinite; color: var(--e-indigo); }
152
+ @keyframes pulse-icon { 0% { scale: 1; } 50% { scale: 1.1; } 100% { scale: 1; } }
153
+
154
+ /* --- ANALYSIS & TECH --- */
155
+ .analysis-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; }
156
+ .analysis-card { padding: 30px; text-align: left; }
157
+ .card-label { margin-bottom: 15px; font-size: 11px; font-weight: 800; color: var(--e-indigo); letter-spacing: 0.2em; text-transform: uppercase; }
158
+ .analysis-card h3 { font-size: 24px; font-weight: 800; margin-bottom: 12px; color: #fff; }
159
+
160
+ .tech-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; margin-bottom: 80px; }
161
+ .tech-card { padding: 45px 30px; text-align: center; }
162
+ .tech-icon-wrap { width: 70px; height: 70px; background: rgba(129, 140, 248, 0.1); border-radius: 22px; display: flex; align-items: center; justify-content: center; margin: 0 auto 30px; color: var(--e-indigo); border: 1px solid var(--e-border); filter: drop-shadow(0 0 10px rgba(129, 140, 248, 0.2)); }
163
+ .tech-card h4 { font-size: 20px; font-weight: 800; margin-bottom: 12px; color: #fff; }
164
+ .tech-card p { color: var(--e-text-muted); font-size: 15px; line-height: 1.6; max-width: 400px; margin: 0 auto; }
165
+
166
+ /* --- FOOTER --- */
167
+ .footer-pro { padding: 80px 0 40px; border-top: 1px solid var(--e-border); width: 100%; margin-top: 100px; }
168
+ .f-row { display: flex; justify-content: space-between; font-size: 11px; font-weight: 900; color: var(--e-text-muted); letter-spacing: 0.15em; }
169
+ .f-tags { display: flex; align-items: center; gap: 20px; }
170
+ .dot-green { width: 8px; height: 8px; background: #10b981; border-radius: 50%; box-shadow: 0 0 10px #10b981; }
171
+
172
+ /* --- SPLASH SCREEN --- */
173
+ .epic-splash { position: fixed; inset: 0; background: #020617; z-index: 5000; display: flex; align-items: center; justify-content: center; }
174
+ .splash-wrap { text-align: center; }
175
+ .glow-icon { color: #fff; filter: drop-shadow(0 0 30px var(--e-indigo)); margin-bottom: 40px; }
176
+ .epic-splash h1 { font-size: 48px; font-weight: 900; letter-spacing: 0.5em; color: #fff; margin-bottom: 40px; text-indent: 0.5em; }
177
+ .splash-bar-outer { width: 300px; height: 3px; background: rgba(255,255,255,0.05); border-radius: 10px; margin: 0 auto; overflow: hidden; }
178
+ .splash-bar-inner { height: 100%; background: var(--e-indigo); box-shadow: 0 0 20px var(--e-indigo); }
179
+
180
+ @media (max-width: 1100px) {
181
+ .hero-section h1 { font-size: 56px; }
182
+ .verify-grid, .analysis-grid, .tech-grid { grid-template-columns: 1fr; }
183
+ .links { display: none; }
184
+ }