Spaces:
Sleeping
Sleeping
Kamal Nayan Kumar commited on
Commit ·
dbc6ebe
0
Parent(s):
Initial commit
Browse files- .gitattributes +2 -0
- .gitignore +56 -0
- README.md +75 -0
- Sample_Queries.md +57 -0
- backend/api.py +244 -0
- backend/parse_data.py +138 -0
- backend/requirements.txt +6 -0
- frontend/.gitignore +24 -0
- frontend/README.md +73 -0
- frontend/bun.lock +501 -0
- frontend/eslint.config.js +23 -0
- frontend/index.html +13 -0
- frontend/package.json +35 -0
- frontend/public/favicon.svg +1 -0
- frontend/public/icons.svg +24 -0
- frontend/src/App.tsx +328 -0
- frontend/src/assets/hero.png +0 -0
- frontend/src/assets/react.svg +1 -0
- frontend/src/assets/vite.svg +1 -0
- frontend/src/index.css +1 -0
- frontend/src/main.tsx +10 -0
- frontend/tsconfig.app.json +28 -0
- frontend/tsconfig.json +7 -0
- frontend/tsconfig.node.json +26 -0
- frontend/vite.config.ts +8 -0
.gitattributes
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
data/*.csv filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
data/*.npy filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Logs
|
| 2 |
+
logs
|
| 3 |
+
*.log
|
| 4 |
+
npm-debug.log*
|
| 5 |
+
yarn-debug.log*
|
| 6 |
+
yarn-error.log*
|
| 7 |
+
pnpm-debug.log*
|
| 8 |
+
lerna-debug.log*
|
| 9 |
+
|
| 10 |
+
# Node
|
| 11 |
+
node_modules
|
| 12 |
+
dist
|
| 13 |
+
dist-ssr
|
| 14 |
+
*.local
|
| 15 |
+
|
| 16 |
+
# Editor directories and files
|
| 17 |
+
.vscode/*
|
| 18 |
+
!.vscode/extensions.json
|
| 19 |
+
.idea
|
| 20 |
+
.DS_Store
|
| 21 |
+
*.suo
|
| 22 |
+
*.ntvs
|
| 23 |
+
*.njsproj
|
| 24 |
+
*.sln
|
| 25 |
+
*.sw?
|
| 26 |
+
|
| 27 |
+
# Python
|
| 28 |
+
__pycache__/
|
| 29 |
+
*.py[cod]
|
| 30 |
+
*$py.class
|
| 31 |
+
*.so
|
| 32 |
+
|
| 33 |
+
# Virtual environments
|
| 34 |
+
backend/.venv/
|
| 35 |
+
.venv/
|
| 36 |
+
venv/
|
| 37 |
+
ENV/
|
| 38 |
+
env.bak/
|
| 39 |
+
venv.bak/
|
| 40 |
+
|
| 41 |
+
# Environment variables
|
| 42 |
+
.env
|
| 43 |
+
.env.test
|
| 44 |
+
.env.production
|
| 45 |
+
|
| 46 |
+
# Data (CRITICAL: Do NOT push 100MB+ CSV/JSON files to GitHub)
|
| 47 |
+
data/*.csv
|
| 48 |
+
data/*.json
|
| 49 |
+
data/*.tsv
|
| 50 |
+
*.zip
|
| 51 |
+
*.tar.gz
|
| 52 |
+
*.sqlite3
|
| 53 |
+
|
| 54 |
+
# OpenCode config
|
| 55 |
+
.opencode/
|
| 56 |
+
.sisyphus/
|
README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Information Retrieval Using Semantic Roles (Semantic IR)
|
| 2 |
+
|
| 3 |
+
A hybrid search engine that compares traditional keyword search, dense neural retrieval, and **Semantic Role Labeling (SRL)** to dramatically improve search precision.
|
| 4 |
+
|
| 5 |
+
By understanding "who did what to whom" (Predicate-Argument structures), this system solves the classic flaws of keyword matching (ignoring grammar) and vector search (confusing directionality, e.g., "Man bit dog" vs. "Dog bit man").
|
| 6 |
+
|
| 7 |
+
## ✨ Features
|
| 8 |
+
* **4-Way Paradigm Comparison:** Real-time side-by-side search results for BM25 (Keyword), Dense (Vector), SRL (Grammar), and Hybrid approaches.
|
| 9 |
+
* **Semantic Highlighting:** Automatically color-codes Predicates (Red), ARG0/Agents (Blue), and ARG1/Patients (Green) directly in the search results.
|
| 10 |
+
* **Interactive Parse Trees:** Integrated `spaCy displaCy` visualizations to prove the grammatical dependency structures of the retrieved documents.
|
| 11 |
+
* **Smart Fallbacks:** Dynamically filters out irrelevant results and visualizes beautiful empty states when strict lexical or structural algorithms fail.
|
| 12 |
+
|
| 13 |
+
## 🛠 Tech Stack
|
| 14 |
+
* **Backend:** Python 3.9+, FastAPI, `rank-bm25`, `sentence-transformers` (Dense Vectors), `spaCy` (NLP & displaCy trees).
|
| 15 |
+
* **Frontend:** React 19, TypeScript, Vite, Tailwind CSS v4, `lucide-react`.
|
| 16 |
+
|
| 17 |
+
## 📁 Folder Structure
|
| 18 |
+
```text
|
| 19 |
+
.
|
| 20 |
+
├── backend/
|
| 21 |
+
│ ├── api.py # FastAPI server (BM25, Dense, SRL, Hybrid logic)
|
| 22 |
+
│ ├── parse_data.py # Parses CoNLL-U CSVs into a searchable JSON corpus
|
| 23 |
+
│ └── requirements.txt # Python dependencies
|
| 24 |
+
├── frontend/
|
| 25 |
+
│ ├── src/ # React source code (App.tsx holds the UI)
|
| 26 |
+
│ ├── package.json # Node dependencies
|
| 27 |
+
│ └── vite.config.ts # Vite bundler config
|
| 28 |
+
└── data/ # Ignored by git. Holds PropBank CSVs & corpus.json
|
| 29 |
+
```
|
| 30 |
+
|
| 31 |
+
## 🚀 Setup Instructions
|
| 32 |
+
|
| 33 |
+
If you want to run this project locally on your machine, follow these steps:
|
| 34 |
+
|
| 35 |
+
### 1. Data Preparation
|
| 36 |
+
1. Create a `data/` folder in the root directory.
|
| 37 |
+
2. Download the PropBank dataset from Hugging Face: [mertas7/propbank](https://huggingface.co/datasets/mertas7/propbank/tree/main).
|
| 38 |
+
3. Place `cleaned-converted-train.csv`, `cleaned-converted-dev.csv`, and `cleaned-converted-test.csv` into the `data/` folder.
|
| 39 |
+
|
| 40 |
+
### 2. Backend Setup
|
| 41 |
+
Open a terminal and navigate to the `backend/` directory:
|
| 42 |
+
```bash
|
| 43 |
+
cd backend
|
| 44 |
+
|
| 45 |
+
# 1. Create and activate a virtual environment
|
| 46 |
+
python -m venv .venv
|
| 47 |
+
source .venv/bin/activate # On Windows use: .venv\Scripts\activate
|
| 48 |
+
|
| 49 |
+
# 2. Install dependencies
|
| 50 |
+
pip install -r requirements.txt
|
| 51 |
+
python -m spacy download en_core_web_sm
|
| 52 |
+
|
| 53 |
+
# 3. Parse the CSVs into a JSON Corpus (takes ~1 minute)
|
| 54 |
+
python parse_data.py
|
| 55 |
+
|
| 56 |
+
# 4. Start the FastAPI Server
|
| 57 |
+
uvicorn api:app --host 127.0.0.1 --port 8001 --reload
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
### 3. Frontend Setup
|
| 61 |
+
Open a *second* terminal and navigate to the `frontend/` directory:
|
| 62 |
+
```bash
|
| 63 |
+
cd frontend
|
| 64 |
+
|
| 65 |
+
# 1. Install dependencies (using bun or npm)
|
| 66 |
+
bun install # or: npm install
|
| 67 |
+
|
| 68 |
+
# 2. Start the development server
|
| 69 |
+
bun run dev # or: npm run dev
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
Open your browser to the local Vite URL (usually `http://localhost:5173`).
|
| 73 |
+
|
| 74 |
+
## 📝 Evaluation & Demo
|
| 75 |
+
Check out `Sample_Queries.md` in this repository for testing demo.
|
Sample_Queries.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Semantic IR: Presentation Script & Sample Queries
|
| 2 |
+
|
| 3 |
+
## Query 1: The "Agent + Action" Match
|
| 4 |
+
**Search for:** `Bush nominated`
|
| 5 |
+
|
| 6 |
+
* **Type:** Simple Structural Query
|
| 7 |
+
* **Why I chose it:** To demonstrate the fundamental difference between Keyword matching and Grammar parsing.
|
| 8 |
+
* **What to expect & point out:**
|
| 9 |
+
* **BM25:** Finds the words "Bush" and "nominated" anywhere in the text, even if they aren't grammatically related.
|
| 10 |
+
* **SRL:** Parses the query to understand that `Bush` is the `ARG0` (The Doer/Agent) and `nominate` is the `Predicate` (The Action). It strictly returns sentences where Bush is the one actively doing the nominating.
|
| 11 |
+
* **Hybrid:** Combines the keyword strength of BM25 with the grammatical strictness of SRL. Point to the top Hybrid result and show the red and blue highlights proving the grammar was enforced.
|
| 12 |
+
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
## Query 2: The Multi-Argument Constraint (Directionality)
|
| 16 |
+
**Search for:** `Google acquired Firefox`
|
| 17 |
+
|
| 18 |
+
* **Type:** Complex Directional Query
|
| 19 |
+
* **Why I chose it:** To prove that our system understands "Who did what to whom." In Information Retrieval, Dense Neural Networks notoriously struggle with directionality (they confuse "Man bit dog" with "Dog bit man" because the vectors are almost identical).
|
| 20 |
+
* **What to expect & point out:**
|
| 21 |
+
* **Dense:** Might return sentences about general tech acquisitions or confuse the acquirer with the acquired.
|
| 22 |
+
* **SRL:** Strictly maps `Google` to `ARG0` (Acquirer) and `Firefox` to `ARG1` (Acquired).
|
| 23 |
+
* **Hybrid:** Look at the Top Result. Click **"View Parse Tree"** to visually prove to the evaluator that the backend built a dependency graph and correctly mapped Google to the subject and Firefox to the object.
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
## Query 3: The Role Reversal Disambiguation
|
| 28 |
+
**Search for:** `guerrillas killed`
|
| 29 |
+
|
| 30 |
+
* **Type:** Ambiguity Resolution
|
| 31 |
+
* **Why I chose it:** "Guerrillas killed" is highly ambiguous. Does it mean guerrillas committed murder, or does it mean they were the victims?
|
| 32 |
+
* **What to expect & point out:**
|
| 33 |
+
* **BM25 & Dense:** Will return a messy mix of both scenarios.
|
| 34 |
+
* **SRL & Hybrid:** Because "guerrillas" precedes the active verb, the system maps it to `ARG0` (The Killer). Point out that the Hybrid column actively suppressed sentences where guerrillas were the victims (`ARG1`), proving that Semantic Roles act as a powerful precision guardrail.
|
| 35 |
+
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
## Query 4: The Conversational Concept
|
| 39 |
+
**Search for:** `buy a phone`
|
| 40 |
+
|
| 41 |
+
* **Type:** Broad/Conversational Concept
|
| 42 |
+
* **Why I chose it:** To show that the system still functions perfectly as a modern search engine for everyday queries, not just complex political sentences.
|
| 43 |
+
* **What to expect & point out:**
|
| 44 |
+
* **Dense:** Does the heavy lifting here, expanding the search to concepts related to purchasing and technology.
|
| 45 |
+
* **Hybrid:** Takes the broad semantic meaning from Dense, but uses the SRL weight to ensure the action is actually related to "buying" (`Predicate: buy.01`) and the object is a "phone" (`ARG1`).
|
| 46 |
+
|
| 47 |
+
---
|
| 48 |
+
|
| 49 |
+
## Query 5: The "Out-of-Vocabulary" Fallback (Empty State)
|
| 50 |
+
**Search for:** `himanshu` *(or any completely made-up word)*
|
| 51 |
+
|
| 52 |
+
* **Type:** Hallucination Guardrail Test
|
| 53 |
+
* **Why I chose it:** To demonstrate how the architecture handles complete failures and how Lexical/Structural algorithms differ from Neural Networks.
|
| 54 |
+
* **What to expect & point out:**
|
| 55 |
+
* **BM25 & SRL:** Will instantly show beautiful **"No lexical/structural matches found"** empty states. Explain that because the word doesn't exist, the strict algorithms correctly scored it a `0.0` and our backend dynamically filtered them out.
|
| 56 |
+
* **Dense:** Will actually return results! Point this out to the evaluator as a classic flaw of Vector Search: Neural Networks *always* guess the "closest mathematical vector", even if the distance is huge, leading to hallucinated results.
|
| 57 |
+
* **Conclusion:** Conclude by saying this proves why the **Hybrid** approach is safest—it relies on Dense for broad recall but uses BM25 and SRL to pull the emergency brake when the neural network is hallucinating.
|
backend/api.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from typing import Any, Dict, List
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
import spacy
|
| 7 |
+
from fastapi import FastAPI, Query
|
| 8 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 9 |
+
from rank_bm25 import BM25Okapi
|
| 10 |
+
|
| 11 |
+
try:
|
| 12 |
+
from sentence_transformers import SentenceTransformer
|
| 13 |
+
except Exception:
|
| 14 |
+
SentenceTransformer = None
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
BASE_DIR = Path(__file__).resolve().parents[1]
|
| 18 |
+
CORPUS_PATH = BASE_DIR / "data" / "corpus.json"
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _norm(values: np.ndarray) -> np.ndarray:
|
| 22 |
+
if values.size == 0:
|
| 23 |
+
return values
|
| 24 |
+
v_max = float(np.max(values))
|
| 25 |
+
if v_max <= 0:
|
| 26 |
+
return np.zeros_like(values, dtype=float)
|
| 27 |
+
return values / v_max
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _tokenize(text: str) -> List[str]:
|
| 31 |
+
return [tok.lower() for tok in text.split() if tok.strip()]
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _extract_query_roles(query: str, nlp_model) -> Dict[str, str]:
|
| 35 |
+
doc = nlp_model(query)
|
| 36 |
+
predicate = ""
|
| 37 |
+
arg0: List[str] = []
|
| 38 |
+
arg1: List[str] = []
|
| 39 |
+
argm_tmp: List[str] = []
|
| 40 |
+
|
| 41 |
+
for tok in doc:
|
| 42 |
+
if not predicate and tok.pos_ == "VERB":
|
| 43 |
+
predicate = tok.lemma_.lower()
|
| 44 |
+
|
| 45 |
+
for tok in doc:
|
| 46 |
+
if tok.dep_ in {"nsubj", "nsubjpass"}:
|
| 47 |
+
arg0.append(" ".join(t.text for t in tok.subtree))
|
| 48 |
+
elif tok.dep_ in {"dobj", "obj", "iobj"}:
|
| 49 |
+
arg1.append(" ".join(t.text for t in tok.subtree))
|
| 50 |
+
elif tok.dep_ in {"obl", "npadvmod"} and tok.ent_type_ in {"DATE", "TIME"}:
|
| 51 |
+
argm_tmp.append(" ".join(t.text for t in tok.subtree))
|
| 52 |
+
|
| 53 |
+
out: Dict[str, str] = {"predicate": predicate}
|
| 54 |
+
if arg0:
|
| 55 |
+
out["ARG0"] = " ".join(dict.fromkeys(arg0))
|
| 56 |
+
if arg1:
|
| 57 |
+
out["ARG1"] = " ".join(dict.fromkeys(arg1))
|
| 58 |
+
if argm_tmp:
|
| 59 |
+
out["ARGM-TMP"] = " ".join(dict.fromkeys(argm_tmp))
|
| 60 |
+
return out
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def _role_overlap_score(
|
| 64 |
+
query_roles: Dict[str, str], doc_roles: Dict[str, str]
|
| 65 |
+
) -> float:
|
| 66 |
+
keys = [k for k, v in query_roles.items() if v]
|
| 67 |
+
if not keys:
|
| 68 |
+
return 0.0
|
| 69 |
+
|
| 70 |
+
scores: List[float] = []
|
| 71 |
+
for key in keys:
|
| 72 |
+
if key == "predicate":
|
| 73 |
+
q_pred = query_roles.get("predicate", "").split(".")[0]
|
| 74 |
+
d_pred = doc_roles.get("predicate", "").split(".")[0]
|
| 75 |
+
scores.append(1.0 if q_pred and d_pred and q_pred == d_pred else 0.0)
|
| 76 |
+
continue
|
| 77 |
+
|
| 78 |
+
q_tokens = set(_tokenize(query_roles.get(key, "")))
|
| 79 |
+
d_tokens = set(_tokenize(doc_roles.get(key, "")))
|
| 80 |
+
if not q_tokens or not d_tokens:
|
| 81 |
+
scores.append(0.0)
|
| 82 |
+
continue
|
| 83 |
+
|
| 84 |
+
inter = len(q_tokens.intersection(d_tokens))
|
| 85 |
+
union = len(q_tokens.union(d_tokens))
|
| 86 |
+
scores.append(inter / union if union else 0.0)
|
| 87 |
+
|
| 88 |
+
return float(np.mean(scores)) if scores else 0.0
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _maybe_load_embedder() -> Any:
|
| 92 |
+
if SentenceTransformer is None:
|
| 93 |
+
return None
|
| 94 |
+
try:
|
| 95 |
+
return SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
|
| 96 |
+
except Exception:
|
| 97 |
+
return None
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def _dense_scores(query: str) -> np.ndarray:
|
| 101 |
+
if EMBEDDER is None or DOC_EMBEDDINGS.size == 0:
|
| 102 |
+
return np.zeros(len(CORPUS), dtype=float)
|
| 103 |
+
query_embedding = EMBEDDER.encode([query], normalize_embeddings=True)[0]
|
| 104 |
+
return np.array(np.dot(DOC_EMBEDDINGS, query_embedding), dtype=float)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
ROLE_KEYS = {
|
| 108 |
+
"predicate",
|
| 109 |
+
"ARG0",
|
| 110 |
+
"ARG1",
|
| 111 |
+
"ARG2",
|
| 112 |
+
"ARG3",
|
| 113 |
+
"ARG4",
|
| 114 |
+
"ARGM-LOC",
|
| 115 |
+
"ARGM-TMP",
|
| 116 |
+
"ARGM-MNR",
|
| 117 |
+
"ARGM-CAU",
|
| 118 |
+
"ARGM-DIS",
|
| 119 |
+
"ARGM-ADV",
|
| 120 |
+
"ARGM-MOD",
|
| 121 |
+
"ARGM-NEG",
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def _build_result(
|
| 126 |
+
idx: int,
|
| 127 |
+
bm25_scores: np.ndarray,
|
| 128 |
+
dense_scores: np.ndarray,
|
| 129 |
+
srl_scores: np.ndarray,
|
| 130 |
+
hybrid_scores: np.ndarray,
|
| 131 |
+
) -> Dict[str, Any]:
|
| 132 |
+
doc = CORPUS[int(idx)]
|
| 133 |
+
text = doc.get("text", "")
|
| 134 |
+
roles = {k: v for k, v in doc.items() if k in ROLE_KEYS and v}
|
| 135 |
+
|
| 136 |
+
try:
|
| 137 |
+
spacy_doc = NLP(text)
|
| 138 |
+
html = spacy.displacy.render(
|
| 139 |
+
spacy_doc,
|
| 140 |
+
style="dep",
|
| 141 |
+
page=False,
|
| 142 |
+
options={
|
| 143 |
+
"compact": True,
|
| 144 |
+
"color": "#e4e4e7",
|
| 145 |
+
"font": "sans-serif",
|
| 146 |
+
"bg": "transparent",
|
| 147 |
+
},
|
| 148 |
+
)
|
| 149 |
+
except Exception:
|
| 150 |
+
html = ""
|
| 151 |
+
|
| 152 |
+
return {
|
| 153 |
+
"id": doc.get("id"),
|
| 154 |
+
"text": text,
|
| 155 |
+
"scores": {
|
| 156 |
+
"bm25": float(bm25_scores[idx]),
|
| 157 |
+
"dense": float(dense_scores[idx]),
|
| 158 |
+
"srl": float(srl_scores[idx]),
|
| 159 |
+
"hybrid": float(hybrid_scores[idx]),
|
| 160 |
+
},
|
| 161 |
+
"roles": roles,
|
| 162 |
+
"displacy_html": html,
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
with CORPUS_PATH.open("r", encoding="utf-8") as f:
|
| 167 |
+
CORPUS: List[Dict[str, Any]] = json.load(f)
|
| 168 |
+
|
| 169 |
+
TEXTS = [doc.get("text", "") for doc in CORPUS]
|
| 170 |
+
BM25 = BM25Okapi([_tokenize(text) for text in TEXTS])
|
| 171 |
+
|
| 172 |
+
EMBEDDER = _maybe_load_embedder()
|
| 173 |
+
if EMBEDDER is not None and TEXTS:
|
| 174 |
+
DOC_EMBEDDINGS = EMBEDDER.encode(TEXTS, normalize_embeddings=True)
|
| 175 |
+
else:
|
| 176 |
+
DOC_EMBEDDINGS = np.array([])
|
| 177 |
+
|
| 178 |
+
try:
|
| 179 |
+
NLP = spacy.load("en_core_web_sm")
|
| 180 |
+
except Exception:
|
| 181 |
+
NLP = spacy.blank("en")
|
| 182 |
+
|
| 183 |
+
app = FastAPI(title="Semantic IR Backend")
|
| 184 |
+
app.add_middleware(
|
| 185 |
+
CORSMiddleware,
|
| 186 |
+
allow_origins=["*"],
|
| 187 |
+
allow_credentials=True,
|
| 188 |
+
allow_methods=["*"],
|
| 189 |
+
allow_headers=["*"],
|
| 190 |
+
)
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
@app.get("/search")
|
| 194 |
+
def search(
|
| 195 |
+
q: str = Query(..., min_length=1), top_k: int = Query(10, ge=1, le=50)
|
| 196 |
+
) -> Dict[str, Any]:
|
| 197 |
+
bm25_scores = np.array(BM25.get_scores(_tokenize(q)), dtype=float)
|
| 198 |
+
dense_scores = _dense_scores(q)
|
| 199 |
+
query_roles = _extract_query_roles(q, NLP)
|
| 200 |
+
srl_scores = np.array(
|
| 201 |
+
[_role_overlap_score(query_roles, doc) for doc in CORPUS], dtype=float
|
| 202 |
+
)
|
| 203 |
+
|
| 204 |
+
bm25_n = _norm(bm25_scores)
|
| 205 |
+
dense_n = _norm(dense_scores)
|
| 206 |
+
srl_n = _norm(srl_scores)
|
| 207 |
+
hybrid_scores = 0.5 * bm25_n + 0.3 * dense_n + 0.2 * srl_n
|
| 208 |
+
|
| 209 |
+
def _build_results(
|
| 210 |
+
idx_array: np.ndarray, score_arr: np.ndarray
|
| 211 |
+
) -> List[Dict[str, Any]]:
|
| 212 |
+
results: List[Dict[str, Any]] = []
|
| 213 |
+
for idx in idx_array:
|
| 214 |
+
if score_arr[idx] <= 0.0:
|
| 215 |
+
continue
|
| 216 |
+
results.append(
|
| 217 |
+
_build_result(
|
| 218 |
+
int(idx), bm25_scores, dense_scores, srl_scores, hybrid_scores
|
| 219 |
+
)
|
| 220 |
+
)
|
| 221 |
+
if len(results) >= top_k:
|
| 222 |
+
break
|
| 223 |
+
return results
|
| 224 |
+
|
| 225 |
+
bm25_results = _build_results(np.argsort(-bm25_scores), bm25_scores)
|
| 226 |
+
dense_results = _build_results(np.argsort(-dense_scores), dense_scores)
|
| 227 |
+
srl_results = _build_results(np.argsort(-srl_scores), srl_scores)
|
| 228 |
+
hybrid_results = _build_results(np.argsort(-hybrid_scores), hybrid_scores)
|
| 229 |
+
|
| 230 |
+
return {
|
| 231 |
+
"bm25_results": bm25_results,
|
| 232 |
+
"dense_results": dense_results,
|
| 233 |
+
"srl_results": srl_results,
|
| 234 |
+
"hybrid_results": hybrid_results,
|
| 235 |
+
}
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
@app.get("/health")
|
| 239 |
+
def health() -> Dict[str, Any]:
|
| 240 |
+
return {
|
| 241 |
+
"status": "ok",
|
| 242 |
+
"documents": len(CORPUS),
|
| 243 |
+
"dense_enabled": EMBEDDER is not None,
|
| 244 |
+
}
|
backend/parse_data.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import csv
|
| 3 |
+
import json
|
| 4 |
+
import sys
|
| 5 |
+
from collections import defaultdict
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from typing import Dict, Iterable, List
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
csv.field_size_limit(sys.maxsize)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
COLS = [
|
| 14 |
+
"sent_id",
|
| 15 |
+
"token_id",
|
| 16 |
+
"word",
|
| 17 |
+
"lemma",
|
| 18 |
+
"upos",
|
| 19 |
+
"xpos",
|
| 20 |
+
"feats",
|
| 21 |
+
"head",
|
| 22 |
+
"deprel",
|
| 23 |
+
"deps",
|
| 24 |
+
"misc",
|
| 25 |
+
"predicate_sense",
|
| 26 |
+
"semantic_role",
|
| 27 |
+
]
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _iter_rows(path: Path) -> Iterable[List[str]]:
|
| 31 |
+
with path.open("r", encoding="utf-8", newline="") as f:
|
| 32 |
+
sample = f.read(4096)
|
| 33 |
+
f.seek(0)
|
| 34 |
+
delimiter = "\t" if "\t" in sample else ","
|
| 35 |
+
reader = csv.reader(f, delimiter=delimiter)
|
| 36 |
+
for row in reader:
|
| 37 |
+
if not row:
|
| 38 |
+
continue
|
| 39 |
+
if len(row) == 1 and not row[0].strip():
|
| 40 |
+
continue
|
| 41 |
+
if row[0].startswith("#"):
|
| 42 |
+
continue
|
| 43 |
+
yield row
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def read_conllu_srl(path: Path) -> List[Dict[str, str]]:
|
| 47 |
+
records: List[Dict[str, str]] = []
|
| 48 |
+
for row in _iter_rows(path):
|
| 49 |
+
if len(row) < len(COLS):
|
| 50 |
+
continue
|
| 51 |
+
row = row[: len(COLS)]
|
| 52 |
+
|
| 53 |
+
sent_id = row[0].strip()
|
| 54 |
+
token_id = row[1].strip()
|
| 55 |
+
|
| 56 |
+
if not sent_id.isdigit():
|
| 57 |
+
continue
|
| 58 |
+
if not token_id.isdigit():
|
| 59 |
+
continue
|
| 60 |
+
|
| 61 |
+
records.append({col: value for col, value in zip(COLS, row)})
|
| 62 |
+
|
| 63 |
+
return records
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def _join_tokens(tokens_with_misc: List[Dict[str, str]]) -> str:
|
| 67 |
+
chunks: List[str] = []
|
| 68 |
+
for token in tokens_with_misc:
|
| 69 |
+
chunks.append(token["word"])
|
| 70 |
+
if "SpaceAfter=No" not in token.get("misc", "_"):
|
| 71 |
+
chunks.append(" ")
|
| 72 |
+
return "".join(chunks).strip()
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def flatten_to_corpus(records: List[Dict[str, str]]) -> List[Dict[str, str]]:
|
| 76 |
+
grouped: Dict[str, List[Dict[str, str]]] = defaultdict(list)
|
| 77 |
+
for rec in records:
|
| 78 |
+
grouped[rec["sent_id"]].append(rec)
|
| 79 |
+
|
| 80 |
+
corpus: List[Dict[str, str]] = []
|
| 81 |
+
for sent_id in sorted(grouped.keys(), key=lambda x: int(x)):
|
| 82 |
+
toks = sorted(grouped[sent_id], key=lambda r: int(r["token_id"]))
|
| 83 |
+
text = _join_tokens(toks)
|
| 84 |
+
|
| 85 |
+
predicate = ""
|
| 86 |
+
roles: Dict[str, List[str]] = defaultdict(list)
|
| 87 |
+
|
| 88 |
+
for tok in toks:
|
| 89 |
+
sense = tok.get("predicate_sense", "_")
|
| 90 |
+
role = tok.get("semantic_role", "_")
|
| 91 |
+
word = tok.get("word", "")
|
| 92 |
+
|
| 93 |
+
if not predicate and sense and sense != "_":
|
| 94 |
+
predicate = sense
|
| 95 |
+
|
| 96 |
+
if role and role != "_":
|
| 97 |
+
roles[role].append(word)
|
| 98 |
+
|
| 99 |
+
doc: Dict[str, str] = {"id": sent_id, "text": text, "predicate": predicate}
|
| 100 |
+
doc.update({role_name: " ".join(words) for role_name, words in roles.items()})
|
| 101 |
+
corpus.append(doc)
|
| 102 |
+
|
| 103 |
+
return corpus
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def main() -> None:
|
| 107 |
+
parser = argparse.ArgumentParser(
|
| 108 |
+
description="Parse CoNLL-U SRL data to flattened JSON corpus"
|
| 109 |
+
)
|
| 110 |
+
parser.add_argument(
|
| 111 |
+
"--input-dir",
|
| 112 |
+
type=Path,
|
| 113 |
+
default=Path(__file__).resolve().parents[1] / "data",
|
| 114 |
+
help="Directory containing CoNLL-U style TSV/CSV files",
|
| 115 |
+
)
|
| 116 |
+
parser.add_argument(
|
| 117 |
+
"--output",
|
| 118 |
+
type=Path,
|
| 119 |
+
default=Path(__file__).resolve().parents[1] / "data" / "corpus.json",
|
| 120 |
+
help="Output JSON corpus path",
|
| 121 |
+
)
|
| 122 |
+
args = parser.parse_args()
|
| 123 |
+
|
| 124 |
+
data_files = [f for f in args.input_dir.glob("*.csv")]
|
| 125 |
+
corpus: List[Dict[str, str]] = []
|
| 126 |
+
for data_file in sorted(data_files):
|
| 127 |
+
records = read_conllu_srl(data_file)
|
| 128 |
+
corpus.extend(flatten_to_corpus(records))
|
| 129 |
+
|
| 130 |
+
args.output.parent.mkdir(parents=True, exist_ok=True)
|
| 131 |
+
with args.output.open("w", encoding="utf-8") as f:
|
| 132 |
+
json.dump(corpus, f, ensure_ascii=False, indent=2)
|
| 133 |
+
|
| 134 |
+
print(f"Wrote {len(corpus)} documents to {args.output}")
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
if __name__ == "__main__":
|
| 138 |
+
main()
|
backend/requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn
|
| 3 |
+
rank-bm25
|
| 4 |
+
sentence-transformers
|
| 5 |
+
spacy
|
| 6 |
+
numpy
|
frontend/.gitignore
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Logs
|
| 2 |
+
logs
|
| 3 |
+
*.log
|
| 4 |
+
npm-debug.log*
|
| 5 |
+
yarn-debug.log*
|
| 6 |
+
yarn-error.log*
|
| 7 |
+
pnpm-debug.log*
|
| 8 |
+
lerna-debug.log*
|
| 9 |
+
|
| 10 |
+
node_modules
|
| 11 |
+
dist
|
| 12 |
+
dist-ssr
|
| 13 |
+
*.local
|
| 14 |
+
|
| 15 |
+
# Editor directories and files
|
| 16 |
+
.vscode/*
|
| 17 |
+
!.vscode/extensions.json
|
| 18 |
+
.idea
|
| 19 |
+
.DS_Store
|
| 20 |
+
*.suo
|
| 21 |
+
*.ntvs*
|
| 22 |
+
*.njsproj
|
| 23 |
+
*.sln
|
| 24 |
+
*.sw?
|
frontend/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# React + TypeScript + Vite
|
| 2 |
+
|
| 3 |
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
| 4 |
+
|
| 5 |
+
Currently, two official plugins are available:
|
| 6 |
+
|
| 7 |
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
|
| 8 |
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
|
| 9 |
+
|
| 10 |
+
## React Compiler
|
| 11 |
+
|
| 12 |
+
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
|
| 13 |
+
|
| 14 |
+
## Expanding the ESLint configuration
|
| 15 |
+
|
| 16 |
+
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
| 17 |
+
|
| 18 |
+
```js
|
| 19 |
+
export default defineConfig([
|
| 20 |
+
globalIgnores(['dist']),
|
| 21 |
+
{
|
| 22 |
+
files: ['**/*.{ts,tsx}'],
|
| 23 |
+
extends: [
|
| 24 |
+
// Other configs...
|
| 25 |
+
|
| 26 |
+
// Remove tseslint.configs.recommended and replace with this
|
| 27 |
+
tseslint.configs.recommendedTypeChecked,
|
| 28 |
+
// Alternatively, use this for stricter rules
|
| 29 |
+
tseslint.configs.strictTypeChecked,
|
| 30 |
+
// Optionally, add this for stylistic rules
|
| 31 |
+
tseslint.configs.stylisticTypeChecked,
|
| 32 |
+
|
| 33 |
+
// Other configs...
|
| 34 |
+
],
|
| 35 |
+
languageOptions: {
|
| 36 |
+
parserOptions: {
|
| 37 |
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
| 38 |
+
tsconfigRootDir: import.meta.dirname,
|
| 39 |
+
},
|
| 40 |
+
// other options...
|
| 41 |
+
},
|
| 42 |
+
},
|
| 43 |
+
])
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
| 47 |
+
|
| 48 |
+
```js
|
| 49 |
+
// eslint.config.js
|
| 50 |
+
import reactX from 'eslint-plugin-react-x'
|
| 51 |
+
import reactDom from 'eslint-plugin-react-dom'
|
| 52 |
+
|
| 53 |
+
export default defineConfig([
|
| 54 |
+
globalIgnores(['dist']),
|
| 55 |
+
{
|
| 56 |
+
files: ['**/*.{ts,tsx}'],
|
| 57 |
+
extends: [
|
| 58 |
+
// Other configs...
|
| 59 |
+
// Enable lint rules for React
|
| 60 |
+
reactX.configs['recommended-typescript'],
|
| 61 |
+
// Enable lint rules for React DOM
|
| 62 |
+
reactDom.configs.recommended,
|
| 63 |
+
],
|
| 64 |
+
languageOptions: {
|
| 65 |
+
parserOptions: {
|
| 66 |
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
| 67 |
+
tsconfigRootDir: import.meta.dirname,
|
| 68 |
+
},
|
| 69 |
+
// other options...
|
| 70 |
+
},
|
| 71 |
+
},
|
| 72 |
+
])
|
| 73 |
+
```
|
frontend/bun.lock
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"lockfileVersion": 1,
|
| 3 |
+
"configVersion": 1,
|
| 4 |
+
"workspaces": {
|
| 5 |
+
"": {
|
| 6 |
+
"name": "frontend",
|
| 7 |
+
"dependencies": {
|
| 8 |
+
"@tailwindcss/vite": "^4.2.2",
|
| 9 |
+
"lucide-react": "^1.7.0",
|
| 10 |
+
"react": "^19.2.4",
|
| 11 |
+
"react-dom": "^19.2.4",
|
| 12 |
+
},
|
| 13 |
+
"devDependencies": {
|
| 14 |
+
"@eslint/js": "^9.39.4",
|
| 15 |
+
"@types/node": "^24.12.0",
|
| 16 |
+
"@types/react": "^19.2.14",
|
| 17 |
+
"@types/react-dom": "^19.2.3",
|
| 18 |
+
"@vitejs/plugin-react": "^6.0.1",
|
| 19 |
+
"autoprefixer": "^10.4.27",
|
| 20 |
+
"eslint": "^9.39.4",
|
| 21 |
+
"eslint-plugin-react-hooks": "^7.0.1",
|
| 22 |
+
"eslint-plugin-react-refresh": "^0.5.2",
|
| 23 |
+
"globals": "^17.4.0",
|
| 24 |
+
"postcss": "^8.5.8",
|
| 25 |
+
"tailwindcss": "^4.2.2",
|
| 26 |
+
"typescript": "~5.9.3",
|
| 27 |
+
"typescript-eslint": "^8.57.0",
|
| 28 |
+
"vite": "^8.0.1",
|
| 29 |
+
},
|
| 30 |
+
},
|
| 31 |
+
},
|
| 32 |
+
"packages": {
|
| 33 |
+
"@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="],
|
| 34 |
+
|
| 35 |
+
"@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="],
|
| 36 |
+
|
| 37 |
+
"@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="],
|
| 38 |
+
|
| 39 |
+
"@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="],
|
| 40 |
+
|
| 41 |
+
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="],
|
| 42 |
+
|
| 43 |
+
"@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
|
| 44 |
+
|
| 45 |
+
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="],
|
| 46 |
+
|
| 47 |
+
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="],
|
| 48 |
+
|
| 49 |
+
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
| 50 |
+
|
| 51 |
+
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
| 52 |
+
|
| 53 |
+
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
| 54 |
+
|
| 55 |
+
"@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="],
|
| 56 |
+
|
| 57 |
+
"@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="],
|
| 58 |
+
|
| 59 |
+
"@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="],
|
| 60 |
+
|
| 61 |
+
"@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="],
|
| 62 |
+
|
| 63 |
+
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
| 64 |
+
|
| 65 |
+
"@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
|
| 66 |
+
|
| 67 |
+
"@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
|
| 68 |
+
|
| 69 |
+
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
|
| 70 |
+
|
| 71 |
+
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="],
|
| 72 |
+
|
| 73 |
+
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
|
| 74 |
+
|
| 75 |
+
"@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="],
|
| 76 |
+
|
| 77 |
+
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="],
|
| 78 |
+
|
| 79 |
+
"@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
|
| 80 |
+
|
| 81 |
+
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg=="],
|
| 82 |
+
|
| 83 |
+
"@eslint/js": ["@eslint/js@9.39.4", "", {}, "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw=="],
|
| 84 |
+
|
| 85 |
+
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
|
| 86 |
+
|
| 87 |
+
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
|
| 88 |
+
|
| 89 |
+
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
| 90 |
+
|
| 91 |
+
"@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
|
| 92 |
+
|
| 93 |
+
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
| 94 |
+
|
| 95 |
+
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
| 96 |
+
|
| 97 |
+
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
| 98 |
+
|
| 99 |
+
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
| 100 |
+
|
| 101 |
+
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
| 102 |
+
|
| 103 |
+
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
| 104 |
+
|
| 105 |
+
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
| 106 |
+
|
| 107 |
+
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="],
|
| 108 |
+
|
| 109 |
+
"@oxc-project/types": ["@oxc-project/types@0.122.0", "", {}, "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA=="],
|
| 110 |
+
|
| 111 |
+
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.12", "", { "os": "android", "cpu": "arm64" }, "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA=="],
|
| 112 |
+
|
| 113 |
+
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg=="],
|
| 114 |
+
|
| 115 |
+
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw=="],
|
| 116 |
+
|
| 117 |
+
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q=="],
|
| 118 |
+
|
| 119 |
+
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm" }, "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q=="],
|
| 120 |
+
|
| 121 |
+
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg=="],
|
| 122 |
+
|
| 123 |
+
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw=="],
|
| 124 |
+
|
| 125 |
+
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g=="],
|
| 126 |
+
|
| 127 |
+
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og=="],
|
| 128 |
+
|
| 129 |
+
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg=="],
|
| 130 |
+
|
| 131 |
+
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.12", "", { "os": "linux", "cpu": "x64" }, "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig=="],
|
| 132 |
+
|
| 133 |
+
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.12", "", { "os": "none", "cpu": "arm64" }, "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA=="],
|
| 134 |
+
|
| 135 |
+
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.12", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "cpu": "none" }, "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg=="],
|
| 136 |
+
|
| 137 |
+
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q=="],
|
| 138 |
+
|
| 139 |
+
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.12", "", { "os": "win32", "cpu": "x64" }, "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw=="],
|
| 140 |
+
|
| 141 |
+
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.7", "", {}, "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA=="],
|
| 142 |
+
|
| 143 |
+
"@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="],
|
| 144 |
+
|
| 145 |
+
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="],
|
| 146 |
+
|
| 147 |
+
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="],
|
| 148 |
+
|
| 149 |
+
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="],
|
| 150 |
+
|
| 151 |
+
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="],
|
| 152 |
+
|
| 153 |
+
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="],
|
| 154 |
+
|
| 155 |
+
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="],
|
| 156 |
+
|
| 157 |
+
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="],
|
| 158 |
+
|
| 159 |
+
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="],
|
| 160 |
+
|
| 161 |
+
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="],
|
| 162 |
+
|
| 163 |
+
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="],
|
| 164 |
+
|
| 165 |
+
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="],
|
| 166 |
+
|
| 167 |
+
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="],
|
| 168 |
+
|
| 169 |
+
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="],
|
| 170 |
+
|
| 171 |
+
"@tailwindcss/vite": ["@tailwindcss/vite@4.2.2", "", { "dependencies": { "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "tailwindcss": "4.2.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w=="],
|
| 172 |
+
|
| 173 |
+
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
| 174 |
+
|
| 175 |
+
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
| 176 |
+
|
| 177 |
+
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
| 178 |
+
|
| 179 |
+
"@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="],
|
| 180 |
+
|
| 181 |
+
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
| 182 |
+
|
| 183 |
+
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
| 184 |
+
|
| 185 |
+
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.58.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/type-utils": "8.58.0", "@typescript-eslint/utils": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.58.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg=="],
|
| 186 |
+
|
| 187 |
+
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.58.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA=="],
|
| 188 |
+
|
| 189 |
+
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.58.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.58.0", "@typescript-eslint/types": "^8.58.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg=="],
|
| 190 |
+
|
| 191 |
+
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0" } }, "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ=="],
|
| 192 |
+
|
| 193 |
+
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.58.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A=="],
|
| 194 |
+
|
| 195 |
+
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/utils": "8.58.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg=="],
|
| 196 |
+
|
| 197 |
+
"@typescript-eslint/types": ["@typescript-eslint/types@8.58.0", "", {}, "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww=="],
|
| 198 |
+
|
| 199 |
+
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.58.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.58.0", "@typescript-eslint/tsconfig-utils": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA=="],
|
| 200 |
+
|
| 201 |
+
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.58.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA=="],
|
| 202 |
+
|
| 203 |
+
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.58.0", "", { "dependencies": { "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ=="],
|
| 204 |
+
|
| 205 |
+
"@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.1", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.7" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ=="],
|
| 206 |
+
|
| 207 |
+
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
| 208 |
+
|
| 209 |
+
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
| 210 |
+
|
| 211 |
+
"ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
|
| 212 |
+
|
| 213 |
+
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
| 214 |
+
|
| 215 |
+
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
| 216 |
+
|
| 217 |
+
"autoprefixer": ["autoprefixer@10.4.27", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA=="],
|
| 218 |
+
|
| 219 |
+
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
| 220 |
+
|
| 221 |
+
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.13", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw=="],
|
| 222 |
+
|
| 223 |
+
"brace-expansion": ["brace-expansion@1.1.13", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w=="],
|
| 224 |
+
|
| 225 |
+
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
|
| 226 |
+
|
| 227 |
+
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
| 228 |
+
|
| 229 |
+
"caniuse-lite": ["caniuse-lite@1.0.30001784", "", {}, "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw=="],
|
| 230 |
+
|
| 231 |
+
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
| 232 |
+
|
| 233 |
+
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
| 234 |
+
|
| 235 |
+
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
| 236 |
+
|
| 237 |
+
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
| 238 |
+
|
| 239 |
+
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
| 240 |
+
|
| 241 |
+
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
| 242 |
+
|
| 243 |
+
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
| 244 |
+
|
| 245 |
+
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
| 246 |
+
|
| 247 |
+
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
| 248 |
+
|
| 249 |
+
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
| 250 |
+
|
| 251 |
+
"electron-to-chromium": ["electron-to-chromium@1.5.331", "", {}, "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q=="],
|
| 252 |
+
|
| 253 |
+
"enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="],
|
| 254 |
+
|
| 255 |
+
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
| 256 |
+
|
| 257 |
+
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
| 258 |
+
|
| 259 |
+
"eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="],
|
| 260 |
+
|
| 261 |
+
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="],
|
| 262 |
+
|
| 263 |
+
"eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.5.2", "", { "peerDependencies": { "eslint": "^9 || ^10" } }, "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA=="],
|
| 264 |
+
|
| 265 |
+
"eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
|
| 266 |
+
|
| 267 |
+
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
|
| 268 |
+
|
| 269 |
+
"espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
|
| 270 |
+
|
| 271 |
+
"esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="],
|
| 272 |
+
|
| 273 |
+
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
| 274 |
+
|
| 275 |
+
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
| 276 |
+
|
| 277 |
+
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
| 278 |
+
|
| 279 |
+
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
| 280 |
+
|
| 281 |
+
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
| 282 |
+
|
| 283 |
+
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
| 284 |
+
|
| 285 |
+
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
| 286 |
+
|
| 287 |
+
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
| 288 |
+
|
| 289 |
+
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
| 290 |
+
|
| 291 |
+
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
| 292 |
+
|
| 293 |
+
"flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="],
|
| 294 |
+
|
| 295 |
+
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
| 296 |
+
|
| 297 |
+
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
| 298 |
+
|
| 299 |
+
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
| 300 |
+
|
| 301 |
+
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
| 302 |
+
|
| 303 |
+
"globals": ["globals@17.4.0", "", {}, "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw=="],
|
| 304 |
+
|
| 305 |
+
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
| 306 |
+
|
| 307 |
+
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
| 308 |
+
|
| 309 |
+
"hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="],
|
| 310 |
+
|
| 311 |
+
"hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="],
|
| 312 |
+
|
| 313 |
+
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
| 314 |
+
|
| 315 |
+
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
| 316 |
+
|
| 317 |
+
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
| 318 |
+
|
| 319 |
+
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
| 320 |
+
|
| 321 |
+
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
| 322 |
+
|
| 323 |
+
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
| 324 |
+
|
| 325 |
+
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
| 326 |
+
|
| 327 |
+
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
| 328 |
+
|
| 329 |
+
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
| 330 |
+
|
| 331 |
+
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
| 332 |
+
|
| 333 |
+
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
| 334 |
+
|
| 335 |
+
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
| 336 |
+
|
| 337 |
+
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
| 338 |
+
|
| 339 |
+
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
| 340 |
+
|
| 341 |
+
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
| 342 |
+
|
| 343 |
+
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
| 344 |
+
|
| 345 |
+
"lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
|
| 346 |
+
|
| 347 |
+
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
|
| 348 |
+
|
| 349 |
+
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
|
| 350 |
+
|
| 351 |
+
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
|
| 352 |
+
|
| 353 |
+
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
|
| 354 |
+
|
| 355 |
+
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
|
| 356 |
+
|
| 357 |
+
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
|
| 358 |
+
|
| 359 |
+
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
|
| 360 |
+
|
| 361 |
+
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
|
| 362 |
+
|
| 363 |
+
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
|
| 364 |
+
|
| 365 |
+
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
|
| 366 |
+
|
| 367 |
+
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
|
| 368 |
+
|
| 369 |
+
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
| 370 |
+
|
| 371 |
+
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
| 372 |
+
|
| 373 |
+
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
| 374 |
+
|
| 375 |
+
"lucide-react": ["lucide-react@1.7.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg=="],
|
| 376 |
+
|
| 377 |
+
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
| 378 |
+
|
| 379 |
+
"minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
|
| 380 |
+
|
| 381 |
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
| 382 |
+
|
| 383 |
+
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
| 384 |
+
|
| 385 |
+
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
| 386 |
+
|
| 387 |
+
"node-releases": ["node-releases@2.0.37", "", {}, "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg=="],
|
| 388 |
+
|
| 389 |
+
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
| 390 |
+
|
| 391 |
+
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
| 392 |
+
|
| 393 |
+
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
| 394 |
+
|
| 395 |
+
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
| 396 |
+
|
| 397 |
+
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
| 398 |
+
|
| 399 |
+
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
| 400 |
+
|
| 401 |
+
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
| 402 |
+
|
| 403 |
+
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
| 404 |
+
|
| 405 |
+
"postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="],
|
| 406 |
+
|
| 407 |
+
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
| 408 |
+
|
| 409 |
+
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
| 410 |
+
|
| 411 |
+
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
| 412 |
+
|
| 413 |
+
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
| 414 |
+
|
| 415 |
+
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
|
| 416 |
+
|
| 417 |
+
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
| 418 |
+
|
| 419 |
+
"rolldown": ["rolldown@1.0.0-rc.12", "", { "dependencies": { "@oxc-project/types": "=0.122.0", "@rolldown/pluginutils": "1.0.0-rc.12" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", "@rolldown/binding-darwin-x64": "1.0.0-rc.12", "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A=="],
|
| 420 |
+
|
| 421 |
+
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
| 422 |
+
|
| 423 |
+
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
| 424 |
+
|
| 425 |
+
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
| 426 |
+
|
| 427 |
+
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
| 428 |
+
|
| 429 |
+
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
| 430 |
+
|
| 431 |
+
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
| 432 |
+
|
| 433 |
+
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
| 434 |
+
|
| 435 |
+
"tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="],
|
| 436 |
+
|
| 437 |
+
"tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="],
|
| 438 |
+
|
| 439 |
+
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
| 440 |
+
|
| 441 |
+
"ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
|
| 442 |
+
|
| 443 |
+
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
| 444 |
+
|
| 445 |
+
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
| 446 |
+
|
| 447 |
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
| 448 |
+
|
| 449 |
+
"typescript-eslint": ["typescript-eslint@8.58.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.58.0", "@typescript-eslint/parser": "8.58.0", "@typescript-eslint/typescript-estree": "8.58.0", "@typescript-eslint/utils": "8.58.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA=="],
|
| 450 |
+
|
| 451 |
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
| 452 |
+
|
| 453 |
+
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
| 454 |
+
|
| 455 |
+
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
| 456 |
+
|
| 457 |
+
"vite": ["vite@8.0.3", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ=="],
|
| 458 |
+
|
| 459 |
+
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
| 460 |
+
|
| 461 |
+
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
| 462 |
+
|
| 463 |
+
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
| 464 |
+
|
| 465 |
+
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
| 466 |
+
|
| 467 |
+
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
| 468 |
+
|
| 469 |
+
"zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="],
|
| 470 |
+
|
| 471 |
+
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
| 472 |
+
|
| 473 |
+
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
| 474 |
+
|
| 475 |
+
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
|
| 476 |
+
|
| 477 |
+
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
|
| 478 |
+
|
| 479 |
+
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
|
| 480 |
+
|
| 481 |
+
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="],
|
| 482 |
+
|
| 483 |
+
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
| 484 |
+
|
| 485 |
+
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
| 486 |
+
|
| 487 |
+
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
| 488 |
+
|
| 489 |
+
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
| 490 |
+
|
| 491 |
+
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
| 492 |
+
|
| 493 |
+
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
|
| 494 |
+
|
| 495 |
+
"rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.12", "", {}, "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw=="],
|
| 496 |
+
|
| 497 |
+
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="],
|
| 498 |
+
|
| 499 |
+
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
|
| 500 |
+
}
|
| 501 |
+
}
|
frontend/eslint.config.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import js from '@eslint/js'
|
| 2 |
+
import globals from 'globals'
|
| 3 |
+
import reactHooks from 'eslint-plugin-react-hooks'
|
| 4 |
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
| 5 |
+
import tseslint from 'typescript-eslint'
|
| 6 |
+
import { defineConfig, globalIgnores } from 'eslint/config'
|
| 7 |
+
|
| 8 |
+
export default defineConfig([
|
| 9 |
+
globalIgnores(['dist']),
|
| 10 |
+
{
|
| 11 |
+
files: ['**/*.{ts,tsx}'],
|
| 12 |
+
extends: [
|
| 13 |
+
js.configs.recommended,
|
| 14 |
+
tseslint.configs.recommended,
|
| 15 |
+
reactHooks.configs.flat.recommended,
|
| 16 |
+
reactRefresh.configs.vite,
|
| 17 |
+
],
|
| 18 |
+
languageOptions: {
|
| 19 |
+
ecmaVersion: 2020,
|
| 20 |
+
globals: globals.browser,
|
| 21 |
+
},
|
| 22 |
+
},
|
| 23 |
+
])
|
frontend/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
+
<title>frontend</title>
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<div id="root"></div>
|
| 11 |
+
<script type="module" src="/src/main.tsx"></script>
|
| 12 |
+
</body>
|
| 13 |
+
</html>
|
frontend/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "frontend",
|
| 3 |
+
"private": true,
|
| 4 |
+
"version": "0.0.0",
|
| 5 |
+
"type": "module",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"dev": "vite",
|
| 8 |
+
"build": "tsc -b && vite build",
|
| 9 |
+
"lint": "eslint .",
|
| 10 |
+
"preview": "vite preview"
|
| 11 |
+
},
|
| 12 |
+
"dependencies": {
|
| 13 |
+
"@tailwindcss/vite": "^4.2.2",
|
| 14 |
+
"lucide-react": "^1.7.0",
|
| 15 |
+
"react": "^19.2.4",
|
| 16 |
+
"react-dom": "^19.2.4"
|
| 17 |
+
},
|
| 18 |
+
"devDependencies": {
|
| 19 |
+
"@eslint/js": "^9.39.4",
|
| 20 |
+
"@types/node": "^24.12.0",
|
| 21 |
+
"@types/react": "^19.2.14",
|
| 22 |
+
"@types/react-dom": "^19.2.3",
|
| 23 |
+
"@vitejs/plugin-react": "^6.0.1",
|
| 24 |
+
"autoprefixer": "^10.4.27",
|
| 25 |
+
"eslint": "^9.39.4",
|
| 26 |
+
"eslint-plugin-react-hooks": "^7.0.1",
|
| 27 |
+
"eslint-plugin-react-refresh": "^0.5.2",
|
| 28 |
+
"globals": "^17.4.0",
|
| 29 |
+
"postcss": "^8.5.8",
|
| 30 |
+
"tailwindcss": "^4.2.2",
|
| 31 |
+
"typescript": "~5.9.3",
|
| 32 |
+
"typescript-eslint": "^8.57.0",
|
| 33 |
+
"vite": "^8.0.1"
|
| 34 |
+
}
|
| 35 |
+
}
|
frontend/public/favicon.svg
ADDED
|
|
frontend/public/icons.svg
ADDED
|
|
frontend/src/App.tsx
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState } from 'react';
|
| 2 |
+
import { Search, Loader2, ChevronDown, ChevronUp } from 'lucide-react';
|
| 3 |
+
|
| 4 |
+
interface ApiResult {
|
| 5 |
+
id: string;
|
| 6 |
+
text: string;
|
| 7 |
+
scores: {
|
| 8 |
+
bm25: number;
|
| 9 |
+
dense: number;
|
| 10 |
+
srl: number;
|
| 11 |
+
hybrid: number;
|
| 12 |
+
};
|
| 13 |
+
roles?: {
|
| 14 |
+
predicate?: string;
|
| 15 |
+
ARG0?: string;
|
| 16 |
+
ARG1?: string;
|
| 17 |
+
[key: string]: string | undefined;
|
| 18 |
+
};
|
| 19 |
+
displacy_html?: string;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
interface ApiResponse {
|
| 23 |
+
bm25_results: ApiResult[];
|
| 24 |
+
dense_results: ApiResult[];
|
| 25 |
+
srl_results: ApiResult[];
|
| 26 |
+
hybrid_results: ApiResult[];
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
const highlightRoles = (text: string, roles?: ApiResult['roles']) => {
|
| 30 |
+
if (!roles || (!roles.predicate && !roles.ARG0 && !roles.ARG1)) return <>{text}</>;
|
| 31 |
+
|
| 32 |
+
const escapeRegExp = (str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
| 33 |
+
const matches: { start: number; end: number; type: 'predicate' | 'ARG0' | 'ARG1'; text: string }[] = [];
|
| 34 |
+
|
| 35 |
+
const findMatches = (roleText: string | undefined, type: 'predicate' | 'ARG0' | 'ARG1') => {
|
| 36 |
+
if (!roleText) return;
|
| 37 |
+
let searchStr = roleText;
|
| 38 |
+
if (type === 'predicate' && searchStr.includes('.')) {
|
| 39 |
+
searchStr = searchStr.split('.')[0];
|
| 40 |
+
}
|
| 41 |
+
const regex = new RegExp(`\\b(${escapeRegExp(searchStr)})\\b`, 'gi');
|
| 42 |
+
let match;
|
| 43 |
+
while ((match = regex.exec(text)) !== null) {
|
| 44 |
+
matches.push({ start: match.index, end: match.index + match[0].length, type, text: match[0] });
|
| 45 |
+
}
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
findMatches(roles.predicate, 'predicate');
|
| 49 |
+
findMatches(roles.ARG0, 'ARG0');
|
| 50 |
+
findMatches(roles.ARG1, 'ARG1');
|
| 51 |
+
|
| 52 |
+
if (matches.length === 0) return <>{text}</>;
|
| 53 |
+
|
| 54 |
+
matches.sort((a, b) => a.start - b.start);
|
| 55 |
+
|
| 56 |
+
const filteredMatches = [];
|
| 57 |
+
let lastEnd = 0;
|
| 58 |
+
for (const match of matches) {
|
| 59 |
+
if (match.start >= lastEnd) {
|
| 60 |
+
filteredMatches.push(match);
|
| 61 |
+
lastEnd = match.end;
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
const nodes = [];
|
| 66 |
+
let currentIndex = 0;
|
| 67 |
+
|
| 68 |
+
filteredMatches.forEach((match, i) => {
|
| 69 |
+
if (match.start > currentIndex) {
|
| 70 |
+
nodes.push(<span key={`text-${i}`}>{text.substring(currentIndex, match.start)}</span>);
|
| 71 |
+
}
|
| 72 |
+
let className = "";
|
| 73 |
+
if (match.type === 'predicate') className = "font-medium text-rose-400 bg-rose-500/10 border border-rose-500/20 px-1 rounded";
|
| 74 |
+
if (match.type === 'ARG0') className = "font-medium text-blue-400 bg-blue-500/10 border border-blue-500/20 px-1 rounded";
|
| 75 |
+
if (match.type === 'ARG1') className = "font-medium text-emerald-400 bg-emerald-500/10 border border-emerald-500/20 px-1 rounded";
|
| 76 |
+
nodes.push(<span key={`match-${i}`} className={className}>{match.text}</span>);
|
| 77 |
+
currentIndex = match.end;
|
| 78 |
+
});
|
| 79 |
+
|
| 80 |
+
if (currentIndex < text.length) {
|
| 81 |
+
nodes.push(<span key="text-end">{text.substring(currentIndex)}</span>);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
return <>{nodes}</>;
|
| 85 |
+
};
|
| 86 |
+
|
| 87 |
+
const ResultCard = ({ result, scoreKey }: { result: ApiResult, scoreKey: keyof ApiResult['scores'] }) => {
|
| 88 |
+
const [showHtml, setShowHtml] = useState(false);
|
| 89 |
+
|
| 90 |
+
return (
|
| 91 |
+
<div className="flex flex-col gap-3 p-4 bg-zinc-900/50 border border-zinc-800 rounded-xl transition-all hover:border-zinc-700">
|
| 92 |
+
<div className="flex items-center justify-between">
|
| 93 |
+
<span className="text-xs font-mono text-zinc-500">#{result.id}</span>
|
| 94 |
+
<span className="text-xs font-mono px-2 py-0.5 rounded-full bg-zinc-800 text-zinc-300">
|
| 95 |
+
Score: {result.scores[scoreKey].toFixed(4)}
|
| 96 |
+
</span>
|
| 97 |
+
</div>
|
| 98 |
+
|
| 99 |
+
<p className="text-sm leading-relaxed text-zinc-200">
|
| 100 |
+
{highlightRoles(result.text, result.roles)}
|
| 101 |
+
</p>
|
| 102 |
+
|
| 103 |
+
{result.roles && Object.keys(result.roles).length > 0 && (
|
| 104 |
+
<div className="flex flex-wrap gap-2 mt-2">
|
| 105 |
+
{result.roles.predicate && (
|
| 106 |
+
<span className="text-xs px-2 py-1 rounded bg-rose-500/10 text-rose-400 border border-rose-500/20">
|
| 107 |
+
Predicate: {result.roles.predicate}
|
| 108 |
+
</span>
|
| 109 |
+
)}
|
| 110 |
+
{result.roles.ARG0 && (
|
| 111 |
+
<span className="text-xs px-2 py-1 rounded bg-blue-500/10 text-blue-400 border border-blue-500/20">
|
| 112 |
+
ARG0: {result.roles.ARG0}
|
| 113 |
+
</span>
|
| 114 |
+
)}
|
| 115 |
+
{result.roles.ARG1 && (
|
| 116 |
+
<span className="text-xs px-2 py-1 rounded bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
|
| 117 |
+
ARG1: {result.roles.ARG1}
|
| 118 |
+
</span>
|
| 119 |
+
)}
|
| 120 |
+
</div>
|
| 121 |
+
)}
|
| 122 |
+
|
| 123 |
+
{result.displacy_html && (
|
| 124 |
+
<div className="mt-3 border-t border-zinc-800/80 pt-3">
|
| 125 |
+
<button
|
| 126 |
+
onClick={() => setShowHtml(!showHtml)}
|
| 127 |
+
className="flex items-center gap-1.5 text-xs text-zinc-400 hover:text-zinc-200 transition-colors w-full bg-zinc-800/50 hover:bg-zinc-800 py-1.5 px-3 rounded-md"
|
| 128 |
+
>
|
| 129 |
+
{showHtml ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />}
|
| 130 |
+
{showHtml ? 'Hide Parse Tree' : 'View Parse Tree'}
|
| 131 |
+
</button>
|
| 132 |
+
|
| 133 |
+
{showHtml && (
|
| 134 |
+
<div
|
| 135 |
+
className="mt-3 overflow-x-auto bg-zinc-950 p-4 rounded-lg border border-zinc-800 text-zinc-300 [&>svg]:max-w-none"
|
| 136 |
+
dangerouslySetInnerHTML={{ __html: result.displacy_html }}
|
| 137 |
+
/>
|
| 138 |
+
)}
|
| 139 |
+
</div>
|
| 140 |
+
)}
|
| 141 |
+
</div>
|
| 142 |
+
);
|
| 143 |
+
};
|
| 144 |
+
|
| 145 |
+
export default function App() {
|
| 146 |
+
const [query, setQuery] = useState('');
|
| 147 |
+
const [loading, setLoading] = useState(false);
|
| 148 |
+
const [error, setError] = useState<string | null>(null);
|
| 149 |
+
const [results, setResults] = useState<ApiResponse | null>(null);
|
| 150 |
+
|
| 151 |
+
const handleSearch = async (e: React.FormEvent) => {
|
| 152 |
+
e.preventDefault();
|
| 153 |
+
if (!query.trim()) return;
|
| 154 |
+
|
| 155 |
+
setLoading(true);
|
| 156 |
+
setError(null);
|
| 157 |
+
|
| 158 |
+
try {
|
| 159 |
+
const response = await fetch(`http://localhost:8001/search?q=${encodeURIComponent(query)}`);
|
| 160 |
+
if (!response.ok) throw new Error('Search request failed');
|
| 161 |
+
const data: ApiResponse = await response.json();
|
| 162 |
+
setResults(data);
|
| 163 |
+
} catch (err) {
|
| 164 |
+
setError(err instanceof Error ? err.message : 'Unknown error occurred');
|
| 165 |
+
|
| 166 |
+
// MOCK DATA FOR TESTING PURPOSES IF BACKEND IS DOWN
|
| 167 |
+
/*
|
| 168 |
+
setResults([
|
| 169 |
+
{
|
| 170 |
+
id: "doc_1",
|
| 171 |
+
text: "The cat quickly chased the small mouse across the floor.",
|
| 172 |
+
scores: { bm25: 0.8, dense: 0.9, srl: 0.7, hybrid: 0.85 },
|
| 173 |
+
roles: { predicate: "chased", ARG0: "The cat", ARG1: "the small mouse" },
|
| 174 |
+
displacy_html: "<svg height='100' width='300'><text x='50' y='50' fill='white'>Dependency Tree Placeholder</text></svg>"
|
| 175 |
+
},
|
| 176 |
+
{
|
| 177 |
+
id: "doc_2",
|
| 178 |
+
text: "A dog barked at the mailman.",
|
| 179 |
+
scores: { bm25: 0.4, dense: 0.6, srl: 0.2, hybrid: 0.5 },
|
| 180 |
+
roles: { predicate: "barked", ARG0: "A dog", ARG1: "at the mailman" }
|
| 181 |
+
}
|
| 182 |
+
]);
|
| 183 |
+
*/
|
| 184 |
+
} finally {
|
| 185 |
+
setLoading(false);
|
| 186 |
+
}
|
| 187 |
+
};
|
| 188 |
+
|
| 189 |
+
return (
|
| 190 |
+
<div className="min-h-screen bg-[#050505] text-zinc-200 font-sans selection:bg-rose-500/30">
|
| 191 |
+
{/* Header / Search Area */}
|
| 192 |
+
<div className="max-w-7xl mx-auto px-6 pt-24 pb-12">
|
| 193 |
+
<div className="max-w-4xl mx-auto space-y-10 text-center">
|
| 194 |
+
<div className="space-y-4">
|
| 195 |
+
<h1 className="text-5xl md:text-6xl font-light tracking-tight text-white">
|
| 196 |
+
Semantic <span className="text-zinc-500 font-serif italic">Explorer</span>
|
| 197 |
+
</h1>
|
| 198 |
+
<p className="text-zinc-500 text-lg font-light tracking-wide">
|
| 199 |
+
Compare retrieval paradigms across lexical, dense, and structural dimensions.
|
| 200 |
+
</p>
|
| 201 |
+
</div>
|
| 202 |
+
|
| 203 |
+
<form onSubmit={handleSearch} className="relative group">
|
| 204 |
+
<div className="absolute -inset-1 bg-gradient-to-r from-rose-500/20 via-blue-500/20 to-emerald-500/20 rounded-3xl blur-2xl opacity-40 group-hover:opacity-60 transition-opacity duration-700" />
|
| 205 |
+
<div className="relative flex items-center bg-zinc-900/80 backdrop-blur-xl border border-zinc-800 rounded-3xl overflow-hidden focus-within:border-zinc-600 transition-colors shadow-2xl">
|
| 206 |
+
<div className="pl-6 pr-4 text-zinc-500">
|
| 207 |
+
<Search className="w-6 h-6" />
|
| 208 |
+
</div>
|
| 209 |
+
<input
|
| 210 |
+
type="text"
|
| 211 |
+
value={query}
|
| 212 |
+
onChange={(e) => setQuery(e.target.value)}
|
| 213 |
+
placeholder="Query documents by semantic roles..."
|
| 214 |
+
className="w-full bg-transparent border-none py-6 pr-6 text-xl focus:outline-none text-zinc-100 placeholder:text-zinc-600 font-light"
|
| 215 |
+
/>
|
| 216 |
+
<button
|
| 217 |
+
type="submit"
|
| 218 |
+
disabled={loading || !query.trim()}
|
| 219 |
+
className="px-8 py-6 bg-zinc-100 text-zinc-900 font-medium hover:bg-white transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2 m-2 rounded-2xl"
|
| 220 |
+
>
|
| 221 |
+
{loading ? <Loader2 className="w-5 h-5 animate-spin" /> : 'Search'}
|
| 222 |
+
</button>
|
| 223 |
+
</div>
|
| 224 |
+
</form>
|
| 225 |
+
|
| 226 |
+
{error && (
|
| 227 |
+
<div className="inline-block text-rose-400 bg-rose-500/10 border border-rose-500/20 py-3 px-6 rounded-xl text-sm font-medium">
|
| 228 |
+
{error}
|
| 229 |
+
</div>
|
| 230 |
+
)}
|
| 231 |
+
</div>
|
| 232 |
+
</div>
|
| 233 |
+
|
| 234 |
+
{/* Results Columns */}
|
| 235 |
+
{results && (
|
| 236 |
+
<div className="max-w-[120rem] mx-auto px-6 pb-32">
|
| 237 |
+
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6 items-start">
|
| 238 |
+
|
| 239 |
+
{/* BM25 Column */}
|
| 240 |
+
<div className="flex flex-col gap-4">
|
| 241 |
+
<div className="sticky top-0 z-10 py-4 bg-[#050505]/90 backdrop-blur-md border-b border-zinc-800/50">
|
| 242 |
+
<h2 className="text-sm font-semibold tracking-widest text-zinc-400 uppercase flex items-center justify-between">
|
| 243 |
+
BM25
|
| 244 |
+
<span className="text-xs font-mono font-normal px-2 py-0.5 bg-zinc-900 border border-zinc-800 rounded-md text-zinc-500">{results.bm25_results?.length || 0}</span>
|
| 245 |
+
</h2>
|
| 246 |
+
</div>
|
| 247 |
+
<div className="flex flex-col gap-4">
|
| 248 |
+
{results.bm25_results?.length === 0 ? (
|
| 249 |
+
<div className="p-4 text-center text-zinc-500 bg-zinc-900/50 rounded-xl border border-zinc-800 border-dashed">
|
| 250 |
+
No lexical matches found.
|
| 251 |
+
</div>
|
| 252 |
+
) : (
|
| 253 |
+
results.bm25_results?.map(res => (
|
| 254 |
+
<ResultCard key={`bm25-${res.id}`} result={res} scoreKey="bm25" />
|
| 255 |
+
))
|
| 256 |
+
)}
|
| 257 |
+
</div>
|
| 258 |
+
</div>
|
| 259 |
+
|
| 260 |
+
{/* Dense Column */}
|
| 261 |
+
<div className="flex flex-col gap-4">
|
| 262 |
+
<div className="sticky top-0 z-10 py-4 bg-[#050505]/90 backdrop-blur-md border-b border-zinc-800/50">
|
| 263 |
+
<h2 className="text-sm font-semibold tracking-widest text-zinc-400 uppercase flex items-center justify-between">
|
| 264 |
+
Dense
|
| 265 |
+
<span className="text-xs font-mono font-normal px-2 py-0.5 bg-zinc-900 border border-zinc-800 rounded-md text-zinc-500">{results.dense_results?.length || 0}</span>
|
| 266 |
+
</h2>
|
| 267 |
+
</div>
|
| 268 |
+
<div className="flex flex-col gap-4">
|
| 269 |
+
{results.dense_results?.length === 0 ? (
|
| 270 |
+
<div className="p-4 text-center text-zinc-500 bg-zinc-900/50 rounded-xl border border-zinc-800 border-dashed">
|
| 271 |
+
No dense matches found.
|
| 272 |
+
</div>
|
| 273 |
+
) : (
|
| 274 |
+
results.dense_results?.map(res => (
|
| 275 |
+
<ResultCard key={`dense-${res.id}`} result={res} scoreKey="dense" />
|
| 276 |
+
))
|
| 277 |
+
)}
|
| 278 |
+
</div>
|
| 279 |
+
</div>
|
| 280 |
+
|
| 281 |
+
{/* SRL Column */}
|
| 282 |
+
<div className="flex flex-col gap-4">
|
| 283 |
+
<div className="sticky top-0 z-10 py-4 bg-[#050505]/90 backdrop-blur-md border-b border-rose-900/30">
|
| 284 |
+
<h2 className="text-sm font-semibold tracking-widest text-rose-400 uppercase flex items-center justify-between">
|
| 285 |
+
SRL
|
| 286 |
+
<span className="text-xs font-mono font-normal px-2 py-0.5 bg-rose-950 border border-rose-900/50 rounded-md text-rose-500">{results.srl_results?.length || 0}</span>
|
| 287 |
+
</h2>
|
| 288 |
+
</div>
|
| 289 |
+
<div className="flex flex-col gap-4">
|
| 290 |
+
{results.srl_results?.length === 0 ? (
|
| 291 |
+
<div className="p-4 text-center text-zinc-500 bg-zinc-900/50 rounded-xl border border-zinc-800 border-dashed">
|
| 292 |
+
No structural matches found.
|
| 293 |
+
</div>
|
| 294 |
+
) : (
|
| 295 |
+
results.srl_results?.map(res => (
|
| 296 |
+
<ResultCard key={`srl-${res.id}`} result={res} scoreKey="srl" />
|
| 297 |
+
))
|
| 298 |
+
)}
|
| 299 |
+
</div>
|
| 300 |
+
</div>
|
| 301 |
+
|
| 302 |
+
{/* Hybrid Column */}
|
| 303 |
+
<div className="flex flex-col gap-4">
|
| 304 |
+
<div className="sticky top-0 z-10 py-4 bg-[#050505]/90 backdrop-blur-md border-b border-emerald-900/30">
|
| 305 |
+
<h2 className="text-sm font-semibold tracking-widest text-emerald-400 uppercase flex items-center justify-between">
|
| 306 |
+
Hybrid
|
| 307 |
+
<span className="text-xs font-mono font-normal px-2 py-0.5 bg-emerald-950 border border-emerald-900/50 rounded-md text-emerald-500">{results.hybrid_results?.length || 0}</span>
|
| 308 |
+
</h2>
|
| 309 |
+
</div>
|
| 310 |
+
<div className="flex flex-col gap-4">
|
| 311 |
+
{results.hybrid_results?.length === 0 ? (
|
| 312 |
+
<div className="p-4 text-center text-zinc-500 bg-zinc-900/50 rounded-xl border border-zinc-800 border-dashed">
|
| 313 |
+
No hybrid matches found.
|
| 314 |
+
</div>
|
| 315 |
+
) : (
|
| 316 |
+
results.hybrid_results?.map(res => (
|
| 317 |
+
<ResultCard key={`hybrid-${res.id}`} result={res} scoreKey="hybrid" />
|
| 318 |
+
))
|
| 319 |
+
)}
|
| 320 |
+
</div>
|
| 321 |
+
</div>
|
| 322 |
+
|
| 323 |
+
</div>
|
| 324 |
+
</div>
|
| 325 |
+
)}
|
| 326 |
+
</div>
|
| 327 |
+
);
|
| 328 |
+
}
|
frontend/src/assets/hero.png
ADDED
|
frontend/src/assets/react.svg
ADDED
|
|
frontend/src/assets/vite.svg
ADDED
|
|
frontend/src/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
@import 'tailwindcss';
|
frontend/src/main.tsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { StrictMode } from 'react'
|
| 2 |
+
import { createRoot } from 'react-dom/client'
|
| 3 |
+
import './index.css'
|
| 4 |
+
import App from './App.tsx'
|
| 5 |
+
|
| 6 |
+
createRoot(document.getElementById('root')!).render(
|
| 7 |
+
<StrictMode>
|
| 8 |
+
<App />
|
| 9 |
+
</StrictMode>,
|
| 10 |
+
)
|
frontend/tsconfig.app.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
| 4 |
+
"target": "ES2023",
|
| 5 |
+
"useDefineForClassFields": true,
|
| 6 |
+
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
| 7 |
+
"module": "ESNext",
|
| 8 |
+
"types": ["vite/client"],
|
| 9 |
+
"skipLibCheck": true,
|
| 10 |
+
|
| 11 |
+
/* Bundler mode */
|
| 12 |
+
"moduleResolution": "bundler",
|
| 13 |
+
"allowImportingTsExtensions": true,
|
| 14 |
+
"verbatimModuleSyntax": true,
|
| 15 |
+
"moduleDetection": "force",
|
| 16 |
+
"noEmit": true,
|
| 17 |
+
"jsx": "react-jsx",
|
| 18 |
+
|
| 19 |
+
/* Linting */
|
| 20 |
+
"strict": true,
|
| 21 |
+
"noUnusedLocals": true,
|
| 22 |
+
"noUnusedParameters": true,
|
| 23 |
+
"erasableSyntaxOnly": true,
|
| 24 |
+
"noFallthroughCasesInSwitch": true,
|
| 25 |
+
"noUncheckedSideEffectImports": true
|
| 26 |
+
},
|
| 27 |
+
"include": ["src"]
|
| 28 |
+
}
|
frontend/tsconfig.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"files": [],
|
| 3 |
+
"references": [
|
| 4 |
+
{ "path": "./tsconfig.app.json" },
|
| 5 |
+
{ "path": "./tsconfig.node.json" }
|
| 6 |
+
]
|
| 7 |
+
}
|
frontend/tsconfig.node.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
| 4 |
+
"target": "ES2023",
|
| 5 |
+
"lib": ["ES2023"],
|
| 6 |
+
"module": "ESNext",
|
| 7 |
+
"types": ["node"],
|
| 8 |
+
"skipLibCheck": true,
|
| 9 |
+
|
| 10 |
+
/* Bundler mode */
|
| 11 |
+
"moduleResolution": "bundler",
|
| 12 |
+
"allowImportingTsExtensions": true,
|
| 13 |
+
"verbatimModuleSyntax": true,
|
| 14 |
+
"moduleDetection": "force",
|
| 15 |
+
"noEmit": true,
|
| 16 |
+
|
| 17 |
+
/* Linting */
|
| 18 |
+
"strict": true,
|
| 19 |
+
"noUnusedLocals": true,
|
| 20 |
+
"noUnusedParameters": true,
|
| 21 |
+
"erasableSyntaxOnly": true,
|
| 22 |
+
"noFallthroughCasesInSwitch": true,
|
| 23 |
+
"noUncheckedSideEffectImports": true
|
| 24 |
+
},
|
| 25 |
+
"include": ["vite.config.ts"]
|
| 26 |
+
}
|
frontend/vite.config.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from 'vite'
|
| 2 |
+
import react from '@vitejs/plugin-react'
|
| 3 |
+
import tailwindcss from '@tailwindcss/vite'
|
| 4 |
+
|
| 5 |
+
// https://vite.dev/config/
|
| 6 |
+
export default defineConfig({
|
| 7 |
+
plugins: [react(), tailwindcss()],
|
| 8 |
+
})
|