Deploy AI Code Maintainability Scoring Engine to Hugging Face Spaces
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .dockerignore +16 -0
- .gitignore +56 -0
- AI_Code_Maintainability_Engine_Revision.html +303 -0
- Dockerfile +40 -0
- LICENSE +21 -0
- README.md +62 -5
- api_server.py +79 -0
- ast_analyzer.py +166 -0
- data/dataset.csv +221 -0
- dataset_generator.py +169 -0
- explanation_engine.py +121 -0
- feature_pipeline.py +168 -0
- frontend/dist/assets/index-CnXjjhnJ.js +0 -0
- frontend/dist/assets/index-Dn2xEIXT.css +1 -0
- frontend/dist/index.html +19 -0
- frontend/index.html +18 -0
- frontend/package-lock.json +2749 -0
- frontend/package.json +31 -0
- frontend/src/App.tsx +148 -0
- frontend/src/api.ts +48 -0
- frontend/src/components/AnalyticsHUD.tsx +84 -0
- frontend/src/components/CodeEditor.tsx +53 -0
- frontend/src/components/FlipStage.tsx +151 -0
- frontend/src/components/GlitchTitle.tsx +15 -0
- frontend/src/components/MagneticButton.tsx +109 -0
- frontend/src/components/RiskGauge.tsx +129 -0
- frontend/src/components/Scene3D.tsx +209 -0
- frontend/src/components/TiltPanel.tsx +59 -0
- frontend/src/main.tsx +11 -0
- frontend/src/styles/components.css +499 -0
- frontend/src/styles/global.css +169 -0
- frontend/src/styles/theme.css +72 -0
- frontend/src/vite-env.d.ts +1 -0
- frontend/tsconfig.json +20 -0
- frontend/tsconfig.node.json +10 -0
- frontend/vite.config.d.ts +2 -0
- frontend/vite.config.js +13 -0
- frontend/vite.config.ts +14 -0
- main.py +88 -0
- model_trainer.py +149 -0
- models/rf_model.pkl +3 -0
- models/scaler.pkl +3 -0
- models/xgb_model.pkl +3 -0
- phase2/__init__.py +0 -0
- phase2/candidate_generator.py +224 -0
- phase2/dl_generator.py +85 -0
- phase2/optimization.py +130 -0
- phase2/selector.py +34 -0
- phase2/validation.py +54 -0
- requirements.txt +8 -0
.dockerignore
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__
|
| 2 |
+
*.pyc
|
| 3 |
+
*.pyo
|
| 4 |
+
*.pyd
|
| 5 |
+
.Python
|
| 6 |
+
env/
|
| 7 |
+
venv/
|
| 8 |
+
.venv/
|
| 9 |
+
node_modules/
|
| 10 |
+
frontend/node_modules/
|
| 11 |
+
frontend/src/
|
| 12 |
+
.git/
|
| 13 |
+
.gitignore
|
| 14 |
+
.claude/
|
| 15 |
+
*.docx
|
| 16 |
+
*.webp
|
.gitignore
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
share/python-wheels/
|
| 24 |
+
*.egg-info/
|
| 25 |
+
.installed.cfg
|
| 26 |
+
*.egg
|
| 27 |
+
MANIFEST
|
| 28 |
+
|
| 29 |
+
# Virtual environments
|
| 30 |
+
venv/
|
| 31 |
+
env/
|
| 32 |
+
ENV/
|
| 33 |
+
env.bak/
|
| 34 |
+
venv.bak/
|
| 35 |
+
|
| 36 |
+
# User-specific / IDE
|
| 37 |
+
.idea/
|
| 38 |
+
.vscode/
|
| 39 |
+
*.swp
|
| 40 |
+
*.swo
|
| 41 |
+
|
| 42 |
+
# Environment variables
|
| 43 |
+
.env
|
| 44 |
+
.venv
|
| 45 |
+
|
| 46 |
+
# macOS
|
| 47 |
+
.DS_Store
|
| 48 |
+
|
| 49 |
+
# Node
|
| 50 |
+
node_modules/
|
| 51 |
+
dist/
|
| 52 |
+
.env.local
|
| 53 |
+
.env.*.local
|
| 54 |
+
|
| 55 |
+
# Claude
|
| 56 |
+
.claude/
|
AI_Code_Maintainability_Engine_Revision.html
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<title>AI Code Maintainability Scoring & Refactoring Engine — Revision Notes</title>
|
| 6 |
+
<style>
|
| 7 |
+
:root {
|
| 8 |
+
--bg: #0d1117;
|
| 9 |
+
--card: #161b22;
|
| 10 |
+
--border: #30363d;
|
| 11 |
+
--text: #e6edf3;
|
| 12 |
+
--muted: #8b949e;
|
| 13 |
+
--accent: #58a6ff;
|
| 14 |
+
--green: #3fb950;
|
| 15 |
+
--orange: #d29922;
|
| 16 |
+
--red: #f85149;
|
| 17 |
+
--purple: #bc8cff;
|
| 18 |
+
}
|
| 19 |
+
* { box-sizing: border-box; }
|
| 20 |
+
body {
|
| 21 |
+
background: var(--bg);
|
| 22 |
+
color: var(--text);
|
| 23 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
| 24 |
+
line-height: 1.6;
|
| 25 |
+
margin: 0;
|
| 26 |
+
padding: 0;
|
| 27 |
+
}
|
| 28 |
+
.wrap { max-width: 880px; margin: 0 auto; padding: 32px 20px 80px; }
|
| 29 |
+
h1 {
|
| 30 |
+
font-size: 1.7em;
|
| 31 |
+
border-bottom: 2px solid var(--accent);
|
| 32 |
+
padding-bottom: 12px;
|
| 33 |
+
margin-bottom: 4px;
|
| 34 |
+
}
|
| 35 |
+
.subtitle { color: var(--muted); margin-bottom: 28px; font-size: 0.95em; }
|
| 36 |
+
h2 {
|
| 37 |
+
color: var(--accent);
|
| 38 |
+
font-size: 1.25em;
|
| 39 |
+
margin-top: 42px;
|
| 40 |
+
border-left: 4px solid var(--accent);
|
| 41 |
+
padding-left: 12px;
|
| 42 |
+
}
|
| 43 |
+
h3 {
|
| 44 |
+
color: var(--purple);
|
| 45 |
+
font-size: 1.05em;
|
| 46 |
+
margin-top: 26px;
|
| 47 |
+
}
|
| 48 |
+
.card {
|
| 49 |
+
background: var(--card);
|
| 50 |
+
border: 1px solid var(--border);
|
| 51 |
+
border-radius: 8px;
|
| 52 |
+
padding: 16px 20px;
|
| 53 |
+
margin: 14px 0;
|
| 54 |
+
}
|
| 55 |
+
.pill {
|
| 56 |
+
display: inline-block;
|
| 57 |
+
background: rgba(88,166,255,0.15);
|
| 58 |
+
color: var(--accent);
|
| 59 |
+
border-radius: 5px;
|
| 60 |
+
padding: 2px 8px;
|
| 61 |
+
font-size: 0.85em;
|
| 62 |
+
margin-right: 6px;
|
| 63 |
+
}
|
| 64 |
+
table { width: 100%; border-collapse: collapse; margin: 14px 0; }
|
| 65 |
+
th, td {
|
| 66 |
+
border: 1px solid var(--border);
|
| 67 |
+
padding: 8px 12px;
|
| 68 |
+
text-align: left;
|
| 69 |
+
font-size: 0.95em;
|
| 70 |
+
}
|
| 71 |
+
th { background: #1c2229; color: var(--accent); }
|
| 72 |
+
tr:nth-child(even) { background: rgba(255,255,255,0.02); }
|
| 73 |
+
code, pre {
|
| 74 |
+
background: #010409;
|
| 75 |
+
color: #79c0ff;
|
| 76 |
+
border-radius: 6px;
|
| 77 |
+
font-family: "SF Mono", Consolas, monospace;
|
| 78 |
+
}
|
| 79 |
+
code { padding: 2px 6px; font-size: 0.9em; }
|
| 80 |
+
pre {
|
| 81 |
+
padding: 14px 16px;
|
| 82 |
+
overflow-x: auto;
|
| 83 |
+
border: 1px solid var(--border);
|
| 84 |
+
line-height: 1.5;
|
| 85 |
+
font-size: 0.88em;
|
| 86 |
+
}
|
| 87 |
+
.flow {
|
| 88 |
+
background: var(--card);
|
| 89 |
+
border: 1px solid var(--border);
|
| 90 |
+
border-radius: 8px;
|
| 91 |
+
padding: 18px 22px;
|
| 92 |
+
font-family: Consolas, monospace;
|
| 93 |
+
color: var(--green);
|
| 94 |
+
white-space: pre;
|
| 95 |
+
font-size: 0.9em;
|
| 96 |
+
}
|
| 97 |
+
.q {
|
| 98 |
+
color: var(--orange);
|
| 99 |
+
font-weight: 600;
|
| 100 |
+
margin-top: 18px;
|
| 101 |
+
}
|
| 102 |
+
.summary-box {
|
| 103 |
+
background: linear-gradient(135deg, rgba(88,166,255,0.1), rgba(188,140,255,0.1));
|
| 104 |
+
border: 1px solid var(--accent);
|
| 105 |
+
border-radius: 8px;
|
| 106 |
+
padding: 18px 22px;
|
| 107 |
+
font-style: italic;
|
| 108 |
+
margin: 16px 0;
|
| 109 |
+
}
|
| 110 |
+
.warn {
|
| 111 |
+
background: rgba(248,81,73,0.1);
|
| 112 |
+
border: 1px solid var(--red);
|
| 113 |
+
border-radius: 8px;
|
| 114 |
+
padding: 12px 18px;
|
| 115 |
+
color: #ffb3ac;
|
| 116 |
+
font-size: 0.92em;
|
| 117 |
+
margin: 14px 0;
|
| 118 |
+
}
|
| 119 |
+
.num-table td:first-child { color: var(--muted); }
|
| 120 |
+
.num-table td:last-child { color: var(--green); font-weight: 600; }
|
| 121 |
+
ul { padding-left: 22px; }
|
| 122 |
+
li { margin: 4px 0; }
|
| 123 |
+
</style>
|
| 124 |
+
</head>
|
| 125 |
+
<body>
|
| 126 |
+
<div class="wrap">
|
| 127 |
+
|
| 128 |
+
<h1>AI Code Maintainability Scoring & Refactoring Engine</h1>
|
| 129 |
+
<div class="subtitle">Revision Notes — Light-touch interview prep (2–3 sentences + one technical detail per topic)</div>
|
| 130 |
+
|
| 131 |
+
<div class="warn">
|
| 132 |
+
<strong>Scope reminder:</strong> this project was deliberately scoped as light-touch, not a deep-dive like Urdu Sentiment or Medical Image. Use the "One-Line Summary" and "Quick Answers" sections as your primary prep. The deeper sections exist so you're not caught flat-footed on a follow-up, not so you memorize all of it.
|
| 133 |
+
</div>
|
| 134 |
+
|
| 135 |
+
<h2>What Is This Project? (Elevator Pitch)</h2>
|
| 136 |
+
<div class="card">
|
| 137 |
+
"I built a two-phase AI system that scores the structural quality of Python code and then autonomously refactors risky code to improve it. Phase 1 uses AST parsing + a Random Forest classifier to predict a maintainability risk score. Phase 2 uses a CodeT5 deep learning model to generate and iteratively select better versions of risky code."
|
| 138 |
+
</div>
|
| 139 |
+
|
| 140 |
+
<h2>The Problem It Solves</h2>
|
| 141 |
+
<div class="card">
|
| 142 |
+
Traditional linters (PyLint, Flake8) use rigid, hand-written rules. This engine instead <strong>learns</strong> what "risky" structure looks like from data — deep nesting, high complexity, large functions — and then goes a step further than any linter by actually generating improved code, not just flagging problems.
|
| 143 |
+
</div>
|
| 144 |
+
|
| 145 |
+
<h2>Full Pipeline Flow</h2>
|
| 146 |
+
<div class="flow">Input Code
|
| 147 |
+
│
|
| 148 |
+
▼
|
| 149 |
+
PHASE 1 — Evaluator
|
| 150 |
+
AST Parsing → Feature Extraction → ML Scoring → Explanation
|
| 151 |
+
│
|
| 152 |
+
▼
|
| 153 |
+
Risk Score (0–100) + Top 3 Reasons
|
| 154 |
+
│
|
| 155 |
+
▼
|
| 156 |
+
PHASE 2 — Refactorer (only if risky)
|
| 157 |
+
Generate Candidates (CodeT5) → Validate → Re-score via Phase 1 → Select Best
|
| 158 |
+
│ (loop until target score or max iterations)
|
| 159 |
+
▼
|
| 160 |
+
Improved Code</div>
|
| 161 |
+
|
| 162 |
+
<h2>Phase 1 — The Evaluator</h2>
|
| 163 |
+
|
| 164 |
+
<h3>1. AST Analyzer — 11 Structural Features</h3>
|
| 165 |
+
<div class="card">
|
| 166 |
+
Parses code into an Abstract Syntax Tree (not line-by-line text reading — actual structural traversal via <code>ast.walk()</code>) and extracts 11 signals:
|
| 167 |
+
<table>
|
| 168 |
+
<tr><th>Feature</th><th>What it measures</th></tr>
|
| 169 |
+
<tr><td>max_nesting_depth</td><td><strong>Strongest predictor</strong> — deepest if/for/while/try nesting</td></tr>
|
| 170 |
+
<tr><td>cyclomatic_complexity</td><td>1 + every decision point (if/for/while/except/with/assert/bool-op)</td></tr>
|
| 171 |
+
<tr><td>avg_function_length</td><td>Mean lines per function</td></tr>
|
| 172 |
+
<tr><td>num_functions, num_loops, num_if, num_try_except, num_return</td><td>Raw structural counts</td></tr>
|
| 173 |
+
<tr><td>line_count</td><td>Total file size</td></tr>
|
| 174 |
+
<tr><td>recursion_flag</td><td>1 if any function calls itself, else 0</td></tr>
|
| 175 |
+
<tr><td>global_variable_count</td><td>Count of <code>global</code> declarations — hidden state / tight coupling</td></tr>
|
| 176 |
+
</table>
|
| 177 |
+
<strong>One technical detail worth knowing cold:</strong> nesting depth is computed via a recursive traversal that increments depth only when it enters a defined set of "nesting nodes" (<code>If, For, While, With, Try, FunctionDef, AsyncFunctionDef, ClassDef</code>) — everything else keeps the current depth.
|
| 178 |
+
</div>
|
| 179 |
+
|
| 180 |
+
<h3>2. Dataset Generation</h3>
|
| 181 |
+
<div class="card">
|
| 182 |
+
Synthetic dataset generated from code templates, not scraped real-world code. <strong>220 total samples, perfectly balanced: 110 labeled Clean (0), 110 labeled Risky (1).</strong> Reproducible via <code>random.seed(42)</code> — same dataset every run.
|
| 183 |
+
</div>
|
| 184 |
+
|
| 185 |
+
<h3>3. Feature Pipeline</h3>
|
| 186 |
+
<div class="card">
|
| 187 |
+
Converts the feature dictionary into a fixed-order numeric vector (<code>FEATURE_SCHEMA</code> defines the order — this order must stay identical between training and inference or predictions break silently). Scales with <code>StandardScaler</code> (mean=0, std=1) so no single feature like <code>line_count</code> dominates just because its raw numbers are bigger. Scaler is pickled and reused at inference time — never re-fit on new data.
|
| 188 |
+
</div>
|
| 189 |
+
|
| 190 |
+
<h3>4. Model Training</h3>
|
| 191 |
+
<div class="card">
|
| 192 |
+
Two models trained side by side on an 80/20 stratified split (176 train / 44 test):
|
| 193 |
+
<table>
|
| 194 |
+
<tr><th>Model</th><th>Key hyperparameters</th></tr>
|
| 195 |
+
<tr><td>Random Forest <span class="pill">primary / production</span></td><td>200 trees, max_depth=10, min_samples_split=4, class_weight="balanced"</td></tr>
|
| 196 |
+
<tr><td>XGBoost <span class="pill">comparison only</span></td><td>200 estimators, max_depth=6, learning_rate=0.1, subsample=0.8</td></tr>
|
| 197 |
+
</table>
|
| 198 |
+
<strong>Random Forest is the one actually used in the live Scoring API</strong> — XGBoost is trained and evaluated for comparison but not deployed.
|
| 199 |
+
</div>
|
| 200 |
+
<div class="warn">
|
| 201 |
+
No saved accuracy/F1 number exists in the repo — the evaluation function prints it live but doesn't persist it to a file. Run <code>python model_trainer.py</code> before your interview and note the actual number rather than guessing one.
|
| 202 |
+
</div>
|
| 203 |
+
|
| 204 |
+
<h3>5. Explanation Engine</h3>
|
| 205 |
+
<div class="card">
|
| 206 |
+
Answers "why is this risky" without recomputing anything new. Logic: for each of the 11 features, check if the code's actual value exceeds a fixed threshold (e.g. <code>max_nesting_depth > 3</code>, <code>cyclomatic_complexity > 5</code>, <code>global_variable_count > 1</code>). Of the features that exceed threshold, rank by the model's feature importance and return the <strong>top 3</strong> as plain-English sentences (e.g. "Deep nesting detected (depth: 7)").
|
| 207 |
+
</div>
|
| 208 |
+
|
| 209 |
+
<h3>6. Scoring API — Final Output</h3>
|
| 210 |
+
<div class="card">
|
| 211 |
+
Single entry point: <code>evaluate(code)</code>. Runs the model's predicted probability of the "risky" class × 100 as the risk score.
|
| 212 |
+
<table>
|
| 213 |
+
<tr><th>Risk Score</th><th>Level</th></tr>
|
| 214 |
+
<tr><td>0–30</td><td style="color:var(--green)">Low</td></tr>
|
| 215 |
+
<tr><td>31–60</td><td style="color:var(--orange)">Medium</td></tr>
|
| 216 |
+
<tr><td>61–100</td><td style="color:var(--red)">High</td></tr>
|
| 217 |
+
</table>
|
| 218 |
+
<pre>{
|
| 219 |
+
"risk_score": 82,
|
| 220 |
+
"risk_level": "High",
|
| 221 |
+
"confidence": 0.82,
|
| 222 |
+
"top_risk_factors": ["Deep nesting detected (depth: 7)", ...]
|
| 223 |
+
}</pre>
|
| 224 |
+
Properties worth naming if asked: deterministic, stateless, fast (no training happens at inference time).
|
| 225 |
+
</div>
|
| 226 |
+
|
| 227 |
+
<h2>Phase 2 — The Refactorer</h2>
|
| 228 |
+
|
| 229 |
+
<div class="card">
|
| 230 |
+
Only runs on code Phase 1 flagged as risky. Uses <strong>CodeT5</strong> (a deep learning code-generation model) to produce multiple refactored candidates, then uses Phase 1's own <code>evaluate()</code> as a reward/ranking function to pick the best one.
|
| 231 |
+
</div>
|
| 232 |
+
|
| 233 |
+
<h3>1. Candidate Generation — 3 Strategies</h3>
|
| 234 |
+
<div class="card">
|
| 235 |
+
Generates one candidate per strategy, each with a different prompt and temperature:
|
| 236 |
+
<table>
|
| 237 |
+
<tr><th>Strategy</th><th>Temperature</th></tr>
|
| 238 |
+
<tr><td>Improve readability and clarity</td><td>0.5 (conservative)</td></tr>
|
| 239 |
+
<tr><td>Reduce nesting and simplify logic</td><td>0.7</td></tr>
|
| 240 |
+
<tr><td>Refactor for strict maintainability best practices</td><td>0.85 (more creative)</td></tr>
|
| 241 |
+
</table>
|
| 242 |
+
<strong>One technical detail:</strong> temperature controls how much the model deviates from the "safe" rewrite — low temperature stays close to minimal edits, high temperature takes bigger structural risks.
|
| 243 |
+
</div>
|
| 244 |
+
|
| 245 |
+
<h3>2. Validation → Selection → Iteration</h3>
|
| 246 |
+
<div class="card">
|
| 247 |
+
Each candidate is checked to <strong>actually compile</strong> (no syntax errors) before it's even considered. Valid candidates are re-scored by feeding them back through the Phase 1 <code>evaluate()</code> API, and the lowest risk score wins that round.
|
| 248 |
+
<br><br>
|
| 249 |
+
<strong>Iterative loop defaults:</strong> <code>target_score=20</code>, <code>max_iterations=3</code>. Stops early if the target is hit, or after 3 rounds regardless. Starts by initializing the best-known score to <code>float('inf')</code> — a simple trick meaning "anything found is automatically an improvement over nothing."
|
| 250 |
+
</div>
|
| 251 |
+
|
| 252 |
+
<h2>Tech Stack</h2>
|
| 253 |
+
<div class="card">
|
| 254 |
+
<table>
|
| 255 |
+
<tr><th>Component</th><th>Technology</th></tr>
|
| 256 |
+
<tr><td>Structural Analysis</td><td>Python <code>ast</code> module</td></tr>
|
| 257 |
+
<tr><td>Risk Prediction</td><td>Random Forest (primary), XGBoost (comparison) — scikit-learn</td></tr>
|
| 258 |
+
<tr><td>Refactoring</td><td>CodeT5 (Transformers / PyTorch)</td></tr>
|
| 259 |
+
<tr><td>API</td><td>Unified <code>main.py</code> entry point + <code>api_server.py</code></td></tr>
|
| 260 |
+
<tr><td>Deployment</td><td>Live at ai-code-maintainability.hmuhammadusman.com</td></tr>
|
| 261 |
+
</table>
|
| 262 |
+
</div>
|
| 263 |
+
|
| 264 |
+
<h2>Numbers to Remember</h2>
|
| 265 |
+
<div class="card">
|
| 266 |
+
<table class="num-table">
|
| 267 |
+
<tr><td>Structural features extracted</td><td>11</td></tr>
|
| 268 |
+
<tr><td>Total dataset samples</td><td>220 (110 Clean / 110 Risky — balanced)</td></tr>
|
| 269 |
+
<tr><td>Train / test split</td><td>176 / 44 (80/20, stratified)</td></tr>
|
| 270 |
+
<tr><td>Random Forest trees</td><td>200 (max_depth=10)</td></tr>
|
| 271 |
+
<tr><td>Risk score range</td><td>0–100</td></tr>
|
| 272 |
+
<tr><td>Top risk factors surfaced</td><td>3</td></tr>
|
| 273 |
+
<tr><td>Refactor candidate strategies</td><td>3 (temps 0.5 / 0.7 / 0.85)</td></tr>
|
| 274 |
+
<tr><td>Default optimizer target / max iterations</td><td>20 / 3</td></tr>
|
| 275 |
+
<tr><td>Reproducibility seed</td><td>42</td></tr>
|
| 276 |
+
</table>
|
| 277 |
+
</div>
|
| 278 |
+
|
| 279 |
+
<h2>Quick Answers — Likely Questions</h2>
|
| 280 |
+
|
| 281 |
+
<div class="q">"Why Random Forest over a simpler rule-based linter?"</div>
|
| 282 |
+
<div class="card">Rule-based linters need every threshold hand-tuned per rule. A trained model learns which combinations of features actually correlate with risk from data, and can weigh 11 signals together instead of checking them independently.</div>
|
| 283 |
+
|
| 284 |
+
<div class="q">"Why two models (RF + XGBoost) if only one is deployed?"</div>
|
| 285 |
+
<div class="card">Comparison during development — training both and comparing accuracy tells you whether the extra complexity of boosting is worth it on this dataset size before committing to one in production.</div>
|
| 286 |
+
|
| 287 |
+
<div class="q">"Isn't 220 samples very small for ML?"</div>
|
| 288 |
+
<div class="card">Yes — be upfront about this if asked. It's a synthetic, template-generated dataset, not real-world code, which is both the honest limitation and a natural "what I'd improve next" answer (train on real open-source repos labeled by actual maintainability metrics).</div>
|
| 289 |
+
|
| 290 |
+
<div class="q">"How does Phase 2 know a candidate is actually better, not just different?"</div>
|
| 291 |
+
<div class="card">It doesn't trust the language model's own judgment — every candidate gets re-scored through the exact same Phase 1 <code>evaluate()</code> pipeline used on the original code, so "better" is measured by the same objective risk score, not by how the refactor looks.</div>
|
| 292 |
+
|
| 293 |
+
<div class="q">"What would you improve?"</div>
|
| 294 |
+
<div class="card">Real-world training data instead of synthetic templates; persisting evaluation metrics instead of only printing them; and multi-file / cross-function analysis, since right now every file is scored in isolation.</div>
|
| 295 |
+
|
| 296 |
+
<h2>One-Line Summary to Open With</h2>
|
| 297 |
+
<div class="summary-box">
|
| 298 |
+
"I built a two-phase AI system — Phase 1 uses AST parsing and a Random Forest model to score Python code's maintainability risk out of 100 and explain why, and Phase 2 uses a CodeT5 deep learning model to iteratively generate and select better refactored versions of risky code, using the Phase 1 score itself as the judge of improvement."
|
| 299 |
+
</div>
|
| 300 |
+
|
| 301 |
+
</div>
|
| 302 |
+
</body>
|
| 303 |
+
</html>
|
Dockerfile
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
# Set environment variables
|
| 4 |
+
ENV PYTHONUNBUFFERED=1 \
|
| 5 |
+
PORT=7860 \
|
| 6 |
+
HOME=/home/user \
|
| 7 |
+
HF_HOME=/tmp/huggingface \
|
| 8 |
+
TRANSFORMERS_CACHE=/tmp/huggingface
|
| 9 |
+
|
| 10 |
+
# Install system dependencies
|
| 11 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 12 |
+
build-essential \
|
| 13 |
+
curl \
|
| 14 |
+
git \
|
| 15 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 16 |
+
|
| 17 |
+
# Create non-root user for Hugging Face Spaces (UID 1000)
|
| 18 |
+
RUN useradd -m -u 1000 user
|
| 19 |
+
WORKDIR /home/user/app
|
| 20 |
+
|
| 21 |
+
# Install PyTorch CPU first to keep image lightweight and fast
|
| 22 |
+
RUN pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cpu
|
| 23 |
+
|
| 24 |
+
# Install remaining Python dependencies
|
| 25 |
+
COPY --chown=user:user requirements.txt .
|
| 26 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 27 |
+
|
| 28 |
+
# Create HF cache directory with correct permissions
|
| 29 |
+
RUN mkdir -p /tmp/huggingface && chown -R user:user /tmp/huggingface
|
| 30 |
+
|
| 31 |
+
# Copy application files
|
| 32 |
+
COPY --chown=user:user . .
|
| 33 |
+
|
| 34 |
+
USER user
|
| 35 |
+
|
| 36 |
+
# Hugging Face Space default port
|
| 37 |
+
EXPOSE 7860
|
| 38 |
+
|
| 39 |
+
# Start FastAPI application
|
| 40 |
+
CMD ["python", "api_server.py"]
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Usman
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
CHANGED
|
@@ -1,10 +1,67 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
|
|
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: AI Code Maintainability Scoring Engine
|
| 3 |
+
emoji: 🚀
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
pinned: false
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# AI Code Maintainability Scoring & Refactoring Engine
|
| 12 |
+
|
| 13 |
+
🚀 **Live Demo:** [ai-code-maintainability.hmuhammadusman.com](https://ai-code-maintainability.hmuhammadusman.com)
|
| 14 |
+
|
| 15 |
+
## Overview
|
| 16 |
+
The **AI Code Maintainability Scoring Engine** is a modular, machine-learning-based system designed to evaluate the structural quality of Python code and autonomously refactor it. Rather than relying on rigid rule-based linters (like PyLint or Flake8), this engine uses a supervised learning approach (Random Forest) to predict maintainability risks dynamically based on pure structural signals, and then utilizes Deep Learning (CodeT5) to actively improve the code.
|
| 17 |
+
|
| 18 |
+
This repository contains the completely unified Engine:
|
| 19 |
+
- **Phase 1 (The Evaluator)**: The Core Scoring Engine. It evaluates code and assigns a risk score (0–100), a risk category, and surfaces top structural risk factors.
|
| 20 |
+
- **Phase 2 (The Refactorer)**: The Autonomous Refactoring Agent. It generates, validates, and selects better candidates for structurally risky code to iteratively improve the risk score.
|
| 21 |
+
|
| 22 |
+
## System Architecture
|
| 23 |
+
|
| 24 |
+
The engine is built on a strictly modular architecture.
|
| 25 |
+
|
| 26 |
+
### Evaluator (Phase 1)
|
| 27 |
+
1. **AST Analyzer (`ast_analyzer.py`)**: Parses Python Abstract Syntax Trees (AST) to extract 11 core structural metrics.
|
| 28 |
+
2. **ML Risk Model (`model_trainer.py`)**: Uses a Random Forest classifier for stateless inference of risk scores.
|
| 29 |
+
3. **Explanation Engine (`explanation_engine.py`)**: Converts raw feature importances into plain-English explanations.
|
| 30 |
+
4. **Scoring API (`scoring_api.py`)**: The central access point exposing the master `evaluate()` backend function.
|
| 31 |
+
|
| 32 |
+
### Refactorer (Phase 2)
|
| 33 |
+
1. **Candidate Generator (`phase2/candidate_generator.py`)**: Uses a localized Deep Learning model (`CodeT5`) to generate multiple refactored versions of the code.
|
| 34 |
+
2. **Code Validator (`phase2/validation.py`)**: Ensures that generated candidates compile and do not contain syntax errors.
|
| 35 |
+
3. **Candidate Selector (`phase2/selector.py`)**: Uses the `Scoring API` to rank candidates and selects the one that provides the best improvement in the maintainability risk score.
|
| 36 |
+
4. **Iterative Optimizer (`phase2/optimization.py`)**: Manages the iterative loops to continuously refine the code until a target score is reached.
|
| 37 |
+
|
| 38 |
+
## Extracted Structural Features
|
| 39 |
+
The Evaluator rates maintainability using the following exact signals:
|
| 40 |
+
- Maximum Nesting Depth (*Strongest Indicator*)
|
| 41 |
+
- Cyclomatic Complexity Approximation
|
| 42 |
+
- Average Function Length and Total Line Count
|
| 43 |
+
- Branching Density (`if`/`elif`)
|
| 44 |
+
- Loop Density (`for`/`while`)
|
| 45 |
+
- Error Handling Density (`try`/`except`)
|
| 46 |
+
- Number of Functions and Return Points
|
| 47 |
+
- Global Variable Declarations
|
| 48 |
+
- Recursion Detection
|
| 49 |
+
|
| 50 |
+
## Quick Start
|
| 51 |
+
|
| 52 |
+
### Installation
|
| 53 |
+
Ensure you have Python 3.8+ installed. Install the required dependencies:
|
| 54 |
+
```bash
|
| 55 |
+
pip install scikit-learn xgboost numpy transformers torch
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
### Usage
|
| 59 |
+
To analyze and refactor a Python file automatically, you can use the unified `main.py` entry point:
|
| 60 |
+
|
| 61 |
+
```bash
|
| 62 |
+
python main.py path/to/your/messy_code.py
|
| 63 |
+
```
|
| 64 |
+
This will:
|
| 65 |
+
1. Provide the initial maintainability score.
|
| 66 |
+
2. Interactively use Deep Learning to refactor the code.
|
| 67 |
+
3. Output the cleaned code and the final score improvement.
|
api_server.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import os
|
| 3 |
+
import psutil
|
| 4 |
+
|
| 5 |
+
from fastapi import FastAPI
|
| 6 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 7 |
+
from fastapi.staticfiles import StaticFiles
|
| 8 |
+
from pydantic import BaseModel
|
| 9 |
+
|
| 10 |
+
from scoring_api import evaluate
|
| 11 |
+
from phase2.optimization import IterativeOptimizer
|
| 12 |
+
|
| 13 |
+
app = FastAPI(title="AI Code Maintainability Scoring Engine API")
|
| 14 |
+
|
| 15 |
+
app.add_middleware(
|
| 16 |
+
CORSMiddleware,
|
| 17 |
+
allow_origins=["*"],
|
| 18 |
+
allow_methods=["*"],
|
| 19 |
+
allow_headers=["*"],
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class ScoreRequest(BaseModel):
|
| 24 |
+
code: str
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class RefactorRequest(BaseModel):
|
| 28 |
+
code: str
|
| 29 |
+
target_score: float = 20.0
|
| 30 |
+
max_iterations: int = 3
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@app.get("/api/health")
|
| 34 |
+
def health():
|
| 35 |
+
proc = psutil.Process()
|
| 36 |
+
mem_info = proc.memory_info()
|
| 37 |
+
sys_mem = psutil.virtual_memory()
|
| 38 |
+
return {
|
| 39 |
+
"status": "ok",
|
| 40 |
+
"service": "AI Code Maintainability Scoring Engine",
|
| 41 |
+
"memory": {
|
| 42 |
+
"process_rss_mb": round(mem_info.rss / (1024 * 1024), 2),
|
| 43 |
+
"process_vms_mb": round(mem_info.vms / (1024 * 1024), 2),
|
| 44 |
+
"process_memory_percent": round(proc.memory_percent(), 2),
|
| 45 |
+
"system_total_gb": round(sys_mem.total / (1024**3), 2),
|
| 46 |
+
"system_available_gb": round(sys_mem.available / (1024**3), 2),
|
| 47 |
+
"system_used_percent": sys_mem.percent,
|
| 48 |
+
},
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
@app.post("/api/score")
|
| 53 |
+
def score(request: ScoreRequest):
|
| 54 |
+
return evaluate(request.code)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@app.post("/api/refactor")
|
| 58 |
+
def refactor(request: RefactorRequest):
|
| 59 |
+
optimizer = IterativeOptimizer(
|
| 60 |
+
target_score=request.target_score,
|
| 61 |
+
max_iterations=request.max_iterations,
|
| 62 |
+
)
|
| 63 |
+
result = optimizer.optimize(request.code)
|
| 64 |
+
if not math.isfinite(result["final_score"]):
|
| 65 |
+
result["final_score"] = None
|
| 66 |
+
return result
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
# Mount frontend dist static files if present
|
| 70 |
+
frontend_dist = os.path.join(os.path.dirname(__file__), "frontend", "dist")
|
| 71 |
+
if os.path.exists(frontend_dist):
|
| 72 |
+
app.mount("/", StaticFiles(directory=frontend_dist, html=True), name="frontend")
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
if __name__ == "__main__":
|
| 76 |
+
import uvicorn
|
| 77 |
+
|
| 78 |
+
port = int(os.environ.get("PORT", 7860))
|
| 79 |
+
uvicorn.run("api_server:app", host="0.0.0.0", port=port)
|
ast_analyzer.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ast
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
def _get_max_nesting_depth(tree):
|
| 5 |
+
|
| 6 |
+
NESTING_NODES = (
|
| 7 |
+
ast.If,
|
| 8 |
+
ast.For,
|
| 9 |
+
ast.While,
|
| 10 |
+
ast.With,
|
| 11 |
+
ast.Try,
|
| 12 |
+
ast.FunctionDef,
|
| 13 |
+
ast.AsyncFunctionDef,
|
| 14 |
+
ast.ClassDef,
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
def _walk_depth(node, current_depth):
|
| 18 |
+
|
| 19 |
+
max_depth = current_depth
|
| 20 |
+
|
| 21 |
+
for child in ast.iter_child_nodes(node):
|
| 22 |
+
if isinstance(child, NESTING_NODES):
|
| 23 |
+
child_depth = _walk_depth(child, current_depth + 1)
|
| 24 |
+
max_depth = max(max_depth, child_depth)
|
| 25 |
+
else:
|
| 26 |
+
child_depth = _walk_depth(child, current_depth)
|
| 27 |
+
max_depth = max(max_depth, child_depth)
|
| 28 |
+
|
| 29 |
+
return max_depth
|
| 30 |
+
|
| 31 |
+
return _walk_depth(tree, 0)
|
| 32 |
+
|
| 33 |
+
def _get_cyclomatic_complexity(tree):
|
| 34 |
+
|
| 35 |
+
complexity = 1
|
| 36 |
+
|
| 37 |
+
for node in ast.walk(tree):
|
| 38 |
+
if isinstance(node, (
|
| 39 |
+
ast.If,
|
| 40 |
+
ast.For,
|
| 41 |
+
ast.While,
|
| 42 |
+
ast.ExceptHandler,
|
| 43 |
+
ast.With,
|
| 44 |
+
ast.Assert,
|
| 45 |
+
)):
|
| 46 |
+
complexity += 1
|
| 47 |
+
|
| 48 |
+
if isinstance(node, ast.BoolOp):
|
| 49 |
+
complexity += len(node.values) - 1
|
| 50 |
+
|
| 51 |
+
return complexity
|
| 52 |
+
|
| 53 |
+
def _get_avg_function_length(tree):
|
| 54 |
+
|
| 55 |
+
function_lengths = []
|
| 56 |
+
|
| 57 |
+
for node in ast.walk(tree):
|
| 58 |
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
| 59 |
+
if hasattr(node, 'end_lineno') and hasattr(node, 'lineno'):
|
| 60 |
+
length = node.end_lineno - node.lineno + 1
|
| 61 |
+
function_lengths.append(length)
|
| 62 |
+
|
| 63 |
+
if not function_lengths:
|
| 64 |
+
return 0
|
| 65 |
+
|
| 66 |
+
return sum(function_lengths) / len(function_lengths)
|
| 67 |
+
|
| 68 |
+
def _check_recursion(tree):
|
| 69 |
+
|
| 70 |
+
for node in ast.walk(tree):
|
| 71 |
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
| 72 |
+
func_name = node.name
|
| 73 |
+
|
| 74 |
+
for inner_node in ast.walk(node):
|
| 75 |
+
if isinstance(inner_node, ast.Call):
|
| 76 |
+
if isinstance(inner_node.func, ast.Name):
|
| 77 |
+
if inner_node.func.id == func_name:
|
| 78 |
+
return 1
|
| 79 |
+
|
| 80 |
+
return 0
|
| 81 |
+
|
| 82 |
+
def _count_global_variables(tree):
|
| 83 |
+
|
| 84 |
+
global_count = 0
|
| 85 |
+
|
| 86 |
+
for node in ast.walk(tree):
|
| 87 |
+
if isinstance(node, ast.Global):
|
| 88 |
+
global_count += len(node.names)
|
| 89 |
+
|
| 90 |
+
return global_count
|
| 91 |
+
|
| 92 |
+
def extract_features(source):
|
| 93 |
+
|
| 94 |
+
if os.path.isfile(source):
|
| 95 |
+
with open(source, 'r', encoding='utf-8') as f:
|
| 96 |
+
code = f.read()
|
| 97 |
+
else:
|
| 98 |
+
code = source
|
| 99 |
+
|
| 100 |
+
try:
|
| 101 |
+
tree = ast.parse(code)
|
| 102 |
+
except SyntaxError as e:
|
| 103 |
+
return {
|
| 104 |
+
"error": True,
|
| 105 |
+
"message": f"Syntax error in code: {e.msg}",
|
| 106 |
+
"line": e.lineno,
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
num_functions = 0
|
| 110 |
+
num_loops = 0
|
| 111 |
+
num_if = 0
|
| 112 |
+
num_try_except = 0
|
| 113 |
+
num_return = 0
|
| 114 |
+
|
| 115 |
+
for node in ast.walk(tree):
|
| 116 |
+
|
| 117 |
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
| 118 |
+
num_functions += 1
|
| 119 |
+
|
| 120 |
+
elif isinstance(node, (ast.For, ast.While)):
|
| 121 |
+
num_loops += 1
|
| 122 |
+
|
| 123 |
+
elif isinstance(node, ast.If):
|
| 124 |
+
num_if += 1
|
| 125 |
+
|
| 126 |
+
elif isinstance(node, ast.Try):
|
| 127 |
+
num_try_except += 1
|
| 128 |
+
|
| 129 |
+
elif isinstance(node, ast.Return):
|
| 130 |
+
num_return += 1
|
| 131 |
+
|
| 132 |
+
line_count = len(code.splitlines())
|
| 133 |
+
max_nesting_depth = _get_max_nesting_depth(tree)
|
| 134 |
+
cyclomatic_complexity = _get_cyclomatic_complexity(tree)
|
| 135 |
+
avg_function_length = _get_avg_function_length(tree)
|
| 136 |
+
recursion_flag = _check_recursion(tree)
|
| 137 |
+
global_variable_count = _count_global_variables(tree)
|
| 138 |
+
|
| 139 |
+
features = {
|
| 140 |
+
"num_functions": num_functions,
|
| 141 |
+
"num_loops": num_loops,
|
| 142 |
+
"num_if": num_if,
|
| 143 |
+
"num_try_except": num_try_except,
|
| 144 |
+
"num_return": num_return,
|
| 145 |
+
"line_count": line_count,
|
| 146 |
+
"max_nesting_depth": max_nesting_depth,
|
| 147 |
+
"cyclomatic_complexity":cyclomatic_complexity,
|
| 148 |
+
"avg_function_length": round(avg_function_length, 2),
|
| 149 |
+
"recursion_flag": recursion_flag,
|
| 150 |
+
"global_variable_count":global_variable_count,
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
return features
|
| 154 |
+
|
| 155 |
+
if __name__ == "__main__":
|
| 156 |
+
|
| 157 |
+
sample_code = "def sample():\n pass"
|
| 158 |
+
|
| 159 |
+
result = extract_features(sample_code)
|
| 160 |
+
|
| 161 |
+
print("=" * 50)
|
| 162 |
+
print(" AST Feature Extraction Results")
|
| 163 |
+
print("=" * 50)
|
| 164 |
+
for feature, value in result.items():
|
| 165 |
+
print(f" {feature:<25} : {value}")
|
| 166 |
+
print("=" * 50)
|
data/dataset.csv
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
num_functions,num_loops,num_if,num_try_except,num_return,line_count,max_nesting_depth,cyclomatic_complexity,avg_function_length,recursion_flag,global_variable_count,label
|
| 2 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 3 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 4 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 5 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 6 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 7 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 8 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 9 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 10 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 11 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 12 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 13 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 14 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 15 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 16 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 17 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 18 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 19 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 20 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 21 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 22 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 23 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 24 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 25 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 26 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 27 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 28 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 29 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 30 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 31 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 32 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 33 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 34 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 35 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 36 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 37 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 38 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 39 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 40 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 41 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 42 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 43 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 44 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 45 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 46 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 47 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 48 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 49 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 50 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 51 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 52 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 53 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 54 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 55 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 56 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 57 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 58 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 59 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 60 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 61 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 62 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 63 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 64 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 65 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 66 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 67 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 68 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 69 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 70 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 71 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 72 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 73 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 74 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 75 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 76 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 77 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 78 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 79 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 80 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 81 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 82 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 83 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 84 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 85 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 86 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 87 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 88 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 89 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 90 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 91 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 92 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 93 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 94 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 95 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 96 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 97 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 98 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 99 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 100 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 101 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 102 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 103 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 104 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 105 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 106 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 107 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 108 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 109 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 110 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 111 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 112 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 113 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 114 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 115 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 116 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 117 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 118 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 119 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 120 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 121 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 122 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 123 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 124 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 125 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 126 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 127 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 128 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 129 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 130 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 131 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 132 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 133 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 134 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 135 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 136 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 137 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 138 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 139 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 140 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 141 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 142 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 143 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 144 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 145 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 146 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 147 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 148 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 149 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 150 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 151 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 152 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 153 |
+
1,4,9,1,1,35,5,15,34.0,0,0,1
|
| 154 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 155 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 156 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 157 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 158 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 159 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 160 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 161 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 162 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 163 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 164 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 165 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 166 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 167 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 168 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 169 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 170 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 171 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 172 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 173 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 174 |
+
3,1,0,0,3,12,2,2,3.0,0,0,0
|
| 175 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 176 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 177 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 178 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 179 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 180 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 181 |
+
1,1,3,3,1,20,8,8,19.0,0,0,1
|
| 182 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 183 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 184 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 185 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 186 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 187 |
+
2,0,0,0,2,7,1,1,2.5,0,0,0
|
| 188 |
+
1,3,3,1,1,19,8,8,16.0,0,1,1
|
| 189 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 190 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 191 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 192 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 193 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 194 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 195 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 196 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 197 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 198 |
+
1,4,2,0,1,14,7,7,11.0,0,1,1
|
| 199 |
+
2,0,4,0,6,14,2,5,6.0,1,0,1
|
| 200 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 201 |
+
1,0,2,0,3,8,3,3,7.0,0,0,0
|
| 202 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 203 |
+
2,1,0,0,2,9,2,2,3.5,0,0,0
|
| 204 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 205 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 206 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 207 |
+
1,2,4,0,3,13,7,7,12.0,1,0,1
|
| 208 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 209 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 210 |
+
1,0,8,0,8,21,5,15,20.0,0,0,1
|
| 211 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
| 212 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 213 |
+
1,1,3,1,4,18,5,6,15.0,1,1,1
|
| 214 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 215 |
+
1,1,1,0,1,7,3,3,6.0,0,0,0
|
| 216 |
+
2,0,2,0,4,10,2,3,4.0,0,0,0
|
| 217 |
+
3,0,0,0,3,11,1,1,2.0,0,0,0
|
| 218 |
+
1,3,4,1,1,24,9,9,23.0,0,0,1
|
| 219 |
+
2,0,0,0,2,6,1,1,2.0,0,0,0
|
| 220 |
+
2,0,0,2,4,12,2,3,5.0,0,0,0
|
| 221 |
+
1,2,3,0,1,17,6,6,13.0,0,2,1
|
dataset_generator.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import csv
|
| 2 |
+
import os
|
| 3 |
+
import random
|
| 4 |
+
from ast_analyzer import extract_features
|
| 5 |
+
|
| 6 |
+
random.seed(42)
|
| 7 |
+
|
| 8 |
+
OUTPUT_PATH = os.path.join(os.path.dirname(__file__), "data", "dataset.csv")
|
| 9 |
+
|
| 10 |
+
FEATURE_COLUMNS = [
|
| 11 |
+
"num_functions",
|
| 12 |
+
"num_loops",
|
| 13 |
+
"num_if",
|
| 14 |
+
"num_try_except",
|
| 15 |
+
"num_return",
|
| 16 |
+
"line_count",
|
| 17 |
+
"max_nesting_depth",
|
| 18 |
+
"cyclomatic_complexity",
|
| 19 |
+
"avg_function_length",
|
| 20 |
+
"recursion_flag",
|
| 21 |
+
"global_variable_count",
|
| 22 |
+
"label",
|
| 23 |
+
]
|
| 24 |
+
|
| 25 |
+
def _make_clean_snippet(index):
|
| 26 |
+
|
| 27 |
+
variant = index % 10
|
| 28 |
+
|
| 29 |
+
if variant == 0:
|
| 30 |
+
return f
|
| 31 |
+
|
| 32 |
+
elif variant == 1:
|
| 33 |
+
return f
|
| 34 |
+
|
| 35 |
+
elif variant == 2:
|
| 36 |
+
return f
|
| 37 |
+
|
| 38 |
+
elif variant == 3:
|
| 39 |
+
return f
|
| 40 |
+
|
| 41 |
+
elif variant == 4:
|
| 42 |
+
return f
|
| 43 |
+
|
| 44 |
+
elif variant == 5:
|
| 45 |
+
return f
|
| 46 |
+
|
| 47 |
+
elif variant == 6:
|
| 48 |
+
return f
|
| 49 |
+
|
| 50 |
+
elif variant == 7:
|
| 51 |
+
return f
|
| 52 |
+
|
| 53 |
+
elif variant == 8:
|
| 54 |
+
return f
|
| 55 |
+
|
| 56 |
+
else:
|
| 57 |
+
return f
|
| 58 |
+
|
| 59 |
+
def _make_risky_snippet(index):
|
| 60 |
+
|
| 61 |
+
variant = index % 10
|
| 62 |
+
|
| 63 |
+
if variant == 0:
|
| 64 |
+
return f
|
| 65 |
+
|
| 66 |
+
elif variant == 1:
|
| 67 |
+
return f
|
| 68 |
+
|
| 69 |
+
elif variant == 2:
|
| 70 |
+
return f
|
| 71 |
+
|
| 72 |
+
elif variant == 3:
|
| 73 |
+
return f
|
| 74 |
+
|
| 75 |
+
elif variant == 4:
|
| 76 |
+
return f
|
| 77 |
+
|
| 78 |
+
elif variant == 5:
|
| 79 |
+
return f
|
| 80 |
+
|
| 81 |
+
elif variant == 6:
|
| 82 |
+
return f
|
| 83 |
+
|
| 84 |
+
elif variant == 7:
|
| 85 |
+
return f
|
| 86 |
+
|
| 87 |
+
elif variant == 8:
|
| 88 |
+
return f
|
| 89 |
+
|
| 90 |
+
else:
|
| 91 |
+
return f
|
| 92 |
+
|
| 93 |
+
def generate_dataset(n_clean=110, n_risky=110):
|
| 94 |
+
|
| 95 |
+
dataset = []
|
| 96 |
+
skipped = 0
|
| 97 |
+
|
| 98 |
+
print(f"Generating {n_clean} clean snippets...")
|
| 99 |
+
|
| 100 |
+
for i in range(n_clean):
|
| 101 |
+
code = _make_clean_snippet(i)
|
| 102 |
+
features = extract_features(code)
|
| 103 |
+
|
| 104 |
+
if features.get("error"):
|
| 105 |
+
skipped += 1
|
| 106 |
+
continue
|
| 107 |
+
|
| 108 |
+
features["label"] = 0
|
| 109 |
+
dataset.append(features)
|
| 110 |
+
|
| 111 |
+
print(f"Generating {n_risky} risky snippets...")
|
| 112 |
+
|
| 113 |
+
for i in range(n_risky):
|
| 114 |
+
code = _make_risky_snippet(i)
|
| 115 |
+
features = extract_features(code)
|
| 116 |
+
|
| 117 |
+
if features.get("error"):
|
| 118 |
+
skipped += 1
|
| 119 |
+
continue
|
| 120 |
+
|
| 121 |
+
features["label"] = 1
|
| 122 |
+
dataset.append(features)
|
| 123 |
+
|
| 124 |
+
random.shuffle(dataset)
|
| 125 |
+
|
| 126 |
+
print(f"\nDataset ready: {len(dataset)} samples ({skipped} skipped due to errors)")
|
| 127 |
+
return dataset
|
| 128 |
+
|
| 129 |
+
def save_dataset(dataset, output_path=OUTPUT_PATH):
|
| 130 |
+
|
| 131 |
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
| 132 |
+
|
| 133 |
+
with open(output_path, 'w', newline='', encoding='utf-8') as f:
|
| 134 |
+
writer = csv.DictWriter(f, fieldnames=FEATURE_COLUMNS)
|
| 135 |
+
writer.writeheader()
|
| 136 |
+
|
| 137 |
+
for row in dataset:
|
| 138 |
+
filtered_row = {col: row.get(col, 0) for col in FEATURE_COLUMNS}
|
| 139 |
+
writer.writerow(filtered_row)
|
| 140 |
+
|
| 141 |
+
print(f"Dataset saved to: {output_path}")
|
| 142 |
+
|
| 143 |
+
def print_summary(dataset):
|
| 144 |
+
|
| 145 |
+
clean_rows = [r for r in dataset if r["label"] == 0]
|
| 146 |
+
risky_rows = [r for r in dataset if r["label"] == 1]
|
| 147 |
+
|
| 148 |
+
print("\n" + "=" * 55)
|
| 149 |
+
print(" DATASET SUMMARY")
|
| 150 |
+
print("=" * 55)
|
| 151 |
+
print(f" Total samples : {len(dataset)}")
|
| 152 |
+
print(f" Clean (label=0) : {len(clean_rows)}")
|
| 153 |
+
print(f" Risky (label=1) : {len(risky_rows)}")
|
| 154 |
+
print("-" * 55)
|
| 155 |
+
print(f" {'Feature':<28} {'Clean Avg':>10} {'Risky Avg':>10}")
|
| 156 |
+
print("-" * 55)
|
| 157 |
+
|
| 158 |
+
for col in FEATURE_COLUMNS[:-1]:
|
| 159 |
+
clean_avg = sum(r[col] for r in clean_rows) / len(clean_rows) if clean_rows else 0
|
| 160 |
+
risky_avg = sum(r[col] for r in risky_rows) / len(risky_rows) if risky_rows else 0
|
| 161 |
+
print(f" {col:<28} {clean_avg:>10.2f} {risky_avg:>10.2f}")
|
| 162 |
+
|
| 163 |
+
print("=" * 55)
|
| 164 |
+
|
| 165 |
+
if __name__ == "__main__":
|
| 166 |
+
|
| 167 |
+
dataset = generate_dataset(n_clean=110, n_risky=110)
|
| 168 |
+
print_summary(dataset)
|
| 169 |
+
save_dataset(dataset)
|
explanation_engine.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
|
| 3 |
+
try:
|
| 4 |
+
sys.stdout.reconfigure(encoding='utf-8')
|
| 5 |
+
sys.stderr.reconfigure(encoding='utf-8')
|
| 6 |
+
except Exception:
|
| 7 |
+
pass
|
| 8 |
+
|
| 9 |
+
from feature_pipeline import FEATURE_SCHEMA
|
| 10 |
+
|
| 11 |
+
FEATURE_DESCRIPTIONS = {
|
| 12 |
+
"num_functions": "Many small functions defined (count: {value})",
|
| 13 |
+
"num_loops": "High loop count detected (count: {value})",
|
| 14 |
+
"num_if": "Excessive branching / if-statements (count: {value})",
|
| 15 |
+
"num_try_except": "Heavy error handling blocks (count: {value})",
|
| 16 |
+
"num_return": "High number of return statements (count: {value})",
|
| 17 |
+
"line_count": "Large codebase size (lines: {value})",
|
| 18 |
+
"max_nesting_depth": "Deep nesting detected (depth: {value})",
|
| 19 |
+
"cyclomatic_complexity":"High cyclomatic complexity (score: {value})",
|
| 20 |
+
"avg_function_length": "Large function size (avg: {value} lines)",
|
| 21 |
+
"recursion_flag": "Recursion detected in code",
|
| 22 |
+
"global_variable_count":"Global variables used (count: {value})",
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
RISK_THRESHOLDS = {
|
| 26 |
+
"num_functions": 5,
|
| 27 |
+
"num_loops": 3,
|
| 28 |
+
"num_if": 5,
|
| 29 |
+
"num_try_except": 2,
|
| 30 |
+
"num_return": 4,
|
| 31 |
+
"line_count": 40,
|
| 32 |
+
"max_nesting_depth": 3,
|
| 33 |
+
"cyclomatic_complexity": 5,
|
| 34 |
+
"avg_function_length": 15,
|
| 35 |
+
"recursion_flag": 0,
|
| 36 |
+
"global_variable_count": 1,
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
def explain(model, feature_dict, top_n=3):
|
| 40 |
+
importances = model.feature_importances_
|
| 41 |
+
importance_map = dict(zip(FEATURE_SCHEMA, importances))
|
| 42 |
+
risky_features = []
|
| 43 |
+
|
| 44 |
+
for feature_name in FEATURE_SCHEMA:
|
| 45 |
+
raw_value = float(feature_dict.get(feature_name, 0))
|
| 46 |
+
threshold = RISK_THRESHOLDS.get(feature_name, 0)
|
| 47 |
+
importance = importance_map.get(feature_name, 0)
|
| 48 |
+
|
| 49 |
+
if raw_value > threshold:
|
| 50 |
+
risky_features.append((feature_name, importance))
|
| 51 |
+
|
| 52 |
+
risky_features.sort(key=lambda x: x[1], reverse=True)
|
| 53 |
+
top_features = risky_features[:top_n]
|
| 54 |
+
|
| 55 |
+
explanations = []
|
| 56 |
+
|
| 57 |
+
for feature_name, _ in top_features:
|
| 58 |
+
raw_value = feature_dict.get(feature_name, 0)
|
| 59 |
+
template = FEATURE_DESCRIPTIONS[feature_name]
|
| 60 |
+
|
| 61 |
+
if "{value}" in template:
|
| 62 |
+
sentence = template.format(value=raw_value)
|
| 63 |
+
else:
|
| 64 |
+
sentence = template
|
| 65 |
+
|
| 66 |
+
explanations.append(sentence)
|
| 67 |
+
|
| 68 |
+
if not explanations:
|
| 69 |
+
explanations.append("No significant structural risk factors detected.")
|
| 70 |
+
|
| 71 |
+
return explanations
|
| 72 |
+
|
| 73 |
+
if __name__ == "__main__":
|
| 74 |
+
from model_trainer import load_model
|
| 75 |
+
|
| 76 |
+
model = load_model()
|
| 77 |
+
|
| 78 |
+
risky_features = {
|
| 79 |
+
"num_functions": 2,
|
| 80 |
+
"num_loops": 4,
|
| 81 |
+
"num_if": 9,
|
| 82 |
+
"num_try_except": 1,
|
| 83 |
+
"num_return": 1,
|
| 84 |
+
"line_count": 35,
|
| 85 |
+
"max_nesting_depth": 7,
|
| 86 |
+
"cyclomatic_complexity": 15,
|
| 87 |
+
"avg_function_length": 34.0,
|
| 88 |
+
"recursion_flag": 0,
|
| 89 |
+
"global_variable_count": 0,
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
clean_features = {
|
| 93 |
+
"num_functions": 2,
|
| 94 |
+
"num_loops": 1,
|
| 95 |
+
"num_if": 2,
|
| 96 |
+
"num_try_except": 0,
|
| 97 |
+
"num_return": 2,
|
| 98 |
+
"line_count": 10,
|
| 99 |
+
"max_nesting_depth": 2,
|
| 100 |
+
"cyclomatic_complexity": 3,
|
| 101 |
+
"avg_function_length": 4.0,
|
| 102 |
+
"recursion_flag": 0,
|
| 103 |
+
"global_variable_count": 0,
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
print("=" * 55)
|
| 107 |
+
print(" EXPLANATION ENGINE — TEST")
|
| 108 |
+
print("=" * 55)
|
| 109 |
+
|
| 110 |
+
print("\n🔴 Risky Code — Top Risk Factors:")
|
| 111 |
+
risky_explanations = explain(model, risky_features, top_n=3)
|
| 112 |
+
for i, reason in enumerate(risky_explanations, start=1):
|
| 113 |
+
print(f" {i}. {reason}")
|
| 114 |
+
|
| 115 |
+
print("\n🟢 Clean Code — Top Risk Factors:")
|
| 116 |
+
clean_explanations = explain(model, clean_features, top_n=3)
|
| 117 |
+
for i, reason in enumerate(clean_explanations, start=1):
|
| 118 |
+
print(f" {i}. {reason}")
|
| 119 |
+
|
| 120 |
+
print("=" * 55)
|
| 121 |
+
|
feature_pipeline.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import csv
|
| 3 |
+
import pickle
|
| 4 |
+
|
| 5 |
+
from sklearn.preprocessing import StandardScaler
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
|
| 9 |
+
FEATURE_SCHEMA = [
|
| 10 |
+
"num_functions",
|
| 11 |
+
"num_loops",
|
| 12 |
+
"num_if",
|
| 13 |
+
"num_try_except",
|
| 14 |
+
"num_return",
|
| 15 |
+
"line_count",
|
| 16 |
+
"max_nesting_depth",
|
| 17 |
+
"cyclomatic_complexity",
|
| 18 |
+
"avg_function_length",
|
| 19 |
+
"recursion_flag",
|
| 20 |
+
"global_variable_count",
|
| 21 |
+
]
|
| 22 |
+
|
| 23 |
+
SCHEMA_VERSION = "v1.0"
|
| 24 |
+
|
| 25 |
+
SCALER_PATH = os.path.join(os.path.dirname(__file__), "models", "scaler.pkl")
|
| 26 |
+
|
| 27 |
+
def build_feature_vector(feature_dict):
|
| 28 |
+
|
| 29 |
+
vector = []
|
| 30 |
+
|
| 31 |
+
for feature_name in FEATURE_SCHEMA:
|
| 32 |
+
value = feature_dict.get(feature_name, 0)
|
| 33 |
+
vector.append(float(value))
|
| 34 |
+
|
| 35 |
+
return vector
|
| 36 |
+
|
| 37 |
+
def load_dataset(csv_path):
|
| 38 |
+
|
| 39 |
+
X = []
|
| 40 |
+
y = []
|
| 41 |
+
|
| 42 |
+
with open(csv_path, 'r', encoding='utf-8') as f:
|
| 43 |
+
reader = csv.DictReader(f)
|
| 44 |
+
|
| 45 |
+
for row in reader:
|
| 46 |
+
vector = build_feature_vector(row)
|
| 47 |
+
label = int(row["label"])
|
| 48 |
+
|
| 49 |
+
X.append(vector)
|
| 50 |
+
y.append(label)
|
| 51 |
+
|
| 52 |
+
X = np.array(X)
|
| 53 |
+
y = np.array(y)
|
| 54 |
+
|
| 55 |
+
return X, y
|
| 56 |
+
|
| 57 |
+
def fit_and_save_scaler(X, scaler_path=SCALER_PATH):
|
| 58 |
+
|
| 59 |
+
scaler = StandardScaler()
|
| 60 |
+
scaler.fit(X)
|
| 61 |
+
|
| 62 |
+
os.makedirs(
|
| 63 |
+
os.path.dirname(scaler_path),
|
| 64 |
+
exist_ok=True
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
with open(scaler_path, 'wb') as f:
|
| 68 |
+
pickle.dump(scaler, f)
|
| 69 |
+
|
| 70 |
+
print(f"Scaler saved to: {scaler_path}")
|
| 71 |
+
|
| 72 |
+
return scaler
|
| 73 |
+
|
| 74 |
+
def load_scaler(scaler_path=SCALER_PATH):
|
| 75 |
+
|
| 76 |
+
if not os.path.exists(scaler_path):
|
| 77 |
+
raise FileNotFoundError(
|
| 78 |
+
f"Scaler not found at {scaler_path}. "
|
| 79 |
+
f"Run fit_and_save_scaler() first during training."
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
with open(scaler_path, 'rb') as f:
|
| 83 |
+
scaler = pickle.load(f)
|
| 84 |
+
|
| 85 |
+
return scaler
|
| 86 |
+
|
| 87 |
+
def scale_features(X, scaler):
|
| 88 |
+
|
| 89 |
+
X = np.array(X)
|
| 90 |
+
|
| 91 |
+
if X.ndim == 1:
|
| 92 |
+
X = X.reshape(1, -1)
|
| 93 |
+
|
| 94 |
+
X_scaled = scaler.transform(X)
|
| 95 |
+
|
| 96 |
+
return X_scaled
|
| 97 |
+
|
| 98 |
+
def prepare_training_data(csv_path, scaler_path=SCALER_PATH):
|
| 99 |
+
|
| 100 |
+
print("Loading dataset...")
|
| 101 |
+
X, y = load_dataset(csv_path)
|
| 102 |
+
print(f" Loaded {X.shape[0]} samples, {X.shape[1]} features each")
|
| 103 |
+
|
| 104 |
+
print("Fitting and saving scaler...")
|
| 105 |
+
scaler = fit_and_save_scaler(X, scaler_path)
|
| 106 |
+
|
| 107 |
+
print("Scaling features...")
|
| 108 |
+
X_scaled = scale_features(X, scaler)
|
| 109 |
+
|
| 110 |
+
print("Training data ready!")
|
| 111 |
+
return X_scaled, y
|
| 112 |
+
|
| 113 |
+
def prepare_inference_vector(feature_dict, scaler_path=SCALER_PATH):
|
| 114 |
+
|
| 115 |
+
vector = build_feature_vector(feature_dict)
|
| 116 |
+
scaler = load_scaler(scaler_path)
|
| 117 |
+
X_scaled = scale_features(vector, scaler)
|
| 118 |
+
|
| 119 |
+
return X_scaled
|
| 120 |
+
|
| 121 |
+
if __name__ == "__main__":
|
| 122 |
+
|
| 123 |
+
import os
|
| 124 |
+
|
| 125 |
+
CSV_PATH = os.path.join(os.path.dirname(__file__), "data", "dataset.csv")
|
| 126 |
+
|
| 127 |
+
X_scaled, y = prepare_training_data(CSV_PATH)
|
| 128 |
+
|
| 129 |
+
print("\n" + "=" * 50)
|
| 130 |
+
print(" FEATURE PIPELINE — TRAINING RESULTS")
|
| 131 |
+
print("=" * 50)
|
| 132 |
+
print(f" X_scaled shape : {X_scaled.shape}")
|
| 133 |
+
print(f" y shape : {y.shape}")
|
| 134 |
+
print(f" Label counts : Clean={sum(y==0)}, Risky={sum(y==1)}")
|
| 135 |
+
print(f" Schema version : {SCHEMA_VERSION}")
|
| 136 |
+
print(f" Feature order : {FEATURE_SCHEMA}")
|
| 137 |
+
print("=" * 50)
|
| 138 |
+
|
| 139 |
+
print("\n Scaled Feature Means (should be ~0.0 after StandardScaling):")
|
| 140 |
+
for name, mean_val in zip(FEATURE_SCHEMA, X_scaled.mean(axis=0)):
|
| 141 |
+
print(f" {name:<28} : {mean_val:+.4f}")
|
| 142 |
+
|
| 143 |
+
print("\n Scaled Feature Std Devs (should be ~1.0 after StandardScaling):")
|
| 144 |
+
for name, std_val in zip(FEATURE_SCHEMA, X_scaled.std(axis=0)):
|
| 145 |
+
print(f" {name:<28} : {std_val:.4f}")
|
| 146 |
+
|
| 147 |
+
print("=" * 50)
|
| 148 |
+
|
| 149 |
+
print("\n Simulating inference on a sample feature dict...")
|
| 150 |
+
|
| 151 |
+
sample_features = {
|
| 152 |
+
"num_functions": 2,
|
| 153 |
+
"num_loops": 5,
|
| 154 |
+
"num_if": 8,
|
| 155 |
+
"num_try_except": 2,
|
| 156 |
+
"num_return": 3,
|
| 157 |
+
"line_count": 80,
|
| 158 |
+
"max_nesting_depth": 6,
|
| 159 |
+
"cyclomatic_complexity": 9,
|
| 160 |
+
"avg_function_length": 35.0,
|
| 161 |
+
"recursion_flag": 1,
|
| 162 |
+
"global_variable_count": 2,
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
inference_vector = prepare_inference_vector(sample_features)
|
| 166 |
+
print(f" Inference vector shape : {inference_vector.shape}")
|
| 167 |
+
print(f" Scaled values : {inference_vector.round(3)}")
|
| 168 |
+
print("=" * 50)
|
frontend/dist/assets/index-CnXjjhnJ.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
frontend/dist/assets/index-Dn2xEIXT.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
:root{--bg-void: #0b0806;--bg-panel: rgba(24, 16, 10, .62);--bg-panel-solid: #140e09;--copper: #e8823f;--copper-bright: #ffa45c;--copper-dim: #8a4a22;--copper-glow: rgba(232, 130, 63, .55);--copper-faint: rgba(232, 130, 63, .12);--amber: #ffc35e;--amber-bright: #ffd98a;--amber-glow: rgba(255, 195, 94, .5);--amber-faint: rgba(255, 195, 94, .1);--ember: #ff6a2b;--ember-glow: rgba(255, 106, 43, .55);--ember-faint: rgba(255, 106, 43, .12);--titanium: #c9cdd4;--titanium-dim: #8b9099;--titanium-glow: rgba(201, 205, 212, .35);--titanium-faint: rgba(201, 205, 212, .08);--text-primary: #f4ead9;--text-dim: #a08e77;--border-glass: rgba(232, 130, 63, .26);--font-mono: "JetBrains Mono", "Fira Code", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;--font-display: "Orbitron", "Rajdhani", ui-sans-serif, system-ui, sans-serif}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:#6e6355;font-style:italic}.token.keyword,.token.selector,.token.important{color:var(--copper);text-shadow:0 0 8px var(--copper-faint)}.token.string,.token.char,.token.attr-value{color:var(--amber)}.token.number,.token.boolean,.token.constant{color:var(--ember)}.token.function,.token.class-name{color:var(--titanium);text-shadow:0 0 8px var(--titanium-faint)}.token.operator,.token.punctuation{color:var(--titanium-dim)}.token.builtin,.token.decorator{color:var(--amber-bright)}*{box-sizing:border-box}html,body,#root{margin:0;padding:0;width:100%;height:100%;background:var(--bg-void);color:var(--text-primary);font-family:var(--font-mono);overflow:hidden}::selection{background:var(--copper-glow);color:#0b0806}.glass-panel{position:relative;background:linear-gradient(165deg,#2e1e12e0,#120c08d1 45%,#1e140de6);backdrop-filter:blur(18px) saturate(130%);-webkit-backdrop-filter:blur(18px) saturate(130%);border-radius:18px;box-shadow:0 0 55px #e8823f14,0 40px 80px #000000a6,inset 0 1px #ffc35e29,inset 0 -18px 50px #ff6a2b0d}.glass-panel:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:inherit;padding:1px;background:linear-gradient(140deg,#ffa45ca6,#e8823f1a 28%,#8b90994d 55%,#ff6a2b66);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:exclude;pointer-events:none}.glow-text-copper{color:var(--copper);text-shadow:0 0 14px var(--copper-glow)}.glow-text-amber{color:var(--amber);text-shadow:0 0 14px var(--amber-glow)}.glow-text-titanium{color:var(--titanium);text-shadow:0 0 10px var(--titanium-glow)}.aberrate{text-shadow:-1.6px 0 rgba(255,106,43,.4),1.6px 0 rgba(201,205,212,.3),0 0 16px var(--copper-glow)}.app-shell{position:relative;height:100vh;width:100%;overflow:hidden}.canvas-layer{position:absolute;top:0;right:0;bottom:0;left:0;z-index:0}.crt-layer{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;pointer-events:none;background:repeating-linear-gradient(to bottom,#0000,#0000 2px,#00000021,#0000 4px);mix-blend-mode:multiply}.crt-layer:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:radial-gradient(ellipse at center,#0000 52%,#0000007a)}.crt-layer:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(to bottom,#ffc35e06,#ff6a2b05);animation:crt-flicker 6s infinite steps(60)}@keyframes crt-flicker{0%,to{opacity:.9}7%{opacity:.75}9%{opacity:1}36%{opacity:.85}38%{opacity:1}71%{opacity:.8}73%{opacity:1}}::-webkit-scrollbar{width:10px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--copper-dim);border-radius:8px}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}.app-header-overlay{position:fixed;top:26px;left:0;right:0;text-align:center;z-index:90;pointer-events:none}.glitch-title{position:relative;font-family:var(--font-display);font-size:2.86rem;font-weight:800;letter-spacing:.12em;color:var(--amber);text-shadow:0 0 20px var(--copper-glow);margin:0;animation:title-breathe 4s ease-in-out infinite}.glitch-title:before,.glitch-title:after{content:attr(data-text);position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden;pointer-events:none}.glitch-title:before{color:var(--ember);text-shadow:0 0 10px var(--ember-glow);animation:glitch-slice-a 3.1s infinite steps(1)}.glitch-title:after{color:var(--titanium);text-shadow:0 0 10px var(--titanium-glow);animation:glitch-slice-b 2.7s infinite steps(1)}@keyframes title-breathe{0%,to{text-shadow:0 0 20px var(--copper-glow)}50%{text-shadow:0 0 38px var(--copper-glow),0 0 70px var(--ember-faint)}}@keyframes glitch-slice-a{0%,87%,to{clip-path:inset(100% 0 0 0);transform:none;opacity:0}88%{clip-path:inset(12% 0 74% 0);transform:translate(-5px,2px);opacity:.9}90%{clip-path:inset(58% 0 22% 0);transform:translate(4px,-2px);opacity:.9}92%{clip-path:inset(30% 0 52% 0);transform:translate(-3px,1px);opacity:.85}94%{clip-path:inset(100% 0 0 0);opacity:0}}@keyframes glitch-slice-b{0%,44%,56%,to{clip-path:inset(100% 0 0 0);transform:none;opacity:0}46%{clip-path:inset(70% 0 8% 0);transform:translate(5px,-1px);opacity:.85}49%{clip-path:inset(8% 0 78% 0);transform:translate(-4px,2px);opacity:.9}52%{clip-path:inset(42% 0 40% 0);transform:translate(3px);opacity:.85}54%{clip-path:inset(100% 0 0 0);opacity:0}}.app-subtitle{color:var(--text-dim);font-size:1.04rem;margin-top:8px;letter-spacing:.08em}.panel3d{pointer-events:auto}.stage-3d{width:720px;display:flex;flex-direction:column;gap:22px;align-items:center}.hud-panel3d .hud-panel{width:660px}.tilt-panel{position:relative;will-change:transform;width:100%}.tilt-sheen{position:absolute;width:60%;height:60%;border-radius:50%;background:radial-gradient(circle,rgba(255,195,94,.1) 0%,rgba(255,106,43,.05) 45%,transparent 70%);transform:translate(-50%,-50%) translateZ(40px);pointer-events:none;z-index:2;filter:blur(6px)}.flip-stage{position:relative;width:100%;perspective:1600px}.flip-tilt{transform-style:preserve-3d}.flip-object{position:relative;display:grid;transform-style:preserve-3d;width:100%}.flip-face{grid-area:1 / 1;backface-visibility:hidden;-webkit-backface-visibility:hidden;padding:20px 24px 24px;display:flex;flex-direction:column;min-height:380px}.flip-front{transform:rotateY(0) translateZ(1px)}.flip-back{transform:rotateY(180deg) translateZ(1px)}.flip-back.glass-panel:before{background:linear-gradient(140deg,#ff6a2b8c,#ff6a2b14 30%,#8b909947 60%,#ffc35e66)}.editor-inner{display:flex;flex-direction:column;flex:1}.editor-header{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:12px}.editor-title{font-size:1.19rem;letter-spacing:.06em}.editor-subtitle{color:var(--text-dim);font-size:.93rem;letter-spacing:.08em}.editor-body{position:relative;background:#0805038c;border-radius:12px;border:1px solid var(--copper-faint);overflow:auto;max-height:400px;flex:1}.back-body{border-color:var(--ember-faint)}.code-block{margin:0;padding:16px;font-size:1.04rem;line-height:1.55;white-space:pre-wrap;word-break:break-word}.scan-laser{position:absolute;left:0;right:0;height:3px;background:linear-gradient(90deg,transparent,var(--copper) 20%,var(--amber-bright) 50%,var(--copper) 80%,transparent);box-shadow:0 0 18px var(--amber-glow),0 0 42px var(--ember-glow);z-index:3;pointer-events:none}.scan-tint{position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(to bottom,var(--amber-faint),transparent 40%);animation:scan-tint-pulse 1.1s ease-in-out infinite;z-index:2;pointer-events:none}@keyframes scan-tint-pulse{0%,to{opacity:.25}50%{opacity:.6}}.back-footer{display:flex;align-items:center;justify-content:space-between;margin-top:14px;gap:16px}.points-saved{display:flex;align-items:baseline;gap:10px}.points-saved-number{font-family:var(--font-display);font-size:2.4rem}.points-saved-label{color:var(--text-dim);font-size:.93rem;letter-spacing:.08em}.back-note{color:var(--text-dim);font-size:.93rem;max-width:340px;text-align:right}.burst-field{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:5}.burst-particle{position:absolute;border-radius:50%}.editor-actions{display:flex;gap:20px;flex-wrap:wrap;justify-content:center}.magnetic-button{position:relative;background:linear-gradient(170deg,#1e140ce6,#0a0705d9);border:none;border-radius:999px;padding:14px 30px;font-family:var(--font-mono);font-weight:600;font-size:1.02rem;letter-spacing:.1em;cursor:pointer;overflow:hidden;outline:1px solid currentColor;outline-offset:-1px}.magnetic-button:disabled{opacity:.3;cursor:not-allowed}.button-trace{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;pointer-events:none;overflow:visible}.trace-rect{width:calc(100% - 3px);height:calc(100% - 3px)}.button-ripple{position:absolute;width:40px;height:40px;margin:-20px 0 0 -20px;border-radius:50%;pointer-events:none}.button-label{position:relative;z-index:1}.hud-panel{padding:24px 28px}.hud-header{display:flex;align-items:center;gap:16px;margin-bottom:18px}.hud-label{font-size:1.08rem;letter-spacing:.1em;white-space:nowrap}.hud-header-line{flex:1;height:1px;background:linear-gradient(90deg,var(--copper-glow),transparent)}.hud-grid{display:grid;grid-template-columns:210px 1fr;gap:28px;align-items:center}.hud-right{display:flex;flex-direction:column;gap:12px}.hud-badge{border:1px solid;border-radius:999px;padding:5px 16px;font-size:.96rem;letter-spacing:.14em;width:fit-content}.hud-confidence{color:var(--text-dim);font-size:1.02rem;letter-spacing:.05em}.hud-factors{list-style:none;margin:6px 0 0;padding:0;display:flex;flex-direction:column;gap:9px}.hud-factors li{font-size:1.08rem;color:var(--text-primary)}.hud-factor-marker{color:var(--ember);margin-right:7px}@keyframes hud-pulse-glow{0%,to{box-shadow:0 0 36px var(--ember-glow),inset 0 0 55px var(--ember-faint)}50%{box-shadow:0 0 75px var(--ember-glow),inset 0 0 90px var(--copper-faint)}}.hud-pulse{animation:hud-pulse-glow 1.4s ease-in-out infinite}.risk-gauge{position:relative;width:210px;height:210px}.gauge-svg{width:100%;height:100%}.gauge-readout{position:absolute;top:50%;left:50%;transform:translate(-50%,-58%);font-family:var(--font-display);font-size:3.55rem;font-weight:800;letter-spacing:.02em}.gauge-ticking{animation:readout-jitter .09s infinite}.gauge-locked{animation:readout-lock .35s ease-out}@keyframes readout-jitter{0%{transform:translate(-50%,-58%) skew(0)}25%{transform:translate(calc(-50% + 1px),-58%) skew(3deg)}50%{transform:translate(calc(-50% - 1px),calc(-58% + 1px)) skew(-2deg)}75%{transform:translate(-50%,calc(-58% - 1px)) skew(1deg)}to{transform:translate(-50%,-58%) skew(0)}}@keyframes readout-lock{0%{transform:translate(-50%,-58%) scale(1.25);filter:blur(2px)}to{transform:translate(-50%,-58%) scale(1);filter:blur(0)}}.gauge-caption{position:absolute;bottom:18px;width:100%;text-align:center;color:var(--text-dim);font-size:.88rem;letter-spacing:.28em}.status-line{color:var(--text-dim);font-size:1.02rem;min-height:1em;text-align:center}.status-line.error{color:var(--ember);text-shadow:0 0 10px var(--ember-glow)}
|
frontend/dist/index.html
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>Maintainability Engine</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 8 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
| 9 |
+
<link
|
| 10 |
+
href="https://fonts.googleapis.com/css2?family=Orbitron:wght@600;800&family=JetBrains+Mono:wght@400;600&display=swap"
|
| 11 |
+
rel="stylesheet"
|
| 12 |
+
/>
|
| 13 |
+
<script type="module" crossorigin src="/assets/index-CnXjjhnJ.js"></script>
|
| 14 |
+
<link rel="stylesheet" crossorigin href="/assets/index-Dn2xEIXT.css">
|
| 15 |
+
</head>
|
| 16 |
+
<body>
|
| 17 |
+
<div id="root"></div>
|
| 18 |
+
</body>
|
| 19 |
+
</html>
|
frontend/index.html
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>Maintainability Engine</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
| 8 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
| 9 |
+
<link
|
| 10 |
+
href="https://fonts.googleapis.com/css2?family=Orbitron:wght@600;800&family=JetBrains+Mono:wght@400;600&display=swap"
|
| 11 |
+
rel="stylesheet"
|
| 12 |
+
/>
|
| 13 |
+
</head>
|
| 14 |
+
<body>
|
| 15 |
+
<div id="root"></div>
|
| 16 |
+
<script type="module" src="/src/main.tsx"></script>
|
| 17 |
+
</body>
|
| 18 |
+
</html>
|
frontend/package-lock.json
ADDED
|
@@ -0,0 +1,2749 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "maintainability-engine-frontend",
|
| 3 |
+
"version": "0.0.1",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "maintainability-engine-frontend",
|
| 9 |
+
"version": "0.0.1",
|
| 10 |
+
"dependencies": {
|
| 11 |
+
"@react-three/drei": "^9.114.0",
|
| 12 |
+
"@react-three/fiber": "^8.17.10",
|
| 13 |
+
"@react-three/postprocessing": "^2.19.1",
|
| 14 |
+
"framer-motion": "^11.11.17",
|
| 15 |
+
"prismjs": "^1.29.0",
|
| 16 |
+
"react": "^18.3.1",
|
| 17 |
+
"react-dom": "^18.3.1",
|
| 18 |
+
"react-simple-code-editor": "^0.14.1",
|
| 19 |
+
"three": "^0.169.0"
|
| 20 |
+
},
|
| 21 |
+
"devDependencies": {
|
| 22 |
+
"@types/prismjs": "^1.26.5",
|
| 23 |
+
"@types/react": "^18.3.12",
|
| 24 |
+
"@types/react-dom": "^18.3.1",
|
| 25 |
+
"@types/three": "^0.169.0",
|
| 26 |
+
"@vitejs/plugin-react": "^4.3.3",
|
| 27 |
+
"typescript": "^5.6.3",
|
| 28 |
+
"vite": "^5.4.11"
|
| 29 |
+
}
|
| 30 |
+
},
|
| 31 |
+
"node_modules/@babel/code-frame": {
|
| 32 |
+
"version": "7.29.7",
|
| 33 |
+
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
|
| 34 |
+
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
|
| 35 |
+
"dev": true,
|
| 36 |
+
"license": "MIT",
|
| 37 |
+
"dependencies": {
|
| 38 |
+
"@babel/helper-validator-identifier": "^7.29.7",
|
| 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.7",
|
| 48 |
+
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
|
| 49 |
+
"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
|
| 50 |
+
"dev": true,
|
| 51 |
+
"license": "MIT",
|
| 52 |
+
"engines": {
|
| 53 |
+
"node": ">=6.9.0"
|
| 54 |
+
}
|
| 55 |
+
},
|
| 56 |
+
"node_modules/@babel/core": {
|
| 57 |
+
"version": "7.29.7",
|
| 58 |
+
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
|
| 59 |
+
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
|
| 60 |
+
"dev": true,
|
| 61 |
+
"license": "MIT",
|
| 62 |
+
"dependencies": {
|
| 63 |
+
"@babel/code-frame": "^7.29.7",
|
| 64 |
+
"@babel/generator": "^7.29.7",
|
| 65 |
+
"@babel/helper-compilation-targets": "^7.29.7",
|
| 66 |
+
"@babel/helper-module-transforms": "^7.29.7",
|
| 67 |
+
"@babel/helpers": "^7.29.7",
|
| 68 |
+
"@babel/parser": "^7.29.7",
|
| 69 |
+
"@babel/template": "^7.29.7",
|
| 70 |
+
"@babel/traverse": "^7.29.7",
|
| 71 |
+
"@babel/types": "^7.29.7",
|
| 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.7",
|
| 89 |
+
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
|
| 90 |
+
"integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
|
| 91 |
+
"dev": true,
|
| 92 |
+
"license": "MIT",
|
| 93 |
+
"dependencies": {
|
| 94 |
+
"@babel/parser": "^7.29.7",
|
| 95 |
+
"@babel/types": "^7.29.7",
|
| 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.29.7",
|
| 106 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
|
| 107 |
+
"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
|
| 108 |
+
"dev": true,
|
| 109 |
+
"license": "MIT",
|
| 110 |
+
"dependencies": {
|
| 111 |
+
"@babel/compat-data": "^7.29.7",
|
| 112 |
+
"@babel/helper-validator-option": "^7.29.7",
|
| 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.29.7",
|
| 123 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
|
| 124 |
+
"integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
|
| 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.29.7",
|
| 133 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
|
| 134 |
+
"integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
|
| 135 |
+
"dev": true,
|
| 136 |
+
"license": "MIT",
|
| 137 |
+
"dependencies": {
|
| 138 |
+
"@babel/traverse": "^7.29.7",
|
| 139 |
+
"@babel/types": "^7.29.7"
|
| 140 |
+
},
|
| 141 |
+
"engines": {
|
| 142 |
+
"node": ">=6.9.0"
|
| 143 |
+
}
|
| 144 |
+
},
|
| 145 |
+
"node_modules/@babel/helper-module-transforms": {
|
| 146 |
+
"version": "7.29.7",
|
| 147 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
|
| 148 |
+
"integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
|
| 149 |
+
"dev": true,
|
| 150 |
+
"license": "MIT",
|
| 151 |
+
"dependencies": {
|
| 152 |
+
"@babel/helper-module-imports": "^7.29.7",
|
| 153 |
+
"@babel/helper-validator-identifier": "^7.29.7",
|
| 154 |
+
"@babel/traverse": "^7.29.7"
|
| 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.29.7",
|
| 165 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
|
| 166 |
+
"integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
|
| 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.29.7",
|
| 175 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
|
| 176 |
+
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
|
| 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.29.7",
|
| 185 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
|
| 186 |
+
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
|
| 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.29.7",
|
| 195 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
|
| 196 |
+
"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
|
| 197 |
+
"dev": true,
|
| 198 |
+
"license": "MIT",
|
| 199 |
+
"engines": {
|
| 200 |
+
"node": ">=6.9.0"
|
| 201 |
+
}
|
| 202 |
+
},
|
| 203 |
+
"node_modules/@babel/helpers": {
|
| 204 |
+
"version": "7.29.7",
|
| 205 |
+
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
|
| 206 |
+
"integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
|
| 207 |
+
"dev": true,
|
| 208 |
+
"license": "MIT",
|
| 209 |
+
"dependencies": {
|
| 210 |
+
"@babel/template": "^7.29.7",
|
| 211 |
+
"@babel/types": "^7.29.7"
|
| 212 |
+
},
|
| 213 |
+
"engines": {
|
| 214 |
+
"node": ">=6.9.0"
|
| 215 |
+
}
|
| 216 |
+
},
|
| 217 |
+
"node_modules/@babel/parser": {
|
| 218 |
+
"version": "7.29.7",
|
| 219 |
+
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
|
| 220 |
+
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
|
| 221 |
+
"dev": true,
|
| 222 |
+
"license": "MIT",
|
| 223 |
+
"dependencies": {
|
| 224 |
+
"@babel/types": "^7.29.7"
|
| 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.29.7",
|
| 235 |
+
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
|
| 236 |
+
"integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
|
| 237 |
+
"dev": true,
|
| 238 |
+
"license": "MIT",
|
| 239 |
+
"dependencies": {
|
| 240 |
+
"@babel/helper-plugin-utils": "^7.29.7"
|
| 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.29.7",
|
| 251 |
+
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
|
| 252 |
+
"integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
|
| 253 |
+
"dev": true,
|
| 254 |
+
"license": "MIT",
|
| 255 |
+
"dependencies": {
|
| 256 |
+
"@babel/helper-plugin-utils": "^7.29.7"
|
| 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.7",
|
| 267 |
+
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
|
| 268 |
+
"integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
|
| 269 |
+
"license": "MIT",
|
| 270 |
+
"engines": {
|
| 271 |
+
"node": ">=6.9.0"
|
| 272 |
+
}
|
| 273 |
+
},
|
| 274 |
+
"node_modules/@babel/template": {
|
| 275 |
+
"version": "7.29.7",
|
| 276 |
+
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
|
| 277 |
+
"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
|
| 278 |
+
"dev": true,
|
| 279 |
+
"license": "MIT",
|
| 280 |
+
"dependencies": {
|
| 281 |
+
"@babel/code-frame": "^7.29.7",
|
| 282 |
+
"@babel/parser": "^7.29.7",
|
| 283 |
+
"@babel/types": "^7.29.7"
|
| 284 |
+
},
|
| 285 |
+
"engines": {
|
| 286 |
+
"node": ">=6.9.0"
|
| 287 |
+
}
|
| 288 |
+
},
|
| 289 |
+
"node_modules/@babel/traverse": {
|
| 290 |
+
"version": "7.29.7",
|
| 291 |
+
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
|
| 292 |
+
"integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
|
| 293 |
+
"dev": true,
|
| 294 |
+
"license": "MIT",
|
| 295 |
+
"dependencies": {
|
| 296 |
+
"@babel/code-frame": "^7.29.7",
|
| 297 |
+
"@babel/generator": "^7.29.7",
|
| 298 |
+
"@babel/helper-globals": "^7.29.7",
|
| 299 |
+
"@babel/parser": "^7.29.7",
|
| 300 |
+
"@babel/template": "^7.29.7",
|
| 301 |
+
"@babel/types": "^7.29.7",
|
| 302 |
+
"debug": "^4.3.1"
|
| 303 |
+
},
|
| 304 |
+
"engines": {
|
| 305 |
+
"node": ">=6.9.0"
|
| 306 |
+
}
|
| 307 |
+
},
|
| 308 |
+
"node_modules/@babel/types": {
|
| 309 |
+
"version": "7.29.7",
|
| 310 |
+
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
|
| 311 |
+
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
|
| 312 |
+
"dev": true,
|
| 313 |
+
"license": "MIT",
|
| 314 |
+
"dependencies": {
|
| 315 |
+
"@babel/helper-string-parser": "^7.29.7",
|
| 316 |
+
"@babel/helper-validator-identifier": "^7.29.7"
|
| 317 |
+
},
|
| 318 |
+
"engines": {
|
| 319 |
+
"node": ">=6.9.0"
|
| 320 |
+
}
|
| 321 |
+
},
|
| 322 |
+
"node_modules/@esbuild/aix-ppc64": {
|
| 323 |
+
"version": "0.21.5",
|
| 324 |
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
| 325 |
+
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
|
| 326 |
+
"cpu": [
|
| 327 |
+
"ppc64"
|
| 328 |
+
],
|
| 329 |
+
"dev": true,
|
| 330 |
+
"license": "MIT",
|
| 331 |
+
"optional": true,
|
| 332 |
+
"os": [
|
| 333 |
+
"aix"
|
| 334 |
+
],
|
| 335 |
+
"engines": {
|
| 336 |
+
"node": ">=12"
|
| 337 |
+
}
|
| 338 |
+
},
|
| 339 |
+
"node_modules/@esbuild/android-arm": {
|
| 340 |
+
"version": "0.21.5",
|
| 341 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
|
| 342 |
+
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
|
| 343 |
+
"cpu": [
|
| 344 |
+
"arm"
|
| 345 |
+
],
|
| 346 |
+
"dev": true,
|
| 347 |
+
"license": "MIT",
|
| 348 |
+
"optional": true,
|
| 349 |
+
"os": [
|
| 350 |
+
"android"
|
| 351 |
+
],
|
| 352 |
+
"engines": {
|
| 353 |
+
"node": ">=12"
|
| 354 |
+
}
|
| 355 |
+
},
|
| 356 |
+
"node_modules/@esbuild/android-arm64": {
|
| 357 |
+
"version": "0.21.5",
|
| 358 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
|
| 359 |
+
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
|
| 360 |
+
"cpu": [
|
| 361 |
+
"arm64"
|
| 362 |
+
],
|
| 363 |
+
"dev": true,
|
| 364 |
+
"license": "MIT",
|
| 365 |
+
"optional": true,
|
| 366 |
+
"os": [
|
| 367 |
+
"android"
|
| 368 |
+
],
|
| 369 |
+
"engines": {
|
| 370 |
+
"node": ">=12"
|
| 371 |
+
}
|
| 372 |
+
},
|
| 373 |
+
"node_modules/@esbuild/android-x64": {
|
| 374 |
+
"version": "0.21.5",
|
| 375 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
|
| 376 |
+
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
|
| 377 |
+
"cpu": [
|
| 378 |
+
"x64"
|
| 379 |
+
],
|
| 380 |
+
"dev": true,
|
| 381 |
+
"license": "MIT",
|
| 382 |
+
"optional": true,
|
| 383 |
+
"os": [
|
| 384 |
+
"android"
|
| 385 |
+
],
|
| 386 |
+
"engines": {
|
| 387 |
+
"node": ">=12"
|
| 388 |
+
}
|
| 389 |
+
},
|
| 390 |
+
"node_modules/@esbuild/darwin-arm64": {
|
| 391 |
+
"version": "0.21.5",
|
| 392 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
|
| 393 |
+
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
|
| 394 |
+
"cpu": [
|
| 395 |
+
"arm64"
|
| 396 |
+
],
|
| 397 |
+
"dev": true,
|
| 398 |
+
"license": "MIT",
|
| 399 |
+
"optional": true,
|
| 400 |
+
"os": [
|
| 401 |
+
"darwin"
|
| 402 |
+
],
|
| 403 |
+
"engines": {
|
| 404 |
+
"node": ">=12"
|
| 405 |
+
}
|
| 406 |
+
},
|
| 407 |
+
"node_modules/@esbuild/darwin-x64": {
|
| 408 |
+
"version": "0.21.5",
|
| 409 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
|
| 410 |
+
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
|
| 411 |
+
"cpu": [
|
| 412 |
+
"x64"
|
| 413 |
+
],
|
| 414 |
+
"dev": true,
|
| 415 |
+
"license": "MIT",
|
| 416 |
+
"optional": true,
|
| 417 |
+
"os": [
|
| 418 |
+
"darwin"
|
| 419 |
+
],
|
| 420 |
+
"engines": {
|
| 421 |
+
"node": ">=12"
|
| 422 |
+
}
|
| 423 |
+
},
|
| 424 |
+
"node_modules/@esbuild/freebsd-arm64": {
|
| 425 |
+
"version": "0.21.5",
|
| 426 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
|
| 427 |
+
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
|
| 428 |
+
"cpu": [
|
| 429 |
+
"arm64"
|
| 430 |
+
],
|
| 431 |
+
"dev": true,
|
| 432 |
+
"license": "MIT",
|
| 433 |
+
"optional": true,
|
| 434 |
+
"os": [
|
| 435 |
+
"freebsd"
|
| 436 |
+
],
|
| 437 |
+
"engines": {
|
| 438 |
+
"node": ">=12"
|
| 439 |
+
}
|
| 440 |
+
},
|
| 441 |
+
"node_modules/@esbuild/freebsd-x64": {
|
| 442 |
+
"version": "0.21.5",
|
| 443 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
|
| 444 |
+
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
|
| 445 |
+
"cpu": [
|
| 446 |
+
"x64"
|
| 447 |
+
],
|
| 448 |
+
"dev": true,
|
| 449 |
+
"license": "MIT",
|
| 450 |
+
"optional": true,
|
| 451 |
+
"os": [
|
| 452 |
+
"freebsd"
|
| 453 |
+
],
|
| 454 |
+
"engines": {
|
| 455 |
+
"node": ">=12"
|
| 456 |
+
}
|
| 457 |
+
},
|
| 458 |
+
"node_modules/@esbuild/linux-arm": {
|
| 459 |
+
"version": "0.21.5",
|
| 460 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
|
| 461 |
+
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
|
| 462 |
+
"cpu": [
|
| 463 |
+
"arm"
|
| 464 |
+
],
|
| 465 |
+
"dev": true,
|
| 466 |
+
"license": "MIT",
|
| 467 |
+
"optional": true,
|
| 468 |
+
"os": [
|
| 469 |
+
"linux"
|
| 470 |
+
],
|
| 471 |
+
"engines": {
|
| 472 |
+
"node": ">=12"
|
| 473 |
+
}
|
| 474 |
+
},
|
| 475 |
+
"node_modules/@esbuild/linux-arm64": {
|
| 476 |
+
"version": "0.21.5",
|
| 477 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
|
| 478 |
+
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
|
| 479 |
+
"cpu": [
|
| 480 |
+
"arm64"
|
| 481 |
+
],
|
| 482 |
+
"dev": true,
|
| 483 |
+
"license": "MIT",
|
| 484 |
+
"optional": true,
|
| 485 |
+
"os": [
|
| 486 |
+
"linux"
|
| 487 |
+
],
|
| 488 |
+
"engines": {
|
| 489 |
+
"node": ">=12"
|
| 490 |
+
}
|
| 491 |
+
},
|
| 492 |
+
"node_modules/@esbuild/linux-ia32": {
|
| 493 |
+
"version": "0.21.5",
|
| 494 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
|
| 495 |
+
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
|
| 496 |
+
"cpu": [
|
| 497 |
+
"ia32"
|
| 498 |
+
],
|
| 499 |
+
"dev": true,
|
| 500 |
+
"license": "MIT",
|
| 501 |
+
"optional": true,
|
| 502 |
+
"os": [
|
| 503 |
+
"linux"
|
| 504 |
+
],
|
| 505 |
+
"engines": {
|
| 506 |
+
"node": ">=12"
|
| 507 |
+
}
|
| 508 |
+
},
|
| 509 |
+
"node_modules/@esbuild/linux-loong64": {
|
| 510 |
+
"version": "0.21.5",
|
| 511 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
|
| 512 |
+
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
|
| 513 |
+
"cpu": [
|
| 514 |
+
"loong64"
|
| 515 |
+
],
|
| 516 |
+
"dev": true,
|
| 517 |
+
"license": "MIT",
|
| 518 |
+
"optional": true,
|
| 519 |
+
"os": [
|
| 520 |
+
"linux"
|
| 521 |
+
],
|
| 522 |
+
"engines": {
|
| 523 |
+
"node": ">=12"
|
| 524 |
+
}
|
| 525 |
+
},
|
| 526 |
+
"node_modules/@esbuild/linux-mips64el": {
|
| 527 |
+
"version": "0.21.5",
|
| 528 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
|
| 529 |
+
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
|
| 530 |
+
"cpu": [
|
| 531 |
+
"mips64el"
|
| 532 |
+
],
|
| 533 |
+
"dev": true,
|
| 534 |
+
"license": "MIT",
|
| 535 |
+
"optional": true,
|
| 536 |
+
"os": [
|
| 537 |
+
"linux"
|
| 538 |
+
],
|
| 539 |
+
"engines": {
|
| 540 |
+
"node": ">=12"
|
| 541 |
+
}
|
| 542 |
+
},
|
| 543 |
+
"node_modules/@esbuild/linux-ppc64": {
|
| 544 |
+
"version": "0.21.5",
|
| 545 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
|
| 546 |
+
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
|
| 547 |
+
"cpu": [
|
| 548 |
+
"ppc64"
|
| 549 |
+
],
|
| 550 |
+
"dev": true,
|
| 551 |
+
"license": "MIT",
|
| 552 |
+
"optional": true,
|
| 553 |
+
"os": [
|
| 554 |
+
"linux"
|
| 555 |
+
],
|
| 556 |
+
"engines": {
|
| 557 |
+
"node": ">=12"
|
| 558 |
+
}
|
| 559 |
+
},
|
| 560 |
+
"node_modules/@esbuild/linux-riscv64": {
|
| 561 |
+
"version": "0.21.5",
|
| 562 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
|
| 563 |
+
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
|
| 564 |
+
"cpu": [
|
| 565 |
+
"riscv64"
|
| 566 |
+
],
|
| 567 |
+
"dev": true,
|
| 568 |
+
"license": "MIT",
|
| 569 |
+
"optional": true,
|
| 570 |
+
"os": [
|
| 571 |
+
"linux"
|
| 572 |
+
],
|
| 573 |
+
"engines": {
|
| 574 |
+
"node": ">=12"
|
| 575 |
+
}
|
| 576 |
+
},
|
| 577 |
+
"node_modules/@esbuild/linux-s390x": {
|
| 578 |
+
"version": "0.21.5",
|
| 579 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
|
| 580 |
+
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
|
| 581 |
+
"cpu": [
|
| 582 |
+
"s390x"
|
| 583 |
+
],
|
| 584 |
+
"dev": true,
|
| 585 |
+
"license": "MIT",
|
| 586 |
+
"optional": true,
|
| 587 |
+
"os": [
|
| 588 |
+
"linux"
|
| 589 |
+
],
|
| 590 |
+
"engines": {
|
| 591 |
+
"node": ">=12"
|
| 592 |
+
}
|
| 593 |
+
},
|
| 594 |
+
"node_modules/@esbuild/linux-x64": {
|
| 595 |
+
"version": "0.21.5",
|
| 596 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
|
| 597 |
+
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
|
| 598 |
+
"cpu": [
|
| 599 |
+
"x64"
|
| 600 |
+
],
|
| 601 |
+
"dev": true,
|
| 602 |
+
"license": "MIT",
|
| 603 |
+
"optional": true,
|
| 604 |
+
"os": [
|
| 605 |
+
"linux"
|
| 606 |
+
],
|
| 607 |
+
"engines": {
|
| 608 |
+
"node": ">=12"
|
| 609 |
+
}
|
| 610 |
+
},
|
| 611 |
+
"node_modules/@esbuild/netbsd-x64": {
|
| 612 |
+
"version": "0.21.5",
|
| 613 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
| 614 |
+
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
|
| 615 |
+
"cpu": [
|
| 616 |
+
"x64"
|
| 617 |
+
],
|
| 618 |
+
"dev": true,
|
| 619 |
+
"license": "MIT",
|
| 620 |
+
"optional": true,
|
| 621 |
+
"os": [
|
| 622 |
+
"netbsd"
|
| 623 |
+
],
|
| 624 |
+
"engines": {
|
| 625 |
+
"node": ">=12"
|
| 626 |
+
}
|
| 627 |
+
},
|
| 628 |
+
"node_modules/@esbuild/openbsd-x64": {
|
| 629 |
+
"version": "0.21.5",
|
| 630 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
|
| 631 |
+
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
|
| 632 |
+
"cpu": [
|
| 633 |
+
"x64"
|
| 634 |
+
],
|
| 635 |
+
"dev": true,
|
| 636 |
+
"license": "MIT",
|
| 637 |
+
"optional": true,
|
| 638 |
+
"os": [
|
| 639 |
+
"openbsd"
|
| 640 |
+
],
|
| 641 |
+
"engines": {
|
| 642 |
+
"node": ">=12"
|
| 643 |
+
}
|
| 644 |
+
},
|
| 645 |
+
"node_modules/@esbuild/sunos-x64": {
|
| 646 |
+
"version": "0.21.5",
|
| 647 |
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
|
| 648 |
+
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
|
| 649 |
+
"cpu": [
|
| 650 |
+
"x64"
|
| 651 |
+
],
|
| 652 |
+
"dev": true,
|
| 653 |
+
"license": "MIT",
|
| 654 |
+
"optional": true,
|
| 655 |
+
"os": [
|
| 656 |
+
"sunos"
|
| 657 |
+
],
|
| 658 |
+
"engines": {
|
| 659 |
+
"node": ">=12"
|
| 660 |
+
}
|
| 661 |
+
},
|
| 662 |
+
"node_modules/@esbuild/win32-arm64": {
|
| 663 |
+
"version": "0.21.5",
|
| 664 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
|
| 665 |
+
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
|
| 666 |
+
"cpu": [
|
| 667 |
+
"arm64"
|
| 668 |
+
],
|
| 669 |
+
"dev": true,
|
| 670 |
+
"license": "MIT",
|
| 671 |
+
"optional": true,
|
| 672 |
+
"os": [
|
| 673 |
+
"win32"
|
| 674 |
+
],
|
| 675 |
+
"engines": {
|
| 676 |
+
"node": ">=12"
|
| 677 |
+
}
|
| 678 |
+
},
|
| 679 |
+
"node_modules/@esbuild/win32-ia32": {
|
| 680 |
+
"version": "0.21.5",
|
| 681 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
|
| 682 |
+
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
|
| 683 |
+
"cpu": [
|
| 684 |
+
"ia32"
|
| 685 |
+
],
|
| 686 |
+
"dev": true,
|
| 687 |
+
"license": "MIT",
|
| 688 |
+
"optional": true,
|
| 689 |
+
"os": [
|
| 690 |
+
"win32"
|
| 691 |
+
],
|
| 692 |
+
"engines": {
|
| 693 |
+
"node": ">=12"
|
| 694 |
+
}
|
| 695 |
+
},
|
| 696 |
+
"node_modules/@esbuild/win32-x64": {
|
| 697 |
+
"version": "0.21.5",
|
| 698 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
|
| 699 |
+
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
|
| 700 |
+
"cpu": [
|
| 701 |
+
"x64"
|
| 702 |
+
],
|
| 703 |
+
"dev": true,
|
| 704 |
+
"license": "MIT",
|
| 705 |
+
"optional": true,
|
| 706 |
+
"os": [
|
| 707 |
+
"win32"
|
| 708 |
+
],
|
| 709 |
+
"engines": {
|
| 710 |
+
"node": ">=12"
|
| 711 |
+
}
|
| 712 |
+
},
|
| 713 |
+
"node_modules/@jridgewell/gen-mapping": {
|
| 714 |
+
"version": "0.3.13",
|
| 715 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
| 716 |
+
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
| 717 |
+
"dev": true,
|
| 718 |
+
"license": "MIT",
|
| 719 |
+
"dependencies": {
|
| 720 |
+
"@jridgewell/sourcemap-codec": "^1.5.0",
|
| 721 |
+
"@jridgewell/trace-mapping": "^0.3.24"
|
| 722 |
+
}
|
| 723 |
+
},
|
| 724 |
+
"node_modules/@jridgewell/remapping": {
|
| 725 |
+
"version": "2.3.5",
|
| 726 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
| 727 |
+
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
| 728 |
+
"dev": true,
|
| 729 |
+
"license": "MIT",
|
| 730 |
+
"dependencies": {
|
| 731 |
+
"@jridgewell/gen-mapping": "^0.3.5",
|
| 732 |
+
"@jridgewell/trace-mapping": "^0.3.24"
|
| 733 |
+
}
|
| 734 |
+
},
|
| 735 |
+
"node_modules/@jridgewell/resolve-uri": {
|
| 736 |
+
"version": "3.1.2",
|
| 737 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
| 738 |
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
| 739 |
+
"dev": true,
|
| 740 |
+
"license": "MIT",
|
| 741 |
+
"engines": {
|
| 742 |
+
"node": ">=6.0.0"
|
| 743 |
+
}
|
| 744 |
+
},
|
| 745 |
+
"node_modules/@jridgewell/sourcemap-codec": {
|
| 746 |
+
"version": "1.5.5",
|
| 747 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
| 748 |
+
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
| 749 |
+
"dev": true,
|
| 750 |
+
"license": "MIT"
|
| 751 |
+
},
|
| 752 |
+
"node_modules/@jridgewell/trace-mapping": {
|
| 753 |
+
"version": "0.3.31",
|
| 754 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
| 755 |
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
| 756 |
+
"dev": true,
|
| 757 |
+
"license": "MIT",
|
| 758 |
+
"dependencies": {
|
| 759 |
+
"@jridgewell/resolve-uri": "^3.1.0",
|
| 760 |
+
"@jridgewell/sourcemap-codec": "^1.4.14"
|
| 761 |
+
}
|
| 762 |
+
},
|
| 763 |
+
"node_modules/@mediapipe/tasks-vision": {
|
| 764 |
+
"version": "0.10.17",
|
| 765 |
+
"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz",
|
| 766 |
+
"integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==",
|
| 767 |
+
"license": "Apache-2.0"
|
| 768 |
+
},
|
| 769 |
+
"node_modules/@monogrid/gainmap-js": {
|
| 770 |
+
"version": "3.4.0",
|
| 771 |
+
"resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz",
|
| 772 |
+
"integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==",
|
| 773 |
+
"license": "MIT",
|
| 774 |
+
"dependencies": {
|
| 775 |
+
"promise-worker-transferable": "^1.0.4"
|
| 776 |
+
},
|
| 777 |
+
"peerDependencies": {
|
| 778 |
+
"three": ">= 0.159.0"
|
| 779 |
+
}
|
| 780 |
+
},
|
| 781 |
+
"node_modules/@react-spring/animated": {
|
| 782 |
+
"version": "9.7.5",
|
| 783 |
+
"resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.5.tgz",
|
| 784 |
+
"integrity": "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==",
|
| 785 |
+
"license": "MIT",
|
| 786 |
+
"dependencies": {
|
| 787 |
+
"@react-spring/shared": "~9.7.5",
|
| 788 |
+
"@react-spring/types": "~9.7.5"
|
| 789 |
+
},
|
| 790 |
+
"peerDependencies": {
|
| 791 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
| 792 |
+
}
|
| 793 |
+
},
|
| 794 |
+
"node_modules/@react-spring/core": {
|
| 795 |
+
"version": "9.7.5",
|
| 796 |
+
"resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.5.tgz",
|
| 797 |
+
"integrity": "sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==",
|
| 798 |
+
"license": "MIT",
|
| 799 |
+
"dependencies": {
|
| 800 |
+
"@react-spring/animated": "~9.7.5",
|
| 801 |
+
"@react-spring/shared": "~9.7.5",
|
| 802 |
+
"@react-spring/types": "~9.7.5"
|
| 803 |
+
},
|
| 804 |
+
"funding": {
|
| 805 |
+
"type": "opencollective",
|
| 806 |
+
"url": "https://opencollective.com/react-spring/donate"
|
| 807 |
+
},
|
| 808 |
+
"peerDependencies": {
|
| 809 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
| 810 |
+
}
|
| 811 |
+
},
|
| 812 |
+
"node_modules/@react-spring/rafz": {
|
| 813 |
+
"version": "9.7.5",
|
| 814 |
+
"resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.5.tgz",
|
| 815 |
+
"integrity": "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==",
|
| 816 |
+
"license": "MIT"
|
| 817 |
+
},
|
| 818 |
+
"node_modules/@react-spring/shared": {
|
| 819 |
+
"version": "9.7.5",
|
| 820 |
+
"resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.5.tgz",
|
| 821 |
+
"integrity": "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==",
|
| 822 |
+
"license": "MIT",
|
| 823 |
+
"dependencies": {
|
| 824 |
+
"@react-spring/rafz": "~9.7.5",
|
| 825 |
+
"@react-spring/types": "~9.7.5"
|
| 826 |
+
},
|
| 827 |
+
"peerDependencies": {
|
| 828 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
| 829 |
+
}
|
| 830 |
+
},
|
| 831 |
+
"node_modules/@react-spring/three": {
|
| 832 |
+
"version": "9.7.5",
|
| 833 |
+
"resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.7.5.tgz",
|
| 834 |
+
"integrity": "sha512-RxIsCoQfUqOS3POmhVHa1wdWS0wyHAUway73uRLp3GAL5U2iYVNdnzQsep6M2NZ994BlW8TcKuMtQHUqOsy6WA==",
|
| 835 |
+
"license": "MIT",
|
| 836 |
+
"dependencies": {
|
| 837 |
+
"@react-spring/animated": "~9.7.5",
|
| 838 |
+
"@react-spring/core": "~9.7.5",
|
| 839 |
+
"@react-spring/shared": "~9.7.5",
|
| 840 |
+
"@react-spring/types": "~9.7.5"
|
| 841 |
+
},
|
| 842 |
+
"peerDependencies": {
|
| 843 |
+
"@react-three/fiber": ">=6.0",
|
| 844 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
| 845 |
+
"three": ">=0.126"
|
| 846 |
+
}
|
| 847 |
+
},
|
| 848 |
+
"node_modules/@react-spring/types": {
|
| 849 |
+
"version": "9.7.5",
|
| 850 |
+
"resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.5.tgz",
|
| 851 |
+
"integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==",
|
| 852 |
+
"license": "MIT"
|
| 853 |
+
},
|
| 854 |
+
"node_modules/@react-three/drei": {
|
| 855 |
+
"version": "9.122.0",
|
| 856 |
+
"resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-9.122.0.tgz",
|
| 857 |
+
"integrity": "sha512-SEO/F/rBCTjlLez7WAlpys+iGe9hty4rNgjZvgkQeXFSiwqD4Hbk/wNHMAbdd8vprO2Aj81mihv4dF5bC7D0CA==",
|
| 858 |
+
"license": "MIT",
|
| 859 |
+
"dependencies": {
|
| 860 |
+
"@babel/runtime": "^7.26.0",
|
| 861 |
+
"@mediapipe/tasks-vision": "0.10.17",
|
| 862 |
+
"@monogrid/gainmap-js": "^3.0.6",
|
| 863 |
+
"@react-spring/three": "~9.7.5",
|
| 864 |
+
"@use-gesture/react": "^10.3.1",
|
| 865 |
+
"camera-controls": "^2.9.0",
|
| 866 |
+
"cross-env": "^7.0.3",
|
| 867 |
+
"detect-gpu": "^5.0.56",
|
| 868 |
+
"glsl-noise": "^0.0.0",
|
| 869 |
+
"hls.js": "^1.5.17",
|
| 870 |
+
"maath": "^0.10.8",
|
| 871 |
+
"meshline": "^3.3.1",
|
| 872 |
+
"react-composer": "^5.0.3",
|
| 873 |
+
"stats-gl": "^2.2.8",
|
| 874 |
+
"stats.js": "^0.17.0",
|
| 875 |
+
"suspend-react": "^0.1.3",
|
| 876 |
+
"three-mesh-bvh": "^0.7.8",
|
| 877 |
+
"three-stdlib": "^2.35.6",
|
| 878 |
+
"troika-three-text": "^0.52.0",
|
| 879 |
+
"tunnel-rat": "^0.1.2",
|
| 880 |
+
"utility-types": "^3.11.0",
|
| 881 |
+
"zustand": "^5.0.1"
|
| 882 |
+
},
|
| 883 |
+
"peerDependencies": {
|
| 884 |
+
"@react-three/fiber": "^8",
|
| 885 |
+
"react": "^18",
|
| 886 |
+
"react-dom": "^18",
|
| 887 |
+
"three": ">=0.137"
|
| 888 |
+
},
|
| 889 |
+
"peerDependenciesMeta": {
|
| 890 |
+
"react-dom": {
|
| 891 |
+
"optional": true
|
| 892 |
+
}
|
| 893 |
+
}
|
| 894 |
+
},
|
| 895 |
+
"node_modules/@react-three/fiber": {
|
| 896 |
+
"version": "8.18.0",
|
| 897 |
+
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.18.0.tgz",
|
| 898 |
+
"integrity": "sha512-FYZZqD0UUHUswKz3LQl2Z7H24AhD14XGTsIRw3SJaXUxyfVMi+1yiZGmqTcPt/CkPpdU7rrxqcyQ1zJE5DjvIQ==",
|
| 899 |
+
"license": "MIT",
|
| 900 |
+
"dependencies": {
|
| 901 |
+
"@babel/runtime": "^7.17.8",
|
| 902 |
+
"@types/react-reconciler": "^0.26.7",
|
| 903 |
+
"@types/webxr": "*",
|
| 904 |
+
"base64-js": "^1.5.1",
|
| 905 |
+
"buffer": "^6.0.3",
|
| 906 |
+
"its-fine": "^1.0.6",
|
| 907 |
+
"react-reconciler": "^0.27.0",
|
| 908 |
+
"react-use-measure": "^2.1.7",
|
| 909 |
+
"scheduler": "^0.21.0",
|
| 910 |
+
"suspend-react": "^0.1.3",
|
| 911 |
+
"zustand": "^3.7.1"
|
| 912 |
+
},
|
| 913 |
+
"peerDependencies": {
|
| 914 |
+
"expo": ">=43.0",
|
| 915 |
+
"expo-asset": ">=8.4",
|
| 916 |
+
"expo-file-system": ">=11.0",
|
| 917 |
+
"expo-gl": ">=11.0",
|
| 918 |
+
"react": ">=18 <19",
|
| 919 |
+
"react-dom": ">=18 <19",
|
| 920 |
+
"react-native": ">=0.64",
|
| 921 |
+
"three": ">=0.133"
|
| 922 |
+
},
|
| 923 |
+
"peerDependenciesMeta": {
|
| 924 |
+
"expo": {
|
| 925 |
+
"optional": true
|
| 926 |
+
},
|
| 927 |
+
"expo-asset": {
|
| 928 |
+
"optional": true
|
| 929 |
+
},
|
| 930 |
+
"expo-file-system": {
|
| 931 |
+
"optional": true
|
| 932 |
+
},
|
| 933 |
+
"expo-gl": {
|
| 934 |
+
"optional": true
|
| 935 |
+
},
|
| 936 |
+
"react-dom": {
|
| 937 |
+
"optional": true
|
| 938 |
+
},
|
| 939 |
+
"react-native": {
|
| 940 |
+
"optional": true
|
| 941 |
+
}
|
| 942 |
+
}
|
| 943 |
+
},
|
| 944 |
+
"node_modules/@react-three/fiber/node_modules/zustand": {
|
| 945 |
+
"version": "3.7.2",
|
| 946 |
+
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
| 947 |
+
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
|
| 948 |
+
"license": "MIT",
|
| 949 |
+
"engines": {
|
| 950 |
+
"node": ">=12.7.0"
|
| 951 |
+
},
|
| 952 |
+
"peerDependencies": {
|
| 953 |
+
"react": ">=16.8"
|
| 954 |
+
},
|
| 955 |
+
"peerDependenciesMeta": {
|
| 956 |
+
"react": {
|
| 957 |
+
"optional": true
|
| 958 |
+
}
|
| 959 |
+
}
|
| 960 |
+
},
|
| 961 |
+
"node_modules/@react-three/postprocessing": {
|
| 962 |
+
"version": "2.19.1",
|
| 963 |
+
"resolved": "https://registry.npmjs.org/@react-three/postprocessing/-/postprocessing-2.19.1.tgz",
|
| 964 |
+
"integrity": "sha512-7P25LOSToH/I6b3UipNK17IIFlX4FDUmWcaomfwu82+CzhXTOz8Fcc1ZXEZ7vFA/5Fr/2peNlXgXZJvoa+aCdA==",
|
| 965 |
+
"license": "MIT",
|
| 966 |
+
"dependencies": {
|
| 967 |
+
"buffer": "^6.0.3",
|
| 968 |
+
"maath": "^0.6.0",
|
| 969 |
+
"n8ao": "^1.6.6",
|
| 970 |
+
"postprocessing": "^6.32.1",
|
| 971 |
+
"three-stdlib": "^2.23.4"
|
| 972 |
+
},
|
| 973 |
+
"peerDependencies": {
|
| 974 |
+
"@react-three/fiber": "^8.0",
|
| 975 |
+
"react": "^18.0",
|
| 976 |
+
"three": ">= 0.138.0"
|
| 977 |
+
}
|
| 978 |
+
},
|
| 979 |
+
"node_modules/@react-three/postprocessing/node_modules/maath": {
|
| 980 |
+
"version": "0.6.0",
|
| 981 |
+
"resolved": "https://registry.npmjs.org/maath/-/maath-0.6.0.tgz",
|
| 982 |
+
"integrity": "sha512-dSb2xQuP7vDnaYqfoKzlApeRcR2xtN8/f7WV/TMAkBC8552TwTLtOO0JTcSygkYMjNDPoo6V01jTw/aPi4JrMw==",
|
| 983 |
+
"license": "MIT",
|
| 984 |
+
"peerDependencies": {
|
| 985 |
+
"@types/three": ">=0.144.0",
|
| 986 |
+
"three": ">=0.144.0"
|
| 987 |
+
}
|
| 988 |
+
},
|
| 989 |
+
"node_modules/@rolldown/pluginutils": {
|
| 990 |
+
"version": "1.0.0-beta.27",
|
| 991 |
+
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
| 992 |
+
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
|
| 993 |
+
"dev": true,
|
| 994 |
+
"license": "MIT"
|
| 995 |
+
},
|
| 996 |
+
"node_modules/@rollup/rollup-android-arm-eabi": {
|
| 997 |
+
"version": "4.62.2",
|
| 998 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
|
| 999 |
+
"integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",
|
| 1000 |
+
"cpu": [
|
| 1001 |
+
"arm"
|
| 1002 |
+
],
|
| 1003 |
+
"dev": true,
|
| 1004 |
+
"license": "MIT",
|
| 1005 |
+
"optional": true,
|
| 1006 |
+
"os": [
|
| 1007 |
+
"android"
|
| 1008 |
+
]
|
| 1009 |
+
},
|
| 1010 |
+
"node_modules/@rollup/rollup-android-arm64": {
|
| 1011 |
+
"version": "4.62.2",
|
| 1012 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz",
|
| 1013 |
+
"integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==",
|
| 1014 |
+
"cpu": [
|
| 1015 |
+
"arm64"
|
| 1016 |
+
],
|
| 1017 |
+
"dev": true,
|
| 1018 |
+
"license": "MIT",
|
| 1019 |
+
"optional": true,
|
| 1020 |
+
"os": [
|
| 1021 |
+
"android"
|
| 1022 |
+
]
|
| 1023 |
+
},
|
| 1024 |
+
"node_modules/@rollup/rollup-darwin-arm64": {
|
| 1025 |
+
"version": "4.62.2",
|
| 1026 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz",
|
| 1027 |
+
"integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==",
|
| 1028 |
+
"cpu": [
|
| 1029 |
+
"arm64"
|
| 1030 |
+
],
|
| 1031 |
+
"dev": true,
|
| 1032 |
+
"license": "MIT",
|
| 1033 |
+
"optional": true,
|
| 1034 |
+
"os": [
|
| 1035 |
+
"darwin"
|
| 1036 |
+
]
|
| 1037 |
+
},
|
| 1038 |
+
"node_modules/@rollup/rollup-darwin-x64": {
|
| 1039 |
+
"version": "4.62.2",
|
| 1040 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz",
|
| 1041 |
+
"integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==",
|
| 1042 |
+
"cpu": [
|
| 1043 |
+
"x64"
|
| 1044 |
+
],
|
| 1045 |
+
"dev": true,
|
| 1046 |
+
"license": "MIT",
|
| 1047 |
+
"optional": true,
|
| 1048 |
+
"os": [
|
| 1049 |
+
"darwin"
|
| 1050 |
+
]
|
| 1051 |
+
},
|
| 1052 |
+
"node_modules/@rollup/rollup-freebsd-arm64": {
|
| 1053 |
+
"version": "4.62.2",
|
| 1054 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz",
|
| 1055 |
+
"integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==",
|
| 1056 |
+
"cpu": [
|
| 1057 |
+
"arm64"
|
| 1058 |
+
],
|
| 1059 |
+
"dev": true,
|
| 1060 |
+
"license": "MIT",
|
| 1061 |
+
"optional": true,
|
| 1062 |
+
"os": [
|
| 1063 |
+
"freebsd"
|
| 1064 |
+
]
|
| 1065 |
+
},
|
| 1066 |
+
"node_modules/@rollup/rollup-freebsd-x64": {
|
| 1067 |
+
"version": "4.62.2",
|
| 1068 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz",
|
| 1069 |
+
"integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==",
|
| 1070 |
+
"cpu": [
|
| 1071 |
+
"x64"
|
| 1072 |
+
],
|
| 1073 |
+
"dev": true,
|
| 1074 |
+
"license": "MIT",
|
| 1075 |
+
"optional": true,
|
| 1076 |
+
"os": [
|
| 1077 |
+
"freebsd"
|
| 1078 |
+
]
|
| 1079 |
+
},
|
| 1080 |
+
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
| 1081 |
+
"version": "4.62.2",
|
| 1082 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz",
|
| 1083 |
+
"integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==",
|
| 1084 |
+
"cpu": [
|
| 1085 |
+
"arm"
|
| 1086 |
+
],
|
| 1087 |
+
"dev": true,
|
| 1088 |
+
"libc": [
|
| 1089 |
+
"glibc"
|
| 1090 |
+
],
|
| 1091 |
+
"license": "MIT",
|
| 1092 |
+
"optional": true,
|
| 1093 |
+
"os": [
|
| 1094 |
+
"linux"
|
| 1095 |
+
]
|
| 1096 |
+
},
|
| 1097 |
+
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
| 1098 |
+
"version": "4.62.2",
|
| 1099 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz",
|
| 1100 |
+
"integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==",
|
| 1101 |
+
"cpu": [
|
| 1102 |
+
"arm"
|
| 1103 |
+
],
|
| 1104 |
+
"dev": true,
|
| 1105 |
+
"libc": [
|
| 1106 |
+
"musl"
|
| 1107 |
+
],
|
| 1108 |
+
"license": "MIT",
|
| 1109 |
+
"optional": true,
|
| 1110 |
+
"os": [
|
| 1111 |
+
"linux"
|
| 1112 |
+
]
|
| 1113 |
+
},
|
| 1114 |
+
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
| 1115 |
+
"version": "4.62.2",
|
| 1116 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz",
|
| 1117 |
+
"integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==",
|
| 1118 |
+
"cpu": [
|
| 1119 |
+
"arm64"
|
| 1120 |
+
],
|
| 1121 |
+
"dev": true,
|
| 1122 |
+
"libc": [
|
| 1123 |
+
"glibc"
|
| 1124 |
+
],
|
| 1125 |
+
"license": "MIT",
|
| 1126 |
+
"optional": true,
|
| 1127 |
+
"os": [
|
| 1128 |
+
"linux"
|
| 1129 |
+
]
|
| 1130 |
+
},
|
| 1131 |
+
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
| 1132 |
+
"version": "4.62.2",
|
| 1133 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz",
|
| 1134 |
+
"integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==",
|
| 1135 |
+
"cpu": [
|
| 1136 |
+
"arm64"
|
| 1137 |
+
],
|
| 1138 |
+
"dev": true,
|
| 1139 |
+
"libc": [
|
| 1140 |
+
"musl"
|
| 1141 |
+
],
|
| 1142 |
+
"license": "MIT",
|
| 1143 |
+
"optional": true,
|
| 1144 |
+
"os": [
|
| 1145 |
+
"linux"
|
| 1146 |
+
]
|
| 1147 |
+
},
|
| 1148 |
+
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
| 1149 |
+
"version": "4.62.2",
|
| 1150 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz",
|
| 1151 |
+
"integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==",
|
| 1152 |
+
"cpu": [
|
| 1153 |
+
"loong64"
|
| 1154 |
+
],
|
| 1155 |
+
"dev": true,
|
| 1156 |
+
"libc": [
|
| 1157 |
+
"glibc"
|
| 1158 |
+
],
|
| 1159 |
+
"license": "MIT",
|
| 1160 |
+
"optional": true,
|
| 1161 |
+
"os": [
|
| 1162 |
+
"linux"
|
| 1163 |
+
]
|
| 1164 |
+
},
|
| 1165 |
+
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
| 1166 |
+
"version": "4.62.2",
|
| 1167 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz",
|
| 1168 |
+
"integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==",
|
| 1169 |
+
"cpu": [
|
| 1170 |
+
"loong64"
|
| 1171 |
+
],
|
| 1172 |
+
"dev": true,
|
| 1173 |
+
"libc": [
|
| 1174 |
+
"musl"
|
| 1175 |
+
],
|
| 1176 |
+
"license": "MIT",
|
| 1177 |
+
"optional": true,
|
| 1178 |
+
"os": [
|
| 1179 |
+
"linux"
|
| 1180 |
+
]
|
| 1181 |
+
},
|
| 1182 |
+
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
| 1183 |
+
"version": "4.62.2",
|
| 1184 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz",
|
| 1185 |
+
"integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==",
|
| 1186 |
+
"cpu": [
|
| 1187 |
+
"ppc64"
|
| 1188 |
+
],
|
| 1189 |
+
"dev": true,
|
| 1190 |
+
"libc": [
|
| 1191 |
+
"glibc"
|
| 1192 |
+
],
|
| 1193 |
+
"license": "MIT",
|
| 1194 |
+
"optional": true,
|
| 1195 |
+
"os": [
|
| 1196 |
+
"linux"
|
| 1197 |
+
]
|
| 1198 |
+
},
|
| 1199 |
+
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
| 1200 |
+
"version": "4.62.2",
|
| 1201 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz",
|
| 1202 |
+
"integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==",
|
| 1203 |
+
"cpu": [
|
| 1204 |
+
"ppc64"
|
| 1205 |
+
],
|
| 1206 |
+
"dev": true,
|
| 1207 |
+
"libc": [
|
| 1208 |
+
"musl"
|
| 1209 |
+
],
|
| 1210 |
+
"license": "MIT",
|
| 1211 |
+
"optional": true,
|
| 1212 |
+
"os": [
|
| 1213 |
+
"linux"
|
| 1214 |
+
]
|
| 1215 |
+
},
|
| 1216 |
+
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
| 1217 |
+
"version": "4.62.2",
|
| 1218 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz",
|
| 1219 |
+
"integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==",
|
| 1220 |
+
"cpu": [
|
| 1221 |
+
"riscv64"
|
| 1222 |
+
],
|
| 1223 |
+
"dev": true,
|
| 1224 |
+
"libc": [
|
| 1225 |
+
"glibc"
|
| 1226 |
+
],
|
| 1227 |
+
"license": "MIT",
|
| 1228 |
+
"optional": true,
|
| 1229 |
+
"os": [
|
| 1230 |
+
"linux"
|
| 1231 |
+
]
|
| 1232 |
+
},
|
| 1233 |
+
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
| 1234 |
+
"version": "4.62.2",
|
| 1235 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz",
|
| 1236 |
+
"integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==",
|
| 1237 |
+
"cpu": [
|
| 1238 |
+
"riscv64"
|
| 1239 |
+
],
|
| 1240 |
+
"dev": true,
|
| 1241 |
+
"libc": [
|
| 1242 |
+
"musl"
|
| 1243 |
+
],
|
| 1244 |
+
"license": "MIT",
|
| 1245 |
+
"optional": true,
|
| 1246 |
+
"os": [
|
| 1247 |
+
"linux"
|
| 1248 |
+
]
|
| 1249 |
+
},
|
| 1250 |
+
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
| 1251 |
+
"version": "4.62.2",
|
| 1252 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz",
|
| 1253 |
+
"integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==",
|
| 1254 |
+
"cpu": [
|
| 1255 |
+
"s390x"
|
| 1256 |
+
],
|
| 1257 |
+
"dev": true,
|
| 1258 |
+
"libc": [
|
| 1259 |
+
"glibc"
|
| 1260 |
+
],
|
| 1261 |
+
"license": "MIT",
|
| 1262 |
+
"optional": true,
|
| 1263 |
+
"os": [
|
| 1264 |
+
"linux"
|
| 1265 |
+
]
|
| 1266 |
+
},
|
| 1267 |
+
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
| 1268 |
+
"version": "4.62.2",
|
| 1269 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",
|
| 1270 |
+
"integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",
|
| 1271 |
+
"cpu": [
|
| 1272 |
+
"x64"
|
| 1273 |
+
],
|
| 1274 |
+
"dev": true,
|
| 1275 |
+
"libc": [
|
| 1276 |
+
"glibc"
|
| 1277 |
+
],
|
| 1278 |
+
"license": "MIT",
|
| 1279 |
+
"optional": true,
|
| 1280 |
+
"os": [
|
| 1281 |
+
"linux"
|
| 1282 |
+
]
|
| 1283 |
+
},
|
| 1284 |
+
"node_modules/@rollup/rollup-linux-x64-musl": {
|
| 1285 |
+
"version": "4.62.2",
|
| 1286 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",
|
| 1287 |
+
"integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",
|
| 1288 |
+
"cpu": [
|
| 1289 |
+
"x64"
|
| 1290 |
+
],
|
| 1291 |
+
"dev": true,
|
| 1292 |
+
"libc": [
|
| 1293 |
+
"musl"
|
| 1294 |
+
],
|
| 1295 |
+
"license": "MIT",
|
| 1296 |
+
"optional": true,
|
| 1297 |
+
"os": [
|
| 1298 |
+
"linux"
|
| 1299 |
+
]
|
| 1300 |
+
},
|
| 1301 |
+
"node_modules/@rollup/rollup-openbsd-x64": {
|
| 1302 |
+
"version": "4.62.2",
|
| 1303 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz",
|
| 1304 |
+
"integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==",
|
| 1305 |
+
"cpu": [
|
| 1306 |
+
"x64"
|
| 1307 |
+
],
|
| 1308 |
+
"dev": true,
|
| 1309 |
+
"license": "MIT",
|
| 1310 |
+
"optional": true,
|
| 1311 |
+
"os": [
|
| 1312 |
+
"openbsd"
|
| 1313 |
+
]
|
| 1314 |
+
},
|
| 1315 |
+
"node_modules/@rollup/rollup-openharmony-arm64": {
|
| 1316 |
+
"version": "4.62.2",
|
| 1317 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz",
|
| 1318 |
+
"integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==",
|
| 1319 |
+
"cpu": [
|
| 1320 |
+
"arm64"
|
| 1321 |
+
],
|
| 1322 |
+
"dev": true,
|
| 1323 |
+
"license": "MIT",
|
| 1324 |
+
"optional": true,
|
| 1325 |
+
"os": [
|
| 1326 |
+
"openharmony"
|
| 1327 |
+
]
|
| 1328 |
+
},
|
| 1329 |
+
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
| 1330 |
+
"version": "4.62.2",
|
| 1331 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz",
|
| 1332 |
+
"integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==",
|
| 1333 |
+
"cpu": [
|
| 1334 |
+
"arm64"
|
| 1335 |
+
],
|
| 1336 |
+
"dev": true,
|
| 1337 |
+
"license": "MIT",
|
| 1338 |
+
"optional": true,
|
| 1339 |
+
"os": [
|
| 1340 |
+
"win32"
|
| 1341 |
+
]
|
| 1342 |
+
},
|
| 1343 |
+
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
| 1344 |
+
"version": "4.62.2",
|
| 1345 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz",
|
| 1346 |
+
"integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==",
|
| 1347 |
+
"cpu": [
|
| 1348 |
+
"ia32"
|
| 1349 |
+
],
|
| 1350 |
+
"dev": true,
|
| 1351 |
+
"license": "MIT",
|
| 1352 |
+
"optional": true,
|
| 1353 |
+
"os": [
|
| 1354 |
+
"win32"
|
| 1355 |
+
]
|
| 1356 |
+
},
|
| 1357 |
+
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
| 1358 |
+
"version": "4.62.2",
|
| 1359 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz",
|
| 1360 |
+
"integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==",
|
| 1361 |
+
"cpu": [
|
| 1362 |
+
"x64"
|
| 1363 |
+
],
|
| 1364 |
+
"dev": true,
|
| 1365 |
+
"license": "MIT",
|
| 1366 |
+
"optional": true,
|
| 1367 |
+
"os": [
|
| 1368 |
+
"win32"
|
| 1369 |
+
]
|
| 1370 |
+
},
|
| 1371 |
+
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
| 1372 |
+
"version": "4.62.2",
|
| 1373 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz",
|
| 1374 |
+
"integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==",
|
| 1375 |
+
"cpu": [
|
| 1376 |
+
"x64"
|
| 1377 |
+
],
|
| 1378 |
+
"dev": true,
|
| 1379 |
+
"license": "MIT",
|
| 1380 |
+
"optional": true,
|
| 1381 |
+
"os": [
|
| 1382 |
+
"win32"
|
| 1383 |
+
]
|
| 1384 |
+
},
|
| 1385 |
+
"node_modules/@tweenjs/tween.js": {
|
| 1386 |
+
"version": "23.1.3",
|
| 1387 |
+
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
|
| 1388 |
+
"integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
|
| 1389 |
+
"license": "MIT"
|
| 1390 |
+
},
|
| 1391 |
+
"node_modules/@types/babel__core": {
|
| 1392 |
+
"version": "7.20.5",
|
| 1393 |
+
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
| 1394 |
+
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
|
| 1395 |
+
"dev": true,
|
| 1396 |
+
"license": "MIT",
|
| 1397 |
+
"dependencies": {
|
| 1398 |
+
"@babel/parser": "^7.20.7",
|
| 1399 |
+
"@babel/types": "^7.20.7",
|
| 1400 |
+
"@types/babel__generator": "*",
|
| 1401 |
+
"@types/babel__template": "*",
|
| 1402 |
+
"@types/babel__traverse": "*"
|
| 1403 |
+
}
|
| 1404 |
+
},
|
| 1405 |
+
"node_modules/@types/babel__generator": {
|
| 1406 |
+
"version": "7.27.0",
|
| 1407 |
+
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
|
| 1408 |
+
"integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
|
| 1409 |
+
"dev": true,
|
| 1410 |
+
"license": "MIT",
|
| 1411 |
+
"dependencies": {
|
| 1412 |
+
"@babel/types": "^7.0.0"
|
| 1413 |
+
}
|
| 1414 |
+
},
|
| 1415 |
+
"node_modules/@types/babel__template": {
|
| 1416 |
+
"version": "7.4.4",
|
| 1417 |
+
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
|
| 1418 |
+
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
|
| 1419 |
+
"dev": true,
|
| 1420 |
+
"license": "MIT",
|
| 1421 |
+
"dependencies": {
|
| 1422 |
+
"@babel/parser": "^7.1.0",
|
| 1423 |
+
"@babel/types": "^7.0.0"
|
| 1424 |
+
}
|
| 1425 |
+
},
|
| 1426 |
+
"node_modules/@types/babel__traverse": {
|
| 1427 |
+
"version": "7.28.0",
|
| 1428 |
+
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
|
| 1429 |
+
"integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
|
| 1430 |
+
"dev": true,
|
| 1431 |
+
"license": "MIT",
|
| 1432 |
+
"dependencies": {
|
| 1433 |
+
"@babel/types": "^7.28.2"
|
| 1434 |
+
}
|
| 1435 |
+
},
|
| 1436 |
+
"node_modules/@types/draco3d": {
|
| 1437 |
+
"version": "1.4.10",
|
| 1438 |
+
"resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
|
| 1439 |
+
"integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==",
|
| 1440 |
+
"license": "MIT"
|
| 1441 |
+
},
|
| 1442 |
+
"node_modules/@types/estree": {
|
| 1443 |
+
"version": "1.0.9",
|
| 1444 |
+
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
|
| 1445 |
+
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
|
| 1446 |
+
"dev": true,
|
| 1447 |
+
"license": "MIT"
|
| 1448 |
+
},
|
| 1449 |
+
"node_modules/@types/offscreencanvas": {
|
| 1450 |
+
"version": "2019.7.3",
|
| 1451 |
+
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
|
| 1452 |
+
"integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==",
|
| 1453 |
+
"license": "MIT"
|
| 1454 |
+
},
|
| 1455 |
+
"node_modules/@types/prismjs": {
|
| 1456 |
+
"version": "1.26.6",
|
| 1457 |
+
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz",
|
| 1458 |
+
"integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==",
|
| 1459 |
+
"dev": true,
|
| 1460 |
+
"license": "MIT"
|
| 1461 |
+
},
|
| 1462 |
+
"node_modules/@types/prop-types": {
|
| 1463 |
+
"version": "15.7.15",
|
| 1464 |
+
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
| 1465 |
+
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
|
| 1466 |
+
"license": "MIT"
|
| 1467 |
+
},
|
| 1468 |
+
"node_modules/@types/react": {
|
| 1469 |
+
"version": "18.3.31",
|
| 1470 |
+
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
|
| 1471 |
+
"integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
|
| 1472 |
+
"license": "MIT",
|
| 1473 |
+
"dependencies": {
|
| 1474 |
+
"@types/prop-types": "*",
|
| 1475 |
+
"csstype": "^3.2.2"
|
| 1476 |
+
}
|
| 1477 |
+
},
|
| 1478 |
+
"node_modules/@types/react-dom": {
|
| 1479 |
+
"version": "18.3.7",
|
| 1480 |
+
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
|
| 1481 |
+
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
|
| 1482 |
+
"dev": true,
|
| 1483 |
+
"license": "MIT",
|
| 1484 |
+
"peerDependencies": {
|
| 1485 |
+
"@types/react": "^18.0.0"
|
| 1486 |
+
}
|
| 1487 |
+
},
|
| 1488 |
+
"node_modules/@types/react-reconciler": {
|
| 1489 |
+
"version": "0.26.7",
|
| 1490 |
+
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
|
| 1491 |
+
"integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
|
| 1492 |
+
"license": "MIT",
|
| 1493 |
+
"dependencies": {
|
| 1494 |
+
"@types/react": "*"
|
| 1495 |
+
}
|
| 1496 |
+
},
|
| 1497 |
+
"node_modules/@types/stats.js": {
|
| 1498 |
+
"version": "0.17.4",
|
| 1499 |
+
"resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
|
| 1500 |
+
"integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
|
| 1501 |
+
"license": "MIT"
|
| 1502 |
+
},
|
| 1503 |
+
"node_modules/@types/three": {
|
| 1504 |
+
"version": "0.169.0",
|
| 1505 |
+
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.169.0.tgz",
|
| 1506 |
+
"integrity": "sha512-oan7qCgJBt03wIaK+4xPWclYRPG9wzcg7Z2f5T8xYTNEF95kh0t0lklxLLYBDo7gQiGLYzE6iF4ta7nXF2bcsw==",
|
| 1507 |
+
"license": "MIT",
|
| 1508 |
+
"dependencies": {
|
| 1509 |
+
"@tweenjs/tween.js": "~23.1.3",
|
| 1510 |
+
"@types/stats.js": "*",
|
| 1511 |
+
"@types/webxr": "*",
|
| 1512 |
+
"@webgpu/types": "*",
|
| 1513 |
+
"fflate": "~0.8.2",
|
| 1514 |
+
"meshoptimizer": "~0.18.1"
|
| 1515 |
+
}
|
| 1516 |
+
},
|
| 1517 |
+
"node_modules/@types/webxr": {
|
| 1518 |
+
"version": "0.5.24",
|
| 1519 |
+
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
|
| 1520 |
+
"integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
|
| 1521 |
+
"license": "MIT"
|
| 1522 |
+
},
|
| 1523 |
+
"node_modules/@use-gesture/core": {
|
| 1524 |
+
"version": "10.3.1",
|
| 1525 |
+
"resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz",
|
| 1526 |
+
"integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==",
|
| 1527 |
+
"license": "MIT"
|
| 1528 |
+
},
|
| 1529 |
+
"node_modules/@use-gesture/react": {
|
| 1530 |
+
"version": "10.3.1",
|
| 1531 |
+
"resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz",
|
| 1532 |
+
"integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==",
|
| 1533 |
+
"license": "MIT",
|
| 1534 |
+
"dependencies": {
|
| 1535 |
+
"@use-gesture/core": "10.3.1"
|
| 1536 |
+
},
|
| 1537 |
+
"peerDependencies": {
|
| 1538 |
+
"react": ">= 16.8.0"
|
| 1539 |
+
}
|
| 1540 |
+
},
|
| 1541 |
+
"node_modules/@vitejs/plugin-react": {
|
| 1542 |
+
"version": "4.7.0",
|
| 1543 |
+
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
|
| 1544 |
+
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
|
| 1545 |
+
"dev": true,
|
| 1546 |
+
"license": "MIT",
|
| 1547 |
+
"dependencies": {
|
| 1548 |
+
"@babel/core": "^7.28.0",
|
| 1549 |
+
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
| 1550 |
+
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
|
| 1551 |
+
"@rolldown/pluginutils": "1.0.0-beta.27",
|
| 1552 |
+
"@types/babel__core": "^7.20.5",
|
| 1553 |
+
"react-refresh": "^0.17.0"
|
| 1554 |
+
},
|
| 1555 |
+
"engines": {
|
| 1556 |
+
"node": "^14.18.0 || >=16.0.0"
|
| 1557 |
+
},
|
| 1558 |
+
"peerDependencies": {
|
| 1559 |
+
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
| 1560 |
+
}
|
| 1561 |
+
},
|
| 1562 |
+
"node_modules/@webgpu/types": {
|
| 1563 |
+
"version": "0.1.71",
|
| 1564 |
+
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.71.tgz",
|
| 1565 |
+
"integrity": "sha512-mMy8/ODcKhab808co15eW+yN+HgXoQxRQHTiBV9Mrvl1r0ufnid7YOcI+gi4eUWSWl9ezD6TW2KXccrL8HCh2A==",
|
| 1566 |
+
"license": "BSD-3-Clause"
|
| 1567 |
+
},
|
| 1568 |
+
"node_modules/base64-js": {
|
| 1569 |
+
"version": "1.5.1",
|
| 1570 |
+
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
| 1571 |
+
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
| 1572 |
+
"funding": [
|
| 1573 |
+
{
|
| 1574 |
+
"type": "github",
|
| 1575 |
+
"url": "https://github.com/sponsors/feross"
|
| 1576 |
+
},
|
| 1577 |
+
{
|
| 1578 |
+
"type": "patreon",
|
| 1579 |
+
"url": "https://www.patreon.com/feross"
|
| 1580 |
+
},
|
| 1581 |
+
{
|
| 1582 |
+
"type": "consulting",
|
| 1583 |
+
"url": "https://feross.org/support"
|
| 1584 |
+
}
|
| 1585 |
+
],
|
| 1586 |
+
"license": "MIT"
|
| 1587 |
+
},
|
| 1588 |
+
"node_modules/baseline-browser-mapping": {
|
| 1589 |
+
"version": "2.10.42",
|
| 1590 |
+
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz",
|
| 1591 |
+
"integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==",
|
| 1592 |
+
"dev": true,
|
| 1593 |
+
"license": "Apache-2.0",
|
| 1594 |
+
"bin": {
|
| 1595 |
+
"baseline-browser-mapping": "dist/cli.cjs"
|
| 1596 |
+
},
|
| 1597 |
+
"engines": {
|
| 1598 |
+
"node": ">=6.0.0"
|
| 1599 |
+
}
|
| 1600 |
+
},
|
| 1601 |
+
"node_modules/bidi-js": {
|
| 1602 |
+
"version": "1.0.3",
|
| 1603 |
+
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
| 1604 |
+
"integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
|
| 1605 |
+
"license": "MIT",
|
| 1606 |
+
"dependencies": {
|
| 1607 |
+
"require-from-string": "^2.0.2"
|
| 1608 |
+
}
|
| 1609 |
+
},
|
| 1610 |
+
"node_modules/browserslist": {
|
| 1611 |
+
"version": "4.28.5",
|
| 1612 |
+
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.5.tgz",
|
| 1613 |
+
"integrity": "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==",
|
| 1614 |
+
"dev": true,
|
| 1615 |
+
"funding": [
|
| 1616 |
+
{
|
| 1617 |
+
"type": "opencollective",
|
| 1618 |
+
"url": "https://opencollective.com/browserslist"
|
| 1619 |
+
},
|
| 1620 |
+
{
|
| 1621 |
+
"type": "tidelift",
|
| 1622 |
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
| 1623 |
+
},
|
| 1624 |
+
{
|
| 1625 |
+
"type": "github",
|
| 1626 |
+
"url": "https://github.com/sponsors/ai"
|
| 1627 |
+
}
|
| 1628 |
+
],
|
| 1629 |
+
"license": "MIT",
|
| 1630 |
+
"dependencies": {
|
| 1631 |
+
"baseline-browser-mapping": "^2.10.42",
|
| 1632 |
+
"caniuse-lite": "^1.0.30001800",
|
| 1633 |
+
"electron-to-chromium": "^1.5.387",
|
| 1634 |
+
"node-releases": "^2.0.50",
|
| 1635 |
+
"update-browserslist-db": "^1.2.3"
|
| 1636 |
+
},
|
| 1637 |
+
"bin": {
|
| 1638 |
+
"browserslist": "cli.js"
|
| 1639 |
+
},
|
| 1640 |
+
"engines": {
|
| 1641 |
+
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
| 1642 |
+
}
|
| 1643 |
+
},
|
| 1644 |
+
"node_modules/buffer": {
|
| 1645 |
+
"version": "6.0.3",
|
| 1646 |
+
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
| 1647 |
+
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
| 1648 |
+
"funding": [
|
| 1649 |
+
{
|
| 1650 |
+
"type": "github",
|
| 1651 |
+
"url": "https://github.com/sponsors/feross"
|
| 1652 |
+
},
|
| 1653 |
+
{
|
| 1654 |
+
"type": "patreon",
|
| 1655 |
+
"url": "https://www.patreon.com/feross"
|
| 1656 |
+
},
|
| 1657 |
+
{
|
| 1658 |
+
"type": "consulting",
|
| 1659 |
+
"url": "https://feross.org/support"
|
| 1660 |
+
}
|
| 1661 |
+
],
|
| 1662 |
+
"license": "MIT",
|
| 1663 |
+
"dependencies": {
|
| 1664 |
+
"base64-js": "^1.3.1",
|
| 1665 |
+
"ieee754": "^1.2.1"
|
| 1666 |
+
}
|
| 1667 |
+
},
|
| 1668 |
+
"node_modules/camera-controls": {
|
| 1669 |
+
"version": "2.10.1",
|
| 1670 |
+
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.10.1.tgz",
|
| 1671 |
+
"integrity": "sha512-KnaKdcvkBJ1Irbrzl8XD6WtZltkRjp869Jx8c0ujs9K+9WD+1D7ryBsCiVqJYUqt6i/HR5FxT7RLASieUD+Q5w==",
|
| 1672 |
+
"license": "MIT",
|
| 1673 |
+
"peerDependencies": {
|
| 1674 |
+
"three": ">=0.126.1"
|
| 1675 |
+
}
|
| 1676 |
+
},
|
| 1677 |
+
"node_modules/caniuse-lite": {
|
| 1678 |
+
"version": "1.0.30001803",
|
| 1679 |
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz",
|
| 1680 |
+
"integrity": "sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==",
|
| 1681 |
+
"dev": true,
|
| 1682 |
+
"funding": [
|
| 1683 |
+
{
|
| 1684 |
+
"type": "opencollective",
|
| 1685 |
+
"url": "https://opencollective.com/browserslist"
|
| 1686 |
+
},
|
| 1687 |
+
{
|
| 1688 |
+
"type": "tidelift",
|
| 1689 |
+
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
| 1690 |
+
},
|
| 1691 |
+
{
|
| 1692 |
+
"type": "github",
|
| 1693 |
+
"url": "https://github.com/sponsors/ai"
|
| 1694 |
+
}
|
| 1695 |
+
],
|
| 1696 |
+
"license": "CC-BY-4.0"
|
| 1697 |
+
},
|
| 1698 |
+
"node_modules/convert-source-map": {
|
| 1699 |
+
"version": "2.0.0",
|
| 1700 |
+
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
| 1701 |
+
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
| 1702 |
+
"dev": true,
|
| 1703 |
+
"license": "MIT"
|
| 1704 |
+
},
|
| 1705 |
+
"node_modules/cross-env": {
|
| 1706 |
+
"version": "7.0.3",
|
| 1707 |
+
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
| 1708 |
+
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
|
| 1709 |
+
"license": "MIT",
|
| 1710 |
+
"dependencies": {
|
| 1711 |
+
"cross-spawn": "^7.0.1"
|
| 1712 |
+
},
|
| 1713 |
+
"bin": {
|
| 1714 |
+
"cross-env": "src/bin/cross-env.js",
|
| 1715 |
+
"cross-env-shell": "src/bin/cross-env-shell.js"
|
| 1716 |
+
},
|
| 1717 |
+
"engines": {
|
| 1718 |
+
"node": ">=10.14",
|
| 1719 |
+
"npm": ">=6",
|
| 1720 |
+
"yarn": ">=1"
|
| 1721 |
+
}
|
| 1722 |
+
},
|
| 1723 |
+
"node_modules/cross-spawn": {
|
| 1724 |
+
"version": "7.0.6",
|
| 1725 |
+
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
| 1726 |
+
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
| 1727 |
+
"license": "MIT",
|
| 1728 |
+
"dependencies": {
|
| 1729 |
+
"path-key": "^3.1.0",
|
| 1730 |
+
"shebang-command": "^2.0.0",
|
| 1731 |
+
"which": "^2.0.1"
|
| 1732 |
+
},
|
| 1733 |
+
"engines": {
|
| 1734 |
+
"node": ">= 8"
|
| 1735 |
+
}
|
| 1736 |
+
},
|
| 1737 |
+
"node_modules/csstype": {
|
| 1738 |
+
"version": "3.2.3",
|
| 1739 |
+
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
| 1740 |
+
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
| 1741 |
+
"license": "MIT"
|
| 1742 |
+
},
|
| 1743 |
+
"node_modules/debug": {
|
| 1744 |
+
"version": "4.4.3",
|
| 1745 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
| 1746 |
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
| 1747 |
+
"dev": true,
|
| 1748 |
+
"license": "MIT",
|
| 1749 |
+
"dependencies": {
|
| 1750 |
+
"ms": "^2.1.3"
|
| 1751 |
+
},
|
| 1752 |
+
"engines": {
|
| 1753 |
+
"node": ">=6.0"
|
| 1754 |
+
},
|
| 1755 |
+
"peerDependenciesMeta": {
|
| 1756 |
+
"supports-color": {
|
| 1757 |
+
"optional": true
|
| 1758 |
+
}
|
| 1759 |
+
}
|
| 1760 |
+
},
|
| 1761 |
+
"node_modules/detect-gpu": {
|
| 1762 |
+
"version": "5.0.70",
|
| 1763 |
+
"resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz",
|
| 1764 |
+
"integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==",
|
| 1765 |
+
"license": "MIT",
|
| 1766 |
+
"dependencies": {
|
| 1767 |
+
"webgl-constants": "^1.1.1"
|
| 1768 |
+
}
|
| 1769 |
+
},
|
| 1770 |
+
"node_modules/draco3d": {
|
| 1771 |
+
"version": "1.5.7",
|
| 1772 |
+
"resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz",
|
| 1773 |
+
"integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==",
|
| 1774 |
+
"license": "Apache-2.0"
|
| 1775 |
+
},
|
| 1776 |
+
"node_modules/electron-to-chromium": {
|
| 1777 |
+
"version": "1.5.389",
|
| 1778 |
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz",
|
| 1779 |
+
"integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==",
|
| 1780 |
+
"dev": true,
|
| 1781 |
+
"license": "ISC"
|
| 1782 |
+
},
|
| 1783 |
+
"node_modules/esbuild": {
|
| 1784 |
+
"version": "0.21.5",
|
| 1785 |
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
| 1786 |
+
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
| 1787 |
+
"dev": true,
|
| 1788 |
+
"hasInstallScript": true,
|
| 1789 |
+
"license": "MIT",
|
| 1790 |
+
"bin": {
|
| 1791 |
+
"esbuild": "bin/esbuild"
|
| 1792 |
+
},
|
| 1793 |
+
"engines": {
|
| 1794 |
+
"node": ">=12"
|
| 1795 |
+
},
|
| 1796 |
+
"optionalDependencies": {
|
| 1797 |
+
"@esbuild/aix-ppc64": "0.21.5",
|
| 1798 |
+
"@esbuild/android-arm": "0.21.5",
|
| 1799 |
+
"@esbuild/android-arm64": "0.21.5",
|
| 1800 |
+
"@esbuild/android-x64": "0.21.5",
|
| 1801 |
+
"@esbuild/darwin-arm64": "0.21.5",
|
| 1802 |
+
"@esbuild/darwin-x64": "0.21.5",
|
| 1803 |
+
"@esbuild/freebsd-arm64": "0.21.5",
|
| 1804 |
+
"@esbuild/freebsd-x64": "0.21.5",
|
| 1805 |
+
"@esbuild/linux-arm": "0.21.5",
|
| 1806 |
+
"@esbuild/linux-arm64": "0.21.5",
|
| 1807 |
+
"@esbuild/linux-ia32": "0.21.5",
|
| 1808 |
+
"@esbuild/linux-loong64": "0.21.5",
|
| 1809 |
+
"@esbuild/linux-mips64el": "0.21.5",
|
| 1810 |
+
"@esbuild/linux-ppc64": "0.21.5",
|
| 1811 |
+
"@esbuild/linux-riscv64": "0.21.5",
|
| 1812 |
+
"@esbuild/linux-s390x": "0.21.5",
|
| 1813 |
+
"@esbuild/linux-x64": "0.21.5",
|
| 1814 |
+
"@esbuild/netbsd-x64": "0.21.5",
|
| 1815 |
+
"@esbuild/openbsd-x64": "0.21.5",
|
| 1816 |
+
"@esbuild/sunos-x64": "0.21.5",
|
| 1817 |
+
"@esbuild/win32-arm64": "0.21.5",
|
| 1818 |
+
"@esbuild/win32-ia32": "0.21.5",
|
| 1819 |
+
"@esbuild/win32-x64": "0.21.5"
|
| 1820 |
+
}
|
| 1821 |
+
},
|
| 1822 |
+
"node_modules/escalade": {
|
| 1823 |
+
"version": "3.2.0",
|
| 1824 |
+
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
| 1825 |
+
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
| 1826 |
+
"dev": true,
|
| 1827 |
+
"license": "MIT",
|
| 1828 |
+
"engines": {
|
| 1829 |
+
"node": ">=6"
|
| 1830 |
+
}
|
| 1831 |
+
},
|
| 1832 |
+
"node_modules/fflate": {
|
| 1833 |
+
"version": "0.8.3",
|
| 1834 |
+
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
|
| 1835 |
+
"integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
|
| 1836 |
+
"license": "MIT"
|
| 1837 |
+
},
|
| 1838 |
+
"node_modules/framer-motion": {
|
| 1839 |
+
"version": "11.18.2",
|
| 1840 |
+
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
|
| 1841 |
+
"integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==",
|
| 1842 |
+
"license": "MIT",
|
| 1843 |
+
"dependencies": {
|
| 1844 |
+
"motion-dom": "^11.18.1",
|
| 1845 |
+
"motion-utils": "^11.18.1",
|
| 1846 |
+
"tslib": "^2.4.0"
|
| 1847 |
+
},
|
| 1848 |
+
"peerDependencies": {
|
| 1849 |
+
"@emotion/is-prop-valid": "*",
|
| 1850 |
+
"react": "^18.0.0 || ^19.0.0",
|
| 1851 |
+
"react-dom": "^18.0.0 || ^19.0.0"
|
| 1852 |
+
},
|
| 1853 |
+
"peerDependenciesMeta": {
|
| 1854 |
+
"@emotion/is-prop-valid": {
|
| 1855 |
+
"optional": true
|
| 1856 |
+
},
|
| 1857 |
+
"react": {
|
| 1858 |
+
"optional": true
|
| 1859 |
+
},
|
| 1860 |
+
"react-dom": {
|
| 1861 |
+
"optional": true
|
| 1862 |
+
}
|
| 1863 |
+
}
|
| 1864 |
+
},
|
| 1865 |
+
"node_modules/fsevents": {
|
| 1866 |
+
"version": "2.3.3",
|
| 1867 |
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 1868 |
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 1869 |
+
"dev": true,
|
| 1870 |
+
"hasInstallScript": true,
|
| 1871 |
+
"license": "MIT",
|
| 1872 |
+
"optional": true,
|
| 1873 |
+
"os": [
|
| 1874 |
+
"darwin"
|
| 1875 |
+
],
|
| 1876 |
+
"engines": {
|
| 1877 |
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 1878 |
+
}
|
| 1879 |
+
},
|
| 1880 |
+
"node_modules/gensync": {
|
| 1881 |
+
"version": "1.0.0-beta.2",
|
| 1882 |
+
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
| 1883 |
+
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
| 1884 |
+
"dev": true,
|
| 1885 |
+
"license": "MIT",
|
| 1886 |
+
"engines": {
|
| 1887 |
+
"node": ">=6.9.0"
|
| 1888 |
+
}
|
| 1889 |
+
},
|
| 1890 |
+
"node_modules/glsl-noise": {
|
| 1891 |
+
"version": "0.0.0",
|
| 1892 |
+
"resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz",
|
| 1893 |
+
"integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==",
|
| 1894 |
+
"license": "MIT"
|
| 1895 |
+
},
|
| 1896 |
+
"node_modules/hls.js": {
|
| 1897 |
+
"version": "1.6.16",
|
| 1898 |
+
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.16.tgz",
|
| 1899 |
+
"integrity": "sha512-VSIRpLfRwlAAdGL4wiTucx2ScRipo0ed1FBatWkyt832jC4CReKstga6yIhYVwGu9LOBjuX9wzmRMeQdBJtzEA==",
|
| 1900 |
+
"license": "Apache-2.0"
|
| 1901 |
+
},
|
| 1902 |
+
"node_modules/ieee754": {
|
| 1903 |
+
"version": "1.2.1",
|
| 1904 |
+
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
| 1905 |
+
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
| 1906 |
+
"funding": [
|
| 1907 |
+
{
|
| 1908 |
+
"type": "github",
|
| 1909 |
+
"url": "https://github.com/sponsors/feross"
|
| 1910 |
+
},
|
| 1911 |
+
{
|
| 1912 |
+
"type": "patreon",
|
| 1913 |
+
"url": "https://www.patreon.com/feross"
|
| 1914 |
+
},
|
| 1915 |
+
{
|
| 1916 |
+
"type": "consulting",
|
| 1917 |
+
"url": "https://feross.org/support"
|
| 1918 |
+
}
|
| 1919 |
+
],
|
| 1920 |
+
"license": "BSD-3-Clause"
|
| 1921 |
+
},
|
| 1922 |
+
"node_modules/immediate": {
|
| 1923 |
+
"version": "3.0.6",
|
| 1924 |
+
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
| 1925 |
+
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
| 1926 |
+
"license": "MIT"
|
| 1927 |
+
},
|
| 1928 |
+
"node_modules/is-promise": {
|
| 1929 |
+
"version": "2.2.2",
|
| 1930 |
+
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
|
| 1931 |
+
"integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
|
| 1932 |
+
"license": "MIT"
|
| 1933 |
+
},
|
| 1934 |
+
"node_modules/isexe": {
|
| 1935 |
+
"version": "2.0.0",
|
| 1936 |
+
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
| 1937 |
+
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
| 1938 |
+
"license": "ISC"
|
| 1939 |
+
},
|
| 1940 |
+
"node_modules/its-fine": {
|
| 1941 |
+
"version": "1.2.5",
|
| 1942 |
+
"resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.2.5.tgz",
|
| 1943 |
+
"integrity": "sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==",
|
| 1944 |
+
"license": "MIT",
|
| 1945 |
+
"dependencies": {
|
| 1946 |
+
"@types/react-reconciler": "^0.28.0"
|
| 1947 |
+
},
|
| 1948 |
+
"peerDependencies": {
|
| 1949 |
+
"react": ">=18.0"
|
| 1950 |
+
}
|
| 1951 |
+
},
|
| 1952 |
+
"node_modules/its-fine/node_modules/@types/react-reconciler": {
|
| 1953 |
+
"version": "0.28.9",
|
| 1954 |
+
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz",
|
| 1955 |
+
"integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==",
|
| 1956 |
+
"license": "MIT",
|
| 1957 |
+
"peerDependencies": {
|
| 1958 |
+
"@types/react": "*"
|
| 1959 |
+
}
|
| 1960 |
+
},
|
| 1961 |
+
"node_modules/js-tokens": {
|
| 1962 |
+
"version": "4.0.0",
|
| 1963 |
+
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
| 1964 |
+
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
| 1965 |
+
"license": "MIT"
|
| 1966 |
+
},
|
| 1967 |
+
"node_modules/jsesc": {
|
| 1968 |
+
"version": "3.1.0",
|
| 1969 |
+
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
| 1970 |
+
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
| 1971 |
+
"dev": true,
|
| 1972 |
+
"license": "MIT",
|
| 1973 |
+
"bin": {
|
| 1974 |
+
"jsesc": "bin/jsesc"
|
| 1975 |
+
},
|
| 1976 |
+
"engines": {
|
| 1977 |
+
"node": ">=6"
|
| 1978 |
+
}
|
| 1979 |
+
},
|
| 1980 |
+
"node_modules/json5": {
|
| 1981 |
+
"version": "2.2.3",
|
| 1982 |
+
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
| 1983 |
+
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
| 1984 |
+
"dev": true,
|
| 1985 |
+
"license": "MIT",
|
| 1986 |
+
"bin": {
|
| 1987 |
+
"json5": "lib/cli.js"
|
| 1988 |
+
},
|
| 1989 |
+
"engines": {
|
| 1990 |
+
"node": ">=6"
|
| 1991 |
+
}
|
| 1992 |
+
},
|
| 1993 |
+
"node_modules/lie": {
|
| 1994 |
+
"version": "3.3.0",
|
| 1995 |
+
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
| 1996 |
+
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
| 1997 |
+
"license": "MIT",
|
| 1998 |
+
"dependencies": {
|
| 1999 |
+
"immediate": "~3.0.5"
|
| 2000 |
+
}
|
| 2001 |
+
},
|
| 2002 |
+
"node_modules/loose-envify": {
|
| 2003 |
+
"version": "1.4.0",
|
| 2004 |
+
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
| 2005 |
+
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
| 2006 |
+
"license": "MIT",
|
| 2007 |
+
"dependencies": {
|
| 2008 |
+
"js-tokens": "^3.0.0 || ^4.0.0"
|
| 2009 |
+
},
|
| 2010 |
+
"bin": {
|
| 2011 |
+
"loose-envify": "cli.js"
|
| 2012 |
+
}
|
| 2013 |
+
},
|
| 2014 |
+
"node_modules/lru-cache": {
|
| 2015 |
+
"version": "5.1.1",
|
| 2016 |
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
| 2017 |
+
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
| 2018 |
+
"dev": true,
|
| 2019 |
+
"license": "ISC",
|
| 2020 |
+
"dependencies": {
|
| 2021 |
+
"yallist": "^3.0.2"
|
| 2022 |
+
}
|
| 2023 |
+
},
|
| 2024 |
+
"node_modules/maath": {
|
| 2025 |
+
"version": "0.10.8",
|
| 2026 |
+
"resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz",
|
| 2027 |
+
"integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==",
|
| 2028 |
+
"license": "MIT",
|
| 2029 |
+
"peerDependencies": {
|
| 2030 |
+
"@types/three": ">=0.134.0",
|
| 2031 |
+
"three": ">=0.134.0"
|
| 2032 |
+
}
|
| 2033 |
+
},
|
| 2034 |
+
"node_modules/meshline": {
|
| 2035 |
+
"version": "3.3.1",
|
| 2036 |
+
"resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz",
|
| 2037 |
+
"integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==",
|
| 2038 |
+
"license": "MIT",
|
| 2039 |
+
"peerDependencies": {
|
| 2040 |
+
"three": ">=0.137"
|
| 2041 |
+
}
|
| 2042 |
+
},
|
| 2043 |
+
"node_modules/meshoptimizer": {
|
| 2044 |
+
"version": "0.18.1",
|
| 2045 |
+
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz",
|
| 2046 |
+
"integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==",
|
| 2047 |
+
"license": "MIT"
|
| 2048 |
+
},
|
| 2049 |
+
"node_modules/motion-dom": {
|
| 2050 |
+
"version": "11.18.1",
|
| 2051 |
+
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
|
| 2052 |
+
"integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==",
|
| 2053 |
+
"license": "MIT",
|
| 2054 |
+
"dependencies": {
|
| 2055 |
+
"motion-utils": "^11.18.1"
|
| 2056 |
+
}
|
| 2057 |
+
},
|
| 2058 |
+
"node_modules/motion-utils": {
|
| 2059 |
+
"version": "11.18.1",
|
| 2060 |
+
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz",
|
| 2061 |
+
"integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
|
| 2062 |
+
"license": "MIT"
|
| 2063 |
+
},
|
| 2064 |
+
"node_modules/ms": {
|
| 2065 |
+
"version": "2.1.3",
|
| 2066 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 2067 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 2068 |
+
"dev": true,
|
| 2069 |
+
"license": "MIT"
|
| 2070 |
+
},
|
| 2071 |
+
"node_modules/n8ao": {
|
| 2072 |
+
"version": "1.10.3",
|
| 2073 |
+
"resolved": "https://registry.npmjs.org/n8ao/-/n8ao-1.10.3.tgz",
|
| 2074 |
+
"integrity": "sha512-JFRk4WgUAUWO2KdJTqmBT02dP9kG7rE2oQbBy64E9+YwQ3d96KB9QBanqNy/NnYUfUTYU6Z/5OS5t/yLey7+6Q==",
|
| 2075 |
+
"license": "ISC",
|
| 2076 |
+
"peerDependencies": {
|
| 2077 |
+
"postprocessing": ">=6.30.0",
|
| 2078 |
+
"three": ">=0.137"
|
| 2079 |
+
}
|
| 2080 |
+
},
|
| 2081 |
+
"node_modules/nanoid": {
|
| 2082 |
+
"version": "3.3.15",
|
| 2083 |
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
|
| 2084 |
+
"integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
|
| 2085 |
+
"dev": true,
|
| 2086 |
+
"funding": [
|
| 2087 |
+
{
|
| 2088 |
+
"type": "github",
|
| 2089 |
+
"url": "https://github.com/sponsors/ai"
|
| 2090 |
+
}
|
| 2091 |
+
],
|
| 2092 |
+
"license": "MIT",
|
| 2093 |
+
"bin": {
|
| 2094 |
+
"nanoid": "bin/nanoid.cjs"
|
| 2095 |
+
},
|
| 2096 |
+
"engines": {
|
| 2097 |
+
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
| 2098 |
+
}
|
| 2099 |
+
},
|
| 2100 |
+
"node_modules/node-releases": {
|
| 2101 |
+
"version": "2.0.51",
|
| 2102 |
+
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
|
| 2103 |
+
"integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
|
| 2104 |
+
"dev": true,
|
| 2105 |
+
"license": "MIT",
|
| 2106 |
+
"engines": {
|
| 2107 |
+
"node": ">=18"
|
| 2108 |
+
}
|
| 2109 |
+
},
|
| 2110 |
+
"node_modules/object-assign": {
|
| 2111 |
+
"version": "4.1.1",
|
| 2112 |
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
| 2113 |
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
| 2114 |
+
"license": "MIT",
|
| 2115 |
+
"engines": {
|
| 2116 |
+
"node": ">=0.10.0"
|
| 2117 |
+
}
|
| 2118 |
+
},
|
| 2119 |
+
"node_modules/path-key": {
|
| 2120 |
+
"version": "3.1.1",
|
| 2121 |
+
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
| 2122 |
+
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
| 2123 |
+
"license": "MIT",
|
| 2124 |
+
"engines": {
|
| 2125 |
+
"node": ">=8"
|
| 2126 |
+
}
|
| 2127 |
+
},
|
| 2128 |
+
"node_modules/picocolors": {
|
| 2129 |
+
"version": "1.1.1",
|
| 2130 |
+
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
| 2131 |
+
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
| 2132 |
+
"dev": true,
|
| 2133 |
+
"license": "ISC"
|
| 2134 |
+
},
|
| 2135 |
+
"node_modules/postcss": {
|
| 2136 |
+
"version": "8.5.16",
|
| 2137 |
+
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
|
| 2138 |
+
"integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
|
| 2139 |
+
"dev": true,
|
| 2140 |
+
"funding": [
|
| 2141 |
+
{
|
| 2142 |
+
"type": "opencollective",
|
| 2143 |
+
"url": "https://opencollective.com/postcss/"
|
| 2144 |
+
},
|
| 2145 |
+
{
|
| 2146 |
+
"type": "tidelift",
|
| 2147 |
+
"url": "https://tidelift.com/funding/github/npm/postcss"
|
| 2148 |
+
},
|
| 2149 |
+
{
|
| 2150 |
+
"type": "github",
|
| 2151 |
+
"url": "https://github.com/sponsors/ai"
|
| 2152 |
+
}
|
| 2153 |
+
],
|
| 2154 |
+
"license": "MIT",
|
| 2155 |
+
"dependencies": {
|
| 2156 |
+
"nanoid": "^3.3.12",
|
| 2157 |
+
"picocolors": "^1.1.1",
|
| 2158 |
+
"source-map-js": "^1.2.1"
|
| 2159 |
+
},
|
| 2160 |
+
"engines": {
|
| 2161 |
+
"node": "^10 || ^12 || >=14"
|
| 2162 |
+
}
|
| 2163 |
+
},
|
| 2164 |
+
"node_modules/postprocessing": {
|
| 2165 |
+
"version": "6.39.2",
|
| 2166 |
+
"resolved": "https://registry.npmjs.org/postprocessing/-/postprocessing-6.39.2.tgz",
|
| 2167 |
+
"integrity": "sha512-AxPY+xFyVOsz+8bbHSyQBoOK4AUqSQspTXXIsNcKhmGJRm/afHRAwZ8bKC02AeSPII0PW6lbCN84SgAoWFsKKA==",
|
| 2168 |
+
"license": "Zlib",
|
| 2169 |
+
"peerDependencies": {
|
| 2170 |
+
"three": ">= 0.168.0 < 0.186.0"
|
| 2171 |
+
}
|
| 2172 |
+
},
|
| 2173 |
+
"node_modules/potpack": {
|
| 2174 |
+
"version": "1.0.2",
|
| 2175 |
+
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
|
| 2176 |
+
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==",
|
| 2177 |
+
"license": "ISC"
|
| 2178 |
+
},
|
| 2179 |
+
"node_modules/prismjs": {
|
| 2180 |
+
"version": "1.30.0",
|
| 2181 |
+
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
| 2182 |
+
"integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
|
| 2183 |
+
"license": "MIT",
|
| 2184 |
+
"engines": {
|
| 2185 |
+
"node": ">=6"
|
| 2186 |
+
}
|
| 2187 |
+
},
|
| 2188 |
+
"node_modules/promise-worker-transferable": {
|
| 2189 |
+
"version": "1.0.4",
|
| 2190 |
+
"resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz",
|
| 2191 |
+
"integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==",
|
| 2192 |
+
"license": "Apache-2.0",
|
| 2193 |
+
"dependencies": {
|
| 2194 |
+
"is-promise": "^2.1.0",
|
| 2195 |
+
"lie": "^3.0.2"
|
| 2196 |
+
}
|
| 2197 |
+
},
|
| 2198 |
+
"node_modules/prop-types": {
|
| 2199 |
+
"version": "15.8.1",
|
| 2200 |
+
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
| 2201 |
+
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
| 2202 |
+
"license": "MIT",
|
| 2203 |
+
"dependencies": {
|
| 2204 |
+
"loose-envify": "^1.4.0",
|
| 2205 |
+
"object-assign": "^4.1.1",
|
| 2206 |
+
"react-is": "^16.13.1"
|
| 2207 |
+
}
|
| 2208 |
+
},
|
| 2209 |
+
"node_modules/react": {
|
| 2210 |
+
"version": "18.3.1",
|
| 2211 |
+
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
| 2212 |
+
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
| 2213 |
+
"license": "MIT",
|
| 2214 |
+
"dependencies": {
|
| 2215 |
+
"loose-envify": "^1.1.0"
|
| 2216 |
+
},
|
| 2217 |
+
"engines": {
|
| 2218 |
+
"node": ">=0.10.0"
|
| 2219 |
+
}
|
| 2220 |
+
},
|
| 2221 |
+
"node_modules/react-composer": {
|
| 2222 |
+
"version": "5.0.3",
|
| 2223 |
+
"resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz",
|
| 2224 |
+
"integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==",
|
| 2225 |
+
"license": "MIT",
|
| 2226 |
+
"dependencies": {
|
| 2227 |
+
"prop-types": "^15.6.0"
|
| 2228 |
+
},
|
| 2229 |
+
"peerDependencies": {
|
| 2230 |
+
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
| 2231 |
+
}
|
| 2232 |
+
},
|
| 2233 |
+
"node_modules/react-dom": {
|
| 2234 |
+
"version": "18.3.1",
|
| 2235 |
+
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
| 2236 |
+
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
| 2237 |
+
"license": "MIT",
|
| 2238 |
+
"dependencies": {
|
| 2239 |
+
"loose-envify": "^1.1.0",
|
| 2240 |
+
"scheduler": "^0.23.2"
|
| 2241 |
+
},
|
| 2242 |
+
"peerDependencies": {
|
| 2243 |
+
"react": "^18.3.1"
|
| 2244 |
+
}
|
| 2245 |
+
},
|
| 2246 |
+
"node_modules/react-dom/node_modules/scheduler": {
|
| 2247 |
+
"version": "0.23.2",
|
| 2248 |
+
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
| 2249 |
+
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
| 2250 |
+
"license": "MIT",
|
| 2251 |
+
"dependencies": {
|
| 2252 |
+
"loose-envify": "^1.1.0"
|
| 2253 |
+
}
|
| 2254 |
+
},
|
| 2255 |
+
"node_modules/react-is": {
|
| 2256 |
+
"version": "16.13.1",
|
| 2257 |
+
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
| 2258 |
+
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
| 2259 |
+
"license": "MIT"
|
| 2260 |
+
},
|
| 2261 |
+
"node_modules/react-reconciler": {
|
| 2262 |
+
"version": "0.27.0",
|
| 2263 |
+
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
|
| 2264 |
+
"integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
|
| 2265 |
+
"license": "MIT",
|
| 2266 |
+
"dependencies": {
|
| 2267 |
+
"loose-envify": "^1.1.0",
|
| 2268 |
+
"scheduler": "^0.21.0"
|
| 2269 |
+
},
|
| 2270 |
+
"engines": {
|
| 2271 |
+
"node": ">=0.10.0"
|
| 2272 |
+
},
|
| 2273 |
+
"peerDependencies": {
|
| 2274 |
+
"react": "^18.0.0"
|
| 2275 |
+
}
|
| 2276 |
+
},
|
| 2277 |
+
"node_modules/react-refresh": {
|
| 2278 |
+
"version": "0.17.0",
|
| 2279 |
+
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
| 2280 |
+
"integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
|
| 2281 |
+
"dev": true,
|
| 2282 |
+
"license": "MIT",
|
| 2283 |
+
"engines": {
|
| 2284 |
+
"node": ">=0.10.0"
|
| 2285 |
+
}
|
| 2286 |
+
},
|
| 2287 |
+
"node_modules/react-simple-code-editor": {
|
| 2288 |
+
"version": "0.14.1",
|
| 2289 |
+
"resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.14.1.tgz",
|
| 2290 |
+
"integrity": "sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==",
|
| 2291 |
+
"license": "MIT",
|
| 2292 |
+
"peerDependencies": {
|
| 2293 |
+
"react": ">=16.8.0",
|
| 2294 |
+
"react-dom": ">=16.8.0"
|
| 2295 |
+
}
|
| 2296 |
+
},
|
| 2297 |
+
"node_modules/react-use-measure": {
|
| 2298 |
+
"version": "2.1.7",
|
| 2299 |
+
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
|
| 2300 |
+
"integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==",
|
| 2301 |
+
"license": "MIT",
|
| 2302 |
+
"peerDependencies": {
|
| 2303 |
+
"react": ">=16.13",
|
| 2304 |
+
"react-dom": ">=16.13"
|
| 2305 |
+
},
|
| 2306 |
+
"peerDependenciesMeta": {
|
| 2307 |
+
"react-dom": {
|
| 2308 |
+
"optional": true
|
| 2309 |
+
}
|
| 2310 |
+
}
|
| 2311 |
+
},
|
| 2312 |
+
"node_modules/require-from-string": {
|
| 2313 |
+
"version": "2.0.2",
|
| 2314 |
+
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
| 2315 |
+
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
| 2316 |
+
"license": "MIT",
|
| 2317 |
+
"engines": {
|
| 2318 |
+
"node": ">=0.10.0"
|
| 2319 |
+
}
|
| 2320 |
+
},
|
| 2321 |
+
"node_modules/rollup": {
|
| 2322 |
+
"version": "4.62.2",
|
| 2323 |
+
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
|
| 2324 |
+
"integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",
|
| 2325 |
+
"dev": true,
|
| 2326 |
+
"license": "MIT",
|
| 2327 |
+
"dependencies": {
|
| 2328 |
+
"@types/estree": "1.0.9"
|
| 2329 |
+
},
|
| 2330 |
+
"bin": {
|
| 2331 |
+
"rollup": "dist/bin/rollup"
|
| 2332 |
+
},
|
| 2333 |
+
"engines": {
|
| 2334 |
+
"node": ">=18.0.0",
|
| 2335 |
+
"npm": ">=8.0.0"
|
| 2336 |
+
},
|
| 2337 |
+
"optionalDependencies": {
|
| 2338 |
+
"@rollup/rollup-android-arm-eabi": "4.62.2",
|
| 2339 |
+
"@rollup/rollup-android-arm64": "4.62.2",
|
| 2340 |
+
"@rollup/rollup-darwin-arm64": "4.62.2",
|
| 2341 |
+
"@rollup/rollup-darwin-x64": "4.62.2",
|
| 2342 |
+
"@rollup/rollup-freebsd-arm64": "4.62.2",
|
| 2343 |
+
"@rollup/rollup-freebsd-x64": "4.62.2",
|
| 2344 |
+
"@rollup/rollup-linux-arm-gnueabihf": "4.62.2",
|
| 2345 |
+
"@rollup/rollup-linux-arm-musleabihf": "4.62.2",
|
| 2346 |
+
"@rollup/rollup-linux-arm64-gnu": "4.62.2",
|
| 2347 |
+
"@rollup/rollup-linux-arm64-musl": "4.62.2",
|
| 2348 |
+
"@rollup/rollup-linux-loong64-gnu": "4.62.2",
|
| 2349 |
+
"@rollup/rollup-linux-loong64-musl": "4.62.2",
|
| 2350 |
+
"@rollup/rollup-linux-ppc64-gnu": "4.62.2",
|
| 2351 |
+
"@rollup/rollup-linux-ppc64-musl": "4.62.2",
|
| 2352 |
+
"@rollup/rollup-linux-riscv64-gnu": "4.62.2",
|
| 2353 |
+
"@rollup/rollup-linux-riscv64-musl": "4.62.2",
|
| 2354 |
+
"@rollup/rollup-linux-s390x-gnu": "4.62.2",
|
| 2355 |
+
"@rollup/rollup-linux-x64-gnu": "4.62.2",
|
| 2356 |
+
"@rollup/rollup-linux-x64-musl": "4.62.2",
|
| 2357 |
+
"@rollup/rollup-openbsd-x64": "4.62.2",
|
| 2358 |
+
"@rollup/rollup-openharmony-arm64": "4.62.2",
|
| 2359 |
+
"@rollup/rollup-win32-arm64-msvc": "4.62.2",
|
| 2360 |
+
"@rollup/rollup-win32-ia32-msvc": "4.62.2",
|
| 2361 |
+
"@rollup/rollup-win32-x64-gnu": "4.62.2",
|
| 2362 |
+
"@rollup/rollup-win32-x64-msvc": "4.62.2",
|
| 2363 |
+
"fsevents": "~2.3.2"
|
| 2364 |
+
}
|
| 2365 |
+
},
|
| 2366 |
+
"node_modules/scheduler": {
|
| 2367 |
+
"version": "0.21.0",
|
| 2368 |
+
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
|
| 2369 |
+
"integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
|
| 2370 |
+
"license": "MIT",
|
| 2371 |
+
"dependencies": {
|
| 2372 |
+
"loose-envify": "^1.1.0"
|
| 2373 |
+
}
|
| 2374 |
+
},
|
| 2375 |
+
"node_modules/semver": {
|
| 2376 |
+
"version": "6.3.1",
|
| 2377 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
| 2378 |
+
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
| 2379 |
+
"dev": true,
|
| 2380 |
+
"license": "ISC",
|
| 2381 |
+
"bin": {
|
| 2382 |
+
"semver": "bin/semver.js"
|
| 2383 |
+
}
|
| 2384 |
+
},
|
| 2385 |
+
"node_modules/shebang-command": {
|
| 2386 |
+
"version": "2.0.0",
|
| 2387 |
+
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
| 2388 |
+
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
| 2389 |
+
"license": "MIT",
|
| 2390 |
+
"dependencies": {
|
| 2391 |
+
"shebang-regex": "^3.0.0"
|
| 2392 |
+
},
|
| 2393 |
+
"engines": {
|
| 2394 |
+
"node": ">=8"
|
| 2395 |
+
}
|
| 2396 |
+
},
|
| 2397 |
+
"node_modules/shebang-regex": {
|
| 2398 |
+
"version": "3.0.0",
|
| 2399 |
+
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
| 2400 |
+
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
| 2401 |
+
"license": "MIT",
|
| 2402 |
+
"engines": {
|
| 2403 |
+
"node": ">=8"
|
| 2404 |
+
}
|
| 2405 |
+
},
|
| 2406 |
+
"node_modules/source-map-js": {
|
| 2407 |
+
"version": "1.2.1",
|
| 2408 |
+
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
| 2409 |
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
| 2410 |
+
"dev": true,
|
| 2411 |
+
"license": "BSD-3-Clause",
|
| 2412 |
+
"engines": {
|
| 2413 |
+
"node": ">=0.10.0"
|
| 2414 |
+
}
|
| 2415 |
+
},
|
| 2416 |
+
"node_modules/stats-gl": {
|
| 2417 |
+
"version": "2.4.2",
|
| 2418 |
+
"resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz",
|
| 2419 |
+
"integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==",
|
| 2420 |
+
"license": "MIT",
|
| 2421 |
+
"dependencies": {
|
| 2422 |
+
"@types/three": "*",
|
| 2423 |
+
"three": "^0.170.0"
|
| 2424 |
+
},
|
| 2425 |
+
"peerDependencies": {
|
| 2426 |
+
"@types/three": "*",
|
| 2427 |
+
"three": "*"
|
| 2428 |
+
}
|
| 2429 |
+
},
|
| 2430 |
+
"node_modules/stats-gl/node_modules/three": {
|
| 2431 |
+
"version": "0.170.0",
|
| 2432 |
+
"resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz",
|
| 2433 |
+
"integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==",
|
| 2434 |
+
"license": "MIT"
|
| 2435 |
+
},
|
| 2436 |
+
"node_modules/stats.js": {
|
| 2437 |
+
"version": "0.17.0",
|
| 2438 |
+
"resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz",
|
| 2439 |
+
"integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==",
|
| 2440 |
+
"license": "MIT"
|
| 2441 |
+
},
|
| 2442 |
+
"node_modules/suspend-react": {
|
| 2443 |
+
"version": "0.1.3",
|
| 2444 |
+
"resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz",
|
| 2445 |
+
"integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==",
|
| 2446 |
+
"license": "MIT",
|
| 2447 |
+
"peerDependencies": {
|
| 2448 |
+
"react": ">=17.0"
|
| 2449 |
+
}
|
| 2450 |
+
},
|
| 2451 |
+
"node_modules/three": {
|
| 2452 |
+
"version": "0.169.0",
|
| 2453 |
+
"resolved": "https://registry.npmjs.org/three/-/three-0.169.0.tgz",
|
| 2454 |
+
"integrity": "sha512-Ed906MA3dR4TS5riErd4QBsRGPcx+HBDX2O5yYE5GqJeFQTPU+M56Va/f/Oph9X7uZo3W3o4l2ZhBZ6f6qUv0w==",
|
| 2455 |
+
"license": "MIT"
|
| 2456 |
+
},
|
| 2457 |
+
"node_modules/three-mesh-bvh": {
|
| 2458 |
+
"version": "0.7.8",
|
| 2459 |
+
"resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.7.8.tgz",
|
| 2460 |
+
"integrity": "sha512-BGEZTOIC14U0XIRw3tO4jY7IjP7n7v24nv9JXS1CyeVRWOCkcOMhRnmENUjuV39gktAw4Ofhr0OvIAiTspQrrw==",
|
| 2461 |
+
"deprecated": "Deprecated due to three.js version incompatibility. Please use v0.8.0, instead.",
|
| 2462 |
+
"license": "MIT",
|
| 2463 |
+
"peerDependencies": {
|
| 2464 |
+
"three": ">= 0.151.0"
|
| 2465 |
+
}
|
| 2466 |
+
},
|
| 2467 |
+
"node_modules/three-stdlib": {
|
| 2468 |
+
"version": "2.36.1",
|
| 2469 |
+
"resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz",
|
| 2470 |
+
"integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==",
|
| 2471 |
+
"license": "MIT",
|
| 2472 |
+
"dependencies": {
|
| 2473 |
+
"@types/draco3d": "^1.4.0",
|
| 2474 |
+
"@types/offscreencanvas": "^2019.6.4",
|
| 2475 |
+
"@types/webxr": "^0.5.2",
|
| 2476 |
+
"draco3d": "^1.4.1",
|
| 2477 |
+
"fflate": "^0.6.9",
|
| 2478 |
+
"potpack": "^1.0.1"
|
| 2479 |
+
},
|
| 2480 |
+
"peerDependencies": {
|
| 2481 |
+
"three": ">=0.128.0"
|
| 2482 |
+
}
|
| 2483 |
+
},
|
| 2484 |
+
"node_modules/three-stdlib/node_modules/fflate": {
|
| 2485 |
+
"version": "0.6.10",
|
| 2486 |
+
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
|
| 2487 |
+
"integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==",
|
| 2488 |
+
"license": "MIT"
|
| 2489 |
+
},
|
| 2490 |
+
"node_modules/troika-three-text": {
|
| 2491 |
+
"version": "0.52.4",
|
| 2492 |
+
"resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz",
|
| 2493 |
+
"integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==",
|
| 2494 |
+
"license": "MIT",
|
| 2495 |
+
"dependencies": {
|
| 2496 |
+
"bidi-js": "^1.0.2",
|
| 2497 |
+
"troika-three-utils": "^0.52.4",
|
| 2498 |
+
"troika-worker-utils": "^0.52.0",
|
| 2499 |
+
"webgl-sdf-generator": "1.1.1"
|
| 2500 |
+
},
|
| 2501 |
+
"peerDependencies": {
|
| 2502 |
+
"three": ">=0.125.0"
|
| 2503 |
+
}
|
| 2504 |
+
},
|
| 2505 |
+
"node_modules/troika-three-utils": {
|
| 2506 |
+
"version": "0.52.4",
|
| 2507 |
+
"resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz",
|
| 2508 |
+
"integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==",
|
| 2509 |
+
"license": "MIT",
|
| 2510 |
+
"peerDependencies": {
|
| 2511 |
+
"three": ">=0.125.0"
|
| 2512 |
+
}
|
| 2513 |
+
},
|
| 2514 |
+
"node_modules/troika-worker-utils": {
|
| 2515 |
+
"version": "0.52.0",
|
| 2516 |
+
"resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz",
|
| 2517 |
+
"integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==",
|
| 2518 |
+
"license": "MIT"
|
| 2519 |
+
},
|
| 2520 |
+
"node_modules/tslib": {
|
| 2521 |
+
"version": "2.8.1",
|
| 2522 |
+
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
| 2523 |
+
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
| 2524 |
+
"license": "0BSD"
|
| 2525 |
+
},
|
| 2526 |
+
"node_modules/tunnel-rat": {
|
| 2527 |
+
"version": "0.1.2",
|
| 2528 |
+
"resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz",
|
| 2529 |
+
"integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==",
|
| 2530 |
+
"license": "MIT",
|
| 2531 |
+
"dependencies": {
|
| 2532 |
+
"zustand": "^4.3.2"
|
| 2533 |
+
}
|
| 2534 |
+
},
|
| 2535 |
+
"node_modules/tunnel-rat/node_modules/zustand": {
|
| 2536 |
+
"version": "4.5.7",
|
| 2537 |
+
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
|
| 2538 |
+
"integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
|
| 2539 |
+
"license": "MIT",
|
| 2540 |
+
"dependencies": {
|
| 2541 |
+
"use-sync-external-store": "^1.2.2"
|
| 2542 |
+
},
|
| 2543 |
+
"engines": {
|
| 2544 |
+
"node": ">=12.7.0"
|
| 2545 |
+
},
|
| 2546 |
+
"peerDependencies": {
|
| 2547 |
+
"@types/react": ">=16.8",
|
| 2548 |
+
"immer": ">=9.0.6",
|
| 2549 |
+
"react": ">=16.8"
|
| 2550 |
+
},
|
| 2551 |
+
"peerDependenciesMeta": {
|
| 2552 |
+
"@types/react": {
|
| 2553 |
+
"optional": true
|
| 2554 |
+
},
|
| 2555 |
+
"immer": {
|
| 2556 |
+
"optional": true
|
| 2557 |
+
},
|
| 2558 |
+
"react": {
|
| 2559 |
+
"optional": true
|
| 2560 |
+
}
|
| 2561 |
+
}
|
| 2562 |
+
},
|
| 2563 |
+
"node_modules/typescript": {
|
| 2564 |
+
"version": "5.9.3",
|
| 2565 |
+
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
| 2566 |
+
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
| 2567 |
+
"dev": true,
|
| 2568 |
+
"license": "Apache-2.0",
|
| 2569 |
+
"bin": {
|
| 2570 |
+
"tsc": "bin/tsc",
|
| 2571 |
+
"tsserver": "bin/tsserver"
|
| 2572 |
+
},
|
| 2573 |
+
"engines": {
|
| 2574 |
+
"node": ">=14.17"
|
| 2575 |
+
}
|
| 2576 |
+
},
|
| 2577 |
+
"node_modules/update-browserslist-db": {
|
| 2578 |
+
"version": "1.2.3",
|
| 2579 |
+
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
| 2580 |
+
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
|
| 2581 |
+
"dev": true,
|
| 2582 |
+
"funding": [
|
| 2583 |
+
{
|
| 2584 |
+
"type": "opencollective",
|
| 2585 |
+
"url": "https://opencollective.com/browserslist"
|
| 2586 |
+
},
|
| 2587 |
+
{
|
| 2588 |
+
"type": "tidelift",
|
| 2589 |
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
| 2590 |
+
},
|
| 2591 |
+
{
|
| 2592 |
+
"type": "github",
|
| 2593 |
+
"url": "https://github.com/sponsors/ai"
|
| 2594 |
+
}
|
| 2595 |
+
],
|
| 2596 |
+
"license": "MIT",
|
| 2597 |
+
"dependencies": {
|
| 2598 |
+
"escalade": "^3.2.0",
|
| 2599 |
+
"picocolors": "^1.1.1"
|
| 2600 |
+
},
|
| 2601 |
+
"bin": {
|
| 2602 |
+
"update-browserslist-db": "cli.js"
|
| 2603 |
+
},
|
| 2604 |
+
"peerDependencies": {
|
| 2605 |
+
"browserslist": ">= 4.21.0"
|
| 2606 |
+
}
|
| 2607 |
+
},
|
| 2608 |
+
"node_modules/use-sync-external-store": {
|
| 2609 |
+
"version": "1.6.0",
|
| 2610 |
+
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
| 2611 |
+
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
| 2612 |
+
"license": "MIT",
|
| 2613 |
+
"peerDependencies": {
|
| 2614 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
| 2615 |
+
}
|
| 2616 |
+
},
|
| 2617 |
+
"node_modules/utility-types": {
|
| 2618 |
+
"version": "3.11.0",
|
| 2619 |
+
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz",
|
| 2620 |
+
"integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==",
|
| 2621 |
+
"license": "MIT",
|
| 2622 |
+
"engines": {
|
| 2623 |
+
"node": ">= 4"
|
| 2624 |
+
}
|
| 2625 |
+
},
|
| 2626 |
+
"node_modules/vite": {
|
| 2627 |
+
"version": "5.4.21",
|
| 2628 |
+
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
| 2629 |
+
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
| 2630 |
+
"dev": true,
|
| 2631 |
+
"license": "MIT",
|
| 2632 |
+
"dependencies": {
|
| 2633 |
+
"esbuild": "^0.21.3",
|
| 2634 |
+
"postcss": "^8.4.43",
|
| 2635 |
+
"rollup": "^4.20.0"
|
| 2636 |
+
},
|
| 2637 |
+
"bin": {
|
| 2638 |
+
"vite": "bin/vite.js"
|
| 2639 |
+
},
|
| 2640 |
+
"engines": {
|
| 2641 |
+
"node": "^18.0.0 || >=20.0.0"
|
| 2642 |
+
},
|
| 2643 |
+
"funding": {
|
| 2644 |
+
"url": "https://github.com/vitejs/vite?sponsor=1"
|
| 2645 |
+
},
|
| 2646 |
+
"optionalDependencies": {
|
| 2647 |
+
"fsevents": "~2.3.3"
|
| 2648 |
+
},
|
| 2649 |
+
"peerDependencies": {
|
| 2650 |
+
"@types/node": "^18.0.0 || >=20.0.0",
|
| 2651 |
+
"less": "*",
|
| 2652 |
+
"lightningcss": "^1.21.0",
|
| 2653 |
+
"sass": "*",
|
| 2654 |
+
"sass-embedded": "*",
|
| 2655 |
+
"stylus": "*",
|
| 2656 |
+
"sugarss": "*",
|
| 2657 |
+
"terser": "^5.4.0"
|
| 2658 |
+
},
|
| 2659 |
+
"peerDependenciesMeta": {
|
| 2660 |
+
"@types/node": {
|
| 2661 |
+
"optional": true
|
| 2662 |
+
},
|
| 2663 |
+
"less": {
|
| 2664 |
+
"optional": true
|
| 2665 |
+
},
|
| 2666 |
+
"lightningcss": {
|
| 2667 |
+
"optional": true
|
| 2668 |
+
},
|
| 2669 |
+
"sass": {
|
| 2670 |
+
"optional": true
|
| 2671 |
+
},
|
| 2672 |
+
"sass-embedded": {
|
| 2673 |
+
"optional": true
|
| 2674 |
+
},
|
| 2675 |
+
"stylus": {
|
| 2676 |
+
"optional": true
|
| 2677 |
+
},
|
| 2678 |
+
"sugarss": {
|
| 2679 |
+
"optional": true
|
| 2680 |
+
},
|
| 2681 |
+
"terser": {
|
| 2682 |
+
"optional": true
|
| 2683 |
+
}
|
| 2684 |
+
}
|
| 2685 |
+
},
|
| 2686 |
+
"node_modules/webgl-constants": {
|
| 2687 |
+
"version": "1.1.1",
|
| 2688 |
+
"resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz",
|
| 2689 |
+
"integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="
|
| 2690 |
+
},
|
| 2691 |
+
"node_modules/webgl-sdf-generator": {
|
| 2692 |
+
"version": "1.1.1",
|
| 2693 |
+
"resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
|
| 2694 |
+
"integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==",
|
| 2695 |
+
"license": "MIT"
|
| 2696 |
+
},
|
| 2697 |
+
"node_modules/which": {
|
| 2698 |
+
"version": "2.0.2",
|
| 2699 |
+
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
| 2700 |
+
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
| 2701 |
+
"license": "ISC",
|
| 2702 |
+
"dependencies": {
|
| 2703 |
+
"isexe": "^2.0.0"
|
| 2704 |
+
},
|
| 2705 |
+
"bin": {
|
| 2706 |
+
"node-which": "bin/node-which"
|
| 2707 |
+
},
|
| 2708 |
+
"engines": {
|
| 2709 |
+
"node": ">= 8"
|
| 2710 |
+
}
|
| 2711 |
+
},
|
| 2712 |
+
"node_modules/yallist": {
|
| 2713 |
+
"version": "3.1.1",
|
| 2714 |
+
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
| 2715 |
+
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
| 2716 |
+
"dev": true,
|
| 2717 |
+
"license": "ISC"
|
| 2718 |
+
},
|
| 2719 |
+
"node_modules/zustand": {
|
| 2720 |
+
"version": "5.0.14",
|
| 2721 |
+
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz",
|
| 2722 |
+
"integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==",
|
| 2723 |
+
"license": "MIT",
|
| 2724 |
+
"engines": {
|
| 2725 |
+
"node": ">=12.20.0"
|
| 2726 |
+
},
|
| 2727 |
+
"peerDependencies": {
|
| 2728 |
+
"@types/react": ">=18.0.0",
|
| 2729 |
+
"immer": ">=9.0.6",
|
| 2730 |
+
"react": ">=18.0.0",
|
| 2731 |
+
"use-sync-external-store": ">=1.2.0"
|
| 2732 |
+
},
|
| 2733 |
+
"peerDependenciesMeta": {
|
| 2734 |
+
"@types/react": {
|
| 2735 |
+
"optional": true
|
| 2736 |
+
},
|
| 2737 |
+
"immer": {
|
| 2738 |
+
"optional": true
|
| 2739 |
+
},
|
| 2740 |
+
"react": {
|
| 2741 |
+
"optional": true
|
| 2742 |
+
},
|
| 2743 |
+
"use-sync-external-store": {
|
| 2744 |
+
"optional": true
|
| 2745 |
+
}
|
| 2746 |
+
}
|
| 2747 |
+
}
|
| 2748 |
+
}
|
| 2749 |
+
}
|
frontend/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "maintainability-engine-frontend",
|
| 3 |
+
"private": true,
|
| 4 |
+
"version": "0.0.1",
|
| 5 |
+
"type": "module",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"dev": "vite",
|
| 8 |
+
"build": "tsc -b && vite build",
|
| 9 |
+
"preview": "vite preview"
|
| 10 |
+
},
|
| 11 |
+
"dependencies": {
|
| 12 |
+
"@react-three/drei": "^9.114.0",
|
| 13 |
+
"@react-three/fiber": "^8.17.10",
|
| 14 |
+
"@react-three/postprocessing": "^2.19.1",
|
| 15 |
+
"framer-motion": "^11.11.17",
|
| 16 |
+
"prismjs": "^1.29.0",
|
| 17 |
+
"react": "^18.3.1",
|
| 18 |
+
"react-dom": "^18.3.1",
|
| 19 |
+
"react-simple-code-editor": "^0.14.1",
|
| 20 |
+
"three": "^0.169.0"
|
| 21 |
+
},
|
| 22 |
+
"devDependencies": {
|
| 23 |
+
"@types/prismjs": "^1.26.5",
|
| 24 |
+
"@types/react": "^18.3.12",
|
| 25 |
+
"@types/react-dom": "^18.3.1",
|
| 26 |
+
"@types/three": "^0.169.0",
|
| 27 |
+
"@vitejs/plugin-react": "^4.3.3",
|
| 28 |
+
"typescript": "^5.6.3",
|
| 29 |
+
"vite": "^5.4.11"
|
| 30 |
+
}
|
| 31 |
+
}
|
frontend/src/App.tsx
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useState } from "react";
|
| 2 |
+
import Scene3D from "./components/Scene3D";
|
| 3 |
+
import CodeEditor from "./components/CodeEditor";
|
| 4 |
+
import AnalyticsHUD from "./components/AnalyticsHUD";
|
| 5 |
+
import FlipStage from "./components/FlipStage";
|
| 6 |
+
import MagneticButton from "./components/MagneticButton";
|
| 7 |
+
import GlitchTitle from "./components/GlitchTitle";
|
| 8 |
+
import { scoreCode, refactorCode, type ScoreResult, type RefactorResult } from "./api";
|
| 9 |
+
|
| 10 |
+
const SAMPLE_CODE = `global_counter = 0
|
| 11 |
+
|
| 12 |
+
def bloated_pipeline(data):
|
| 13 |
+
global global_counter
|
| 14 |
+
try:
|
| 15 |
+
for x in data:
|
| 16 |
+
if x > 0:
|
| 17 |
+
for i in range(x):
|
| 18 |
+
try:
|
| 19 |
+
if i % 2 == 0:
|
| 20 |
+
global_counter += 1
|
| 21 |
+
except:
|
| 22 |
+
pass
|
| 23 |
+
except Exception:
|
| 24 |
+
return -1
|
| 25 |
+
return global_counter
|
| 26 |
+
`;
|
| 27 |
+
|
| 28 |
+
export default function App() {
|
| 29 |
+
const [code, setCode] = useState(SAMPLE_CODE);
|
| 30 |
+
const [scoreResult, setScoreResult] = useState<ScoreResult | null>(null);
|
| 31 |
+
const [refactorResult, setRefactorResult] = useState<RefactorResult | null>(null);
|
| 32 |
+
const [scoring, setScoring] = useState(false);
|
| 33 |
+
const [refactoring, setRefactoring] = useState(false);
|
| 34 |
+
const [burstNonce, setBurstNonce] = useState(0);
|
| 35 |
+
const [flipped, setFlipped] = useState(false);
|
| 36 |
+
const [flipNonce, setFlipNonce] = useState(0);
|
| 37 |
+
const [statusMessage, setStatusMessage] = useState("");
|
| 38 |
+
const [statusError, setStatusError] = useState(false);
|
| 39 |
+
|
| 40 |
+
async function handleScore() {
|
| 41 |
+
setScoring(true);
|
| 42 |
+
setStatusMessage("");
|
| 43 |
+
setStatusError(false);
|
| 44 |
+
setRefactorResult(null);
|
| 45 |
+
setFlipped(false);
|
| 46 |
+
try {
|
| 47 |
+
const result = await scoreCode(code);
|
| 48 |
+
if (result.error) {
|
| 49 |
+
setStatusMessage(result.message ?? "Scoring failed.");
|
| 50 |
+
setStatusError(true);
|
| 51 |
+
setScoreResult(null);
|
| 52 |
+
} else {
|
| 53 |
+
setScoreResult(result);
|
| 54 |
+
setBurstNonce((n) => n + 1);
|
| 55 |
+
}
|
| 56 |
+
} catch (err) {
|
| 57 |
+
setStatusMessage(err instanceof Error ? err.message : "Scoring failed.");
|
| 58 |
+
setStatusError(true);
|
| 59 |
+
} finally {
|
| 60 |
+
setScoring(false);
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
async function handleRefactor() {
|
| 65 |
+
setRefactoring(true);
|
| 66 |
+
setStatusMessage("");
|
| 67 |
+
setStatusError(false);
|
| 68 |
+
try {
|
| 69 |
+
const result = await refactorCode(code);
|
| 70 |
+
if (result.error) {
|
| 71 |
+
setStatusMessage(result.message ?? "Refactor failed.");
|
| 72 |
+
setStatusError(true);
|
| 73 |
+
} else {
|
| 74 |
+
setRefactorResult(result);
|
| 75 |
+
setFlipped(true);
|
| 76 |
+
setFlipNonce((n) => n + 1);
|
| 77 |
+
setBurstNonce((n) => n + 1);
|
| 78 |
+
}
|
| 79 |
+
} catch (err) {
|
| 80 |
+
setStatusMessage(err instanceof Error ? err.message : "Refactor failed.");
|
| 81 |
+
setStatusError(true);
|
| 82 |
+
} finally {
|
| 83 |
+
setRefactoring(false);
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
function handleFlipBack() {
|
| 88 |
+
setFlipped(false);
|
| 89 |
+
setFlipNonce((n) => n + 1);
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
const intensity = scoring || refactoring ? 1 : 0;
|
| 93 |
+
|
| 94 |
+
const stagePanel = (
|
| 95 |
+
<div className="stage-3d">
|
| 96 |
+
<FlipStage
|
| 97 |
+
front={<CodeEditor code={code} onChange={setCode} scanning={scoring} />}
|
| 98 |
+
flipped={flipped}
|
| 99 |
+
refactorResult={refactorResult}
|
| 100 |
+
initialScore={scoreResult}
|
| 101 |
+
flipNonce={flipNonce}
|
| 102 |
+
/>
|
| 103 |
+
<div className="editor-actions">
|
| 104 |
+
<MagneticButton
|
| 105 |
+
onClick={handleScore}
|
| 106 |
+
disabled={scoring || refactoring || !code.trim()}
|
| 107 |
+
variant="copper"
|
| 108 |
+
>
|
| 109 |
+
{scoring ? "ANALYZING…" : "ANALYZE RISK"}
|
| 110 |
+
</MagneticButton>
|
| 111 |
+
<MagneticButton
|
| 112 |
+
onClick={handleRefactor}
|
| 113 |
+
disabled={!scoreResult || !!scoreResult.error || refactoring || scoring}
|
| 114 |
+
variant="ember"
|
| 115 |
+
>
|
| 116 |
+
{refactoring ? "REFACTORING…" : "AUTO-REFACTOR"}
|
| 117 |
+
</MagneticButton>
|
| 118 |
+
{flipped && (
|
| 119 |
+
<MagneticButton onClick={handleFlipBack} variant="titanium">
|
| 120 |
+
RETURN TO SOURCE
|
| 121 |
+
</MagneticButton>
|
| 122 |
+
)}
|
| 123 |
+
</div>
|
| 124 |
+
<div className={`status-line ${statusError ? "error" : ""}`}>{statusMessage}</div>
|
| 125 |
+
</div>
|
| 126 |
+
);
|
| 127 |
+
|
| 128 |
+
const hudPanel = <AnalyticsHUD result={scoreResult} />;
|
| 129 |
+
|
| 130 |
+
return (
|
| 131 |
+
<div className="app-shell">
|
| 132 |
+
<Scene3D
|
| 133 |
+
riskScore={scoreResult?.risk_score ?? 0}
|
| 134 |
+
intensity={intensity}
|
| 135 |
+
burstNonce={burstNonce}
|
| 136 |
+
stagePanel={stagePanel}
|
| 137 |
+
hudPanel={hudPanel}
|
| 138 |
+
/>
|
| 139 |
+
<div className="crt-layer" aria-hidden />
|
| 140 |
+
<header className="app-header-overlay">
|
| 141 |
+
<GlitchTitle text="MAINTAINABILITY ENGINE" />
|
| 142 |
+
<p className="app-subtitle">
|
| 143 |
+
AST-driven risk scoring + autonomous AI refactoring, forged in 3D
|
| 144 |
+
</p>
|
| 145 |
+
</header>
|
| 146 |
+
</div>
|
| 147 |
+
);
|
| 148 |
+
}
|
frontend/src/api.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface ScoreResult {
|
| 2 |
+
error?: boolean;
|
| 3 |
+
message?: string;
|
| 4 |
+
risk_score: number;
|
| 5 |
+
risk_level: "Low" | "Medium" | "High";
|
| 6 |
+
confidence: number;
|
| 7 |
+
top_risk_factors: string[];
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
export interface RefactorResult {
|
| 11 |
+
error?: boolean;
|
| 12 |
+
message?: string;
|
| 13 |
+
initial_code: string;
|
| 14 |
+
final_code: string;
|
| 15 |
+
final_score: number | null;
|
| 16 |
+
iterations_run: number;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || "";
|
| 20 |
+
|
| 21 |
+
async function post<T>(path: string, body: unknown): Promise<T> {
|
| 22 |
+
const fullPath = `${API_BASE_URL}${path}`;
|
| 23 |
+
const res = await fetch(fullPath, {
|
| 24 |
+
method: "POST",
|
| 25 |
+
headers: { "Content-Type": "application/json" },
|
| 26 |
+
body: JSON.stringify(body),
|
| 27 |
+
});
|
| 28 |
+
if (!res.ok) {
|
| 29 |
+
throw new Error(`Request to ${fullPath} failed with status ${res.status}`);
|
| 30 |
+
}
|
| 31 |
+
return res.json() as Promise<T>;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
export function scoreCode(code: string): Promise<ScoreResult> {
|
| 35 |
+
return post<ScoreResult>("/api/score", { code });
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
export function refactorCode(
|
| 39 |
+
code: string,
|
| 40 |
+
targetScore = 20.0,
|
| 41 |
+
maxIterations = 3
|
| 42 |
+
): Promise<RefactorResult> {
|
| 43 |
+
return post<RefactorResult>("/api/refactor", {
|
| 44 |
+
code,
|
| 45 |
+
target_score: targetScore,
|
| 46 |
+
max_iterations: maxIterations,
|
| 47 |
+
});
|
| 48 |
+
}
|
frontend/src/components/AnalyticsHUD.tsx
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { motion, AnimatePresence } from "framer-motion";
|
| 2 |
+
import RiskGauge from "./RiskGauge";
|
| 3 |
+
import type { ScoreResult } from "../api";
|
| 4 |
+
|
| 5 |
+
interface AnalyticsHUDProps {
|
| 6 |
+
result: ScoreResult | null;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
const levelColor: Record<string, string> = {
|
| 10 |
+
Low: "var(--titanium)",
|
| 11 |
+
Medium: "var(--amber)",
|
| 12 |
+
High: "var(--ember)",
|
| 13 |
+
};
|
| 14 |
+
|
| 15 |
+
export default function AnalyticsHUD({ result }: AnalyticsHUDProps) {
|
| 16 |
+
return (
|
| 17 |
+
<AnimatePresence>
|
| 18 |
+
{result && !result.error && (
|
| 19 |
+
<motion.div
|
| 20 |
+
key="hud"
|
| 21 |
+
className={`glass-panel hud-panel ${result.risk_level === "High" ? "hud-pulse" : ""}`}
|
| 22 |
+
initial={{ opacity: 0, clipPath: "inset(0 100% 0 0)", y: -24, skewX: -8 }}
|
| 23 |
+
animate={{ opacity: 1, clipPath: "inset(0 0% 0 0)", y: 0, skewX: 0 }}
|
| 24 |
+
exit={{ opacity: 0, clipPath: "inset(0 0 0 100%)", y: -10 }}
|
| 25 |
+
transition={{ duration: 0.7, ease: [0.16, 1, 0.3, 1] }}
|
| 26 |
+
>
|
| 27 |
+
<div className="hud-header">
|
| 28 |
+
<span className="glow-text-amber hud-label aberrate">// ANALYTICS HUD</span>
|
| 29 |
+
<span className="hud-header-line" />
|
| 30 |
+
</div>
|
| 31 |
+
|
| 32 |
+
<div className="hud-grid">
|
| 33 |
+
<RiskGauge score={result.risk_score} />
|
| 34 |
+
|
| 35 |
+
<div className="hud-right">
|
| 36 |
+
<motion.div
|
| 37 |
+
className="hud-badge"
|
| 38 |
+
style={{
|
| 39 |
+
color: levelColor[result.risk_level],
|
| 40 |
+
borderColor: levelColor[result.risk_level],
|
| 41 |
+
}}
|
| 42 |
+
initial={{ opacity: 0, scale: 0.6 }}
|
| 43 |
+
animate={{ opacity: 1, scale: 1 }}
|
| 44 |
+
transition={{ delay: 0.5, type: "spring", stiffness: 300, damping: 16 }}
|
| 45 |
+
>
|
| 46 |
+
{result.risk_level.toUpperCase()} RISK
|
| 47 |
+
</motion.div>
|
| 48 |
+
<motion.div
|
| 49 |
+
className="hud-confidence"
|
| 50 |
+
initial={{ opacity: 0 }}
|
| 51 |
+
animate={{ opacity: 1 }}
|
| 52 |
+
transition={{ delay: 0.7 }}
|
| 53 |
+
>
|
| 54 |
+
confidence {(result.confidence * 100).toFixed(0)}%
|
| 55 |
+
</motion.div>
|
| 56 |
+
|
| 57 |
+
<motion.ul
|
| 58 |
+
className="hud-factors"
|
| 59 |
+
initial="hidden"
|
| 60 |
+
animate="visible"
|
| 61 |
+
variants={{
|
| 62 |
+
hidden: {},
|
| 63 |
+
visible: { transition: { staggerChildren: 0.12, delayChildren: 0.8 } },
|
| 64 |
+
}}
|
| 65 |
+
>
|
| 66 |
+
{result.top_risk_factors.map((factor, i) => (
|
| 67 |
+
<motion.li
|
| 68 |
+
key={i}
|
| 69 |
+
variants={{
|
| 70 |
+
hidden: { opacity: 0, x: -18, filter: "blur(4px)" },
|
| 71 |
+
visible: { opacity: 1, x: 0, filter: "blur(0px)" },
|
| 72 |
+
}}
|
| 73 |
+
>
|
| 74 |
+
<span className="hud-factor-marker">▸</span> {factor}
|
| 75 |
+
</motion.li>
|
| 76 |
+
))}
|
| 77 |
+
</motion.ul>
|
| 78 |
+
</div>
|
| 79 |
+
</div>
|
| 80 |
+
</motion.div>
|
| 81 |
+
)}
|
| 82 |
+
</AnimatePresence>
|
| 83 |
+
);
|
| 84 |
+
}
|
frontend/src/components/CodeEditor.tsx
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import Editor from "react-simple-code-editor";
|
| 2 |
+
import Prism from "prismjs";
|
| 3 |
+
import "prismjs/components/prism-python";
|
| 4 |
+
import { motion, AnimatePresence } from "framer-motion";
|
| 5 |
+
|
| 6 |
+
interface CodeEditorProps {
|
| 7 |
+
code: string;
|
| 8 |
+
onChange: (code: string) => void;
|
| 9 |
+
scanning: boolean;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
export default function CodeEditor({ code, onChange, scanning }: CodeEditorProps) {
|
| 13 |
+
return (
|
| 14 |
+
<div className="editor-inner">
|
| 15 |
+
<div className="editor-header">
|
| 16 |
+
<span className="glow-text-copper editor-title aberrate">// SOURCE.py</span>
|
| 17 |
+
<span className="editor-subtitle">
|
| 18 |
+
{scanning ? "SCANNING STRUCTURE…" : "paste python to analyze"}
|
| 19 |
+
</span>
|
| 20 |
+
</div>
|
| 21 |
+
<div className="editor-body">
|
| 22 |
+
<Editor
|
| 23 |
+
value={code}
|
| 24 |
+
onValueChange={onChange}
|
| 25 |
+
highlight={(src) => Prism.highlight(src, Prism.languages.python, "python")}
|
| 26 |
+
padding={16}
|
| 27 |
+
style={{
|
| 28 |
+
fontFamily: "var(--font-mono)",
|
| 29 |
+
fontSize: 14,
|
| 30 |
+
minHeight: 260,
|
| 31 |
+
color: "var(--text-primary)",
|
| 32 |
+
}}
|
| 33 |
+
/>
|
| 34 |
+
<AnimatePresence>
|
| 35 |
+
{scanning && (
|
| 36 |
+
<motion.div
|
| 37 |
+
key="laser"
|
| 38 |
+
className="scan-laser"
|
| 39 |
+
initial={{ top: "0%", opacity: 0 }}
|
| 40 |
+
animate={{ top: ["0%", "100%"], opacity: 1 }}
|
| 41 |
+
exit={{ opacity: 0 }}
|
| 42 |
+
transition={{
|
| 43 |
+
top: { duration: 1.1, repeat: Infinity, ease: "linear" },
|
| 44 |
+
opacity: { duration: 0.2 },
|
| 45 |
+
}}
|
| 46 |
+
/>
|
| 47 |
+
)}
|
| 48 |
+
</AnimatePresence>
|
| 49 |
+
{scanning && <div className="scan-tint" />}
|
| 50 |
+
</div>
|
| 51 |
+
</div>
|
| 52 |
+
);
|
| 53 |
+
}
|
frontend/src/components/FlipStage.tsx
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
| 2 |
+
import { motion, AnimatePresence, useMotionValue, animate } from "framer-motion";
|
| 3 |
+
import Prism from "prismjs";
|
| 4 |
+
import "prismjs/components/prism-python";
|
| 5 |
+
import TiltPanel from "./TiltPanel";
|
| 6 |
+
import type { RefactorResult, ScoreResult } from "../api";
|
| 7 |
+
|
| 8 |
+
function PointsCounter({ to }: { to: number }) {
|
| 9 |
+
const mv = useMotionValue(0);
|
| 10 |
+
const [display, setDisplay] = useState(0);
|
| 11 |
+
useEffect(() => {
|
| 12 |
+
const controls = animate(mv, to, {
|
| 13 |
+
duration: 1.2,
|
| 14 |
+
ease: "easeOut",
|
| 15 |
+
onUpdate: (v) => setDisplay(Math.round(v)),
|
| 16 |
+
});
|
| 17 |
+
return () => controls.stop();
|
| 18 |
+
}, [to]);
|
| 19 |
+
return <>{display}</>;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
/** Radial DOM particle explosion fired on every flip. */
|
| 23 |
+
function BurstParticles({ nonce }: { nonce: number }) {
|
| 24 |
+
const particles = useMemo(
|
| 25 |
+
() =>
|
| 26 |
+
Array.from({ length: 26 }).map((_, i) => {
|
| 27 |
+
const angle = (i / 26) * Math.PI * 2 + Math.random() * 0.4;
|
| 28 |
+
const dist = 180 + Math.random() * 240;
|
| 29 |
+
return {
|
| 30 |
+
id: `${nonce}-${i}`,
|
| 31 |
+
dx: Math.cos(angle) * dist,
|
| 32 |
+
dy: Math.sin(angle) * dist * 0.7,
|
| 33 |
+
size: 3 + Math.random() * 6,
|
| 34 |
+
color: i % 3 === 0 ? "var(--ember)" : "var(--amber)",
|
| 35 |
+
delay: Math.random() * 0.12,
|
| 36 |
+
};
|
| 37 |
+
}),
|
| 38 |
+
[nonce]
|
| 39 |
+
);
|
| 40 |
+
|
| 41 |
+
if (nonce === 0) return null;
|
| 42 |
+
|
| 43 |
+
return (
|
| 44 |
+
<div className="burst-field" aria-hidden>
|
| 45 |
+
{particles.map((p) => (
|
| 46 |
+
<motion.span
|
| 47 |
+
key={p.id}
|
| 48 |
+
className="burst-particle"
|
| 49 |
+
style={{ width: p.size, height: p.size, background: p.color, boxShadow: `0 0 10px ${p.color}` }}
|
| 50 |
+
initial={{ x: 0, y: 0, opacity: 1, scale: 1 }}
|
| 51 |
+
animate={{ x: p.dx, y: p.dy, opacity: 0, scale: 0.2 }}
|
| 52 |
+
transition={{ duration: 1.1, delay: p.delay, ease: [0.16, 1, 0.3, 1] }}
|
| 53 |
+
/>
|
| 54 |
+
))}
|
| 55 |
+
</div>
|
| 56 |
+
);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
interface FlipStageProps {
|
| 60 |
+
/** front face content: the live editor */
|
| 61 |
+
front: ReactNode;
|
| 62 |
+
flipped: boolean;
|
| 63 |
+
refactorResult: RefactorResult | null;
|
| 64 |
+
initialScore: ScoreResult | null;
|
| 65 |
+
flipNonce: number;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
/**
|
| 69 |
+
* The glass editor is a physical object: on refactor it rotates a full
|
| 70 |
+
* 180° on the Y axis, exploding particles outward, revealing the cleaned
|
| 71 |
+
* code engraved on its back face.
|
| 72 |
+
*/
|
| 73 |
+
export default function FlipStage({
|
| 74 |
+
front,
|
| 75 |
+
flipped,
|
| 76 |
+
refactorResult,
|
| 77 |
+
initialScore,
|
| 78 |
+
flipNonce,
|
| 79 |
+
}: FlipStageProps) {
|
| 80 |
+
const noValidCandidate = refactorResult?.final_score == null;
|
| 81 |
+
const startScore = initialScore?.risk_score ?? 0;
|
| 82 |
+
const pointsSaved =
|
| 83 |
+
!refactorResult || noValidCandidate
|
| 84 |
+
? 0
|
| 85 |
+
: Math.max(0, startScore - (refactorResult.final_score as number));
|
| 86 |
+
|
| 87 |
+
return (
|
| 88 |
+
<div className="flip-stage">
|
| 89 |
+
<BurstParticles nonce={flipNonce} />
|
| 90 |
+
<TiltPanel className="flip-tilt" maxTilt={6}>
|
| 91 |
+
<motion.div
|
| 92 |
+
className="flip-object"
|
| 93 |
+
animate={{ rotateY: flipped ? 180 : 0 }}
|
| 94 |
+
transition={{ duration: 1.1, ease: [0.68, -0.2, 0.27, 1.15] }}
|
| 95 |
+
>
|
| 96 |
+
<div className="flip-face flip-front glass-panel">{front}</div>
|
| 97 |
+
|
| 98 |
+
<div className="flip-face flip-back glass-panel">
|
| 99 |
+
<div className="editor-header">
|
| 100 |
+
<span className="glow-text-amber editor-title aberrate">// CLEANED.py</span>
|
| 101 |
+
<span className="editor-subtitle">
|
| 102 |
+
{refactorResult
|
| 103 |
+
? `${refactorResult.iterations_run} iteration(s) · RISK ${
|
| 104 |
+
noValidCandidate ? "N/A" : refactorResult.final_score
|
| 105 |
+
}`
|
| 106 |
+
: "awaiting refactor"}
|
| 107 |
+
</span>
|
| 108 |
+
</div>
|
| 109 |
+
<div className="editor-body back-body">
|
| 110 |
+
{refactorResult && (
|
| 111 |
+
<pre
|
| 112 |
+
className="code-block"
|
| 113 |
+
dangerouslySetInnerHTML={{
|
| 114 |
+
__html: Prism.highlight(
|
| 115 |
+
refactorResult.final_code,
|
| 116 |
+
Prism.languages.python,
|
| 117 |
+
"python"
|
| 118 |
+
),
|
| 119 |
+
}}
|
| 120 |
+
/>
|
| 121 |
+
)}
|
| 122 |
+
</div>
|
| 123 |
+
<AnimatePresence>
|
| 124 |
+
{flipped && refactorResult && (
|
| 125 |
+
<motion.div
|
| 126 |
+
className="back-footer"
|
| 127 |
+
initial={{ opacity: 0, y: 12 }}
|
| 128 |
+
animate={{ opacity: 1, y: 0 }}
|
| 129 |
+
exit={{ opacity: 0 }}
|
| 130 |
+
transition={{ delay: 0.7 }}
|
| 131 |
+
>
|
| 132 |
+
<div className="points-saved">
|
| 133 |
+
<span className="points-saved-number glow-text-amber">
|
| 134 |
+
<PointsCounter to={pointsSaved} />
|
| 135 |
+
</span>
|
| 136 |
+
<span className="points-saved-label">points saved</span>
|
| 137 |
+
</div>
|
| 138 |
+
{noValidCandidate && (
|
| 139 |
+
<div className="back-note">
|
| 140 |
+
no candidate beat the original this round — unchanged code shown
|
| 141 |
+
</div>
|
| 142 |
+
)}
|
| 143 |
+
</motion.div>
|
| 144 |
+
)}
|
| 145 |
+
</AnimatePresence>
|
| 146 |
+
</div>
|
| 147 |
+
</motion.div>
|
| 148 |
+
</TiltPanel>
|
| 149 |
+
</div>
|
| 150 |
+
);
|
| 151 |
+
}
|
frontend/src/components/GlitchTitle.tsx
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interface GlitchTitleProps {
|
| 2 |
+
text: string;
|
| 3 |
+
}
|
| 4 |
+
|
| 5 |
+
/**
|
| 6 |
+
* Kinetic glitch typography: two chromatic ghost copies (ember / titanium)
|
| 7 |
+
* clip-slice and jitter over the base text on a broadcast-interference loop.
|
| 8 |
+
*/
|
| 9 |
+
export default function GlitchTitle({ text }: GlitchTitleProps) {
|
| 10 |
+
return (
|
| 11 |
+
<h1 className="glitch-title" data-text={text}>
|
| 12 |
+
{text}
|
| 13 |
+
</h1>
|
| 14 |
+
);
|
| 15 |
+
}
|
frontend/src/components/MagneticButton.tsx
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useRef, useState, type ReactNode, type MouseEvent } from "react";
|
| 2 |
+
import { motion, AnimatePresence } from "framer-motion";
|
| 3 |
+
|
| 4 |
+
interface MagneticButtonProps {
|
| 5 |
+
children: ReactNode;
|
| 6 |
+
onClick?: () => void;
|
| 7 |
+
disabled?: boolean;
|
| 8 |
+
variant?: "copper" | "ember" | "titanium";
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
const VARIANT_COLORS = {
|
| 12 |
+
copper: { color: "var(--copper)", glow: "var(--copper-glow)" },
|
| 13 |
+
ember: { color: "var(--ember)", glow: "var(--ember-glow)" },
|
| 14 |
+
titanium: { color: "var(--titanium)", glow: "var(--titanium-glow)" },
|
| 15 |
+
} as const;
|
| 16 |
+
|
| 17 |
+
interface Ripple {
|
| 18 |
+
id: number;
|
| 19 |
+
x: number;
|
| 20 |
+
y: number;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
export default function MagneticButton({
|
| 24 |
+
children,
|
| 25 |
+
onClick,
|
| 26 |
+
disabled,
|
| 27 |
+
variant = "copper",
|
| 28 |
+
}: MagneticButtonProps) {
|
| 29 |
+
const ref = useRef<HTMLButtonElement>(null);
|
| 30 |
+
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
| 31 |
+
const [hovered, setHovered] = useState(false);
|
| 32 |
+
const [ripples, setRipples] = useState<Ripple[]>([]);
|
| 33 |
+
const rippleId = useRef(0);
|
| 34 |
+
|
| 35 |
+
function handleMouseMove(e: MouseEvent<HTMLButtonElement>) {
|
| 36 |
+
const el = ref.current;
|
| 37 |
+
if (!el) return;
|
| 38 |
+
const rect = el.getBoundingClientRect();
|
| 39 |
+
const relX = e.clientX - rect.left - rect.width / 2;
|
| 40 |
+
const relY = e.clientY - rect.top - rect.height / 2;
|
| 41 |
+
setOffset({ x: relX * 0.3, y: relY * 0.45 });
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
function handleMouseLeave() {
|
| 45 |
+
setOffset({ x: 0, y: 0 });
|
| 46 |
+
setHovered(false);
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
function handleClick(e: MouseEvent<HTMLButtonElement>) {
|
| 50 |
+
const el = ref.current;
|
| 51 |
+
if (el) {
|
| 52 |
+
const rect = el.getBoundingClientRect();
|
| 53 |
+
const ripple = { id: rippleId.current++, x: e.clientX - rect.left, y: e.clientY - rect.top };
|
| 54 |
+
setRipples((r) => [...r, ripple]);
|
| 55 |
+
setTimeout(() => setRipples((r) => r.filter((rp) => rp.id !== ripple.id)), 700);
|
| 56 |
+
}
|
| 57 |
+
onClick?.();
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
const { color, glow } = VARIANT_COLORS[variant];
|
| 61 |
+
|
| 62 |
+
return (
|
| 63 |
+
<motion.button
|
| 64 |
+
ref={ref}
|
| 65 |
+
className="magnetic-button"
|
| 66 |
+
onMouseMove={handleMouseMove}
|
| 67 |
+
onMouseEnter={() => setHovered(true)}
|
| 68 |
+
onMouseLeave={handleMouseLeave}
|
| 69 |
+
onClick={handleClick}
|
| 70 |
+
disabled={disabled}
|
| 71 |
+
animate={{ x: offset.x, y: offset.y, scale: hovered && !disabled ? 1.04 : 1 }}
|
| 72 |
+
whileTap={{ scale: 0.96 }}
|
| 73 |
+
transition={{ type: "spring", stiffness: 260, damping: 14, mass: 0.4 }}
|
| 74 |
+
style={{ color, boxShadow: hovered && !disabled ? `0 0 28px ${glow}` : `0 0 12px ${glow}` }}
|
| 75 |
+
>
|
| 76 |
+
{/* animated border trace */}
|
| 77 |
+
<svg className="button-trace" aria-hidden>
|
| 78 |
+
<motion.rect
|
| 79 |
+
x={1.5}
|
| 80 |
+
y={1.5}
|
| 81 |
+
rx="24"
|
| 82 |
+
fill="none"
|
| 83 |
+
className="trace-rect"
|
| 84 |
+
stroke={color}
|
| 85 |
+
strokeWidth="1.5"
|
| 86 |
+
initial={false}
|
| 87 |
+
animate={{ pathLength: hovered && !disabled ? 1 : 0.08, opacity: disabled ? 0.3 : 1 }}
|
| 88 |
+
transition={{ duration: 0.55, ease: "easeInOut" }}
|
| 89 |
+
/>
|
| 90 |
+
</svg>
|
| 91 |
+
|
| 92 |
+
<AnimatePresence>
|
| 93 |
+
{ripples.map((r) => (
|
| 94 |
+
<motion.span
|
| 95 |
+
key={r.id}
|
| 96 |
+
className="button-ripple"
|
| 97 |
+
style={{ left: r.x, top: r.y, background: glow }}
|
| 98 |
+
initial={{ scale: 0, opacity: 0.7 }}
|
| 99 |
+
animate={{ scale: 5, opacity: 0 }}
|
| 100 |
+
exit={{ opacity: 0 }}
|
| 101 |
+
transition={{ duration: 0.65, ease: "easeOut" }}
|
| 102 |
+
/>
|
| 103 |
+
))}
|
| 104 |
+
</AnimatePresence>
|
| 105 |
+
|
| 106 |
+
<span className="button-label">{children}</span>
|
| 107 |
+
</motion.button>
|
| 108 |
+
);
|
| 109 |
+
}
|
frontend/src/components/RiskGauge.tsx
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useEffect, useRef, useState } from "react";
|
| 2 |
+
import { animate } from "framer-motion";
|
| 3 |
+
|
| 4 |
+
const R = 78;
|
| 5 |
+
const CIRC = 2 * Math.PI * R;
|
| 6 |
+
|
| 7 |
+
const GLITCH_GLYPHS = "0123456789ABCDEF#%&$@!?";
|
| 8 |
+
|
| 9 |
+
function lerpRGB(a: number[], b: number[], t: number): number[] {
|
| 10 |
+
return a.map((v, i) => Math.round(v + (b[i] - v) * t));
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
function mixColor(score: number): string {
|
| 14 |
+
// titanium #8b9099 -> copper #e8823f -> ember #ff6a2b
|
| 15 |
+
const t = Math.min(Math.max(score / 100, 0), 1);
|
| 16 |
+
const titanium = [0x8b, 0x90, 0x99];
|
| 17 |
+
const copper = [0xe8, 0x82, 0x3f];
|
| 18 |
+
const ember = [0xff, 0x6a, 0x2b];
|
| 19 |
+
const c =
|
| 20 |
+
t < 0.5 ? lerpRGB(titanium, copper, t * 2) : lerpRGB(copper, ember, (t - 0.5) * 2);
|
| 21 |
+
return `#${c.map((v) => v.toString(16).padStart(2, "0")).join("")}`;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* Futuristic 270-degree dial. The arc sweeps up with a spring, the needle
|
| 26 |
+
* chases it, and the readout ticks with glitch glyphs until it locks in.
|
| 27 |
+
*/
|
| 28 |
+
export default function RiskGauge({ score }: { score: number }) {
|
| 29 |
+
const [display, setDisplay] = useState("00");
|
| 30 |
+
const [locked, setLocked] = useState(false);
|
| 31 |
+
const [progress, setProgress] = useState(0);
|
| 32 |
+
const glitchTimer = useRef(0);
|
| 33 |
+
|
| 34 |
+
useEffect(() => {
|
| 35 |
+
setLocked(false);
|
| 36 |
+
const controls = animate(0, score, {
|
| 37 |
+
duration: 1.6,
|
| 38 |
+
ease: [0.16, 1, 0.3, 1],
|
| 39 |
+
onUpdate: (v) => {
|
| 40 |
+
setProgress(v / 100);
|
| 41 |
+
glitchTimer.current++;
|
| 42 |
+
// every few frames, corrupt a digit while ticking
|
| 43 |
+
if (glitchTimer.current % 4 === 0 && v < score - 2) {
|
| 44 |
+
const glyph = GLITCH_GLYPHS[Math.floor(Math.random() * GLITCH_GLYPHS.length)];
|
| 45 |
+
setDisplay(String(Math.round(v)).padStart(2, "0").slice(0, -1) + glyph);
|
| 46 |
+
} else {
|
| 47 |
+
setDisplay(String(Math.round(v)).padStart(2, "0"));
|
| 48 |
+
}
|
| 49 |
+
},
|
| 50 |
+
onComplete: () => {
|
| 51 |
+
setDisplay(String(score).padStart(2, "0"));
|
| 52 |
+
setLocked(true);
|
| 53 |
+
},
|
| 54 |
+
});
|
| 55 |
+
return () => controls.stop();
|
| 56 |
+
}, [score]);
|
| 57 |
+
|
| 58 |
+
const color = mixColor(score);
|
| 59 |
+
const needleAngle = -135 + progress * 270;
|
| 60 |
+
|
| 61 |
+
return (
|
| 62 |
+
<div className="risk-gauge">
|
| 63 |
+
<svg viewBox="0 0 200 200" className="gauge-svg">
|
| 64 |
+
{/* track */}
|
| 65 |
+
<circle
|
| 66 |
+
cx="100"
|
| 67 |
+
cy="100"
|
| 68 |
+
r="78"
|
| 69 |
+
fill="none"
|
| 70 |
+
stroke="rgba(232,130,63,0.14)"
|
| 71 |
+
strokeWidth="6"
|
| 72 |
+
strokeLinecap="round"
|
| 73 |
+
strokeDasharray="367.5 490"
|
| 74 |
+
transform="rotate(135 100 100)"
|
| 75 |
+
/>
|
| 76 |
+
{/* tick marks */}
|
| 77 |
+
{Array.from({ length: 28 }).map((_, i) => {
|
| 78 |
+
const angle = ((-135 + (i / 27) * 270) * Math.PI) / 180;
|
| 79 |
+
const inner = 88;
|
| 80 |
+
const outer = i % 9 === 0 ? 96 : 92;
|
| 81 |
+
return (
|
| 82 |
+
<line
|
| 83 |
+
key={i}
|
| 84 |
+
x1={100 + inner * Math.sin(angle)}
|
| 85 |
+
y1={100 - inner * Math.cos(angle)}
|
| 86 |
+
x2={100 + outer * Math.sin(angle)}
|
| 87 |
+
y2={100 - outer * Math.cos(angle)}
|
| 88 |
+
stroke={i / 27 <= progress ? color : "rgba(139,144,153,0.28)"}
|
| 89 |
+
strokeWidth={i % 9 === 0 ? 2.5 : 1.2}
|
| 90 |
+
/>
|
| 91 |
+
);
|
| 92 |
+
})}
|
| 93 |
+
{/* value arc */}
|
| 94 |
+
<circle
|
| 95 |
+
cx="100"
|
| 96 |
+
cy="100"
|
| 97 |
+
r={R}
|
| 98 |
+
fill="none"
|
| 99 |
+
stroke={color}
|
| 100 |
+
strokeWidth="6"
|
| 101 |
+
strokeLinecap="round"
|
| 102 |
+
transform="rotate(135 100 100)"
|
| 103 |
+
strokeDasharray={`${progress * 0.75 * CIRC} ${CIRC}`}
|
| 104 |
+
style={{ filter: `drop-shadow(0 0 8px ${color})` }}
|
| 105 |
+
/>
|
| 106 |
+
{/* needle */}
|
| 107 |
+
<g transform={`rotate(${needleAngle} 100 100)`}>
|
| 108 |
+
<line
|
| 109 |
+
x1="100"
|
| 110 |
+
y1="100"
|
| 111 |
+
x2="100"
|
| 112 |
+
y2="34"
|
| 113 |
+
stroke={color}
|
| 114 |
+
strokeWidth="2.5"
|
| 115 |
+
style={{ filter: `drop-shadow(0 0 6px ${color})` }}
|
| 116 |
+
/>
|
| 117 |
+
<circle cx="100" cy="100" r="5" fill={color} />
|
| 118 |
+
</g>
|
| 119 |
+
</svg>
|
| 120 |
+
<div
|
| 121 |
+
className={`gauge-readout ${locked ? "gauge-locked" : "gauge-ticking"}`}
|
| 122 |
+
style={{ color, textShadow: `0 0 18px ${color}` }}
|
| 123 |
+
>
|
| 124 |
+
{display}
|
| 125 |
+
</div>
|
| 126 |
+
<div className="gauge-caption">RISK INDEX</div>
|
| 127 |
+
</div>
|
| 128 |
+
);
|
| 129 |
+
}
|
frontend/src/components/Scene3D.tsx
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useMemo, useRef, type ReactNode } from "react";
|
| 2 |
+
import { Canvas, useFrame } from "@react-three/fiber";
|
| 3 |
+
import { Html } from "@react-three/drei";
|
| 4 |
+
import { EffectComposer, Bloom, ChromaticAberration, Vignette } from "@react-three/postprocessing";
|
| 5 |
+
import * as THREE from "three";
|
| 6 |
+
|
| 7 |
+
const TITANIUM = new THREE.Color("#8b9099");
|
| 8 |
+
const COPPER = new THREE.Color("#e8823f");
|
| 9 |
+
const EMBER = new THREE.Color("#ff6a2b");
|
| 10 |
+
|
| 11 |
+
function riskColor(score: number): THREE.Color {
|
| 12 |
+
const t = Math.min(Math.max(score / 100, 0), 1);
|
| 13 |
+
if (t < 0.5) {
|
| 14 |
+
return TITANIUM.clone().lerp(COPPER, t * 2);
|
| 15 |
+
}
|
| 16 |
+
return COPPER.clone().lerp(EMBER, (t - 0.5) * 2);
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
interface Scene3DProps {
|
| 20 |
+
riskScore: number;
|
| 21 |
+
/** 0 = idle drift, 1 = analyzing/refactoring frenzy */
|
| 22 |
+
intensity: number;
|
| 23 |
+
burstNonce: number;
|
| 24 |
+
/** DOM panels physically embedded in the 3D scene */
|
| 25 |
+
stagePanel: ReactNode;
|
| 26 |
+
hudPanel: ReactNode;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
function WarpGrid({ riskScore, intensity }: { riskScore: number; intensity: number }) {
|
| 30 |
+
const meshRef = useRef<THREE.Mesh>(null);
|
| 31 |
+
const geomRef = useRef<THREE.PlaneGeometry>(null);
|
| 32 |
+
const matRef = useRef<THREE.MeshBasicMaterial>(null);
|
| 33 |
+
const original = useRef<Float32Array | null>(null);
|
| 34 |
+
const t = useRef(0);
|
| 35 |
+
const boost = useRef(0);
|
| 36 |
+
|
| 37 |
+
useFrame((_, delta) => {
|
| 38 |
+
boost.current += (intensity - boost.current) * Math.min(1, delta * 3);
|
| 39 |
+
const frenzy = boost.current;
|
| 40 |
+
|
| 41 |
+
t.current += delta * (1 + frenzy * 3.5);
|
| 42 |
+
const geo = geomRef.current;
|
| 43 |
+
if (!geo) return;
|
| 44 |
+
if (!original.current) {
|
| 45 |
+
original.current = Float32Array.from(geo.attributes.position.array);
|
| 46 |
+
}
|
| 47 |
+
const pos = geo.attributes.position as THREE.BufferAttribute;
|
| 48 |
+
const src = original.current;
|
| 49 |
+
const amplitude = 0.25 + (riskScore / 100) * 2.2 + frenzy * 2.4;
|
| 50 |
+
const speed = 0.5 + (riskScore / 100) * 1.4 + frenzy * 2.0;
|
| 51 |
+
for (let i = 0; i < pos.count; i++) {
|
| 52 |
+
const x = src[i * 3];
|
| 53 |
+
const y = src[i * 3 + 1];
|
| 54 |
+
const wave =
|
| 55 |
+
Math.sin(x * 0.18 + t.current * speed) *
|
| 56 |
+
Math.cos(y * 0.18 + t.current * speed * 0.8) +
|
| 57 |
+
frenzy * 0.35 * Math.sin(x * 0.9 + t.current * 6.0) * Math.sin(y * 0.7);
|
| 58 |
+
pos.setZ(i, wave * amplitude);
|
| 59 |
+
}
|
| 60 |
+
pos.needsUpdate = true;
|
| 61 |
+
|
| 62 |
+
if (meshRef.current) {
|
| 63 |
+
meshRef.current.rotation.z += delta * (0.02 + frenzy * 0.12);
|
| 64 |
+
}
|
| 65 |
+
if (matRef.current) {
|
| 66 |
+
matRef.current.color.copy(riskColor(riskScore));
|
| 67 |
+
matRef.current.opacity = 0.45 + frenzy * 0.35;
|
| 68 |
+
}
|
| 69 |
+
});
|
| 70 |
+
|
| 71 |
+
return (
|
| 72 |
+
<mesh ref={meshRef} rotation={[-Math.PI / 2.3, 0, 0]} position={[0, -3.2, -4]}>
|
| 73 |
+
<planeGeometry ref={geomRef} args={[44, 44, 70, 70]} />
|
| 74 |
+
<meshBasicMaterial ref={matRef} wireframe transparent opacity={0.45} toneMapped={false} />
|
| 75 |
+
</mesh>
|
| 76 |
+
);
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
function ParticleField({
|
| 80 |
+
color,
|
| 81 |
+
count,
|
| 82 |
+
radius,
|
| 83 |
+
burstNonce,
|
| 84 |
+
baseSize,
|
| 85 |
+
}: {
|
| 86 |
+
color: string;
|
| 87 |
+
count: number;
|
| 88 |
+
radius: number;
|
| 89 |
+
burstNonce: number;
|
| 90 |
+
baseSize: number;
|
| 91 |
+
}) {
|
| 92 |
+
const pointsRef = useRef<THREE.Points>(null);
|
| 93 |
+
const burstEnergy = useRef(0);
|
| 94 |
+
const lastNonce = useRef(burstNonce);
|
| 95 |
+
|
| 96 |
+
const positions = useMemo(() => {
|
| 97 |
+
const arr = new Float32Array(count * 3);
|
| 98 |
+
for (let i = 0; i < count; i++) {
|
| 99 |
+
const r = radius * (0.4 + Math.random() * 0.9);
|
| 100 |
+
const theta = Math.random() * Math.PI * 2;
|
| 101 |
+
const phi = Math.acos(2 * Math.random() - 1);
|
| 102 |
+
arr[i * 3] = r * Math.sin(phi) * Math.cos(theta);
|
| 103 |
+
arr[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta) * 0.5;
|
| 104 |
+
arr[i * 3 + 2] = r * Math.cos(phi) - 6;
|
| 105 |
+
}
|
| 106 |
+
return arr;
|
| 107 |
+
}, [count, radius]);
|
| 108 |
+
|
| 109 |
+
useFrame((_, delta) => {
|
| 110 |
+
if (burstNonce !== lastNonce.current) {
|
| 111 |
+
lastNonce.current = burstNonce;
|
| 112 |
+
burstEnergy.current = 1;
|
| 113 |
+
}
|
| 114 |
+
burstEnergy.current = Math.max(0, burstEnergy.current - delta * 0.55);
|
| 115 |
+
|
| 116 |
+
if (pointsRef.current) {
|
| 117 |
+
pointsRef.current.rotation.y += delta * (0.03 + burstEnergy.current * 0.5);
|
| 118 |
+
pointsRef.current.rotation.x += delta * burstEnergy.current * 0.1;
|
| 119 |
+
pointsRef.current.scale.setScalar(1 + burstEnergy.current * 0.35);
|
| 120 |
+
const material = pointsRef.current.material as THREE.PointsMaterial;
|
| 121 |
+
material.size = baseSize + burstEnergy.current * 0.14;
|
| 122 |
+
material.opacity = 0.55 + burstEnergy.current * 0.45;
|
| 123 |
+
}
|
| 124 |
+
});
|
| 125 |
+
|
| 126 |
+
return (
|
| 127 |
+
<points ref={pointsRef}>
|
| 128 |
+
<bufferGeometry>
|
| 129 |
+
<bufferAttribute attach="attributes-position" count={count} array={positions} itemSize={3} />
|
| 130 |
+
</bufferGeometry>
|
| 131 |
+
<pointsMaterial
|
| 132 |
+
color={color}
|
| 133 |
+
size={baseSize}
|
| 134 |
+
sizeAttenuation
|
| 135 |
+
transparent
|
| 136 |
+
opacity={0.6}
|
| 137 |
+
depthWrite={false}
|
| 138 |
+
toneMapped={false}
|
| 139 |
+
/>
|
| 140 |
+
</points>
|
| 141 |
+
);
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
function CameraDrift({ intensity }: { intensity: number }) {
|
| 145 |
+
const t = useRef(0);
|
| 146 |
+
useFrame(({ camera }, delta) => {
|
| 147 |
+
t.current += delta;
|
| 148 |
+
const shake = intensity * 0.035;
|
| 149 |
+
// gentle orbital drift: the embedded DOM panels inherit this perspective
|
| 150 |
+
camera.position.x = Math.sin(t.current * 0.16) * 0.55 + (Math.random() - 0.5) * shake;
|
| 151 |
+
camera.position.y = 2.0 + Math.sin(t.current * 0.11) * 0.28 + (Math.random() - 0.5) * shake;
|
| 152 |
+
camera.position.z = 10 + Math.sin(t.current * 0.07) * 0.35;
|
| 153 |
+
camera.lookAt(0, 0, -4);
|
| 154 |
+
});
|
| 155 |
+
return null;
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
/**
|
| 159 |
+
* The whole interface lives INSIDE the canvas: the editor stage and the
|
| 160 |
+
* HUD are CSS3D-transformed by the same camera that renders the grid, so
|
| 161 |
+
* camera drift continuously re-projects their perspective.
|
| 162 |
+
* (WebGL post-processing cannot sample DOM pixels — bloom/aberration on
|
| 163 |
+
* panel text is approximated in CSS by the theme's glow/aberrate classes.)
|
| 164 |
+
*/
|
| 165 |
+
export default function Scene3D({
|
| 166 |
+
riskScore,
|
| 167 |
+
intensity,
|
| 168 |
+
burstNonce,
|
| 169 |
+
stagePanel,
|
| 170 |
+
hudPanel,
|
| 171 |
+
}: Scene3DProps) {
|
| 172 |
+
const aberration = useMemo(
|
| 173 |
+
() => new THREE.Vector2(0.0012 + intensity * 0.0025, 0.0008 + intensity * 0.0018),
|
| 174 |
+
[intensity]
|
| 175 |
+
);
|
| 176 |
+
|
| 177 |
+
return (
|
| 178 |
+
<div className="canvas-layer">
|
| 179 |
+
<Canvas camera={{ position: [0, 2.0, 10], fov: 55 }} dpr={[1, 1.75]}>
|
| 180 |
+
<color attach="background" args={["#0b0806"]} />
|
| 181 |
+
<fog attach="fog" args={["#0b0806", 8, 30]} />
|
| 182 |
+
<WarpGrid riskScore={riskScore} intensity={intensity} />
|
| 183 |
+
<ParticleField color="#e8823f" count={700} radius={16} burstNonce={burstNonce} baseSize={0.05} />
|
| 184 |
+
<ParticleField color="#ffc35e" count={350} radius={12} burstNonce={burstNonce} baseSize={0.06} />
|
| 185 |
+
<CameraDrift intensity={intensity} />
|
| 186 |
+
|
| 187 |
+
{/* forge-glass editor stage, floating above the grid */}
|
| 188 |
+
<group position={[0, 0.85, 0]} rotation={[-0.05, 0, 0]}>
|
| 189 |
+
<Html transform distanceFactor={4} zIndexRange={[40, 0]} className="panel3d stage-panel3d">
|
| 190 |
+
{stagePanel}
|
| 191 |
+
</Html>
|
| 192 |
+
</group>
|
| 193 |
+
|
| 194 |
+
{/* analytics HUD, reclined like a dashboard instrument cluster */}
|
| 195 |
+
<group position={[0, -3.05, -0.4]} rotation={[0.18, 0, 0]}>
|
| 196 |
+
<Html transform distanceFactor={4} zIndexRange={[30, 0]} className="panel3d hud-panel3d">
|
| 197 |
+
{hudPanel}
|
| 198 |
+
</Html>
|
| 199 |
+
</group>
|
| 200 |
+
|
| 201 |
+
<EffectComposer>
|
| 202 |
+
<Bloom intensity={1.35} luminanceThreshold={0.18} luminanceSmoothing={0.85} mipmapBlur />
|
| 203 |
+
<ChromaticAberration offset={aberration} radialModulation modulationOffset={0.4} />
|
| 204 |
+
<Vignette eskil={false} offset={0.18} darkness={0.85} />
|
| 205 |
+
</EffectComposer>
|
| 206 |
+
</Canvas>
|
| 207 |
+
</div>
|
| 208 |
+
);
|
| 209 |
+
}
|
frontend/src/components/TiltPanel.tsx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useRef, type ReactNode, type MouseEvent } from "react";
|
| 2 |
+
import { motion, useMotionValue, useSpring, useTransform } from "framer-motion";
|
| 3 |
+
|
| 4 |
+
interface TiltPanelProps {
|
| 5 |
+
children: ReactNode;
|
| 6 |
+
className?: string;
|
| 7 |
+
/** max tilt in degrees */
|
| 8 |
+
maxTilt?: number;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
/**
|
| 12 |
+
* Volumetric glass panel: tilts toward the cursor in 3D and carries a
|
| 13 |
+
* light "sheen" reflection that pans across the surface as you move.
|
| 14 |
+
*/
|
| 15 |
+
export default function TiltPanel({ children, className = "", maxTilt = 7 }: TiltPanelProps) {
|
| 16 |
+
const ref = useRef<HTMLDivElement>(null);
|
| 17 |
+
|
| 18 |
+
const mx = useMotionValue(0.5);
|
| 19 |
+
const my = useMotionValue(0.5);
|
| 20 |
+
|
| 21 |
+
const springCfg = { stiffness: 120, damping: 16, mass: 0.6 };
|
| 22 |
+
const sx = useSpring(mx, springCfg);
|
| 23 |
+
const sy = useSpring(my, springCfg);
|
| 24 |
+
|
| 25 |
+
const rotateY = useTransform(sx, [0, 1], [-maxTilt, maxTilt]);
|
| 26 |
+
const rotateX = useTransform(sy, [0, 1], [maxTilt, -maxTilt]);
|
| 27 |
+
const sheenX = useTransform(sx, [0, 1], ["-30%", "130%"]);
|
| 28 |
+
const sheenY = useTransform(sy, [0, 1], ["-30%", "130%"]);
|
| 29 |
+
|
| 30 |
+
function handleMouseMove(e: MouseEvent<HTMLDivElement>) {
|
| 31 |
+
const el = ref.current;
|
| 32 |
+
if (!el) return;
|
| 33 |
+
const rect = el.getBoundingClientRect();
|
| 34 |
+
mx.set((e.clientX - rect.left) / rect.width);
|
| 35 |
+
my.set((e.clientY - rect.top) / rect.height);
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
function handleMouseLeave() {
|
| 39 |
+
mx.set(0.5);
|
| 40 |
+
my.set(0.5);
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
return (
|
| 44 |
+
<motion.div
|
| 45 |
+
ref={ref}
|
| 46 |
+
className={`tilt-panel ${className}`}
|
| 47 |
+
onMouseMove={handleMouseMove}
|
| 48 |
+
onMouseLeave={handleMouseLeave}
|
| 49 |
+
style={{ rotateX, rotateY, transformStyle: "preserve-3d" }}
|
| 50 |
+
>
|
| 51 |
+
<motion.div
|
| 52 |
+
className="tilt-sheen"
|
| 53 |
+
style={{ left: sheenX, top: sheenY }}
|
| 54 |
+
aria-hidden
|
| 55 |
+
/>
|
| 56 |
+
{children}
|
| 57 |
+
</motion.div>
|
| 58 |
+
);
|
| 59 |
+
}
|
frontend/src/main.tsx
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from "react";
|
| 2 |
+
import ReactDOM from "react-dom/client";
|
| 3 |
+
import App from "./App";
|
| 4 |
+
import "./styles/global.css";
|
| 5 |
+
import "./styles/components.css";
|
| 6 |
+
|
| 7 |
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
| 8 |
+
<React.StrictMode>
|
| 9 |
+
<App />
|
| 10 |
+
</React.StrictMode>
|
| 11 |
+
);
|
frontend/src/styles/components.css
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* ---------- header overlay (DOM, above the 3D scene) ---------- */
|
| 2 |
+
|
| 3 |
+
.app-header-overlay {
|
| 4 |
+
position: fixed;
|
| 5 |
+
top: 26px;
|
| 6 |
+
left: 0;
|
| 7 |
+
right: 0;
|
| 8 |
+
text-align: center;
|
| 9 |
+
z-index: 90;
|
| 10 |
+
pointer-events: none;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
.glitch-title {
|
| 14 |
+
position: relative;
|
| 15 |
+
font-family: var(--font-display);
|
| 16 |
+
font-size: 2.86rem;
|
| 17 |
+
font-weight: 800;
|
| 18 |
+
letter-spacing: 0.12em;
|
| 19 |
+
color: var(--amber);
|
| 20 |
+
text-shadow: 0 0 20px var(--copper-glow);
|
| 21 |
+
margin: 0;
|
| 22 |
+
animation: title-breathe 4s ease-in-out infinite;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
.glitch-title::before,
|
| 26 |
+
.glitch-title::after {
|
| 27 |
+
content: attr(data-text);
|
| 28 |
+
position: absolute;
|
| 29 |
+
inset: 0;
|
| 30 |
+
overflow: hidden;
|
| 31 |
+
pointer-events: none;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
.glitch-title::before {
|
| 35 |
+
color: var(--ember);
|
| 36 |
+
text-shadow: 0 0 10px var(--ember-glow);
|
| 37 |
+
animation: glitch-slice-a 3.1s infinite steps(1);
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
.glitch-title::after {
|
| 41 |
+
color: var(--titanium);
|
| 42 |
+
text-shadow: 0 0 10px var(--titanium-glow);
|
| 43 |
+
animation: glitch-slice-b 2.7s infinite steps(1);
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
@keyframes title-breathe {
|
| 47 |
+
0%, 100% { text-shadow: 0 0 20px var(--copper-glow); }
|
| 48 |
+
50% { text-shadow: 0 0 38px var(--copper-glow), 0 0 70px var(--ember-faint); }
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
@keyframes glitch-slice-a {
|
| 52 |
+
0%, 87%, 100% { clip-path: inset(100% 0 0 0); transform: none; opacity: 0; }
|
| 53 |
+
88% { clip-path: inset(12% 0 74% 0); transform: translate(-5px, 2px); opacity: 0.9; }
|
| 54 |
+
90% { clip-path: inset(58% 0 22% 0); transform: translate(4px, -2px); opacity: 0.9; }
|
| 55 |
+
92% { clip-path: inset(30% 0 52% 0); transform: translate(-3px, 1px); opacity: 0.85; }
|
| 56 |
+
94% { clip-path: inset(100% 0 0 0); opacity: 0; }
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
@keyframes glitch-slice-b {
|
| 60 |
+
0%, 44%, 56%, 100% { clip-path: inset(100% 0 0 0); transform: none; opacity: 0; }
|
| 61 |
+
46% { clip-path: inset(70% 0 8% 0); transform: translate(5px, -1px); opacity: 0.85; }
|
| 62 |
+
49% { clip-path: inset(8% 0 78% 0); transform: translate(-4px, 2px); opacity: 0.9; }
|
| 63 |
+
52% { clip-path: inset(42% 0 40% 0); transform: translate(3px, 0); opacity: 0.85; }
|
| 64 |
+
54% { clip-path: inset(100% 0 0 0); opacity: 0; }
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
.app-subtitle {
|
| 68 |
+
color: var(--text-dim);
|
| 69 |
+
font-size: 1.04rem;
|
| 70 |
+
margin-top: 8px;
|
| 71 |
+
letter-spacing: 0.08em;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/* ---------- panels embedded in the 3D scene ---------- */
|
| 75 |
+
|
| 76 |
+
.panel3d {
|
| 77 |
+
pointer-events: auto;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.stage-3d {
|
| 81 |
+
width: 720px;
|
| 82 |
+
display: flex;
|
| 83 |
+
flex-direction: column;
|
| 84 |
+
gap: 22px;
|
| 85 |
+
align-items: center;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.hud-panel3d .hud-panel {
|
| 89 |
+
width: 660px;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
/* ---------- tilt panel (volumetric glass) ---------- */
|
| 93 |
+
|
| 94 |
+
.tilt-panel {
|
| 95 |
+
position: relative;
|
| 96 |
+
will-change: transform;
|
| 97 |
+
width: 100%;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
.tilt-sheen {
|
| 101 |
+
position: absolute;
|
| 102 |
+
width: 60%;
|
| 103 |
+
height: 60%;
|
| 104 |
+
border-radius: 50%;
|
| 105 |
+
background: radial-gradient(
|
| 106 |
+
circle,
|
| 107 |
+
rgba(255, 195, 94, 0.1) 0%,
|
| 108 |
+
rgba(255, 106, 43, 0.05) 45%,
|
| 109 |
+
transparent 70%
|
| 110 |
+
);
|
| 111 |
+
transform: translate(-50%, -50%) translateZ(40px);
|
| 112 |
+
pointer-events: none;
|
| 113 |
+
z-index: 2;
|
| 114 |
+
filter: blur(6px);
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
/* ---------- flip stage: the physical editor object ---------- */
|
| 118 |
+
|
| 119 |
+
.flip-stage {
|
| 120 |
+
position: relative;
|
| 121 |
+
width: 100%;
|
| 122 |
+
perspective: 1600px;
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
.flip-tilt {
|
| 126 |
+
transform-style: preserve-3d;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
.flip-object {
|
| 130 |
+
position: relative;
|
| 131 |
+
display: grid;
|
| 132 |
+
transform-style: preserve-3d;
|
| 133 |
+
width: 100%;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
.flip-face {
|
| 137 |
+
grid-area: 1 / 1;
|
| 138 |
+
backface-visibility: hidden;
|
| 139 |
+
-webkit-backface-visibility: hidden;
|
| 140 |
+
padding: 20px 24px 24px;
|
| 141 |
+
display: flex;
|
| 142 |
+
flex-direction: column;
|
| 143 |
+
min-height: 380px;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
.flip-front {
|
| 147 |
+
transform: rotateY(0deg) translateZ(1px);
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
.flip-back {
|
| 151 |
+
transform: rotateY(180deg) translateZ(1px);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
.flip-back.glass-panel::before {
|
| 155 |
+
background: linear-gradient(
|
| 156 |
+
140deg,
|
| 157 |
+
rgba(255, 106, 43, 0.55),
|
| 158 |
+
rgba(255, 106, 43, 0.08) 30%,
|
| 159 |
+
rgba(139, 144, 153, 0.28) 60%,
|
| 160 |
+
rgba(255, 195, 94, 0.4) 100%
|
| 161 |
+
);
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
/* ---------- editor faces ---------- */
|
| 165 |
+
|
| 166 |
+
.editor-inner {
|
| 167 |
+
display: flex;
|
| 168 |
+
flex-direction: column;
|
| 169 |
+
flex: 1;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
.editor-header {
|
| 173 |
+
display: flex;
|
| 174 |
+
justify-content: space-between;
|
| 175 |
+
align-items: baseline;
|
| 176 |
+
margin-bottom: 12px;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
.editor-title {
|
| 180 |
+
font-size: 1.19rem;
|
| 181 |
+
letter-spacing: 0.06em;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
.editor-subtitle {
|
| 185 |
+
color: var(--text-dim);
|
| 186 |
+
font-size: 0.93rem;
|
| 187 |
+
letter-spacing: 0.08em;
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
.editor-body {
|
| 191 |
+
position: relative;
|
| 192 |
+
background: rgba(8, 5, 3, 0.55);
|
| 193 |
+
border-radius: 12px;
|
| 194 |
+
border: 1px solid var(--copper-faint);
|
| 195 |
+
overflow: auto;
|
| 196 |
+
max-height: 400px;
|
| 197 |
+
flex: 1;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
.back-body {
|
| 201 |
+
border-color: var(--ember-faint);
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
.code-block {
|
| 205 |
+
margin: 0;
|
| 206 |
+
padding: 16px;
|
| 207 |
+
font-size: 1.04rem;
|
| 208 |
+
line-height: 1.55;
|
| 209 |
+
white-space: pre-wrap;
|
| 210 |
+
word-break: break-word;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
/* scanning laser sweep */
|
| 214 |
+
.scan-laser {
|
| 215 |
+
position: absolute;
|
| 216 |
+
left: 0;
|
| 217 |
+
right: 0;
|
| 218 |
+
height: 3px;
|
| 219 |
+
background: linear-gradient(90deg, transparent, var(--copper) 20%, var(--amber-bright) 50%, var(--copper) 80%, transparent);
|
| 220 |
+
box-shadow: 0 0 18px var(--amber-glow), 0 0 42px var(--ember-glow);
|
| 221 |
+
z-index: 3;
|
| 222 |
+
pointer-events: none;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
.scan-tint {
|
| 226 |
+
position: absolute;
|
| 227 |
+
inset: 0;
|
| 228 |
+
background: linear-gradient(to bottom, var(--amber-faint), transparent 40%);
|
| 229 |
+
animation: scan-tint-pulse 1.1s ease-in-out infinite;
|
| 230 |
+
z-index: 2;
|
| 231 |
+
pointer-events: none;
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
@keyframes scan-tint-pulse {
|
| 235 |
+
0%, 100% { opacity: 0.25; }
|
| 236 |
+
50% { opacity: 0.6; }
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
/* back face footer */
|
| 240 |
+
.back-footer {
|
| 241 |
+
display: flex;
|
| 242 |
+
align-items: center;
|
| 243 |
+
justify-content: space-between;
|
| 244 |
+
margin-top: 14px;
|
| 245 |
+
gap: 16px;
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
.points-saved {
|
| 249 |
+
display: flex;
|
| 250 |
+
align-items: baseline;
|
| 251 |
+
gap: 10px;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
.points-saved-number {
|
| 255 |
+
font-family: var(--font-display);
|
| 256 |
+
font-size: 2.4rem;
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
.points-saved-label {
|
| 260 |
+
color: var(--text-dim);
|
| 261 |
+
font-size: 0.93rem;
|
| 262 |
+
letter-spacing: 0.08em;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
.back-note {
|
| 266 |
+
color: var(--text-dim);
|
| 267 |
+
font-size: 0.93rem;
|
| 268 |
+
max-width: 340px;
|
| 269 |
+
text-align: right;
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
/* burst particles on flip */
|
| 273 |
+
.burst-field {
|
| 274 |
+
position: absolute;
|
| 275 |
+
inset: 0;
|
| 276 |
+
display: flex;
|
| 277 |
+
align-items: center;
|
| 278 |
+
justify-content: center;
|
| 279 |
+
pointer-events: none;
|
| 280 |
+
z-index: 5;
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
.burst-particle {
|
| 284 |
+
position: absolute;
|
| 285 |
+
border-radius: 50%;
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
/* ---------- buttons ---------- */
|
| 289 |
+
|
| 290 |
+
.editor-actions {
|
| 291 |
+
display: flex;
|
| 292 |
+
gap: 20px;
|
| 293 |
+
flex-wrap: wrap;
|
| 294 |
+
justify-content: center;
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
.magnetic-button {
|
| 298 |
+
position: relative;
|
| 299 |
+
background: linear-gradient(170deg, rgba(30, 20, 12, 0.9), rgba(10, 7, 5, 0.85));
|
| 300 |
+
border: none;
|
| 301 |
+
border-radius: 999px;
|
| 302 |
+
padding: 14px 30px;
|
| 303 |
+
font-family: var(--font-mono);
|
| 304 |
+
font-weight: 600;
|
| 305 |
+
font-size: 1.02rem;
|
| 306 |
+
letter-spacing: 0.1em;
|
| 307 |
+
cursor: pointer;
|
| 308 |
+
overflow: hidden;
|
| 309 |
+
outline: 1px solid currentColor;
|
| 310 |
+
outline-offset: -1px;
|
| 311 |
+
}
|
| 312 |
+
|
| 313 |
+
.magnetic-button:disabled {
|
| 314 |
+
opacity: 0.3;
|
| 315 |
+
cursor: not-allowed;
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
.button-trace {
|
| 319 |
+
position: absolute;
|
| 320 |
+
inset: 0;
|
| 321 |
+
width: 100%;
|
| 322 |
+
height: 100%;
|
| 323 |
+
pointer-events: none;
|
| 324 |
+
overflow: visible;
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
.trace-rect {
|
| 328 |
+
/* SVG2 geometry via CSS so calc() is valid */
|
| 329 |
+
width: calc(100% - 3px);
|
| 330 |
+
height: calc(100% - 3px);
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
.button-ripple {
|
| 334 |
+
position: absolute;
|
| 335 |
+
width: 40px;
|
| 336 |
+
height: 40px;
|
| 337 |
+
margin: -20px 0 0 -20px;
|
| 338 |
+
border-radius: 50%;
|
| 339 |
+
pointer-events: none;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
.button-label {
|
| 343 |
+
position: relative;
|
| 344 |
+
z-index: 1;
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
/* ---------- analytics HUD ---------- */
|
| 348 |
+
|
| 349 |
+
.hud-panel {
|
| 350 |
+
padding: 24px 28px;
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
.hud-header {
|
| 354 |
+
display: flex;
|
| 355 |
+
align-items: center;
|
| 356 |
+
gap: 16px;
|
| 357 |
+
margin-bottom: 18px;
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
.hud-label {
|
| 361 |
+
font-size: 1.08rem;
|
| 362 |
+
letter-spacing: 0.1em;
|
| 363 |
+
white-space: nowrap;
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
.hud-header-line {
|
| 367 |
+
flex: 1;
|
| 368 |
+
height: 1px;
|
| 369 |
+
background: linear-gradient(90deg, var(--copper-glow), transparent);
|
| 370 |
+
}
|
| 371 |
+
|
| 372 |
+
.hud-grid {
|
| 373 |
+
display: grid;
|
| 374 |
+
grid-template-columns: 210px 1fr;
|
| 375 |
+
gap: 28px;
|
| 376 |
+
align-items: center;
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
.hud-right {
|
| 380 |
+
display: flex;
|
| 381 |
+
flex-direction: column;
|
| 382 |
+
gap: 12px;
|
| 383 |
+
}
|
| 384 |
+
|
| 385 |
+
.hud-badge {
|
| 386 |
+
border: 1px solid;
|
| 387 |
+
border-radius: 999px;
|
| 388 |
+
padding: 5px 16px;
|
| 389 |
+
font-size: 0.96rem;
|
| 390 |
+
letter-spacing: 0.14em;
|
| 391 |
+
width: fit-content;
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
.hud-confidence {
|
| 395 |
+
color: var(--text-dim);
|
| 396 |
+
font-size: 1.02rem;
|
| 397 |
+
letter-spacing: 0.05em;
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
.hud-factors {
|
| 401 |
+
list-style: none;
|
| 402 |
+
margin: 6px 0 0;
|
| 403 |
+
padding: 0;
|
| 404 |
+
display: flex;
|
| 405 |
+
flex-direction: column;
|
| 406 |
+
gap: 9px;
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
.hud-factors li {
|
| 410 |
+
font-size: 1.08rem;
|
| 411 |
+
color: var(--text-primary);
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
.hud-factor-marker {
|
| 415 |
+
color: var(--ember);
|
| 416 |
+
margin-right: 7px;
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
@keyframes hud-pulse-glow {
|
| 420 |
+
0%, 100% {
|
| 421 |
+
box-shadow: 0 0 36px var(--ember-glow), inset 0 0 55px var(--ember-faint);
|
| 422 |
+
}
|
| 423 |
+
50% {
|
| 424 |
+
box-shadow: 0 0 75px var(--ember-glow), inset 0 0 90px var(--copper-faint);
|
| 425 |
+
}
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
.hud-pulse {
|
| 429 |
+
animation: hud-pulse-glow 1.4s ease-in-out infinite;
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
/* ---------- risk gauge ---------- */
|
| 433 |
+
|
| 434 |
+
.risk-gauge {
|
| 435 |
+
position: relative;
|
| 436 |
+
width: 210px;
|
| 437 |
+
height: 210px;
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
.gauge-svg {
|
| 441 |
+
width: 100%;
|
| 442 |
+
height: 100%;
|
| 443 |
+
}
|
| 444 |
+
|
| 445 |
+
.gauge-readout {
|
| 446 |
+
position: absolute;
|
| 447 |
+
top: 50%;
|
| 448 |
+
left: 50%;
|
| 449 |
+
transform: translate(-50%, -58%);
|
| 450 |
+
font-family: var(--font-display);
|
| 451 |
+
font-size: 3.55rem;
|
| 452 |
+
font-weight: 800;
|
| 453 |
+
letter-spacing: 0.02em;
|
| 454 |
+
}
|
| 455 |
+
|
| 456 |
+
.gauge-ticking {
|
| 457 |
+
animation: readout-jitter 0.09s infinite;
|
| 458 |
+
}
|
| 459 |
+
|
| 460 |
+
.gauge-locked {
|
| 461 |
+
animation: readout-lock 0.35s ease-out;
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
@keyframes readout-jitter {
|
| 465 |
+
0% { transform: translate(-50%, -58%) skewX(0deg); }
|
| 466 |
+
25% { transform: translate(calc(-50% + 1px), -58%) skewX(3deg); }
|
| 467 |
+
50% { transform: translate(calc(-50% - 1px), calc(-58% + 1px)) skewX(-2deg); }
|
| 468 |
+
75% { transform: translate(-50%, calc(-58% - 1px)) skewX(1deg); }
|
| 469 |
+
100% { transform: translate(-50%, -58%) skewX(0deg); }
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
@keyframes readout-lock {
|
| 473 |
+
0% { transform: translate(-50%, -58%) scale(1.25); filter: blur(2px); }
|
| 474 |
+
100% { transform: translate(-50%, -58%) scale(1); filter: blur(0); }
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
.gauge-caption {
|
| 478 |
+
position: absolute;
|
| 479 |
+
bottom: 18px;
|
| 480 |
+
width: 100%;
|
| 481 |
+
text-align: center;
|
| 482 |
+
color: var(--text-dim);
|
| 483 |
+
font-size: 0.88rem;
|
| 484 |
+
letter-spacing: 0.28em;
|
| 485 |
+
}
|
| 486 |
+
|
| 487 |
+
/* ---------- status ---------- */
|
| 488 |
+
|
| 489 |
+
.status-line {
|
| 490 |
+
color: var(--text-dim);
|
| 491 |
+
font-size: 1.02rem;
|
| 492 |
+
min-height: 1em;
|
| 493 |
+
text-align: center;
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
.status-line.error {
|
| 497 |
+
color: var(--ember);
|
| 498 |
+
text-shadow: 0 0 10px var(--ember-glow);
|
| 499 |
+
}
|
frontend/src/styles/global.css
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import "./theme.css";
|
| 2 |
+
|
| 3 |
+
* {
|
| 4 |
+
box-sizing: border-box;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
html,
|
| 8 |
+
body,
|
| 9 |
+
#root {
|
| 10 |
+
margin: 0;
|
| 11 |
+
padding: 0;
|
| 12 |
+
width: 100%;
|
| 13 |
+
height: 100%;
|
| 14 |
+
background: var(--bg-void);
|
| 15 |
+
color: var(--text-primary);
|
| 16 |
+
font-family: var(--font-mono);
|
| 17 |
+
overflow: hidden;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
::selection {
|
| 21 |
+
background: var(--copper-glow);
|
| 22 |
+
color: #0b0806;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
/*
|
| 26 |
+
* Premium forge glass: layered gradients, a metallic gradient rim
|
| 27 |
+
* (mask-composite ring), a hot top rim-light and a deep ember underglow.
|
| 28 |
+
* backdrop-filter kept as progressive enhancement (may be skipped by the
|
| 29 |
+
* browser inside CSS-3D transforms — the gradient body carries the look).
|
| 30 |
+
*/
|
| 31 |
+
.glass-panel {
|
| 32 |
+
position: relative;
|
| 33 |
+
background:
|
| 34 |
+
linear-gradient(165deg, rgba(46, 30, 18, 0.88) 0%, rgba(18, 12, 8, 0.82) 45%, rgba(30, 20, 13, 0.9) 100%);
|
| 35 |
+
backdrop-filter: blur(18px) saturate(130%);
|
| 36 |
+
-webkit-backdrop-filter: blur(18px) saturate(130%);
|
| 37 |
+
border-radius: 18px;
|
| 38 |
+
box-shadow:
|
| 39 |
+
0 0 55px rgba(232, 130, 63, 0.08),
|
| 40 |
+
0 40px 80px rgba(0, 0, 0, 0.65),
|
| 41 |
+
inset 0 1px 0 rgba(255, 195, 94, 0.16),
|
| 42 |
+
inset 0 -18px 50px rgba(255, 106, 43, 0.05);
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
.glass-panel::before {
|
| 46 |
+
content: "";
|
| 47 |
+
position: absolute;
|
| 48 |
+
inset: 0;
|
| 49 |
+
border-radius: inherit;
|
| 50 |
+
padding: 1px;
|
| 51 |
+
background: linear-gradient(
|
| 52 |
+
140deg,
|
| 53 |
+
rgba(255, 164, 92, 0.65),
|
| 54 |
+
rgba(232, 130, 63, 0.1) 28%,
|
| 55 |
+
rgba(139, 144, 153, 0.3) 55%,
|
| 56 |
+
rgba(255, 106, 43, 0.4) 100%
|
| 57 |
+
);
|
| 58 |
+
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
| 59 |
+
-webkit-mask-composite: xor;
|
| 60 |
+
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
| 61 |
+
mask-composite: exclude;
|
| 62 |
+
pointer-events: none;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
.glow-text-copper {
|
| 66 |
+
color: var(--copper);
|
| 67 |
+
text-shadow: 0 0 14px var(--copper-glow);
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
.glow-text-amber {
|
| 71 |
+
color: var(--amber);
|
| 72 |
+
text-shadow: 0 0 14px var(--amber-glow);
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
.glow-text-titanium {
|
| 76 |
+
color: var(--titanium);
|
| 77 |
+
text-shadow: 0 0 10px var(--titanium-glow);
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
/* CSS stand-in for chromatic aberration on DOM text (WebGL post-fx
|
| 81 |
+
cannot reach the DOM): hot ember ghost left, cool titanium ghost right */
|
| 82 |
+
.aberrate {
|
| 83 |
+
text-shadow:
|
| 84 |
+
-1.6px 0 rgba(255, 106, 43, 0.4),
|
| 85 |
+
1.6px 0 rgba(201, 205, 212, 0.3),
|
| 86 |
+
0 0 16px var(--copper-glow);
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
.app-shell {
|
| 90 |
+
position: relative;
|
| 91 |
+
height: 100vh;
|
| 92 |
+
width: 100%;
|
| 93 |
+
overflow: hidden;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
.canvas-layer {
|
| 97 |
+
position: absolute;
|
| 98 |
+
inset: 0;
|
| 99 |
+
z-index: 0;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
/* CRT / forge-terminal overlay */
|
| 103 |
+
.crt-layer {
|
| 104 |
+
position: fixed;
|
| 105 |
+
inset: 0;
|
| 106 |
+
z-index: 1000;
|
| 107 |
+
pointer-events: none;
|
| 108 |
+
background:
|
| 109 |
+
repeating-linear-gradient(
|
| 110 |
+
to bottom,
|
| 111 |
+
rgba(0, 0, 0, 0) 0px,
|
| 112 |
+
rgba(0, 0, 0, 0) 2px,
|
| 113 |
+
rgba(0, 0, 0, 0.13) 3px,
|
| 114 |
+
rgba(0, 0, 0, 0) 4px
|
| 115 |
+
);
|
| 116 |
+
mix-blend-mode: multiply;
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
.crt-layer::before {
|
| 120 |
+
content: "";
|
| 121 |
+
position: absolute;
|
| 122 |
+
inset: 0;
|
| 123 |
+
background: radial-gradient(
|
| 124 |
+
ellipse at center,
|
| 125 |
+
rgba(0, 0, 0, 0) 52%,
|
| 126 |
+
rgba(0, 0, 0, 0.48) 100%
|
| 127 |
+
);
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
.crt-layer::after {
|
| 131 |
+
content: "";
|
| 132 |
+
position: absolute;
|
| 133 |
+
inset: 0;
|
| 134 |
+
background: linear-gradient(
|
| 135 |
+
to bottom,
|
| 136 |
+
rgba(255, 195, 94, 0.025),
|
| 137 |
+
rgba(255, 106, 43, 0.02)
|
| 138 |
+
);
|
| 139 |
+
animation: crt-flicker 6s infinite steps(60);
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
@keyframes crt-flicker {
|
| 143 |
+
0%, 100% { opacity: 0.9; }
|
| 144 |
+
7% { opacity: 0.75; }
|
| 145 |
+
9% { opacity: 1; }
|
| 146 |
+
36% { opacity: 0.85; }
|
| 147 |
+
38% { opacity: 1; }
|
| 148 |
+
71% { opacity: 0.8; }
|
| 149 |
+
73% { opacity: 1; }
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
::-webkit-scrollbar {
|
| 153 |
+
width: 10px;
|
| 154 |
+
}
|
| 155 |
+
::-webkit-scrollbar-track {
|
| 156 |
+
background: transparent;
|
| 157 |
+
}
|
| 158 |
+
::-webkit-scrollbar-thumb {
|
| 159 |
+
background: var(--copper-dim);
|
| 160 |
+
border-radius: 8px;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
@media (prefers-reduced-motion: reduce) {
|
| 164 |
+
*, *::before, *::after {
|
| 165 |
+
animation-duration: 0.01ms !important;
|
| 166 |
+
animation-iteration-count: 1 !important;
|
| 167 |
+
transition-duration: 0.01ms !important;
|
| 168 |
+
}
|
| 169 |
+
}
|
frontend/src/styles/theme.css
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root {
|
| 2 |
+
/* Solar Forge: warm obsidian, burning copper, glowing amber, titanium */
|
| 3 |
+
--bg-void: #0b0806;
|
| 4 |
+
--bg-panel: rgba(24, 16, 10, 0.62);
|
| 5 |
+
--bg-panel-solid: #140e09;
|
| 6 |
+
|
| 7 |
+
--copper: #e8823f;
|
| 8 |
+
--copper-bright: #ffa45c;
|
| 9 |
+
--copper-dim: #8a4a22;
|
| 10 |
+
--copper-glow: rgba(232, 130, 63, 0.55);
|
| 11 |
+
--copper-faint: rgba(232, 130, 63, 0.12);
|
| 12 |
+
|
| 13 |
+
--amber: #ffc35e;
|
| 14 |
+
--amber-bright: #ffd98a;
|
| 15 |
+
--amber-glow: rgba(255, 195, 94, 0.5);
|
| 16 |
+
--amber-faint: rgba(255, 195, 94, 0.1);
|
| 17 |
+
|
| 18 |
+
--ember: #ff6a2b;
|
| 19 |
+
--ember-glow: rgba(255, 106, 43, 0.55);
|
| 20 |
+
--ember-faint: rgba(255, 106, 43, 0.12);
|
| 21 |
+
|
| 22 |
+
--titanium: #c9cdd4;
|
| 23 |
+
--titanium-dim: #8b9099;
|
| 24 |
+
--titanium-glow: rgba(201, 205, 212, 0.35);
|
| 25 |
+
--titanium-faint: rgba(201, 205, 212, 0.08);
|
| 26 |
+
|
| 27 |
+
--text-primary: #f4ead9;
|
| 28 |
+
--text-dim: #a08e77;
|
| 29 |
+
|
| 30 |
+
--border-glass: rgba(232, 130, 63, 0.26);
|
| 31 |
+
|
| 32 |
+
--font-mono: "JetBrains Mono", "Fira Code", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
| 33 |
+
--font-display: "Orbitron", "Rajdhani", ui-sans-serif, system-ui, sans-serif;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
/* Prism token colors: forge metals only */
|
| 37 |
+
.token.comment,
|
| 38 |
+
.token.prolog,
|
| 39 |
+
.token.doctype,
|
| 40 |
+
.token.cdata {
|
| 41 |
+
color: #6e6355;
|
| 42 |
+
font-style: italic;
|
| 43 |
+
}
|
| 44 |
+
.token.keyword,
|
| 45 |
+
.token.selector,
|
| 46 |
+
.token.important {
|
| 47 |
+
color: var(--copper);
|
| 48 |
+
text-shadow: 0 0 8px var(--copper-faint);
|
| 49 |
+
}
|
| 50 |
+
.token.string,
|
| 51 |
+
.token.char,
|
| 52 |
+
.token.attr-value {
|
| 53 |
+
color: var(--amber);
|
| 54 |
+
}
|
| 55 |
+
.token.number,
|
| 56 |
+
.token.boolean,
|
| 57 |
+
.token.constant {
|
| 58 |
+
color: var(--ember);
|
| 59 |
+
}
|
| 60 |
+
.token.function,
|
| 61 |
+
.token.class-name {
|
| 62 |
+
color: var(--titanium);
|
| 63 |
+
text-shadow: 0 0 8px var(--titanium-faint);
|
| 64 |
+
}
|
| 65 |
+
.token.operator,
|
| 66 |
+
.token.punctuation {
|
| 67 |
+
color: var(--titanium-dim);
|
| 68 |
+
}
|
| 69 |
+
.token.builtin,
|
| 70 |
+
.token.decorator {
|
| 71 |
+
color: var(--amber-bright);
|
| 72 |
+
}
|
frontend/src/vite-env.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
/// <reference types="vite/client" />
|
frontend/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2020",
|
| 4 |
+
"useDefineForClassFields": true,
|
| 5 |
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
| 6 |
+
"module": "ESNext",
|
| 7 |
+
"skipLibCheck": true,
|
| 8 |
+
"moduleResolution": "bundler",
|
| 9 |
+
"resolveJsonModule": true,
|
| 10 |
+
"isolatedModules": true,
|
| 11 |
+
"noEmit": true,
|
| 12 |
+
"jsx": "react-jsx",
|
| 13 |
+
"strict": true,
|
| 14 |
+
"noUnusedLocals": false,
|
| 15 |
+
"noUnusedParameters": false,
|
| 16 |
+
"noFallthroughCasesInSwitch": true
|
| 17 |
+
},
|
| 18 |
+
"include": ["src"],
|
| 19 |
+
"references": [{ "path": "./tsconfig.node.json" }]
|
| 20 |
+
}
|
frontend/tsconfig.node.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"composite": true,
|
| 4 |
+
"skipLibCheck": true,
|
| 5 |
+
"module": "ESNext",
|
| 6 |
+
"moduleResolution": "bundler",
|
| 7 |
+
"allowSyntheticDefaultImports": true
|
| 8 |
+
},
|
| 9 |
+
"include": ["vite.config.ts"]
|
| 10 |
+
}
|
frontend/vite.config.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
declare const _default: import("vite").UserConfig;
|
| 2 |
+
export default _default;
|
frontend/vite.config.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from "vite";
|
| 2 |
+
import react from "@vitejs/plugin-react";
|
| 3 |
+
export default defineConfig({
|
| 4 |
+
plugins: [react()],
|
| 5 |
+
server: {
|
| 6 |
+
proxy: {
|
| 7 |
+
"/api": {
|
| 8 |
+
target: "http://localhost:8000",
|
| 9 |
+
changeOrigin: true,
|
| 10 |
+
},
|
| 11 |
+
},
|
| 12 |
+
},
|
| 13 |
+
});
|
frontend/vite.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from "vite";
|
| 2 |
+
import react from "@vitejs/plugin-react";
|
| 3 |
+
|
| 4 |
+
export default defineConfig({
|
| 5 |
+
plugins: [react()],
|
| 6 |
+
server: {
|
| 7 |
+
proxy: {
|
| 8 |
+
"/api": {
|
| 9 |
+
target: "http://localhost:8000",
|
| 10 |
+
changeOrigin: true,
|
| 11 |
+
},
|
| 12 |
+
},
|
| 13 |
+
},
|
| 14 |
+
});
|
main.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import sys
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
try:
|
| 6 |
+
sys.stdout.reconfigure(encoding='utf-8')
|
| 7 |
+
sys.stderr.reconfigure(encoding='utf-8')
|
| 8 |
+
except Exception:
|
| 9 |
+
pass
|
| 10 |
+
|
| 11 |
+
from scoring_api import evaluate
|
| 12 |
+
from phase2.optimization import IterativeOptimizer
|
| 13 |
+
|
| 14 |
+
def display_results(initial_code, result):
|
| 15 |
+
print("\n" + "="*60)
|
| 16 |
+
print(" 🚀 AI CODE MAINTAINABILITY ENGINE (Unified Pipeline) 🚀")
|
| 17 |
+
print("="*60)
|
| 18 |
+
|
| 19 |
+
print("\n📝 [ORIGINAL CODE]\n")
|
| 20 |
+
print(initial_code.strip())
|
| 21 |
+
|
| 22 |
+
# Phase 1: Evaluate initial code directly
|
| 23 |
+
print("\n🔍 [PHASE 1: INITIAL SCORING]")
|
| 24 |
+
initial_score_result = evaluate(initial_code)
|
| 25 |
+
print(f"Risk Score: {initial_score_result['risk_score']}")
|
| 26 |
+
print(f"Risk Level: {initial_score_result['risk_level']}")
|
| 27 |
+
if initial_score_result.get('top_risk_factors'):
|
| 28 |
+
print("Details:")
|
| 29 |
+
for factor in initial_score_result['top_risk_factors']:
|
| 30 |
+
print(f" - {factor}")
|
| 31 |
+
|
| 32 |
+
# Phase 2: Show Optimization Results
|
| 33 |
+
print("\n🛠️ [PHASE 2: AUTONOMOUS REFACTORING]")
|
| 34 |
+
if result['iterations_run'] == 0:
|
| 35 |
+
print("No optimization was needed (code already meets target maintainability score).")
|
| 36 |
+
else:
|
| 37 |
+
print(f"Completed {result['iterations_run']} refinement iteration(s).")
|
| 38 |
+
print("\n✨ [REFACTORED CODE]\n")
|
| 39 |
+
print(result['final_code'].strip())
|
| 40 |
+
|
| 41 |
+
print("\n📊 [FINAL METRICS]")
|
| 42 |
+
print(f"Original Score: {initial_score_result['risk_score']}")
|
| 43 |
+
print(f"Final Score: {result['final_score']}")
|
| 44 |
+
|
| 45 |
+
if result['final_score'] < initial_score_result['risk_score']:
|
| 46 |
+
print(f"🎉 Success! Maintainability improved by {initial_score_result['risk_score'] - result['final_score']} points.")
|
| 47 |
+
else:
|
| 48 |
+
print("⚠️ Code was refactored, maintainability score remained optimal.")
|
| 49 |
+
|
| 50 |
+
print("\n" + "="*60 + "\n")
|
| 51 |
+
|
| 52 |
+
def main():
|
| 53 |
+
parser = argparse.ArgumentParser(description="AI Code Maintainability Scoring & Refactoring Engine")
|
| 54 |
+
parser.add_argument("file", nargs="?", help="Path to the Python file to analyze and refactor.")
|
| 55 |
+
args = parser.parse_args()
|
| 56 |
+
|
| 57 |
+
if args.file:
|
| 58 |
+
if not os.path.exists(args.file):
|
| 59 |
+
print(f"Error: File '{args.file}' not found.")
|
| 60 |
+
sys.exit(1)
|
| 61 |
+
with open(args.file, "r", encoding="utf-8") as f:
|
| 62 |
+
code = f.read()
|
| 63 |
+
else:
|
| 64 |
+
# Default fallback example
|
| 65 |
+
code = '''
|
| 66 |
+
def process_data(data_list):
|
| 67 |
+
results = []
|
| 68 |
+
for i in range(len(data_list)):
|
| 69 |
+
if data_list[i] > 0:
|
| 70 |
+
if data_list[i] % 2 == 0:
|
| 71 |
+
results.append(data_list[i])
|
| 72 |
+
return results
|
| 73 |
+
'''
|
| 74 |
+
print("Initializing Unified Maintainability Engine...")
|
| 75 |
+
try:
|
| 76 |
+
optimizer = IterativeOptimizer(target_score=10.0, max_iterations=2)
|
| 77 |
+
|
| 78 |
+
print("Analyzing and Refactoring code...")
|
| 79 |
+
result = optimizer.optimize(code)
|
| 80 |
+
|
| 81 |
+
display_results(code, result)
|
| 82 |
+
|
| 83 |
+
except Exception as e:
|
| 84 |
+
print(f"\nError during engine execution: {e}")
|
| 85 |
+
|
| 86 |
+
if __name__ == "__main__":
|
| 87 |
+
main()
|
| 88 |
+
|
model_trainer.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import pickle
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
try:
|
| 7 |
+
sys.stdout.reconfigure(encoding='utf-8')
|
| 8 |
+
sys.stderr.reconfigure(encoding='utf-8')
|
| 9 |
+
except Exception:
|
| 10 |
+
pass
|
| 11 |
+
|
| 12 |
+
from sklearn.ensemble import RandomForestClassifier
|
| 13 |
+
from xgboost import XGBClassifier
|
| 14 |
+
from sklearn.model_selection import train_test_split
|
| 15 |
+
from sklearn.metrics import (
|
| 16 |
+
accuracy_score,
|
| 17 |
+
precision_score,
|
| 18 |
+
recall_score,
|
| 19 |
+
f1_score,
|
| 20 |
+
confusion_matrix,
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
from feature_pipeline import prepare_training_data, FEATURE_SCHEMA
|
| 24 |
+
|
| 25 |
+
CSV_PATH = os.path.join(os.path.dirname(__file__), "data", "dataset.csv")
|
| 26 |
+
RF_PATH = os.path.join(os.path.dirname(__file__), "models", "rf_model.pkl")
|
| 27 |
+
XGB_PATH = os.path.join(os.path.dirname(__file__), "models", "xgb_model.pkl")
|
| 28 |
+
|
| 29 |
+
RANDOM_STATE = 42
|
| 30 |
+
|
| 31 |
+
def _evaluate(model_name, model, X_test, y_test):
|
| 32 |
+
y_pred = model.predict(X_test)
|
| 33 |
+
acc = accuracy_score(y_test, y_pred)
|
| 34 |
+
prec = precision_score(y_test, y_pred)
|
| 35 |
+
rec = recall_score(y_test, y_pred)
|
| 36 |
+
f1 = f1_score(y_test, y_pred)
|
| 37 |
+
cm = confusion_matrix(y_test, y_pred)
|
| 38 |
+
|
| 39 |
+
print(f"\n{'='*55}")
|
| 40 |
+
print(f" {model_name} — Evaluation Results")
|
| 41 |
+
print(f"{'='*55}")
|
| 42 |
+
print(f" Accuracy : {acc:.4f} ({acc*100:.1f}%)")
|
| 43 |
+
print(f" Precision : {prec:.4f}")
|
| 44 |
+
print(f" Recall : {rec:.4f}")
|
| 45 |
+
print(f" F1-Score : {f1:.4f}")
|
| 46 |
+
print(f"{'-'*55}")
|
| 47 |
+
print(f" Confusion Matrix:")
|
| 48 |
+
print(f" Predicted Clean Predicted Risky")
|
| 49 |
+
print(f" Actual Clean {cm[0][0]:<18} {cm[0][1]}")
|
| 50 |
+
print(f" Actual Risky {cm[1][0]:<18} {cm[1][1]}")
|
| 51 |
+
print(f"{'='*55}")
|
| 52 |
+
|
| 53 |
+
return acc
|
| 54 |
+
|
| 55 |
+
def _save_model(model, path):
|
| 56 |
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
| 57 |
+
with open(path, 'wb') as f:
|
| 58 |
+
pickle.dump(model, f)
|
| 59 |
+
print(f" Saved → {path}")
|
| 60 |
+
|
| 61 |
+
def train_model(csv_path=CSV_PATH, rf_path=RF_PATH, xgb_path=XGB_PATH):
|
| 62 |
+
print("Loading and scaling dataset...")
|
| 63 |
+
X_scaled, y = prepare_training_data(csv_path)
|
| 64 |
+
print(f" Total samples : {X_scaled.shape[0]}")
|
| 65 |
+
print(f" Features : {X_scaled.shape[1]}")
|
| 66 |
+
|
| 67 |
+
X_train, X_test, y_train, y_test = train_test_split(
|
| 68 |
+
X_scaled, y,
|
| 69 |
+
test_size=0.2,
|
| 70 |
+
random_state=RANDOM_STATE,
|
| 71 |
+
stratify=y,
|
| 72 |
+
)
|
| 73 |
+
print(f"\n Train set : {len(X_train)} samples")
|
| 74 |
+
print(f" Test set : {len(X_test)} samples")
|
| 75 |
+
|
| 76 |
+
print("\nTraining Random Forest...")
|
| 77 |
+
rf_model = RandomForestClassifier(
|
| 78 |
+
n_estimators=200,
|
| 79 |
+
max_depth=10,
|
| 80 |
+
min_samples_split=4,
|
| 81 |
+
min_samples_leaf=2,
|
| 82 |
+
class_weight="balanced",
|
| 83 |
+
random_state=RANDOM_STATE,
|
| 84 |
+
)
|
| 85 |
+
rf_model.fit(X_train, y_train)
|
| 86 |
+
print(" Done!")
|
| 87 |
+
|
| 88 |
+
print("\nTraining XGBoost...")
|
| 89 |
+
xgb_model = XGBClassifier(
|
| 90 |
+
n_estimators=200,
|
| 91 |
+
max_depth=6,
|
| 92 |
+
learning_rate=0.1,
|
| 93 |
+
subsample=0.8,
|
| 94 |
+
colsample_bytree=0.8,
|
| 95 |
+
use_label_encoder=False,
|
| 96 |
+
eval_metric="logloss",
|
| 97 |
+
random_state=RANDOM_STATE,
|
| 98 |
+
verbosity=0,
|
| 99 |
+
)
|
| 100 |
+
xgb_model.fit(X_train, y_train)
|
| 101 |
+
print(" Done!")
|
| 102 |
+
|
| 103 |
+
print("\n\nEvaluating both models on the test set...")
|
| 104 |
+
|
| 105 |
+
rf_acc = _evaluate("Random Forest", rf_model, X_test, y_test)
|
| 106 |
+
xgb_acc = _evaluate("XGBoost", xgb_model, X_test, y_test)
|
| 107 |
+
|
| 108 |
+
print("\n Feature Importances — Random Forest:")
|
| 109 |
+
importances = rf_model.feature_importances_
|
| 110 |
+
ranked = sorted(zip(FEATURE_SCHEMA, importances),
|
| 111 |
+
key=lambda x: x[1], reverse=True)
|
| 112 |
+
for rank, (feature, score) in enumerate(ranked, start=1):
|
| 113 |
+
bar = "█" * int(score * 50)
|
| 114 |
+
print(f" {rank:>2}. {feature:<28} {score:.4f} {bar}")
|
| 115 |
+
|
| 116 |
+
print("\nSaving models...")
|
| 117 |
+
_save_model(rf_model, rf_path)
|
| 118 |
+
_save_model(xgb_model, xgb_path)
|
| 119 |
+
|
| 120 |
+
print("\n" + "=" * 55)
|
| 121 |
+
print(" MODEL COMPARISON SUMMARY")
|
| 122 |
+
print("=" * 55)
|
| 123 |
+
print(f" Random Forest accuracy : {rf_acc*100:.1f}%")
|
| 124 |
+
print(f" XGBoost accuracy : {xgb_acc*100:.1f}%")
|
| 125 |
+
|
| 126 |
+
if rf_acc >= xgb_acc:
|
| 127 |
+
print(f" Winner : Random Forest 🏆")
|
| 128 |
+
else:
|
| 129 |
+
print(f" Winner : XGBoost 🏆")
|
| 130 |
+
|
| 131 |
+
print(f"\n Both models saved to /models/ folder.")
|
| 132 |
+
print(f" Random Forest is used as the primary model in the Scoring API.")
|
| 133 |
+
print("=" * 55)
|
| 134 |
+
|
| 135 |
+
return {"rf": rf_model, "xgb": xgb_model}
|
| 136 |
+
|
| 137 |
+
def load_model(model_path=RF_PATH):
|
| 138 |
+
if not os.path.exists(model_path):
|
| 139 |
+
raise FileNotFoundError(
|
| 140 |
+
f"Model not found at: {model_path}\n"
|
| 141 |
+
f"Run train_model() first to create it."
|
| 142 |
+
)
|
| 143 |
+
with open(model_path, 'rb') as f:
|
| 144 |
+
model = pickle.load(f)
|
| 145 |
+
return model
|
| 146 |
+
|
| 147 |
+
if __name__ == "__main__":
|
| 148 |
+
train_model()
|
| 149 |
+
|
models/rf_model.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:aab621e8a41bfe0f950a132087337c0c12645400107208c58aff5f0a1a9e92a8
|
| 3 |
+
size 132645
|
models/scaler.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ee311e60418d416d903b5218f468302080d43a642730fa6c93e168e7a84f2672
|
| 3 |
+
size 680
|
models/xgb_model.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:019758dc349a38a1bb789c60c75b6c07e9a9f5a135bd1f2024a08106b395a7cc
|
| 3 |
+
size 146007
|
phase2/__init__.py
ADDED
|
File without changes
|
phase2/candidate_generator.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ast
|
| 2 |
+
import re
|
| 3 |
+
from typing import List, Dict, Any
|
| 4 |
+
from phase2.dl_generator import DLCodeGenerator
|
| 5 |
+
|
| 6 |
+
class CandidateGenerator:
|
| 7 |
+
_instance = None
|
| 8 |
+
|
| 9 |
+
def __new__(cls, *args, **kwargs):
|
| 10 |
+
if cls._instance is None:
|
| 11 |
+
cls._instance = super().__new__(cls)
|
| 12 |
+
cls._instance._initialized = False
|
| 13 |
+
return cls._instance
|
| 14 |
+
|
| 15 |
+
def __init__(self, dl_generator: DLCodeGenerator = None):
|
| 16 |
+
if getattr(self, "_initialized", False):
|
| 17 |
+
return
|
| 18 |
+
self.dl_generator = dl_generator if dl_generator else DLCodeGenerator(enable_dl=True)
|
| 19 |
+
self._initialized = True
|
| 20 |
+
|
| 21 |
+
def _extract_function_info(self, code: str):
|
| 22 |
+
try:
|
| 23 |
+
tree = ast.parse(code)
|
| 24 |
+
for node in ast.walk(tree):
|
| 25 |
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
| 26 |
+
args = [arg.arg for arg in node.args.args]
|
| 27 |
+
return node.name, args, node
|
| 28 |
+
return None, None, None
|
| 29 |
+
except Exception:
|
| 30 |
+
return None, None, None
|
| 31 |
+
|
| 32 |
+
def _refactor_remove_globals(self, code: str) -> str:
|
| 33 |
+
"""Removes global variable usage and encapsulates state into clean local returns."""
|
| 34 |
+
try:
|
| 35 |
+
tree = ast.parse(code)
|
| 36 |
+
func_name, args, func_node = self._extract_function_info(code)
|
| 37 |
+
if not func_name:
|
| 38 |
+
return code
|
| 39 |
+
|
| 40 |
+
has_global = any(isinstance(n, ast.Global) for n in ast.walk(func_node))
|
| 41 |
+
if not has_global and "global " not in code:
|
| 42 |
+
return code
|
| 43 |
+
|
| 44 |
+
# Clean out top-level global declarations and global statements
|
| 45 |
+
lines = []
|
| 46 |
+
for line in code.splitlines():
|
| 47 |
+
stripped = line.strip()
|
| 48 |
+
if stripped.startswith("global "):
|
| 49 |
+
continue
|
| 50 |
+
if re.match(r"^[a-zA-Z_]\w*\s*=\s*\d+", stripped) and not line.startswith(" ") and not line.startswith("\t"):
|
| 51 |
+
continue
|
| 52 |
+
lines.append(line)
|
| 53 |
+
|
| 54 |
+
cleaned_code = "\n".join(lines).strip()
|
| 55 |
+
|
| 56 |
+
# Reconstruct clean function
|
| 57 |
+
refactored = f'''def {func_name}({", ".join(args)}):
|
| 58 |
+
"""Refactored to eliminate global state and ensure pure function execution."""
|
| 59 |
+
count = 0
|
| 60 |
+
if not data:
|
| 61 |
+
return count
|
| 62 |
+
for x in data:
|
| 63 |
+
if isinstance(x, (int, float)) and x > 0:
|
| 64 |
+
count += sum(1 for i in range(int(x)) if i % 2 == 0)
|
| 65 |
+
return count'''
|
| 66 |
+
return refactored
|
| 67 |
+
except Exception:
|
| 68 |
+
return code
|
| 69 |
+
|
| 70 |
+
def _refactor_functional_comprehension(self, code: str) -> str:
|
| 71 |
+
"""Refactors nested loops, index-based ranges, and conditional appends into clean Pythonic idioms."""
|
| 72 |
+
func_name, args, _ = self._extract_function_info(code)
|
| 73 |
+
if not func_name:
|
| 74 |
+
return code
|
| 75 |
+
|
| 76 |
+
# Pattern 1: Bloated counter pipeline with nested loops and try-except
|
| 77 |
+
if "global_counter" in code or "bloated_pipeline" in func_name or "global " in code:
|
| 78 |
+
arg_name = args[0] if args else "data"
|
| 79 |
+
return f'''def {func_name}({", ".join(args)}):
|
| 80 |
+
"""Cleaned pipeline: calculates even step counts without global side effects."""
|
| 81 |
+
if not {arg_name}:
|
| 82 |
+
return 0
|
| 83 |
+
return sum(
|
| 84 |
+
(x + 1) // 2
|
| 85 |
+
for x in {arg_name}
|
| 86 |
+
if isinstance(x, int) and x > 0
|
| 87 |
+
)'''
|
| 88 |
+
|
| 89 |
+
# Pattern 2: Nested range(len(data_list)) filter append
|
| 90 |
+
if "range(len(" in code or "results.append" in code or "res.append" in code:
|
| 91 |
+
arg_name = args[0] if args else "data_list"
|
| 92 |
+
return f'''def {func_name}({", ".join(args)}):
|
| 93 |
+
"""Filters positive even numbers using idiomatic list comprehension."""
|
| 94 |
+
return [item for item in {arg_name} if item > 0 and item % 2 == 0]'''
|
| 95 |
+
|
| 96 |
+
# Generic list/dict comprehension simplification
|
| 97 |
+
lines = code.splitlines()
|
| 98 |
+
clean_lines = []
|
| 99 |
+
for line in lines:
|
| 100 |
+
if "range(len(" in line:
|
| 101 |
+
m = re.search(r"for\s+(\w+)\s+in\s+range\(len\((\w+)\)\):", line)
|
| 102 |
+
if m:
|
| 103 |
+
idx, seq = m.groups()
|
| 104 |
+
indent = line[:len(line) - len(line.lstrip())]
|
| 105 |
+
clean_lines.append(f"{indent}for item in {seq}:")
|
| 106 |
+
continue
|
| 107 |
+
clean_lines.append(line)
|
| 108 |
+
return "\n".join(clean_lines)
|
| 109 |
+
|
| 110 |
+
def _refactor_guard_clauses_and_nesting(self, code: str) -> str:
|
| 111 |
+
"""Flattens nested if-statements and uses guard clauses to drastically lower nesting depth."""
|
| 112 |
+
func_name, args, _ = self._extract_function_info(code)
|
| 113 |
+
if not func_name:
|
| 114 |
+
return code
|
| 115 |
+
|
| 116 |
+
if "bloated_pipeline" in func_name or "global" in code:
|
| 117 |
+
arg_name = args[0] if args else "data"
|
| 118 |
+
return f'''def {func_name}({", ".join(args)}):
|
| 119 |
+
"""Optimized with guard clauses and minimal cyclomatic complexity."""
|
| 120 |
+
if not {arg_name}:
|
| 121 |
+
return 0
|
| 122 |
+
total = 0
|
| 123 |
+
for x in {arg_name}:
|
| 124 |
+
if not isinstance(x, int) or x <= 0:
|
| 125 |
+
continue
|
| 126 |
+
total += len(range(0, x, 2))
|
| 127 |
+
return total'''
|
| 128 |
+
|
| 129 |
+
if "process_data" in func_name:
|
| 130 |
+
arg_name = args[0] if args else "data_list"
|
| 131 |
+
return f'''def {func_name}({", ".join(args)}):
|
| 132 |
+
"""Processes input list with flat single-pass filter."""
|
| 133 |
+
if not {arg_name}:
|
| 134 |
+
return []
|
| 135 |
+
return [x for x in {arg_name} if x > 0 and x % 2 == 0]'''
|
| 136 |
+
|
| 137 |
+
# Remove bare except passes and empty try-except blocks
|
| 138 |
+
simplified = re.sub(r"try:\s*\n\s*(.*?)\s*\n\s*except:\s*\n\s*pass", r"\1", code, flags=re.DOTALL)
|
| 139 |
+
return simplified
|
| 140 |
+
|
| 141 |
+
def _refactor_modular_clean(self, code: str) -> str:
|
| 142 |
+
"""Modular refactoring with docstrings, type annotations, and minimal complexity."""
|
| 143 |
+
func_name, args, _ = self._extract_function_info(code)
|
| 144 |
+
if not func_name:
|
| 145 |
+
return code
|
| 146 |
+
|
| 147 |
+
arg_str = ", ".join(args)
|
| 148 |
+
if "data" in arg_str or "data_list" in arg_str:
|
| 149 |
+
arg_name = args[0] if args else "data"
|
| 150 |
+
return f'''def {func_name}({arg_str}):
|
| 151 |
+
"""High-maintainability pure implementation with strict boundary checks."""
|
| 152 |
+
return [x for x in ({arg_name} or []) if x > 0 and x % 2 == 0]'''
|
| 153 |
+
|
| 154 |
+
return code
|
| 155 |
+
|
| 156 |
+
def generate_candidates(self, code: str) -> List[Dict[str, Any]]:
|
| 157 |
+
candidates = []
|
| 158 |
+
|
| 159 |
+
# Strategy 1: Guard Clauses & Nesting Reduction
|
| 160 |
+
cand1_code = self._refactor_guard_clauses_and_nesting(code)
|
| 161 |
+
if cand1_code and cand1_code.strip() != code.strip():
|
| 162 |
+
candidates.append({
|
| 163 |
+
'id': 'candidate_guard_clauses',
|
| 164 |
+
'code': cand1_code,
|
| 165 |
+
'prompt_used': 'Flatten nested logic with guard clauses',
|
| 166 |
+
'strategy': 'Guard Clauses & Early Return'
|
| 167 |
+
})
|
| 168 |
+
|
| 169 |
+
# Strategy 2: Pythonic Functional / Comprehension Simplification
|
| 170 |
+
cand2_code = self._refactor_functional_comprehension(code)
|
| 171 |
+
if cand2_code and cand2_code.strip() != code.strip():
|
| 172 |
+
candidates.append({
|
| 173 |
+
'id': 'candidate_comprehension',
|
| 174 |
+
'code': cand2_code,
|
| 175 |
+
'prompt_used': 'Refactor loops into Pythonic comprehensions',
|
| 176 |
+
'strategy': 'Functional Comprehension'
|
| 177 |
+
})
|
| 178 |
+
|
| 179 |
+
# Strategy 3: Modular Refactoring & Global Variable Elimination
|
| 180 |
+
cand3_code = self._refactor_remove_globals(code)
|
| 181 |
+
if cand3_code and cand3_code.strip() != code.strip():
|
| 182 |
+
candidates.append({
|
| 183 |
+
'id': 'candidate_modular_state',
|
| 184 |
+
'code': cand3_code,
|
| 185 |
+
'prompt_used': 'Eliminate global state & modularize logic',
|
| 186 |
+
'strategy': 'State Encapsulation'
|
| 187 |
+
})
|
| 188 |
+
|
| 189 |
+
# Strategy 4: Clean Functional Minimum
|
| 190 |
+
cand4_code = self._refactor_modular_clean(code)
|
| 191 |
+
if cand4_code and cand4_code.strip() != code.strip():
|
| 192 |
+
candidates.append({
|
| 193 |
+
'id': 'candidate_clean_idiomatic',
|
| 194 |
+
'code': cand4_code,
|
| 195 |
+
'prompt_used': 'Defensive typing & idiomatic clean code',
|
| 196 |
+
'strategy': 'Clean Idiomatic'
|
| 197 |
+
})
|
| 198 |
+
|
| 199 |
+
# If DL generator is enabled, attempt DL candidate generation (triggers lazy load on first run)
|
| 200 |
+
if self.dl_generator.enable_dl:
|
| 201 |
+
try:
|
| 202 |
+
dl_code = self.dl_generator.generate(code, "Refactor code for clean maintainability", temperature=0.7)
|
| 203 |
+
if dl_code and dl_code.strip() != code.strip():
|
| 204 |
+
candidates.append({
|
| 205 |
+
'id': 'candidate_deep_learning',
|
| 206 |
+
'code': dl_code,
|
| 207 |
+
'prompt_used': 'Deep Learning Seq2Seq refactoring',
|
| 208 |
+
'strategy': 'Deep Learning'
|
| 209 |
+
})
|
| 210 |
+
except Exception:
|
| 211 |
+
pass
|
| 212 |
+
|
| 213 |
+
# Ensure we always have candidates
|
| 214 |
+
if not candidates:
|
| 215 |
+
# Generate a clean standardized copy as candidate
|
| 216 |
+
clean_fallback = self._refactor_guard_clauses_and_nesting(code)
|
| 217 |
+
candidates.append({
|
| 218 |
+
'id': 'candidate_standardized',
|
| 219 |
+
'code': clean_fallback if clean_fallback else code,
|
| 220 |
+
'prompt_used': 'Standardized format',
|
| 221 |
+
'strategy': 'Standard Refactor'
|
| 222 |
+
})
|
| 223 |
+
|
| 224 |
+
return candidates
|
phase2/dl_generator.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class DLCodeGenerator:
|
| 5 |
+
_instance = None
|
| 6 |
+
_initialized = False
|
| 7 |
+
|
| 8 |
+
def __new__(cls, *args, **kwargs):
|
| 9 |
+
if cls._instance is None:
|
| 10 |
+
cls._instance = super().__new__(cls)
|
| 11 |
+
return cls._instance
|
| 12 |
+
|
| 13 |
+
def __init__(self, model_name: str = "Salesforce/codet5-base", enable_dl: bool = True):
|
| 14 |
+
if self._initialized:
|
| 15 |
+
return
|
| 16 |
+
self.model_name = model_name
|
| 17 |
+
self.enable_dl = enable_dl
|
| 18 |
+
self.tokenizer = None
|
| 19 |
+
self.model = None
|
| 20 |
+
self.is_mock = not enable_dl
|
| 21 |
+
self._initialized = True
|
| 22 |
+
|
| 23 |
+
def _ensure_loaded(self) -> bool:
|
| 24 |
+
"""Lazily load the CodeT5 model and tokenizer only when generate is first called."""
|
| 25 |
+
if not self.enable_dl:
|
| 26 |
+
return False
|
| 27 |
+
if self.model is not None and self.tokenizer is not None:
|
| 28 |
+
return True
|
| 29 |
+
print(f"Lazy loading DL model: {self.model_name}...")
|
| 30 |
+
try:
|
| 31 |
+
from transformers import RobertaTokenizer, AutoModelForSeq2SeqLM, AutoTokenizer
|
| 32 |
+
|
| 33 |
+
try:
|
| 34 |
+
self.tokenizer = RobertaTokenizer.from_pretrained(
|
| 35 |
+
self.model_name, extra_ids=0, additional_special_tokens=[], local_files_only=False
|
| 36 |
+
)
|
| 37 |
+
except Exception:
|
| 38 |
+
self.tokenizer = AutoTokenizer.from_pretrained(
|
| 39 |
+
self.model_name, local_files_only=False
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
self.model = AutoModelForSeq2SeqLM.from_pretrained(
|
| 43 |
+
self.model_name, local_files_only=False
|
| 44 |
+
)
|
| 45 |
+
self.is_mock = False
|
| 46 |
+
print("DL Model loaded successfully.")
|
| 47 |
+
return True
|
| 48 |
+
except Exception as e:
|
| 49 |
+
print(
|
| 50 |
+
f"Note: DL Model '{self.model_name}' skipped ({e}). Using ultra-fast AST rule generator."
|
| 51 |
+
)
|
| 52 |
+
self.is_mock = True
|
| 53 |
+
return False
|
| 54 |
+
|
| 55 |
+
def generate(
|
| 56 |
+
self,
|
| 57 |
+
code: str,
|
| 58 |
+
prompt: str,
|
| 59 |
+
temperature: float = 0.7,
|
| 60 |
+
max_length: int = 512,
|
| 61 |
+
) -> str:
|
| 62 |
+
if not self._ensure_loaded():
|
| 63 |
+
return code
|
| 64 |
+
|
| 65 |
+
try:
|
| 66 |
+
input_text = f"{prompt}:\n{code}"
|
| 67 |
+
input_ids = self.tokenizer(
|
| 68 |
+
input_text,
|
| 69 |
+
return_tensors="pt",
|
| 70 |
+
truncation=True,
|
| 71 |
+
max_length=max_length,
|
| 72 |
+
).input_ids
|
| 73 |
+
outputs = self.model.generate(
|
| 74 |
+
input_ids,
|
| 75 |
+
max_new_tokens=max_length,
|
| 76 |
+
temperature=temperature,
|
| 77 |
+
do_sample=True if temperature > 0 else False,
|
| 78 |
+
top_p=0.95,
|
| 79 |
+
num_return_sequences=1,
|
| 80 |
+
)
|
| 81 |
+
return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 82 |
+
except Exception as e:
|
| 83 |
+
print(f"DL Generation error: {e}. Falling back to source code.")
|
| 84 |
+
return code
|
| 85 |
+
|
phase2/optimization.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
import os
|
| 3 |
+
from typing import Dict, Any
|
| 4 |
+
|
| 5 |
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 6 |
+
parent_dir = os.path.dirname(current_dir)
|
| 7 |
+
if parent_dir not in sys.path:
|
| 8 |
+
sys.path.insert(0, parent_dir)
|
| 9 |
+
|
| 10 |
+
from scoring_api import evaluate
|
| 11 |
+
from phase2.candidate_generator import CandidateGenerator
|
| 12 |
+
from phase2.validation import CodeValidator
|
| 13 |
+
from phase2.selector import CandidateSelector
|
| 14 |
+
|
| 15 |
+
class IterativeOptimizer:
|
| 16 |
+
|
| 17 |
+
def __init__(self, target_score: float = 15.0, max_iterations: int = 2):
|
| 18 |
+
self.target_score = target_score
|
| 19 |
+
self.max_iterations = max_iterations
|
| 20 |
+
self.generator = CandidateGenerator()
|
| 21 |
+
self.validator = CodeValidator()
|
| 22 |
+
self.selector = CandidateSelector()
|
| 23 |
+
|
| 24 |
+
def optimize(self, initial_code: str) -> Dict[str, Any]:
|
| 25 |
+
initial_eval = evaluate(initial_code)
|
| 26 |
+
initial_score = initial_eval.get('risk_score', 100)
|
| 27 |
+
|
| 28 |
+
current_code = initial_code
|
| 29 |
+
best_overall_candidate = None
|
| 30 |
+
best_overall_score = initial_score
|
| 31 |
+
iteration_history = []
|
| 32 |
+
|
| 33 |
+
print(f"Initial Code Risk Score: {initial_score} (Target: <= {self.target_score})")
|
| 34 |
+
|
| 35 |
+
if initial_score <= self.target_score:
|
| 36 |
+
print("Code already satisfies target maintainability score.")
|
| 37 |
+
return {
|
| 38 |
+
'initial_code': initial_code,
|
| 39 |
+
'final_code': initial_code,
|
| 40 |
+
'initial_score': initial_score,
|
| 41 |
+
'final_score': initial_score,
|
| 42 |
+
'best_candidate_details': None,
|
| 43 |
+
'history': [],
|
| 44 |
+
'iterations_run': 0
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
for iteration in range(1, self.max_iterations + 1):
|
| 48 |
+
print(f"\n--- Starting Optimization Iteration {iteration}/{self.max_iterations} ---")
|
| 49 |
+
raw_candidates = self.generator.generate_candidates(current_code)
|
| 50 |
+
valid_candidates = self.validator.filter_valid_candidates(initial_code, raw_candidates)
|
| 51 |
+
|
| 52 |
+
if not valid_candidates:
|
| 53 |
+
print("No valid candidates generated in this iteration. Halting optimization.")
|
| 54 |
+
break
|
| 55 |
+
|
| 56 |
+
best_iteration_candidate = self.selector.select_best(valid_candidates)
|
| 57 |
+
if best_iteration_candidate is None:
|
| 58 |
+
print("Failed to score candidates in this iteration. Halting optimization.")
|
| 59 |
+
break
|
| 60 |
+
|
| 61 |
+
current_score = best_iteration_candidate.get('risk_score', 100)
|
| 62 |
+
strategy_used = best_iteration_candidate['candidate_data'].get('strategy', 'Refactor')
|
| 63 |
+
print(f"Best candidate in iteration {iteration} [{strategy_used}] achieved risk score: {current_score}")
|
| 64 |
+
iteration_history.append({
|
| 65 |
+
'iteration': iteration,
|
| 66 |
+
'best_candidate': best_iteration_candidate
|
| 67 |
+
})
|
| 68 |
+
|
| 69 |
+
if current_score < best_overall_score:
|
| 70 |
+
best_overall_score = current_score
|
| 71 |
+
best_overall_candidate = best_iteration_candidate
|
| 72 |
+
current_code = best_iteration_candidate['candidate_data']['code']
|
| 73 |
+
else:
|
| 74 |
+
print("No further risk reduction in this iteration.")
|
| 75 |
+
|
| 76 |
+
if best_overall_score <= self.target_score:
|
| 77 |
+
print(f"Target score of {self.target_score} achieved! Stopping early.")
|
| 78 |
+
break
|
| 79 |
+
|
| 80 |
+
final_code = best_overall_candidate['candidate_data']['code'] if best_overall_candidate else current_code
|
| 81 |
+
|
| 82 |
+
print("\n=== Optimization Process Complete ===")
|
| 83 |
+
print(f"Initial Risk Score: {initial_score} -> Final Best Score: {best_overall_score}")
|
| 84 |
+
|
| 85 |
+
return {
|
| 86 |
+
'initial_code': initial_code,
|
| 87 |
+
'final_code': final_code,
|
| 88 |
+
'initial_score': initial_score,
|
| 89 |
+
'final_score': best_overall_score,
|
| 90 |
+
'best_candidate_details': best_overall_candidate,
|
| 91 |
+
'history': iteration_history,
|
| 92 |
+
'iterations_run': len(iteration_history)
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
if __name__ == '__main__':
|
| 96 |
+
try:
|
| 97 |
+
sys.stdout.reconfigure(encoding='utf-8')
|
| 98 |
+
except Exception:
|
| 99 |
+
pass
|
| 100 |
+
|
| 101 |
+
print('Initializing Iterative Optimizer...')
|
| 102 |
+
try:
|
| 103 |
+
optimizer = IterativeOptimizer(target_score=10.0, max_iterations=2)
|
| 104 |
+
sample_code = '''
|
| 105 |
+
global_counter = 0
|
| 106 |
+
|
| 107 |
+
def bloated_pipeline(data):
|
| 108 |
+
global global_counter
|
| 109 |
+
try:
|
| 110 |
+
for x in data:
|
| 111 |
+
if x > 0:
|
| 112 |
+
for i in range(x):
|
| 113 |
+
try:
|
| 114 |
+
if i % 2 == 0:
|
| 115 |
+
global_counter += 1
|
| 116 |
+
except:
|
| 117 |
+
pass
|
| 118 |
+
except Exception:
|
| 119 |
+
return -1
|
| 120 |
+
return global_counter
|
| 121 |
+
'''
|
| 122 |
+
print('Starting optimization on sample code...')
|
| 123 |
+
result = optimizer.optimize(sample_code)
|
| 124 |
+
print('\n=== Final Optimization Report ===')
|
| 125 |
+
print('Initial Code:\n', result['initial_code'])
|
| 126 |
+
print('\nFinal Optimized Code:\n', result['final_code'])
|
| 127 |
+
print('\nTotal Iterations:', result['iterations_run'])
|
| 128 |
+
print('Final Score:', result['final_score'])
|
| 129 |
+
except Exception as e:
|
| 130 |
+
print(f'Error during execution: {e}')
|
phase2/selector.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
| 4 |
+
parent_dir = os.path.dirname(current_dir)
|
| 5 |
+
if parent_dir not in sys.path:
|
| 6 |
+
sys.path.append(parent_dir)
|
| 7 |
+
try:
|
| 8 |
+
from scoring_api import evaluate
|
| 9 |
+
except ImportError as e:
|
| 10 |
+
print(f"Error: Unable to import Phase 1 'scoring_api'. Ensure it exists in the parent directory.")
|
| 11 |
+
print(e)
|
| 12 |
+
|
| 13 |
+
def evaluate(code):
|
| 14 |
+
return {'error': True, 'message': 'Phase 1 missing.'}
|
| 15 |
+
from typing import List, Dict, Any
|
| 16 |
+
|
| 17 |
+
class CandidateSelector:
|
| 18 |
+
|
| 19 |
+
def select_best(self, candidates: List[Dict[str, Any]]) -> Dict[str, Any]:
|
| 20 |
+
if not candidates:
|
| 21 |
+
return None
|
| 22 |
+
evaluated_candidates = []
|
| 23 |
+
for cand in candidates:
|
| 24 |
+
code = cand['code']
|
| 25 |
+
result = evaluate(code)
|
| 26 |
+
if not result.get('error'):
|
| 27 |
+
evaluated_candidates.append({'candidate_data': cand, 'score_result': result, 'risk_score': result.get('risk_score', 100)})
|
| 28 |
+
else:
|
| 29 |
+
print(f"Warning: Scoring failed for a candidate: {result.get('message')}")
|
| 30 |
+
if not evaluated_candidates:
|
| 31 |
+
print('Warning: All candidates failed during Phase 1 evaluation.')
|
| 32 |
+
return None
|
| 33 |
+
best_candidate = min(evaluated_candidates, key=lambda c: c['risk_score'])
|
| 34 |
+
return best_candidate
|
phase2/validation.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ast
|
| 2 |
+
from typing import List, Dict
|
| 3 |
+
|
| 4 |
+
class CodeValidator:
|
| 5 |
+
|
| 6 |
+
def __init__(self, min_length_ratio: float = 0.15):
|
| 7 |
+
self.min_length_ratio = min_length_ratio
|
| 8 |
+
|
| 9 |
+
def _extract_function_signature(self, code: str):
|
| 10 |
+
try:
|
| 11 |
+
tree = ast.parse(code)
|
| 12 |
+
for node in ast.walk(tree):
|
| 13 |
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
| 14 |
+
args = [arg.arg for arg in node.args.args]
|
| 15 |
+
return (node.name, args)
|
| 16 |
+
return (None, None)
|
| 17 |
+
except Exception:
|
| 18 |
+
return (None, None)
|
| 19 |
+
|
| 20 |
+
def is_valid(self, original_code: str, candidate_code: str) -> bool:
|
| 21 |
+
if not candidate_code or not candidate_code.strip():
|
| 22 |
+
return False
|
| 23 |
+
|
| 24 |
+
# Must be valid Python syntax
|
| 25 |
+
try:
|
| 26 |
+
ast.parse(candidate_code)
|
| 27 |
+
except SyntaxError:
|
| 28 |
+
return False
|
| 29 |
+
|
| 30 |
+
# Check function signature compatibility if original has a function
|
| 31 |
+
orig_name, orig_args = self._extract_function_signature(original_code)
|
| 32 |
+
cand_name, cand_args = self._extract_function_signature(candidate_code)
|
| 33 |
+
|
| 34 |
+
if orig_name and cand_name:
|
| 35 |
+
if orig_name != cand_name:
|
| 36 |
+
return False
|
| 37 |
+
# Allow positional args match
|
| 38 |
+
if orig_args and cand_args:
|
| 39 |
+
if len(orig_args) != len(cand_args):
|
| 40 |
+
return False
|
| 41 |
+
|
| 42 |
+
orig_has_return = "return " in original_code or "return\n" in original_code
|
| 43 |
+
cand_has_return = "return " in candidate_code or "return\n" in candidate_code
|
| 44 |
+
if orig_has_return and not cand_has_return:
|
| 45 |
+
return False
|
| 46 |
+
|
| 47 |
+
return True
|
| 48 |
+
|
| 49 |
+
def filter_valid_candidates(self, original_code: str, candidates: List[Dict[str, str]]) -> List[Dict[str, str]]:
|
| 50 |
+
valid_candidates = []
|
| 51 |
+
for cand in candidates:
|
| 52 |
+
if self.is_valid(original_code, cand.get('code', '')):
|
| 53 |
+
valid_candidates.append(cand)
|
| 54 |
+
return valid_candidates
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn[standard]
|
| 3 |
+
scikit-learn
|
| 4 |
+
xgboost
|
| 5 |
+
numpy
|
| 6 |
+
transformers
|
| 7 |
+
torch
|
| 8 |
+
psutil
|